--- libinit.c +++ libinit.c 2011-01-27 12:58:48.944425833 +0000 @@ -218,6 +218,26 @@ static struct _sys_signals { { 0, "EXIT" } }; +/* + * Calculate the depth of a directory, root has zero depth. + */ +static size_t dirdepth(const char *const path) +{ + const char *ptr = path; + size_t cnt = 0; + + do { + const size_t off = strcspn(ptr, "/"); + ptr += off; + if (*ptr++ != '/') + break; + if (*ptr) + cnt++; + } while (*ptr); + + return cnt; +} + typedef struct _mntinfo_ { struct _mntinfo_ *next; @@ -226,6 +246,7 @@ typedef struct _mntinfo_ int id, parid; dev_t dev; size_t nlen; + size_t deep; char * point; } MNTINFO; @@ -253,6 +274,7 @@ static void init_mounts(void) p->point = ((char*)p)+alignof(MNTINFO); strcpy(p->point, point); p->nlen = nlen; + p->deep = dirdepth(point); p->parid = parid; p->dev = makedev(maj, min); p->id = mid; @@ -262,20 +284,20 @@ static void init_mounts(void) p->prev = (MNTINFO*)0; p->parent = p; mounts = p; - if (mid > max) - max = mid; + if (p->id > max) + max = p->id; } fclose(mnt); - /* Sort mount points accordingly to the reverse mount order */ + /* Sort mount points accordingly to their reverse directory depth */ save = (MNTINFO*)0; - for (mid = 1; mid <= max; mid++) { + for (mid = 0; mounts; mid++) { MNTINFO *m, *n, *l; n = mounts; l = (MNTINFO*)0; for (m = mounts; n; m = n) { n = m->next; - if (m->id != mid) + if (m->deep != mid) continue; l = m->prev; if (m == mounts) { @@ -290,7 +312,6 @@ static void init_mounts(void) m->next = save; m->prev = (MNTINFO*)0; save = m; - break; } } mounts = save; @@ -307,15 +328,14 @@ static void init_mounts(void) } } -static MNTINFO *find_mount(const char * path) +static int find_mount(const char * path, MNTINFO *s) { MNTINFO *m; for (m = mounts; m; m = m->next) { - if (strncmp(path, m->point, m->nlen)) - continue; - break; + if (s == m) + return 1; } - return m; + return 0; } extern inline char * handl_buf(char *restrict buf) @@ -627,6 +647,7 @@ int pidof (const char * inname, const ch pid_t pid; char *swapname = NULL; char *fullname = (char *)inname; + char *realname = NULL; MNTINFO *prefix = NULL; PROC *p, *n; @@ -659,12 +680,17 @@ int pidof (const char * inname, const ch warn("cannot stat %s: %s\n", fullname, strerror(errno)); return -1; } - init_mounts(); - for (m = mounts; m; m = m->next) { - if (m->dev != full_st.st_dev) - continue; - prefix = m; - break; + realname = expandpath(fullname); + if (realname) { + init_mounts(); + for (m = mounts; m; m = m->next) { + if (m->dev != full_st.st_dev) + continue; + if (strncmp(realname, m->point, m->nlen) != 0) + continue; + prefix = m; + break; + } } } @@ -727,20 +753,21 @@ int pidof (const char * inname, const ch */ if (!(flags & (KTHREAD|KSHORT)) && !isscrpt) { char entry[PATH_MAX+1]; - char *realname = NULL; - const char *name; + const char *name = NULL; boolean found; - if ((rll = readlinkat(dfd, here(d->d_name, "exe"), entry, PATH_MAX)) < 0) { - if (errno != EPERM && errno != EACCES) - goto risky; - continue; - } - entry[rll] = '\0'; - name = handl_buf(entry); + if (prefix) { + if ((rll = readlinkat(dfd, here(d->d_name, "exe"), entry, PATH_MAX)) < 0) { + if (errno != EPERM && errno != EACCES) + goto risky; + continue; + } + entry[rll] = '\0'; + name = handl_buf(entry); - if (prefix && find_mount(name) != prefix) - continue; + if (!find_mount(name, prefix)) + continue; + } if (fstatat(dfd, here(d->d_name, "exe"), &pid_st, 0) < 0) { if (errno != EPERM && errno != EACCES) @@ -765,18 +792,24 @@ int pidof (const char * inname, const ch if (strlen(fullname) > PATH_MAX) continue; + if (!name) { + if ((rll = readlinkat(dfd, here(d->d_name, "exe"), entry, PATH_MAX)) < 0) { + if (errno != EPERM && errno != EACCES) + goto risky; + continue; + } + entry[rll] = '\0'; + name = handl_buf(entry); + } + if (strncmp(fullname, name, PATH_MAX) == 0) { found = true; break; } - if ((realname = realpath(name, NULL)) == (char*)0) - continue; - - if (strncmp(realname, name, PATH_MAX) == 0) + if (realname && strncmp(realname, name, PATH_MAX) == 0) found = true; - free(realname); break; } @@ -896,6 +929,7 @@ int verify_pidfile (const char * pid_fil pid_t pid; char *swapname = NULL, *bufp; char *fullname = (char *)inname; + char *realname = NULL; struct stat pid_st, full_st; char buf[BUFSIZ]; @@ -958,6 +992,7 @@ int verify_pidfile (const char * pid_fil warn("cannot stat %s: %s\n", fullname, strerror(errno)); return -1; } + realname = expandpath(fullname); } if (flags & (KTHREAD|KSHORT)) { @@ -997,7 +1032,6 @@ int verify_pidfile (const char * pid_fil char entry[PATH_MAX+1]; const char *name; - char *realname; boolean found; ssize_t rll; @@ -1028,14 +1062,9 @@ int verify_pidfile (const char * pid_fil break; } - if ((realname = realpath(fullname, NULL)) == (char*)0) - goto out; - - if (strncmp(realname, name, PATH_MAX) == 0) + if (realname && strncmp(realname, name, PATH_MAX) == 0) found = true; - free(realname); - break; } @@ -1115,6 +1144,7 @@ int check_pids (const char * inname, con boolean isscrpt = false; char *swapname = (char*)0; char *fullname = (char *)inname; + char *realname = (char*)0; const char *pid; struct stat pid_st, full_st; PROC *p, *n, *l; @@ -1132,6 +1162,7 @@ int check_pids (const char * inname, con warn("cannot stat %s: %s\n", fullname, strerror(errno)); return -1; } + realname = expandpath(fullname); } if (flags & (KTHREAD|KSHORT)) { @@ -1191,7 +1222,6 @@ int check_pids (const char * inname, con char entry[PATH_MAX+1]; const char *name; - char *realname; ssize_t rll; if (pid_st.st_dev != full_st.st_dev) @@ -1218,14 +1248,8 @@ int check_pids (const char * inname, con if (strncmp(fullname, name, PATH_MAX) == 0) continue; /* Found */ - if ((realname = realpath(fullname, NULL)) == (char*)0) - goto ignore; /* Bogus */ - - if (strncmp(realname, name, PATH_MAX) == 0) { - free(realname); + if (realname && strncmp(realname, name, PATH_MAX) == 0) continue; /* Found */ - } - free(realname); break; } @@ -1807,4 +1831,100 @@ void clear_nfs(void) } } +/* + * Somehow the realpath(3) glibc function call, nevertheless + * it avoids lstat(2) system calls. + */ +static char real[PATH_MAX+1]; +char* expandpath(const char * path) +{ + char tmpbuf[PATH_MAX+1]; + const char *start, *end; + char *curr, *dest; + int deep = MAXSYMLINKS; + + if (!path || *path == '\0') + return (char*)0; + + curr = &real[0]; + + if (*path != '/') { + if (!getcwd(curr, PATH_MAX)) + return (char*)0; + dest = rawmemchr(curr, '\0'); + } else { + *curr = '/'; + dest = curr + 1; + } + + for (start = end = path; *start; start = end) { + + while (*start == '/') + ++start; + + for (end = start; *end && *end != '/'; ++end) + ; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') { + ; + } else if (end - start == 2 && start[0] == '.' && start[1] == '.') { + if (dest > curr + 1) + while ((--dest)[-1] != '/') + ; + } else { + char lnkbuf[PATH_MAX+1]; + size_t len; + ssize_t n; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) > curr + PATH_MAX) { + errno = ENAMETOOLONG; + return (char*)0; + } + + dest = mempcpy(dest, start, end - start); + *dest = '\0'; + + if (deep-- < 0) { + errno = ELOOP; + return (char*)0; + } + + errno = 0; + if ((n = readlink(curr, lnkbuf, PATH_MAX)) < 0) { + deep = MAXSYMLINKS; + if (errno == EINVAL) + continue; /* Not a symlink */ + return (char*)0; + } + lnkbuf[n] = '\0'; /* Don't be fooled by readlink(2) */ + + len = strlen(end); + if ((n + len) > PATH_MAX) { + errno = ENAMETOOLONG; + return (char*)0; + } + + memmove(&tmpbuf[n], end, len + 1); + path = end = memcpy(tmpbuf, lnkbuf, n); + + if (lnkbuf[0] == '/') + dest = curr + 1; + else if (dest > curr + 1) + while ((--dest)[-1] != '/'); + + } + } + + if (dest > curr + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + return curr; +} + /* libinit.c ends here */