2a38e12053
Remove the CONFIG_CIFS_SMB2 ifdef and Kconfig option since they must always be on now. For various security reasons, SMB3 and later are STRONGLY preferred over CIFS and older dialects, and SMB3 (and later) will now be the default dialects so we do not want to allow them to be ifdeffed out. In the longer term, we may be able to make older CIFS support disableable in Kconfig with a new set of #ifdef, but we always want SMB3 and later support enabled. Signed-off-by: Steven French <smfrench@gmail.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
236 lines
6.4 KiB
C
236 lines
6.4 KiB
C
/*
|
|
* fs/cifs/ioctl.c
|
|
*
|
|
* vfs operations that deal with io control
|
|
*
|
|
* Copyright (C) International Business Machines Corp., 2005,2013
|
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
* the GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/pagemap.h>
|
|
#include "cifspdu.h"
|
|
#include "cifsglob.h"
|
|
#include "cifsproto.h"
|
|
#include "cifs_debug.h"
|
|
#include "cifsfs.h"
|
|
#include "cifs_ioctl.h"
|
|
#include <linux/btrfs.h>
|
|
|
|
static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
|
|
unsigned long srcfd)
|
|
{
|
|
int rc;
|
|
struct fd src_file;
|
|
struct inode *src_inode;
|
|
|
|
cifs_dbg(FYI, "ioctl copychunk range\n");
|
|
/* the destination must be opened for writing */
|
|
if (!(dst_file->f_mode & FMODE_WRITE)) {
|
|
cifs_dbg(FYI, "file target not open for write\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check if target volume is readonly and take reference */
|
|
rc = mnt_want_write_file(dst_file);
|
|
if (rc) {
|
|
cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
src_file = fdget(srcfd);
|
|
if (!src_file.file) {
|
|
rc = -EBADF;
|
|
goto out_drop_write;
|
|
}
|
|
|
|
if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
|
|
rc = -EBADF;
|
|
cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
|
|
goto out_fput;
|
|
}
|
|
|
|
src_inode = file_inode(src_file.file);
|
|
rc = -EINVAL;
|
|
if (S_ISDIR(src_inode->i_mode))
|
|
goto out_fput;
|
|
|
|
rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0,
|
|
src_inode->i_size, 0);
|
|
if (rc > 0)
|
|
rc = 0;
|
|
out_fput:
|
|
fdput(src_file);
|
|
out_drop_write:
|
|
mnt_drop_write_file(dst_file);
|
|
return rc;
|
|
}
|
|
|
|
static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
|
|
void __user *arg)
|
|
{
|
|
int rc = 0;
|
|
struct smb_mnt_fs_info *fsinf;
|
|
|
|
fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);
|
|
if (fsinf == NULL)
|
|
return -ENOMEM;
|
|
|
|
fsinf->version = 1;
|
|
fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
|
|
fsinf->device_characteristics =
|
|
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
|
|
fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
|
|
fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);
|
|
fsinf->max_path_component =
|
|
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
|
|
fsinf->vol_serial_number = tcon->vol_serial_number;
|
|
fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);
|
|
fsinf->share_flags = tcon->share_flags;
|
|
fsinf->share_caps = le32_to_cpu(tcon->capabilities);
|
|
fsinf->sector_flags = tcon->ss_flags;
|
|
fsinf->optimal_sector_size = tcon->perf_sector_size;
|
|
fsinf->max_bytes_chunk = tcon->max_bytes_chunk;
|
|
fsinf->maximal_access = tcon->maximal_access;
|
|
fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
|
|
|
if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))
|
|
rc = -EFAULT;
|
|
|
|
kfree(fsinf);
|
|
return rc;
|
|
}
|
|
|
|
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
|
{
|
|
struct inode *inode = file_inode(filep);
|
|
int rc = -ENOTTY; /* strange error - but the precedent */
|
|
unsigned int xid;
|
|
struct cifs_sb_info *cifs_sb;
|
|
struct cifsFileInfo *pSMBFile = filep->private_data;
|
|
struct cifs_tcon *tcon;
|
|
__u64 ExtAttrBits = 0;
|
|
__u64 caps;
|
|
|
|
xid = get_xid();
|
|
|
|
cifs_sb = CIFS_SB(inode->i_sb);
|
|
cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
|
|
switch (command) {
|
|
case FS_IOC_GETFLAGS:
|
|
if (pSMBFile == NULL)
|
|
break;
|
|
tcon = tlink_tcon(pSMBFile->tlink);
|
|
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
|
#ifdef CONFIG_CIFS_POSIX
|
|
if (CIFS_UNIX_EXTATTR_CAP & caps) {
|
|
__u64 ExtAttrMask = 0;
|
|
rc = CIFSGetExtAttr(xid, tcon,
|
|
pSMBFile->fid.netfid,
|
|
&ExtAttrBits, &ExtAttrMask);
|
|
if (rc == 0)
|
|
rc = put_user(ExtAttrBits &
|
|
FS_FL_USER_VISIBLE,
|
|
(int __user *)arg);
|
|
if (rc != EOPNOTSUPP)
|
|
break;
|
|
}
|
|
#endif /* CONFIG_CIFS_POSIX */
|
|
rc = 0;
|
|
if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
|
|
/* add in the compressed bit */
|
|
ExtAttrBits = FS_COMPR_FL;
|
|
rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
|
|
(int __user *)arg);
|
|
}
|
|
break;
|
|
case FS_IOC_SETFLAGS:
|
|
if (pSMBFile == NULL)
|
|
break;
|
|
tcon = tlink_tcon(pSMBFile->tlink);
|
|
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
|
|
|
if (get_user(ExtAttrBits, (int __user *)arg)) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* if (CIFS_UNIX_EXTATTR_CAP & caps)
|
|
* rc = CIFSSetExtAttr(xid, tcon,
|
|
* pSMBFile->fid.netfid,
|
|
* extAttrBits,
|
|
* &ExtAttrMask);
|
|
* if (rc != EOPNOTSUPP)
|
|
* break;
|
|
*/
|
|
|
|
/* Currently only flag we can set is compressed flag */
|
|
if ((ExtAttrBits & FS_COMPR_FL) == 0)
|
|
break;
|
|
|
|
/* Try to set compress flag */
|
|
if (tcon->ses->server->ops->set_compression) {
|
|
rc = tcon->ses->server->ops->set_compression(
|
|
xid, tcon, pSMBFile);
|
|
cifs_dbg(FYI, "set compress flag rc %d\n", rc);
|
|
}
|
|
break;
|
|
case CIFS_IOC_COPYCHUNK_FILE:
|
|
rc = cifs_ioctl_copychunk(xid, filep, arg);
|
|
break;
|
|
case CIFS_IOC_SET_INTEGRITY:
|
|
if (pSMBFile == NULL)
|
|
break;
|
|
tcon = tlink_tcon(pSMBFile->tlink);
|
|
if (tcon->ses->server->ops->set_integrity)
|
|
rc = tcon->ses->server->ops->set_integrity(xid,
|
|
tcon, pSMBFile);
|
|
else
|
|
rc = -EOPNOTSUPP;
|
|
break;
|
|
case CIFS_IOC_GET_MNT_INFO:
|
|
if (pSMBFile == NULL)
|
|
break;
|
|
tcon = tlink_tcon(pSMBFile->tlink);
|
|
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
|
|
break;
|
|
case CIFS_ENUMERATE_SNAPSHOTS:
|
|
if (pSMBFile == NULL)
|
|
break;
|
|
if (arg == 0) {
|
|
rc = -EINVAL;
|
|
goto cifs_ioc_exit;
|
|
}
|
|
tcon = tlink_tcon(pSMBFile->tlink);
|
|
if (tcon->ses->server->ops->enum_snapshots)
|
|
rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
|
|
pSMBFile, (void __user *)arg);
|
|
else
|
|
rc = -EOPNOTSUPP;
|
|
break;
|
|
default:
|
|
cifs_dbg(FYI, "unsupported ioctl\n");
|
|
break;
|
|
}
|
|
cifs_ioc_exit:
|
|
free_xid(xid);
|
|
return rc;
|
|
}
|