aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.txt6
-rw-r--r--pseudo.c28
-rw-r--r--pseudo_util.c7
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.
diff --git a/pseudo.c b/pseudo.c
index 2b33b05..5eae180 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -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, &current, 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",