summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2024-10-10 18:10:15 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2024-10-10 18:10:15 -0300
commitfb828e2a72ccdc28a63b9f3a3a7203b217e6feaa (patch)
treeb7c92e6a11ee327ec76fe68be68502e56c132e41
parent780fc3252e08af50e1c80138c1dd7c365fce6b90 (diff)
downloadlinux-fix-paths-case.tar.gz
linux-fix-paths-case.tar.bz2
linux-fix-paths-case.zip
(WIP) smb: client: attemp to fix case-sensitivity issues with DFS pathsfix-paths-case
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/namespace.c132
1 files changed, 130 insertions, 2 deletions
diff --git a/fs/smb/client/namespace.c b/fs/smb/client/namespace.c
index 9bfc0a592d27..f0534c7ff5c4 100644
--- a/fs/smb/client/namespace.c
+++ b/fs/smb/client/namespace.c
@@ -183,10 +183,115 @@ static void fs_context_set_ids(struct smb3_fs_context *ctx)
ctx->cred_uid = uid;
}
+#include "../../mount.h"
+
+static inline char *get_d_fullpath(struct dentry *d)
+{
+ struct dentry *cur;
+ struct list_head ld = LIST_HEAD_INIT(ld);
+ struct {
+ struct list_head l;
+ struct dentry *e;
+ } dl[32] = { 0 }, *dlp;
+ char *path;
+ size_t len = 0, n = 0;
+
+ cur = d;
+ while (cur) {
+ if (!strncmp(cur->d_name.name, "/", 1))
+ break;
+
+ dl[n].e = cur;
+ INIT_LIST_HEAD(&dl[n].l);
+ list_add(&dl[n].l, &ld);
+
+ n++;
+ len += cur->d_name.len;
+ cur = cur->d_parent;
+ }
+
+ if (len == 0 || n == 0)
+ return kstrdup("/", GFP_KERNEL);
+
+ path = kzalloc(len + n + 1, GFP_KERNEL);
+ if (!path)
+ return NULL;
+
+ /* reuse @n */
+ n = 0;
+
+ list_for_each_entry(dlp, &ld, l) {
+ len = dlp->e->d_name.len;
+ path[n++] = '/';
+
+ memcpy(&path[n], dlp->e->d_name.name, len);
+ n += len;
+ }
+
+ return path;
+}
+
+static inline struct vfsmount *find_existing_mnt(struct dentry *new, const char *src)
+{
+ struct super_block *sb = new->d_sb;
+ struct mount *m, *subm;
+ char *newp = get_d_fullpath(new);
+ //int p = 0;
+
+ //pr_err("%s: finding existing mount for (%s @ %s)\n", __func__, src, newp);
+
+ m = list_first_entry_or_null(&sb->s_mounts, struct mount, mnt_instance);
+ if (!m || list_empty(&m->mnt_mounts))
+ return NULL;
+
+#if 0
+ mp = get_d_fullpath(m->mnt_mountpoint);
+ pr_err("%s: mount #%d (%p): mpt=%s (raw=%pd), dev=%s\n",
+ __func__, p, m, mp, m->mnt_mountpoint, m->mnt_devname);
+ kfree(mp);
+#endif
+
+ list_for_each_entry(subm, &m->mnt_mounts, mnt_child) {
+ char *oldp = get_d_fullpath(subm->mnt_mountpoint);
+ int len = strlen(oldp);
+
+ len = umin(len, strlen(newp));
+ //pr_err("%s: submount #%d (%p): mpt=%s (raw=%pd), dev=%s, matches=%d (oldp=%s == newp=%s)\n",
+ // __func__, p, subm, oldp, subm->mnt_mountpoint, subm->mnt_devname, !strncasecmp(oldp, newp, len), oldp, newp);
+ //p++;
+
+ if (!strncasecmp(oldp, newp, len)) {
+ len = strlen(src);
+ len = umin(len, strlen(subm->mnt_devname));
+
+ if (!strncasecmp(src, subm->mnt_devname, len)) {
+ pr_err("%s: Found existing VFS mount for %s @ %p\n", __func__, newp, &m->mnt);
+ kfree(oldp);
+ kfree(newp);
+
+ return &m->mnt;
+ }
+ }
+
+#if 0
+ pr_err("%s: submount #%d (%p): mpt=%s, dev=%s, matches=%d\n",
+ __func__, c, subm, oldp, subm->mnt_devname, !strncasecmp(oldp, newp, len));
+
+ //pr_err("%s: submount #%d (%p): mpt=%s, dev=%s, vfs=%p, sb=%p, has_children=%d, id=%d, parent=%p, matches=%d\n",
+ // __func__, c, subm, oldp, subm->mnt_devname, &subm->mnt, subm->mnt.mnt_sb, !list_empty(&subm->mnt_mounts), subm->mnt_id, subm->mnt_parent, !strncasecmp(oldp, newp, len));
+#endif
+ kfree(oldp);
+ }
+
+ kfree(newp);
+
+ return NULL;
+}
+
/*
* Create a vfsmount that we can automount
*/
-static struct vfsmount *cifs_do_automount(struct path *path)
+static struct vfsmount *cifs_do_automount(struct path *path, bool *exist)
{
int rc;
struct dentry *mntpt = path->dentry;
@@ -245,7 +350,26 @@ static struct vfsmount *cifs_do_automount(struct path *path)
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n",
__func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_conn);
+ //(void)find_existing_mnt(mntpt->d_sb->s_root, mntpt);
+ mnt = find_existing_mnt(mntpt, ctx->source);
+ if (mnt) {
+ pr_err("%s: mnt d flags %d, path d flags %d\n", __func__, !!(mnt->mnt_root->d_flags & DCACHE_NEED_AUTOMOUNT), !!(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT));
+ *exist = true;
+ //mntget(mnt);
+ goto out;
+ }
+
+ if (!fc->source) {
+ fc->source = kstrndup(ctx->source, strlen(ctx->source), GFP_KERNEL);
+ if (!fc->source) {
+ mnt = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ }
+
mnt = fc_mount(fc);
+ //if (!IS_ERR(mnt))
+ //pr_err("%s: adding mount dev %s\n", __func__, container_of(mnt, struct mount, mnt)->mnt_devname);
out:
put_fs_context(fc);
free_dentry_path(page);
@@ -258,15 +382,19 @@ out:
struct vfsmount *cifs_d_automount(struct path *path)
{
struct vfsmount *newmnt;
+ bool exist = false;
cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry);
- newmnt = cifs_do_automount(path);
+ newmnt = cifs_do_automount(path, &exist);
if (IS_ERR(newmnt)) {
cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__);
return newmnt;
}
+ if (exist)
+ return NULL;
+
mntget(newmnt); /* prevent immediate expiration */
mnt_set_expiry(newmnt, &cifs_automount_list);
schedule_delayed_work(&cifs_automount_task,