mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 15:40:03 +02:00
ksmbd: support normalized name information
FILE_NORMALIZED_NAME_INFORMATION is not handled and is returned as STATUS_INVALID_INFO_CLASS. SMB 3.1.1 clients use this information class to obtain the share-relative path with the on-disk name casing. Build the normalized path from the opened dentry, remove the leading share-relative separator, and recover the canonical named-stream casing from its backing xattr. Return an empty name for the share root and STATUS_NOT_SUPPORTED for dialects older than SMB 3.1.1. Also distinguish a named $DATA stream on a directory from the unnamed data stream so that directory:stream:$DATA can be opened normally. This fixes smb2.getinfo.normalized. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
2103add92a
commit
10aeff72ab
|
|
@ -3618,7 +3618,7 @@ int smb2_open(struct ksmbd_work *work)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) &&
|
if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) &&
|
||||||
s_type == DATA_STREAM) {
|
!stream_name && s_type == DATA_STREAM) {
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY;
|
rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY;
|
||||||
}
|
}
|
||||||
|
|
@ -5578,6 +5578,80 @@ static void get_file_alternate_info(struct ksmbd_work *work,
|
||||||
cpu_to_le32(struct_size(file_info, FileName, conv_len));
|
cpu_to_le32(struct_size(file_info, FileName, conv_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *smb2_get_normalized_stream_name(struct ksmbd_file *fp)
|
||||||
|
{
|
||||||
|
char *name, *stream_name = NULL, *xattr_list = NULL;
|
||||||
|
ssize_t xattr_list_len;
|
||||||
|
|
||||||
|
if (!ksmbd_stream_fd(fp))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
xattr_list_len = ksmbd_vfs_listxattr(fp->filp->f_path.dentry,
|
||||||
|
&xattr_list);
|
||||||
|
if (xattr_list_len <= 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (name = xattr_list; name - xattr_list < xattr_list_len;
|
||||||
|
name += strlen(name) + 1) {
|
||||||
|
char *type;
|
||||||
|
|
||||||
|
if (strlen(name) + 1 != fp->stream.size ||
|
||||||
|
strncasecmp(name, fp->stream.name, fp->stream.size - 1))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
name += XATTR_NAME_STREAM_LEN;
|
||||||
|
type = strrchr(name, ':');
|
||||||
|
if (type)
|
||||||
|
stream_name = kstrndup(name, type - name,
|
||||||
|
KSMBD_DEFAULT_GFP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
kvfree(xattr_list);
|
||||||
|
return stream_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_file_normalized_name_info(struct ksmbd_work *work,
|
||||||
|
struct smb2_query_info_rsp *rsp,
|
||||||
|
struct ksmbd_file *fp)
|
||||||
|
{
|
||||||
|
struct smb2_file_alt_name_info *file_info;
|
||||||
|
char *filename, *normalized, *stream_name;
|
||||||
|
int conv_len, filename_len;
|
||||||
|
|
||||||
|
if (work->conn->dialect < SMB311_PROT_ID) {
|
||||||
|
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = convert_to_nt_pathname(work->tcon->share_conf,
|
||||||
|
&fp->filp->f_path);
|
||||||
|
if (IS_ERR(filename))
|
||||||
|
return PTR_ERR(filename);
|
||||||
|
if (filename[0] == '\\')
|
||||||
|
memmove(filename, filename + 1, strlen(filename));
|
||||||
|
|
||||||
|
stream_name = smb2_get_normalized_stream_name(fp);
|
||||||
|
normalized = kasprintf(KSMBD_DEFAULT_GFP, "%s%s%s", filename,
|
||||||
|
stream_name ? ":" : "",
|
||||||
|
stream_name ? stream_name : "");
|
||||||
|
kfree(stream_name);
|
||||||
|
kfree(filename);
|
||||||
|
if (!normalized)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
filename_len = strlen(normalized);
|
||||||
|
file_info = (struct smb2_file_alt_name_info *)rsp->Buffer;
|
||||||
|
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName,
|
||||||
|
normalized, filename_len,
|
||||||
|
work->conn->local_nls, 0);
|
||||||
|
kfree(normalized);
|
||||||
|
conv_len *= 2;
|
||||||
|
file_info->FileNameLength = cpu_to_le32(conv_len);
|
||||||
|
rsp->OutputBufferLength = cpu_to_le32(sizeof(*file_info) + conv_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_file_stream_info(struct ksmbd_work *work,
|
static int get_file_stream_info(struct ksmbd_work *work,
|
||||||
struct smb2_query_info_rsp *rsp,
|
struct smb2_query_info_rsp *rsp,
|
||||||
struct ksmbd_file *fp,
|
struct ksmbd_file *fp,
|
||||||
|
|
@ -5969,6 +6043,9 @@ static int smb2_get_info_file(struct ksmbd_work *work,
|
||||||
case FILE_ALTERNATE_NAME_INFORMATION:
|
case FILE_ALTERNATE_NAME_INFORMATION:
|
||||||
get_file_alternate_info(work, rsp, fp, work->response_buf);
|
get_file_alternate_info(work, rsp, fp, work->response_buf);
|
||||||
break;
|
break;
|
||||||
|
case FILE_NORMALIZED_NAME_INFORMATION:
|
||||||
|
rc = get_file_normalized_name_info(work, rsp, fp);
|
||||||
|
break;
|
||||||
|
|
||||||
case FILE_STREAM_INFORMATION:
|
case FILE_STREAM_INFORMATION:
|
||||||
rc = get_file_stream_info(work, rsp, fp, work->response_buf);
|
rc = get_file_stream_info(work, rsp, fp, work->response_buf);
|
||||||
|
|
@ -6478,7 +6555,7 @@ int smb2_query_info(struct ksmbd_work *work)
|
||||||
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
|
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
else if (rc == -EINVAL && rsp->hdr.Status == 0)
|
else if (rc == -EINVAL && rsp->hdr.Status == 0)
|
||||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||||
else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0)
|
else if (rsp->hdr.Status == 0)
|
||||||
rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
|
rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
|
||||||
smb2_set_err_rsp(work);
|
smb2_set_err_rsp(work);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user