Commit 11c7d0b9 authored by Pavel Shilovsky's avatar Pavel Shilovsky

Update 4.4 sources from stable (v4.4.14)

parent f0aec17d
...@@ -50,7 +50,7 @@ void cifs_vfs_err(const char *fmt, ...) ...@@ -50,7 +50,7 @@ void cifs_vfs_err(const char *fmt, ...)
vaf.fmt = fmt; vaf.fmt = fmt;
vaf.va = &args; vaf.va = &args;
pr_err("CIFS VFS: %pV", &vaf); pr_err_ratelimited("CIFS VFS: %pV", &vaf);
va_end(args); va_end(args);
} }
......
...@@ -51,14 +51,13 @@ __printf(1, 2) void cifs_vfs_err(const char *fmt, ...); ...@@ -51,14 +51,13 @@ __printf(1, 2) void cifs_vfs_err(const char *fmt, ...);
/* information message: e.g., configuration, major event */ /* information message: e.g., configuration, major event */
#define cifs_dbg(type, fmt, ...) \ #define cifs_dbg(type, fmt, ...) \
do { \ do { \
if (type == FYI) { \ if (type == FYI && cifsFYI & CIFS_INFO) { \
if (cifsFYI & CIFS_INFO) { \ pr_debug_ratelimited("%s: " \
pr_debug("%s: " fmt, __FILE__, ##__VA_ARGS__); \ fmt, __FILE__, ##__VA_ARGS__); \
} \
} else if (type == VFS) { \ } else if (type == VFS) { \
cifs_vfs_err(fmt, ##__VA_ARGS__); \ cifs_vfs_err(fmt, ##__VA_ARGS__); \
} else if (type == NOISY && type != 0) { \ } else if (type == NOISY && type != 0) { \
pr_debug(fmt, ##__VA_ARGS__); \ pr_debug_ratelimited(fmt, ##__VA_ARGS__); \
} \ } \
} while (0) } while (0)
......
...@@ -714,7 +714,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -714,7 +714,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
if (!ses->auth_key.response) { if (!ses->auth_key.response) {
rc = ENOMEM; rc = -ENOMEM;
ses->auth_key.len = 0; ses->auth_key.len = 0;
goto setup_ntlmv2_rsp_ret; goto setup_ntlmv2_rsp_ret;
} }
......
...@@ -31,19 +31,15 @@ ...@@ -31,19 +31,15 @@
* so that it will fit. We use hash_64 to convert the value to 31 bits, and * so that it will fit. We use hash_64 to convert the value to 31 bits, and
* then add 1, to ensure that we don't end up with a 0 as the value. * then add 1, to ensure that we don't end up with a 0 as the value.
*/ */
#if BITS_PER_LONG == 64
static inline ino_t static inline ino_t
cifs_uniqueid_to_ino_t(u64 fileid) cifs_uniqueid_to_ino_t(u64 fileid)
{ {
if ((sizeof(ino_t)) < (sizeof(u64)))
return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1;
return (ino_t)fileid; return (ino_t)fileid;
} }
#else
static inline ino_t
cifs_uniqueid_to_ino_t(u64 fileid)
{
return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1;
}
#endif
extern struct file_system_type cifs_fs_type; extern struct file_system_type cifs_fs_type;
extern const struct address_space_operations cifs_addr_ops; extern const struct address_space_operations cifs_addr_ops;
......
...@@ -1403,11 +1403,10 @@ openRetry: ...@@ -1403,11 +1403,10 @@ openRetry:
* current bigbuf. * current bigbuf.
*/ */
static int static int
cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) discard_remaining_data(struct TCP_Server_Info *server)
{ {
unsigned int rfclen = get_rfc1002_length(server->smallbuf); unsigned int rfclen = get_rfc1002_length(server->smallbuf);
int remaining = rfclen + 4 - server->total_read; int remaining = rfclen + 4 - server->total_read;
struct cifs_readdata *rdata = mid->callback_data;
while (remaining > 0) { while (remaining > 0) {
int length; int length;
...@@ -1421,10 +1420,20 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1421,10 +1420,20 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
remaining -= length; remaining -= length;
} }
dequeue_mid(mid, rdata->result);
return 0; return 0;
} }
static int
cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
int length;
struct cifs_readdata *rdata = mid->callback_data;
length = discard_remaining_data(server);
dequeue_mid(mid, rdata->result);
return length;
}
int int
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
...@@ -1453,6 +1462,12 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1453,6 +1462,12 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return length; return length;
server->total_read += length; server->total_read += length;
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, 0)) {
discard_remaining_data(server);
return -1;
}
/* Was the SMB read successful? */ /* Was the SMB read successful? */
rdata->result = server->ops->map_error(buf, false); rdata->result = server->ops->map_error(buf, false);
if (rdata->result != 0) { if (rdata->result != 0) {
......
...@@ -369,7 +369,6 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -369,7 +369,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->session_key.response = NULL; server->session_key.response = NULL;
server->session_key.len = 0; server->session_key.len = 0;
server->lstrp = jiffies; server->lstrp = jiffies;
mutex_unlock(&server->srv_mutex);
/* mark submitted MIDs for retry and issue callback */ /* mark submitted MIDs for retry and issue callback */
INIT_LIST_HEAD(&retry_list); INIT_LIST_HEAD(&retry_list);
...@@ -382,6 +381,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -382,6 +381,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
list_move(&mid_entry->qhead, &retry_list); list_move(&mid_entry->qhead, &retry_list);
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
list_for_each_safe(tmp, tmp2, &retry_list) { list_for_each_safe(tmp, tmp2, &retry_list) {
......
...@@ -1836,11 +1836,11 @@ cifs_invalidate_mapping(struct inode *inode) ...@@ -1836,11 +1836,11 @@ cifs_invalidate_mapping(struct inode *inode)
* @word: long word containing the bit lock * @word: long word containing the bit lock
*/ */
static int static int
cifs_wait_bit_killable(struct wait_bit_key *key) cifs_wait_bit_killable(struct wait_bit_key *key, int mode)
{ {
if (fatal_signal_pending(current))
return -ERESTARTSYS;
freezable_schedule_unsafe(); freezable_schedule_unsafe();
if (signal_pending_state(mode, current))
return -ERESTARTSYS;
return 0; return 0;
} }
......
...@@ -847,6 +847,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) ...@@ -847,6 +847,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
* if buggy server returns . and .. late do we want to * if buggy server returns . and .. late do we want to
* check for that here? * check for that here?
*/ */
*tmp_buf = 0;
rc = cifs_filldir(current_entry, file, ctx, rc = cifs_filldir(current_entry, file, ctx,
tmp_buf, max_len); tmp_buf, max_len);
if (rc) { if (rc) {
......
...@@ -400,19 +400,27 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -400,19 +400,27 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
sec_blob->LmChallengeResponse.MaximumLength = 0; sec_blob->LmChallengeResponse.MaximumLength = 0;
sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer);
rc = setup_ntlmv2_rsp(ses, nls_cp); if (ses->user_name != NULL) {
if (rc) { rc = setup_ntlmv2_rsp(ses, nls_cp);
cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); if (rc) {
goto setup_ntlmv2_ret; cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
goto setup_ntlmv2_ret;
}
memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
ses->auth_key.len - CIFS_SESS_KEY_SIZE);
tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
sec_blob->NtChallengeResponse.Length =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
sec_blob->NtChallengeResponse.MaximumLength =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
} else {
/*
* don't send an NT Response for anonymous access
*/
sec_blob->NtChallengeResponse.Length = 0;
sec_blob->NtChallengeResponse.MaximumLength = 0;
} }
memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
ses->auth_key.len - CIFS_SESS_KEY_SIZE);
tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
sec_blob->NtChallengeResponse.Length =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
sec_blob->NtChallengeResponse.MaximumLength =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
if (ses->domainName == NULL) { if (ses->domainName == NULL) {
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
...@@ -670,20 +678,24 @@ sess_auth_lanman(struct sess_data *sess_data) ...@@ -670,20 +678,24 @@ sess_auth_lanman(struct sess_data *sess_data)
pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
/* no capabilities flags in old lanman negotiation */ if (ses->user_name != NULL) {
pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); /* no capabilities flags in old lanman negotiation */
pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
/* Calculate hash with password and copy into bcc_ptr.
* Encryption Key (stored as in cryptkey) gets used if the /* Calculate hash with password and copy into bcc_ptr.
* security mode bit in Negottiate Protocol response states * Encryption Key (stored as in cryptkey) gets used if the
* to use challenge/response method (i.e. Password bit is 1). * security mode bit in Negottiate Protocol response states
*/ * to use challenge/response method (i.e. Password bit is 1).
rc = calc_lanman_hash(ses->password, ses->server->cryptkey, */
ses->server->sec_mode & SECMODE_PW_ENCRYPT ? rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
true : false, lnm_session_key); ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
true : false, lnm_session_key);
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
bcc_ptr += CIFS_AUTH_RESP_SIZE; memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
bcc_ptr += CIFS_AUTH_RESP_SIZE;
} else {
pSMB->old_req.PasswordLength = 0;
}
/* /*
* can not sign if LANMAN negotiated so no need * can not sign if LANMAN negotiated so no need
...@@ -769,26 +781,31 @@ sess_auth_ntlm(struct sess_data *sess_data) ...@@ -769,26 +781,31 @@ sess_auth_ntlm(struct sess_data *sess_data)
capabilities = cifs_ssetup_hdr(ses, pSMB); capabilities = cifs_ssetup_hdr(ses, pSMB);
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
pSMB->req_no_secext.CaseInsensitivePasswordLength = if (ses->user_name != NULL) {
cpu_to_le16(CIFS_AUTH_RESP_SIZE); pSMB->req_no_secext.CaseInsensitivePasswordLength =
pSMB->req_no_secext.CaseSensitivePasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
cpu_to_le16(CIFS_AUTH_RESP_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(CIFS_AUTH_RESP_SIZE);
/* calculate ntlm response and session key */
rc = setup_ntlm_response(ses, sess_data->nls_cp); /* calculate ntlm response and session key */
if (rc) { rc = setup_ntlm_response(ses, sess_data->nls_cp);
cifs_dbg(VFS, "Error %d during NTLM authentication\n", if (rc) {
rc); cifs_dbg(VFS, "Error %d during NTLM authentication\n",
goto out; rc);
} goto out;
}
/* copy ntlm response */ /* copy ntlm response */
memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
CIFS_AUTH_RESP_SIZE); CIFS_AUTH_RESP_SIZE);
bcc_ptr += CIFS_AUTH_RESP_SIZE; bcc_ptr += CIFS_AUTH_RESP_SIZE;
memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
CIFS_AUTH_RESP_SIZE); CIFS_AUTH_RESP_SIZE);
bcc_ptr += CIFS_AUTH_RESP_SIZE; bcc_ptr += CIFS_AUTH_RESP_SIZE;
} else {
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
}
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
/* unicode strings must be word aligned */ /* unicode strings must be word aligned */
...@@ -878,22 +895,26 @@ sess_auth_ntlmv2(struct sess_data *sess_data) ...@@ -878,22 +895,26 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
/* LM2 password would be here if we supported it */ /* LM2 password would be here if we supported it */
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
/* calculate nlmv2 response and session key */ if (ses->user_name != NULL) {
rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); /* calculate nlmv2 response and session key */
if (rc) { rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); if (rc) {
goto out; cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
} goto out;
}
memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
ses->auth_key.len - CIFS_SESS_KEY_SIZE); ses->auth_key.len - CIFS_SESS_KEY_SIZE);
bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
/* set case sensitive password length after tilen may get /* set case sensitive password length after tilen may get
* assigned, tilen is 0 otherwise. * assigned, tilen is 0 otherwise.
*/ */
pSMB->req_no_secext.CaseSensitivePasswordLength = pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
} else {
pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
}
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
if (sess_data->iov[0].iov_len % 2) { if (sess_data->iov[0].iov_len % 2) {
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#define SMB2_OP_DELETE 7 #define SMB2_OP_DELETE 7
#define SMB2_OP_HARDLINK 8 #define SMB2_OP_HARDLINK 8
#define SMB2_OP_SET_EOF 9 #define SMB2_OP_SET_EOF 9
#define SMB2_OP_RMDIR 10
/* Used when constructing chained read requests. */ /* Used when constructing chained read requests. */
#define CHAINED_REQUEST 1 #define CHAINED_REQUEST 1
......
...@@ -81,6 +81,10 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -81,6 +81,10 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
* SMB2_open() call. * SMB2_open() call.
*/ */
break; break;
case SMB2_OP_RMDIR:
tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid,
fid.volatile_fid);
break;
case SMB2_OP_RENAME: case SMB2_OP_RENAME:
tmprc = SMB2_rename(xid, tcon, fid.persistent_fid, tmprc = SMB2_rename(xid, tcon, fid.persistent_fid,
fid.volatile_fid, (__le16 *)data); fid.volatile_fid, (__le16 *)data);
...@@ -192,8 +196,8 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -192,8 +196,8 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE, CREATE_NOT_FILE,
NULL, SMB2_OP_DELETE); NULL, SMB2_OP_RMDIR);
} }
int int
......
...@@ -1109,21 +1109,25 @@ parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, ...@@ -1109,21 +1109,25 @@ parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp,
{ {
char *data_offset; char *data_offset;
struct create_context *cc; struct create_context *cc;
unsigned int next = 0; unsigned int next;
unsigned int remaining;
char *name; char *name;
data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset); data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
remaining = le32_to_cpu(rsp->CreateContextsLength);
cc = (struct create_context *)data_offset; cc = (struct create_context *)data_offset;
do { while (remaining >= sizeof(struct create_context)) {
cc = (struct create_context *)((char *)cc + next);
name = le16_to_cpu(cc->NameOffset) + (char *)cc; name = le16_to_cpu(cc->NameOffset) + (char *)cc;
if (le16_to_cpu(cc->NameLength) != 4 || if (le16_to_cpu(cc->NameLength) == 4 &&
strncmp(name, "RqLs", 4)) { strncmp(name, "RqLs", 4) == 0)
next = le32_to_cpu(cc->Next); return server->ops->parse_lease_buf(cc, epoch);
continue;
} next = le32_to_cpu(cc->Next);
return server->ops->parse_lease_buf(cc, epoch); if (!next)
} while (next != 0); break;
remaining -= next;
cc = (struct create_context *)((char *)cc + next);
}
return 0; return 0;
} }
...@@ -2573,6 +2577,22 @@ SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2573,6 +2577,22 @@ SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
} }
int int
SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid)
{
__u8 delete_pending = 1;
void *data;
unsigned int size;
data = &delete_pending;
size = 1; /* sizeof __u8 */
return send_set_info(xid, tcon, persistent_fid, volatile_fid,
current->tgid, FILE_DISPOSITION_INFORMATION, 1, &data,
&size);
}
int
SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, __le16 *target_file) u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
{ {
......
...@@ -140,6 +140,8 @@ extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -140,6 +140,8 @@ extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u64 persistent_fid, u64 volatile_fid,
__le16 *target_file); __le16 *target_file);
extern int SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid);
extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u64 persistent_fid, u64 volatile_fid,
__le16 *target_file); __le16 *target_file);
......
...@@ -576,14 +576,16 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, ...@@ -576,14 +576,16 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
cifs_in_send_dec(server); cifs_in_send_dec(server);
cifs_save_when_sent(mid); cifs_save_when_sent(mid);
if (rc < 0) if (rc < 0) {
server->sequence_number -= 2; server->sequence_number -= 2;
cifs_delete_mid(mid);
}
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
if (rc == 0) if (rc == 0)
return 0; return 0;
cifs_delete_mid(mid);
add_credits_and_wake_if(server, credits, optype); add_credits_and_wake_if(server, credits, optype);
return rc; return rc;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment