diff mbox series

[EXPERIMENT] new mount API verbose errors from userspace

Message ID 87tupuya6t.fsf@suse.com
State New
Headers show
Series [EXPERIMENT] new mount API verbose errors from userspace | expand

Commit Message

Aurélien Aptel March 1, 2021, 6:56 p.m. UTC
Hi,

I have some code to use the new mount API from user space.

The kernel changes are just making the code use the fs_context logging
feature.

The sample userspace prog (fsopen.c) is just a PoC showing how mounting
is done and how the mount errors are read.

If you change the prog to use a wrong version for example (vers=4.0) you
get this:

    $ gcc -o fsopen fsopen.c
    $ ./fsopen
    fsconfig(sfd, FSCONFIG_SET_STRING, "vers", "4.0", 0): Invalid argument
    kernel mount errors:
    e Unknown vers= option specified: 4.0

There are some cons to using this API, mainly that mount.cifs will have
to encode more knowledge and processing of the user-provided option
before passing it to the kernel.

Where previously we would pass most options as-is to the kernel, making
it easier to add new ones without patching mount.cifs; we know have to
know if the option is a key=string, or key=int, or boolean (key/nokey)
and call the appropriate FSCONFIG_SET_{STRING,FLAG,PATH,...}.

The pros are that we process one option at a time and we can fail early
with verbose, helpful messages to the user.
Cheers,

Comments

Aurélien Aptel March 1, 2021, 8:57 p.m. UTC | #1
Oops, sent the wrong set of patches. Resending right one.
ronnie sahlberg March 2, 2021, 9:59 p.m. UTC | #2
On Tue, Mar 2, 2021 at 5:03 AM Aurélien Aptel <aaptel@suse.com> wrote:
>
> Hi,
>
> I have some code to use the new mount API from user space.
>
> The kernel changes are just making the code use the fs_context logging
> feature.
>
> The sample userspace prog (fsopen.c) is just a PoC showing how mounting
> is done and how the mount errors are read.
>
> If you change the prog to use a wrong version for example (vers=4.0) you
> get this:
>
>     $ gcc -o fsopen fsopen.c
>     $ ./fsopen
>     fsconfig(sfd, FSCONFIG_SET_STRING, "vers", "4.0", 0): Invalid argument
>     kernel mount errors:
>     e Unknown vers= option specified: 4.0
>
> There are some cons to using this API, mainly that mount.cifs will have
> to encode more knowledge and processing of the user-provided option
> before passing it to the kernel.
>
> Where previously we would pass most options as-is to the kernel, making
> it easier to add new ones without patching mount.cifs; we know have to
> know if the option is a key=string, or key=int, or boolean (key/nokey)
> and call the appropriate FSCONFIG_SET_{STRING,FLAG,PATH,...}.
>
> The pros are that we process one option at a time and we can fail early
> with verbose, helpful messages to the user.

I like this, this is very nice.
But, as you touch upon, it requires we know in mount.c what type each
argument is.
Which is problematic because the list of mount arguments in cifs.ko
has a fair amount of crunch
and I think it would be unworkable to keep cifs-utils and cifs.ko in
lockstep for every release where
we modify a mount argument.

What I think we should have is a ioctl(),  system-call,
/proc/fs/cifs/options,  where we can query the kernel/file-system
module
for "give me a list of all recognized mount options and their type"
i.e. basically a way to fetch the "struct fs_parameter_spec" to userspace.

This is probably something that would not be specific to cifs, but
would apply to all filesystem modules.


regards
ronnie sahlberg


>
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
Aurélien Aptel March 3, 2021, 11:18 a.m. UTC | #3
ronnie sahlberg <ronniesahlberg@gmail.com> writes:
> I like this, this is very nice.
> But, as you touch upon, it requires we know in mount.c what type each
> argument is.
> Which is problematic because the list of mount arguments in cifs.ko
> has a fair amount of crunch
> and I think it would be unworkable to keep cifs-utils and cifs.ko in
> lockstep for every release where
> we modify a mount argument.

We could do some basic pattern matching: like if has no '=', assume
boolean flag. If value only made of number, assume int. String for
everything else.

But this might fail on numbers which are actually string
(e.g. password=123456).

> What I think we should have is a ioctl(),  system-call,
> /proc/fs/cifs/options,  where we can query the kernel/file-system
> module
> for "give me a list of all recognized mount options and their type"
> i.e. basically a way to fetch the "struct fs_parameter_spec" to userspace.
>
> This is probably something that would not be specific to cifs, but
> would apply to all filesystem modules.

