aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.txt6
-rw-r--r--pseudo_util.c106
2 files changed, 72 insertions, 40 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index e0783fb..8a8b71b 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,9 @@
+2018-03-06:
+ * (seebs) Update path handling a bit to correctly fail if a path
+ tries to have a slash after a plain file name, even in cases
+ like "foo/.", which were previously ignored (or in the case of
+ "..", resolved as though it had been a directory).
+
2018-03-01:
* (seebs) If you get a CREAT for an existing file, and it matches
both path and inode, don't delete the entry because the inode
diff --git a/pseudo_util.c b/pseudo_util.c
index 6a1fac2..addf503 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -265,7 +265,7 @@ int pseudo_util_evlog_fd = 2;
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, int leave_this);
+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);
extern char **environ;
static ssize_t pseudo_max_pathlen = -1;
@@ -618,11 +618,11 @@ pseudo_new_pid() {
* the symlink, appending each element in turn the same way.
*/
static int
-pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurrent, const char *element, size_t elen, int leave_this) {
+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);
char *current;
- PSEUDO_STATBUF buf;
if (!newpath ||
!pcurrent || !*pcurrent ||
!root || !element) {
@@ -630,25 +630,31 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre
return -1;
}
current = *pcurrent;
- /* sanity-check: ignore // or /./ */
- if (elen == 0 || (elen == 1 && *element == '.')) {
- return 1;
- }
- /* backtrack for .. */
- if (elen == 2 && element[0] == '.' && element[1] == '.') {
- /* if newpath's whole contents are '/', do nothing */
- if (current <= root + 1)
+ /* the special cases here to skip empty paths, or ./.., should not
+ * apply to plain files, which should just get bogus
+ * paths.
+ */
+ if (!is_reg) {
+ /* sanity-check: ignore // or /./ */
+ if (elen == 0 || (elen == 1 && *element == '.')) {
+ return 1;
+ }
+ /* 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;
+ /* now find the previous slash */
+ while (current > root && *current != '/') {
+ --current;
+ }
+ /* and point to the nul just past it */
+ *(++current) = '\0';
+ *pcurrent = current;
return 1;
- /* backtrack to the character before the / */
- current -= 2;
- /* now find the previous slash */
- while (current > root && *current != '/') {
- --current;
}
- /* and point to the nul just past it */
- *(++current) = '\0';
- *pcurrent = current;
- return 1;
}
curlen = current - newpath;
/* current length, plus / <element> / \0 */
@@ -659,13 +665,20 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre
}
memcpy(current, element, elen);
current += elen;
- /* nul-terminate, and we now point to the nul after the slash */
+ /* 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 */
- if (!leave_this) {
- int is_link;
- is_link = (pseudo_real_lstat) && (pseudo_real_lstat(newpath, &buf) != -1) && S_ISLNK(buf.st_mode);
+ /* 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 (!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);
+ }
+ 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);
is_link = 0;
@@ -699,9 +712,9 @@ pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurre
return retval;
}
}
- /* okay, not a symlink, go ahead and append a slash */
- *(current++) = '/';
- *current = '\0';
+ /* we used to always append a slash here. now we don't; append_elements
+ * handles slashes, so just update the pointer.
+ */
*pcurrent = current;
return 1;
}
@@ -709,6 +722,8 @@ 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) {
int retval = 1;
+ PSEUDO_STATBUF buf;
+ buf.st_mode = 0;
const char * start = element;
if (!newpath || !root ||
!current || !*current ||
@@ -716,30 +731,41 @@ pseudo_append_elements(char *newpath, char *root, size_t allocated, char **curre
pseudo_diag("pseudo_append_elements: invalid arguments.");
return -1;
}
+ /* 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) {
size_t this_elen;
int leave_this = 0;
+ int is_reg = S_ISREG(buf.st_mode);
char *next = strchr(element, '/');
if (!next) {
next = strchr(element, '\0');
leave_this = leave_last;
}
this_elen = next - element;
- switch (this_elen) {
- case 0: /* path => '/' */
- break;
- case 1: /* path => '?/' */
- if (*element != '.') {
- if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, leave_this) == -1) {
- retval = -1;
- }
- }
- break;
- default:
- if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, leave_this) == -1) {
+ /* for an empty path, or a "/.", we skip the append, but not for regular
+ * files; regular files get it appended so they can fail properly
+ * later for being invalid paths.
+ */
+ if (is_reg || (this_elen > 1) || ((this_elen == 1) && (*element != '.'))) {
+ if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, &buf, leave_this) == -1) {
retval = -1;
+ break;
}
- break;
+ /* 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;
+ }
+ /* having grabbed a stat for the path, we append a slash so we can append to it again
+ * if needed.
+ */
+ *(*current)++ = '/';
+ *(*current) = '\0';
}
/* and now move past the separator */
element += this_elen + 1;