mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-11 23:03:55 +08:00
ksmbd: use LOOKUP_BENEATH to prevent the out of share access
instead of removing '..' in a given path, call kern_path with LOOKUP_BENEATH flag to prevent the out of share access. ran various test on this: smb2-cat-async smb://127.0.0.1/homes/../out_of_share smb2-cat-async smb://127.0.0.1/homes/foo/../../out_of_share smbclient //127.0.0.1/homes -c "mkdir ../foo2" smbclient //127.0.0.1/homes -c "rename bar ../bar" Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com> Cc: Ralph Boehme <slow@samba.org> Tested-by: Steve French <smfrench@gmail.com> Tested-by: Namjae Jeon <linkinjeon@kernel.org> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
4ea477988c
commit
265fd1991c
100
fs/ksmbd/misc.c
100
fs/ksmbd/misc.c
@ -158,25 +158,21 @@ out:
|
|||||||
* Return : windows path string or error
|
* Return : windows path string or error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char *convert_to_nt_pathname(char *filename, char *sharepath)
|
char *convert_to_nt_pathname(char *filename)
|
||||||
{
|
{
|
||||||
char *ab_pathname;
|
char *ab_pathname;
|
||||||
int len, name_len;
|
|
||||||
|
|
||||||
name_len = strlen(filename);
|
if (strlen(filename) == 0) {
|
||||||
ab_pathname = kmalloc(name_len, GFP_KERNEL);
|
ab_pathname = kmalloc(2, GFP_KERNEL);
|
||||||
if (!ab_pathname)
|
ab_pathname[0] = '\\';
|
||||||
return NULL;
|
ab_pathname[1] = '\0';
|
||||||
|
} else {
|
||||||
|
ab_pathname = kstrdup(filename, GFP_KERNEL);
|
||||||
|
if (!ab_pathname)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
ab_pathname[0] = '\\';
|
|
||||||
ab_pathname[1] = '\0';
|
|
||||||
|
|
||||||
len = strlen(sharepath);
|
|
||||||
if (!strncmp(filename, sharepath, len) && name_len != len) {
|
|
||||||
strscpy(ab_pathname, &filename[len], name_len);
|
|
||||||
ksmbd_conv_path_to_windows(ab_pathname);
|
ksmbd_conv_path_to_windows(ab_pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ab_pathname;
|
return ab_pathname;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,77 +187,19 @@ int get_nlink(struct kstat *st)
|
|||||||
return nlink;
|
return nlink;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *ksmbd_conv_path_to_unix(char *path)
|
void ksmbd_conv_path_to_unix(char *path)
|
||||||
{
|
{
|
||||||
size_t path_len, remain_path_len, out_path_len;
|
|
||||||
char *out_path, *out_next;
|
|
||||||
int i, pre_dotdot_cnt = 0, slash_cnt = 0;
|
|
||||||
bool is_last;
|
|
||||||
|
|
||||||
strreplace(path, '\\', '/');
|
strreplace(path, '\\', '/');
|
||||||
path_len = strlen(path);
|
}
|
||||||
remain_path_len = path_len;
|
|
||||||
if (path_len == 0)
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
|
|
||||||
out_path = kzalloc(path_len + 2, GFP_KERNEL);
|
void ksmbd_strip_last_slash(char *path)
|
||||||
if (!out_path)
|
{
|
||||||
return ERR_PTR(-ENOMEM);
|
int len = strlen(path);
|
||||||
out_path_len = 0;
|
|
||||||
out_next = out_path;
|
|
||||||
|
|
||||||
do {
|
while (len && path[len - 1] == '/') {
|
||||||
char *name = path + path_len - remain_path_len;
|
path[len - 1] = '\0';
|
||||||
char *next = strchrnul(name, '/');
|
len--;
|
||||||
size_t name_len = next - name;
|
}
|
||||||
|
|
||||||
is_last = !next[0];
|
|
||||||
if (name_len == 2 && name[0] == '.' && name[1] == '.') {
|
|
||||||
pre_dotdot_cnt++;
|
|
||||||
/* handle the case that path ends with "/.." */
|
|
||||||
if (is_last)
|
|
||||||
goto follow_dotdot;
|
|
||||||
} else {
|
|
||||||
if (pre_dotdot_cnt) {
|
|
||||||
follow_dotdot:
|
|
||||||
slash_cnt = 0;
|
|
||||||
for (i = out_path_len - 1; i >= 0; i--) {
|
|
||||||
if (out_path[i] == '/' &&
|
|
||||||
++slash_cnt == pre_dotdot_cnt + 1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < 0 &&
|
|
||||||
slash_cnt != pre_dotdot_cnt) {
|
|
||||||
kfree(out_path);
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
out_next = &out_path[i+1];
|
|
||||||
*out_next = '\0';
|
|
||||||
out_path_len = i + 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name_len != 0 &&
|
|
||||||
!(name_len == 1 && name[0] == '.') &&
|
|
||||||
!(name_len == 2 && name[0] == '.' && name[1] == '.')) {
|
|
||||||
next[0] = '\0';
|
|
||||||
sprintf(out_next, "%s/", name);
|
|
||||||
out_next += name_len + 1;
|
|
||||||
out_path_len += name_len + 1;
|
|
||||||
next[0] = '/';
|
|
||||||
}
|
|
||||||
pre_dotdot_cnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
remain_path_len -= name_len + 1;
|
|
||||||
} while (!is_last);
|
|
||||||
|
|
||||||
if (out_path_len > 0)
|
|
||||||
out_path[out_path_len-1] = '\0';
|
|
||||||
path[path_len] = '\0';
|
|
||||||
return out_path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksmbd_conv_path_to_windows(char *path)
|
void ksmbd_conv_path_to_windows(char *path)
|
||||||
@ -298,7 +236,7 @@ char *ksmbd_extract_sharename(char *treename)
|
|||||||
*
|
*
|
||||||
* Return: converted name on success, otherwise NULL
|
* Return: converted name on success, otherwise NULL
|
||||||
*/
|
*/
|
||||||
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name)
|
char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
|
||||||
{
|
{
|
||||||
int no_slash = 0, name_len, path_len;
|
int no_slash = 0, name_len, path_len;
|
||||||
char *new_name;
|
char *new_name;
|
||||||
|
@ -14,12 +14,13 @@ struct ksmbd_file;
|
|||||||
int match_pattern(const char *str, size_t len, const char *pattern);
|
int match_pattern(const char *str, size_t len, const char *pattern);
|
||||||
int ksmbd_validate_filename(char *filename);
|
int ksmbd_validate_filename(char *filename);
|
||||||
int parse_stream_name(char *filename, char **stream_name, int *s_type);
|
int parse_stream_name(char *filename, char **stream_name, int *s_type);
|
||||||
char *convert_to_nt_pathname(char *filename, char *sharepath);
|
char *convert_to_nt_pathname(char *filename);
|
||||||
int get_nlink(struct kstat *st);
|
int get_nlink(struct kstat *st);
|
||||||
char *ksmbd_conv_path_to_unix(char *path);
|
void ksmbd_conv_path_to_unix(char *path);
|
||||||
|
void ksmbd_strip_last_slash(char *path);
|
||||||
void ksmbd_conv_path_to_windows(char *path);
|
void ksmbd_conv_path_to_windows(char *path);
|
||||||
char *ksmbd_extract_sharename(char *treename);
|
char *ksmbd_extract_sharename(char *treename);
|
||||||
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
|
char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
|
||||||
|
|
||||||
#define KSMBD_DIR_INFO_ALIGNMENT 8
|
#define KSMBD_DIR_INFO_ALIGNMENT 8
|
||||||
struct ksmbd_dir_info;
|
struct ksmbd_dir_info;
|
||||||
|
@ -634,7 +634,7 @@ static char *
|
|||||||
smb2_get_name(struct ksmbd_share_config *share, const char *src,
|
smb2_get_name(struct ksmbd_share_config *share, const char *src,
|
||||||
const int maxlen, struct nls_table *local_nls)
|
const int maxlen, struct nls_table *local_nls)
|
||||||
{
|
{
|
||||||
char *name, *norm_name, *unixname;
|
char *name;
|
||||||
|
|
||||||
name = smb_strndup_from_utf16(src, maxlen, 1, local_nls);
|
name = smb_strndup_from_utf16(src, maxlen, 1, local_nls);
|
||||||
if (IS_ERR(name)) {
|
if (IS_ERR(name)) {
|
||||||
@ -642,23 +642,9 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src,
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* change it to absolute unix name */
|
ksmbd_conv_path_to_unix(name);
|
||||||
norm_name = ksmbd_conv_path_to_unix(name);
|
ksmbd_strip_last_slash(name);
|
||||||
if (IS_ERR(norm_name)) {
|
return name;
|
||||||
kfree(name);
|
|
||||||
return norm_name;
|
|
||||||
}
|
|
||||||
kfree(name);
|
|
||||||
|
|
||||||
unixname = convert_to_unix_name(share, norm_name);
|
|
||||||
kfree(norm_name);
|
|
||||||
if (!unixname) {
|
|
||||||
pr_err("can not convert absolute name\n");
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
ksmbd_debug(SMB, "absolute name = %s\n", unixname);
|
|
||||||
return unixname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
|
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
|
||||||
@ -2352,7 +2338,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ksmbd_vfs_kern_path(name, 0, path, 0);
|
rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("cannot get linux path (%s), err = %d\n",
|
pr_err("cannot get linux path (%s), err = %d\n",
|
||||||
name, rc);
|
name, rc);
|
||||||
@ -2427,7 +2413,7 @@ int smb2_open(struct ksmbd_work *work)
|
|||||||
struct oplock_info *opinfo;
|
struct oplock_info *opinfo;
|
||||||
__le32 *next_ptr = NULL;
|
__le32 *next_ptr = NULL;
|
||||||
int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
|
int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
|
||||||
int rc = 0, len = 0;
|
int rc = 0;
|
||||||
int contxt_cnt = 0, query_disk_id = 0;
|
int contxt_cnt = 0, query_disk_id = 0;
|
||||||
int maximal_access_ctxt = 0, posix_ctxt = 0;
|
int maximal_access_ctxt = 0, posix_ctxt = 0;
|
||||||
int s_type = 0;
|
int s_type = 0;
|
||||||
@ -2499,17 +2485,11 @@ int smb2_open(struct ksmbd_work *work)
|
|||||||
goto err_out1;
|
goto err_out1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
len = strlen(share->path);
|
name = kstrdup("", GFP_KERNEL);
|
||||||
ksmbd_debug(SMB, "share path len %d\n", len);
|
|
||||||
name = kmalloc(len + 1, GFP_KERNEL);
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
rsp->hdr.Status = STATUS_NO_MEMORY;
|
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto err_out1;
|
goto err_out1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(name, share->path, len);
|
|
||||||
*(name + len) = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req_op_level = req->RequestedOplockLevel;
|
req_op_level = req->RequestedOplockLevel;
|
||||||
@ -2632,7 +2612,7 @@ int smb2_open(struct ksmbd_work *work)
|
|||||||
goto err_out1;
|
goto err_out1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ksmbd_vfs_kern_path(name, LOOKUP_NO_SYMLINKS, &path, 1);
|
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
|
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
|
||||||
/*
|
/*
|
||||||
@ -2661,11 +2641,8 @@ int smb2_open(struct ksmbd_work *work)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
if (rc == -EACCES) {
|
if (rc != -ENOENT)
|
||||||
ksmbd_debug(SMB,
|
|
||||||
"User does not have right permission\n");
|
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
|
||||||
ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
|
ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
|
||||||
name, rc);
|
name, rc);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
@ -3161,7 +3138,7 @@ err_out1:
|
|||||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||||
else if (rc == -EOPNOTSUPP)
|
else if (rc == -EOPNOTSUPP)
|
||||||
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
|
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
|
||||||
else if (rc == -EACCES || rc == -ESTALE)
|
else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV)
|
||||||
rsp->hdr.Status = STATUS_ACCESS_DENIED;
|
rsp->hdr.Status = STATUS_ACCESS_DENIED;
|
||||||
else if (rc == -ENOENT)
|
else if (rc == -ENOENT)
|
||||||
rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
|
rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
|
||||||
@ -4277,8 +4254,7 @@ static int get_file_all_info(struct ksmbd_work *work,
|
|||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = convert_to_nt_pathname(fp->filename,
|
filename = convert_to_nt_pathname(fp->filename);
|
||||||
work->tcon->share_conf->path);
|
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -4733,7 +4709,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
|
|||||||
int rc = 0, len;
|
int rc = 0, len;
|
||||||
int fs_infoclass_size = 0;
|
int fs_infoclass_size = 0;
|
||||||
|
|
||||||
rc = ksmbd_vfs_kern_path(share->path, LOOKUP_NO_SYMLINKS, &path, 0);
|
rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("cannot create vfs path\n");
|
pr_err("cannot create vfs path\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@ -5282,7 +5258,7 @@ static int smb2_rename(struct ksmbd_work *work,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
len = strlen(new_name);
|
len = strlen(new_name);
|
||||||
if (new_name[len - 1] != '/') {
|
if (len > 0 && new_name[len - 1] != '/') {
|
||||||
pr_err("not allow base filename in rename\n");
|
pr_err("not allow base filename in rename\n");
|
||||||
rc = -ESHARE;
|
rc = -ESHARE;
|
||||||
goto out;
|
goto out;
|
||||||
@ -5310,11 +5286,14 @@ static int smb2_rename(struct ksmbd_work *work,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ksmbd_debug(SMB, "new name %s\n", new_name);
|
ksmbd_debug(SMB, "new name %s\n", new_name);
|
||||||
rc = ksmbd_vfs_kern_path(new_name, LOOKUP_NO_SYMLINKS, &path, 1);
|
rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
if (rc != -ENOENT)
|
||||||
|
goto out;
|
||||||
file_present = false;
|
file_present = false;
|
||||||
else
|
} else {
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
|
}
|
||||||
|
|
||||||
if (ksmbd_share_veto_filename(share, new_name)) {
|
if (ksmbd_share_veto_filename(share, new_name)) {
|
||||||
rc = -ENOENT;
|
rc = -ENOENT;
|
||||||
@ -5384,11 +5363,14 @@ static int smb2_create_link(struct ksmbd_work *work,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ksmbd_debug(SMB, "target name is %s\n", target_name);
|
ksmbd_debug(SMB, "target name is %s\n", target_name);
|
||||||
rc = ksmbd_vfs_kern_path(link_name, LOOKUP_NO_SYMLINKS, &path, 0);
|
rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
if (rc != -ENOENT)
|
||||||
|
goto out;
|
||||||
file_present = false;
|
file_present = false;
|
||||||
else
|
} else {
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
|
}
|
||||||
|
|
||||||
if (file_info->ReplaceIfExists) {
|
if (file_info->ReplaceIfExists) {
|
||||||
if (file_present) {
|
if (file_present) {
|
||||||
@ -5548,7 +5530,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
|
|||||||
* inode size is retained by backup inode size.
|
* inode size is retained by backup inode size.
|
||||||
*/
|
*/
|
||||||
size = i_size_read(inode);
|
size = i_size_read(inode);
|
||||||
rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512);
|
rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("truncate failed! filename : %s, err %d\n",
|
pr_err("truncate failed! filename : %s, err %d\n",
|
||||||
fp->filename, rc);
|
fp->filename, rc);
|
||||||
@ -5585,7 +5567,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
|
|||||||
if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
|
if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
|
||||||
ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n",
|
ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n",
|
||||||
fp->filename, newsize);
|
fp->filename, newsize);
|
||||||
rc = ksmbd_vfs_truncate(work, NULL, fp, newsize);
|
rc = ksmbd_vfs_truncate(work, fp, newsize);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n",
|
ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n",
|
||||||
fp->filename, rc);
|
fp->filename, rc);
|
||||||
@ -5862,7 +5844,7 @@ int smb2_set_info(struct ksmbd_work *work)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
if (rc == -EACCES || rc == -EPERM)
|
if (rc == -EACCES || rc == -EPERM || rc == -EXDEV)
|
||||||
rsp->hdr.Status = STATUS_ACCESS_DENIED;
|
rsp->hdr.Status = STATUS_ACCESS_DENIED;
|
||||||
else if (rc == -EINVAL)
|
else if (rc == -EINVAL)
|
||||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||||
|
166
fs/ksmbd/vfs.c
166
fs/ksmbd/vfs.c
@ -19,6 +19,8 @@
|
|||||||
#include <linux/sched/xacct.h>
|
#include <linux/sched/xacct.h>
|
||||||
#include <linux/crc32c.h>
|
#include <linux/crc32c.h>
|
||||||
|
|
||||||
|
#include "../internal.h" /* for vfs_path_lookup */
|
||||||
|
|
||||||
#include "glob.h"
|
#include "glob.h"
|
||||||
#include "oplock.h"
|
#include "oplock.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
@ -44,7 +46,6 @@ static char *extract_last_component(char *path)
|
|||||||
p++;
|
p++;
|
||||||
} else {
|
} else {
|
||||||
p = NULL;
|
p = NULL;
|
||||||
pr_err("Invalid path %s\n", path);
|
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@ -155,7 +156,7 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
|
|||||||
/**
|
/**
|
||||||
* ksmbd_vfs_create() - vfs helper for smb create file
|
* ksmbd_vfs_create() - vfs helper for smb create file
|
||||||
* @work: work
|
* @work: work
|
||||||
* @name: file name
|
* @name: file name that is relative to share
|
||||||
* @mode: file create mode
|
* @mode: file create mode
|
||||||
*
|
*
|
||||||
* Return: 0 on success, otherwise error
|
* Return: 0 on success, otherwise error
|
||||||
@ -166,7 +167,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
|
|||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_NO_SYMLINKS);
|
dentry = ksmbd_vfs_kern_path_create(work, name,
|
||||||
|
LOOKUP_NO_SYMLINKS, &path);
|
||||||
if (IS_ERR(dentry)) {
|
if (IS_ERR(dentry)) {
|
||||||
err = PTR_ERR(dentry);
|
err = PTR_ERR(dentry);
|
||||||
if (err != -ENOENT)
|
if (err != -ENOENT)
|
||||||
@ -191,7 +193,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
|
|||||||
/**
|
/**
|
||||||
* ksmbd_vfs_mkdir() - vfs helper for smb create directory
|
* ksmbd_vfs_mkdir() - vfs helper for smb create directory
|
||||||
* @work: work
|
* @work: work
|
||||||
* @name: directory name
|
* @name: directory name that is relative to share
|
||||||
* @mode: directory create mode
|
* @mode: directory create mode
|
||||||
*
|
*
|
||||||
* Return: 0 on success, otherwise error
|
* Return: 0 on success, otherwise error
|
||||||
@ -203,8 +205,9 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
|
|||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
dentry = kern_path_create(AT_FDCWD, name, &path,
|
dentry = ksmbd_vfs_kern_path_create(work, name,
|
||||||
LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY);
|
LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
|
||||||
|
&path);
|
||||||
if (IS_ERR(dentry)) {
|
if (IS_ERR(dentry)) {
|
||||||
err = PTR_ERR(dentry);
|
err = PTR_ERR(dentry);
|
||||||
if (err != -EEXIST)
|
if (err != -EEXIST)
|
||||||
@ -579,7 +582,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink
|
* ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink
|
||||||
* @name: absolute directory or file name
|
* @name: directory or file name that is relative to share
|
||||||
*
|
*
|
||||||
* Return: 0 on success, otherwise error
|
* Return: 0 on success, otherwise error
|
||||||
*/
|
*/
|
||||||
@ -593,7 +596,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
|
|||||||
if (ksmbd_override_fsids(work))
|
if (ksmbd_override_fsids(work))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
err = kern_path(name, LOOKUP_NO_SYMLINKS, &path);
|
err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false);
|
||||||
if (err) {
|
if (err) {
|
||||||
ksmbd_debug(VFS, "can't get %s, err %d\n", name, err);
|
ksmbd_debug(VFS, "can't get %s, err %d\n", name, err);
|
||||||
ksmbd_revert_fsids(work);
|
ksmbd_revert_fsids(work);
|
||||||
@ -638,7 +641,7 @@ out_err:
|
|||||||
/**
|
/**
|
||||||
* ksmbd_vfs_link() - vfs helper for creating smb hardlink
|
* ksmbd_vfs_link() - vfs helper for creating smb hardlink
|
||||||
* @oldname: source file name
|
* @oldname: source file name
|
||||||
* @newname: hardlink name
|
* @newname: hardlink name that is relative to share
|
||||||
*
|
*
|
||||||
* Return: 0 on success, otherwise error
|
* Return: 0 on success, otherwise error
|
||||||
*/
|
*/
|
||||||
@ -659,8 +662,9 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
|
|||||||
goto out1;
|
goto out1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dentry = kern_path_create(AT_FDCWD, newname, &newpath,
|
dentry = ksmbd_vfs_kern_path_create(work, newname,
|
||||||
LOOKUP_NO_SYMLINKS | LOOKUP_REVAL);
|
LOOKUP_NO_SYMLINKS | LOOKUP_REVAL,
|
||||||
|
&newpath);
|
||||||
if (IS_ERR(dentry)) {
|
if (IS_ERR(dentry)) {
|
||||||
err = PTR_ERR(dentry);
|
err = PTR_ERR(dentry);
|
||||||
pr_err("path create err for %s, err %d\n", newname, err);
|
pr_err("path create err for %s, err %d\n", newname, err);
|
||||||
@ -781,14 +785,17 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
dst_name = extract_last_component(newname);
|
dst_name = extract_last_component(newname);
|
||||||
if (!dst_name)
|
if (!dst_name) {
|
||||||
return -EINVAL;
|
dst_name = newname;
|
||||||
|
newname = "";
|
||||||
|
}
|
||||||
|
|
||||||
src_dent_parent = dget_parent(fp->filp->f_path.dentry);
|
src_dent_parent = dget_parent(fp->filp->f_path.dentry);
|
||||||
src_dent = fp->filp->f_path.dentry;
|
src_dent = fp->filp->f_path.dentry;
|
||||||
|
|
||||||
err = kern_path(newname, LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
|
err = ksmbd_vfs_kern_path(work, newname,
|
||||||
&dst_path);
|
LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
|
||||||
|
&dst_path, false);
|
||||||
if (err) {
|
if (err) {
|
||||||
ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err);
|
ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err);
|
||||||
goto out;
|
goto out;
|
||||||
@ -834,61 +841,43 @@ out:
|
|||||||
/**
|
/**
|
||||||
* ksmbd_vfs_truncate() - vfs helper for smb file truncate
|
* ksmbd_vfs_truncate() - vfs helper for smb file truncate
|
||||||
* @work: work
|
* @work: work
|
||||||
* @name: old filename
|
|
||||||
* @fid: file id of old file
|
* @fid: file id of old file
|
||||||
* @size: truncate to given size
|
* @size: truncate to given size
|
||||||
*
|
*
|
||||||
* Return: 0 on success, otherwise error
|
* Return: 0 on success, otherwise error
|
||||||
*/
|
*/
|
||||||
int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name,
|
int ksmbd_vfs_truncate(struct ksmbd_work *work,
|
||||||
struct ksmbd_file *fp, loff_t size)
|
struct ksmbd_file *fp, loff_t size)
|
||||||
{
|
{
|
||||||
struct path path;
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
struct file *filp;
|
||||||
|
|
||||||
|
filp = fp->filp;
|
||||||
|
|
||||||
|
/* Do we need to break any of a levelII oplock? */
|
||||||
|
smb_break_all_levII_oplock(work, fp, 1);
|
||||||
|
|
||||||
|
if (!work->tcon->posix_extensions) {
|
||||||
|
struct inode *inode = file_inode(filp);
|
||||||
|
|
||||||
|
if (size < inode->i_size) {
|
||||||
|
err = check_lock_range(filp, size,
|
||||||
|
inode->i_size - 1, WRITE);
|
||||||
|
} else {
|
||||||
|
err = check_lock_range(filp, inode->i_size,
|
||||||
|
size - 1, WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
if (name) {
|
|
||||||
err = kern_path(name, LOOKUP_NO_SYMLINKS, &path);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("cannot get linux path for %s, err %d\n",
|
pr_err("failed due to lock\n");
|
||||||
name, err);
|
return -EAGAIN;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
err = vfs_truncate(&path, size);
|
|
||||||
if (err)
|
|
||||||
pr_err("truncate failed for %s err %d\n",
|
|
||||||
name, err);
|
|
||||||
path_put(&path);
|
|
||||||
} else {
|
|
||||||
struct file *filp;
|
|
||||||
|
|
||||||
filp = fp->filp;
|
|
||||||
|
|
||||||
/* Do we need to break any of a levelII oplock? */
|
|
||||||
smb_break_all_levII_oplock(work, fp, 1);
|
|
||||||
|
|
||||||
if (!work->tcon->posix_extensions) {
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
|
|
||||||
if (size < inode->i_size) {
|
|
||||||
err = check_lock_range(filp, size,
|
|
||||||
inode->i_size - 1, WRITE);
|
|
||||||
} else {
|
|
||||||
err = check_lock_range(filp, inode->i_size,
|
|
||||||
size - 1, WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
pr_err("failed due to lock\n");
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = vfs_truncate(&filp->f_path, size);
|
|
||||||
if (err)
|
|
||||||
pr_err("truncate failed for filename : %s err %d\n",
|
|
||||||
fp->filename, err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = vfs_truncate(&filp->f_path, size);
|
||||||
|
if (err)
|
||||||
|
pr_err("truncate failed for filename : %s err %d\n",
|
||||||
|
fp->filename, err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1206,22 +1195,25 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* ksmbd_vfs_kern_path() - lookup a file and get path info
|
* ksmbd_vfs_kern_path() - lookup a file and get path info
|
||||||
* @name: name of file for lookup
|
* @name: file path that is relative to share
|
||||||
* @flags: lookup flags
|
* @flags: lookup flags
|
||||||
* @path: if lookup succeed, return path info
|
* @path: if lookup succeed, return path info
|
||||||
* @caseless: caseless filename lookup
|
* @caseless: caseless filename lookup
|
||||||
*
|
*
|
||||||
* Return: 0 on success, otherwise error
|
* Return: 0 on success, otherwise error
|
||||||
*/
|
*/
|
||||||
int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
|
int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
|
||||||
bool caseless)
|
unsigned int flags, struct path *path, bool caseless)
|
||||||
{
|
{
|
||||||
|
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (name[0] != '/')
|
flags |= LOOKUP_BENEATH;
|
||||||
return -EINVAL;
|
err = vfs_path_lookup(share_conf->vfs_path.dentry,
|
||||||
|
share_conf->vfs_path.mnt,
|
||||||
err = kern_path(name, flags, path);
|
name,
|
||||||
|
flags,
|
||||||
|
path);
|
||||||
if (!err)
|
if (!err)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1235,11 +1227,10 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
path_len = strlen(filepath);
|
path_len = strlen(filepath);
|
||||||
remain_len = path_len - 1;
|
remain_len = path_len;
|
||||||
|
|
||||||
err = kern_path("/", flags, &parent);
|
parent = share_conf->vfs_path;
|
||||||
if (err)
|
path_get(&parent);
|
||||||
goto out;
|
|
||||||
|
|
||||||
while (d_can_lookup(parent.dentry)) {
|
while (d_can_lookup(parent.dentry)) {
|
||||||
char *filename = filepath + path_len - remain_len;
|
char *filename = filepath + path_len - remain_len;
|
||||||
@ -1252,21 +1243,21 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
|
|||||||
|
|
||||||
err = ksmbd_vfs_lookup_in_dir(&parent, filename,
|
err = ksmbd_vfs_lookup_in_dir(&parent, filename,
|
||||||
filename_len);
|
filename_len);
|
||||||
if (err) {
|
|
||||||
path_put(&parent);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
path_put(&parent);
|
path_put(&parent);
|
||||||
next[0] = '\0';
|
|
||||||
|
|
||||||
err = kern_path(filepath, flags, &parent);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (is_last) {
|
next[0] = '\0';
|
||||||
path->mnt = parent.mnt;
|
|
||||||
path->dentry = parent.dentry;
|
err = vfs_path_lookup(share_conf->vfs_path.dentry,
|
||||||
|
share_conf->vfs_path.mnt,
|
||||||
|
filepath,
|
||||||
|
flags,
|
||||||
|
&parent);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
else if (is_last) {
|
||||||
|
*path = parent;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1282,6 +1273,23 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
|
||||||
|
const char *name,
|
||||||
|
unsigned int flags,
|
||||||
|
struct path *path)
|
||||||
|
{
|
||||||
|
char *abs_name;
|
||||||
|
struct dentry *dent;
|
||||||
|
|
||||||
|
abs_name = convert_to_unix_name(work->tcon->share_conf, name);
|
||||||
|
if (!abs_name)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
dent = kern_path_create(AT_FDCWD, abs_name, path, flags);
|
||||||
|
kfree(abs_name);
|
||||||
|
return dent;
|
||||||
|
}
|
||||||
|
|
||||||
int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
|
int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
|
||||||
struct dentry *dentry)
|
struct dentry *dentry)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +126,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work,
|
|||||||
int ksmbd_vfs_getattr(struct path *path, struct kstat *stat);
|
int ksmbd_vfs_getattr(struct path *path, struct kstat *stat);
|
||||||
int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
|
int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||||
char *newname);
|
char *newname);
|
||||||
int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name,
|
int ksmbd_vfs_truncate(struct ksmbd_work *work,
|
||||||
struct ksmbd_file *fp, loff_t size);
|
struct ksmbd_file *fp, loff_t size);
|
||||||
struct srv_copychunk;
|
struct srv_copychunk;
|
||||||
int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
|
int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
|
||||||
@ -152,8 +152,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
|
|||||||
size_t *xattr_stream_name_size, int s_type);
|
size_t *xattr_stream_name_size, int s_type);
|
||||||
int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
|
int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
|
||||||
struct dentry *dentry, char *attr_name);
|
struct dentry *dentry, char *attr_name);
|
||||||
int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
|
int ksmbd_vfs_kern_path(struct ksmbd_work *work,
|
||||||
|
char *name, unsigned int flags, struct path *path,
|
||||||
bool caseless);
|
bool caseless);
|
||||||
|
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
|
||||||
|
const char *name,
|
||||||
|
unsigned int flags,
|
||||||
|
struct path *path);
|
||||||
int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
|
int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
|
||||||
void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
|
void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
|
||||||
int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||||
|
Loading…
Reference in New Issue
Block a user