That sounds like a good idea yes. I wonder if David Howells has
considered this.

We can already sort-of do this actually. We can have a special boolean
option "help" which on the kernel side would log the list of
options&types in the fs_context log (as "i" (info) messages).

Cheers,
David Howells March 9, 2021, 5:37 p.m. UTC | #4
Aurélien Aptel <aaptel@suse.com> wrote:

> > What I think we should have is a ioctl(),  system-call,
> > /proc/fs/cifs/options,  where we can query the kernel/file-system
> > module
> > for "give me a list of all recognized mount options and their type"
> > i.e. basically a way to fetch the "struct fs_parameter_spec" to userspace.
> >
> > This is probably something that would not be specific to cifs, but
> > would apply to all filesystem modules.
> 
> That sounds like a good idea yes. I wonder if David Howells has
> considered this.

I had this.  Al disliked it and removed it before the patches got upstream.
Also Linus hated the fsinfo() syscall that was the way to get this (amongst
other things).

David
Aurélien Aptel March 9, 2021, 6:29 p.m. UTC | #5
David Howells <dhowells@redhat.com> writes:
> I had this.  Al disliked it and removed it before the patches got upstream.
> Also Linus hated the fsinfo() syscall that was the way to get this (amongst
> other things).

I see, that's too bad :/ thanks for trying anyway.

Cheers,
Aurélien Aptel March 18, 2021, 1:10 p.m. UTC | #6
Since there's no standard VFS way to get the supported mount options
from userspace, I thought I would do what Ronnie suggested and export
them from a cifs /proc file.
That's the only change since v1, in the 4th patch.

David, maybe this can give your arguments for the need for fsinfo() if
we end up using this in cifs-utils.

I have added some dumb code in userspace to parse it and see if the
option exists and what type it is. This removes the requirement of
having to keep cifs-utils and kernel updated at the same time to use new
options.

Previous intro
=======================================================================
I have some code to use the new mount API from user space.

The kernel changes are just making the code use the fs_context logging
feature.

The sample userspace prog (fsopen.c attached) is just a PoC showing how
mounting is done and how the mount errors are read.

If you change the prog to use a wrong version for example (vers=4.0) you
get this:

    $ gcc -o fsopen fsopen.c
    $ ./fsopen
    fsconfig(sfd, FSCONFIG_SET_STRING, "vers", "4.0", 0): Invalid argument
    kernel mount errors:
    e Unknown vers= option specified: 4.0

The pros are that we process one option at a time and we can fail early
with verbose, helpful messages to the user.
=======================================================================
Cheers,
Steve French April 9, 2021, 4:36 a.m. UTC | #7
Tentatively merged into cifs-2.6.git for-next but would like more
feedback on other's thoughts on this. Getting more verbose error
information back on mount errors (to userspace returning something
more than a primitive small set of return codes, and a message logged
to dmesg) is critical, and this approach seems reasonable at first
glance but if there are better ways ...

On Thu, Mar 18, 2021 at 8:12 AM Aurélien Aptel <aaptel@suse.com> wrote:
>
>
> Since there's no standard VFS way to get the supported mount options
> from userspace, I thought I would do what Ronnie suggested and export
> them from a cifs /proc file.
> That's the only change since v1, in the 4th patch.
>
> David, maybe this can give your arguments for the need for fsinfo() if
> we end up using this in cifs-utils.
>
> I have added some dumb code in userspace to parse it and see if the
> option exists and what type it is. This removes the requirement of
> having to keep cifs-utils and kernel updated at the same time to use new
> options.
>
> Previous intro
> =======================================================================
> I have some code to use the new mount API from user space.
>
> The kernel changes are just making the code use the fs_context logging
> feature.
>
> The sample userspace prog (fsopen.c attached) is just a PoC showing how
> mounting is done and how the mount errors are read.
>
> If you change the prog to use a wrong version for example (vers=4.0) you
> get this:
>
>     $ gcc -o fsopen fsopen.c
>     $ ./fsopen
>     fsconfig(sfd, FSCONFIG_SET_STRING, "vers", "4.0", 0): Invalid argument
>     kernel mount errors:
>     e Unknown vers= option specified: 4.0
>
> The pros are that we process one option at a time and we can fail early
> with verbose, helpful messages to the user.
> =======================================================================
>
>
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
Aurélien Aptel April 12, 2021, 5:52 p.m. UTC | #8
Steve French <smfrench@gmail.com> writes:
> Tentatively merged into cifs-2.6.git for-next but would like more
> feedback on other's thoughts on this. Getting more verbose error
> information back on mount errors (to userspace returning something
> more than a primitive small set of return codes, and a message logged
> to dmesg) is critical, and this approach seems reasonable at first
> glance but if there are better ways ...

