diff mbox series

[3/6] efivarfs: make variable_is_present use dcache lookup

Message ID 20241210170224.19159-4-James.Bottomley@HansenPartnership.com (mailing list archive)
State New
Headers show
Series convert efivarfs to manage object data correctly | expand

Commit Message

James Bottomley Dec. 10, 2024, 5:02 p.m. UTC
Instead of searching the variable entry list for a variable, use the
dcache lookup functions to find it instead.  Also add an efivarfs_
prefix to the function now it is no longer static.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 fs/efivarfs/internal.h |  4 ++++
 fs/efivarfs/super.c    | 20 ++++++++++++++++++++
 fs/efivarfs/vars.c     | 26 ++------------------------
 3 files changed, 26 insertions(+), 24 deletions(-)

Comments

Dionna Amalie Glaze Dec. 10, 2024, 5:14 p.m. UTC | #1
On Tue, Dec 10, 2024 at 9:03 AM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:

>  extern const struct file_operations efivarfs_file_operations;
>  extern const struct inode_operations efivarfs_dir_inode_operations;
> @@ -64,4 +66,6 @@ extern struct inode *efivarfs_get_inode(struct super_block *sb,
>                         const struct inode *dir, int mode, dev_t dev,
>                         bool is_removable);
>
> +
> +

Unnecessary
>  #endif /* EFIVAR_FS_INTERNAL_H */
> diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
> index b22441f7f7c6..dc3870ae784b 100644
> --- a/fs/efivarfs/super.c
> +++ b/fs/efivarfs/super.c
> @@ -181,6 +181,26 @@ static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
>         return ERR_PTR(-ENOMEM);
>  }
>
> +bool efivarfs_variable_is_present(efi_char16_t *variable_name,
> +                                 efi_guid_t *vendor, void *data)
> +{
> +       char *name = efivar_get_utf8name(variable_name, vendor);
> +       struct super_block *sb = data;
> +       struct dentry *dentry;
> +       struct qstr qstr;
> +
> +       if (!name)
> +               return true;

Why is this true? I understand the previous implementation would have
hit a null dereference trying to calculate strsize1 on null, so this
isn't worse, but if we considered its length to be 0, it would not be
found.

> +
> +       qstr.name = name;
> +       qstr.len = strlen(name);
> +       dentry = d_hash_and_lookup(sb->s_root, &qstr);
> +       kfree(name);
> +       if (dentry)
> +               dput(dentry);
> +       return dentry != NULL;
> +}
> +
>  static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
>                              unsigned long name_size, void *data,
>                              struct list_head *list)
> diff --git a/fs/efivarfs/vars.c b/fs/efivarfs/vars.c
> index 7a07b767e2cc..f6380fdbe173 100644
> --- a/fs/efivarfs/vars.c
> +++ b/fs/efivarfs/vars.c
> @@ -313,28 +313,6 @@ efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
>         return found;
>  }
>
> -static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
> -                               struct list_head *head)
> -{
> -       struct efivar_entry *entry, *n;
> -       unsigned long strsize1, strsize2;
> -       bool found = false;
> -
> -       strsize1 = ucs2_strsize(variable_name, EFI_VAR_NAME_LEN);
> -       list_for_each_entry_safe(entry, n, head, list) {
> -               strsize2 = ucs2_strsize(entry->var.VariableName, EFI_VAR_NAME_LEN);
> -               if (strsize1 == strsize2 &&
> -                       !memcmp(variable_name, &(entry->var.VariableName),
> -                               strsize2) &&
> -                       !efi_guidcmp(entry->var.VendorGuid,
> -                               *vendor)) {
> -                       found = true;
> -                       break;
> -               }
> -       }
> -       return found;
> -}
> -
>  /*
>   * Returns the size of variable_name, in bytes, including the
>   * terminating NULL character, or variable_name_size if no NULL
> @@ -439,8 +417,8 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
>                          * we'll ever see a different variable name,
>                          * and may end up looping here forever.
>                          */
> -                       if (variable_is_present(variable_name, &vendor_guid,
> -                                               head)) {
> +                       if (efivarfs_variable_is_present(variable_name,
> +                                                        &vendor_guid, data)) {
>                                 dup_variable_bug(variable_name, &vendor_guid,
>                                                  variable_name_size);
>                                 status = EFI_NOT_FOUND;
> --
> 2.35.3
>
>
James Bottomley Dec. 10, 2024, 5:27 p.m. UTC | #2
On Tue, 2024-12-10 at 09:14 -0800, Dionna Amalie Glaze wrote:
> On Tue, Dec 10, 2024 at 9:03 AM James Bottomley
> <James.Bottomley@hansenpartnership.com> wrote:
> 
> >  extern const struct file_operations efivarfs_file_operations;
> >  extern const struct inode_operations
> > efivarfs_dir_inode_operations;
> > @@ -64,4 +66,6 @@ extern struct inode *efivarfs_get_inode(struct
> > super_block *sb,
> >                         const struct inode *dir, int mode, dev_t
> > dev,
> >                         bool is_removable);
> > 
> > +
> > +
> 
> Unnecessary

I can remove the extra line.

> >  #endif /* EFIVAR_FS_INTERNAL_H */
> > diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
> > index b22441f7f7c6..dc3870ae784b 100644
> > --- a/fs/efivarfs/super.c
> > +++ b/fs/efivarfs/super.c
> > @@ -181,6 +181,26 @@ static struct dentry
> > *efivarfs_alloc_dentry(struct dentry *parent, char *name)
> >         return ERR_PTR(-ENOMEM);
> >  }
> > 
> > +bool efivarfs_variable_is_present(efi_char16_t *variable_name,
> > +                                 efi_guid_t *vendor, void *data)
> > +{
> > +       char *name = efivar_get_utf8name(variable_name, vendor);
> > +       struct super_block *sb = data;
> > +       struct dentry *dentry;
> > +       struct qstr qstr;
> > +
> > +       if (!name)
> > +               return true;
> 
> Why is this true? I understand the previous implementation would have
> hit a null dereference trying to calculate strsize1 on null, so this
> isn't worse, but if we considered its length to be 0, it would not be
> found.

Because for safety on failure we need to assume a collision.  kmalloc
failing will already have dropped an error message so adding another
here (particularly when the log will likely be filling up with these
because we're in a critical memory shortage situation) would seem to be
overkill.  The memory allocation will never fail ordinarily and if it
does the system will be degrading fast, so EFI filesystem variable
collision will be the least of the problem.

Regards,

James
diff mbox series

Patch

diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
index 4b7330b90958..84a36e6fb653 100644
--- a/fs/efivarfs/internal.h
+++ b/fs/efivarfs/internal.h
@@ -56,6 +56,8 @@  bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
 bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
 				  size_t len);
 char *efivar_get_utf8name(const efi_char16_t *name16, efi_guid_t *vendor);
+bool efivarfs_variable_is_present(efi_char16_t *variable_name,
+				  efi_guid_t *vendor, void *data);
 
 extern const struct file_operations efivarfs_file_operations;
 extern const struct inode_operations efivarfs_dir_inode_operations;
@@ -64,4 +66,6 @@  extern struct inode *efivarfs_get_inode(struct super_block *sb,
 			const struct inode *dir, int mode, dev_t dev,
 			bool is_removable);
 
+
+
 #endif /* EFIVAR_FS_INTERNAL_H */
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index b22441f7f7c6..dc3870ae784b 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -181,6 +181,26 @@  static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
 	return ERR_PTR(-ENOMEM);
 }
 
+bool efivarfs_variable_is_present(efi_char16_t *variable_name,
+				  efi_guid_t *vendor, void *data)
+{
+	char *name = efivar_get_utf8name(variable_name, vendor);
+	struct super_block *sb = data;
+	struct dentry *dentry;
+	struct qstr qstr;
+
+	if (!name)
+		return true;
+
+	qstr.name = name;
+	qstr.len = strlen(name);
+	dentry = d_hash_and_lookup(sb->s_root, &qstr);
+	kfree(name);
+	if (dentry)
+		dput(dentry);
+	return dentry != NULL;
+}
+
 static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
 			     unsigned long name_size, void *data,
 			     struct list_head *list)
