When looking for a device match, do a breadth-first search instead

of depth-first.  We already special case /dev/pts/ so chances are
good that if it is not a pseudo-tty it is in the base of /dev/.
Also avoid a stat(2) when possible if struct dirent has d_type.
This commit is contained in:
Todd C. Miller
2012-04-13 16:00:32 -04:00
parent 5f969cc12a
commit 48d3b5aad1

View File

@@ -154,7 +154,7 @@ sudo_ttyname_dev(dev_t tdev)
} }
#else #else
/* /*
* Devices to search before doing a depth-first scan. * Devices to search before doing a breadth-first scan.
* XXX - use /etc/ttysrch too (for Solaris). * XXX - use /etc/ttysrch too (for Solaris).
* XXX - add tty* w/ wildcards? * XXX - add tty* w/ wildcards?
*/ */
@@ -177,14 +177,14 @@ static char *ignore_devs[] = {
}; };
/* /*
* Do a depth-first scan of dir looking for the specified device. * Do a breadth-first scan of dir looking for the specified device.
*/ */
static static
char *sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin) char *sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin)
{ {
DIR *d; DIR *d;
char pathbuf[PATH_MAX], *devname = NULL; char pathbuf[PATH_MAX], **subdirs = NULL, *devname = NULL;
size_t sdlen, d_len, len; size_t sdlen, d_len, len, num_subdirs = 0, max_subdirs = 0;
struct dirent *dp; struct dirent *dp;
struct stat sb; struct stat sb;
int i; int i;
@@ -237,21 +237,41 @@ char *sudo_ttyname_scan(const char *dir, dev_t rdev, bool builtin)
if (search_devs[i] != NULL) if (search_devs[i] != NULL)
continue; continue;
} }
if (stat(pathbuf, &sb) == 0) { # if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
if (S_ISDIR(sb.st_mode)) { /* Use d_type to avoid a stat() if possible. */
if (!builtin) /* Convert d_type to stat-style type bits but follow links. */
devname = sudo_ttyname_scan(pathbuf, rdev, builtin); if (dp->d_type != DT_LNK && dp->d_type != DT_CHR)
continue; sb.st_mode = DTTOIF(dp->d_type);
} else
if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) { # endif
devname = estrdup(pathbuf); if (stat(pathbuf, &sb) == -1)
break; continue;
if (S_ISDIR(sb.st_mode)) {
if (!builtin) {
/* Add to list of subdirs to search. */
if (num_subdirs + 1 > max_subdirs) {
max_subdirs += 64;
subdirs = erealloc3(subdirs, max_subdirs, sizeof(char *));
}
subdirs[num_subdirs++] = estrdup(pathbuf);
} }
continue;
}
if (S_ISCHR(sb.st_mode) && sb.st_rdev == rdev) {
devname = estrdup(pathbuf);
break;
} }
} }
closedir(d); closedir(d);
/* Search subdirs if we didn't find it in the root level. */
for (i = 0; devname == NULL && i < num_subdirs; i++)
devname = sudo_ttyname_scan(subdirs[i], rdev, false);
done: done:
for (i = 0; i < num_subdirs; i++)
efree(subdirs[i]);
efree(subdirs);
debug_return_str(devname); debug_return_str(devname);
} }
@@ -299,7 +319,7 @@ sudo_ttyname_dev(dev_t rdev)
} }
/* /*
* Not found? Do a depth-first traversal of /dev/. * Not found? Do a breadth-first traversal of /dev/.
*/ */
if (tty == NULL) if (tty == NULL)
tty = sudo_ttyname_scan(_PATH_DEV, rdev, false); tty = sudo_ttyname_scan(_PATH_DEV, rdev, false);