Yes more feedback would be reasonable. I've basically redone a barebone
version of the missing fsinfo() call just for cifs.

New patch version attached.

Changes since v2:
- add missing call to remove_proc_entry() (fix splat on rmmod)
Cheers,
diff mbox series

Patch

From fe07bfda2fb9cdef8a4d4008a409bb02f35f1bd8 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Sun, 28 Feb 2021 16:05:19 -0800
Subject: [PATCH 1/3] Linux 5.12-rc1

---
 Makefile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index b0e1bb472202..f9b54da2fca0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@ 
 # SPDX-License-Identifier: GPL-2.0
 VERSION = 5
-PATCHLEVEL = 11
+PATCHLEVEL = 12
 SUBLEVEL = 0
-EXTRAVERSION =
-NAME = 💕 Valentine's Day Edition 💕
+EXTRAVERSION = -rc1
+NAME = Frozen Wasteland
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
-- 
2.30.0


From a671cb46c29416c0fcb4b8e64e1d8e9013586b36 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Sat, 27 Feb 2021 02:01:46 -0600
Subject: [PATCH 2/3] smb3: allow files to be created with backslash in name

Backslash is reserved in Windows (and SMB2/SMB3 by default) but
allowed in POSIX so must be remapped when POSIX extensions are
not enabled.

The default mapping for SMB3 mounts ("SFM") allows mapping backslash
(ie 0x5C in UTF8) to 0xF026 in UCS-2 (using the Unicode remapping
range reserved for these characters), but this was not mapped by
cifs.ko (unlike asterisk, greater than, question mark etc).  This patch
fixes that to allow creating files and directories with backslash
in the file or directory name.

Before this patch:
   touch "/mnt2/filewith\slash"
would return
   touch: setting times of '/mnt2/filewith\slash': Invalid argument

With the patch touch and mkdir with the backslash in the name works.

This problem was found while debugging xfstest generic/453
    https://bugzilla.kernel.org/show_bug.cgi?id=210961

Reported-by: Xiaoli Feng <xifeng@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cifs_unicode.c | 15 ++++++++++-----
 fs/cifs/cifs_unicode.h |  3 +++
 fs/cifs/cifsglob.h     |  5 +----
 fs/cifs/dir.c          | 18 ++++++++++++------
 fs/cifs/misc.c         |  2 +-
 fs/cifs/smb2misc.c     | 18 +++++++++++-------
 6 files changed, 38 insertions(+), 23 deletions(-)

diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 9bd03a231032..4898b1553796 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -98,6 +98,9 @@  convert_sfm_char(const __u16 src_char, char *target)
 	case SFM_PERIOD:
 		*target = '.';
 		break;
+	case SFM_SLASH:
+		*target = '\\';
+		break;
 	default:
 		return false;
 	}
@@ -431,6 +434,9 @@  static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
 	case '|':
 		dest_char = cpu_to_le16(SFM_PIPE);
 		break;
+	case '\\':
+		dest_char = cpu_to_le16(SFM_SLASH);
+		break;
 	case '.':
 		if (end_of_string)
 			dest_char = cpu_to_le16(SFM_PERIOD);
@@ -443,6 +449,9 @@  static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
 		else
 			dest_char = 0;
 		break;
+	case '/':
+		dest_char = cpu_to_le16(UCS2_SLASH);
+		break;
 	default:
 		dest_char = 0;
 	}
@@ -502,11 +511,7 @@  cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
 			dst_char = convert_to_sfm_char(src_char, end_of_string);
 		} else
 			dst_char = 0;