diff --git a/fs/efivarfs/vars.c b/fs/efivarfs/vars.c
index 7a07b767e2cc..f6380fdbe173 100644
--- a/fs/efivarfs/vars.c
+++ b/fs/efivarfs/vars.c
@@ -313,28 +313,6 @@  efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
 	return found;
 }
 
-static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
-				struct list_head *head)
-{
-	struct efivar_entry *entry, *n;
-	unsigned long strsize1, strsize2;
-	bool found = false;
-
-	strsize1 = ucs2_strsize(variable_name, EFI_VAR_NAME_LEN);
-	list_for_each_entry_safe(entry, n, head, list) {
-		strsize2 = ucs2_strsize(entry->var.VariableName, EFI_VAR_NAME_LEN);
-		if (strsize1 == strsize2 &&
-			!memcmp(variable_name, &(entry->var.VariableName),
-				strsize2) &&
-			!efi_guidcmp(entry->var.VendorGuid,
-				*vendor)) {
-			found = true;
-			break;
-		}
-	}
-	return found;
-}
-
 /*
  * Returns the size of variable_name, in bytes, including the
  * terminating NULL character, or variable_name_size if no NULL
@@ -439,8 +417,8 @@  int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
 			 * we'll ever see a different variable name,
 			 * and may end up looping here forever.
 			 */
-			if (variable_is_present(variable_name, &vendor_guid,
-						head)) {
+			if (efivarfs_variable_is_present(variable_name,
+							 &vendor_guid, data)) {
 				dup_variable_bug(variable_name, &vendor_guid,
 						 variable_name_size);
 				status = EFI_NOT_FOUND;