Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions sshfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3984,6 +3984,7 @@ static char *tokenize_on_space(char *str)
{
static char *pos = NULL;
char *start = NULL;
char *end = NULL;

if (str)
pos = str;
Expand All @@ -3996,22 +3997,27 @@ static char *tokenize_on_space(char *str)
pos++;

start = pos;
end = pos;

while (*pos != '\0') {
// break on space, but not on '\ '
if (*pos == ' ' && *(pos - 1) != '\\') {
break;
if (*pos == ' ') {
if (*(pos - 1) == '\\') {
end--;
} else {
break;
}
}
pos++;
*end++ = *pos++;
}

if (*pos == '\0') {
pos = NULL;
}
else {
*pos = '\0';
pos++;
}
*end = '\0';

return start;
}
Expand Down
30 changes: 30 additions & 0 deletions test/test_sshfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1080,3 +1080,33 @@ def test_contain_symlinks_option_precedence(tmpdir, capfd) -> None:
with pytest.raises(OSError) as exc_info:
os.readlink(pjoin(mnt_dir, "abs"))
assert exc_info.value.errno == errno.EPERM


def test_backslash_escape(tmpdir, capfd):
"""Regression test for parsing backslash escape sequences in options"""

cases = [
(r"one two three", "<one> <two> <three>"),
(r"one\\ two three", "<one two> <three>"),
# fuse_opt_parse interprets "\ " as " ", so these are still separate
(r"one two\ three", "<one> <two> <three>"),
]

mnt_dir = str(tmpdir.mkdir("mnt"))
# we can skip cleanup since the mount always fails

for line, args in cases:
cmdline = base_cmdline + [
pjoin(basename, "sshfs"),
"-f",
"localhost:foo",
mnt_dir,
"-o",
"sshfs_debug",
"-o",
"ssh_command=/dev/null " + line,
]
assert subprocess.run(cmdline).returncode != 0

captured = capfd.readouterr()
assert "executing </dev/null> " + args in captured.err