Skip to content

Commit 12b466e

Browse files
paliSteve French
authored andcommitted
cifs: Fix creating and resolving absolute NT-style symlinks
If the SMB symlink is stored on NT server in absolute form then it points to the NT object hierarchy, which is different from POSIX one and needs some conversion / mapping. To make interoperability with Windows SMB server and WSL subsystem, reuse its logic of mapping between NT paths and POSIX paths into Linux SMB client. WSL subsystem on Windows uses for -t drvfs mount option -o symlinkroot= which specifies the POSIX path where are expected to be mounted lowercase Windows drive letters (without colon). Do same for Linux SMB client and add a new mount option -o symlinkroot= which mimics the drvfs mount option of the same name. It specifies where in the Linux VFS hierarchy is the root of the DOS / Windows drive letters, and translates between absolute NT-style symlinks and absolute Linux VFS symlinks. Default value of symlinkroot is "/mnt", same what is using WSL. Note that DOS / Windows drive letter symlinks are just subset of all possible NT-style symlinks. Drive letters live in NT subtree \??\ and important details about NT paths and object hierarchy are in the comments in this change. When symlink target location from non-POSIX SMB server is in absolute form (indicated by absence of SYMLINK_FLAG_RELATIVE) then it is converted to Linux absolute symlink according to symlinkroot configuration. And when creating a new symlink on non-POSIX SMB server in absolute form then Linux absolute target is converted to NT-style according to symlinkroot configuration. When SMB server is POSIX, then this change does not affect neither reading target location of symlink, nor creating a new symlink. It is expected that POSIX SMB server works with POSIX paths where the absolute root is /. This change improves interoperability of absolute SMB symlinks with Windows SMB servers. Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 32ba030 commit 12b466e

File tree

3 files changed

+273
-18
lines changed

3 files changed

+273
-18
lines changed

fs/smb/client/fs_context.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
185185
fsparam_string("cache", Opt_cache),
186186
fsparam_string("reparse", Opt_reparse),
187187
fsparam_string("upcall_target", Opt_upcalltarget),
188+
fsparam_string("symlinkroot", Opt_symlinkroot),
188189

189190
/* Arguments that should be ignored */
190191
fsparam_flag("guest", Opt_ignore),
@@ -386,6 +387,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
386387
new_ctx->iocharset = NULL;
387388
new_ctx->leaf_fullpath = NULL;
388389
new_ctx->dns_dom = NULL;
390+
new_ctx->symlinkroot = NULL;
389391
/*
390392
* Make sure to stay in sync with smb3_cleanup_fs_context_contents()
391393
*/
@@ -401,6 +403,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
401403
DUP_CTX_STR(iocharset);
402404
DUP_CTX_STR(leaf_fullpath);
403405
DUP_CTX_STR(dns_dom);
406+
DUP_CTX_STR(symlinkroot);
404407

405408
return 0;
406409
}
@@ -1727,6 +1730,16 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
17271730
if (parse_reparse_flavor(fc, param->string, ctx))
17281731
goto cifs_parse_mount_err;
17291732
break;
1733+
case Opt_symlinkroot:
1734+
if (param->string[0] != '/') {
1735+
cifs_errorf(fc, "symlinkroot mount options must be absolute path\n");
1736+
goto cifs_parse_mount_err;
1737+
}
1738+
kfree(ctx->symlinkroot);
1739+
ctx->symlinkroot = kstrdup(param->string, GFP_KERNEL);
1740+
if (!ctx->symlinkroot)
1741+
goto cifs_parse_mount_err;
1742+
break;
17301743
}
17311744
/* case Opt_ignore: - is ignored as expected ... */
17321745

@@ -1735,6 +1748,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
17351748
goto cifs_parse_mount_err;
17361749
}
17371750

1751+
/*
1752+
* By default resolve all native absolute symlinks relative to "/mnt/".
1753+
* Same default has drvfs driver running in WSL for resolving SMB shares.
1754+
*/
1755+
if (!ctx->symlinkroot)
1756+
ctx->symlinkroot = kstrdup("/mnt/", GFP_KERNEL);
1757+
17381758
return 0;
17391759

17401760
cifs_parse_mount_err:
@@ -1867,6 +1887,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
18671887
ctx->leaf_fullpath = NULL;
18681888
kfree(ctx->dns_dom);
18691889
ctx->dns_dom = NULL;
1890+
kfree(ctx->symlinkroot);
1891+
ctx->symlinkroot = NULL;
18701892
}
18711893

18721894
void

fs/smb/client/fs_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ enum cifs_param {
166166
Opt_cache,
167167
Opt_reparse,
168168
Opt_upcalltarget,
169+
Opt_symlinkroot,
169170

170171
/* Mount options to be ignored */
171172
Opt_ignore,
@@ -296,6 +297,7 @@ struct smb3_fs_context {
296297
enum cifs_reparse_type reparse_type;
297298
bool dfs_conn:1; /* set for dfs mounts */
298299
char *dns_dom;
300+
char *symlinkroot; /* top level directory for native SMB symlinks in absolute format */
299301
};
300302

301303
extern const struct fs_parameter_spec smb3_fs_parameters[];

0 commit comments

Comments
 (0)