diff options
author | Enzo Matsumiya <ematsumiya@suse.de> | 2024-10-10 18:10:15 -0300 |
---|---|---|
committer | Enzo Matsumiya <ematsumiya@suse.de> | 2024-10-10 18:10:15 -0300 |
commit | fb828e2a72ccdc28a63b9f3a3a7203b217e6feaa (patch) | |
tree | b7c92e6a11ee327ec76fe68be68502e56c132e41 | |
parent | 780fc3252e08af50e1c80138c1dd7c365fce6b90 (diff) | |
download | linux-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.c | 132 |
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, |