mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 12:21:22 -05:00
Make some preparatory cleanups prior to running a script to organise the
function declarations within the fs/smb/client/ headers. These include:
(1) Remove "inline" from the dummy cifs_proc_init/clean() functions as
they are in a .c file.
(2) Move should_compress()'s kdoc comment to the .c file and remove kdoc
markers from the comments.
(3) Rename CIFS_ALLOW_INSECURE_LEGACY in #endif comments to have CONFIG_
on the front to allow the script to recognise it.
(4) Don't let comments have bare words at the left margin as that confused
the simplistic function detection code in the script.
(5) Adjust some argument lists so that when and if the cleanup script is
run they don't end up over 100 chars.
(6) Fix a few comments to have missing '*' added or the "*/" moved to
their own lines so that checkpatch doesn't moan over the cleanup
script patch.
(7) Move struct cifs_calc_sig_ctx to cifsglob.h.
(8) Remove some __KERNEL__ conditionals.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
cc: linux-cifs@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
440 lines
12 KiB
C
440 lines
12 KiB
C
// SPDX-License-Identifier: LGPL-2.1
|
|
/*
|
|
*
|
|
* Copyright (C) International Business Machines Corp., 2002, 2011
|
|
* Author(s): Steve French (sfrench@us.ibm.com),
|
|
* Pavel Shilovsky ((pshilovsky@samba.org) 2012
|
|
*
|
|
*/
|
|
#include <linux/fs.h>
|
|
#include <linux/filelock.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/pagemap.h>
|
|
#include <asm/div64.h>
|
|
#include "cifsfs.h"
|
|
#include "cifspdu.h"
|
|
#include "cifsglob.h"
|
|
#include "cifsproto.h"
|
|
#include "cifs_debug.h"
|
|
#include "cifs_fs_sb.h"
|
|
#include "cifs_unicode.h"
|
|
#include "fscache.h"
|
|
#include "smb2proto.h"
|
|
#include "../common/smb2status.h"
|
|
|
|
static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
|
{
|
|
struct smb2_err_rsp *err = iov->iov_base;
|
|
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
|
|
u32 len;
|
|
|
|
if (err->ErrorContextCount) {
|
|
struct smb2_error_context_rsp *p, *end;
|
|
|
|
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
|
|
ErrorContextData) +
|
|
sizeof(struct smb2_symlink_err_rsp));
|
|
if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err) + 1)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
p = (struct smb2_error_context_rsp *)err->ErrorData;
|
|
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
|
|
do {
|
|
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
|
|
sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
|
|
break;
|
|
}
|
|
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
|
|
__func__, le32_to_cpu(p->ErrorId));
|
|
|
|
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
|
|
p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
|
|
} while (p < end);
|
|
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
|
|
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
|
|
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
|
|
}
|
|
|
|
if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
|
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
|
|
sym = ERR_PTR(-EINVAL);
|
|
|
|
return sym;
|
|
}
|
|
|
|
int smb2_fix_symlink_target_type(char **target, bool directory, struct cifs_sb_info *cifs_sb)
|
|
{
|
|
char *buf;
|
|
int len;
|
|
|
|
/*
|
|
* POSIX server does not distinguish between symlinks to file and
|
|
* symlink directory. So nothing is needed to fix on the client side.
|
|
*/
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
|
|
return 0;
|
|
|
|
if (!*target)
|
|
return smb_EIO(smb_eio_trace_null_pointers);
|
|
|
|
len = strlen(*target);
|
|
if (!len)
|
|
return smb_EIO1(smb_eio_trace_sym_target_len, len);
|
|
|
|
/*
|
|
* If this is directory symlink and it does not have trailing slash then
|
|
* append it. Trailing slash simulates Windows/SMB behavior which do not
|
|
* allow resolving directory symlink to file.
|
|
*/
|
|
if (directory && (*target)[len-1] != '/') {
|
|
buf = krealloc(*target, len+2, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
buf[len] = '/';
|
|
buf[len+1] = '\0';
|
|
*target = buf;
|
|
len++;
|
|
}
|
|
|
|
/*
|
|
* If this is a file (non-directory) symlink and it points to path name
|
|
* with trailing slash then this is an invalid symlink because file name
|
|
* cannot contain slash character. File name with slash is invalid on
|
|
* both Windows and Linux systems. So return an error for such symlink.
|
|
*/
|
|
if (!directory && (*target)[len-1] == '/')
|
|
return smb_EIO(smb_eio_trace_sym_slash);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov,
|
|
const char *full_path, char **path)
|
|
{
|
|
struct smb2_symlink_err_rsp *sym;
|
|
unsigned int sub_offs, sub_len;
|
|
unsigned int print_offs, print_len;
|
|
|
|
if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
|
|
return -EINVAL;
|
|
|
|
sym = symlink_data(iov);
|
|
if (IS_ERR(sym))
|
|
return PTR_ERR(sym);
|
|
|
|
sub_len = le16_to_cpu(sym->SubstituteNameLength);
|
|
sub_offs = le16_to_cpu(sym->SubstituteNameOffset);
|
|
print_len = le16_to_cpu(sym->PrintNameLength);
|
|
print_offs = le16_to_cpu(sym->PrintNameOffset);
|
|
|
|
if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
|
|
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
|
|
return -EINVAL;
|
|
|
|
return smb2_parse_native_symlink(path,
|
|
(char *)sym->PathBuffer + sub_offs,
|
|
sub_len,
|
|
le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE,
|
|
full_path,
|
|
cifs_sb);
|
|
}
|
|
|
|
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
|
__u32 *oplock, void *buf)
|
|
{
|
|
int rc;
|
|
__le16 *smb2_path;
|
|
__u8 smb2_oplock;
|
|
struct cifs_open_info_data *data = buf;
|
|
struct smb2_file_all_info file_info = {};
|
|
struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
|
|
struct kvec err_iov = {};
|
|
int err_buftype = CIFS_NO_BUFFER;
|
|
struct cifs_fid *fid = oparms->fid;
|
|
struct network_resiliency_req nr_ioctl_req;
|
|
bool retry_without_read_attributes = false;
|
|
|
|
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
|
|
if (smb2_path == NULL)
|
|
return -ENOMEM;
|
|
|
|
/*
|
|
* GENERIC_READ, GENERIC_EXECUTE, GENERIC_ALL and MAXIMUM_ALLOWED
|
|
* contains also FILE_READ_ATTRIBUTES access right. So do not append
|
|
* FILE_READ_ATTRIBUTES when not needed and prevent calling code path
|
|
* for retry_without_read_attributes.
|
|
*/
|
|
if (!(oparms->desired_access & FILE_READ_ATTRIBUTES) &&
|
|
!(oparms->desired_access & GENERIC_READ) &&
|
|
!(oparms->desired_access & GENERIC_EXECUTE) &&
|
|
!(oparms->desired_access & GENERIC_ALL) &&
|
|
!(oparms->desired_access & MAXIMUM_ALLOWED)) {
|
|
oparms->desired_access |= FILE_READ_ATTRIBUTES;
|
|
retry_without_read_attributes = true;
|
|
}
|
|
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
|
|
|
|
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
|
|
&err_buftype);
|
|
if (rc == -EACCES && retry_without_read_attributes) {
|
|
oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
|
|
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
|
|
&err_buftype);
|
|
}
|
|
if (rc && data) {
|
|
struct smb2_hdr *hdr = err_iov.iov_base;
|
|
|
|
if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER))
|
|
goto out;
|
|
if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
|
|
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
|
|
oparms->path,
|
|
&data->symlink_target);
|
|
if (!rc) {
|
|
memset(smb2_data, 0, sizeof(*smb2_data));
|
|
oparms->create_options |= OPEN_REPARSE_POINT;
|
|
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
|
|
NULL, NULL, NULL);
|
|
oparms->create_options &= ~OPEN_REPARSE_POINT;
|
|
}
|
|
if (!rc) {
|
|
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
|
|
rc = smb2_fix_symlink_target_type(&data->symlink_target,
|
|
directory, oparms->cifs_sb);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rc)
|
|
goto out;
|
|
|
|
if (oparms->tcon->use_resilient) {
|
|
/* default timeout is 0, servers pick default (120 seconds) */
|
|
nr_ioctl_req.Timeout =
|
|
cpu_to_le32(oparms->tcon->handle_timeout);
|
|
nr_ioctl_req.Reserved = 0;
|
|
rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
|
|
fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
|
|
(char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
|
|
CIFSMaxBufSize, NULL, NULL /* no return info */);
|
|
if (rc == -EOPNOTSUPP) {
|
|
cifs_dbg(VFS,
|
|
"resiliency not supported by server, disabling\n");
|
|
oparms->tcon->use_resilient = false;
|
|
} else if (rc)
|
|
cifs_dbg(FYI, "error %d setting resiliency\n", rc);
|
|
|
|
rc = 0;
|
|
}
|
|
|
|
if (smb2_data) {
|
|
/* if open response does not have IndexNumber field - get it */
|
|
if (smb2_data->IndexNumber == 0) {
|
|
rc = SMB2_get_srv_num(xid, oparms->tcon,
|
|
fid->persistent_fid,
|
|
fid->volatile_fid,
|
|
&smb2_data->IndexNumber);
|
|
if (rc) {
|
|
/*
|
|
* let get_inode_info disable server inode
|
|
* numbers
|
|
*/
|
|
smb2_data->IndexNumber = 0;
|
|
rc = 0;
|
|
}
|
|
}
|
|
memcpy(&data->fi, smb2_data, sizeof(data->fi));
|
|
}
|
|
|
|
*oplock = smb2_oplock;
|
|
out:
|
|
free_rsp_buf(err_buftype, err_iov.iov_base);
|
|
kfree(smb2_path);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
|
|
const unsigned int xid)
|
|
{
|
|
int rc = 0, stored_rc;
|
|
unsigned int max_num, num = 0, max_buf;
|
|
struct smb2_lock_element *buf, *cur;
|
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
|
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
|
|
struct cifsLockInfo *li, *tmp;
|
|
__u64 length = 1 + flock->fl_end - flock->fl_start;
|
|
LIST_HEAD(tmp_llist);
|
|
|
|
/*
|
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
|
* and check it before using.
|
|
*/
|
|
max_buf = tcon->ses->server->maxBuf;
|
|
if (max_buf < sizeof(struct smb2_lock_element))
|
|
return -EINVAL;
|
|
|
|
BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE);
|
|
max_buf = min_t(unsigned int, max_buf, PAGE_SIZE);
|
|
max_num = max_buf / sizeof(struct smb2_lock_element);
|
|
buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
cur = buf;
|
|
|
|
cifs_down_write(&cinode->lock_sem);
|
|
list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
|
|
if (flock->fl_start > li->offset ||
|
|
(flock->fl_start + length) <
|
|
(li->offset + li->length))
|
|
continue;
|
|
if (current->tgid != li->pid)
|
|
/*
|
|
* flock and OFD lock are associated with an open
|
|
* file description, not the process.
|
|
*/
|
|
if (!(flock->c.flc_flags & (FL_FLOCK | FL_OFDLCK)))
|
|
continue;
|
|
if (cinode->can_cache_brlcks) {
|
|
/*
|
|
* We can cache brlock requests - simply remove a lock
|
|
* from the file's list.
|
|
*/
|
|
list_del(&li->llist);
|
|
cifs_del_lock_waiters(li);
|
|
kfree(li);
|
|
continue;
|
|
}
|
|
cur->Length = cpu_to_le64(li->length);
|
|
cur->Offset = cpu_to_le64(li->offset);
|
|
cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK);
|
|
/*
|
|
* We need to save a lock here to let us add it again to the
|
|
* file's list if the unlock range request fails on the server.
|
|
*/
|
|
list_move(&li->llist, &tmp_llist);
|
|
if (++num == max_num) {
|
|
stored_rc = smb2_lockv(xid, tcon,
|
|
cfile->fid.persistent_fid,
|
|
cfile->fid.volatile_fid,
|
|
current->tgid, num, buf);
|
|
if (stored_rc) {
|
|
/*
|
|
* We failed on the unlock range request - add
|
|
* all locks from the tmp list to the head of
|
|
* the file's list.
|
|
*/
|
|
cifs_move_llist(&tmp_llist,
|
|
&cfile->llist->locks);
|
|
rc = stored_rc;
|
|
} else
|
|
/*
|
|
* The unlock range request succeed - free the
|
|
* tmp list.
|
|
*/
|
|
cifs_free_llist(&tmp_llist);
|
|
cur = buf;
|
|
num = 0;
|
|
} else
|
|
cur++;
|
|
}
|
|
if (num) {
|
|
stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid,
|
|
cfile->fid.volatile_fid, current->tgid,
|
|
num, buf);
|
|
if (stored_rc) {
|
|
cifs_move_llist(&tmp_llist, &cfile->llist->locks);
|
|
rc = stored_rc;
|
|
} else
|
|
cifs_free_llist(&tmp_llist);
|
|
}
|
|
up_write(&cinode->lock_sem);
|
|
|
|
kfree(buf);
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid,
|
|
struct smb2_lock_element *buf, unsigned int max_num)
|
|
{
|
|
int rc = 0, stored_rc;
|
|
struct cifsFileInfo *cfile = fdlocks->cfile;
|
|
struct cifsLockInfo *li;
|
|
unsigned int num = 0;
|
|
struct smb2_lock_element *cur = buf;
|
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
|
|
|
list_for_each_entry(li, &fdlocks->locks, llist) {
|
|
cur->Length = cpu_to_le64(li->length);
|
|
cur->Offset = cpu_to_le64(li->offset);
|
|
cur->Flags = cpu_to_le32(li->type |
|
|
SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
|
|
if (++num == max_num) {
|
|
stored_rc = smb2_lockv(xid, tcon,
|
|
cfile->fid.persistent_fid,
|
|
cfile->fid.volatile_fid,
|
|
current->tgid, num, buf);
|
|
if (stored_rc)
|
|
rc = stored_rc;
|
|
cur = buf;
|
|
num = 0;
|
|
} else
|
|
cur++;
|
|
}
|
|
if (num) {
|
|
stored_rc = smb2_lockv(xid, tcon,
|
|
cfile->fid.persistent_fid,
|
|
cfile->fid.volatile_fid,
|
|
current->tgid, num, buf);
|
|
if (stored_rc)
|
|
rc = stored_rc;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
|
|
{
|
|
int rc = 0, stored_rc;
|
|
unsigned int xid;
|
|
unsigned int max_num, max_buf;
|
|
struct smb2_lock_element *buf;
|
|
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
|
|
struct cifs_fid_locks *fdlocks;
|
|
|
|
xid = get_xid();
|
|
|
|
/*
|
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
|
* and check it for zero before using.
|
|
*/
|
|
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
|
|
if (max_buf < sizeof(struct smb2_lock_element)) {
|
|
free_xid(xid);
|
|
return -EINVAL;
|
|
}
|
|
|
|
BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE);
|
|
max_buf = min_t(unsigned int, max_buf, PAGE_SIZE);
|
|
max_num = max_buf / sizeof(struct smb2_lock_element);
|
|
buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL);
|
|
if (!buf) {
|
|
free_xid(xid);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
list_for_each_entry(fdlocks, &cinode->llist, llist) {
|
|
stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num);
|
|
if (stored_rc)
|
|
rc = stored_rc;
|
|
}
|
|
|
|
kfree(buf);
|
|
free_xid(xid);
|
|
return rc;
|
|
}
|