[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20250621165246.5yvdolrrdchrbe22@pali>
Date: Sat, 21 Jun 2025 18:52:46 +0200
From: Pali Rohár <pali@...nel.org>
To: Paulo Alcantara <pc@...guebit.org>
Cc: Steve French <sfrench@...ba.org>, Remy Monsen <monsen@...sen.cc>,
linux-cifs@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] cifs: Fix lstat() and AT_SYMLINK_NOFOLLOW to work on
broken symlink nodes
On Saturday 21 June 2025 12:38:34 Paulo Alcantara wrote:
> Pali Rohár <pali@...nel.org> writes:
>
> > On Friday 20 June 2025 20:44:37 Paulo Alcantara wrote:
> >> Pali Rohár <pali@...nel.org> writes:
> >>
> >> > Currently Linux SMB client returns EIO for lstat() and AT_SYMLINK_NOFOLLOW
> >> > calls on symlink node when the symlink target location is broken or cannot
> >> > be read or parsed.
> >> >
> >> > Fix this problem by relaxing the errors from various locations which parses
> >> > information about symlink file node (UNIX SMB1, native SMB2+, NFS-style,
> >> > WSL-style) and let readlink() syscall to return EIO when the symlink target
> >> > location is not available.
> >>
> >> Please, don't. We still want those validations for the other types of
> >> symlinks.
> >
> > Well, validation was not removed. Validation is still there, just the
> > error is signalled by the readlink() syscall instead of the lstat() or
> > AT_SYMLINK_NOFOLLOW syscalls.
> >
> > My opinion is that the lstat() or AT_SYMLINK_NOFOLLOW should work on
> > symlink node independently of where the symlink points (and whether the
> > symlink target is valid POSIX path or not). That is because the lstat()
> > and AT_SYMLINK_NOFOLLOW says that the symlink target location must not
> > be used and must not be resolved.
> >
> > But still the invalid / incorrect / broken or non-representable symlink
> > target path in POSIX notation should be reported as an issue and the
> > readlink() is the correct syscall which should report these errors.
>
> The only issue is breaking existing customer or user applications that
> really don't care if cifs.ko could follow those kind of symlinks.
>
> Samba create symlinks to represent DFS links with targets like
> 'msdfs:srv1\share,srv2\share', which are not valid POSIX paths. Does
> that mean the filesystem should not allow readlink(2) to succeed just
> because it is not a valid POSIX path? Is that what you mean?
But this is something totally different thing.
Here you are referring to the behavior of Samba server, which interprets
symlink node stored on local filesystem named e.g. "link1" pointing to
target relative file name 'msdfs:srv1\share,srv2\share' specially.
Calling "ln -s 'msdfs:srv1\share,srv2\share' link1" is perfectly fine on
the ext4 filesystem. It creates a relative symlink to the specified
file.
And if you call "echo test > 'msdfs:srv1\share,srv2\share'" then it would
world correctly and "cat link1" will print "test".
The 'msdfs:srv1\share,srv2\share' is a valid POSIX path and it is stored
on the local Linux filesystem. So I do not see anything wrong with it or
reason why local filesystem should disallow creating such symlink or why
would realink() should fail on such node.
That example has nothing with symlinks stored on NTFS-compatible
filesystems which has ability to store symlinks pointing to non-POSIX
NT object model paths.
Here the issue is with symlink target locations which are coming from
the remote NT server and are pointing to location which cannot be
directly represented by the Linux system. The translation needs to be
done in both directions and reversible. Otherwise moving the file or
symlink from cifs to ext4 and back would damage the file or symlink.
> >> The problem is just that cifs.ko can't handle absolute
> >> symlink targets in the form of '\??\UNC\srv\share\foo', while Windows
> >> client can. They are still valid symlink targets, but cifs.ko doesn't
> >> know how to follow them.
> >
> > Windows client can represent and follow such symlink because the symlink
> > is in the NT style format and Windows kernel uses NT style of paths
> > internally. Linux kernel uses POSIX paths and POSIX does not contain any
> > GLOBAL?? namespace for NT object hierarchy.
> >
> > Leaking raw NT object hierarchy from SMB to POSIX userspace via
> > readlink() syscall is a bad idea. Applications are really not expecting
> > that the readlink() syscall will return NT kernel internals (exported
> > over SMB protocol and passed to cifs.ko).
> >
> > For UNC paths encoded in NT object hierarchy, which is just some subset
> > of all possible NT paths, I had an idea that we could convert these
> > paths to some format like:
> >
> > <prefix>/server/share/path...
> >
> > Where <prefix> would be specified by the string mount option. So user
> > could say that wants all UNC symlinks pointing to /mnt/unc/.
> >
> > And in the same way if user would want to create symlink pointing to
> > /mnt/unc/server/share/path... then cifs.ko will transform it into valid
> > NT UNC path and create a symlink to this location.
>
> That's really a terrible idea. The symlink targets in the form of
> '\??\UNC\...' could be resolved by cifs.ko. The ones that refer to a
> file outside the mounted share, we would set those as automounts.
I agree that above is not the best idea, but I wrote at least something
as an idea as I do not know how it could be solved in better way.
And I do not see how it could be resolved by cifs.ko somehow
automatically. I'm not sure to which you refer how it can be resolved by
cifs.ko. I understood you message as it could automount another share
and do the whole path symlink resolving in cifs.ko.
And I think that this is even worse idea than mine. Because that
automount means that symlinks pointing outside of the share would start
behaving like a mount point. Such thing can cause even a security issues
if not used carefully.
But moreover there is a big difference between symlink and mount point.
Symlinks are not resolved by filesystem itself (but rather by the VFS,
to ensure that all access checks are applied) and also moving the
symlink between filesystems should not break them. In this idea when the
symlink is going to be moved from smb share to e.g. ext4 local fs, then
it would stops working (if the path resolved is in the cifs.ko) as
ext4.ko would not be able to process special cifs.ko symlinks.
> > But this would solve only problem with UNC symlink, not symlinks
> > pointing to NT object hierarchy in general.
> >
> >> The following should do it and then restore old behavior
> >>
> >> diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> >> index bb25e77c5540..11d44288e75a 100644
> >> --- a/fs/smb/client/reparse.c
> >> +++ b/fs/smb/client/reparse.c
> >> @@ -875,15 +875,8 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
> >> abs_path += sizeof("\\DosDevices\\")-1;
> >> else if (strstarts(abs_path, "\\GLOBAL??\\"))
> >> abs_path += sizeof("\\GLOBAL??\\")-1;
> >> - else {
> >> - /* Unhandled absolute symlink, points outside of DOS/Win32 */
> >> - cifs_dbg(VFS,
> >> - "absolute symlink '%s' cannot be converted from NT format "
> >> - "because points to unknown target\n",
> >> - smb_target);
> >> - rc = -EIO;
> >> - goto out;
> >> - }
> >> + else
> >> + goto out_unhandled_target;
> >>
> >> /* Sometimes path separator after \?? is double backslash */
> >> if (abs_path[0] == '\\')
> >> @@ -910,13 +903,7 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
> >> abs_path++;
> >> abs_path[0] = drive_letter;
> >> } else {
> >> - /* Unhandled absolute symlink. Report an error. */
> >> - cifs_dbg(VFS,
> >> - "absolute symlink '%s' cannot be converted from NT format "
> >> - "because points to unknown target\n",
> >> - smb_target);
> >> - rc = -EIO;
> >> - goto out;
> >> + goto out_unhandled_target;
> >> }
> >>
> >> abs_path_len = strlen(abs_path)+1;
> >> @@ -966,6 +953,7 @@ int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
> >> * These paths have same format as Linux symlinks, so no
> >> * conversion is needed.
> >> */
> >> +out_unhandled_target:
> >> linux_target = smb_target;
> >> smb_target = NULL;
> >> }
> >
> > I'm really not sure if removing the messages and error reporting about
> > symlinks which cannot be represented in POSIX system is a good idea.
>
> Those messages are just useless and noisy. Do you think it's useful
> printing that message for _every_ symlink when someone is calling
> readdir(2) in a directory that contain such files?
I though that for any debugging purposes these messages are useful.
Now I see that VFS level is printed always, so maybe the FYI level could
be better. Or do you really think that it is useless even for debugging?
Powered by blists - more mailing lists