return 0;
 }
 
+struct wsl_query_ea {
+       __le32  next;
+       __u8    name_len;
+       __u8    name[SMB2_WSL_XATTR_NAME_LEN + 1];
+} __packed;
+
+#define NEXT_OFF cpu_to_le32(sizeof(struct wsl_query_ea))
+
+static const struct wsl_query_ea wsl_query_eas[] = {
+       { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_UID, },
+       { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_GID, },
+       { .next = NEXT_OFF, .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_MODE, },
+       { .next = 0,        .name_len = SMB2_WSL_XATTR_NAME_LEN, .name = SMB2_WSL_XATTR_DEV, },
+};
+
+static int check_wsl_eas(struct kvec *rsp_iov)
+{
+       struct smb2_file_full_ea_info *ea;
+       struct smb2_query_info_rsp *rsp = rsp_iov->iov_base;
+       unsigned long addr;
+       u32 outlen, next;
+       u16 vlen;
+       u8 nlen;
+       u8 *end;
+
+       outlen = le32_to_cpu(rsp->OutputBufferLength);
+       if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
+           outlen > SMB2_WSL_MAX_QUERY_EA_RESP_SIZE)
+               return -EINVAL;
+
+       ea = (void *)((u8 *)rsp_iov->iov_base +
+                     le16_to_cpu(rsp->OutputBufferOffset));
+       end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+       for (;;) {
+               if ((u8 *)ea > end - sizeof(*ea))
+                       return -EINVAL;
+
+               nlen = ea->ea_name_length;
+               vlen = le16_to_cpu(ea->ea_value_length);
+               if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
+                   (u8 *)ea + nlen + 1 + vlen > end)
+                       return -EINVAL;
+
+               switch (vlen) {
+               case 4:
+                       if (strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) &&
+                           strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) &&
+                           strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen))
+                               return -EINVAL;
+                       break;
+               case 8:
+                       if (strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
+                               return -EINVAL;
+                       break;
+               case 0:
+                       if (!strncmp(ea->ea_data, SMB2_WSL_XATTR_UID, nlen) ||
+                           !strncmp(ea->ea_data, SMB2_WSL_XATTR_GID, nlen) ||
+                           !strncmp(ea->ea_data, SMB2_WSL_XATTR_MODE, nlen) ||
+                           !strncmp(ea->ea_data, SMB2_WSL_XATTR_DEV, nlen))
+                               break;
+                       fallthrough;
+               default:
+                       return -EINVAL;
+               }
+
+               next = le32_to_cpu(ea->next_entry_offset);
+               if (!next)
+                       break;
+               if (!IS_ALIGNED(next, 4) ||
+                   check_add_overflow((unsigned long)ea, next, &addr))
+                       return -EINVAL;
+               ea = (void *)addr;
+       }
+       return 0;
+}
+
 /*
  * note: If cfile is passed, the reference to it is dropped here.
  * So make sure that you do not reuse cfile after return from this func.
        __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
        unsigned int size[2];
        void *data[2];
-       int len;
+       unsigned int len;
        int retries = 0, cur_sleep = 1;
 
 replay_again:
                        trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
                                                              tcon->tid, full_path);
                        break;
+               case SMB2_OP_QUERY_WSL_EA:
+                       rqst[num_rqst].rq_iov = &vars->ea_iov;
+                       rqst[num_rqst].rq_nvec = 1;
+
+                       if (cfile) {
+                               rc = SMB2_query_info_init(tcon, server,
+                                                         &rqst[num_rqst],
+                                                         cfile->fid.persistent_fid,
+                                                         cfile->fid.volatile_fid,
+                                                         FILE_FULL_EA_INFORMATION,
+                                                         SMB2_O_INFO_FILE, 0,
+                                                         SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
+                                                         sizeof(wsl_query_eas),
+                                                         (void *)wsl_query_eas);
+                       } else {
+                               rc = SMB2_query_info_init(tcon, server,
+                                                         &rqst[num_rqst],
+                                                         COMPOUND_FID,
+                                                         COMPOUND_FID,
+                                                         FILE_FULL_EA_INFORMATION,
+                                                         SMB2_O_INFO_FILE, 0,
+                                                         SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
+                                                         sizeof(wsl_query_eas),
+                                                         (void *)wsl_query_eas);
+                       }
+                       if (!rc && (!cfile || num_rqst > 1)) {
+                               smb2_set_next_command(tcon, &rqst[num_rqst]);
+                               smb2_set_related(&rqst[num_rqst]);
+                       } else if (rc) {
+                               goto finished;
+                       }
+                       num_rqst++;
+                       break;
                default:
                        cifs_dbg(VFS, "Invalid command\n");
                        rc = -EINVAL;
                                memset(iov, 0, sizeof(*iov));
                                resp_buftype[i + 1] = CIFS_NO_BUFFER;
                        } else {
-                               trace_smb3_set_reparse_compound_err(xid,  ses->Suid,
+                               trace_smb3_set_reparse_compound_err(xid, ses->Suid,
                                                                    tcon->tid, rc);
                        }
                        SMB2_ioctl_free(&rqst[num_rqst++]);
                        break;
+               case SMB2_OP_QUERY_WSL_EA:
+                       if (!rc) {
+                               idata = in_iov[i].iov_base;
+                               qi_rsp = rsp_iov[i + 1].iov_base;
+                               data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
+                               size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
+                               rc = check_wsl_eas(&rsp_iov[i + 1]);
+                               if (!rc) {
+                                       memcpy(idata->wsl.eas, data[0], size[0]);
+                                       idata->wsl.eas_len = size[0];
+                               }
+                       }
+                       if (!rc) {
+                               trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
+                                                                     tcon->tid);
+                       } else {
+                               trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
+                                                                    tcon->tid, rc);
+                       }
+                       SMB2_query_info_free(&rqst[num_rqst++]);
+                       break;
                }
        }
        SMB2_close_free(&rqst[num_rqst]);
        struct cifsFileInfo *cfile;
        struct cached_fid *cfid = NULL;
        struct smb2_hdr *hdr;
-       struct kvec in_iov[2], out_iov[3] = {};
+       struct kvec in_iov[3], out_iov[3] = {};
        int out_buftype[3] = {};
-       int cmds[2];
+       int cmds[3];
        bool islink;
-       int i, num_cmds;
+       int i, num_cmds = 0;
        int rc, rc2;
 
        data->adjust_tz = false;
                        close_cached_dir(cfid);
                        return rc;
                }
-               cmds[0] = SMB2_OP_QUERY_INFO;
+               cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
        } else {
-               cmds[0] = SMB2_OP_POSIX_QUERY_INFO;
+               cmds[num_cmds++] = SMB2_OP_POSIX_QUERY_INFO;
        }
 
        in_iov[0].iov_base = data;
        in_iov[0].iov_len = sizeof(*data);
        in_iov[1] = in_iov[0];
+       in_iov[2] = in_iov[0];
 
        cifs_get_readable_path(tcon, full_path, &cfile);
        oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_ATTRIBUTES,
                             FILE_OPEN, create_options, ACL_NO_MODE);
        rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
-                             &oparms, in_iov, cmds, 1, cfile,
-                             out_iov, out_buftype, NULL);
+                             &oparms, in_iov, cmds, num_cmds,
+                             cfile, out_iov, out_buftype, NULL);
        hdr = out_iov[0].iov_base;
        /*
         * If first iov is unset, then SMB session was dropped or we've got a
                if (rc || !data->reparse_point)
                        goto out;
 
-               if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK) {
-                       /* symlink already parsed in create response */
-                       num_cmds = 1;
-               } else {
-                       cmds[1] = SMB2_OP_GET_REPARSE;
-                       num_cmds = 2;
-               }
+               cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
+               /*
+                * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
+                * response.
+                */
+               if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
+                       cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
+
                oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
-                                    FILE_READ_ATTRIBUTES, FILE_OPEN,
-                                    create_options | OPEN_REPARSE_POINT,
-                                    ACL_NO_MODE);
+                                    FILE_READ_ATTRIBUTES | FILE_READ_EA,
+                                    FILE_OPEN, create_options |
+                                    OPEN_REPARSE_POINT, ACL_NO_MODE);
                cifs_get_readable_path(tcon, full_path, &cfile);
                rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
                                      &oparms, in_iov, cmds, num_cmds,