diff options
-rw-r--r-- | ChangeLog.txt | 6 | ||||
-rw-r--r-- | pseudo.c | 28 | ||||
-rw-r--r-- | pseudo_util.c | 7 |
3 files changed, 39 insertions, 2 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt index 90b38f6..ba44239 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,9 @@ +2015-01-05: + * (seebs) First try at handling trailing slashes in path names + correctly. (Which is to say, a trailing slash on a file name + which is not a directory name should yield ENOTDIR from a lot + of things.) + 2014-10-03: * (seebs) in fact, suppress a lot of sanity checks entirely for did_unlink. @@ -497,6 +497,7 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **respon int found_path = 0, found_ino = 0; int prefer_ino = 0; int xattr_flags = 0; + int trailing_slash = 0, old_trailing_slash = 0; if (!msg) return 1; @@ -556,10 +557,30 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **respon msg_header = *msg; by_ino = msg_header; + /* trailing slashes are kept in paths because they affect + * path resolution, but we don't want them in the database + * because they're optional. For now, any error-checking on + * this server-side is purely advisory, but the client should + * bail with ENOTDIR a lot earlier in many cases, before the + * server even sees anything. + */ + if (msg->pathlen) { + if (msg->path[msg->pathlen - 1] == '/') { + msg->path[--msg->pathlen] = '\0'; + trailing_slash = 1; + } + } + + if (oldpathlen > 0) { + if (oldpath[oldpathlen - 1] == '/') { + oldpath[--oldpathlen] = '\0'; + old_trailing_slash = 1; + } + } + /* There should usually be a path. Even for f* ops, the client * tries to provide a path from its table of known fd paths. */ - /* Lookup the full path, with inode and dev if available */ if (msg->pathlen && msg->dev && msg->ino) { if (!pdb_find_file_exact(msg, &row)) { @@ -661,6 +682,11 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **respon pdb_unlink_file_dev(&by_path); found_path = 0; } + if (!!S_ISDIR(by_path.mode) != trailing_slash) { + pseudo_diag("dir quasi-mismatch: '%s' [%llu] db mode 0%o, incoming path had trailing slash. Not unlinking.\n", + msg->path, (unsigned long long) by_path.ino, + (int) by_path.mode); + } } /* for OP_DID_UNLINK, the reason this op exists is that the same diff --git a/pseudo_util.c b/pseudo_util.c index e4e1fc8..9ff2587 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -624,12 +624,17 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel char *newpath; char *current; char *effective_root; + int trailing_slash = 0; if (!path) { pseudo_diag("can't fix empty path.\n"); return 0; } pathlen = strlen(path); + /* a trailing slash has special meaning */ + if (pathlen > 0 && path[pathlen - 1] == '/') { + trailing_slash = 1; + } newpathlen = pathlen; /* If the path starts with /, we don't care about base, UNLESS * rootlen is non-zero, in which case we're doing a chroot thing @@ -667,7 +672,7 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel */ if (pseudo_append_elements(&newpath, &effective_root, &newpathlen, ¤t, path, pathlen, leave_last) != -1) { --current; - if (*current == '/' && current > effective_root) { + if (*current == '/' && current > effective_root && !trailing_slash) { *current = '\0'; } pseudo_debug(PDBGF_PATH, "%s + %s => <%s>\n", |