]> www.infradead.org Git - users/hch/misc.git/commitdiff
cifs: Query EA $LXMOD in cifs_query_path_info() for WSL reparse points
authorPali Rohár <pali@kernel.org>
Sat, 7 Jun 2025 16:11:10 +0000 (18:11 +0200)
committerSteve French <stfrench@microsoft.com>
Fri, 10 Oct 2025 04:01:24 +0000 (23:01 -0500)
EA $LXMOD is required for WSL non-symlink reparse points.

Fixes: ef86ab131d91 ("cifs: Fix querying of WSL CHR and BLK reparse points over SMB1")
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/smb1ops.c

index a02d41d1ce4a3fd78a658495c36d75526131b6ea..3fdbb71036cff2f940345a461815c511ad7db989 100644 (file)
@@ -651,14 +651,72 @@ static int cifs_query_path_info(const unsigned int xid,
        }
 
 #ifdef CONFIG_CIFS_XATTR
+       /*
+        * For non-symlink WSL reparse points it is required to fetch
+        * EA $LXMOD which contains in its S_DT part the mandatory file type.
+        */
+       if (!rc && data->reparse_point) {
+               struct smb2_file_full_ea_info *ea;
+               u32 next = 0;
+
+               ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
+               do {
+                       ea = (void *)((u8 *)ea + next);
+                       next = le32_to_cpu(ea->next_entry_offset);
+               } while (next);
+               if (le16_to_cpu(ea->ea_value_length)) {
+                       ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
+                                               ea->ea_name_length + 1 +
+                                               le16_to_cpu(ea->ea_value_length), 4));
+                       ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
+               }
+
+               rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE,
+                                   &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
+                                   SMB2_WSL_XATTR_MODE_SIZE, cifs_sb);
+               if (rc == SMB2_WSL_XATTR_MODE_SIZE) {
+                       ea->next_entry_offset = cpu_to_le32(0);
+                       ea->flags = 0;
+                       ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
+                       ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_MODE_SIZE);
+                       memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_MODE, SMB2_WSL_XATTR_NAME_LEN + 1);
+                       data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
+                                                  SMB2_WSL_XATTR_MODE_SIZE, 4);
+                       rc = 0;
+               } else if (rc >= 0) {
+                       /* It is an error if EA $LXMOD has wrong size. */
+                       rc = -EINVAL;
+               } else {
+                       /*
+                        * In all other cases ignore error if fetching
+                        * of EA $LXMOD failed. It is needed only for
+                        * non-symlink WSL reparse points and wsl_to_fattr()
+                        * handle the case when EA is missing.
+                        */
+                       rc = 0;
+               }
+       }
+
        /*
         * For WSL CHR and BLK reparse points it is required to fetch
         * EA $LXDEV which contains major and minor device numbers.
         */
        if (!rc && data->reparse_point) {
                struct smb2_file_full_ea_info *ea;
+               u32 next = 0;
 
                ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
+               do {
+                       ea = (void *)((u8 *)ea + next);
+                       next = le32_to_cpu(ea->next_entry_offset);
+               } while (next);
+               if (le16_to_cpu(ea->ea_value_length)) {
+                       ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
+                                               ea->ea_name_length + 1 +
+                                               le16_to_cpu(ea->ea_value_length), 4));
+                       ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
+               }
+
                rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_DEV,
                                    &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
                                    SMB2_WSL_XATTR_DEV_SIZE, cifs_sb);
@@ -668,8 +726,8 @@ static int cifs_query_path_info(const unsigned int xid,
                        ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
                        ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
                        memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
-                       data->wsl.eas_len sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
-                                           SMB2_WSL_XATTR_DEV_SIZE;
+                       data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
+                                                  SMB2_WSL_XATTR_MODE_SIZE, 4);
                        rc = 0;
                } else if (rc >= 0) {
                        /* It is an error if EA $LXDEV has wrong size. */