diff options
Diffstat (limited to 'pseudo_util.c')
-rw-r--r-- | pseudo_util.c | 168 |
1 files changed, 86 insertions, 82 deletions
diff --git a/pseudo_util.c b/pseudo_util.c index 7859ad9..e01dfc2 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -266,7 +266,7 @@ static int debugged_newline = 1; static char pid_text[32]; static size_t pid_len; static int pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurrent, const char *element, size_t elen, PSEUDO_STATBUF *buf, int leave_this); -static int pseudo_append_elements(char *newpath, char *root, size_t allocated, char **current, const char *elements, size_t elen, int leave_last); +static int pseudo_append_elements(char *newpath, char *root, size_t allocated, char **current, const char *elements, size_t elen, int leave_last, PSEUDO_STATBUF *sbuf); extern char **environ; static ssize_t pseudo_max_pathlen = -1; static ssize_t pseudo_sys_max_pathlen = -1; @@ -621,7 +621,7 @@ static int pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurrent, const char *element, size_t elen, PSEUDO_STATBUF *buf, int leave_this) { static int link_recursion = 0; size_t curlen; - int is_reg = S_ISREG(buf->st_mode); + int is_dir = S_ISDIR(buf->st_mode); char *current; if (!newpath || !pcurrent || !*pcurrent || @@ -630,30 +630,31 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre return -1; } current = *pcurrent; - pseudo_debug(PDBGF_PATH | PDBGF_VERBOSE, "pae: %s, + %.*s\n", - newpath, (int) elen, element); - /* the special cases here to skip empty paths, or ./.., should not - * apply to plain files, which should just get bogus - * paths. + pseudo_debug(PDBGF_PATH | PDBGF_VERBOSE, "pae: '%s', + '%.*s', is_dir %d\n", + newpath, (int) elen, element, is_dir); + /* the special cases here to skip empty paths, or ./.., should apply + * only to directories; plain files, nodes, etcetera should just get + * bogus paths. */ - if (!is_reg) { + if (is_dir) { /* sanity-check: ignore // or /./ */ if (elen == 0 || (elen == 1 && *element == '.')) { return 0; } /* backtrack for .. */ if (elen == 2 && element[0] == '.' && element[1] == '.') { - /* if newpath's whole contents are '/', do nothing */ - if (current <= root + 1) - return 1; - /* backtrack to the character before the / */ - current -= 2; + /* if newpath is empty, do nothing. */ + if (current <= root) + return 0; /* now find the previous slash */ while (current > root && *current != '/') { --current; } - /* and point to the nul just past it */ - *(++current) = '\0'; + /* either we're at root, or we're at a slash. + * either way, nul that out, leaving us with a + * possibly-empty path which is not slash-terminated. + */ + *current = '\0'; *pcurrent = current; return 1; } @@ -665,21 +666,27 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre pseudo_diag("pseudo_append_element: path too long (wanted %lu bytes).\n", (unsigned long) curlen + elen + 3); return -1; } + /* append a slash */ + *current++ = '/'; memcpy(current, element, elen); current += elen; /* nul-terminate, and we now point to the nul after the element just added. */ *current = '\0'; - /* now, the moment of truth... is that a symlink? */ - /* if lstat fails, that's fine -- nonexistent files aren't symlinks */ - /* don't do this if the parent was a regular file. it shouldn't matter, - * because readlink can't succeed in that case anyway. + /* if we are not on the last element of a path and supposed to leave + * it alone (for SYMLINK_NOFOLLOW type cases), and we are not trying to + * go further under a regular file, we want to know whether this is a symlink. + * either way, we want to update buf to show the correct state of the file. */ - if (!leave_this && !is_reg) { - /* if this fails, just use the parent's mode. which shouldn't have - * been a symlink, I hope? */ - if (pseudo_real_lstat) { - pseudo_real_lstat(newpath, buf); - } + if (!pseudo_real_lstat || (pseudo_real_lstat(newpath, buf) == -1)) { + // if we can't stat it, zero mode so we don't think it's + // known to be a link or a regular file. + buf->st_mode = 0; + } + /* it is intentional that this uses the "stale" is_dir for the file we + * were appending to. we don't want to actually try to do this when + * we're appending names to a regular file. + */ + if (!leave_this && is_dir) { int is_link = S_ISLNK(buf->st_mode); if (link_recursion >= PSEUDO_MAX_LINK_RECURSION && is_link) { pseudo_diag("link recursion too deep, not expanding path '%s'.\n", newpath); @@ -702,14 +709,20 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre current = newpath + 1; } else { /* point back at the end of the previous path... */ - current -= elen; + current -= (elen + 1); } /* null terminate at the new pointer */ *current = '\0'; - /* append all the elements in series */ *pcurrent = current; + /* we know that we're now pointing either at a directory we + * already decided was safe to go into, or root. either way, + * the parent item mode should reflect it being a directory. + * we don't need to call stat for that. + */ + buf->st_mode = S_IFDIR; + /* append all the elements in series */ ++link_recursion; - retval = pseudo_append_elements(newpath, root, allocated, pcurrent, linkbuf, linklen, 0); + retval = pseudo_append_elements(newpath, root, allocated, pcurrent, linkbuf, linklen, 0, buf); --link_recursion; return retval; } @@ -722,69 +735,55 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre } static int -pseudo_append_elements(char *newpath, char *root, size_t allocated, char **current, const char *element, size_t elen, int leave_last) { +pseudo_append_elements(char *newpath, char *root, size_t allocated, char **current, const char *path, size_t elen, int leave_last, PSEUDO_STATBUF *sbuf) { int retval = 1; + /* a shareable buffer so we can cache stat results while walking the path */ PSEUDO_STATBUF buf; buf.st_mode = 0; - int add_slash = 0; - const char * start = element; + const char * start = path; if (!newpath || !root || !current || !*current || - !element) { + !path) { pseudo_diag("pseudo_append_elements: invalid arguments."); return -1; } + if (!sbuf) { + /* we will use this buffer to hold "the current state of newpath". + * append_element will update that whenever it appends an element, + * and any calls back here from there will pass in the same buffer. + * if we didn't get one, we start using this local one, which will + * then get shared by anything we call. + */ + sbuf = &buf; + if (!pseudo_real_lstat || (pseudo_real_lstat(newpath, sbuf) == -1)) { + sbuf->st_mode = (*current > root) ? 0 : S_IFDIR; + } + } pseudo_debug(PDBGF_PATH | PDBGF_VERBOSE, "paes: newpath %s, element list <%.*s>\n", - newpath, (int) elen, element); - /* coming into append_elements, we should always have a trailing slash on - * the path. append_element won't provide one, though. - */ - while (element < (start + elen) && *element) { + newpath, (int) elen, path); + while (path < (start + elen) && *path) { size_t this_elen; int leave_this = 0; - int is_reg = S_ISREG(buf.st_mode); - char *next = strchr(element, '/'); + char *next = strchr(path, '/'); if (!next) { - next = strchr(element, '\0'); + next = strchr(path, '\0'); leave_this = leave_last; } - this_elen = next - element; + this_elen = next - path; /* for a directory, we skip the append for empty path or "."; * regular files get it appended so they can fail properly * later for being invalid paths. */ pseudo_debug(PDBGF_PATH | PDBGF_VERBOSE, "element to add: '%.*s'\n", - (int) this_elen, element); - /* we initially had a trailing slash. we don't - * want append_element to append slashes, so every time through - * this loop after the first, we want to add a slash. - */ - if (add_slash) { - *(*current)++ = '/'; - *(*current) = '\0'; - } - if (is_reg || (this_elen > 1) || ((this_elen == 1) && (*element != '.'))) { - int appended = pseudo_append_element(newpath, root, allocated, current, element, this_elen, &buf, leave_this); - if (appended == -1) { - retval = -1; - break; - } - if (appended == 1) { - add_slash = 1; - } - pseudo_debug(PDBGF_FILE | PDBGF_VERBOSE, "paes: append_element gave us '%s', current '%s'\n", - newpath, *current); - /* if a path element was appended, we want to know whether the resulting - * thing is an existing regular file; regular files can't be further - * explored, which actually means we *do* append path things to them - * that would otherwise be skipped. - */ - if (!pseudo_real_lstat || (pseudo_real_lstat(newpath, &buf) == -1)) { - buf.st_mode = 0; - } + (int) this_elen, path); + if (pseudo_append_element(newpath, root, allocated, current, path, this_elen, sbuf, leave_this) == -1) { + retval = -1; + break; } + pseudo_debug(PDBGF_FILE | PDBGF_VERBOSE, "paes: append_element gave us '%s', current '%s'\n", + newpath, *current); /* and now move past the separator */ - element += this_elen + 1; + path += this_elen + 1; } return retval; } @@ -820,9 +819,12 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel newpath = pathbufs[pathbuf]; pathbuf = (pathbuf + 1) % PATHBUFS; pathlen = strlen(path); - /* a trailing slash has special meaning */ - if (pathlen > 0 && path[pathlen - 1] == '/') { + /* a trailing slash has special meaning, but processing + * trailing slashes is expensive. + */ + while (pathlen > 0 && path[pathlen - 1] == '/') { trailing_slash = 1; + --pathlen; } /* allow a bit of slush. overallocating a bit won't * hurt. rounding to 256's in the hopes that it makes life @@ -842,22 +844,24 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel * part of the string; you can't back up over it. */ effective_root = newpath + rootlen; - *current++ = '/'; *current = '\0'; /* at any given point: - * current points to just after the last / of newpath + * path is not slash-terminated + * current points to the null byte immediately after the path * path points to the next element of path * newpathlen is the total allocated length of newpath * (current - newpath) is the used length of newpath - * - * ... the above is now slightly wrong, we start append_elements - * and append_element with a trailing slash on the path, but - * end them without. */ - if (pseudo_append_elements(newpath, effective_root, newpathlen, ¤t, path, pathlen, leave_last) != -1) { - if (current > effective_root && trailing_slash) { - *current++ = '/'; - *current++ = '\0'; + if (pseudo_append_elements(newpath, effective_root, newpathlen, ¤t, path, pathlen, leave_last, 0) != -1) { + /* if we are expecting a trailing slash, or the path ended up being completely + * empty (meaning it's pointing at either effective_root or the beginning of + * the path), we need a slash here. + */ + if ((current == effective_root) || trailing_slash) { + if ((current - newpath) < (int) newpathlen) { + *current++ = '/'; + *current = '\0'; + } } pseudo_debug(PDBGF_PATH, "%s + %s => <%s>\n", base ? base : "<nil>", |