Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS updates from Steve French: "Includes a couple of fixes, plus changes to make multiplex identifiers easier to read and correlate with network traces, and a set of enhancements for SMB3 dialect. Also adds support for per-file compression for both cifs and smb2/smb3 ("chattr +c filename). Should have at least one other merge request ready by next week with some new SMB3 security features and copy offload support" * 'for-linus' of git://git.samba.org/sfrench/cifs-2.6: Query network adapter info at mount time for debugging Fix unused variable warning when CIFS POSIX disabled Allow setting per-file compression via CIFS protocol Query File System Alignment Query device characteristics at mount time from server on SMB2/3 not just on cifs mounts cifs: Send a logoff request before removing a smb session cifs: Make big endian multiplex ID sequences monotonic on the wire cifs: Remove redundant multiplex identifier check from check_smb_hdr() Query file system attributes from server on SMB2, not just cifs, mounts Allow setting per-file compression via SMB2/3 Fix corrupt SMB2 ioctl requests
This commit is contained in:
commit
8efdf2b759
@ -278,6 +278,8 @@ struct smb_version_operations {
|
||||
/* set attributes */
|
||||
int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
|
||||
const unsigned int);
|
||||
int (*set_compression)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifsFileInfo *);
|
||||
/* check if we can send an echo or nor */
|
||||
bool (*can_echo)(struct TCP_Server_Info *);
|
||||
/* send echo request */
|
||||
@ -620,11 +622,34 @@ set_credits(struct TCP_Server_Info *server, const int val)
|
||||
}
|
||||
|
||||
static inline __u64
|
||||
get_next_mid(struct TCP_Server_Info *server)
|
||||
get_next_mid64(struct TCP_Server_Info *server)
|
||||
{
|
||||
return server->ops->get_next_mid(server);
|
||||
}
|
||||
|
||||
static inline __le16
|
||||
get_next_mid(struct TCP_Server_Info *server)
|
||||
{
|
||||
__u16 mid = get_next_mid64(server);
|
||||
/*
|
||||
* The value in the SMB header should be little endian for easy
|
||||
* on-the-wire decoding.
|
||||
*/
|
||||
return cpu_to_le16(mid);
|
||||
}
|
||||
|
||||
static inline __u16
|
||||
get_mid(const struct smb_hdr *smb)
|
||||
{
|
||||
return le16_to_cpu(smb->Mid);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
compare_mid(__u16 mid, const struct smb_hdr *smb)
|
||||
{
|
||||
return mid == le16_to_cpu(smb->Mid);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the server supports very large reads and writes via POSIX extensions,
|
||||
* we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
|
||||
@ -828,6 +853,8 @@ struct cifs_tcon {
|
||||
__u32 maximal_access;
|
||||
__u32 vol_serial_number;
|
||||
__le64 vol_create_time;
|
||||
__u32 ss_flags; /* sector size flags */
|
||||
__u32 perf_sector_size; /* best sector size for perf */
|
||||
#endif /* CONFIG_CIFS_SMB2 */
|
||||
#ifdef CONFIG_CIFS_FSCACHE
|
||||
u64 resource_id; /* server resource id */
|
||||
|
@ -428,7 +428,7 @@ struct smb_hdr {
|
||||
__u16 Tid;
|
||||
__le16 Pid;
|
||||
__u16 Uid;
|
||||
__u16 Mid;
|
||||
__le16 Mid;
|
||||
__u8 WordCount;
|
||||
} __attribute__((packed));
|
||||
|
||||
@ -1352,6 +1352,35 @@ typedef struct smb_com_transaction_ioctl_req {
|
||||
__u8 Data[1];
|
||||
} __attribute__((packed)) TRANSACT_IOCTL_REQ;
|
||||
|
||||
typedef struct smb_com_transaction_compr_ioctl_req {
|
||||
struct smb_hdr hdr; /* wct = 23 */
|
||||
__u8 MaxSetupCount;
|
||||
__u16 Reserved;
|
||||
__le32 TotalParameterCount;
|
||||
__le32 TotalDataCount;
|
||||
__le32 MaxParameterCount;
|
||||
__le32 MaxDataCount;
|
||||
__le32 ParameterCount;
|
||||
__le32 ParameterOffset;
|
||||
__le32 DataCount;
|
||||
__le32 DataOffset;
|
||||
__u8 SetupCount; /* four setup words follow subcommand */
|
||||
/* SNIA spec incorrectly included spurious pad here */
|
||||
__le16 SubCommand; /* 2 = IOCTL/FSCTL */
|
||||
__le32 FunctionCode;
|
||||
__u16 Fid;
|
||||
__u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */
|
||||
__u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */
|
||||
__le16 ByteCount;
|
||||
__u8 Pad[3];
|
||||
__le16 compression_state; /* See below for valid flags */
|
||||
} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ;
|
||||
|
||||
/* compression state flags */
|
||||
#define COMPRESSION_FORMAT_NONE 0x0000
|
||||
#define COMPRESSION_FORMAT_DEFAULT 0x0001
|
||||
#define COMPRESSION_FORMAT_LZNT1 0x0002
|
||||
|
||||
typedef struct smb_com_transaction_ioctl_rsp {
|
||||
struct smb_hdr hdr; /* wct = 19 */
|
||||
__u8 Reserved[3];
|
||||
@ -2215,6 +2244,9 @@ typedef struct {
|
||||
__le32 DeviceCharacteristics;
|
||||
} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
|
||||
|
||||
/* minimum includes first three fields, and empty FS Name */
|
||||
#define MIN_FS_ATTR_INFO_SIZE 12
|
||||
|
||||
typedef struct {
|
||||
__le32 Attributes;
|
||||
__le32 MaxPathNameComponentLength;
|
||||
|
@ -360,6 +360,8 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
|
||||
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u16 fid, char **symlinkinfo,
|
||||
const struct nls_table *nls_codepage);
|
||||
extern int CIFSSMB_set_compression(const unsigned int xid,
|
||||
struct cifs_tcon *tcon, __u16 fid);
|
||||
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *fileName, const int disposition,
|
||||
const int access_flags, const int omode,
|
||||
|
@ -3199,6 +3199,60 @@ qreparse_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u16 fid)
|
||||
{
|
||||
int rc = 0;
|
||||
int bytes_returned;
|
||||
struct smb_com_transaction_compr_ioctl_req *pSMB;
|
||||
struct smb_com_transaction_ioctl_rsp *pSMBr;
|
||||
|
||||
cifs_dbg(FYI, "Set compression for %u\n", fid);
|
||||
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
|
||||
(void **) &pSMBr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
|
||||
|
||||
pSMB->TotalParameterCount = 0;
|
||||
pSMB->TotalDataCount = __constant_cpu_to_le32(2);
|
||||
pSMB->MaxParameterCount = 0;
|
||||
pSMB->MaxDataCount = 0;
|
||||
pSMB->MaxSetupCount = 4;
|
||||
pSMB->Reserved = 0;
|
||||
pSMB->ParameterOffset = 0;
|
||||
pSMB->DataCount = __constant_cpu_to_le32(2);
|
||||
pSMB->DataOffset =
|
||||
cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req,
|
||||
compression_state) - 4); /* 84 */
|
||||
pSMB->SetupCount = 4;
|
||||
pSMB->SubCommand = __constant_cpu_to_le16(NT_TRANSACT_IOCTL);
|
||||
pSMB->ParameterCount = 0;
|
||||
pSMB->FunctionCode = __constant_cpu_to_le32(FSCTL_SET_COMPRESSION);
|
||||
pSMB->IsFsctl = 1; /* FSCTL */
|
||||
pSMB->IsRootFlag = 0;
|
||||
pSMB->Fid = fid; /* file handle always le */
|
||||
/* 3 byte pad, followed by 2 byte compress state */
|
||||
pSMB->ByteCount = __constant_cpu_to_le16(5);
|
||||
inc_rfc1001_len(pSMB, 5);
|
||||
|
||||
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
|
||||
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
|
||||
if (rc)
|
||||
cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc);
|
||||
|
||||
cifs_buf_release(pSMB);
|
||||
|
||||
/*
|
||||
* Note: On -EAGAIN error only caller can retry on handle based calls
|
||||
* since file handle passed in no longer valid.
|
||||
*/
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_CIFS_POSIX
|
||||
|
||||
/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
|
||||
|
@ -2242,6 +2242,8 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
if (ses->status == CifsExiting)
|
||||
continue;
|
||||
if (!match_session(ses, vol))
|
||||
continue;
|
||||
++ses->ses_count;
|
||||
@ -2255,24 +2257,37 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
|
||||
static void
|
||||
cifs_put_smb_ses(struct cifs_ses *ses)
|
||||
{
|
||||
unsigned int xid;
|
||||
unsigned int rc, xid;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
|
||||
cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ses->status == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
if (--ses->ses_count > 0) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
if (ses->status == CifsGood)
|
||||
ses->status = CifsExiting;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (ses->status == CifsExiting && server->ops->logoff) {
|
||||
xid = get_xid();
|
||||
rc = server->ops->logoff(xid, ses);
|
||||
if (rc)
|
||||
cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
|
||||
__func__, rc);
|
||||
_free_xid(xid);
|
||||
}
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_del_init(&ses->smb_ses_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (ses->status == CifsGood && server->ops->logoff) {
|
||||
xid = get_xid();
|
||||
server->ops->logoff(xid, ses);
|
||||
_free_xid(xid);
|
||||
}
|
||||
sesInfoFree(ses);
|
||||
cifs_put_tcp_session(server);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* vfs operations that deal with io control
|
||||
*
|
||||
* Copyright (C) International Business Machines Corp., 2005,2007
|
||||
* 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
|
||||
@ -34,13 +34,10 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
int rc = -ENOTTY; /* strange error - but the precedent */
|
||||
unsigned int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
#ifdef CONFIG_CIFS_POSIX
|
||||
struct cifsFileInfo *pSMBFile = filep->private_data;
|
||||
struct cifs_tcon *tcon;
|
||||
__u64 ExtAttrBits = 0;
|
||||
__u64 ExtAttrMask = 0;
|
||||
__u64 caps;
|
||||
#endif /* CONFIG_CIFS_POSIX */
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
@ -49,13 +46,14 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
|
||||
switch (command) {
|
||||
#ifdef CONFIG_CIFS_POSIX
|
||||
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);
|
||||
@ -63,29 +61,50 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||
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 (CIFS_UNIX_EXTATTR_CAP & caps) {
|
||||
if (get_user(ExtAttrBits, (int __user *)arg)) {
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* rc = CIFSGetExtAttr(xid, tcon,
|
||||
* pSMBFile->fid.netfid,
|
||||
* extAttrBits,
|
||||
* &ExtAttrMask);
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
cifs_dbg(FYI, "set flags not implemented yet\n");
|
||||
break;
|
||||
#endif /* CONFIG_CIFS_POSIX */
|
||||
default:
|
||||
cifs_dbg(FYI, "unsupported ioctl\n");
|
||||
break;
|
||||
|
@ -278,7 +278,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
||||
}
|
||||
|
||||
static int
|
||||
check_smb_hdr(struct smb_hdr *smb, __u16 mid)
|
||||
check_smb_hdr(struct smb_hdr *smb)
|
||||
{
|
||||
/* does it have the right SMB "signature" ? */
|
||||
if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) {
|
||||
@ -287,13 +287,6 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Make sure that message ids match */
|
||||
if (mid != smb->Mid) {
|
||||
cifs_dbg(VFS, "Mids do not match. received=%u expected=%u\n",
|
||||
smb->Mid, mid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if it's a response then accept */
|
||||
if (smb->Flags & SMBFLG_RESPONSE)
|
||||
return 0;
|
||||
@ -302,7 +295,8 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)
|
||||
if (smb->Command == SMB_COM_LOCKING_ANDX)
|
||||
return 0;
|
||||
|
||||
cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", smb->Mid);
|
||||
cifs_dbg(VFS, "Server sent request, not response. mid=%u\n",
|
||||
get_mid(smb));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -310,7 +304,6 @@ int
|
||||
checkSMB(char *buf, unsigned int total_read)
|
||||
{
|
||||
struct smb_hdr *smb = (struct smb_hdr *)buf;
|
||||
__u16 mid = smb->Mid;
|
||||
__u32 rfclen = be32_to_cpu(smb->smb_buf_length);
|
||||
__u32 clc_len; /* calculated length */
|
||||
cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
|
||||
@ -348,7 +341,7 @@ checkSMB(char *buf, unsigned int total_read)
|
||||
}
|
||||
|
||||
/* otherwise, there is enough to get to the BCC */
|
||||
if (check_smb_hdr(smb, mid))
|
||||
if (check_smb_hdr(smb))
|
||||
return -EIO;
|
||||
clc_len = smbCalcSize(smb);
|
||||
|
||||
@ -359,6 +352,7 @@ checkSMB(char *buf, unsigned int total_read)
|
||||
}
|
||||
|
||||
if (4 + rfclen != clc_len) {
|
||||
__u16 mid = get_mid(smb);
|
||||
/* check if bcc wrapped around for large read responses */
|
||||
if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
|
||||
/* check if lengths match mod 64K */
|
||||
@ -366,11 +360,11 @@ checkSMB(char *buf, unsigned int total_read)
|
||||
return 0; /* bcc wrapped */
|
||||
}
|
||||
cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n",
|
||||
clc_len, 4 + rfclen, smb->Mid);
|
||||
clc_len, 4 + rfclen, mid);
|
||||
|
||||
if (4 + rfclen < clc_len) {
|
||||
cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
|
||||
rfclen, smb->Mid);
|
||||
rfclen, mid);
|
||||
return -EIO;
|
||||
} else if (rfclen > clc_len + 512) {
|
||||
/*
|
||||
@ -383,7 +377,7 @@ checkSMB(char *buf, unsigned int total_read)
|
||||
* data to 512 bytes.
|
||||
*/
|
||||
cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n",
|
||||
rfclen, smb->Mid);
|
||||
rfclen, mid);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf,
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n",
|
||||
in_buf->Mid, rc);
|
||||
get_mid(in_buf), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -101,7 +101,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
|
||||
if (mid->mid == buf->Mid &&
|
||||
if (compare_mid(mid->mid, buf) &&
|
||||
mid->mid_state == MID_REQUEST_SUBMITTED &&
|
||||
le16_to_cpu(mid->command) == buf->Command) {
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
@ -806,6 +806,13 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile)
|
||||
{
|
||||
return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid);
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *path, struct cifs_sb_info *cifs_sb,
|
||||
@ -956,6 +963,7 @@ struct smb_version_operations smb1_operations = {
|
||||
.set_path_size = CIFSSMBSetEOF,
|
||||
.set_file_size = CIFSSMBSetFileSize,
|
||||
.set_file_info = smb_set_file_info,
|
||||
.set_compression = cifs_set_compression,
|
||||
.echo = CIFSSMBEcho,
|
||||
.mkdir = CIFSSMBMkDir,
|
||||
.mkdir_setinfo = cifs_mkdir_setinfo,
|
||||
|
@ -209,6 +209,94 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
return rsize;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
static int
|
||||
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
{
|
||||
int rc;
|
||||
unsigned int ret_data_len = 0;
|
||||
struct network_interface_info_ioctl_rsp *out_buf;
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
|
||||
NULL /* no data input */, 0 /* no data input */,
|
||||
(char **)&out_buf, &ret_data_len);
|
||||
|
||||
if ((rc == 0) && (ret_data_len > 0)) {
|
||||
/* Dump info on first interface */
|
||||
cifs_dbg(FYI, "Adapter Capability 0x%x\t",
|
||||
le32_to_cpu(out_buf->Capability));
|
||||
cifs_dbg(FYI, "Link Speed %lld\n",
|
||||
le64_to_cpu(out_buf->LinkSpeed));
|
||||
} else
|
||||
cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* STATS2 */
|
||||
|
||||
static void
|
||||
smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
{
|
||||
int rc;
|
||||
__le16 srch_path = 0; /* Null - open root of share */
|
||||
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
struct cifs_open_parms oparms;
|
||||
struct cifs_fid fid;
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
||||
oparms.disposition = FILE_OPEN;
|
||||
oparms.create_options = 0;
|
||||
oparms.fid = &fid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
|
||||
if (rc)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
SMB3_request_interfaces(xid, tcon);
|
||||
#endif /* STATS2 */
|
||||
|
||||
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
FS_ATTRIBUTE_INFORMATION);
|
||||
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
FS_DEVICE_INFORMATION);
|
||||
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */
|
||||
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
{
|
||||
int rc;
|
||||
__le16 srch_path = 0; /* Null - open root of share */
|
||||
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
struct cifs_open_parms oparms;
|
||||
struct cifs_fid fid;
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
||||
oparms.disposition = FILE_OPEN;
|
||||
oparms.create_options = 0;
|
||||
oparms.fid = &fid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
|
||||
if (rc)
|
||||
return;
|
||||
|
||||
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
FS_ATTRIBUTE_INFORMATION);
|
||||
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
||||
FS_DEVICE_INFORMATION);
|
||||
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path)
|
||||
@ -304,7 +392,19 @@ smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon)
|
||||
seq_puts(m, " ASYMMETRIC,");
|
||||
if (tcon->capabilities == 0)
|
||||
seq_puts(m, " None");
|
||||
if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE)
|
||||
seq_puts(m, " Aligned,");
|
||||
if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE)
|
||||
seq_puts(m, " Partition Aligned,");
|
||||
if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY)
|
||||
seq_puts(m, " SSD,");
|
||||
if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED)
|
||||
seq_puts(m, " TRIM-support,");
|
||||
|
||||
seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags);
|
||||
if (tcon->perf_sector_size)
|
||||
seq_printf(m, "\tOptimal sector size: 0x%x",
|
||||
tcon->perf_sector_size);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -445,6 +545,14 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile)
|
||||
{
|
||||
return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid);
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *path, struct cifs_sb_info *cifs_sb,
|
||||
@ -865,6 +973,7 @@ struct smb_version_operations smb20_operations = {
|
||||
.logoff = SMB2_logoff,
|
||||
.tree_connect = SMB2_tcon,
|
||||
.tree_disconnect = SMB2_tdis,
|
||||
.qfs_tcon = smb2_qfs_tcon,
|
||||
.is_path_accessible = smb2_is_path_accessible,
|
||||
.can_echo = smb2_can_echo,
|
||||
.echo = SMB2_echo,
|
||||
@ -874,6 +983,7 @@ struct smb_version_operations smb20_operations = {
|
||||
.set_path_size = smb2_set_path_size,
|
||||
.set_file_size = smb2_set_file_size,
|
||||
.set_file_info = smb2_set_file_info,
|
||||
.set_compression = smb2_set_compression,
|
||||
.mkdir = smb2_mkdir,
|
||||
.mkdir_setinfo = smb2_mkdir_setinfo,
|
||||
.rmdir = smb2_rmdir,
|
||||
@ -936,6 +1046,7 @@ struct smb_version_operations smb21_operations = {
|
||||
.logoff = SMB2_logoff,
|
||||
.tree_connect = SMB2_tcon,
|
||||
.tree_disconnect = SMB2_tdis,
|
||||
.qfs_tcon = smb2_qfs_tcon,
|
||||
.is_path_accessible = smb2_is_path_accessible,
|
||||
.can_echo = smb2_can_echo,
|
||||
.echo = SMB2_echo,
|
||||
@ -945,6 +1056,7 @@ struct smb_version_operations smb21_operations = {
|
||||
.set_path_size = smb2_set_path_size,
|
||||
.set_file_size = smb2_set_file_size,
|
||||
.set_file_info = smb2_set_file_info,
|
||||
.set_compression = smb2_set_compression,
|
||||
.mkdir = smb2_mkdir,
|
||||
.mkdir_setinfo = smb2_mkdir_setinfo,
|
||||
.rmdir = smb2_rmdir,
|
||||
@ -1008,6 +1120,7 @@ struct smb_version_operations smb30_operations = {
|
||||
.logoff = SMB2_logoff,
|
||||
.tree_connect = SMB2_tcon,
|
||||
.tree_disconnect = SMB2_tdis,
|
||||
.qfs_tcon = smb3_qfs_tcon,
|
||||
.is_path_accessible = smb2_is_path_accessible,
|
||||
.can_echo = smb2_can_echo,
|
||||
.echo = SMB2_echo,
|
||||
@ -1017,6 +1130,7 @@ struct smb_version_operations smb30_operations = {
|
||||
.set_path_size = smb2_set_path_size,
|
||||
.set_file_size = smb2_set_file_size,
|
||||
.set_file_info = smb2_set_file_info,
|
||||
.set_compression = smb2_set_compression,
|
||||
.mkdir = smb2_mkdir,
|
||||
.mkdir_setinfo = smb2_mkdir_setinfo,
|
||||
.rmdir = smb2_rmdir,
|
||||
|
@ -1137,6 +1137,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
|
||||
cifs_dbg(FYI, "SMB2 IOCTL\n");
|
||||
|
||||
*out_data = NULL;
|
||||
/* zero out returned data len, in case of error */
|
||||
if (plen)
|
||||
*plen = 0;
|
||||
@ -1182,11 +1183,23 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
req->Flags = 0;
|
||||
|
||||
iov[0].iov_base = (char *)req;
|
||||
/* 4 for rfc1002 length field */
|
||||
iov[0].iov_len = get_rfc1002_length(req) + 4;
|
||||
|
||||
if (indatalen)
|
||||
inc_rfc1001_len(req, indatalen);
|
||||
/*
|
||||
* If no input data, the size of ioctl struct in
|
||||
* protocol spec still includes a 1 byte data buffer,
|
||||
* but if input data passed to ioctl, we do not
|
||||
* want to double count this, so we do not send
|
||||
* the dummy one byte of data in iovec[0] if sending
|
||||
* input data (in iovec[1]). We also must add 4 bytes
|
||||
* in first iovec to allow for rfc1002 length field.
|
||||
*/
|
||||
|
||||
if (indatalen) {
|
||||
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
||||
inc_rfc1001_len(req, indatalen - 1);
|
||||
} else
|
||||
iov[0].iov_len = get_rfc1002_length(req) + 4;
|
||||
|
||||
|
||||
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
|
||||
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
|
||||
@ -1234,6 +1247,33 @@ ioctl_exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Individual callers to ioctl worker function follow
|
||||
*/
|
||||
|
||||
int
|
||||
SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid)
|
||||
{
|
||||
int rc;
|
||||
char *res_key = NULL;
|
||||
struct compress_ioctl fsctl_input;
|
||||
char *ret_data = NULL;
|
||||
|
||||
fsctl_input.CompressionState =
|
||||
__constant_cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
|
||||
FSCTL_SET_COMPRESSION, true /* is_fsctl */,
|
||||
(char *)&fsctl_input /* data input */,
|
||||
2 /* in data len */, &ret_data /* out data */, NULL);
|
||||
|
||||
cifs_dbg(FYI, "set compression rc %d\n", rc);
|
||||
kfree(res_key);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid)
|
||||
@ -2299,7 +2339,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto qinf_exit;
|
||||
goto qfsinf_exit;
|
||||
}
|
||||
rsp = (struct smb2_query_info_rsp *)iov.iov_base;
|
||||
|
||||
@ -2311,7 +2351,70 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
if (!rc)
|
||||
copy_fs_info_to_kstatfs(info, fsdata);
|
||||
|
||||
qinf_exit:
|
||||
qfsinf_exit:
|
||||
free_rsp_buf(resp_buftype, iov.iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid, int level)
|
||||
{
|
||||
struct smb2_query_info_rsp *rsp = NULL;
|
||||
struct kvec iov;
|
||||
int rc = 0;
|
||||
int resp_buftype, max_len, min_len;
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
unsigned int rsp_len, offset;
|
||||
|
||||
if (level == FS_DEVICE_INFORMATION) {
|
||||
max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
|
||||
min_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
|
||||
} else if (level == FS_ATTRIBUTE_INFORMATION) {
|
||||
max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
|
||||
min_len = MIN_FS_ATTR_INFO_SIZE;
|
||||
} else if (level == FS_SECTOR_SIZE_INFORMATION) {
|
||||
max_len = sizeof(struct smb3_fs_ss_info);
|
||||
min_len = sizeof(struct smb3_fs_ss_info);
|
||||
} else {
|
||||
cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = build_qfs_info_req(&iov, tcon, level, max_len,
|
||||
persistent_fid, volatile_fid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto qfsattr_exit;
|
||||
}
|
||||
rsp = (struct smb2_query_info_rsp *)iov.iov_base;
|
||||
|
||||
rsp_len = le32_to_cpu(rsp->OutputBufferLength);
|
||||
offset = le16_to_cpu(rsp->OutputBufferOffset);
|
||||
rc = validate_buf(offset, rsp_len, &rsp->hdr, min_len);
|
||||
if (rc)
|
||||
goto qfsattr_exit;
|
||||
|
||||
if (level == FS_ATTRIBUTE_INFORMATION)
|
||||
memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset
|
||||
+ (char *)&rsp->hdr, min_t(unsigned int,
|
||||
rsp_len, max_len));
|
||||
else if (level == FS_DEVICE_INFORMATION)
|
||||
memcpy(&tcon->fsDevInfo, 4 /* RFC1001 len */ + offset
|
||||
+ (char *)&rsp->hdr, sizeof(FILE_SYSTEM_DEVICE_INFO));
|
||||
else if (level == FS_SECTOR_SIZE_INFORMATION) {
|
||||
struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *)
|
||||
(4 /* RFC1001 len */ + offset + (char *)&rsp->hdr);
|
||||
tcon->ss_flags = le32_to_cpu(ss_info->Flags);
|
||||
tcon->perf_sector_size =
|
||||
le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf);
|
||||
}
|
||||
|
||||
qfsattr_exit:
|
||||
free_rsp_buf(resp_buftype, iov.iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
@ -569,6 +569,10 @@ struct network_interface_info_ioctl_rsp {
|
||||
|
||||
#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
|
||||
|
||||
struct compress_ioctl {
|
||||
__le16 CompressionState; /* See cifspdu.h for possible flag values */
|
||||
} __packed;
|
||||
|
||||
struct smb2_ioctl_req {
|
||||
struct smb2_hdr hdr;
|
||||
__le16 StructureSize; /* Must be 57 */
|
||||
@ -584,7 +588,7 @@ struct smb2_ioctl_req {
|
||||
__le32 MaxOutputResponse;
|
||||
__le32 Flags;
|
||||
__u32 Reserved2;
|
||||
char Buffer[0];
|
||||
__u8 Buffer[0];
|
||||
} __packed;
|
||||
|
||||
struct smb2_ioctl_rsp {
|
||||
@ -870,14 +874,16 @@ struct smb2_lease_ack {
|
||||
|
||||
/* File System Information Classes */
|
||||
#define FS_VOLUME_INFORMATION 1 /* Query */
|
||||
#define FS_LABEL_INFORMATION 2 /* Set */
|
||||
#define FS_LABEL_INFORMATION 2 /* Local only */
|
||||
#define FS_SIZE_INFORMATION 3 /* Query */
|
||||
#define FS_DEVICE_INFORMATION 4 /* Query */
|
||||
#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
|
||||
#define FS_CONTROL_INFORMATION 6 /* Query, Set */
|
||||
#define FS_FULL_SIZE_INFORMATION 7 /* Query */
|
||||
#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
|
||||
#define FS_DRIVER_PATH_INFORMATION 9 /* Query */
|
||||
#define FS_DRIVER_PATH_INFORMATION 9 /* Local only */
|
||||
#define FS_VOLUME_FLAGS_INFORMATION 10 /* Local only */
|
||||
#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */
|
||||
|
||||
struct smb2_fs_full_size_info {
|
||||
__le64 TotalAllocationUnits;
|
||||
@ -887,6 +893,22 @@ struct smb2_fs_full_size_info {
|
||||
__le32 BytesPerSector;
|
||||
} __packed;
|
||||
|
||||
#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001
|
||||
#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002
|
||||
#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004
|
||||
#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008
|
||||
|
||||
/* sector size info struct */
|
||||
struct smb3_fs_ss_info {
|
||||
__le32 LogicalBytesPerSector;
|
||||
__le32 PhysicalBytesPerSectorForAtomicity;
|
||||
__le32 PhysicalBytesPerSectorForPerf;
|
||||
__le32 FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
|
||||
__le32 Flags;
|
||||
__le32 ByteOffsetForSectorAlignment;
|
||||
__le32 ByteOffsetForPartitionAlignment;
|
||||
} __packed;
|
||||
|
||||
/* partial list of QUERY INFO levels */
|
||||
#define FILE_DIRECTORY_INFORMATION 1
|
||||
#define FILE_FULL_DIRECTORY_INFORMATION 2
|
||||
|
@ -142,12 +142,16 @@ extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid,
|
||||
FILE_BASIC_INFO *buf);
|
||||
extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid);
|
||||
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const u64 persistent_fid, const u64 volatile_fid,
|
||||
const __u8 oplock_level);
|
||||
extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_file_id, u64 volatile_file_id,
|
||||
struct kstatfs *FSData);
|
||||
extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_file_id, u64 volatile_file_id, int lvl);
|
||||
extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const __u64 persist_fid, const __u64 volatile_fid,
|
||||
const __u32 pid, const __u64 length, const __u64 offset,
|
||||
|
@ -466,7 +466,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
static inline void
|
||||
smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr)
|
||||
{
|
||||
hdr->MessageId = get_next_mid(server);
|
||||
hdr->MessageId = get_next_mid64(server);
|
||||
}
|
||||
|
||||
static struct mid_q_entry *
|
||||
@ -516,13 +516,19 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf,
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ses->status != CifsGood) {
|
||||
/* check if SMB2 session is bad because we are setting it up */
|
||||
if (ses->status == CifsNew) {
|
||||
if ((buf->Command != SMB2_SESSION_SETUP) &&
|
||||
(buf->Command != SMB2_NEGOTIATE))
|
||||
return -EAGAIN;
|
||||
/* else ok - we are setting up session */
|
||||
}
|
||||
|
||||
if (ses->status == CifsExiting) {
|
||||
if (buf->Command != SMB2_LOGOFF)
|
||||
return -EAGAIN;
|
||||
/* else ok - we are shutting down the session */
|
||||
}
|
||||
|
||||
*mid = smb2_mid_entry_alloc(buf, ses->server);
|
||||
if (*mid == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -58,7 +58,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
|
||||
return temp;
|
||||
else {
|
||||
memset(temp, 0, sizeof(struct mid_q_entry));
|
||||
temp->mid = smb_buffer->Mid; /* always LE */
|
||||
temp->mid = get_mid(smb_buffer);
|
||||
temp->pid = current->pid;
|
||||
temp->command = cpu_to_le16(smb_buffer->Command);
|
||||
cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
|
||||
@ -431,13 +431,20 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ses->status != CifsGood) {
|
||||
/* check if SMB session is bad because we are setting it up */
|
||||
if (ses->status == CifsNew) {
|
||||
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
|
||||
(in_buf->Command != SMB_COM_NEGOTIATE))
|
||||
return -EAGAIN;
|
||||
/* else ok - we are setting up session */
|
||||
}
|
||||
|
||||
if (ses->status == CifsExiting) {
|
||||
/* check if SMB session is bad because we are setting it up */
|
||||
if (in_buf->Command != SMB_COM_LOGOFF_ANDX)
|
||||
return -EAGAIN;
|
||||
/* else ok - we are shutting down session */
|
||||
}
|
||||
|
||||
*ppmidQ = AllocMidQEntry(in_buf, ses->server);
|
||||
if (*ppmidQ == NULL)
|
||||
return -ENOMEM;
|
||||
|
Loading…
Reference in New Issue
Block a user