-		/*
-		 * FIXME: We can not handle remapping backslash (UNI_SLASH)
-		 * until all the calls to build_path_from_dentry are modified,
-		 * as they use backslash as separator.
-		 */
+
 		if (dst_char == 0) {
 			charlen = cp->char2uni(source + i, srclen - i, &tmp);
 			dst_char = cpu_to_le16(tmp);
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
index 80b3d845419f..8cd58c71cbb6 100644
--- a/fs/cifs/cifs_unicode.h
+++ b/fs/cifs/cifs_unicode.h
@@ -24,6 +24,9 @@ 
 
 #define  UNIUPR_NOLOWER		/* Example to not expand lower case tables */
 
+/* Unicode encoding of backslash character */
+#define UCS2_SLASH 0x005C
+
 /*
  * Windows maps these to the user defined 16 bit Unicode range since they are
  * reserved symbols (along with \ and /), otherwise illegal to store
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 3de3c5908a72..95bd980ec849 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1430,10 +1430,7 @@  CIFS_FILE_SB(struct file *file)
 
 static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
 {
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
-		return '/';
-	else
-		return '\\';
+	return '/';
 }
 
 static inline void
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index a3fb81e0ba17..b3ee9871f6b6 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -209,12 +209,18 @@  check_name(struct dentry *direntry, struct cifs_tcon *tcon)
 		     le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength)))
 		return -ENAMETOOLONG;
 
-	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
-		for (i = 0; i < direntry->d_name.len; i++) {
-			if (direntry->d_name.name[i] == '\\') {
-				cifs_dbg(FYI, "Invalid file name\n");
-				return -EINVAL;
-			}
+	/*
+	 * SMB3.1.1 POSIX Extensions, CIFS Unix Extensions and SFM mappings
+	 * allow \ in paths (or in latter case remaps \ to 0xF026)
+	 */
+	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) ||
+	    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR))
+		return 0;
+
+	for (i = 0; i < direntry->d_name.len; i++) {
+		if (direntry->d_name.name[i] == '\\') {
+			cifs_dbg(FYI, "Invalid file name\n");
+			return -EINVAL;
 		}
 	}
 	return 0;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 82e176720ca6..2b4c53e47888 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1186,7 +1186,7 @@  int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
 			goto out;
 		}
 
-		convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+		convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); /* BB Check this */
 	} else
 		cifs_sb->prepath = NULL;
 
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 60d4bd1eae2b..ce4f00069653 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -476,13 +476,17 @@  cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
 	if (from[0] == '\\')
 		start_of_path = from + 1;
 
-	/* SMB311 POSIX extensions paths do not include leading slash */
-	else if (cifs_sb_master_tlink(cifs_sb) &&
-		 cifs_sb_master_tcon(cifs_sb)->posix_extensions &&
-		 (from[0] == '/')) {
-		start_of_path = from + 1;
-	} else
-		start_of_path = from;
+	start_of_path = from;
+	/*
+	 * Only old CIFS Unix extensions paths include leading slash
+	 * Need to skip if for SMB3.1.1 POSIX Extensions and SMB1/2/3
+	 */
+	if (from[0] == '/') {
+		if (((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == false) ||
+		    (cifs_sb_master_tlink(cifs_sb) &&
+		     (cifs_sb_master_tcon(cifs_sb)->posix_extensions)))
+			start_of_path = from + 1;
+	}
 
 	to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len,
 				   cifs_sb->local_nls, map_type);
-- 
2.30.0


From 2b0fa815fe8337f93174eba888dc67b140498af9 Mon Sep 17 00:00:00 2001
From: Aurelien Aptel <aaptel@suse.com>
Date: Mon, 1 Mar 2021 19:25:00 +0100
Subject: [PATCH 3/3] cifs: make fs_context error logging wrapper

This new helper will be used in the fs_context mount option parsing
code. It log errors both in:
* the fs_context log queue for userspace to read
* kernel printk buffer (dmesg, old behaviour)

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/fs_context.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h
index 87dd1f7168f2..dc0b7c9489f5 100644
--- a/fs/cifs/fs_context.h
+++ b/fs/cifs/fs_context.h
@@ -13,7 +13,12 @@ 
 #include <linux/parser.h>
 #include <linux/fs_parser.h>
 
-#define cifs_invalf(fc, fmt, ...) invalf(fc, fmt, ## __VA_ARGS__)
+/* Log errors in fs_context (new mount api) but also in dmesg (old style) */
+#define cifs_errorf(fc, fmt, ...)			\
+	do {						\
+		errorf(fc, fmt, ## __VA_ARGS__);	\
+		cifs_dbg(VFS, fmt, ## __VA_ARGS__);	\
+	} while (0)
 
 enum smb_version {
 	Smb_1 = 1,
-- 
2.30.0