From: Fredric Cover <fredric.cover.lkernel@gmail.com>
To: Paulo Alcantara <pc@manguebit.org>, Namjae Jeon <linkinjeon@kernel.org>
Cc: ChenXiaoSong <chenxiaosong@chenxiaosong.com>,
Tom Talpey <tom@talpey.com>,
Ronnie Sahlberg <ronniesahlberg@gmail.com>,
Shyam Prasad N <sprasad@microsoft.com>,
Bharath SM <bharathsm@microsoft.com>,
linux-cifs@vger.kernel.org, linux-kernel@vger.kernel.org,
Fredric Cover <fredric.cover.lkernel@gmail.com>
Subject: [PATCH] smb: client: convert path_consumed to mount charset units
Date: Thu, 12 Nov 2026 04:21:36 +0000 [thread overview]
Message-ID: <20261112042137.1642509-1-fredric.cover.lkernel@gmail.com> (raw)
path_consumed in a DFS referral counts bytes of the canonical path,
which is in the DFS cache charset (utf8). dfs_parse_target_referral()
applies it as an offset into full_path, which is in the mount charset.
When the mount charset is not utf8, each non-ASCII character in the
consumed part that encodes longer in utf8 than in the mount charset
makes the offset too large, so leading characters of the prefix path
are dropped. For example, with iocharset=iso8859-1, mounting
//srv/dfs/café é/sub yields the prefix "ub" instead of "sub" and the
mount fails with -ENOENT. (A single accented character only worked
because the offset happened to land after the delimiter.)
Translate the count in parse_dfs_target(), where both strings are
available, by matching path components: count the delimiters in the
consumed part of the canonical path and find the same position in the
mount charset path. Delimiters are ASCII in both and are not affected
by remapping. If the consumed bytes are identical in both strings,
which is the case for utf8 mounts and ASCII-only prefixes using the
default backslash delimiter, the value is used unchanged.
This assumes the server reports PathConsumed on a component boundary;
a translated value that ends mid-component is rounded up to the next
boundary.
Tested against Samba on a KASAN kernel: with iocharset=iso8859-1, links
with 0 to 3 accented characters followed by a tail, a two-level tail,
an accented tail, a link in a subdirectory, and mapposix with an
accented name; iso8859-15 and cp932 mounts; and iocharset=utf8
controls. Not tested against Windows servers, with posixpaths, or with
reserved characters under mapchars/mapposix.
Signed-off-by: Fredric Cover <fredric.cover.lkernel@gmail.com>
---
Hi Paulo,
This is the follow-up you asked for in your reply to the earlier series.
Based on cifs-next at 25912ed3b75d.
Separately, reconnecting a mount whose prefix path contains non-ASCII
characters still fails with a non-utf8 charset; I'm looking at that
and will send it as its own patch.
---
fs/smb/client/dfs.c | 70 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 68 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
index 57c90a2512a3..cbc2ea6a190b 100644
--- a/fs/smb/client/dfs.c
+++ b/fs/smb/client/dfs.c
@@ -85,16 +85,82 @@ static void set_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
ctx->dfs_root_ses = ses;
}
+static bool dfs_is_delim(wchar_t c)
+{
+ return c == '/' || c == '\\';
+}
+
+/*
+ * @consumed counts bytes of the canonical (cache charset) path @cpath.
+ * Return the matching byte offset in @fpath, which holds the same path in
+ * the mount charset. Offsets are matched by path component, since the
+ * delimiters are ASCII in every charset and survive remapping.
+ */
+static int dfs_consumed_to_local(const struct nls_table *nls,
+ const char *cpath, const char *fpath,
+ int consumed)
+{
+ size_t clen = strlen(cpath), flen = strlen(fpath), off = 0;
+ int ndelims = 0, seen = 0, i;
+ bool ends_on_delim;
+
+ if (consumed < 0)
+ return -EINVAL;
+
+ /* Identical bytes in both strings: units already agree */
+ if ((size_t)consumed <= min(clen, flen) && !memcmp(cpath, fpath, consumed))
+ return consumed;
+
+ if ((size_t)consumed > clen)
+ consumed = clen;
+
+ for (i = 0; i < consumed; i++)
+ if (dfs_is_delim(cpath[i]))
+ ndelims++;
+ ends_on_delim = consumed && dfs_is_delim(cpath[consumed - 1]);
+
+ while (off < flen) {
+ wchar_t uni;
+ int len = nls->char2uni((const unsigned char *)fpath + off,
+ flen - off, &uni);
+
+ if (len <= 0) {
+ uni = (unsigned char)fpath[off];
+ len = 1;
+ }
+ if (dfs_is_delim(uni)) {
+ if (!ends_on_delim && seen == ndelims)
+ break; /* start of next component */
+ seen++;
+ if (ends_on_delim && seen == ndelims) {
+ off += len; /* just past the last consumed delimiter */
+ break;
+ }
+ }
+ off += len;
+ }
+ return off;
+}
+
static inline int parse_dfs_target(struct smb3_fs_context *ctx,
struct dfs_ref_walk *rw,
struct dfs_info3_param *tgt)
{
int rc;
const char *fpath = ref_walk_fpath(rw) + 1;
+ const char *cpath = ref_walk_path(rw) + 1;
rc = ref_walk_get_tgt(rw, tgt);
- if (!rc)
- rc = dfs_parse_target_referral(fpath, tgt, ctx);
+ if (!rc) {
+ struct dfs_info3_param local = *tgt;
+ int off = dfs_consumed_to_local(rw->mnt_ctx->cifs_sb->local_nls,
+ cpath, fpath, tgt->path_consumed);
+
+ if (off < 0)
+ return off;
+ local.path_consumed = off;
+ rc = dfs_parse_target_referral(fpath, &local, ctx);
+ }
return rc;
}
base-commit: 25912ed3b75dcf5ff623dcb549b925dd0d0948f7
--
2.53.0
reply other threads:[~2026-11-12 4:21 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112042137.1642509-1-fredric.cover.lkernel@gmail.com \
--to=fredric.cover.lkernel@gmail.com \
--cc=bharathsm@microsoft.com \
--cc=chenxiaosong@chenxiaosong.com \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pc@manguebit.org \
--cc=ronniesahlberg@gmail.com \
--cc=sprasad@microsoft.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®