diff options
Diffstat (limited to 'fs/smb/server')
52 files changed, 5246 insertions, 6270 deletions
diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index 7055cb5d2880..2775162c535c 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -1,18 +1,19 @@ config SMB_SERVER - tristate "SMB3 server support (EXPERIMENTAL)" + tristate "SMB3 server support" depends on INET depends on MULTIUSER depends on FILE_LOCKING select NLS select NLS_UTF8 + select NLS_UCS2_UTILS select CRYPTO - select CRYPTO_MD5 - select CRYPTO_HMAC select CRYPTO_ECB + select CRYPTO_LIB_ARC4 select CRYPTO_LIB_DES - select CRYPTO_SHA256 + select CRYPTO_LIB_MD5 + select CRYPTO_LIB_SHA256 + select CRYPTO_LIB_SHA512 select CRYPTO_CMAC - select CRYPTO_SHA512 select CRYPTO_AEAD2 select CRYPTO_CCM select CRYPTO_GCM @@ -69,4 +70,4 @@ config SMB_SERVER_CHECK_CAP_NET_ADMIN config SMB_SERVER_KERBEROS5 bool "Support for Kerberos 5" depends on SMB_SERVER - default n + default y diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c index cc6384f79675..5c4c5121fece 100644 --- a/fs/smb/server/asn1.c +++ b/fs/smb/server/asn1.c @@ -104,7 +104,7 @@ int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, oid_len + ntlmssp_len) * 2 + neg_result_len + oid_len + ntlmssp_len; - buf = kmalloc(total_len, GFP_KERNEL); + buf = kmalloc(total_len, KSMBD_DEFAULT_GFP); if (!buf) return -ENOMEM; @@ -140,7 +140,7 @@ int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + neg_result_len; - buf = kmalloc(total_len, GFP_KERNEL); + buf = kmalloc(total_len, KSMBD_DEFAULT_GFP); if (!buf) return -ENOMEM; @@ -214,12 +214,15 @@ static int ksmbd_neg_token_alloc(void *context, size_t hdrlen, { struct ksmbd_conn *conn = context; - conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!vlen) + return -EINVAL; + + conn->mechToken = kmemdup_nul(value, vlen, KSMBD_DEFAULT_GFP); if (!conn->mechToken) return -ENOMEM; - memcpy(conn->mechToken, value, vlen); - conn->mechToken[vlen] = '\0'; + conn->mechTokenLen = (unsigned int)vlen; + return 0; } diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 5e5e120edcc2..f2767c4b5132 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -13,6 +13,8 @@ #include <linux/xattr.h> #include <crypto/hash.h> #include <crypto/aead.h> +#include <crypto/md5.h> +#include <crypto/sha2.h> #include <linux/random.h> #include <linux/scatterlist.h> @@ -20,6 +22,7 @@ #include "glob.h" #include <linux/fips.h> +#include <crypto/arc4.h> #include <crypto/des.h> #include "server.h" @@ -29,7 +32,6 @@ #include "mgmt/user_config.h" #include "crypto_ctx.h" #include "transport_ipc.h" -#include "../common/arc4.h" /* * Fixed format data defining GSS header and fixed string @@ -69,89 +71,20 @@ void ksmbd_copy_gss_neg_header(void *buf) memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); } -/** - * ksmbd_gen_sess_key() - function to generate session key - * @sess: session of connection - * @hash: source hash value to be used for find session key - * @hmac: source hmac value to be used for finding session key - * - */ -static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, - char *hmac) -{ - struct ksmbd_crypto_ctx *ctx; - int rc; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); - goto out; - } - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), - hmac, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); - goto out; - } - -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, char *ntlmv2_hash, char *dname) { int ret, len, conv_len; wchar_t *domain = NULL; __le16 *uniname = NULL; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); - return -ENOMEM; - } + struct hmac_md5_ctx ctx; - ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - user_passkey(sess->user), + hmac_md5_init_usingrawkey(&ctx, user_passkey(sess->user), CIFS_ENCPWD_SIZE); - if (ret) { - ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); - goto out; - } - - ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (ret) { - ksmbd_debug(AUTH, "could not init hmacmd5\n"); - goto out; - } /* convert user_name to unicode */ len = strlen(user_name(sess->user)); - uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + uniname = kzalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP); if (!uniname) { ret = -ENOMEM; goto out; @@ -165,17 +98,11 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, } UniStrupr(uniname); - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)uniname, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with user\n"); - goto out; - } + hmac_md5_update(&ctx, (const u8 *)uniname, UNICODE_LEN(conv_len)); /* Convert domain name or conn name to unicode and uppercase */ len = strlen(dname); - domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + domain = kzalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP); if (!domain) { ret = -ENOMEM; goto out; @@ -188,30 +115,23 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, goto out; } - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)domain, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with domain\n"); - goto out; - } - - ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); - if (ret) - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + hmac_md5_update(&ctx, (const u8 *)domain, UNICODE_LEN(conv_len)); + hmac_md5_final(&ctx, ntlmv2_hash); + ret = 0; out: kfree(uniname); kfree(domain); - ksmbd_release_crypto_ctx(ctx); return ret; } /** * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler - * @sess: session of connection + * @conn: connection + * @sess: session of connection * @ntlmv2: NTLMv2 challenge response * @blen: NTLMv2 blob length * @domain_name: domain name + * @cryptkey: session crypto key * * Return: 0 on success, error number on error */ @@ -221,80 +141,41 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - struct ksmbd_crypto_ctx *ctx = NULL; - char *construct = NULL; - int rc, len; - - rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); - if (rc) { - ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); - goto out; - } - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init hmacmd5\n"); - goto out; - } + struct hmac_md5_ctx ctx; + int rc; - len = CIFS_CRYPTO_KEY_SIZE + blen; - construct = kzalloc(len, GFP_KERNEL); - if (!construct) { - rc = -ENOMEM; - goto out; + if (fips_enabled) { + ksmbd_debug(AUTH, "NTLMv2 support is disabled due to FIPS\n"); + return -EOPNOTSUPP; } - memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); - memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); if (rc) { - ksmbd_debug(AUTH, "Could not update with response\n"); - goto out; + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + return rc; } - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); - goto out; - } - ksmbd_release_crypto_ctx(ctx); - ctx = NULL; + hmac_md5_init_usingrawkey(&ctx, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + hmac_md5_update(&ctx, cryptkey, CIFS_CRYPTO_KEY_SIZE); + hmac_md5_update(&ctx, (const u8 *)&ntlmv2->blob_signature, blen); + hmac_md5_final(&ctx, ntlmv2_rsp); - rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate sess key\n"); - goto out; - } + /* Generate the session key */ + hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, + ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, + sess->sess_key); if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) - rc = -EINVAL; -out: - if (ctx) - ksmbd_release_crypto_ctx(ctx); - kfree(construct); - return rc; + return -EINVAL; + return 0; } /** * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct * authenticate blob * @authblob: authenticate blob source pointer - * @usr: user details + * @blob_len: length of the @authblob message + * @conn: connection * @sess: session of connection * * Return: 0 on success, error number on error @@ -355,14 +236,16 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, if (blob_len < (u64)sess_key_off + sess_key_len) return -EINVAL; - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + if (sess_key_len > CIFS_KEY_SIZE) + return -EINVAL; + + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), KSMBD_DEFAULT_GFP); if (!ctx_arc4) return -ENOMEM; - cifs_arc4_setkey(ctx_arc4, sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - cifs_arc4_crypt(ctx_arc4, sess->sess_key, - (char *)authblob + sess_key_off, sess_key_len); + arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE); + arc4_crypt(ctx_arc4, sess->sess_key, + (char *)authblob + sess_key_off, sess_key_len); kfree_sensitive(ctx_arc4); } @@ -373,8 +256,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct * negotiate blob * @negblob: negotiate blob source pointer - * @rsp: response header pointer to be updated - * @sess: session of connection + * @blob_len: length of the @authblob message + * @conn: connection * */ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, @@ -400,8 +283,7 @@ int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct * challenge blob * @chgblob: challenge blob source pointer to initialize - * @rsp: response header pointer to be updated - * @sess: session of connection + * @conn: connection * */ unsigned int @@ -446,7 +328,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, chgblob->NegotiateFlags = cpu_to_le32(flags); len = strlen(ksmbd_netbios_name()); - name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + name = kmalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP); if (!name) return -ENOMEM; @@ -507,6 +389,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, int in_len, char *out_blob, int *out_len) { struct ksmbd_spnego_authen_response *resp; + struct ksmbd_login_response_ext *resp_ext = NULL; struct ksmbd_user *user = NULL; int retval; @@ -535,13 +418,28 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, goto out; } - user = ksmbd_alloc_user(&resp->login_response); + if (resp->login_response.status & KSMBD_USER_FLAG_EXTENSION) + resp_ext = ksmbd_ipc_login_request_ext(resp->login_response.account); + + user = ksmbd_alloc_user(&resp->login_response, resp_ext); if (!user) { ksmbd_debug(AUTH, "login failure\n"); retval = -ENOMEM; goto out; } - sess->user = user; + + if (!sess->user) { + /* First successful authentication */ + sess->user = user; + } else { + if (!ksmbd_compare_user(sess->user, user)) { + ksmbd_debug(AUTH, "different user tried to reuse session\n"); + retval = -EPERM; + ksmbd_free_user(user); + goto out; + } + ksmbd_free_user(user); + } memcpy(sess->sess_key, resp->payload, resp->session_key_len); memcpy(out_blob, resp->payload + resp->session_key_len, @@ -569,46 +467,16 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, * @sig: signature value generated for client request packet * */ -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) +void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) { - struct ksmbd_crypto_ctx *ctx; - int rc, i; + struct hmac_sha256_ctx ctx; + int i; - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; + hmac_sha256_init_usingrawkey(&ctx, key, SMB2_NTLMV2_SESSKEY_SIZE); + for (i = 0; i < n_vec; i++) + hmac_sha256_update(&ctx, iov[i].iov_base, iov[i].iov_len); + hmac_sha256_final(&ctx, sig); } /** @@ -668,98 +536,39 @@ struct derivation { bool binding; }; -static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct kvec label, struct kvec context, __u8 *key, - unsigned int key_size) +static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct kvec label, struct kvec context, __u8 *key, + unsigned int key_size) { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; __u8 L128[4] = {0, 0, 0, 128}; __u8 L256[4] = {0, 0, 1, 0}; - int rc; unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct ksmbd_crypto_ctx *ctx; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } + struct hmac_sha256_ctx ctx; - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto smb3signkey_ret; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - label.iov_base, - label.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with label\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); - if (rc) { - ksmbd_debug(AUTH, "could not update with zero\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - context.iov_base, - context.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with context\n"); - goto smb3signkey_ret; - } + hmac_sha256_init_usingrawkey(&ctx, sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + hmac_sha256_update(&ctx, i, 4); + hmac_sha256_update(&ctx, label.iov_base, label.iov_len); + hmac_sha256_update(&ctx, &zero, 1); + hmac_sha256_update(&ctx, context.iov_base, context.iov_len); if (key_size == SMB3_ENC_DEC_KEY_SIZE && (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + hmac_sha256_update(&ctx, L256, 4); else - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with L\n"); - goto smb3signkey_ret; - } + hmac_sha256_update(&ctx, L128, 4); - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - ksmbd_release_crypto_ctx(ctx); - return rc; + hmac_sha256_final(&ctx, prfhash); + memcpy(key, prfhash, key_size); } static int generate_smb3signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn, const struct derivation *signing) { - int rc; struct channel *chann; char *key; @@ -772,10 +581,8 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, else key = sess->smb3signingkey; - rc = generate_key(conn, sess, signing->label, signing->context, key, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; + generate_key(conn, sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); @@ -831,23 +638,17 @@ struct derivation_twin { struct derivation decryption; }; -static int generate_smb3encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess, - const struct derivation_twin *ptwin) +static void generate_smb3encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + const struct derivation_twin *ptwin) { - int rc; - - rc = generate_key(conn, sess, ptwin->encryption.label, - ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; + generate_key(conn, sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); - rc = generate_key(conn, sess, ptwin->decryption.label, - ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; + generate_key(conn, sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); @@ -866,11 +667,10 @@ static int generate_smb3encryptionkey(struct ksmbd_conn *conn, ksmbd_debug(AUTH, "ServerOut Key %*ph\n", SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); } - return 0; } -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) +void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { struct derivation_twin twin; struct derivation *d; @@ -887,11 +687,11 @@ int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, d->context.iov_base = "ServerIn "; d->context.iov_len = 10; - return generate_smb3encryptionkey(conn, sess, &twin); + generate_smb3encryptionkey(conn, sess, &twin); } -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) +void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { struct derivation_twin twin; struct derivation *d; @@ -908,88 +708,26 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, d->context.iov_base = sess->Preauth_HashValue; d->context.iov_len = 64; - return generate_smb3encryptionkey(conn, sess, &twin); + generate_smb3encryptionkey(conn, sess, &twin); } int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash) { - int rc; struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; int msg_size = get_rfc1002_len(buf); - struct ksmbd_crypto_ctx *ctx = NULL; + struct sha512_ctx sha_ctx; if (conn->preauth_info->Preauth_HashId != SMB2_PREAUTH_INTEGRITY_SHA512) return -EINVAL; - ctx = ksmbd_crypto_ctx_find_sha512(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA512(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash) -{ - int rc; - struct ksmbd_crypto_ctx *ctx = NULL; - - ctx = ksmbd_crypto_ctx_find_sha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha256\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; + sha512_init(&sha_ctx); + sha512_update(&sha_ctx, pi_hash, 64); + sha512_update(&sha_ctx, all_bytes_msg, msg_size); + sha512_final(&sha_ctx, pi_hash); + return 0; } static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, @@ -1008,6 +746,8 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, ses_enc_key = enc ? sess->smb3encryptionkey : sess->smb3decryptionkey; memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + if (!enc) + ksmbd_user_session_put(sess); return 0; } @@ -1029,11 +769,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, { struct scatterlist *sg; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + int i, *nr_entries, total_entries = 0, sg_idx = 0; if (!nvec) return NULL; + nr_entries = kcalloc(nvec, sizeof(int), KSMBD_DEFAULT_GFP); + if (!nr_entries) + return NULL; + for (i = 0; i < nvec - 1; i++) { unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; @@ -1050,9 +794,12 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, /* Add two entries for transform header and signature */ total_entries += 2; - sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); - if (!sg) + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), + KSMBD_DEFAULT_GFP); + if (!sg) { + kfree(nr_entries); return NULL; + } sg_init_table(sg, total_entries); smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); @@ -1086,6 +833,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, } } smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); + kfree(nr_entries); return sg; } @@ -1147,7 +895,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, goto free_ctx; } - req = aead_request_alloc(tfm, GFP_KERNEL); + req = aead_request_alloc(tfm, KSMBD_DEFAULT_GFP); if (!req) { rc = -ENOMEM; goto free_ctx; @@ -1166,7 +914,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, } iv_len = crypto_aead_ivsize(tfm); - iv = kzalloc(iv_len, GFP_KERNEL); + iv = kzalloc(iv_len, KSMBD_DEFAULT_GFP); if (!iv) { rc = -ENOMEM; goto free_sg; @@ -1199,7 +947,7 @@ free_iv: free_sg: kfree(sg); free_req: - kfree(req); + aead_request_free(req); free_ctx: ksmbd_release_crypto_ctx(ctx); return rc; diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h index 362b6159a6cf..6d351d61b0e5 100644 --- a/fs/smb/server/auth.h +++ b/fs/smb/server/auth.h @@ -52,20 +52,18 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, struct ksmbd_conn *conn); int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, int in_len, char *out_blob, int *out_len); -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); +void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, +void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, struct ksmbd_session *sess); +void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash); -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash); #endif diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 2a717d158f02..b6b4f1286b9c 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -19,13 +19,13 @@ static DEFINE_MUTEX(init_lock); static struct ksmbd_conn_ops default_conn_ops; -LIST_HEAD(conn_list); +DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS); DECLARE_RWSEM(conn_list_lock); /** * ksmbd_conn_free() - free resources of the connection instance * - * @conn: connection instance to be cleand up + * @conn: connection instance to be cleaned up * * During the thread termination, the corresponding conn instance * resources(sock/memory) are released and finally the conn object is freed. @@ -33,13 +33,16 @@ DECLARE_RWSEM(conn_list_lock); void ksmbd_conn_free(struct ksmbd_conn *conn) { down_write(&conn_list_lock); - list_del(&conn->conns_list); + hash_del(&conn->hlist); up_write(&conn_list_lock); xa_destroy(&conn->sessions); kvfree(conn->request_buf); kfree(conn->preauth_info); - kfree(conn); + if (atomic_dec_and_test(&conn->refcnt)) { + conn->transport->ops->free_transport(conn->transport); + kfree(conn); + } } /** @@ -51,7 +54,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) { struct ksmbd_conn *conn; - conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + conn = kzalloc(sizeof(struct ksmbd_conn), KSMBD_DEFAULT_GFP); if (!conn) return NULL; @@ -68,12 +71,12 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) conn->um = NULL; atomic_set(&conn->req_running, 0); atomic_set(&conn->r_count, 0); + atomic_set(&conn->refcnt, 1); conn->total_credits = 1; conn->outstanding_credits = 0; init_waitqueue_head(&conn->req_running_q); init_waitqueue_head(&conn->r_count_q); - INIT_LIST_HEAD(&conn->conns_list); INIT_LIST_HEAD(&conn->requests); INIT_LIST_HEAD(&conn->async_requests); spin_lock_init(&conn->request_lock); @@ -84,19 +87,19 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) spin_lock_init(&conn->llist_lock); INIT_LIST_HEAD(&conn->lock_list); - down_write(&conn_list_lock); - list_add(&conn->conns_list, &conn_list); - up_write(&conn_list_lock); + init_rwsem(&conn->session_lock); + return conn; } bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) { struct ksmbd_conn *t; + int bkt; bool ret = false; down_read(&conn_list_lock); - list_for_each_entry(t, &conn_list, conns_list) { + hash_for_each(conn_list, bkt, t, hlist) { if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) continue; @@ -115,36 +118,33 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work) if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) requests_queue = &conn->requests; + atomic_inc(&conn->req_running); if (requests_queue) { - atomic_inc(&conn->req_running); spin_lock(&conn->request_lock); list_add_tail(&work->request_entry, requests_queue); spin_unlock(&conn->request_lock); } } -int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) +void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - int ret = 1; + + atomic_dec(&conn->req_running); + if (waitqueue_active(&conn->req_running_q)) + wake_up(&conn->req_running_q); if (list_empty(&work->request_entry) && list_empty(&work->async_request_entry)) - return 0; + return; - if (!work->multiRsp) - atomic_dec(&conn->req_running); - if (!work->multiRsp) { - spin_lock(&conn->request_lock); - list_del_init(&work->request_entry); - spin_unlock(&conn->request_lock); - if (work->asynchronous) - release_async_work(work); - ret = 0; - } + spin_lock(&conn->request_lock); + list_del_init(&work->request_entry); + spin_unlock(&conn->request_lock); + if (work->asynchronous) + release_async_work(work); wake_up_all(&conn->req_running_q); - return ret; } void ksmbd_conn_lock(struct ksmbd_conn *conn) @@ -160,74 +160,75 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn) void ksmbd_all_conn_set_status(u64 sess_id, u32 status) { struct ksmbd_conn *conn; + int bkt; down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { + hash_for_each(conn_list, bkt, conn, hlist) { if (conn->binding || xa_load(&conn->sessions, sess_id)) WRITE_ONCE(conn->status, status); } up_read(&conn_list_lock); } -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) { - struct ksmbd_conn *bind_conn; - wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); +} - down_read(&conn_list_lock); - list_for_each_entry(bind_conn, &conn_list, conns_list) { - if (bind_conn == conn) - continue; +int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id) +{ + struct ksmbd_conn *conn; + int rc, retry_count = 0, max_timeout = 120; + int rcount = 1, bkt; + +retry_idle: + if (retry_count >= max_timeout) + return -EIO; - if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && - !ksmbd_conn_releasing(bind_conn) && - atomic_read(&bind_conn->req_running)) { - wait_event(bind_conn->req_running_q, - atomic_read(&bind_conn->req_running) == 0); + down_read(&conn_list_lock); + hash_for_each(conn_list, bkt, conn, hlist) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) { + if (conn == curr_conn) + rcount = 2; + if (atomic_read(&conn->req_running) >= rcount) { + rc = wait_event_timeout(conn->req_running_q, + atomic_read(&conn->req_running) < rcount, + HZ); + if (!rc) { + up_read(&conn_list_lock); + retry_count++; + goto retry_idle; + } + } } } up_read(&conn_list_lock); + + return 0; } int ksmbd_conn_write(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - size_t len = 0; int sent; - struct kvec iov[3]; - int iov_idx = 0; if (!work->response_buf) { pr_err("NULL response header\n"); return -EINVAL; } - if (work->tr_buf) { - iov[iov_idx] = (struct kvec) { work->tr_buf, - sizeof(struct smb2_transform_hdr) + 4 }; - len += iov[iov_idx++].iov_len; - } + if (work->send_no_response) + return 0; - if (work->aux_payload_sz) { - iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; - len += iov[iov_idx++].iov_len; - iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; - len += iov[iov_idx++].iov_len; - } else { - if (work->tr_buf) - iov[iov_idx].iov_len = work->resp_hdr_sz; - else - iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; - iov[iov_idx].iov_base = work->response_buf; - len += iov[iov_idx++].iov_len; - } + if (!work->iov_idx) + return -EINVAL; ksmbd_conn_lock(conn); - sent = conn->transport->ops->writev(conn->transport, &iov[0], - iov_idx, len, - work->need_invalidate_rkey, - work->remote_key); + sent = conn->transport->ops->writev(conn->transport, work->iov, + work->iov_cnt, + get_rfc1002_len(work->iov[0].iov_base) + 4, + work->need_invalidate_rkey, + work->remote_key); ksmbd_conn_unlock(conn); if (sent < 0) { @@ -240,7 +241,7 @@ int ksmbd_conn_write(struct ksmbd_work *work) int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len) { int ret = -EINVAL; @@ -254,7 +255,7 @@ int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len) { int ret = -EINVAL; @@ -309,7 +310,7 @@ int ksmbd_conn_handler_loop(void *p) { struct ksmbd_conn *conn = (struct ksmbd_conn *)p; struct ksmbd_transport *t = conn->transport; - unsigned int pdu_size, max_allowed_pdu_size; + unsigned int pdu_size, max_allowed_pdu_size, max_req; char hdr_buf[4] = {0,}; int size; @@ -319,7 +320,9 @@ int ksmbd_conn_handler_loop(void *p) if (t->ops->prepare && t->ops->prepare(t)) goto out; + max_req = server_conf.max_inflight_req; conn->last_active = jiffies; + set_freezable(); while (ksmbd_conn_alive(conn)) { if (try_to_freeze()) continue; @@ -327,6 +330,13 @@ int ksmbd_conn_handler_loop(void *p) kvfree(conn->request_buf); conn->request_buf = NULL; +recheck: + if (atomic_read(&conn->req_running) + 1 > max_req) { + wait_event_interruptible(conn->req_running_q, + atomic_read(&conn->req_running) < max_req); + goto recheck; + } + size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1); if (size != sizeof(hdr_buf)) break; @@ -359,7 +369,7 @@ int ksmbd_conn_handler_loop(void *p) /* 4 for rfc1002 length field */ /* 1 for implied bcc[0] */ size = pdu_size + 4 + 1; - conn->request_buf = kvmalloc(size, GFP_KERNEL); + conn->request_buf = kvmalloc(size, KSMBD_DEFAULT_GFP); if (!conn->request_buf) break; @@ -404,6 +414,7 @@ int ksmbd_conn_handler_loop(void *p) out: ksmbd_conn_set_releasing(conn); /* Wait till all reference dropped to the Server object*/ + ksmbd_debug(CONN, "Wait for all pending requests(%d)\n", atomic_read(&conn->r_count)); wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); if (IS_ENABLED(CONFIG_UNICODE)) @@ -422,6 +433,26 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) default_conn_ops.terminate_fn = ops->terminate_fn; } +void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn) +{ + atomic_inc(&conn->r_count); +} + +void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn) +{ + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + atomic_inc(&conn->refcnt); + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); + + if (atomic_dec_and_test(&conn->refcnt)) + kfree(conn); +} + int ksmbd_conn_transport_init(void) { int ret; @@ -447,17 +478,12 @@ static void stop_sessions(void) { struct ksmbd_conn *conn; struct ksmbd_transport *t; + int bkt; again: down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - struct task_struct *task; - + hash_for_each(conn_list, bkt, conn, hlist) { t = conn->transport; - task = t->handler; - if (task) - ksmbd_debug(CONN, "Stop session handler %s/%d\n", - task->comm, task_pid_nr(task)); ksmbd_conn_set_exiting(conn); if (t->ops->shutdown) { up_read(&conn_list_lock); @@ -467,8 +493,8 @@ again: } up_read(&conn_list_lock); - if (!list_empty(&conn_list)) { - schedule_timeout_interruptible(HZ / 10); /* 100ms */ + if (!hash_empty(conn_list)) { + msleep(100); goto again; } } @@ -477,7 +503,8 @@ void ksmbd_conn_transport_destroy(void) { mutex_lock(&init_lock); ksmbd_tcp_destroy(); - ksmbd_rdma_destroy(); + ksmbd_rdma_stop_listening(); stop_sessions(); + ksmbd_rdma_destroy(); mutex_unlock(&init_lock); } diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index ad8dfaa48ffb..7f9bcd9817b5 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -19,6 +19,8 @@ #include "smb_common.h" #include "ksmbd_work.h" +struct smbdirect_buffer_descriptor_v1; + #define KSMBD_SOCKET_BACKLOG 16 enum { @@ -27,6 +29,7 @@ enum { KSMBD_SESS_EXITING, KSMBD_SESS_NEED_RECONNECT, KSMBD_SESS_NEED_NEGOTIATE, + KSMBD_SESS_NEED_SETUP, KSMBD_SESS_RELEASING }; @@ -45,11 +48,19 @@ struct ksmbd_conn { struct mutex srv_mutex; int status; unsigned int cli_cap; + union { + __be32 inet_addr; +#if IS_ENABLED(CONFIG_IPV6) + u8 inet6_addr[16]; +#endif + }; + unsigned int inet_hash; char *request_buf; struct ksmbd_transport *transport; struct nls_table *local_nls; struct unicode_map *um; - struct list_head conns_list; + struct hlist_node hlist; + struct rw_semaphore session_lock; /* smb session 1 per user */ struct xarray sessions; unsigned long last_active; @@ -87,6 +98,7 @@ struct ksmbd_conn { __u16 dialect; char *mechToken; + unsigned int mechTokenLen; struct ksmbd_conn_ops *conn_ops; @@ -104,6 +116,8 @@ struct ksmbd_conn { bool signing_negotiated; __le16 signing_algorithm; bool binding; + atomic_t refcnt; + bool is_aapl; }; struct ksmbd_conn_ops { @@ -122,49 +136,53 @@ struct ksmbd_transport_ops { unsigned int remote_key); int (*rdma_read)(struct ksmbd_transport *t, void *buf, unsigned int len, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len); int (*rdma_write)(struct ksmbd_transport *t, void *buf, unsigned int len, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len); + void (*free_transport)(struct ksmbd_transport *kt); }; struct ksmbd_transport { - struct ksmbd_conn *conn; - struct ksmbd_transport_ops *ops; - struct task_struct *handler; + struct ksmbd_conn *conn; + const struct ksmbd_transport_ops *ops; }; #define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) #define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) #define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) -extern struct list_head conn_list; +#define CONN_HASH_BITS 12 +extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS); extern struct rw_semaphore conn_list_lock; bool ksmbd_conn_alive(struct ksmbd_conn *conn); -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); +int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id); struct ksmbd_conn *ksmbd_conn_alloc(void); void ksmbd_conn_free(struct ksmbd_conn *conn); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); int ksmbd_conn_write(struct ksmbd_work *work); int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len); int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len); void ksmbd_conn_enqueue_request(struct ksmbd_work *work); -int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); +void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); int ksmbd_conn_handler_loop(void *p); int ksmbd_conn_transport_init(void); void ksmbd_conn_transport_destroy(void); void ksmbd_conn_lock(struct ksmbd_conn *conn); void ksmbd_conn_unlock(struct ksmbd_conn *conn); +void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn); +void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn); /* * WARNING @@ -182,6 +200,11 @@ static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; } +static inline bool ksmbd_conn_need_setup(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_SETUP; +} + static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn) { return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT; @@ -212,6 +235,11 @@ static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn) WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE); } +static inline void ksmbd_conn_set_need_setup(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_SETUP); +} + static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn) { WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT); diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c index 81488d04199d..fe29d186baf6 100644 --- a/fs/smb/server/crypto_ctx.c +++ b/fs/smb/server/crypto_ctx.c @@ -66,21 +66,9 @@ static struct shash_desc *alloc_shash_desc(int id) struct shash_desc *shash; switch (id) { - case CRYPTO_SHASH_HMACMD5: - tfm = crypto_alloc_shash("hmac(md5)", 0, 0); - break; - case CRYPTO_SHASH_HMACSHA256: - tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); - break; case CRYPTO_SHASH_CMACAES: tfm = crypto_alloc_shash("cmac(aes)", 0, 0); break; - case CRYPTO_SHASH_SHA256: - tfm = crypto_alloc_shash("sha256", 0, 0); - break; - case CRYPTO_SHASH_SHA512: - tfm = crypto_alloc_shash("sha512", 0, 0); - break; default: return NULL; } @@ -89,7 +77,7 @@ static struct shash_desc *alloc_shash_desc(int id) return NULL; shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!shash) crypto_free_shash(tfm); else @@ -133,7 +121,7 @@ static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) ctx_list.avail_ctx++; spin_unlock(&ctx_list.ctx_lock); - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP); if (!ctx) { spin_lock(&ctx_list.ctx_lock); ctx_list.avail_ctx--; @@ -183,31 +171,11 @@ static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) return NULL; } -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); -} - struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) { return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); } -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); -} - static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) { struct ksmbd_crypto_ctx *ctx; @@ -258,7 +226,7 @@ int ksmbd_crypto_create(void) init_waitqueue_head(&ctx_list.ctx_wait); ctx_list.avail_ctx = 1; - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP); if (!ctx) return -ENOMEM; list_add(&ctx->list, &ctx_list.idle_ctx); diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h index 4a367c62f653..b9476ed520ae 100644 --- a/fs/smb/server/crypto_ctx.h +++ b/fs/smb/server/crypto_ctx.h @@ -10,11 +10,7 @@ #include <crypto/aead.h> enum { - CRYPTO_SHASH_HMACMD5 = 0, - CRYPTO_SHASH_HMACSHA256, - CRYPTO_SHASH_CMACAES, - CRYPTO_SHASH_SHA256, - CRYPTO_SHASH_SHA512, + CRYPTO_SHASH_CMACAES = 0, CRYPTO_SHASH_MAX, }; @@ -36,28 +32,15 @@ struct ksmbd_crypto_ctx { struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; }; -#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) -#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) #define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) -#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) -#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) -#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) -#define CRYPTO_HMACSHA256_TFM(c)\ - ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) -#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) -#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) #define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) #define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); void ksmbd_crypto_destroy(void); diff --git a/fs/smb/server/glob.h b/fs/smb/server/glob.h index 5b8f3e0ebdb3..4ea187af2348 100644 --- a/fs/smb/server/glob.h +++ b/fs/smb/server/glob.h @@ -12,8 +12,6 @@ #include "unicode.h" #include "vfs_cache.h" -#define KSMBD_VERSION "3.4.2" - extern int ksmbd_debug_types; #define KSMBD_DEBUG_SMB BIT(0) @@ -46,4 +44,6 @@ extern int ksmbd_debug_types; #define UNICODE_LEN(x) ((x) * 2) +#define KSMBD_DEFAULT_GFP (GFP_KERNEL | __GFP_RETRY_MAYFAIL) + #endif /* __KSMBD_GLOB_H */ diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h index b7521e41402e..8ccd57fd904b 100644 --- a/fs/smb/server/ksmbd_netlink.h +++ b/fs/smb/server/ksmbd_netlink.h @@ -51,6 +51,9 @@ * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) * This event is to make kerberos authentication to be processed in * userspace. + * + * - KSMBD_EVENT_LOGIN_REQUEST_EXT/RESPONSE_EXT(ksmbd_login_request_ext/response_ext) + * This event is to get user account extension info to user IPC daemon. */ #define KSMBD_GENL_NAME "SMBD_GENL" @@ -75,6 +78,7 @@ struct ksmbd_heartbeat { #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3) +#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4) /* * IPC request for ksmbd server startup @@ -107,10 +111,12 @@ struct ksmbd_startup_request { __u32 smb2_max_credits; /* MAX credits */ __u32 smbd_max_io_size; /* smbd read write size */ __u32 max_connections; /* Number of maximum simultaneous connections */ - __u32 reserved[126]; /* Reserved room */ + __s8 bind_interfaces_only; + __u32 max_ip_connections; /* Number of maximum connection per ip address */ + __s8 reserved[499]; /* Reserved room */ __u32 ifc_list_sz; /* interfaces list size */ __s8 ____payload[]; -}; +} __packed; #define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) @@ -145,6 +151,16 @@ struct ksmbd_login_response { }; /* + * IPC user login response extension. + */ +struct ksmbd_login_response_ext { + __u32 handle; + __s32 ngroups; /* supplementary group count */ + __s8 reserved[128]; /* Reserved room */ + __s8 ____payload[]; +}; + +/* * IPC request to fetch net share config. */ struct ksmbd_share_config_request { @@ -166,7 +182,8 @@ struct ksmbd_share_config_response { __u16 force_uid; __u16 force_gid; __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; - __u32 reserved[112]; /* Reserved room */ + __u32 reserved[111]; /* Reserved room */ + __u32 payload_sz; __u32 veto_list_sz; __s8 ____payload[]; }; @@ -211,7 +228,7 @@ struct ksmbd_tree_connect_response { }; /* - * IPC Request struture to disconnect tree connection. + * IPC Request structure to disconnect tree connection. */ struct ksmbd_tree_disconnect_request { __u64 session_id; /* session id */ @@ -304,7 +321,11 @@ enum ksmbd_event { KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, - KSMBD_EVENT_MAX + KSMBD_EVENT_LOGIN_REQUEST_EXT, + KSMBD_EVENT_LOGIN_RESPONSE_EXT, + + __KSMBD_EVENT_MAX, + KSMBD_EVENT_MAX = __KSMBD_EVENT_MAX - 1 }; /* @@ -333,27 +354,29 @@ enum KSMBD_TREE_CONN_STATUS { #define KSMBD_USER_FLAG_BAD_USER BIT(3) #define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) #define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) +#define KSMBD_USER_FLAG_EXTENSION BIT(6) /* * Share config flags. */ -#define KSMBD_SHARE_FLAG_INVALID (0) -#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) -#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) -#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) -#define KSMBD_SHARE_FLAG_READONLY BIT(3) -#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) -#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) -#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) -#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) -#define KSMBD_SHARE_FLAG_PIPE BIT(8) -#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) -#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) -#define KSMBD_SHARE_FLAG_STREAMS BIT(11) -#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) -#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) -#define KSMBD_SHARE_FLAG_UPDATE BIT(14) -#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15) +#define KSMBD_SHARE_FLAG_INVALID (0) +#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +#define KSMBD_SHARE_FLAG_READONLY BIT(3) +#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +#define KSMBD_SHARE_FLAG_PIPE BIT(8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +#define KSMBD_SHARE_FLAG_UPDATE BIT(14) +#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15) +#define KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY BIT(16) /* * Tree connect request flags. diff --git a/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 b/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 index 0065f191b54b..001513806fc0 100644 --- a/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 +++ b/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 @@ -1,3 +1,11 @@ +-- SPDX-License-Identifier: BSD-3-Clause +-- +-- Copyright (C) 1998, 2000 IETF Trust and the persons identified as authors +-- of the code +-- +-- https://www.rfc-editor.org/rfc/rfc2478#section-3.2.1 +-- https://www.rfc-editor.org/rfc/rfc2743#section-3.1 + GSSAPI ::= [APPLICATION 0] IMPLICIT SEQUENCE { thisMech diff --git a/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 b/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 index 1151933e7b9c..797e485d57f1 100644 --- a/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 +++ b/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 @@ -1,3 +1,10 @@ +-- SPDX-License-Identifier: BSD-3-Clause +-- +-- Copyright (C) 1998 IETF Trust and the persons identified as authors +-- of the code +-- +-- https://www.rfc-editor.org/rfc/rfc2478#section-3.2.1 + GSSAPI ::= CHOICE { negTokenInit diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c index 14b9caebf7a4..4a71f46d7020 100644 --- a/fs/smb/server/ksmbd_work.c +++ b/fs/smb/server/ksmbd_work.c @@ -18,7 +18,7 @@ static struct workqueue_struct *ksmbd_wq; struct ksmbd_work *ksmbd_alloc_work_struct(void) { - struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, KSMBD_DEFAULT_GFP); if (work) { work->compound_fid = KSMBD_NO_FID; @@ -26,19 +26,36 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) INIT_LIST_HEAD(&work->request_entry); INIT_LIST_HEAD(&work->async_request_entry); INIT_LIST_HEAD(&work->fp_entry); - INIT_LIST_HEAD(&work->interim_entry); + INIT_LIST_HEAD(&work->aux_read_list); + work->iov_alloc_cnt = 4; + work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec), + KSMBD_DEFAULT_GFP); + if (!work->iov) { + kmem_cache_free(work_cache, work); + work = NULL; + } } return work; } void ksmbd_free_work_struct(struct ksmbd_work *work) { + struct aux_read *ar, *tmp; + WARN_ON(work->saved_cred != NULL); kvfree(work->response_buf); - kvfree(work->aux_payload_buf); + + list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) { + kvfree(ar->buf); + list_del(&ar->entry); + kfree(ar); + } + kfree(work->tr_buf); kvfree(work->request_buf); + kfree(work->iov); + if (work->async_id) ksmbd_release_id(&work->conn->async_ida, work->async_id); kmem_cache_free(work_cache, work); @@ -61,7 +78,7 @@ int ksmbd_work_pool_init(void) int ksmbd_workqueue_init(void) { - ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); + ksmbd_wq = alloc_workqueue("ksmbd-io", WQ_PERCPU, 0); if (!ksmbd_wq) return -ENOMEM; return 0; @@ -77,3 +94,81 @@ bool ksmbd_queue_work(struct ksmbd_work *work) { return queue_work(ksmbd_wq, &work->work); } + +static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib, + unsigned int ib_len) +{ + work->iov[++work->iov_idx].iov_base = ib; + work->iov[work->iov_idx].iov_len = ib_len; + work->iov_cnt++; +} + +static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, + void *aux_buf, unsigned int aux_size) +{ + struct aux_read *ar = NULL; + int need_iov_cnt = 1; + + if (aux_size) { + need_iov_cnt++; + ar = kmalloc(sizeof(struct aux_read), KSMBD_DEFAULT_GFP); + if (!ar) + return -ENOMEM; + } + + if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) { + struct kvec *new; + + work->iov_alloc_cnt += 4; + new = krealloc(work->iov, + sizeof(struct kvec) * work->iov_alloc_cnt, + KSMBD_DEFAULT_GFP | __GFP_ZERO); + if (!new) { + kfree(ar); + work->iov_alloc_cnt -= 4; + return -ENOMEM; + } + work->iov = new; + } + + /* Plus rfc_length size on first iov */ + if (!work->iov_idx) { + work->iov[work->iov_idx].iov_base = work->response_buf; + *(__be32 *)work->iov[0].iov_base = 0; + work->iov[work->iov_idx].iov_len = 4; + work->iov_cnt++; + } + + __ksmbd_iov_pin(work, ib, len); + inc_rfc1001_len(work->iov[0].iov_base, len); + + if (aux_size) { + __ksmbd_iov_pin(work, aux_buf, aux_size); + inc_rfc1001_len(work->iov[0].iov_base, aux_size); + + ar->buf = aux_buf; + list_add(&ar->entry, &work->aux_read_list); + } + + return 0; +} + +int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len) +{ + return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0); +} + +int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, + void *aux_buf, unsigned int aux_size) +{ + return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size); +} + +int allocate_interim_rsp_buf(struct ksmbd_work *work) +{ + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, KSMBD_DEFAULT_GFP); + if (!work->response_buf) + return -ENOMEM; + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + return 0; +} diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index f8ae6144c0ae..d36393ff8310 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -19,6 +19,11 @@ enum { KSMBD_WORK_CLOSED, }; +struct aux_read { + void *buf; + struct list_head entry; +}; + /* one of these for every pending CIFS request at the connection */ struct ksmbd_work { /* Server corresponding to this mid */ @@ -31,13 +36,19 @@ struct ksmbd_work { /* Response buffer */ void *response_buf; - /* Read data buffer */ - void *aux_payload_buf; + struct list_head aux_read_list; + + struct kvec *iov; + int iov_alloc_cnt; + int iov_cnt; + int iov_idx; /* Next cmd hdr in compound req buf*/ int next_smb2_rcv_hdr_off; /* Next cmd hdr in compound rsp buf*/ int next_smb2_rsp_hdr_off; + /* Current cmd hdr in compound rsp buf*/ + int curr_smb2_rsp_hdr_off; /* * Current Local FID assigned compound response if SMB2 CREATE @@ -53,16 +64,11 @@ struct ksmbd_work { unsigned int credits_granted; /* response smb header size */ - unsigned int resp_hdr_sz; unsigned int response_sz; - /* Read data count */ - unsigned int aux_payload_sz; void *tr_buf; unsigned char state; - /* Multiple responses for one request e.g. SMB ECHO */ - bool multiRsp:1; /* No response for cancelled request */ bool send_no_response:1; /* Request is encrypted */ @@ -83,7 +89,6 @@ struct ksmbd_work { /* List head at conn->async_requests */ struct list_head async_request_entry; struct list_head fp_entry; - struct list_head interim_entry; }; /** @@ -96,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) } /** + * ksmbd_resp_buf_curr - Get current buffer on compound response. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work) +{ + return work->response_buf + work->curr_smb2_rsp_hdr_off + 4; +} + +/** * ksmbd_req_buf_next - Get next buffer on compound request. * @work: smb work containing response buffer */ @@ -113,5 +127,8 @@ int ksmbd_work_pool_init(void); int ksmbd_workqueue_init(void); void ksmbd_workqueue_destroy(void); bool ksmbd_queue_work(struct ksmbd_work *work); - +int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, + void *aux_buf, unsigned int aux_size); +int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len); +int allocate_interim_rsp_buf(struct ksmbd_work *work); #endif /* __KSMBD_WORK_H__ */ diff --git a/fs/smb/server/mgmt/ksmbd_ida.c b/fs/smb/server/mgmt/ksmbd_ida.c index 54194d959a5e..0e2ae994ab52 100644 --- a/fs/smb/server/mgmt/ksmbd_ida.c +++ b/fs/smb/server/mgmt/ksmbd_ida.c @@ -4,43 +4,35 @@ */ #include "ksmbd_ida.h" - -static inline int __acquire_id(struct ida *ida, int from, int to) -{ - return ida_simple_get(ida, from, to, GFP_KERNEL); -} +#include "../glob.h" int ksmbd_acquire_smb2_tid(struct ida *ida) { - int id; - - id = __acquire_id(ida, 1, 0xFFFFFFFF); - - return id; + return ida_alloc_range(ida, 1, 0xFFFFFFFE, KSMBD_DEFAULT_GFP); } int ksmbd_acquire_smb2_uid(struct ida *ida) { int id; - id = __acquire_id(ida, 1, 0); + id = ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP); if (id == 0xFFFE) - id = __acquire_id(ida, 1, 0); + id = ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP); return id; } int ksmbd_acquire_async_msg_id(struct ida *ida) { - return __acquire_id(ida, 1, 0); + return ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP); } int ksmbd_acquire_id(struct ida *ida) { - return __acquire_id(ida, 0, 0); + return ida_alloc(ida, KSMBD_DEFAULT_GFP); } void ksmbd_release_id(struct ida *ida, int id) { - ida_simple_remove(ida, id); + ida_free(ida, id); } diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c index 328a412259dc..c9b1108d6e96 100644 --- a/fs/smb/server/mgmt/share_config.c +++ b/fs/smb/server/mgmt/share_config.c @@ -15,10 +15,11 @@ #include "share_config.h" #include "user_config.h" #include "user_session.h" +#include "../connection.h" #include "../transport_ipc.h" #include "../misc.h" -#define SHARE_HASH_BITS 3 +#define SHARE_HASH_BITS 12 static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); static DECLARE_RWSEM(shares_table_lock); @@ -101,11 +102,11 @@ static int parse_veto_list(struct ksmbd_share_config *share, if (!sz) break; - p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + p = kzalloc(sizeof(struct ksmbd_veto_pattern), KSMBD_DEFAULT_GFP); if (!p) return -ENOMEM; - p->pattern = kstrdup(veto_list, GFP_KERNEL); + p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP); if (!p->pattern) { kfree(p); return -ENOMEM; @@ -120,12 +121,13 @@ static int parse_veto_list(struct ksmbd_share_config *share, return 0; } -static struct ksmbd_share_config *share_config_request(struct unicode_map *um, +static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, const char *name) { struct ksmbd_share_config_response *resp; struct ksmbd_share_config *share = NULL; struct ksmbd_share_config *lookup; + struct unicode_map *um = work->conn->um; int ret; resp = ksmbd_ipc_share_config_request(name); @@ -148,20 +150,29 @@ static struct ksmbd_share_config *share_config_request(struct unicode_map *um, goto out; } - share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); + share = kzalloc(sizeof(struct ksmbd_share_config), KSMBD_DEFAULT_GFP); if (!share) goto out; share->flags = resp->flags; atomic_set(&share->refcount, 1); INIT_LIST_HEAD(&share->veto_list); - share->name = kstrdup(name, GFP_KERNEL); + share->name = kstrdup(name, KSMBD_DEFAULT_GFP); if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - share->path = kstrdup(ksmbd_share_config_path(resp), - GFP_KERNEL); - if (share->path) + int path_len = PATH_MAX; + + if (resp->payload_sz) + path_len = resp->payload_sz - resp->veto_list_sz; + + share->path = kstrndup(ksmbd_share_config_path(resp), path_len, + KSMBD_DEFAULT_GFP); + if (share->path) { share->path_sz = strlen(share->path); + while (share->path_sz > 1 && + share->path[share->path_sz - 1] == '/') + share->path[--share->path_sz] = '\0'; + } share->create_mask = resp->create_mask; share->directory_mask = resp->directory_mask; share->force_create_mode = resp->force_create_mode; @@ -172,7 +183,14 @@ static struct ksmbd_share_config *share_config_request(struct unicode_map *um, KSMBD_SHARE_CONFIG_VETO_LIST(resp), resp->veto_list_sz); if (!ret && share->path) { + if (__ksmbd_override_fsids(work, share)) { + kill_share(share); + share = NULL; + goto out; + } + ret = kern_path(share->path, 0, &share->vfs_path); + ksmbd_revert_fsids(work); if (ret) { ksmbd_debug(SMB, "failed to access '%s'\n", share->path); @@ -205,7 +223,7 @@ out: return share; } -struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, +struct ksmbd_share_config *ksmbd_share_config_get(struct ksmbd_work *work, const char *name) { struct ksmbd_share_config *share; @@ -218,7 +236,7 @@ struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, if (share) return share; - return share_config_request(um, name); + return share_config_request(work, name); } bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h index 3fd338293942..d4ac2dd4de20 100644 --- a/fs/smb/server/mgmt/share_config.h +++ b/fs/smb/server/mgmt/share_config.h @@ -11,6 +11,8 @@ #include <linux/path.h> #include <linux/unicode.h> +struct ksmbd_work; + struct ksmbd_share_config { char *name; char *path; @@ -34,29 +36,22 @@ struct ksmbd_share_config { #define KSMBD_SHARE_INVALID_UID ((__u16)-1) #define KSMBD_SHARE_INVALID_GID ((__u16)-1) -static inline int share_config_create_mode(struct ksmbd_share_config *share, - umode_t posix_mode) +static inline umode_t +share_config_create_mode(struct ksmbd_share_config *share, + umode_t posix_mode) { - if (!share->force_create_mode) { - if (!posix_mode) - return share->create_mask; - else - return posix_mode & share->create_mask; - } - return share->force_create_mode & share->create_mask; + umode_t mode = (posix_mode ?: (umode_t)-1) & share->create_mask; + + return mode | share->force_create_mode; } -static inline int share_config_directory_mode(struct ksmbd_share_config *share, - umode_t posix_mode) +static inline umode_t +share_config_directory_mode(struct ksmbd_share_config *share, + umode_t posix_mode) { - if (!share->force_directory_mode) { - if (!posix_mode) - return share->directory_mask; - else - return posix_mode & share->directory_mask; - } - - return share->force_directory_mode & share->directory_mask; + umode_t mode = (posix_mode ?: (umode_t)-1) & share->directory_mask; + + return mode | share->force_directory_mode; } static inline int test_share_config_flag(struct ksmbd_share_config *share, @@ -75,7 +70,7 @@ static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) __ksmbd_share_config_put(share); } -struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, +struct ksmbd_share_config *ksmbd_share_config_get(struct ksmbd_work *work, const char *name); bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, const char *filename); diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c index 408cddf2f094..d3483d9c757c 100644 --- a/fs/smb/server/mgmt/tree_connect.c +++ b/fs/smb/server/mgmt/tree_connect.c @@ -16,21 +16,23 @@ #include "user_session.h" struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, - const char *share_name) +ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name) { struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; struct ksmbd_tree_connect_response *resp = NULL; struct ksmbd_share_config *sc; struct ksmbd_tree_connect *tree_conn = NULL; struct sockaddr *peer_addr; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; int ret; - sc = ksmbd_share_config_get(conn->um, share_name); + sc = ksmbd_share_config_get(work, share_name); if (!sc) return status; - tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), + KSMBD_DEFAULT_GFP); if (!tree_conn) { status.ret = -ENOMEM; goto out_error; @@ -61,7 +63,7 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, struct ksmbd_share_config *new_sc; ksmbd_share_config_del(sc); - new_sc = ksmbd_share_config_get(conn->um, share_name); + new_sc = ksmbd_share_config_get(work, share_name); if (!new_sc) { pr_err("Failed to update stale share config\n"); status.ret = -ESTALE; @@ -73,10 +75,12 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, tree_conn->user = sess->user; tree_conn->share_conf = sc; + tree_conn->t_state = TREE_NEW; status.tree_conn = tree_conn; + atomic_set(&tree_conn->refcount, 1); ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, - GFP_KERNEL)); + KSMBD_DEFAULT_GFP)); if (ret) { status.ret = -ENOMEM; goto out_error; @@ -93,16 +97,26 @@ out_error: return status; } +void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) +{ + if (atomic_dec_and_test(&tcon->refcount)) + kfree(tcon); +} + int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *tree_conn) { int ret; + write_lock(&sess->tree_conns_lock); + xa_erase(&sess->tree_conns, tree_conn->id); + write_unlock(&sess->tree_conns_lock); + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); ksmbd_release_tree_conn_id(sess, tree_conn->id); - xa_erase(&sess->tree_conns, tree_conn->id); ksmbd_share_config_put(tree_conn->share_conf); - kfree(tree_conn); + if (atomic_dec_and_test(&tree_conn->refcount)) + kfree(tree_conn); return ret; } @@ -111,11 +125,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, { struct ksmbd_tree_connect *tcon; + read_lock(&sess->tree_conns_lock); tcon = xa_load(&sess->tree_conns, id); if (tcon) { - if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) + if (tcon->t_state != TREE_CONNECTED) + tcon = NULL; + else if (!atomic_inc_not_zero(&tcon->refcount)) tcon = NULL; } + read_unlock(&sess->tree_conns_lock); return tcon; } @@ -129,8 +147,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) if (!sess) return -EINVAL; - xa_for_each(&sess->tree_conns, id, tc) + xa_for_each(&sess->tree_conns, id, tc) { + write_lock(&sess->tree_conns_lock); + if (tc->t_state == TREE_DISCONNECTED) { + write_unlock(&sess->tree_conns_lock); + ret = -ENOENT; + continue; + } + tc->t_state = TREE_DISCONNECTED; + write_unlock(&sess->tree_conns_lock); + ret |= ksmbd_tree_conn_disconnect(sess, tc); + } xa_destroy(&sess->tree_conns); return ret; } diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h index 562d647ad9fa..f0023d86716f 100644 --- a/fs/smb/server/mgmt/tree_connect.h +++ b/fs/smb/server/mgmt/tree_connect.h @@ -13,8 +13,13 @@ struct ksmbd_share_config; struct ksmbd_user; struct ksmbd_conn; +struct ksmbd_work; -#define TREE_CONN_EXPIRE 1 +enum { + TREE_NEW = 0, + TREE_CONNECTED, + TREE_DISCONNECTED +}; struct ksmbd_tree_connect { int id; @@ -27,7 +32,8 @@ struct ksmbd_tree_connect { int maximal_access; bool posix_extensions; - unsigned long status; + atomic_t refcount; + unsigned int t_state; }; struct ksmbd_tree_conn_status { @@ -44,8 +50,8 @@ static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, struct ksmbd_session; struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, - const char *share_name); +ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name); +void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon); int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *tree_conn); diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c index 279d00feff21..56c9a38ca878 100644 --- a/fs/smb/server/mgmt/user_config.c +++ b/fs/smb/server/mgmt/user_config.c @@ -12,6 +12,7 @@ struct ksmbd_user *ksmbd_login_user(const char *account) { struct ksmbd_login_response *resp; + struct ksmbd_login_response_ext *resp_ext = NULL; struct ksmbd_user *user = NULL; resp = ksmbd_ipc_login_request(account); @@ -21,41 +22,69 @@ struct ksmbd_user *ksmbd_login_user(const char *account) if (!(resp->status & KSMBD_USER_FLAG_OK)) goto out; - user = ksmbd_alloc_user(resp); + if (resp->status & KSMBD_USER_FLAG_EXTENSION) + resp_ext = ksmbd_ipc_login_request_ext(account); + + user = ksmbd_alloc_user(resp, resp_ext); out: kvfree(resp); return user; } -struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, + struct ksmbd_login_response_ext *resp_ext) { - struct ksmbd_user *user = NULL; + struct ksmbd_user *user; - user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); + user = kmalloc(sizeof(struct ksmbd_user), KSMBD_DEFAULT_GFP); if (!user) return NULL; - user->name = kstrdup(resp->account, GFP_KERNEL); + user->name = kstrdup(resp->account, KSMBD_DEFAULT_GFP); user->flags = resp->status; user->gid = resp->gid; user->uid = resp->uid; user->passkey_sz = resp->hash_sz; - user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); + user->passkey = kmalloc(resp->hash_sz, KSMBD_DEFAULT_GFP); if (user->passkey) memcpy(user->passkey, resp->hash, resp->hash_sz); - if (!user->name || !user->passkey) { - kfree(user->name); - kfree(user->passkey); - kfree(user); - user = NULL; + user->ngroups = 0; + user->sgid = NULL; + + if (!user->name || !user->passkey) + goto err_free; + + if (resp_ext) { + if (resp_ext->ngroups > NGROUPS_MAX) { + pr_err("ngroups(%u) from login response exceeds max groups(%d)\n", + resp_ext->ngroups, NGROUPS_MAX); + goto err_free; + } + + user->sgid = kmemdup(resp_ext->____payload, + resp_ext->ngroups * sizeof(gid_t), + KSMBD_DEFAULT_GFP); + if (!user->sgid) + goto err_free; + + user->ngroups = resp_ext->ngroups; + ksmbd_debug(SMB, "supplementary groups : %d\n", user->ngroups); } + return user; + +err_free: + kfree(user->name); + kfree(user->passkey); + kfree(user); + return NULL; } void ksmbd_free_user(struct ksmbd_user *user) { ksmbd_ipc_logout_request(user->name, user->flags); + kfree(user->sgid); kfree(user->name); kfree(user->passkey); kfree(user); diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h index 6a44109617f1..8c227b8d4954 100644 --- a/fs/smb/server/mgmt/user_config.h +++ b/fs/smb/server/mgmt/user_config.h @@ -18,7 +18,8 @@ struct ksmbd_user { size_t passkey_sz; char *passkey; - unsigned int failed_login_count; + int ngroups; + gid_t *sgid; }; static inline bool user_guest(struct ksmbd_user *user) @@ -61,7 +62,8 @@ static inline unsigned int user_gid(struct ksmbd_user *user) } struct ksmbd_user *ksmbd_login_user(const char *account); -struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, + struct ksmbd_login_response_ext *resp_ext); void ksmbd_free_user(struct ksmbd_user *user); int ksmbd_anonymous_user(struct ksmbd_user *user); bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index 8a5dcab05614..1c181ef99929 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -18,7 +18,7 @@ static DEFINE_IDA(session_ida); -#define SESSION_HASH_BITS 3 +#define SESSION_HASH_BITS 12 static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); static DECLARE_RWSEM(sessions_table_lock); @@ -59,10 +59,12 @@ static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) struct ksmbd_session_rpc *entry; long index; + down_write(&sess->rpc_lock); xa_for_each(&sess->rpc_handle_list, index, entry) { xa_erase(&sess->rpc_handle_list, index); __session_rpc_close(sess, entry); } + up_write(&sess->rpc_lock); xa_destroy(&sess->rpc_handle_list); } @@ -90,32 +92,41 @@ static int __rpc_method(char *rpc_name) int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) { - struct ksmbd_session_rpc *entry; + struct ksmbd_session_rpc *entry, *old; struct ksmbd_rpc_command *resp; - int method; + int method, id; method = __rpc_method(rpc_name); if (!method) return -EINVAL; - entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + entry = kzalloc(sizeof(struct ksmbd_session_rpc), KSMBD_DEFAULT_GFP); if (!entry) return -ENOMEM; entry->method = method; - entry->id = ksmbd_ipc_id_alloc(); - if (entry->id < 0) + entry->id = id = ksmbd_ipc_id_alloc(); + if (id < 0) goto free_entry; - xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL); - resp = ksmbd_rpc_open(sess, entry->id); - if (!resp) + down_write(&sess->rpc_lock); + old = xa_store(&sess->rpc_handle_list, id, entry, KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + up_write(&sess->rpc_lock); + goto free_id; + } + + resp = ksmbd_rpc_open(sess, id); + if (!resp) { + xa_erase(&sess->rpc_handle_list, entry->id); + up_write(&sess->rpc_lock); goto free_id; + } + up_write(&sess->rpc_lock); kvfree(resp); - return entry->id; + return id; free_id: - xa_erase(&sess->rpc_handle_list, entry->id); ksmbd_rpc_id_free(entry->id); free_entry: kfree(entry); @@ -126,16 +137,20 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) { struct ksmbd_session_rpc *entry; + down_write(&sess->rpc_lock); entry = xa_erase(&sess->rpc_handle_list, id); if (entry) __session_rpc_close(sess, entry); + up_write(&sess->rpc_lock); } int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) { struct ksmbd_session_rpc *entry; + lockdep_assert_held(&sess->rpc_lock); entry = xa_load(&sess->rpc_handle_list, id); + return entry ? entry->method : 0; } @@ -149,6 +164,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) ksmbd_tree_conn_session_logoff(sess); ksmbd_destroy_file_table(&sess->file_table); + ksmbd_launch_ksmbd_durable_scavenger(); ksmbd_session_rpc_clear_list(sess); free_channel_list(sess); kfree(sess->Preauth_HashValue); @@ -156,7 +172,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) kfree(sess); } -static struct ksmbd_session *__session_lookup(unsigned long long id) +struct ksmbd_session *__session_lookup(unsigned long long id) { struct ksmbd_session *sess; @@ -175,16 +191,19 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) struct ksmbd_session *sess; down_write(&sessions_table_lock); + down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { - if (sess->state != SMB2_SESSION_VALID || - time_after(jiffies, - sess->last_active + SMB2_SESSION_TIMEOUT)) { + if (atomic_read(&sess->refcnt) <= 1 && + (sess->state != SMB2_SESSION_VALID || + time_after(jiffies, + sess->last_active + SMB2_SESSION_TIMEOUT))) { xa_erase(&conn->sessions, sess->id); hash_del(&sess->hlist); ksmbd_session_destroy(sess); continue; } } + up_write(&conn->session_lock); up_write(&sessions_table_lock); } @@ -194,7 +213,7 @@ int ksmbd_session_register(struct ksmbd_conn *conn, sess->dialect = conn->dialect; memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); ksmbd_expire_session(conn); - return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); + return xa_err(xa_store(&conn->sessions, sess->id, sess, KSMBD_DEFAULT_GFP)); } static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) @@ -223,11 +242,16 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) if (!ksmbd_chann_del(conn, sess) && xa_empty(&sess->ksmbd_chann_list)) { hash_del(&sess->hlist); - ksmbd_session_destroy(sess); + down_write(&conn->session_lock); + xa_erase(&conn->sessions, sess->id); + up_write(&conn->session_lock); + if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); } } } + down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { unsigned long chann_id; struct channel *chann; @@ -241,20 +265,42 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) if (xa_empty(&sess->ksmbd_chann_list)) { xa_erase(&conn->sessions, sess->id); hash_del(&sess->hlist); - ksmbd_session_destroy(sess); + if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); } } + up_write(&conn->session_lock); up_write(&sessions_table_lock); } +bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + down_read(&conn->session_lock); + sess = xa_load(&conn->sessions, id); + if (sess) { + up_read(&conn->session_lock); + return true; + } + up_read(&conn->session_lock); + + return false; +} + struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, unsigned long long id) { struct ksmbd_session *sess; + down_read(&conn->session_lock); sess = xa_load(&conn->sessions, id); - if (sess) + if (sess) { sess->last_active = jiffies; + ksmbd_user_session_get(sess); + } + up_read(&conn->session_lock); return sess; } @@ -265,7 +311,7 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) down_read(&sessions_table_lock); sess = __session_lookup(id); if (sess) - sess->last_active = jiffies; + ksmbd_user_session_get(sess); up_read(&sessions_table_lock); return sess; @@ -284,12 +330,28 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, return sess; } +void ksmbd_user_session_get(struct ksmbd_session *sess) +{ + atomic_inc(&sess->refcnt); +} + +void ksmbd_user_session_put(struct ksmbd_session *sess) +{ + if (!sess) + return; + + if (atomic_read(&sess->refcnt) <= 0) + WARN_ON(1); + else if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); +} + struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, u64 sess_id) { struct preauth_session *sess; - sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + sess = kmalloc(sizeof(struct preauth_session), KSMBD_DEFAULT_GFP); if (!sess) return NULL; @@ -301,6 +363,42 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, return sess; } +void destroy_previous_session(struct ksmbd_conn *conn, + struct ksmbd_user *user, u64 id) +{ + struct ksmbd_session *prev_sess; + struct ksmbd_user *prev_user; + int err; + + down_write(&sessions_table_lock); + down_write(&conn->session_lock); + prev_sess = __session_lookup(id); + if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED) + goto out; + + prev_user = prev_sess->user; + if (!prev_user || + strcmp(user->name, prev_user->name) || + user->passkey_sz != prev_user->passkey_sz || + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) + goto out; + + ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT); + err = ksmbd_conn_wait_idle_sess_id(conn, id); + if (err) { + ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP); + goto out; + } + + ksmbd_destroy_file_table(&prev_sess->file_table); + prev_sess->state = SMB2_SESSION_EXPIRED; + ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP); + ksmbd_launch_ksmbd_durable_scavenger(); +out: + up_write(&conn->session_lock); + up_write(&sessions_table_lock); +} + static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, unsigned long long id) { @@ -337,7 +435,7 @@ static struct ksmbd_session *__session_create(int protocol) if (protocol != CIFDS_SESSION_FLAG_SMB2) return NULL; - sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); + sess = kzalloc(sizeof(struct ksmbd_session), KSMBD_DEFAULT_GFP); if (!sess) return NULL; @@ -351,6 +449,9 @@ static struct ksmbd_session *__session_create(int protocol) xa_init(&sess->ksmbd_chann_list); xa_init(&sess->rpc_handle_list); sess->sequence_number = 1; + rwlock_init(&sess->tree_conns_lock); + atomic_set(&sess->refcnt, 2); + init_rwsem(&sess->rpc_lock); ret = __init_smb2_session(sess); if (ret) diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index f99d475b28db..c5749d6ec715 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -60,6 +60,10 @@ struct ksmbd_session { struct ksmbd_file_table file_table; unsigned long last_active; + rwlock_t tree_conns_lock; + + atomic_t refcnt; + struct rw_semaphore rpc_lock; }; static inline int test_session_flag(struct ksmbd_session *sess, int bit) @@ -84,11 +88,16 @@ void ksmbd_session_destroy(struct ksmbd_session *sess); struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, unsigned long long id); +bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn, + unsigned long long id); int ksmbd_session_register(struct ksmbd_conn *conn, struct ksmbd_session *sess); void ksmbd_sessions_deregister(struct ksmbd_conn *conn); +struct ksmbd_session *__session_lookup(unsigned long long id); struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, unsigned long long id); +void destroy_previous_session(struct ksmbd_conn *conn, + struct ksmbd_user *user, u64 id); struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, u64 sess_id); struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, @@ -100,4 +109,6 @@ void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +void ksmbd_user_session_get(struct ksmbd_session *sess); +void ksmbd_user_session_put(struct ksmbd_session *sess); #endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c index 9e8afaa686e3..a543ec9d3581 100644 --- a/fs/smb/server/misc.c +++ b/fs/smb/server/misc.c @@ -164,8 +164,10 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share, { char *pathname, *ab_pathname, *nt_pathname; int share_path_len = share->path_sz; + size_t ab_pathname_len; + int prefix; - pathname = kmalloc(PATH_MAX, GFP_KERNEL); + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); if (!pathname) return ERR_PTR(-EACCES); @@ -180,14 +182,18 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share, goto free_pathname; } - nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); + ab_pathname_len = strlen(&ab_pathname[share_path_len]); + prefix = ab_pathname[share_path_len] == '\0' ? 1 : 0; + nt_pathname = kmalloc(prefix + ab_pathname_len + 1, KSMBD_DEFAULT_GFP); if (!nt_pathname) { nt_pathname = ERR_PTR(-ENOMEM); goto free_pathname; } - if (ab_pathname[share_path_len] == '\0') - strcpy(nt_pathname, "/"); - strcat(nt_pathname, &ab_pathname[share_path_len]); + + if (prefix) + *nt_pathname = '/'; + memcpy(nt_pathname + prefix, &ab_pathname[share_path_len], + ab_pathname_len + 1); ksmbd_conv_path_to_windows(nt_pathname); @@ -232,7 +238,7 @@ char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) char *cf_name; int cf_len; - cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); + cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, KSMBD_DEFAULT_GFP); if (!cf_name) return ERR_PTR(-ENOMEM); @@ -261,6 +267,7 @@ out_ascii: /** * ksmbd_extract_sharename() - get share name from tree connect request + * @um: pointer to a unicode_map structure for character encoding handling * @treename: buffer containing tree name and share name * * Return: share name on success, otherwise error @@ -293,7 +300,7 @@ char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) path_len = share->path_sz; name_len = strlen(name); - new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP); if (!new_name) return new_name; @@ -319,7 +326,7 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, if (!sz) return NULL; - conv = kmalloc(sz, GFP_KERNEL); + conv = kmalloc(sz, KSMBD_DEFAULT_GFP); if (!conv) return NULL; diff --git a/fs/smb/server/ndr.c b/fs/smb/server/ndr.c index 3507d8f89074..58d71560f626 100644 --- a/fs/smb/server/ndr.c +++ b/fs/smb/server/ndr.c @@ -18,7 +18,7 @@ static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) { char *data; - data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + data = krealloc(n->data, n->offset + sz + 1024, KSMBD_DEFAULT_GFP); if (!data) return -ENOMEM; @@ -174,7 +174,7 @@ int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) n->offset = 0; n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); + n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP); if (!n->data) return -ENOMEM; @@ -350,7 +350,7 @@ int ndr_encode_posix_acl(struct ndr *n, n->offset = 0; n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); + n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP); if (!n->data) return -ENOMEM; @@ -401,7 +401,7 @@ int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) n->offset = 0; n->length = 2048; - n->data = kzalloc(n->length, GFP_KERNEL); + n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP); if (!n->data) return -ENOMEM; @@ -505,7 +505,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) return ret; acl->sd_size = n->length - n->offset; - acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + acl->sd_buf = kzalloc(acl->sd_size, KSMBD_DEFAULT_GFP); if (!acl->sd_buf) return -ENOMEM; diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 844b303baf29..1f07ebf431d7 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -10,7 +10,7 @@ #include "oplock.h" #include "smb_common.h" -#include "smbstatus.h" +#include "../common/smb2status.h" #include "connection.h" #include "mgmt/user_session.h" #include "mgmt/share_config.h" @@ -34,7 +34,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; - opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + opinfo = kzalloc(sizeof(struct oplock_info), KSMBD_DEFAULT_GFP); if (!opinfo) return NULL; @@ -46,11 +46,11 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, opinfo->fid = id; opinfo->Tid = Tid; INIT_LIST_HEAD(&opinfo->op_entry); - INIT_LIST_HEAD(&opinfo->interim_list); init_waitqueue_head(&opinfo->oplock_q); init_waitqueue_head(&opinfo->oplock_brk); atomic_set(&opinfo->refcount, 1); atomic_set(&opinfo->breaking_cnt, 0); + atomic_inc(&opinfo->conn->refcnt); return opinfo; } @@ -93,7 +93,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) { struct lease *lease; - lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + lease = kmalloc(sizeof(struct lease), KSMBD_DEFAULT_GFP); if (!lease) return -ENOMEM; @@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) lease->new_state = 0; lease->flags = lctx->flags; lease->duration = lctx->duration; + lease->is_dir = lctx->is_dir; memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); lease->version = lctx->version; - lease->epoch = 0; + lease->epoch = le16_to_cpu(lctx->epoch) + 1; INIT_LIST_HEAD(&opinfo->lease_entry); opinfo->o_lease = lease; @@ -123,17 +124,11 @@ static void free_opinfo(struct oplock_info *opinfo) { if (opinfo->is_lease) free_lease(opinfo); + if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt)) + kfree(opinfo->conn); kfree(opinfo); } -static inline void opinfo_free_rcu(struct rcu_head *rcu_head) -{ - struct oplock_info *opinfo; - - opinfo = container_of(rcu_head, struct oplock_info, rcu_head); - free_opinfo(opinfo); -} - struct oplock_info *opinfo_get(struct ksmbd_file *fp) { struct oplock_info *opinfo; @@ -151,63 +146,43 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) { struct oplock_info *opinfo; - if (list_empty(&ci->m_op_list)) - return NULL; - - rcu_read_lock(); - opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, - op_entry); + down_read(&ci->m_lock); + opinfo = list_first_entry_or_null(&ci->m_op_list, struct oplock_info, + op_entry); if (opinfo) { - if (!atomic_inc_not_zero(&opinfo->refcount)) + if (opinfo->conn == NULL || + !atomic_inc_not_zero(&opinfo->refcount)) opinfo = NULL; else { - atomic_inc(&opinfo->conn->r_count); if (ksmbd_conn_releasing(opinfo->conn)) { - atomic_dec(&opinfo->conn->r_count); atomic_dec(&opinfo->refcount); opinfo = NULL; } } } - - rcu_read_unlock(); + up_read(&ci->m_lock); return opinfo; } -static void opinfo_conn_put(struct oplock_info *opinfo) +void opinfo_put(struct oplock_info *opinfo) { - struct ksmbd_conn *conn; - if (!opinfo) return; - conn = opinfo->conn; - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); - opinfo_put(opinfo); -} - -void opinfo_put(struct oplock_info *opinfo) -{ if (!atomic_dec_and_test(&opinfo->refcount)) return; - call_rcu(&opinfo->rcu_head, opinfo_free_rcu); + free_opinfo(opinfo); } static void opinfo_add(struct oplock_info *opinfo) { struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - write_lock(&ci->m_lock); - list_add_rcu(&opinfo->op_entry, &ci->m_op_list); - write_unlock(&ci->m_lock); + down_write(&ci->m_lock); + list_add(&opinfo->op_entry, &ci->m_op_list); + up_write(&ci->m_lock); } static void opinfo_del(struct oplock_info *opinfo) @@ -219,9 +194,9 @@ static void opinfo_del(struct oplock_info *opinfo) lease_del_list(opinfo); write_unlock(&lease_list_lock); } - write_lock(&ci->m_lock); - list_del_rcu(&opinfo->op_entry); - write_unlock(&ci->m_lock); + down_write(&ci->m_lock); + list_del(&opinfo->op_entry); + up_write(&ci->m_lock); } static unsigned long opinfo_count(struct ksmbd_file *fp) @@ -395,8 +370,8 @@ void close_id_del_oplock(struct ksmbd_file *fp) { struct oplock_info *opinfo; - if (S_ISDIR(file_inode(fp->filp)->i_mode)) - return; + if (fp->reserve_lease_break) + smb_lazy_parent_lease_break_close(fp); opinfo = opinfo_get(fp); if (!opinfo) @@ -524,47 +499,49 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, * Compare lease key and client_guid to know request from same owner * of same client */ - read_lock(&ci->m_lock); + down_read(&ci->m_lock); list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { - if (!opinfo->is_lease) + if (!opinfo->is_lease || !opinfo->conn) continue; - read_unlock(&ci->m_lock); lease = opinfo->o_lease; ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); if (ret) { m_opinfo = opinfo; /* skip upgrading lease about breaking lease */ - if (atomic_read(&opinfo->breaking_cnt)) { - read_lock(&ci->m_lock); + if (atomic_read(&opinfo->breaking_cnt)) continue; - } /* upgrading lease */ if ((atomic_read(&ci->op_count) + atomic_read(&ci->sop_count)) == 1) { - if (lease->state == - (lctx->req_state & lease->state)) { + if (lease->state != SMB2_LEASE_NONE_LE && + lease->state == (lctx->req_state & lease->state)) { + lease->epoch++; lease->state |= lctx->req_state; if (lctx->req_state & SMB2_LEASE_WRITE_CACHING_LE) lease_read_to_write(opinfo); + } } else if ((atomic_read(&ci->op_count) + atomic_read(&ci->sop_count)) > 1) { if (lctx->req_state == (SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) + SMB2_LEASE_HANDLE_CACHING_LE)) { + lease->epoch++; lease->state = lctx->req_state; + } } if (lctx->req_state && lease->state == - SMB2_LEASE_NONE_LE) + SMB2_LEASE_NONE_LE) { + lease->epoch++; lease_none_upgrade(opinfo, lctx->req_state); + } } - read_lock(&ci->m_lock); } - read_unlock(&ci->m_lock); + up_read(&ci->m_lock); return m_opinfo; } @@ -605,26 +582,32 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) if (opinfo->op_state == OPLOCK_CLOSING) return -ENOENT; - else if (!opinfo->is_lease && opinfo->level <= req_op_level) - return 1; + else if (opinfo->level <= req_op_level) { + if (opinfo->is_lease == false) + return 1; + + if (opinfo->o_lease->state != + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE)) + return 1; + } } - if (!opinfo->is_lease && opinfo->level <= req_op_level) { - wake_up_oplock_break(opinfo); - return 1; + if (opinfo->level <= req_op_level) { + if (opinfo->is_lease == false) { + wake_up_oplock_break(opinfo); + return 1; + } + if (opinfo->o_lease->state != + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE)) { + wake_up_oplock_break(opinfo); + return 1; + } } return 0; } -static inline int allocate_oplock_break_buf(struct ksmbd_work *work) -{ - work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); - if (!work->response_buf) - return -ENOMEM; - work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - return 0; -} - /** * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn * to client @@ -644,11 +627,11 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) struct smb2_hdr *rsp_hdr; struct ksmbd_file *fp; - fp = ksmbd_lookup_durable_fd(br_info->fid); + fp = ksmbd_lookup_global_fd(br_info->fid); if (!fp) goto out; - if (allocate_oplock_break_buf(work)) { + if (allocate_interim_rsp_buf(work)) { pr_err("smb2_allocate_rsp_buf failed! "); ksmbd_fd_put(work, fp); goto out; @@ -656,8 +639,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(0); @@ -684,17 +665,20 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp->PersistentFid = fp->persistent_id; rsp->VolatileFid = fp->volatile_id; - inc_rfc1001_len(work->response_buf, 24); + ksmbd_fd_put(work, fp); + if (ksmbd_iov_pin_rsp(work, (void *)rsp, + sizeof(struct smb2_oplock_break))) + goto out; ksmbd_debug(OPLOCK, "sending oplock break v_id %llu p_id = %llu lock level = %d\n", rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); - ksmbd_fd_put(work, fp); ksmbd_conn_write(work); out: ksmbd_free_work_struct(work); + ksmbd_conn_r_count_dec(conn); } /** @@ -714,7 +698,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) if (!work) return -ENOMEM; - br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + br_info = kmalloc(sizeof(struct oplock_break_info), KSMBD_DEFAULT_GFP); if (!br_info) { ksmbd_free_work_struct(work); return -ENOMEM; @@ -728,6 +712,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) work->conn = conn; work->sess = opinfo->sess; + ksmbd_conn_r_count_inc(conn); if (opinfo->op_state == OPLOCK_ACK_WAIT) { INIT_WORK(&work->work, __smb2_oplock_break_noti); ksmbd_queue_work(work); @@ -750,19 +735,17 @@ static void __smb2_lease_break_noti(struct work_struct *wk) { struct smb2_lease_break *rsp = NULL; struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct lease_break_info *br_info = work->request_buf; struct ksmbd_conn *conn = work->conn; + struct lease_break_info *br_info = work->request_buf; struct smb2_hdr *rsp_hdr; - if (allocate_oplock_break_buf(work)) { + if (allocate_interim_rsp_buf(work)) { ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); goto out; } rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(0); @@ -791,25 +774,27 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp->AccessMaskHint = 0; rsp->ShareMaskHint = 0; - inc_rfc1001_len(work->response_buf, 44); + if (ksmbd_iov_pin_rsp(work, (void *)rsp, + sizeof(struct smb2_lease_break))) + goto out; ksmbd_conn_write(work); out: ksmbd_free_work_struct(work); + ksmbd_conn_r_count_dec(conn); } /** * smb2_lease_break_noti() - break lease when a new client request * write lease - * @opinfo: conains lease state information + * @opinfo: contains lease state information * * Return: 0 on success, otherwise error */ static int smb2_lease_break_noti(struct oplock_info *opinfo) { struct ksmbd_conn *conn = opinfo->conn; - struct list_head *tmp, *t; struct ksmbd_work *work; struct lease_break_info *br_info; struct lease *lease = opinfo->o_lease; @@ -818,7 +803,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) if (!work) return -ENOMEM; - br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + br_info = kmalloc(sizeof(struct lease_break_info), KSMBD_DEFAULT_GFP); if (!br_info) { ksmbd_free_work_struct(work); return -ENOMEM; @@ -836,16 +821,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) work->conn = conn; work->sess = opinfo->sess; + ksmbd_conn_r_count_inc(conn); if (opinfo->op_state == OPLOCK_ACK_WAIT) { - list_for_each_safe(tmp, t, &opinfo->interim_list) { - struct ksmbd_work *in_work; - - in_work = list_entry(tmp, struct ksmbd_work, - interim_entry); - setup_async_work(in_work, NULL, NULL); - smb2_send_interim_resp(in_work, STATUS_PENDING); - list_del(&in_work->interim_entry); - } INIT_WORK(&work->work, __smb2_lease_break_noti); ksmbd_queue_work(work); wait_for_break_ack(opinfo); @@ -876,7 +853,8 @@ static void wait_lease_breaking(struct oplock_info *opinfo) } } -static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) +static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, + struct ksmbd_work *in_work) { int err = 0; @@ -889,7 +867,6 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) struct lease *lease = brk_opinfo->o_lease; atomic_inc(&brk_opinfo->breaking_cnt); - err = oplock_break_pending(brk_opinfo, req_op_level); if (err) return err < 0 ? err : 0; @@ -910,7 +887,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) lease->new_state = SMB2_LEASE_READ_CACHING_LE; } else { - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE && + !lease->is_dir) lease->new_state = SMB2_LEASE_READ_CACHING_LE; else @@ -919,9 +897,15 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) } if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) + SMB2_LEASE_HANDLE_CACHING_LE)) { + if (in_work) { + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + release_async_work(in_work); + } + brk_opinfo->op_state = OPLOCK_ACK_WAIT; - else + } else atomic_dec(&brk_opinfo->breaking_cnt); } else { err = oplock_break_pending(brk_opinfo, req_op_level); @@ -1042,6 +1026,8 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) SMB2_LEASE_KEY_SIZE); lease2->duration = lease1->duration; lease2->flags = lease1->flags; + lease2->epoch = lease1->epoch; + lease2->version = lease1->version; } static int add_lease_global_list(struct oplock_info *opinfo) @@ -1060,7 +1046,7 @@ static int add_lease_global_list(struct oplock_info *opinfo) } read_unlock(&lease_list_lock); - lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + lb = kmalloc(sizeof(struct lease_table), KSMBD_DEFAULT_GFP); if (!lb) return -ENOMEM; @@ -1091,6 +1077,84 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, } } +void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + struct ksmbd_inode *p_ci = NULL; + + if (lctx->version != 2) + return; + + p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); + if (!p_ci) + return; + + down_read(&p_ci->m_lock); + list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { + if (opinfo->conn == NULL || !opinfo->is_lease) + continue; + + if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE && + (!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) || + !compare_guid_key(opinfo, fp->conn->ClientGUID, + lctx->parent_lease_key))) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + + if (ksmbd_conn_releasing(opinfo->conn)) { + opinfo_put(opinfo); + continue; + } + + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); + opinfo_put(opinfo); + } + } + up_read(&p_ci->m_lock); + + ksmbd_inode_put(p_ci); +} + +void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + struct ksmbd_inode *p_ci = NULL; + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rcu_read_unlock(); + + if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2) + return; + + p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); + if (!p_ci) + return; + + down_read(&p_ci->m_lock); + list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { + if (opinfo->conn == NULL || !opinfo->is_lease) + continue; + + if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + + if (ksmbd_conn_releasing(opinfo->conn)) { + opinfo_put(opinfo); + continue; + } + + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); + opinfo_put(opinfo); + } + } + up_read(&p_ci->m_lock); + + ksmbd_inode_put(p_ci); +} + /** * smb_grant_oplock() - handle oplock/lease request on file open * @work: smb work @@ -1114,9 +1178,13 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, bool prev_op_has_lease; __le32 prev_op_state = 0; - /* not support directory lease */ - if (S_ISDIR(file_inode(fp->filp)->i_mode)) - return 0; + /* Only v2 leases handle the directory */ + if (S_ISDIR(file_inode(fp->filp)->i_mode)) { + if (!lctx || lctx->version != 2 || + (lctx->flags != SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE && + !lctx->epoch)) + return 0; + } opinfo = alloc_opinfo(work, pid, tid); if (!opinfo) @@ -1158,7 +1226,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_opinfo = opinfo_get_list(ci); if (!prev_opinfo || (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { - opinfo_conn_put(prev_opinfo); + opinfo_put(prev_opinfo); goto set_lev; } prev_op_has_lease = prev_opinfo->is_lease; @@ -1168,19 +1236,18 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (share_ret < 0 && prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { err = share_ret; - opinfo_conn_put(prev_opinfo); + opinfo_put(prev_opinfo); goto err_out; } if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_conn_put(prev_opinfo); + opinfo_put(prev_opinfo); goto op_break_not_needed; } - list_add(&work->interim_entry, &prev_opinfo->interim_list); - err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_conn_put(prev_opinfo); + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work); + opinfo_put(prev_opinfo); if (err == -ENOENT) goto set_lev; /* Check all oplock was freed by close */ @@ -1243,14 +1310,13 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work, return; if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_conn_put(brk_opinfo); + opinfo_put(brk_opinfo); return; } brk_opinfo->open_trunc = is_trunc; - list_add(&work->interim_entry, &brk_opinfo->interim_list); - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_conn_put(brk_opinfo); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work); + opinfo_put(brk_opinfo); } /** @@ -1274,18 +1340,19 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, ci = fp->f_ci; op = opinfo_get(fp); - rcu_read_lock(); - list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + down_read(&ci->m_lock); + list_for_each_entry(brk_op, &ci->m_op_list, op_entry) { + if (brk_op->conn == NULL) + continue; + if (!atomic_inc_not_zero(&brk_op->refcount)) continue; - atomic_inc(&brk_op->conn->r_count); if (ksmbd_conn_releasing(brk_op->conn)) { - atomic_dec(&brk_op->conn->r_count); + opinfo_put(brk_op); continue; } - rcu_read_unlock(); if (brk_op->is_lease && (brk_op->o_lease->state & (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)))) { @@ -1312,12 +1379,11 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; - oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL); next: - opinfo_conn_put(brk_op); - rcu_read_lock(); + opinfo_put(brk_op); } - rcu_read_unlock(); + up_read(&ci->m_lock); if (op) opinfo_put(op); @@ -1374,9 +1440,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.Epoch = cpu_to_le16(lease->epoch); buf->lcontext.LeaseState = lease->state; - memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, - SMB2_LEASE_KEY_SIZE); + if (lease->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_lease_v2, lcontext)); buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); @@ -1408,10 +1476,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) } /** - * parse_lease_state() - parse lease context containted in file open request + * parse_lease_state() - parse lease context contained in file open request * @open_req: buffer containing smb2 file open(create) request * - * Return: oplock state, -ENOENT if create lease context not found + * Return: allocated lease context object on success, otherwise NULL */ struct lease_ctx_info *parse_lease_state(void *open_req) { @@ -1423,23 +1491,33 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (IS_ERR_OR_NULL(cc)) return NULL; - lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); + lreq = kzalloc(sizeof(struct lease_ctx_info), KSMBD_DEFAULT_GFP); if (!lreq) return NULL; if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) < + sizeof(struct create_lease_v2) - 4) + goto err_out; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; + lreq->epoch = lc->lcontext.Epoch; lreq->duration = lc->lcontext.LeaseDuration; - memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, - SMB2_LEASE_KEY_SIZE); + if (lreq->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); lreq->version = 2; } else { struct create_lease *lc = (struct create_lease *)cc; + if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) < + sizeof(struct create_lease)) + goto err_out; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; @@ -1447,6 +1525,9 @@ struct lease_ctx_info *parse_lease_state(void *open_req) lreq->version = 1; } return lreq; +err_out: + kfree(lreq); + return NULL; } /** @@ -1492,7 +1573,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag, i name_len < 4 || name_off + name_len > cc_len || (value_off & 0x7) != 0 || - (value_off && (value_off < name_off + name_len)) || + (value_len && value_off < name_off + (name_len < 8 ? 8 : name_len)) || ((u64)value_off + value_len > cc_len)) return ERR_PTR(-EINVAL); @@ -1536,9 +1617,9 @@ void create_durable_rsp_buf(char *cc) */ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) { - struct create_durable_v2_rsp *buf; + struct create_durable_rsp_v2 *buf; - buf = (struct create_durable_v2_rsp *)cc; + buf = (struct create_durable_rsp_v2 *)cc; memset(buf, 0, sizeof(struct create_durable_rsp)); buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_durable_rsp, Data)); @@ -1552,7 +1633,9 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) buf->Name[2] = '2'; buf->Name[3] = 'Q'; - buf->Timeout = cpu_to_le32(fp->durable_timeout); + buf->dcontext.Timeout = cpu_to_le32(fp->durable_timeout); + if (fp->is_persistent) + buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); } /** @@ -1720,3 +1803,71 @@ out: read_unlock(&lease_list_lock); return ret_op; } + +int smb2_check_durable_oplock(struct ksmbd_conn *conn, + struct ksmbd_share_config *share, + struct ksmbd_file *fp, + struct lease_ctx_info *lctx, + char *name) +{ + struct oplock_info *opinfo = opinfo_get(fp); + int ret = 0; + + if (!opinfo) + return 0; + + if (opinfo->is_lease == false) { + if (lctx) { + pr_err("create context include lease\n"); + ret = -EBADF; + goto out; + } + + if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) { + pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n"); + ret = -EBADF; + } + + goto out; + } + + if (memcmp(conn->ClientGUID, fp->client_guid, + SMB2_CLIENT_GUID_SIZE)) { + ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n"); + ret = -EBADF; + goto out; + } + + if (!lctx) { + ksmbd_debug(SMB, "create context does not include lease\n"); + ret = -EBADF; + goto out; + } + + if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE)) { + ksmbd_debug(SMB, + "lease key of fp does not match lease key in create context\n"); + ret = -EBADF; + goto out; + } + + if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) { + ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n"); + ret = -EBADF; + goto out; + } + + if (opinfo->o_lease->version != lctx->version) { + ksmbd_debug(SMB, + "lease version of fp does not match the one in create context\n"); + ret = -EBADF; + goto out; + } + + if (!ksmbd_inode_pending_delete(fp)) + ret = ksmbd_validate_name_reconnect(share, fp, name); +out: + opinfo_put(opinfo); + return ret; +} diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 4b0fe6da7694..9a56eaadd0dd 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -11,13 +11,6 @@ #define OPLOCK_WAIT_TIME (35 * HZ) -/* SMB2 Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF - /* Oplock states */ #define OPLOCK_STATE_NONE 0x00 #define OPLOCK_ACK_WAIT 0x01 @@ -34,7 +27,9 @@ struct lease_ctx_info { __le32 flags; __le64 duration; __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + __le16 epoch; int version; + bool is_dir; }; struct lease_table { @@ -53,6 +48,7 @@ struct lease { __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; int version; unsigned short epoch; + bool is_dir; struct lease_table *l_lb; }; @@ -71,12 +67,10 @@ struct oplock_info { bool is_lease; bool open_trunc; /* truncate on open */ struct lease *o_lease; - struct list_head interim_list; struct list_head op_entry; struct list_head lease_entry; wait_queue_head_t oplock_q; /* Other server threads */ wait_queue_head_t oplock_brk; /* oplock breaking wait */ - struct rcu_head rcu_head; }; struct lease_break_info { @@ -124,4 +118,12 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, struct lease_ctx_info *lctx); void destroy_lease_table(struct ksmbd_conn *conn); +void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, + struct lease_ctx_info *lctx); +void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp); +int smb2_check_durable_oplock(struct ksmbd_conn *conn, + struct ksmbd_share_config *share, + struct ksmbd_file *fp, + struct lease_ctx_info *lctx, + char *name); #endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 9df121bdf349..3cea16050e4f 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -15,7 +15,7 @@ #include "server.h" #include "smb_common.h" -#include "smbstatus.h" +#include "../common/smb2status.h" #include "connection.h" #include "transport_ipc.h" #include "mgmt/user_session.h" @@ -47,7 +47,7 @@ static int ___server_conf_set(int idx, char *val) return -EINVAL; kfree(server_conf.conf[idx]); - server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + server_conf.conf[idx] = kstrdup(val, KSMBD_DEFAULT_GFP); if (!server_conf.conf[idx]) return -ENOMEM; return 0; @@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, if (check_conn_state(work)) return SERVER_HANDLER_CONTINUE; - if (ksmbd_verify_smb_message(work)) + if (ksmbd_verify_smb_message(work)) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); return SERVER_HANDLER_ABORT; + } command = conn->ops->get_cmd_val(work); *cmd = command; @@ -163,21 +165,19 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, { u16 command = 0; int rc; - - if (conn->ops->allocate_rsp_buf(work)) - return; + bool is_chained = false; if (conn->ops->is_transform_hdr && conn->ops->is_transform_hdr(work->request_buf)) { rc = conn->ops->decrypt_req(work); - if (rc < 0) { - conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); - goto send; - } - + if (rc < 0) + return; work->encrypted = true; } + if (conn->ops->allocate_rsp_buf(work)) + return; + rc = conn->ops->init_rsp_hdr(work); if (rc) { /* either uid or tid is not correct */ @@ -229,16 +229,17 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, } } + is_chained = is_chained_smb2_message(work); + if (work->sess && (work->sess->sign || smb3_11_final_sess_setup_resp(work) || conn->ops->is_sign_req(work, command))) conn->ops->set_sign_rsp(work); - } while (is_chained_smb2_message(work)); - - if (work->send_no_response) - return; + } while (is_chained == true); send: + if (work->tcon) + ksmbd_tree_connect_put(work->tcon); smb3_preauth_hash_rsp(work); if (work->sess && work->sess->enc && work->encrypted && conn->ops->encrypt_resp) { @@ -246,6 +247,8 @@ send: if (rc < 0) conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); } + if (work->sess) + ksmbd_user_session_put(work->sess); ksmbd_conn_write(work); } @@ -267,18 +270,12 @@ static void handle_ksmbd_work(struct work_struct *wk) ksmbd_conn_try_dequeue_request(work); ksmbd_free_work_struct(work); - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); + ksmbd_conn_r_count_dec(conn); } /** * queue_ksmbd_work() - queue a smb request to worker thread queue - * for proccessing smb command and sending response + * for processing smb command and sending response * @conn: connection instance * * read remaining data from socket create and submit work. @@ -288,6 +285,10 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn) struct ksmbd_work *work; int err; + err = ksmbd_init_smb_server(conn); + if (err) + return 0; + work = ksmbd_alloc_work_struct(); if (!work) { pr_err("allocation for work failed\n"); @@ -298,14 +299,8 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn) work->request_buf = conn->request_buf; conn->request_buf = NULL; - err = ksmbd_init_smb_server(work); - if (err) { - ksmbd_free_work_struct(work); - return 0; - } - ksmbd_conn_enqueue_request(work); - atomic_inc(&conn->r_count); + ksmbd_conn_r_count_inc(conn); /* update activity on connection */ conn->last_active = jiffies; INIT_WORK(&work->work, handle_ksmbd_work); @@ -356,6 +351,7 @@ static int server_conf_init(void) server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5; #endif + server_conf.max_inflight_req = SMB2_MAX_CREDITS; return 0; } @@ -369,6 +365,7 @@ static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) return; } + pr_info("running\n"); WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); } @@ -376,6 +373,7 @@ static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) { ksmbd_ipc_soft_reset(); ksmbd_conn_transport_destroy(); + ksmbd_stop_durable_scavenger(); server_conf_free(); server_conf_init(); WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); @@ -407,7 +405,7 @@ static int __queue_ctrl_work(int type) { struct server_ctrl_struct *ctrl; - ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + ctrl = kmalloc(sizeof(struct server_ctrl_struct), KSMBD_DEFAULT_GFP); if (!ctrl) return -ENOMEM; @@ -590,8 +588,6 @@ static int __init ksmbd_server_init(void) if (ret) goto err_crypto_destroy; - pr_warn_once("The ksmbd server is experimental\n"); - return 0; err_crypto_destroy: @@ -623,20 +619,14 @@ static void __exit ksmbd_server_exit(void) } MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>"); -MODULE_VERSION(KSMBD_VERSION); MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); MODULE_SOFTDEP("pre: ecb"); -MODULE_SOFTDEP("pre: hmac"); -MODULE_SOFTDEP("pre: md5"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); MODULE_SOFTDEP("pre: cmac"); -MODULE_SOFTDEP("pre: sha256"); -MODULE_SOFTDEP("pre: sha512"); MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("pre: gcm"); -MODULE_SOFTDEP("pre: crc32"); module_init(ksmbd_server_init) module_exit(ksmbd_server_exit) diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h index db7278181760..b8a7317be86b 100644 --- a/fs/smb/server/server.h +++ b/fs/smb/server/server.h @@ -42,8 +42,12 @@ struct ksmbd_server_config { struct smb_sid domain_sid; unsigned int auth_mechs; unsigned int max_connections; + unsigned int max_inflight_req; + unsigned int max_ip_connections; char *conf[SERVER_CONF_WORK_GROUP + 1]; + struct task_struct *dh_task; + bool bind_interfaces_only; }; extern struct ksmbd_server_config server_conf; diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index e881df1d10cb..67a2d7a793f6 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -7,7 +7,7 @@ #include "glob.h" #include "nterr.h" #include "smb_common.h" -#include "smbstatus.h" +#include "../common/smb2status.h" #include "mgmt/user_session.h" #include "connection.h" @@ -101,29 +101,46 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); break; case SMB2_TREE_CONNECT: - *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); + *off = max_t(unsigned short int, + le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset), + offsetof(struct smb2_tree_connect_req, Buffer)); *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); break; case SMB2_CREATE: { + unsigned short int name_off = + max_t(unsigned short int, + le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset), + offsetof(struct smb2_create_req, Buffer)); + unsigned short int name_len = + le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { *off = le32_to_cpu(((struct smb2_create_req *) hdr)->CreateContextsOffset); *len = le32_to_cpu(((struct smb2_create_req *) hdr)->CreateContextsLength); - break; + if (!name_len) + break; + + if (name_off + name_len < (u64)*off + *len) + break; } - *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); - *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + *off = name_off; + *len = name_len; break; } case SMB2_QUERY_INFO: - *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *off = max_t(unsigned int, + le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset), + offsetof(struct smb2_query_info_req, Buffer)); *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); break; case SMB2_SET_INFO: - *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); + *off = max_t(unsigned int, + le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset), + offsetof(struct smb2_set_info_req, Buffer)); *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); break; case SMB2_READ: @@ -133,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, case SMB2_WRITE: if (((struct smb2_write_req *)hdr)->DataOffset || ((struct smb2_write_req *)hdr)->Length) { - *off = max_t(unsigned int, + *off = max_t(unsigned short int, le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), offsetof(struct smb2_write_req, Buffer)); *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); @@ -144,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); break; case SMB2_QUERY_DIRECTORY: - *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *off = max_t(unsigned short int, + le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset), + offsetof(struct smb2_query_directory_req, Buffer)); *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); break; case SMB2_LOCK: @@ -159,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, break; } case SMB2_IOCTL: - *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); + *off = max_t(unsigned int, + le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset), + offsetof(struct smb2_ioctl_req, Buffer)); *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); break; default: @@ -439,11 +460,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } validate_credit: - if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(work->conn, hdr)) { - work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(work->conn, hdr)) return 1; - } return 0; } diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c index aed7704a0672..edd7eca0714a 100644 --- a/fs/smb/server/smb2ops.c +++ b/fs/smb/server/smb2ops.c @@ -15,7 +15,7 @@ static struct smb_version_values smb21_server_values = { .version_string = SMB21_VERSION_STRING, .protocol_id = SMB21_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB21_DEFAULT_IOSIZE, .max_write_size = SMB21_DEFAULT_IOSIZE, .max_trans_size = SMB21_DEFAULT_IOSIZE, @@ -41,7 +41,7 @@ static struct smb_version_values smb21_server_values = { static struct smb_version_values smb30_server_values = { .version_string = SMB30_VERSION_STRING, .protocol_id = SMB30_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB3_DEFAULT_IOSIZE, .max_write_size = SMB3_DEFAULT_IOSIZE, .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, @@ -59,7 +59,7 @@ static struct smb_version_values smb30_server_values = { .cap_large_files = SMB2_LARGE_FILES, .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_durable_v2_size = sizeof(struct create_durable_rsp_v2), .create_mxac_size = sizeof(struct create_mxac_rsp), .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), @@ -68,7 +68,7 @@ static struct smb_version_values smb30_server_values = { static struct smb_version_values smb302_server_values = { .version_string = SMB302_VERSION_STRING, .protocol_id = SMB302_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB3_DEFAULT_IOSIZE, .max_write_size = SMB3_DEFAULT_IOSIZE, .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, @@ -86,7 +86,7 @@ static struct smb_version_values smb302_server_values = { .cap_large_files = SMB2_LARGE_FILES, .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_durable_v2_size = sizeof(struct create_durable_rsp_v2), .create_mxac_size = sizeof(struct create_mxac_rsp), .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), @@ -95,7 +95,7 @@ static struct smb_version_values smb302_server_values = { static struct smb_version_values smb311_server_values = { .version_string = SMB311_VERSION_STRING, .protocol_id = SMB311_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB3_DEFAULT_IOSIZE, .max_write_size = SMB3_DEFAULT_IOSIZE, .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, @@ -113,7 +113,7 @@ static struct smb_version_values smb311_server_values = { .cap_large_files = SMB2_LARGE_FILES, .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_durable_v2_size = sizeof(struct create_durable_rsp_v2), .create_mxac_size = sizeof(struct create_mxac_rsp), .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), @@ -204,7 +204,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn) conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING; } /** @@ -221,14 +221,20 @@ void init_smb3_0_server(struct ksmbd_conn *conn) conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING | + SMB2_GLOBAL_CAP_DIRECTORY_LEASING; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || + (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; } /** @@ -245,15 +251,19 @@ void init_smb3_02_server(struct ksmbd_conn *conn) conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING | + SMB2_GLOBAL_CAP_DIRECTORY_LEASING; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE) + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; } /** @@ -270,15 +280,14 @@ int init_smb3_11_server(struct ksmbd_conn *conn) conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || - (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING | + SMB2_GLOBAL_CAP_DIRECTORY_LEASING; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE) + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; INIT_LIST_HEAD(&conn->preauth_sess_table); return 0; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a947c18915c2..f3184b217575 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -23,6 +23,7 @@ #include "asn1.h" #include "connection.h" #include "transport_ipc.h" +#include "../common/smbdirect/smbdirect.h" #include "transport_rdma.h" #include "vfs.h" #include "vfs_cache.h" @@ -30,7 +31,7 @@ #include "server.h" #include "smb_common.h" -#include "smbstatus.h" +#include "../common/smb2status.h" #include "ksmbd_work.h" #include "mgmt/user_config.h" #include "mgmt/share_config.h" @@ -38,6 +39,7 @@ #include "mgmt/user_session.h" #include "mgmt/ksmbd_ida.h" #include "ndr.h" +#include "transport_tcp.h" static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) { @@ -67,8 +69,10 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) return false; sess = ksmbd_session_lookup_all(conn, id); - if (sess) + if (sess) { + ksmbd_user_session_put(sess); return true; + } pr_err("Invalid user session id: %llu\n", id); return false; } @@ -145,12 +149,18 @@ void smb2_set_err_rsp(struct ksmbd_work *work) err_rsp = smb2_get_msg(work->response_buf); if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { + int err; + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; err_rsp->ErrorContextCount = 0; err_rsp->Reserved = 0; err_rsp->ByteCount = 0; err_rsp->ErrorData[0] = 0; - inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); + err = ksmbd_iov_pin_rsp(work, (void *)err_rsp, + __SMB2_HEADER_STRUCTURE_SIZE + + SMB2_ERROR_STRUCTURE_SIZE2); + if (err) + work->send_no_response = 1; } } @@ -225,11 +235,12 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) { struct smb2_hdr *rsp_hdr; - if (work->next_smb2_rcv_hdr_off) - rsp_hdr = ksmbd_resp_buf_next(work); - else - rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr = smb2_get_msg(work->response_buf); rsp_hdr->Status = err; + + work->iov_idx = 0; + work->iov_cnt = 0; + work->next_smb2_rcv_hdr_off = 0; smb2_set_err_rsp(work); } @@ -245,9 +256,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) struct smb2_hdr *rsp_hdr; struct smb2_negotiate_rsp *rsp; struct ksmbd_conn *conn = work->conn; - - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); + int err; rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); @@ -273,7 +282,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) /* Not setting conn guid rsp->ServerGUID, as it * not used by client for identifying connection */ - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); /* Default Max Message Size till SMB2.0, 64K*/ rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); @@ -286,12 +295,13 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(work->response_buf, - sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) + AUTH_GSS_LENGTH); rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + err = ksmbd_iov_pin_rsp(work, rsp, + sizeof(struct smb2_negotiate_rsp) + AUTH_GSS_LENGTH); + if (err) + return err; conn->use_spnego = true; ksmbd_conn_set_need_negotiate(conn); @@ -390,11 +400,12 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) next_hdr_offset = le32_to_cpu(req->NextCommand); new_len = ALIGN(len, 8); - inc_rfc1001_len(work->response_buf, - sizeof(struct smb2_hdr) + new_len - len); + work->iov[work->iov_idx].iov_len += (new_len - len); + inc_rfc1001_len(work->response_buf, new_len - len); rsp->NextCommand = cpu_to_le32(new_len); work->next_smb2_rcv_hdr_off += next_hdr_offset; + work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; work->next_smb2_rsp_hdr_off += new_len; ksmbd_debug(SMB, "Compound req new_len = %d rcv off = %d rsp off = %d\n", @@ -470,10 +481,10 @@ bool is_chained_smb2_message(struct ksmbd_work *work) len = len - get_rfc1002_len(work->response_buf); if (len) { ksmbd_debug(SMB, "padding len %u\n", len); + work->iov[work->iov_idx].iov_len += len; inc_rfc1001_len(work->response_buf, len); - if (work->aux_payload_sz) - work->aux_payload_sz += len; } + work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; } return false; } @@ -488,11 +499,8 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) { struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); - struct ksmbd_conn *conn = work->conn; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->Command = rcv_hdr->Command; @@ -515,7 +523,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) * smb2_allocate_rsp_buf() - allocate smb2 response buffer * @work: smb work containing smb request buffer * - * Return: 0 on success, otherwise -ENOMEM + * Return: 0 on success, otherwise error */ int smb2_allocate_rsp_buf(struct ksmbd_work *work) { @@ -531,6 +539,10 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (cmd == SMB2_QUERY_INFO_HE) { struct smb2_query_info_req *req; + if (get_rfc1002_len(work->request_buf) < + offsetof(struct smb2_query_info_req, OutputBufferLength)) + return -EINVAL; + req = smb2_get_msg(work->request_buf); if ((req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || @@ -543,7 +555,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (le32_to_cpu(hdr->NextCommand) > 0) sz = large_sz; - work->response_buf = kvzalloc(sz, GFP_KERNEL); + work->response_buf = kvzalloc(sz, KSMBD_DEFAULT_GFP); if (!work->response_buf) return -ENOMEM; @@ -603,30 +615,6 @@ int smb2_check_user_session(struct ksmbd_work *work) return -ENOENT; } -static void destroy_previous_session(struct ksmbd_conn *conn, - struct ksmbd_user *user, u64 id) -{ - struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); - struct ksmbd_user *prev_user; - struct channel *chann; - long index; - - if (!prev_sess) - return; - - prev_user = prev_sess->user; - - if (!prev_user || - strcmp(user->name, prev_user->name) || - user->passkey_sz != prev_user->passkey_sz || - memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) - return; - - prev_sess->state = SMB2_SESSION_EXPIRED; - xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) - ksmbd_conn_set_exiting(chann->conn); -} - /** * smb2_get_name() - get filename string from on the wire smb format * @src: source buffer @@ -646,6 +634,17 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) return name; } + if (*name == '\0') { + kfree(name); + return ERR_PTR(-EINVAL); + } + + if (*name == '\\') { + pr_err("not allow directory name included leading slash\n"); + kfree(name); + return ERR_PTR(-EINVAL); + } + ksmbd_conv_path_to_unix(name); ksmbd_strip_last_slash(name); return name; @@ -653,13 +652,9 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) { - struct smb2_hdr *rsp_hdr; struct ksmbd_conn *conn = work->conn; int id; - rsp_hdr = smb2_get_msg(work->response_buf); - rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; - id = ksmbd_acquire_async_msg_id(&conn->async_ida); if (id < 0) { pr_err("Failed to alloc async message id\n"); @@ -667,7 +662,6 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) } work->asynchronous = true; work->async_id = id; - rsp_hdr->Id.AsyncId = cpu_to_le64(id); ksmbd_debug(SMB, "Send interim Response to inform async request id : %d\n", @@ -706,15 +700,29 @@ void release_async_work(struct ksmbd_work *work) void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) { struct smb2_hdr *rsp_hdr; + struct ksmbd_work *in_work = ksmbd_alloc_work_struct(); - rsp_hdr = smb2_get_msg(work->response_buf); - smb2_set_err_rsp(work); + if (!in_work) + return; + + if (allocate_interim_rsp_buf(in_work)) { + pr_err("smb_allocate_rsp_buf failed!\n"); + ksmbd_free_work_struct(in_work); + return; + } + + in_work->conn = work->conn; + memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work), + __SMB2_HEADER_STRUCTURE_SIZE); + + rsp_hdr = smb2_get_msg(in_work->response_buf); + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + rsp_hdr->Id.AsyncId = cpu_to_le64(work->async_id); + smb2_set_err_rsp(in_work); rsp_hdr->Status = status; - work->multiRsp = 1; - ksmbd_conn_write(work); - rsp_hdr->Status = 0; - work->multiRsp = 0; + ksmbd_conn_write(in_work); + ksmbd_free_work_struct(in_work); } static __le32 smb2_get_reparse_tag_special_file(umode_t mode) @@ -821,9 +829,8 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) pneg_ctxt->Name[15] = 0x7C; } -static void assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp, - void *smb2_buf_len) +static unsigned int assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp) { char * const pneg_ctxt = (char *)rsp + le32_to_cpu(rsp->NegotiateContextOffset); @@ -834,7 +841,6 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, conn->preauth_info->Preauth_HashId); - inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); ctxt_size = sizeof(struct smb2_preauth_neg_context); if (conn->cipher_type) { @@ -874,7 +880,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, } rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); - inc_rfc1001_len(smb2_buf_len, ctxt_size); + return ctxt_size + AUTH_GSS_PADDING; } static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, @@ -890,7 +896,7 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, return STATUS_INVALID_PARAMETER; if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) - return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + return STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; return STATUS_SUCCESS; @@ -950,7 +956,7 @@ bool smb3_encryption_negotiated(struct ksmbd_conn *conn) * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. * SMB 3.1.1 uses the cipher_type field. */ - return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + return (conn->vals->req_capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || conn->cipher_type; } @@ -1090,7 +1096,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); int rc = 0; - unsigned int smb2_buf_len, smb2_neg_size; + unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0; __le32 status; ksmbd_debug(SMB, "Received negotiate request\n"); @@ -1101,6 +1107,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) return rc; } + ksmbd_conn_lock(conn); smb2_buf_len = get_rfc1002_len(work->request_buf); smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { @@ -1151,7 +1158,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) case SMB311_PROT_ID: conn->preauth_info = kzalloc(sizeof(struct preauth_integrity_info), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!conn->preauth_info) { rc = -ENOMEM; rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -1183,7 +1190,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) conn->preauth_info->Preauth_HashValue); rsp->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT); - assemble_neg_contexts(conn, rsp, work->response_buf); + neg_ctxt_len = assemble_neg_contexts(conn, rsp); break; case SMB302_PROT_ID: init_smb3_02_server(conn); @@ -1203,7 +1210,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rc = -EINVAL; goto err_out; } - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); /* For stats */ conn->connection_type = conn->dialect; @@ -1233,8 +1240,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; conn->use_spnego = true; @@ -1249,12 +1255,19 @@ int smb2_handle_negotiate(struct ksmbd_work *work) } conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); - ksmbd_conn_set_need_negotiate(conn); + ksmbd_conn_set_need_setup(conn); err_out: + ksmbd_conn_unlock(conn); + if (rc) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + + if (!rc) + rc = ksmbd_iov_pin_rsp(work, rsp, + sizeof(struct smb2_negotiate_rsp) + + AUTH_GSS_LENGTH + neg_ctxt_len); if (rc < 0) smb2_set_err_rsp(work); - return rc; } @@ -1264,8 +1277,11 @@ static int alloc_preauth_hash(struct ksmbd_session *sess, if (sess->Preauth_HashValue) return 0; + if (!conn->preauth_info) + return -ENOMEM; + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP); if (!sess->Preauth_HashValue) return -ENOMEM; @@ -1336,8 +1352,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, return rc; sz = le16_to_cpu(rsp->SecurityBufferOffset); - chgblob = - (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + chgblob = (struct challenge_message *)rsp->Buffer; memset(chgblob, 0, sizeof(struct challenge_message)); if (!work->conn->use_spnego) { @@ -1352,7 +1367,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, sz = sizeof(struct challenge_message); sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; - neg_blob = kzalloc(sz, GFP_KERNEL); + neg_blob = kzalloc(sz, KSMBD_DEFAULT_GFP); if (!neg_blob) return -ENOMEM; @@ -1370,8 +1385,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, goto out; } - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + memcpy(rsp->Buffer, spnego_blob, spnego_blob_len); rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); out: @@ -1401,7 +1415,10 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, char *name; unsigned int name_off, name_len, secbuf_len; - secbuf_len = le16_to_cpu(req->SecurityBufferLength); + if (conn->use_spnego && conn->mechToken) + secbuf_len = conn->mechTokenLen; + else + secbuf_len = le16_to_cpu(req->SecurityBufferLength); if (secbuf_len < sizeof(struct authenticate_message)) { ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); return NULL; @@ -1434,7 +1451,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; - struct channel *chann = NULL; + struct channel *chann = NULL, *old; struct ksmbd_user *user; u64 prev_id; int sz, rc; @@ -1450,11 +1467,9 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (rc) return -ENOMEM; - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + memcpy(rsp->Buffer, spnego_blob, spnego_blob_len); rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); kfree(spnego_blob); - inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); } user = session_user(conn, req); @@ -1493,7 +1508,10 @@ static int ntlm_authenticate(struct ksmbd_work *work, struct authenticate_message *authblob; authblob = user_authblob(conn, req); - sz = le16_to_cpu(req->SecurityBufferLength); + if (conn->use_spnego && conn->mechToken) + sz = conn->mechTokenLen; + else + sz = le16_to_cpu(req->SecurityBufferLength); rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); if (rc) { set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); @@ -1520,12 +1538,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (smb3_encryption_negotiated(conn) && !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - rc = conn->ops->generate_encryptionkey(conn, sess); - if (rc) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } + conn->ops->generate_encryptionkey(conn, sess); sess->enc = true; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; @@ -1540,12 +1553,17 @@ binding_session: if (conn->dialect >= SMB30_PROT_ID) { chann = lookup_chann_list(sess, conn); if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP); if (!chann) return -ENOMEM; chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, + KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + kfree(chann); + return xa_err(old); + } } } @@ -1572,7 +1590,7 @@ static int krb5_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; - struct channel *chann = NULL; + struct channel *chann = NULL, *old; u64 prev_sess_id; int in_len, out_len; int retval; @@ -1585,49 +1603,60 @@ static int krb5_authenticate(struct ksmbd_work *work, out_len = work->response_sz - (le16_to_cpu(rsp->SecurityBufferOffset) + 4); - /* Check previous session */ - prev_sess_id = le64_to_cpu(req->PreviousSessionId); - if (prev_sess_id && prev_sess_id != sess->id) - destroy_previous_session(conn, sess->user, prev_sess_id); - - if (sess->state == SMB2_SESSION_VALID) - ksmbd_free_user(sess->user); - retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, out_blob, &out_len); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); return -EINVAL; } + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(conn, sess->user, prev_sess_id); + rsp->SecurityBufferLength = cpu_to_le16(out_len); - inc_rfc1001_len(work->response_buf, out_len - 1); - if ((conn->sign || server_conf.enforced_signing) || + /* + * If session state is SMB2_SESSION_VALID, We can assume + * that it is reauthentication. And the user/password + * has been verified, so return it here. + */ + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; + return 0; + } + + if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && + (conn->sign || server_conf.enforced_signing)) || (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) sess->sign = true; - if (smb3_encryption_negotiated(conn)) { - retval = conn->ops->generate_encryptionkey(conn, sess); - if (retval) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } + if (smb3_encryption_negotiated(conn) && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + conn->ops->generate_encryptionkey(conn, sess); sess->enc = true; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; sess->sign = false; } +binding_session: if (conn->dialect >= SMB30_PROT_ID) { chann = lookup_chann_list(sess, conn); if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP); if (!chann) return -ENOMEM; chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + old = xa_store(&sess->ksmbd_chann_list, (long)conn, + chann, KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + kfree(chann); + return xa_err(old); + } } } @@ -1664,7 +1693,12 @@ int smb2_sess_setup(struct ksmbd_work *work) unsigned int negblob_len, negblob_off; int rc = 0; - ksmbd_debug(SMB, "Received request for session setup\n"); + ksmbd_debug(SMB, "Received smb2 session setup request\n"); + + if (!ksmbd_conn_need_setup(conn) && !ksmbd_conn_good(conn)) { + work->send_no_response = 1; + return rc; + } WORK_BUFFERS(work, req, rsp); @@ -1672,7 +1706,6 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->SessionFlags = 0; rsp->SecurityBufferOffset = cpu_to_le16(72); rsp->SecurityBufferLength = 0; - inc_rfc1001_len(work->response_buf, 9); ksmbd_conn_lock(conn); if (!req->hdr.SessionId) { @@ -1685,6 +1718,8 @@ int smb2_sess_setup(struct ksmbd_work *work) rc = ksmbd_session_register(conn, sess); if (rc) goto out_err; + + conn->binding = false; } else if (conn->dialect >= SMB30_PROT_ID && (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { @@ -1724,11 +1759,12 @@ int smb2_sess_setup(struct ksmbd_work *work) if (ksmbd_conn_need_reconnect(conn)) { rc = -EFAULT; + ksmbd_user_session_put(sess); sess = NULL; goto out_err; } - if (ksmbd_session_lookup(conn, sess_id)) { + if (is_ksmbd_session_in_connection(conn, sess_id)) { rc = -EACCES; goto out_err; } @@ -1760,16 +1796,18 @@ int smb2_sess_setup(struct ksmbd_work *work) if (ksmbd_conn_need_reconnect(conn)) { rc = -EFAULT; + ksmbd_user_session_put(sess); sess = NULL; goto out_err; } + + conn->binding = false; } work->sess = sess; negblob_off = le16_to_cpu(req->SecurityBufferOffset); negblob_len = le16_to_cpu(req->SecurityBufferLength); - if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || - negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { + if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer)) { rc = -EINVAL; goto out_err; } @@ -1778,8 +1816,15 @@ int smb2_sess_setup(struct ksmbd_work *work) negblob_off); if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { - if (conn->mechToken) + if (conn->mechToken) { negblob = (struct negotiate_message *)conn->mechToken; + negblob_len = conn->mechTokenLen; + } + } + + if (negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { + rc = -EINVAL; + goto out_err; } if (server_conf.auth_mechs & conn->auth_mechs) { @@ -1799,8 +1844,6 @@ int smb2_sess_setup(struct ksmbd_work *work) ksmbd_conn_set_good(conn); sess->state = SMB2_SESSION_VALID; } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { if (negblob->MessageType == NtLmNegotiate) { rc = ntlm_negotiate(work, negblob, negblob_len, rsp); @@ -1808,13 +1851,6 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; rsp->hdr.Status = STATUS_MORE_PROCESSING_REQUIRED; - /* - * Note: here total size -1 is done as an - * adjustment for 0 size blob - */ - inc_rfc1001_len(work->response_buf, - le16_to_cpu(rsp->SecurityBufferLength) - 1); - } else if (negblob->MessageType == NtLmAuthenticate) { rc = ntlm_authenticate(work, req, rsp); if (rc) @@ -1834,8 +1870,6 @@ int smb2_sess_setup(struct ksmbd_work *work) kfree(preauth_sess); } } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; } else { pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", le32_to_cpu(negblob->MessageType)); @@ -1893,12 +1927,26 @@ out_err: sess->last_active = jiffies; sess->state = SMB2_SESSION_EXPIRED; + ksmbd_user_session_put(sess); + work->sess = NULL; if (try_delay) { ksmbd_conn_set_need_reconnect(conn); ssleep(5); - ksmbd_conn_set_need_negotiate(conn); + ksmbd_conn_set_need_setup(conn); } } + smb2_set_err_rsp(work); + } else { + unsigned int iov_len; + + if (rsp->SecurityBufferLength) + iov_len = offsetof(struct smb2_sess_setup_rsp, Buffer) + + le16_to_cpu(rsp->SecurityBufferLength); + else + iov_len = sizeof(struct smb2_sess_setup_rsp); + rc = ksmbd_iov_pin_rsp(work, rsp, iov_len); + if (rc) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; } ksmbd_conn_unlock(conn); @@ -1919,12 +1967,14 @@ int smb2_tree_connect(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; char *treename = NULL, *name = NULL; struct ksmbd_tree_conn_status status; - struct ksmbd_share_config *share; + struct ksmbd_share_config *share = NULL; int rc = -EINVAL; + ksmbd_debug(SMB, "Received smb2 tree connect request\n"); + WORK_BUFFERS(work, req, rsp); - treename = smb_strndup_from_utf16(req->Buffer, + treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset), le16_to_cpu(req->PathLength), true, conn->local_nls); if (IS_ERR(treename)) { @@ -1942,7 +1992,7 @@ int smb2_tree_connect(struct ksmbd_work *work) ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", name, treename); - status = ksmbd_tree_conn_connect(conn, sess, name); + status = ksmbd_tree_conn_connect(work, name); if (status.ret == KSMBD_TREE_CONN_STATUS_OK) rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); else @@ -1976,14 +2026,25 @@ int smb2_tree_connect(struct ksmbd_work *work) if (conn->posix_ext_supported) status.tree_conn->posix_extensions = true; + write_lock(&sess->tree_conns_lock); + status.tree_conn->t_state = TREE_CONNECTED; + write_unlock(&sess->tree_conns_lock); rsp->StructureSize = cpu_to_le16(16); - inc_rfc1001_len(work->response_buf, 16); out_err1: - rsp->Capabilities = 0; + if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && share && + test_share_config_flag(share, + KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY)) + rsp->Capabilities = SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY; + else + rsp->Capabilities = 0; rsp->Reserved = 0; /* default manual caching */ rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp)); + if (rc) + status.ret = KSMBD_TREE_CONN_STATUS_NOMEM; + if (!IS_ERR(treename)) kfree(treename); if (!IS_ERR(name)) @@ -2027,15 +2088,24 @@ out_err1: * @access: file access flags * @disposition: file disposition flags * @may_flags: set with MAY_ flags + * @coptions: file creation options + * @mode: file mode * * Return: file open flags */ static int smb2_create_open_flags(bool file_present, __le32 access, __le32 disposition, - int *may_flags) + int *may_flags, + __le32 coptions, + umode_t mode) { int oflags = O_NONBLOCK | O_LARGEFILE; + if (coptions & FILE_DIRECTORY_FILE_LE || S_ISDIR(mode)) { + access &= ~FILE_WRITE_DESIRE_ACCESS_LE; + ksmbd_debug(SMB, "Discard write access to a directory\n"); + } + if (access & FILE_READ_DESIRED_ACCESS_LE && access & FILE_WRITE_DESIRE_ACCESS_LE) { oflags |= O_RDWR; @@ -2048,7 +2118,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, *may_flags = MAY_OPEN | MAY_READ; } - if (access == FILE_READ_ATTRIBUTES_LE) + if (access == FILE_READ_ATTRIBUTES_LE || S_ISBLK(mode) || S_ISCHR(mode)) oflags |= O_PATH; if (file_present) { @@ -2088,7 +2158,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, * smb2_tree_disconnect() - handler for smb tree connect request * @work: smb work containing request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_tree_disconnect(struct ksmbd_work *work) { @@ -2096,73 +2166,108 @@ int smb2_tree_disconnect(struct ksmbd_work *work) struct smb2_tree_disconnect_req *req; struct ksmbd_session *sess = work->sess; struct ksmbd_tree_connect *tcon = work->tcon; + int err; - WORK_BUFFERS(work, req, rsp); - - rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(work->response_buf, 4); + ksmbd_debug(SMB, "Received smb2 tree disconnect request\n"); - ksmbd_debug(SMB, "request\n"); + WORK_BUFFERS(work, req, rsp); - if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { + if (!tcon) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - smb2_set_err_rsp(work); - return 0; + err = -ENOENT; + goto err_out; } ksmbd_close_tree_conn_fds(work); - ksmbd_tree_conn_disconnect(sess, tcon); - work->tcon = NULL; + + write_lock(&sess->tree_conns_lock); + if (tcon->t_state == TREE_DISCONNECTED) { + write_unlock(&sess->tree_conns_lock); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + err = -ENOENT; + goto err_out; + } + + tcon->t_state = TREE_DISCONNECTED; + write_unlock(&sess->tree_conns_lock); + + err = ksmbd_tree_conn_disconnect(sess, tcon); + if (err) { + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + goto err_out; + } + + rsp->StructureSize = cpu_to_le16(4); + err = ksmbd_iov_pin_rsp(work, rsp, + sizeof(struct smb2_tree_disconnect_rsp)); + if (err) { + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + goto err_out; + } + return 0; + +err_out: + smb2_set_err_rsp(work); + return err; + } /** * smb2_session_logoff() - handler for session log off request * @work: smb work containing request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_session_logoff(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; struct smb2_logoff_req *req; struct smb2_logoff_rsp *rsp; - struct ksmbd_session *sess; u64 sess_id; + int err; WORK_BUFFERS(work, req, rsp); - sess_id = le64_to_cpu(req->hdr.SessionId); - - rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(work->response_buf, 4); - - ksmbd_debug(SMB, "request\n"); + ksmbd_debug(SMB, "Received smb2 session logoff request\n"); + ksmbd_conn_lock(conn); + if (!ksmbd_conn_good(conn)) { + ksmbd_conn_unlock(conn); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return -ENOENT; + } + sess_id = le64_to_cpu(req->hdr.SessionId); ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); + ksmbd_conn_unlock(conn); + ksmbd_close_session_fds(work); - ksmbd_conn_wait_idle(conn, sess_id); + ksmbd_conn_wait_idle(conn); - /* - * Re-lookup session to validate if session is deleted - * while waiting request complete - */ - sess = ksmbd_session_lookup_all(conn, sess_id); if (ksmbd_tree_conn_session_logoff(sess)) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; smb2_set_err_rsp(work); - return 0; + return -ENOENT; } - ksmbd_destroy_file_table(&sess->file_table); + down_write(&conn->session_lock); sess->state = SMB2_SESSION_EXPIRED; + up_write(&conn->session_lock); - ksmbd_free_user(sess->user); - sess->user = NULL; - ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP); + + rsp->StructureSize = cpu_to_le16(4); + err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); + if (err) { + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + smb2_set_err_rsp(work); + return err; + } return 0; } @@ -2215,7 +2320,10 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) rsp->CreateContextsOffset = 0; rsp->CreateContextsLength = 0; - inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_create_rsp, Buffer)); + if (err) + goto out; + kfree(name); return 0; @@ -2243,11 +2351,12 @@ out: * @eabuf: set info command buffer * @buf_len: set info command buffer length * @path: dentry path for get ea + * @get_write: get write access to a mount * * Return: 0 on success, otherwise error */ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, - const struct path *path) + const struct path *path, bool get_write) { struct mnt_idmap *idmap = mnt_idmap(path->mnt); char *attr_name = NULL, *value; @@ -2258,7 +2367,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, le16_to_cpu(eabuf->EaValueLength)) return -EINVAL; - attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + attr_name = kmalloc(XATTR_NAME_MAX + 1, KSMBD_DEFAULT_GFP); if (!attr_name) return -ENOMEM; @@ -2295,7 +2404,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, if (rc > 0) { rc = ksmbd_vfs_remove_xattr(idmap, path, - attr_name); + attr_name, + get_write); if (rc < 0) { ksmbd_debug(SMB, @@ -2309,7 +2419,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, rc = 0; } else { rc = ksmbd_vfs_setxattr(idmap, path, attr_name, value, - le16_to_cpu(eabuf->EaValueLength), 0); + le16_to_cpu(eabuf->EaValueLength), + 0, get_write); if (rc < 0) { ksmbd_debug(SMB, "ksmbd_vfs_setxattr is failed(%d)\n", @@ -2372,7 +2483,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path, return -EBADF; } - rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name, NULL, 0, 0); + rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name, NULL, 0, 0, false); if (rc < 0) pr_err("Failed to store XATTR stream name :%d\n", rc); return 0; @@ -2401,7 +2512,7 @@ static int smb2_remove_smb_xattrs(const struct path *path) !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, STREAM_PREFIX_LEN)) { err = ksmbd_vfs_remove_xattr(idmap, path, - name); + name, true); if (err) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); @@ -2447,7 +2558,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path * da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | XATTR_DOSINFO_ITIME; - rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da); + rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da, true); if (rc) ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); } @@ -2474,7 +2585,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, } } -static int smb2_creat(struct ksmbd_work *work, struct path *parent_path, +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, int open_flags, umode_t posix_mode, bool is_dir) { @@ -2503,7 +2614,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *parent_path, return rc; } - rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0); + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); if (rc) { pr_err("cannot get linux path (%s), err = %d\n", name, rc); @@ -2537,7 +2648,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, sizeof(struct create_sd_buf_req)) return -EINVAL; return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, - le32_to_cpu(sd_buf->ccontext.DataLength), true); + le32_to_cpu(sd_buf->ccontext.DataLength), true, false); } static void ksmbd_acls_fattr(struct smb_fattr *fattr, @@ -2560,6 +2671,186 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr, } } +enum { + DURABLE_RECONN_V2 = 1, + DURABLE_RECONN, + DURABLE_REQ_V2, + DURABLE_REQ, +}; + +struct durable_info { + struct ksmbd_file *fp; + unsigned short int type; + bool persistent; + bool reconnected; + unsigned int timeout; + char *CreateGuid; +}; + +static int parse_durable_handle_context(struct ksmbd_work *work, + struct smb2_create_req *req, + struct lease_ctx_info *lc, + struct durable_info *dh_info) +{ + struct ksmbd_conn *conn = work->conn; + struct create_context *context; + int dh_idx, err = 0; + u64 persistent_id = 0; + int req_op_level; + static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ"}; + + req_op_level = req->RequestedOplockLevel; + for (dh_idx = DURABLE_RECONN_V2; dh_idx <= ARRAY_SIZE(durable_arr); + dh_idx++) { + context = smb2_find_context_vals(req, durable_arr[dh_idx - 1], 4); + if (IS_ERR(context)) { + err = PTR_ERR(context); + goto out; + } + if (!context) + continue; + + switch (dh_idx) { + case DURABLE_RECONN_V2: + { + struct create_durable_handle_reconnect_v2 *recon_v2; + + if (dh_info->type == DURABLE_RECONN || + dh_info->type == DURABLE_REQ_V2) { + err = -EINVAL; + goto out; + } + + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_durable_handle_reconnect_v2)) { + err = -EINVAL; + goto out; + } + + recon_v2 = (struct create_durable_handle_reconnect_v2 *)context; + persistent_id = recon_v2->dcontext.Fid.PersistentFileId; + dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); + if (!dh_info->fp) { + ksmbd_debug(SMB, "Failed to get durable handle state\n"); + err = -EBADF; + goto out; + } + + if (memcmp(dh_info->fp->create_guid, recon_v2->dcontext.CreateGuid, + SMB2_CREATE_GUID_SIZE)) { + err = -EBADF; + ksmbd_put_durable_fd(dh_info->fp); + goto out; + } + + dh_info->type = dh_idx; + dh_info->reconnected = true; + ksmbd_debug(SMB, + "reconnect v2 Persistent-id from reconnect = %llu\n", + persistent_id); + break; + } + case DURABLE_RECONN: + { + create_durable_reconn_t *recon; + + if (dh_info->type == DURABLE_RECONN_V2 || + dh_info->type == DURABLE_REQ_V2) { + err = -EINVAL; + goto out; + } + + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(create_durable_reconn_t)) { + err = -EINVAL; + goto out; + } + + recon = (create_durable_reconn_t *)context; + persistent_id = recon->Data.Fid.PersistentFileId; + dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); + if (!dh_info->fp) { + ksmbd_debug(SMB, "Failed to get durable handle state\n"); + err = -EBADF; + goto out; + } + + dh_info->type = dh_idx; + dh_info->reconnected = true; + ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n", + persistent_id); + break; + } + case DURABLE_REQ_V2: + { + struct create_durable_req_v2 *durable_v2_blob; + + if (dh_info->type == DURABLE_RECONN || + dh_info->type == DURABLE_RECONN_V2) { + err = -EINVAL; + goto out; + } + + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_durable_req_v2)) { + err = -EINVAL; + goto out; + } + + durable_v2_blob = + (struct create_durable_req_v2 *)context; + ksmbd_debug(SMB, "Request for durable v2 open\n"); + dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->dcontext.CreateGuid); + if (dh_info->fp) { + if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid, + SMB2_CLIENT_GUID_SIZE)) { + if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATION)) { + err = -ENOEXEC; + goto out; + } + + dh_info->fp->conn = conn; + dh_info->reconnected = true; + goto out; + } + } + + if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || + req_op_level == SMB2_OPLOCK_LEVEL_BATCH) { + dh_info->CreateGuid = + durable_v2_blob->dcontext.CreateGuid; + dh_info->persistent = + le32_to_cpu(durable_v2_blob->dcontext.Flags); + dh_info->timeout = + le32_to_cpu(durable_v2_blob->dcontext.Timeout); + dh_info->type = dh_idx; + } + break; + } + case DURABLE_REQ: + if (dh_info->type == DURABLE_RECONN) + goto out; + if (dh_info->type == DURABLE_RECONN_V2 || + dh_info->type == DURABLE_REQ_V2) { + err = -EINVAL; + goto out; + } + + if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || + req_op_level == SMB2_OPLOCK_LEVEL_BATCH) { + ksmbd_debug(SMB, "Request for durable open\n"); + dh_info->type = dh_idx; + } + } + } + +out: + return err; +} + /** * smb2_open() - handler for smb file open request * @work: smb work containing request buffer @@ -2573,7 +2864,7 @@ int smb2_open(struct ksmbd_work *work) struct ksmbd_tree_connect *tcon = work->tcon; struct smb2_create_req *req; struct smb2_create_rsp *rsp; - struct path path, parent_path; + struct path path; struct ksmbd_share_config *share = tcon->share_conf; struct ksmbd_file *fp = NULL; struct file *filp = NULL; @@ -2583,11 +2874,12 @@ int smb2_open(struct ksmbd_work *work) struct lease_ctx_info *lc = NULL; struct create_ea_buf_req *ea_buf = NULL; struct oplock_info *opinfo; + struct durable_info dh_info = {0}; __le32 *next_ptr = NULL; int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; int rc = 0; int contxt_cnt = 0, query_disk_id = 0; - int maximal_access_ctxt = 0, posix_ctxt = 0; + bool maximal_access_ctxt = false, posix_ctxt = false; int s_type = 0; int next_off = 0; char *name = NULL; @@ -2597,6 +2889,9 @@ int smb2_open(struct ksmbd_work *work) u64 time; umode_t posix_mode = 0; __le32 daccess, maximal_access = 0; + int iov_len = 0; + + ksmbd_debug(SMB, "Received smb2 create request\n"); WORK_BUFFERS(work, req, rsp); @@ -2613,57 +2908,111 @@ int smb2_open(struct ksmbd_work *work) return create_smb2_pipe(work); } - if (req->NameLength) { - if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - *(char *)req->Buffer == '\\') { - pr_err("not allow directory name included leading slash\n"); - rc = -EINVAL; - goto err_out1; + if (req->CreateContextsOffset && tcon->posix_extensions) { + context = smb2_find_context_vals(req, SMB2_CREATE_TAG_POSIX, 16); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out2; + } else if (context) { + struct create_posix *posix = (struct create_posix *)context; + + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; + goto err_out2; + } + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = true; } + } - name = smb2_get_name(req->Buffer, + if (req->NameLength) { + name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset), le16_to_cpu(req->NameLength), work->conn->local_nls); if (IS_ERR(name)) { rc = PTR_ERR(name); - if (rc != -ENOMEM) - rc = -ENOENT; name = NULL; - goto err_out1; + goto err_out2; } ksmbd_debug(SMB, "converted name = %s\n", name); - if (strchr(name, ':')) { - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) { - rc = -EBADF; - goto err_out1; + + if (posix_ctxt == false) { + if (strchr(name, ':')) { + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; + goto err_out2; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out2; } - rc = parse_stream_name(name, &stream_name, &s_type); + + rc = ksmbd_validate_filename(name); if (rc < 0) - goto err_out1; + goto err_out2; } - rc = ksmbd_validate_filename(name); - if (rc < 0) - goto err_out1; - if (ksmbd_share_veto_filename(share, name)) { rc = -ENOENT; ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", name); - goto err_out1; + goto err_out2; } } else { - name = kstrdup("", GFP_KERNEL); + name = kstrdup("", KSMBD_DEFAULT_GFP); if (!name) { rc = -ENOMEM; - goto err_out1; + goto err_out2; } } req_op_level = req->RequestedOplockLevel; - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && + req->CreateContextsOffset) { + lc = parse_lease_state(req); + rc = parse_durable_handle_context(work, req, lc, &dh_info); + if (rc) { + ksmbd_debug(SMB, "error parsing durable handle context\n"); + goto err_out2; + } + + if (dh_info.reconnected == true) { + rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name); + if (rc) { + ksmbd_put_durable_fd(dh_info.fp); + goto err_out2; + } + + rc = ksmbd_reopen_durable_fd(work, dh_info.fp); + if (rc) { + ksmbd_put_durable_fd(dh_info.fp); + goto err_out2; + } + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + ksmbd_put_durable_fd(dh_info.fp); + goto err_out2; + } + + fp = dh_info.fp; + file_info = FILE_OPENED; + + rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat); + if (rc) + goto err_out2; + + ksmbd_put_durable_fd(fp); + goto reconnected_fp; + } + } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) lc = parse_lease_state(req); if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { @@ -2671,14 +3020,14 @@ int smb2_open(struct ksmbd_work *work) le32_to_cpu(req->ImpersonationLevel)); rc = -EIO; rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; - goto err_out1; + goto err_out2; } if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { pr_err("Invalid create options : 0x%x\n", le32_to_cpu(req->CreateOptions)); rc = -EINVAL; - goto err_out1; + goto err_out2; } else { if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && req->CreateOptions & FILE_RANDOM_ACCESS_LE) @@ -2688,13 +3037,13 @@ int smb2_open(struct ksmbd_work *work) (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | FILE_RESERVE_OPFILTER_LE)) { rc = -EOPNOTSUPP; - goto err_out1; + goto err_out2; } if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { rc = -EINVAL; - goto err_out1; + goto err_out2; } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); } @@ -2706,21 +3055,21 @@ int smb2_open(struct ksmbd_work *work) pr_err("Invalid create disposition : 0x%x\n", le32_to_cpu(req->CreateDisposition)); rc = -EINVAL; - goto err_out1; + goto err_out2; } if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { pr_err("Invalid desired access : 0x%x\n", le32_to_cpu(req->DesiredAccess)); rc = -EACCES; - goto err_out1; + goto err_out2; } if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { pr_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); rc = -EINVAL; - goto err_out1; + goto err_out2; } if (req->CreateContextsOffset) { @@ -2728,19 +3077,19 @@ int smb2_open(struct ksmbd_work *work) context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); - goto err_out1; + goto err_out2; } else if (context) { ea_buf = (struct create_ea_buf_req *)context; if (le16_to_cpu(context->DataOffset) + le32_to_cpu(context->DataLength) < sizeof(struct create_ea_buf_req)) { rc = -EINVAL; - goto err_out1; + goto err_out2; } if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { rsp->hdr.Status = STATUS_ACCESS_DENIED; rc = -EACCES; - goto err_out1; + goto err_out2; } } @@ -2748,7 +3097,7 @@ int smb2_open(struct ksmbd_work *work) SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); - goto err_out1; + goto err_out2; } else if (context) { ksmbd_debug(SMB, "get query maximal access context\n"); @@ -2759,43 +3108,21 @@ int smb2_open(struct ksmbd_work *work) SMB2_CREATE_TIMEWARP_REQUEST, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); - goto err_out1; + goto err_out2; } else if (context) { ksmbd_debug(SMB, "get timewarp context\n"); rc = -EBADF; - goto err_out1; - } - - if (tcon->posix_extensions) { - context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX, 16); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - struct create_posix *posix = - (struct create_posix *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_posix) - 4) { - rc = -EINVAL; - goto err_out1; - } - ksmbd_debug(SMB, "get posix context\n"); - - posix_mode = le32_to_cpu(posix->Mode); - posix_ctxt = 1; - } + goto err_out2; } } if (ksmbd_override_fsids(work)) { rc = -ENOMEM; - goto err_out1; + goto err_out2; } - rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, - &parent_path, &path, 1); + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, + &path, 1); if (!rc) { file_present = true; @@ -2821,7 +3148,6 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } - file_present = true; idmap = mnt_idmap(path.mnt); } else { if (rc != -ENOENT) @@ -2902,10 +3228,12 @@ int smb2_open(struct ksmbd_work *work) open_flags = smb2_create_open_flags(file_present, daccess, req->CreateDisposition, - &may_flags); + &may_flags, + req->CreateOptions, + file_present ? d_inode(path.dentry)->i_mode : 0); if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - if (open_flags & O_CREAT) { + if (open_flags & (O_CREAT | O_TRUNC)) { ksmbd_debug(SMB, "User does not have write permission\n"); rc = -EACCES; @@ -2915,7 +3243,7 @@ int smb2_open(struct ksmbd_work *work) /*create file if not present */ if (!file_present) { - rc = smb2_creat(work, &parent_path, &path, name, open_flags, + rc = smb2_creat(work, &path, name, open_flags, posix_mode, req->CreateOptions & FILE_DIRECTORY_FILE_LE); if (rc) { @@ -2937,7 +3265,7 @@ int smb2_open(struct ksmbd_work *work) rc = smb2_set_ea(&ea_buf->ea, le32_to_cpu(ea_buf->ccontext.DataLength), - &path); + &path, false); if (rc == -EOPNOTSUPP) rc = 0; else if (rc) @@ -2966,7 +3294,7 @@ int smb2_open(struct ksmbd_work *work) } } - rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + rc = ksmbd_query_inode_status(path.dentry->d_parent); if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { rc = -EBUSY; goto err_out; @@ -3056,7 +3384,7 @@ int smb2_open(struct ksmbd_work *work) sizeof(struct smb_sid) * 3 + sizeof(struct smb_acl) + sizeof(struct smb_ace) * ace_num * 2, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!pntsd) { posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); @@ -3080,7 +3408,8 @@ int smb2_open(struct ksmbd_work *work) idmap, &path, pntsd, - pntsd_size); + pntsd_size, + false); kfree(pntsd); if (rc) pr_err("failed to store ntacl in xattr : %d\n", @@ -3103,19 +3432,16 @@ int smb2_open(struct ksmbd_work *work) fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); - if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && - !fp->attrib_only && !stream_name) { - smb_break_all_oplock(work, fp); - need_truncate = 1; - } + + fp->is_posix_ctxt = posix_ctxt; /* fp should be searchable through ksmbd_inode.m_fp_list * after daccess, saccess, attrib_only, and stream are * initialized. */ - write_lock(&fp->f_ci->m_lock); + down_write(&fp->f_ci->m_lock); list_add(&fp->node, &fp->f_ci->m_fp_list); - write_unlock(&fp->f_ci->m_lock); + up_write(&fp->f_ci->m_lock); /* Check delete pending among previous fp before oplock break */ if (ksmbd_inode_pending_delete(fp)) { @@ -3123,23 +3449,44 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } + if (file_present || created) + path_put(&path); + + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && + !fp->attrib_only && !stream_name) { + smb_break_all_oplock(work, fp); + need_truncate = 1; + } + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && - !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + !(conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LEASING))) { if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { rc = share_ret; - goto err_out; + goto err_out1; } } else { - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && lc) { + if (S_ISDIR(file_inode(filp)->i_mode)) { + lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE; + lc->is_dir = true; + } + + /* + * Compare parent lease using parent key. If there is no + * a lease that has same parent key, Send lease break + * notification. + */ + smb_send_parent_lease_break_noti(fp, lc); + req_op_level = smb2_map_lease_to_oplock(lc->req_state); ksmbd_debug(SMB, "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", name, req_op_level, lc->req_state); rc = find_same_lease_key(sess, fp->f_ci, lc); if (rc) - goto err_out; + goto err_out1; } else if (open_flags == O_RDONLY && (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) @@ -3150,16 +3497,16 @@ int smb2_open(struct ksmbd_work *work) le32_to_cpu(req->hdr.Id.SyncId.TreeId), lc, share_ret); if (rc < 0) - goto err_out; + goto err_out1; } if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) ksmbd_fd_set_delete_on_close(fp, file_info); if (need_truncate) { - rc = smb2_create_truncate(&path); + rc = smb2_create_truncate(&fp->filp->f_path); if (rc) - goto err_out; + goto err_out1; } if (req->CreateContextsOffset) { @@ -3169,7 +3516,7 @@ int smb2_open(struct ksmbd_work *work) SMB2_CREATE_ALLOCATION_SIZE, 4); if (IS_ERR(az_req)) { rc = PTR_ERR(az_req); - goto err_out; + goto err_out1; } else if (az_req) { loff_t alloc_size; int err; @@ -3178,7 +3525,7 @@ int smb2_open(struct ksmbd_work *work) le32_to_cpu(az_req->ccontext.DataLength) < sizeof(struct create_alloc_size_req)) { rc = -EINVAL; - goto err_out; + goto err_out1; } alloc_size = le64_to_cpu(az_req->AllocationSize); ksmbd_debug(SMB, @@ -3196,16 +3543,25 @@ int smb2_open(struct ksmbd_work *work) context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); - goto err_out; + goto err_out1; } else if (context) { ksmbd_debug(SMB, "get query on disk id context\n"); query_disk_id = 1; } + + if (conn->is_aapl == false) { + context = smb2_find_context_vals(req, SMB2_CREATE_AAPL, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) + conn->is_aapl = true; + } } rc = ksmbd_vfs_getattr(&path, &stat); if (rc) - goto err_out; + goto err_out1; if (stat.result_mask & STATX_BTIME) fp->create_time = ksmbd_UnixTimeToNT(stat.btime); @@ -3222,6 +3578,27 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) { + if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY)) + fp->is_persistent = true; + else + fp->is_durable = true; + + if (dh_info.type == DURABLE_REQ_V2) { + memcpy(fp->create_guid, dh_info.CreateGuid, + SMB2_CREATE_GUID_SIZE); + if (dh_info.timeout) + fp->durable_timeout = + min_t(unsigned int, dh_info.timeout, + DURABLE_HANDLE_MAX_TIMEOUT); + else + fp->durable_timeout = 60; + } + } + +reconnected_fp: rsp->StructureSize = cpu_to_le16(89); rcu_read_lock(); opinfo = rcu_dereference(fp->f_opinfo); @@ -3248,7 +3625,7 @@ int smb2_open(struct ksmbd_work *work) rsp->CreateContextsOffset = 0; rsp->CreateContextsLength = 0; - inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + iov_len = offsetof(struct smb2_create_rsp, Buffer); /* If lease is request send lease context response */ if (opinfo && opinfo->is_lease) { @@ -3263,8 +3640,7 @@ int smb2_open(struct ksmbd_work *work) create_lease_buf(rsp->Buffer, opinfo->o_lease); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_lease_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_lease_size); + iov_len += conn->vals->create_lease_size; next_ptr = &lease_ccontext->Next; next_off = conn->vals->create_lease_size; } @@ -3284,8 +3660,7 @@ int smb2_open(struct ksmbd_work *work) le32_to_cpu(maximal_access)); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_mxac_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_mxac_size); + iov_len += conn->vals->create_mxac_size; if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &mxac_ccontext->Next; @@ -3303,14 +3678,40 @@ int smb2_open(struct ksmbd_work *work) stat.ino, tcon->id); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_disk_id_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_disk_id_size); + iov_len += conn->vals->create_disk_id_size; if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &disk_id_ccontext->Next; next_off = conn->vals->create_disk_id_size; } + if (dh_info.type == DURABLE_REQ || dh_info.type == DURABLE_REQ_V2) { + struct create_context *durable_ccontext; + + durable_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + if (dh_info.type == DURABLE_REQ) { + create_durable_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_durable_size); + iov_len += conn->vals->create_durable_size; + } else { + create_durable_v2_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_durable_v2_size); + iov_len += conn->vals->create_durable_v2_size; + } + + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &durable_ccontext->Next; + next_off = conn->vals->create_durable_size; + } + if (posix_ctxt) { contxt_cnt++; create_posix_rsp_buf(rsp->Buffer + @@ -3318,8 +3719,7 @@ int smb2_open(struct ksmbd_work *work) fp); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_posix_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_posix_size); + iov_len += conn->vals->create_posix_size; if (next_ptr) *next_ptr = cpu_to_le32(next_off); } @@ -3330,14 +3730,17 @@ int smb2_open(struct ksmbd_work *work) } err_out: - if (file_present || created) { - inode_unlock(d_inode(parent_path.dentry)); + if (rc && (file_present || created)) path_put(&path); - path_put(&parent_path); - } - ksmbd_revert_fsids(work); + err_out1: + ksmbd_revert_fsids(work); +err_out2: + if (!rc) { + ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); + } if (rc) { if (rc == -EINVAL) rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -3373,22 +3776,22 @@ err_out1: kfree(name); kfree(lc); - return 0; + return rc; } static int readdir_info_level_struct_sz(int info_level) { switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_full_directory_info); + return sizeof(FILE_FULL_DIRECTORY_INFO); case FILE_BOTH_DIRECTORY_INFORMATION: - return sizeof(struct file_both_directory_info); + return sizeof(FILE_BOTH_DIRECTORY_INFO); case FILE_DIRECTORY_INFORMATION: - return sizeof(struct file_directory_info); + return sizeof(FILE_DIRECTORY_INFO); case FILE_NAMES_INFORMATION: return sizeof(struct file_names_info); case FILEID_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_id_full_dir_info); + return sizeof(FILE_ID_FULL_DIR_INFO); case FILEID_BOTH_DIRECTORY_INFORMATION: return sizeof(struct file_id_both_directory_info); case SMB_FIND_FILE_POSIX_INFO: @@ -3403,9 +3806,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: { - struct file_full_directory_info *ffdinfo; + FILE_FULL_DIRECTORY_INFO *ffdinfo; - ffdinfo = (struct file_full_directory_info *)d_info->rptr; + ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); d_info->name = ffdinfo->FileName; d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); @@ -3413,9 +3816,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) } case FILE_BOTH_DIRECTORY_INFORMATION: { - struct file_both_directory_info *fbdinfo; + FILE_BOTH_DIRECTORY_INFO *fbdinfo; - fbdinfo = (struct file_both_directory_info *)d_info->rptr; + fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); d_info->name = fbdinfo->FileName; d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); @@ -3423,9 +3826,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) } case FILE_DIRECTORY_INFORMATION: { - struct file_directory_info *fdinfo; + FILE_DIRECTORY_INFO *fdinfo; - fdinfo = (struct file_directory_info *)d_info->rptr; + fdinfo = (FILE_DIRECTORY_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); d_info->name = fdinfo->FileName; d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); @@ -3443,9 +3846,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) } case FILEID_FULL_DIRECTORY_INFORMATION: { - struct file_id_full_dir_info *dinfo; + FILE_ID_FULL_DIR_INFO *dinfo; - dinfo = (struct file_id_full_dir_info *)d_info->rptr; + dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); d_info->name = dinfo->FileName; d_info->name_len = le32_to_cpu(dinfo->FileNameLength); @@ -3528,9 +3931,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: { - struct file_full_directory_info *ffdinfo; + FILE_FULL_DIRECTORY_INFO *ffdinfo; - ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo = (FILE_FULL_DIRECTORY_INFO *)kstat; ffdinfo->FileNameLength = cpu_to_le32(conv_len); ffdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); @@ -3544,9 +3947,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, } case FILE_BOTH_DIRECTORY_INFORMATION: { - struct file_both_directory_info *fbdinfo; + FILE_BOTH_DIRECTORY_INFO *fbdinfo; - fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)kstat; fbdinfo->FileNameLength = cpu_to_le32(conv_len); fbdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); @@ -3562,9 +3965,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, } case FILE_DIRECTORY_INFORMATION: { - struct file_directory_info *fdinfo; + FILE_DIRECTORY_INFO *fdinfo; - fdinfo = (struct file_directory_info *)kstat; + fdinfo = (FILE_DIRECTORY_INFO *)kstat; fdinfo->FileNameLength = cpu_to_le32(conv_len); if (d_info->hide_dot_file && d_info->name[0] == '.') fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; @@ -3584,16 +3987,19 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, } case FILEID_FULL_DIRECTORY_INFORMATION: { - struct file_id_full_dir_info *dinfo; + FILE_ID_FULL_DIR_INFO *dinfo; - dinfo = (struct file_id_full_dir_info *)kstat; + dinfo = (FILE_ID_FULL_DIR_INFO *)kstat; dinfo->FileNameLength = cpu_to_le32(conv_len); dinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (dinfo->EaSize) dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; dinfo->Reserved = 0; - dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (conn->is_aapl) + dinfo->UniqueId = 0; + else + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); if (d_info->hide_dot_file && d_info->name[0] == '.') dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(dinfo->FileName, conv_name, conv_len); @@ -3610,7 +4016,10 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (fibdinfo->EaSize) fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (conn->is_aapl) + fibdinfo->UniqueId = 0; + else + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); fibdinfo->ShortNameLength = 0; fibdinfo->Reserved = 0; fibdinfo->Reserved2 = cpu_to_le16(0); @@ -3639,6 +4048,26 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); + switch (ksmbd_kstat->kstat->mode & S_IFMT) { + case S_IFDIR: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_DIR << POSIX_FILETYPE_SHIFT); + break; + case S_IFLNK: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_SYMLINK << POSIX_FILETYPE_SHIFT); + break; + case S_IFCHR: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_CHARDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFBLK: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_BLKDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFIFO: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_FIFO << POSIX_FILETYPE_SHIFT); + break; + case S_IFSOCK: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_SOCKET << POSIX_FILETYPE_SHIFT); + } + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); posix_info->DosAttributes = S_ISDIR(ksmbd_kstat->kstat->mode) ? @@ -3686,20 +4115,6 @@ struct smb2_query_dir_private { int info_level; }; -static void lock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); -} - -static void unlock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_unlock(d_inode(dir)); -} - static int process_query_dir_entries(struct smb2_query_dir_private *priv) { struct mnt_idmap *idmap = file_mnt_idmap(priv->dir_fp->filp); @@ -3714,11 +4129,10 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) if (dentry_name(priv->d_info, priv->info_level)) return -EINVAL; - lock_dir(priv->dir_fp); - dent = lookup_one(idmap, priv->d_info->name, - priv->dir_fp->filp->f_path.dentry, - priv->d_info->name_len); - unlock_dir(priv->dir_fp); + dent = lookup_one_unlocked(idmap, + &QSTR_LEN(priv->d_info->name, + priv->d_info->name_len), + priv->dir_fp->filp->f_path.dentry); if (IS_ERR(dent)) { ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", @@ -3734,11 +4148,16 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) } ksmbd_kstat.kstat = &kstat; - if (priv->info_level != FILE_NAMES_INFORMATION) - ksmbd_vfs_fill_dentry_attrs(priv->work, - idmap, - dent, - &ksmbd_kstat); + if (priv->info_level != FILE_NAMES_INFORMATION) { + rc = ksmbd_vfs_fill_dentry_attrs(priv->work, + idmap, + dent, + &ksmbd_kstat); + if (rc) { + dput(dent); + continue; + } + } rc = smb2_populate_readdir_entry(priv->work->conn, priv->info_level, @@ -3774,9 +4193,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: { - struct file_full_directory_info *ffdinfo; + FILE_FULL_DIRECTORY_INFO *ffdinfo; - ffdinfo = (struct file_full_directory_info *)d_info->wptr; + ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->wptr; memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); ffdinfo->FileName[d_info->name_len] = 0x00; ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -3785,9 +4204,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } case FILE_BOTH_DIRECTORY_INFORMATION: { - struct file_both_directory_info *fbdinfo; + FILE_BOTH_DIRECTORY_INFO *fbdinfo; - fbdinfo = (struct file_both_directory_info *)d_info->wptr; + fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->wptr; memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); fbdinfo->FileName[d_info->name_len] = 0x00; fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -3796,9 +4215,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } case FILE_DIRECTORY_INFORMATION: { - struct file_directory_info *fdinfo; + FILE_DIRECTORY_INFO *fdinfo; - fdinfo = (struct file_directory_info *)d_info->wptr; + fdinfo = (FILE_DIRECTORY_INFO *)d_info->wptr; memcpy(fdinfo->FileName, d_info->name, d_info->name_len); fdinfo->FileName[d_info->name_len] = 0x00; fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -3818,9 +4237,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } case FILEID_FULL_DIRECTORY_INFORMATION: { - struct file_id_full_dir_info *dinfo; + FILE_ID_FULL_DIR_INFO *dinfo; - dinfo = (struct file_id_full_dir_info *)d_info->wptr; + dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->wptr; memcpy(dinfo->FileName, d_info->name, d_info->name_len); dinfo->FileName[d_info->name_len] = 0x00; dinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -3873,6 +4292,7 @@ static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, /* dot and dotdot entries are already reserved */ if (!strcmp(".", name) || !strcmp("..", name)) return true; + d_info->num_scan++; if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) return true; if (!match_pattern(name, namlen, priv->search_pattern)) @@ -3945,6 +4365,8 @@ int smb2_query_dir(struct ksmbd_work *work) int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; + ksmbd_debug(SMB, "Received smb2 query directory request\n"); + WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { @@ -3981,7 +4403,7 @@ int smb2_query_dir(struct ksmbd_work *work) } srch_flag = req->Flags; - srch_ptr = smb_strndup_from_utf16(req->Buffer, + srch_ptr = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->FileNameOffset), le16_to_cpu(req->FileNameLength), 1, conn->local_nls); if (IS_ERR(srch_ptr)) { @@ -4033,9 +4455,18 @@ int smb2_query_dir(struct ksmbd_work *work) query_dir_private.info_level = req->FileInformationClass; dir_fp->readdir_data.private = &query_dir_private; set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); - +again: + d_info.num_scan = 0; rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); /* + * num_entry can be 0 if the directory iteration stops before reaching + * the end of the directory and no file is matched with the search + * pattern. + */ + if (rc >= 0 && !d_info.num_entry && d_info.num_scan && + d_info.out_buf_len > 0) + goto again; + /* * req->OutputBufferLength is too small to contain even one entry. * In this case, it immediately returns OutputBufferLength 0 to client. */ @@ -4063,10 +4494,14 @@ int smb2_query_dir(struct ksmbd_work *work) rsp->OutputBufferOffset = cpu_to_le16(0); rsp->OutputBufferLength = cpu_to_le32(0); rsp->Buffer[0] = 0; - inc_rfc1001_len(work->response_buf, 9); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_directory_rsp, Buffer) + + 1); + if (rc) + goto err_out; } else { no_buf_len: - ((struct file_directory_info *) + ((FILE_DIRECTORY_INFO *) ((char *)rsp->Buffer + d_info.last_entry_offset)) ->NextEntryOffset = 0; if (d_info.data_count >= d_info.last_entry_off_align) @@ -4075,7 +4510,11 @@ no_buf_len: rsp->StructureSize = cpu_to_le16(9); rsp->OutputBufferOffset = cpu_to_le16(72); rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); - inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_directory_rsp, Buffer) + + d_info.data_count); + if (rc) + goto err_out; } kfree(srch_ptr); @@ -4108,7 +4547,7 @@ err_out2: smb2_set_err_rsp(work); ksmbd_fd_put(work, dir_fp); ksmbd_revert_fsids(work); - return 0; + return rc; } /** @@ -4116,27 +4555,18 @@ err_out2: * @reqOutputBufferLength: max buffer length expected in command response * @rsp: query info response buffer contains output buffer length * @rsp_org: base response buffer pointer in case of chained response - * @infoclass_size: query info class response buffer size * * Return: 0 on success, otherwise error */ static int buffer_check_err(int reqOutputBufferLength, struct smb2_query_info_rsp *rsp, - void *rsp_org, int infoclass_size) + void *rsp_org) { if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { - if (reqOutputBufferLength < infoclass_size) { - pr_err("Invalid Buffer Size Requested\n"); - rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; - *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); - return -EINVAL; - } - - ksmbd_debug(SMB, "Buffer Overflow\n"); - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + - reqOutputBufferLength); - rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); + pr_err("Invalid Buffer Size Requested\n"); + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); + return -EINVAL; } return 0; } @@ -4155,7 +4585,6 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, sinfo->Directory = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); } static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, @@ -4169,7 +4598,6 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); } static int smb2_get_info_file_pipe(struct ksmbd_session *sess, @@ -4185,8 +4613,15 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, * pipe without opening it, checking error condition here */ id = req->VolatileFileId; - if (!ksmbd_session_rpc_method(sess, id)) + + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); + if (!ksmbd_session_rpc_method(sess, id)) { + up_read(&sess->rpc_lock); return -ENOENT; + } + up_read(&sess->rpc_lock); ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", req->FileInfoClass, req->VolatileFileId); @@ -4195,14 +4630,12 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, case FILE_STANDARD_INFORMATION: get_standard_info_pipe(rsp, rsp_org); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, rsp_org, - FILE_STANDARD_INFORMATION_SIZE); + rsp, rsp_org); break; case FILE_INTERNAL_INFORMATION: get_internal_info_pipe(rsp, id, rsp_org); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, rsp_org, - FILE_INTERNAL_INFORMATION_SIZE); + rsp, rsp_org); break; default: ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", @@ -4243,11 +4676,12 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, path = &fp->filp->f_path; /* single EA entry is requested with given user.* name */ if (req->InputBufferLength) { - if (le32_to_cpu(req->InputBufferLength) < + if (le32_to_cpu(req->InputBufferLength) <= sizeof(struct smb2_ea_info_req)) return -EINVAL; - ea_req = (struct smb2_ea_info_req *)req->Buffer; + ea_req = (struct smb2_ea_info_req *)((char *)req + + le16_to_cpu(req->InputBufferOffset)); } else { /* need to send all EAs, if no specific EA is requested*/ if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) @@ -4308,7 +4742,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) name_len -= XATTR_USER_PREFIX_LEN; - ptr = (char *)(&eainfo->name + name_len + 1); + ptr = eainfo->name + name_len + 1; buf_free_len -= (offsetof(struct smb2_ea_info, name) + name_len + 1); /* bailout if xattr can't fit in buf_free_len */ @@ -4370,7 +4804,6 @@ done: if (rsp_data_cnt == 0) rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); - inc_rfc1001_len(rsp_org, rsp_data_cnt); out: kvfree(xattr_list); return rc; @@ -4385,7 +4818,6 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, file_info->AccessFlags = fp->daccess; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_access_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); } static int get_file_basic_info(struct smb2_query_info_rsp *rsp, @@ -4394,6 +4826,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, struct smb2_file_basic_info *basic_info; struct kstat stat; u64 time; + int ret; if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { pr_err("no right to read the attributes : 0x%x\n", @@ -4401,9 +4834,12 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, return -EACCES; } + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; + basic_info = (struct smb2_file_basic_info *)rsp->Buffer; - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, - file_inode(fp->filp), &stat); basic_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); basic_info->LastAccessTime = cpu_to_le64(time); @@ -4415,33 +4851,39 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, basic_info->Pad1 = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_basic_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); return 0; } -static void get_file_standard_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) +static int get_file_standard_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_standard_info *sinfo; unsigned int delete_pending; - struct inode *inode; struct kstat stat; + int ret; - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat); + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; sinfo = (struct smb2_file_standard_info *)rsp->Buffer; delete_pending = ksmbd_inode_pending_delete(fp); - sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); - sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + if (ksmbd_stream_fd(fp) == false) { + sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + } else { + sinfo->AllocationSize = cpu_to_le64(fp->stream.size); + sinfo->EndOfFile = cpu_to_le64(fp->stream.size); + } sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); sinfo->DeletePending = delete_pending; sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_standard_info)); + + return 0; } static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, @@ -4453,8 +4895,6 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, file_info->AlignmentRequirement = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_alignment_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_alignment_info)); } static int get_file_all_info(struct ksmbd_work *work, @@ -4465,11 +4905,11 @@ static int get_file_all_info(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct smb2_file_all_info *file_info; unsigned int delete_pending; - struct inode *inode; struct kstat stat; int conv_len; char *filename; u64 time; + int ret; if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", @@ -4481,8 +4921,10 @@ static int get_file_all_info(struct ksmbd_work *work, if (IS_ERR(filename)) return PTR_ERR(filename); - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat); + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; ksmbd_debug(SMB, "filename = %s\n", filename); delete_pending = ksmbd_inode_pending_delete(fp); @@ -4497,9 +4939,14 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->ChangeTime = cpu_to_le64(time); file_info->Attributes = fp->f_ci->m_fattr; file_info->Pad1 = 0; - file_info->AllocationSize = - cpu_to_le64(inode->i_blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + if (ksmbd_stream_fd(fp) == false) { + file_info->AllocationSize = + cpu_to_le64(stat.blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + } else { + file_info->AllocationSize = cpu_to_le64(fp->stream.size); + file_info->EndOfFile = cpu_to_le64(fp->stream.size); + } file_info->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); file_info->DeletePending = delete_pending; @@ -4508,7 +4955,10 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->IndexNumber = cpu_to_le64(stat.ino); file_info->EASize = 0; file_info->AccessFlags = fp->daccess; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + if (ksmbd_stream_fd(fp) == false) + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + else + file_info->CurrentByteOffset = cpu_to_le64(fp->stream.pos); file_info->Mode = fp->coption; file_info->AlignmentRequirement = 0; conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, @@ -4518,7 +4968,6 @@ static int get_file_all_info(struct ksmbd_work *work, rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); kfree(filename); - inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); return 0; } @@ -4540,14 +4989,13 @@ static void get_file_alternate_info(struct ksmbd_work *work, spin_unlock(&dentry->d_lock); file_info->FileNameLength = cpu_to_le32(conv_len); rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); - inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + cpu_to_le32(struct_size(file_info, FileName, conv_len)); } -static void get_file_stream_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) +static int get_file_stream_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) { struct ksmbd_conn *conn = work->conn; struct smb2_file_stream_info *file_info; @@ -4558,9 +5006,13 @@ static void get_file_stream_info(struct ksmbd_work *work, int nbytes = 0, streamlen, stream_name_len, next, idx = 0; int buf_free_len; struct smb2_query_info_req *req = ksmbd_req_buf_next(work); + int ret; + + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, - file_inode(fp->filp), &stat); file_info = (struct smb2_file_stream_info *)rsp->Buffer; buf_free_len = @@ -4594,7 +5046,7 @@ static void get_file_stream_info(struct ksmbd_work *work, /* plus : size */ streamlen += 1; - stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + stream_buf = kmalloc(streamlen + 1, KSMBD_DEFAULT_GFP); if (!stream_buf) break; @@ -4641,31 +5093,37 @@ out: kvfree(xattr_list); rsp->OutputBufferLength = cpu_to_le32(nbytes); - inc_rfc1001_len(rsp_org, nbytes); + + return 0; } -static void get_file_internal_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) +static int get_file_internal_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_internal_info *file_info; struct kstat stat; + int ret; + + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, - file_inode(fp->filp), &stat); file_info = (struct smb2_file_internal_info *)rsp->Buffer; file_info->IndexNumber = cpu_to_le64(stat.ino); rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); + + return 0; } static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, void *rsp_org) { - struct smb2_file_ntwrk_info *file_info; - struct inode *inode; + struct smb2_file_network_open_info *file_info; struct kstat stat; u64 time; + int ret; if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { pr_err("no right to read the attributes : 0x%x\n", @@ -4673,10 +5131,12 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, return -EACCES; } - file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat); + file_info = (struct smb2_file_network_open_info *)rsp->Buffer; file_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); @@ -4686,13 +5146,16 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, time = ksmbd_UnixTimeToNT(stat.ctime); file_info->ChangeTime = cpu_to_le64(time); file_info->Attributes = fp->f_ci->m_fattr; - file_info->AllocationSize = - cpu_to_le64(inode->i_blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + if (ksmbd_stream_fd(fp) == false) { + file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + } else { + file_info->AllocationSize = cpu_to_le64(fp->stream.size); + file_info->EndOfFile = cpu_to_le64(fp->stream.size); + } file_info->Reserved = cpu_to_le32(0); rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + cpu_to_le32(sizeof(struct smb2_file_network_open_info)); return 0; } @@ -4704,7 +5167,6 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) file_info->EASize = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_ea_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); } static void get_file_position_info(struct smb2_query_info_rsp *rsp, @@ -4713,10 +5175,13 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp, struct smb2_file_pos_info *file_info; file_info = (struct smb2_file_pos_info *)rsp->Buffer; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + if (ksmbd_stream_fd(fp) == false) + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + else + file_info->CurrentByteOffset = cpu_to_le64(fp->stream.pos); + rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_pos_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); } static void get_file_mode_info(struct smb2_query_info_rsp *rsp, @@ -4728,17 +5193,19 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp, file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_mode_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); } -static void get_file_compression_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) +static int get_file_compression_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) { struct smb2_file_comp_info *file_info; struct kstat stat; + int ret; - generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, - file_inode(fp->filp), &stat); + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; file_info = (struct smb2_file_comp_info *)rsp->Buffer; file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); @@ -4750,7 +5217,8 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_comp_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); + + return 0; } static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, @@ -4769,7 +5237,6 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, file_info->ReparseTag = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); return 0; } @@ -4781,24 +5248,56 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, struct mnt_idmap *idmap = file_mnt_idmap(fp->filp); vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode); + struct kstat stat; u64 time; int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; + int ret; + + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) + return ret; file_info = (struct smb311_posix_qinfo *)rsp->Buffer; file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(inode->i_atime); + time = ksmbd_UnixTimeToNT(stat.atime); file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_mtime); + time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode_get_ctime(inode)); + time = ksmbd_UnixTimeToNT(stat.ctime); file_info->ChangeTime = cpu_to_le64(time); file_info->DosAttributes = fp->f_ci->m_fattr; - file_info->Inode = cpu_to_le64(inode->i_ino); - file_info->EndOfFile = cpu_to_le64(inode->i_size); - file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); - file_info->HardLinks = cpu_to_le32(inode->i_nlink); - file_info->Mode = cpu_to_le32(inode->i_mode & 0777); - file_info->DeviceId = cpu_to_le32(inode->i_rdev); + file_info->Inode = cpu_to_le64(stat.ino); + if (ksmbd_stream_fd(fp) == false) { + file_info->EndOfFile = cpu_to_le64(stat.size); + file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + } else { + file_info->EndOfFile = cpu_to_le64(fp->stream.size); + file_info->AllocationSize = cpu_to_le64(fp->stream.size); + } + file_info->HardLinks = cpu_to_le32(stat.nlink); + file_info->Mode = cpu_to_le32(stat.mode & 0777); + switch (stat.mode & S_IFMT) { + case S_IFDIR: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_DIR << POSIX_FILETYPE_SHIFT); + break; + case S_IFLNK: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_SYMLINK << POSIX_FILETYPE_SHIFT); + break; + case S_IFCHR: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_CHARDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFBLK: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_BLKDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFIFO: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_FIFO << POSIX_FILETYPE_SHIFT); + break; + case S_IFSOCK: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_SOCKET << POSIX_FILETYPE_SHIFT); + } + + file_info->DeviceId = cpu_to_le32(stat.rdev); /* * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). @@ -4811,8 +5310,8 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); rsp->OutputBufferLength = cpu_to_le32(out_buf_len); - inc_rfc1001_len(rsp_org, out_buf_len); - return out_buf_len; + + return 0; } static int smb2_get_info_file(struct ksmbd_work *work, @@ -4822,7 +5321,6 @@ static int smb2_get_info_file(struct ksmbd_work *work, struct ksmbd_file *fp; int fileinfoclass = 0; int rc = 0; - int file_infoclass_size; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; if (test_share_config_flag(work->tcon->share_conf, @@ -4855,85 +5353,69 @@ static int smb2_get_info_file(struct ksmbd_work *work, switch (fileinfoclass) { case FILE_ACCESS_INFORMATION: get_file_access_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; break; case FILE_BASIC_INFORMATION: rc = get_file_basic_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; break; case FILE_STANDARD_INFORMATION: - get_file_standard_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + rc = get_file_standard_info(rsp, fp, work->response_buf); break; case FILE_ALIGNMENT_INFORMATION: get_file_alignment_info(rsp, work->response_buf); - file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; break; case FILE_ALL_INFORMATION: rc = get_file_all_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_ALL_INFORMATION_SIZE; break; case FILE_ALTERNATE_NAME_INFORMATION: get_file_alternate_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; break; case FILE_STREAM_INFORMATION: - get_file_stream_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + rc = get_file_stream_info(work, rsp, fp, work->response_buf); break; case FILE_INTERNAL_INFORMATION: - get_file_internal_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + rc = get_file_internal_info(rsp, fp, work->response_buf); break; case FILE_NETWORK_OPEN_INFORMATION: rc = get_file_network_open_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; break; case FILE_EA_INFORMATION: get_file_ea_info(rsp, work->response_buf); - file_infoclass_size = FILE_EA_INFORMATION_SIZE; break; case FILE_FULL_EA_INFORMATION: rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); - file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; break; case FILE_POSITION_INFORMATION: get_file_position_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; break; case FILE_MODE_INFORMATION: get_file_mode_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_MODE_INFORMATION_SIZE; break; case FILE_COMPRESSION_INFORMATION: - get_file_compression_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + rc = get_file_compression_info(rsp, fp, work->response_buf); break; case FILE_ATTRIBUTE_TAG_INFORMATION: rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; break; case SMB_FIND_FILE_POSIX_INFO: if (!work->tcon->posix_extensions) { pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); rc = -EOPNOTSUPP; } else { - file_infoclass_size = find_file_posix_info(rsp, fp, - work->response_buf); + rc = find_file_posix_info(rsp, fp, work->response_buf); } break; default: @@ -4943,8 +5425,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, } if (!rc) rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, work->response_buf, - file_infoclass_size); + rsp, work->response_buf); ksmbd_fd_put(work, fp); return rc; } @@ -4960,7 +5441,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, struct kstatfs stfs; struct path path; int rc = 0, len; - int fs_infoclass_size = 0; if (!share->path) return -EIO; @@ -4983,23 +5463,26 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, switch (fsinfoclass) { case FS_DEVICE_INFORMATION: { - struct filesystem_device_info *info; + FILE_SYSTEM_DEVICE_INFO *info; - info = (struct filesystem_device_info *)rsp->Buffer; + info = (FILE_SYSTEM_DEVICE_INFO *)rsp->Buffer; - info->DeviceType = cpu_to_le32(stfs.f_type); - info->DeviceCharacteristics = cpu_to_le32(0x00000020); + info->DeviceType = cpu_to_le32(FILE_DEVICE_DISK); + info->DeviceCharacteristics = + cpu_to_le32(FILE_DEVICE_IS_MOUNTED); + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) + info->DeviceCharacteristics |= + cpu_to_le32(FILE_READ_ONLY_DEVICE); rsp->OutputBufferLength = cpu_to_le32(8); - inc_rfc1001_len(work->response_buf, 8); - fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; break; } case FS_ATTRIBUTE_INFORMATION: { - struct filesystem_attribute_info *info; + FILE_SYSTEM_ATTRIBUTE_INFO *info; size_t sz; - info = (struct filesystem_attribute_info *)rsp->Buffer; + info = (FILE_SYSTEM_ATTRIBUTE_INFO *)rsp->Buffer; info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | FILE_PERSISTENT_ACLS | FILE_UNICODE_ON_DISK | @@ -5018,10 +5501,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, "NTFS", PATH_MAX, conn->local_nls, 0); len = len * 2; info->FileSystemNameLen = cpu_to_le32(len); - sz = sizeof(struct filesystem_attribute_info) - 2 + len; + sz = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + len; rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(work->response_buf, sz); - fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; break; } case FS_VOLUME_INFORMATION: @@ -5046,24 +5527,20 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, len = len * 2; info->VolumeLabelSize = cpu_to_le32(len); info->Reserved = 0; - sz = sizeof(struct filesystem_vol_info) - 2 + len; + sz = sizeof(struct filesystem_vol_info) + len; rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(work->response_buf, sz); - fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; break; } case FS_SIZE_INFORMATION: { - struct filesystem_info *info; + FILE_SYSTEM_SIZE_INFO *info; - info = (struct filesystem_info *)(rsp->Buffer); + info = (FILE_SYSTEM_SIZE_INFO *)(rsp->Buffer); info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); - info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->AvailableAllocationUnits = cpu_to_le64(stfs.f_bfree); info->SectorsPerAllocationUnit = cpu_to_le32(1); info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(24); - inc_rfc1001_len(work->response_buf, 24); - fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; break; } case FS_FULL_SIZE_INFORMATION: @@ -5079,8 +5556,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->SectorsPerAllocationUnit = cpu_to_le32(1); info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(32); - inc_rfc1001_len(work->response_buf, 32); - fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; break; } case FS_OBJECT_ID_INFORMATION: @@ -5100,8 +5575,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->extended_info.rel_date = 0; memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); rsp->OutputBufferLength = cpu_to_le32(64); - inc_rfc1001_len(work->response_buf, 64); - fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; break; } case FS_SECTOR_SIZE_INFORMATION: @@ -5123,8 +5596,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->ByteOffsetForSectorAlignment = 0; info->ByteOffsetForPartitionAlignment = 0; rsp->OutputBufferLength = cpu_to_le32(28); - inc_rfc1001_len(work->response_buf, 28); - fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; break; } case FS_CONTROL_INFORMATION: @@ -5145,19 +5616,18 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); info->Padding = 0; rsp->OutputBufferLength = cpu_to_le32(48); - inc_rfc1001_len(work->response_buf, 48); - fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; break; } case FS_POSIX_INFORMATION: { - struct filesystem_posix_info *info; + FILE_SYSTEM_POSIX_INFO *info; if (!work->tcon->posix_extensions) { pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); - rc = -EOPNOTSUPP; + path_put(&path); + return -EOPNOTSUPP; } else { - info = (struct filesystem_posix_info *)(rsp->Buffer); + info = (FILE_SYSTEM_POSIX_INFO *)(rsp->Buffer); info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); info->BlockSize = cpu_to_le32(stfs.f_bsize); info->TotalBlocks = cpu_to_le64(stfs.f_blocks); @@ -5166,8 +5636,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->TotalFileNodes = cpu_to_le64(stfs.f_files); info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); rsp->OutputBufferLength = cpu_to_le32(56); - inc_rfc1001_len(work->response_buf, 56); - fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; } break; } @@ -5176,8 +5644,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, return -EOPNOTSUPP; } rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, work->response_buf, - fs_infoclass_size); + rsp, work->response_buf); path_put(&path); return rc; } @@ -5211,7 +5678,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work, secdesclen = sizeof(struct smb_ntsd); rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(work->response_buf, secdesclen); return 0; } @@ -5256,7 +5722,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work, return rc; rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(work->response_buf, secdesclen); return 0; } @@ -5272,9 +5737,14 @@ int smb2_query_info(struct ksmbd_work *work) struct smb2_query_info_rsp *rsp; int rc = 0; + ksmbd_debug(SMB, "Received request smb2 query info request\n"); + WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "GOT query info request\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } switch (req->InfoType) { case SMB2_O_INFO_FILE: @@ -5294,7 +5764,17 @@ int smb2_query_info(struct ksmbd_work *work) req->InfoType); rc = -EOPNOTSUPP; } + ksmbd_revert_fsids(work); + if (!rc) { + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_query_info_rsp, Buffer) + + le32_to_cpu(rsp->OutputBufferLength)); + } + +err_out: if (rc < 0) { if (rc == -EACCES) rsp->hdr.Status = STATUS_ACCESS_DENIED; @@ -5302,6 +5782,8 @@ int smb2_query_info(struct ksmbd_work *work) rsp->hdr.Status = STATUS_FILE_CLOSED; else if (rc == -EIO) rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; smb2_set_err_rsp(work); @@ -5310,9 +5792,6 @@ int smb2_query_info(struct ksmbd_work *work) rc); return rc; } - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - inc_rfc1001_len(work->response_buf, 8); return 0; } @@ -5343,15 +5822,16 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) rsp->AllocationSize = 0; rsp->EndOfFile = 0; rsp->Attributes = 0; - inc_rfc1001_len(work->response_buf, 60); - return 0; + + return ksmbd_iov_pin_rsp(work, (void *)rsp, + sizeof(struct smb2_close_rsp)); } /** * smb2_close() - handler for smb2 close file command * @work: smb work containing close request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_close(struct ksmbd_work *work) { @@ -5361,10 +5841,11 @@ int smb2_close(struct ksmbd_work *work) struct smb2_close_rsp *rsp; struct ksmbd_conn *conn = work->conn; struct ksmbd_file *fp; - struct inode *inode; u64 time; int err = 0; + ksmbd_debug(SMB, "Received smb2 close request\n"); + WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -5416,24 +5897,33 @@ int smb2_close(struct ksmbd_work *work) rsp->Reserved = 0; if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { + struct kstat stat; + int ret; + fp = ksmbd_lookup_fd_fast(work, volatile_id); if (!fp) { err = -ENOENT; goto out; } - inode = file_inode(fp->filp); + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (ret) { + ksmbd_fd_put(work, fp); + goto out; + } + rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : - cpu_to_le64(inode->i_blocks << 9); - rsp->EndOfFile = cpu_to_le64(inode->i_size); + rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.blocks << 9); + rsp->EndOfFile = cpu_to_le64(stat.size); rsp->Attributes = fp->f_ci->m_fattr; rsp->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(inode->i_atime); + time = ksmbd_UnixTimeToNT(stat.atime); rsp->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_mtime); + time = ksmbd_UnixTimeToNT(stat.mtime); rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode_get_ctime(inode)); + time = ksmbd_UnixTimeToNT(stat.ctime); rsp->ChangeTime = cpu_to_le64(time); ksmbd_fd_put(work, fp); } else { @@ -5449,34 +5939,37 @@ int smb2_close(struct ksmbd_work *work) err = ksmbd_close_fd(work, volatile_id); out: + if (!err) + err = ksmbd_iov_pin_rsp(work, (void *)rsp, + sizeof(struct smb2_close_rsp)); + if (err) { if (rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_FILE_CLOSED; smb2_set_err_rsp(work); - } else { - inc_rfc1001_len(work->response_buf, 60); } - return 0; + return err; } /** * smb2_echo() - handler for smb2 echo(ping) command * @work: smb work containing echo request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_echo(struct ksmbd_work *work) { struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + ksmbd_debug(SMB, "Received smb2 echo request\n"); + if (work->next_smb2_rcv_hdr_off) rsp = ksmbd_resp_buf_next(work); rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - return 0; + return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_echo_rsp)); } static int smb2_rename(struct ksmbd_work *work, @@ -5495,7 +5988,7 @@ static int smb2_rename(struct ksmbd_work *work, if (IS_ERR(new_name)) return PTR_ERR(new_name); - if (strchr(new_name, ':')) { + if (fp->is_posix_ctxt == false && strchr(new_name, ':')) { int s_type; char *xattr_stream_name, *stream_name = NULL; size_t xattr_stream_size; @@ -5522,7 +6015,7 @@ static int smb2_rename(struct ksmbd_work *work, rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp), &fp->filp->f_path, xattr_stream_name, - NULL, 0, 0); + NULL, 0, 0, true); if (rc < 0) { pr_err("failed to store stream name in xattr: %d\n", rc); @@ -5544,6 +6037,8 @@ static int smb2_rename(struct ksmbd_work *work, flags = RENAME_NOREPLACE; rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags); + if (!rc) + smb_break_all_levII_oplock(work, fp, 0); out: kfree(new_name); return rc; @@ -5556,8 +6051,7 @@ static int smb2_create_link(struct ksmbd_work *work, struct nls_table *local_nls) { char *link_name = NULL, *target_name = NULL, *pathname = NULL; - struct path path, parent_path; - bool file_present = false; + struct path path; int rc; if (buf_len < (u64)sizeof(struct smb2_file_link_info) + @@ -5565,7 +6059,7 @@ static int smb2_create_link(struct ksmbd_work *work, return -EINVAL; ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); if (!pathname) return -ENOMEM; @@ -5585,16 +6079,13 @@ static int smb2_create_link(struct ksmbd_work *work, } ksmbd_debug(SMB, "target name is %s\n", target_name); - rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, - &parent_path, &path, 0); + rc = ksmbd_vfs_kern_path_start_removing(work, link_name, LOOKUP_NO_SYMLINKS, + &path, 0); if (rc) { if (rc != -ENOENT) goto out; - } else - file_present = true; - - if (file_info->ReplaceIfExists) { - if (file_present) { + } else { + if (file_info->ReplaceIfExists) { rc = ksmbd_vfs_remove_file(work, &path); if (rc) { rc = -EINVAL; @@ -5602,24 +6093,18 @@ static int smb2_create_link(struct ksmbd_work *work, link_name); goto out; } - } - } else { - if (file_present) { + } else { rc = -EEXIST; ksmbd_debug(SMB, "link already exists\n"); goto out; } + ksmbd_vfs_kern_path_end_removing(&path); } - rc = ksmbd_vfs_link(work, target_name, link_name); if (rc) rc = -EINVAL; out: - if (file_present) { - inode_unlock(d_inode(parent_path.dentry)); - path_put(&path); - path_put(&parent_path); - } + if (!IS_ERR(link_name)) kfree(link_name); kfree(pathname); @@ -5652,15 +6137,13 @@ static int set_file_basic_info(struct ksmbd_file *fp, attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); } - attrs.ia_valid |= ATTR_CTIME; if (file_info->ChangeTime) - attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); - else - attrs.ia_ctime = inode_get_ctime(inode); + inode_set_ctime_to_ts(inode, + ksmbd_NTtimeToUnix(file_info->ChangeTime)); if (file_info->LastWriteTime) { attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); - attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET | ATTR_CTIME); } if (file_info->Attributes) { @@ -5686,7 +6169,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | XATTR_DOSINFO_ITIME; - rc = ksmbd_vfs_set_dos_attrib_xattr(idmap, &filp->f_path, &da); + rc = ksmbd_vfs_set_dos_attrib_xattr(idmap, &filp->f_path, &da, + true); if (rc) ksmbd_debug(SMB, "failed to restore file attribute in EA\n"); @@ -5701,8 +6185,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, return -EACCES; inode_lock(inode); - inode_set_ctime_to_ts(inode, attrs.ia_ctime); - attrs.ia_valid &= ~ATTR_CTIME; rc = notify_change(idmap, dentry, &attrs, NULL); inode_unlock(inode); } @@ -5721,15 +6203,24 @@ static int set_file_allocation_info(struct ksmbd_work *work, loff_t alloc_blks; struct inode *inode; + struct kstat stat; int rc; if (!(fp->daccess & FILE_WRITE_DATA_LE)) return -EACCES; + if (ksmbd_stream_fd(fp) == true) + return 0; + + rc = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (rc) + return rc; + alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; inode = file_inode(fp->filp); - if (alloc_blks > inode->i_blocks) { + if (alloc_blks > stat.blocks) { smb_break_all_levII_oplock(work, fp, 1); rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, alloc_blks * 512); @@ -5737,7 +6228,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, pr_err("vfs_fallocate is failed : %d\n", rc); return rc; } - } else if (alloc_blks < inode->i_blocks) { + } else if (alloc_blks < stat.blocks) { loff_t size; /* @@ -5779,7 +6270,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, * truncate of some filesystem like FAT32 fill zero data in * truncated range. */ - if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC && + ksmbd_stream_fd(fp) == false) { ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); rc = ksmbd_vfs_truncate(work, fp, newsize); if (rc) { @@ -5852,7 +6344,13 @@ static int set_file_position_info(struct ksmbd_file *fp, return -EINVAL; } - fp->filp->f_pos = current_byte_offset; + if (ksmbd_stream_fd(fp) == false) + fp->filp->f_pos = current_byte_offset; + else { + if (current_byte_offset > XATTR_SIZE_MAX) + current_byte_offset = XATTR_SIZE_MAX; + fp->stream.pos = current_byte_offset; + } return 0; } @@ -5885,76 +6383,64 @@ static int set_file_mode_info(struct ksmbd_file *fp, * @share: ksmbd_share_config pointer * * Return: 0 on success, otherwise error - * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH */ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, struct smb2_set_info_req *req, struct ksmbd_share_config *share) { unsigned int buf_len = le32_to_cpu(req->BufferLength); + char *buffer = (char *)req + le16_to_cpu(req->BufferOffset); switch (req->FileInfoClass) { case FILE_BASIC_INFORMATION: { if (buf_len < sizeof(struct smb2_file_basic_info)) - return -EINVAL; + return -EMSGSIZE; - return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); + return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share); } case FILE_ALLOCATION_INFORMATION: { if (buf_len < sizeof(struct smb2_file_alloc_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_allocation_info(work, fp, - (struct smb2_file_alloc_info *)req->Buffer); + (struct smb2_file_alloc_info *)buffer); } case FILE_END_OF_FILE_INFORMATION: { if (buf_len < sizeof(struct smb2_file_eof_info)) - return -EINVAL; + return -EMSGSIZE; return set_end_of_file_info(work, fp, - (struct smb2_file_eof_info *)req->Buffer); + (struct smb2_file_eof_info *)buffer); } case FILE_RENAME_INFORMATION: { - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - return -EACCES; - } - if (buf_len < sizeof(struct smb2_file_rename_info)) - return -EINVAL; + return -EMSGSIZE; return set_rename_info(work, fp, - (struct smb2_file_rename_info *)req->Buffer, + (struct smb2_file_rename_info *)buffer, buf_len); } case FILE_LINK_INFORMATION: { if (buf_len < sizeof(struct smb2_file_link_info)) - return -EINVAL; + return -EMSGSIZE; return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)req->Buffer, + (struct smb2_file_link_info *)buffer, buf_len, fp->filp, work->conn->local_nls); } case FILE_DISPOSITION_INFORMATION: { - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - return -EACCES; - } - if (buf_len < sizeof(struct smb2_file_disposition_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_disposition_info(fp, - (struct smb2_file_disposition_info *)req->Buffer); + (struct smb2_file_disposition_info *)buffer); } case FILE_FULL_EA_INFORMATION: { @@ -5965,24 +6451,24 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } if (buf_len < sizeof(struct smb2_ea_info)) - return -EINVAL; + return -EMSGSIZE; - return smb2_set_ea((struct smb2_ea_info *)req->Buffer, - buf_len, &fp->filp->f_path); + return smb2_set_ea((struct smb2_ea_info *)buffer, + buf_len, &fp->filp->f_path, true); } case FILE_POSITION_INFORMATION: { if (buf_len < sizeof(struct smb2_file_pos_info)) - return -EINVAL; + return -EMSGSIZE; - return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); + return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer); } case FILE_MODE_INFORMATION: { if (buf_len < sizeof(struct smb2_file_mode_info)) - return -EINVAL; + return -EMSGSIZE; - return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); + return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer); } } @@ -5998,7 +6484,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, fp->saccess |= FILE_SHARE_DELETE_LE; return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, - buf_len, false); + buf_len, false, true); } /** @@ -6011,11 +6497,11 @@ int smb2_set_info(struct ksmbd_work *work) { struct smb2_set_info_req *req; struct smb2_set_info_rsp *rsp; - struct ksmbd_file *fp; + struct ksmbd_file *fp = NULL; int rc = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - ksmbd_debug(SMB, "Received set info request\n"); + ksmbd_debug(SMB, "Received smb2 set info request\n"); if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); @@ -6031,6 +6517,13 @@ int smb2_set_info(struct ksmbd_work *work) rsp = smb2_get_msg(work->response_buf); } + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + pr_err("User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + if (!has_file_id(id)) { id = req->VolatileFileId; pid = req->PersistentFileId; @@ -6056,7 +6549,7 @@ int smb2_set_info(struct ksmbd_work *work) } rc = smb2_set_info_sec(fp, le32_to_cpu(req->AdditionalInformation), - req->Buffer, + (char *)req + le16_to_cpu(req->BufferOffset), le32_to_cpu(req->BufferLength)); ksmbd_revert_fsids(work); break; @@ -6068,7 +6561,10 @@ int smb2_set_info(struct ksmbd_work *work) goto err_out; rsp->StructureSize = cpu_to_le16(2); - inc_rfc1001_len(work->response_buf, 2); + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, + sizeof(struct smb2_set_info_rsp)); + if (rc) + goto err_out; ksmbd_fd_put(work, fp); return 0; @@ -6077,6 +6573,8 @@ err_out: rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc == -EINVAL) rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EMSGSIZE) + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; else if (rc == -ESHARE) rsp->hdr.Status = STATUS_SHARING_VIOLATION; else if (rc == -ENOENT) @@ -6115,28 +6613,38 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) id = req->VolatileFileId; - inc_rfc1001_len(work->response_buf, 16); rpc_resp = ksmbd_rpc_read(work->sess, id); if (rpc_resp) { + void *aux_payload_buf; + if (rpc_resp->flags != KSMBD_RPC_OK) { err = -EINVAL; goto out; } - work->aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); - if (!work->aux_payload_buf) { + aux_payload_buf = + kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP); + if (!aux_payload_buf) { err = -ENOMEM; goto out; } - memcpy(work->aux_payload_buf, rpc_resp->payload, - rpc_resp->payload_sz); + memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); nbytes = rpc_resp->payload_sz; - work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; - work->aux_payload_sz = nbytes; + err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, + offsetof(struct smb2_read_rsp, Buffer), + aux_payload_buf, nbytes); + if (err) { + kvfree(aux_payload_buf); + goto out; + } kvfree(rpc_resp); + } else { + err = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_read_rsp, Buffer)); + if (err) + goto out; } rsp->StructureSize = cpu_to_le16(17); @@ -6145,7 +6653,6 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = 0; rsp->Flags = 0; - inc_rfc1001_len(work->response_buf, nbytes); return 0; out: @@ -6156,7 +6663,7 @@ out: } static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, __le32 Channel, __le16 ChannelInfoLength) { @@ -6192,7 +6699,7 @@ static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, int err; err = ksmbd_conn_rdma_write(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), le16_to_cpu(req->ReadChannelInfoLength)); if (err) @@ -6219,13 +6726,10 @@ int smb2_read(struct ksmbd_work *work) int err = 0; bool is_rdma_channel = false; unsigned int max_read_size = conn->vals->max_read_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + void *aux_payload_buf; - WORK_BUFFERS(work, req, rsp); - if (work->next_smb2_rcv_hdr_off) { - work->send_no_response = 1; - err = -EOPNOTSUPP; - goto out; - } + ksmbd_debug(SMB, "Received smb2 read request\n"); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { @@ -6233,10 +6737,33 @@ int smb2_read(struct ksmbd_work *work) return smb2_read_pipe(work); } + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || req->Channel == SMB2_CHANNEL_RDMA_V1) { is_rdma_channel = true; - max_read_size = get_smbd_max_read_write_size(); + max_read_size = get_smbd_max_read_write_size(work->conn->transport); + if (max_read_size == 0) { + err = -EINVAL; + goto out; + } } if (is_rdma_channel == true) { @@ -6247,7 +6774,7 @@ int smb2_read(struct ksmbd_work *work) goto out; } err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + ch_offset), req->Channel, req->ReadChannelInfoLength); @@ -6255,7 +6782,7 @@ int smb2_read(struct ksmbd_work *work) goto out; } - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + fp = ksmbd_lookup_fd_slow(work, id, pid); if (!fp) { err = -ENOENT; goto out; @@ -6268,6 +6795,10 @@ int smb2_read(struct ksmbd_work *work) } offset = le64_to_cpu(req->Offset); + if (offset < 0) { + err = -EINVAL; + goto out; + } length = le32_to_cpu(req->Length); mincount = le32_to_cpu(req->MinimumCount); @@ -6281,25 +6812,25 @@ int smb2_read(struct ksmbd_work *work) ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", fp->filp, offset, length); - work->aux_payload_buf = kvzalloc(length, GFP_KERNEL); - if (!work->aux_payload_buf) { + aux_payload_buf = kvzalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP); + if (!aux_payload_buf) { err = -ENOMEM; goto out; } - nbytes = ksmbd_vfs_read(work, fp, length, &offset); + nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf); if (nbytes < 0) { + kvfree(aux_payload_buf); err = nbytes; goto out; } if ((nbytes == 0 && length != 0) || nbytes < mincount) { - kvfree(work->aux_payload_buf); - work->aux_payload_buf = NULL; + kvfree(aux_payload_buf); rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); - return 0; + return -ENODATA; } ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", @@ -6308,11 +6839,10 @@ int smb2_read(struct ksmbd_work *work) if (is_rdma_channel == true) { /* write data to the client using rdma channel */ remain_bytes = smb2_read_rdma_channel(work, req, - work->aux_payload_buf, + aux_payload_buf, nbytes); - kvfree(work->aux_payload_buf); - work->aux_payload_buf = NULL; - + kvfree(aux_payload_buf); + aux_payload_buf = NULL; nbytes = 0; if (remain_bytes < 0) { err = (int)remain_bytes; @@ -6326,10 +6856,13 @@ int smb2_read(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = cpu_to_le32(remain_bytes); rsp->Flags = 0; - inc_rfc1001_len(work->response_buf, 16); - work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; - work->aux_payload_sz = nbytes; - inc_rfc1001_len(work->response_buf, nbytes); + err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, + offsetof(struct smb2_read_rsp, Buffer), + aux_payload_buf, nbytes); + if (err) { + kvfree(aux_payload_buf); + goto out; + } ksmbd_fd_put(work, fp); return 0; @@ -6412,8 +6945,8 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(length); rsp->DataRemaining = 0; rsp->Reserved2 = 0; - inc_rfc1001_len(work->response_buf, 16); - return 0; + err = ksmbd_iov_pin_rsp(work, (void *)rsp, + offsetof(struct smb2_write_rsp, Buffer)); out: if (err) { rsp->hdr.Status = STATUS_INVALID_HANDLE; @@ -6432,12 +6965,12 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, int ret; ssize_t nbytes; - data_buf = kvzalloc(length, GFP_KERNEL); + data_buf = kvzalloc(length, KSMBD_DEFAULT_GFP); if (!data_buf) return -ENOMEM; ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), le16_to_cpu(req->WriteChannelInfoLength)); if (ret < 0) { @@ -6472,6 +7005,8 @@ int smb2_write(struct ksmbd_work *work) int err = 0; unsigned int max_write_size = work->conn->vals->max_write_size; + ksmbd_debug(SMB, "Received smb2 write request\n"); + WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { @@ -6480,12 +7015,18 @@ int smb2_write(struct ksmbd_work *work) } offset = le64_to_cpu(req->Offset); + if (offset < 0) + return -EINVAL; length = le32_to_cpu(req->Length); if (req->Channel == SMB2_CHANNEL_RDMA_V1 || req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { is_rdma_channel = true; - max_write_size = get_smbd_max_read_write_size(); + max_write_size = get_smbd_max_read_write_size(work->conn->transport); + if (max_write_size == 0) { + err = -EINVAL; + goto out; + } length = le32_to_cpu(req->RemainingBytes); } @@ -6498,7 +7039,7 @@ int smb2_write(struct ksmbd_work *work) goto out; } err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + ch_offset), req->Channel, req->WriteChannelInfoLength); @@ -6569,7 +7110,9 @@ int smb2_write(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = 0; rsp->Reserved2 = 0; - inc_rfc1001_len(work->response_buf, 16); + err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer)); + if (err) + goto out; ksmbd_fd_put(work, fp); return 0; @@ -6608,7 +7151,7 @@ int smb2_flush(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); + ksmbd_debug(SMB, "Received smb2 flush request(fid : %llu)\n", req->VolatileFileId); err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); if (err) @@ -6616,15 +7159,11 @@ int smb2_flush(struct ksmbd_work *work) rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - return 0; + return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_flush_rsp)); out: - if (err) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - } - + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); return err; } @@ -6706,10 +7245,10 @@ struct file_lock *smb_flock_init(struct file *f) locks_init_lock(fl); - fl->fl_owner = f; - fl->fl_pid = current->tgid; - fl->fl_file = f; - fl->fl_flags = FL_POSIX; + fl->c.flc_owner = f; + fl->c.flc_pid = current->tgid; + fl->c.flc_file = f; + fl->c.flc_flags = FL_POSIX; fl->fl_ops = NULL; fl->fl_lmops = NULL; @@ -6726,30 +7265,30 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) case SMB2_LOCKFLAG_SHARED: ksmbd_debug(SMB, "received shared request\n"); cmd = F_SETLKW; - flock->fl_type = F_RDLCK; - flock->fl_flags |= FL_SLEEP; + flock->c.flc_type = F_RDLCK; + flock->c.flc_flags |= FL_SLEEP; break; case SMB2_LOCKFLAG_EXCLUSIVE: ksmbd_debug(SMB, "received exclusive request\n"); cmd = F_SETLKW; - flock->fl_type = F_WRLCK; - flock->fl_flags |= FL_SLEEP; + flock->c.flc_type = F_WRLCK; + flock->c.flc_flags |= FL_SLEEP; break; case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ksmbd_debug(SMB, "received shared & fail immediately request\n"); cmd = F_SETLK; - flock->fl_type = F_RDLCK; + flock->c.flc_type = F_RDLCK; break; case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: ksmbd_debug(SMB, "received exclusive & fail immediately request\n"); cmd = F_SETLK; - flock->fl_type = F_WRLCK; + flock->c.flc_type = F_WRLCK; break; case SMB2_LOCKFLAG_UNLOCK: ksmbd_debug(SMB, "received unlock request\n"); - flock->fl_type = F_UNLCK; + flock->c.flc_type = F_UNLCK; cmd = F_SETLK; break; } @@ -6763,7 +7302,7 @@ static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, { struct ksmbd_lock *lock; - lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + lock = kzalloc(sizeof(struct ksmbd_lock), KSMBD_DEFAULT_GFP); if (!lock) return NULL; @@ -6787,13 +7326,13 @@ static void smb2_remove_blocked_lock(void **argv) struct file_lock *flock = (struct file_lock *)argv[0]; ksmbd_vfs_posix_lock_unblock(flock); - wake_up(&flock->fl_wait); + locks_wake_up(flock); } static inline bool lock_defer_pending(struct file_lock *fl) { /* check pending lock waiters */ - return waitqueue_active(&fl->fl_wait); + return waitqueue_active(&fl->c.flc_wait); } /** @@ -6820,11 +7359,11 @@ int smb2_lock(struct ksmbd_work *work) int nolock = 0; LIST_HEAD(lock_list); LIST_HEAD(rollback_list); - int prior_lock = 0; + int prior_lock = 0, bkt; WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "Received lock request\n"); + ksmbd_debug(SMB, "Received smb2 lock request\n"); fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); @@ -6884,8 +7423,8 @@ int smb2_lock(struct ksmbd_work *work) list_for_each_entry(cmp_lock, &lock_list, llist) { if (cmp_lock->fl->fl_start <= flock->fl_start && cmp_lock->fl->fl_end >= flock->fl_end) { - if (cmp_lock->fl->fl_type != F_UNLCK && - flock->fl_type != F_UNLCK) { + if (cmp_lock->fl->c.flc_type != F_UNLCK && + flock->c.flc_type != F_UNLCK) { pr_err("conflict two locks in one request\n"); err = -EINVAL; locks_free_lock(flock); @@ -6930,15 +7469,15 @@ int smb2_lock(struct ksmbd_work *work) nolock = 1; /* check locks in connection list */ down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { + hash_for_each(conn_list, bkt, conn, hlist) { spin_lock(&conn->llist_lock); list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { - if (file_inode(cmp_lock->fl->fl_file) != - file_inode(smb_lock->fl->fl_file)) + if (file_inode(cmp_lock->fl->c.flc_file) != + file_inode(smb_lock->fl->c.flc_file)) continue; - if (smb_lock->fl->fl_type == F_UNLCK) { - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + if (lock_is_unlock(smb_lock->fl)) { + if (cmp_lock->fl->c.flc_file == smb_lock->fl->c.flc_file && cmp_lock->start == smb_lock->start && cmp_lock->end == smb_lock->end && !lock_defer_pending(cmp_lock->fl)) { @@ -6955,7 +7494,7 @@ int smb2_lock(struct ksmbd_work *work) continue; } - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (cmp_lock->fl->c.flc_file == smb_lock->fl->c.flc_file) { if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) continue; } else { @@ -6997,24 +7536,24 @@ int smb2_lock(struct ksmbd_work *work) } up_read(&conn_list_lock); out_check_cl: - if (smb_lock->fl->fl_type == F_UNLCK && nolock) { + if (lock_is_unlock(smb_lock->fl) && nolock) { pr_err("Try to unlock nolocked range\n"); rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; goto out; } no_check_cl: + flock = smb_lock->fl; + list_del(&smb_lock->llist); + if (smb_lock->zero_len) { err = 0; goto skip; } - - flock = smb_lock->fl; - list_del(&smb_lock->llist); retry: rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); skip: - if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) { if (!rc) { ksmbd_debug(SMB, "File unlocked\n"); } else if (rc == -ENOENT) { @@ -7029,13 +7568,9 @@ skip: ksmbd_debug(SMB, "would have to wait for getting lock\n"); - spin_lock(&work->conn->llist_lock); - list_add_tail(&smb_lock->clist, - &work->conn->lock_list); - spin_unlock(&work->conn->llist_lock); list_add(&smb_lock->llist, &rollback_list); - argv = kmalloc(sizeof(void *), GFP_KERNEL); + argv = kmalloc(sizeof(void *), KSMBD_DEFAULT_GFP); if (!argv) { err = -ENOMEM; goto out; @@ -7046,6 +7581,7 @@ skip: smb2_remove_blocked_lock, argv); if (rc) { + kfree(argv); err = -ENOMEM; goto out; } @@ -7063,9 +7599,6 @@ skip: if (work->state != KSMBD_WORK_ACTIVE) { list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); locks_free_lock(flock); if (work->state == KSMBD_WORK_CANCELLED) { @@ -7078,8 +7611,6 @@ skip: goto out; } - init_smb2_rsp_hdr(work); - smb2_set_err_rsp(work); rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; kfree(smb_lock); @@ -7087,19 +7618,16 @@ skip: } list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); release_async_work(work); goto retry; } else if (!rc) { + list_add(&smb_lock->llist, &rollback_list); spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); list_add_tail(&smb_lock->flist, &fp->lock_list); spin_unlock(&work->conn->llist_lock); - list_add(&smb_lock->llist, &rollback_list); ksmbd_debug(SMB, "successful in taking lock\n"); } else { goto out; @@ -7114,7 +7642,10 @@ skip: ksmbd_debug(SMB, "successful in taking lock\n"); rsp->hdr.Status = STATUS_SUCCESS; rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); + err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp)); + if (err) + goto out; + ksmbd_fd_put(work, fp); return 0; @@ -7129,7 +7660,7 @@ out: struct file_lock *rlock = NULL; rlock = smb_flock_init(filp); - rlock->fl_type = F_UNLCK; + rlock->c.flc_type = F_UNLCK; rlock->fl_start = smb_lock->start; rlock->fl_end = smb_lock->end; @@ -7194,7 +7725,6 @@ static int fsctl_copychunk(struct ksmbd_work *work, ci_rsp->TotalBytesWritten = cpu_to_le32(ksmbd_server_side_copy_max_total_size()); - chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; chunk_count = le32_to_cpu(ci_req->ChunkCount); if (chunk_count == 0) goto out; @@ -7202,12 +7732,12 @@ static int fsctl_copychunk(struct ksmbd_work *work, /* verify the SRV_COPYCHUNK_COPY packet */ if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || - input_count < offsetof(struct copychunk_ioctl_req, Chunks) + - chunk_count * sizeof(struct srv_copychunk)) { + input_count < struct_size(ci_req, Chunks, chunk_count)) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; return -EINVAL; } + chunks = &ci_req->Chunks[0]; for (i = 0; i < chunk_count; i++) { if (le32_to_cpu(chunks[i].Length) == 0 || le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) @@ -7222,11 +7752,11 @@ static int fsctl_copychunk(struct ksmbd_work *work, } src_fp = ksmbd_lookup_foreign_fd(work, - le64_to_cpu(ci_req->ResumeKey[0])); + le64_to_cpu(ci_req->SourceKeyU64[0])); dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); ret = -EINVAL; if (!src_fp || - src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { + src_fp->persistent_id != le64_to_cpu(ci_req->SourceKeyU64[1])) { rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; goto out; } @@ -7315,7 +7845,10 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, if (netdev->type == ARPHRD_LOOPBACK) continue; - flags = dev_get_flags(netdev); + if (!ksmbd_find_netdev_name_iface_list(netdev->name)) + continue; + + flags = netif_get_flags(netdev); if (!(flags & IFF_RUNNING)) continue; ipv6_retry: @@ -7331,9 +7864,9 @@ ipv6_retry: nii_rsp->Capability = 0; if (netdev->real_num_tx_queues > 1) - nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + nii_rsp->Capability |= RSS_CAPABLE; if (ksmbd_rdma_capable_netdev(netdev)) - nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); + nii_rsp->Capability |= RDMA_CAPABLE; nii_rsp->Next = cpu_to_le32(152); nii_rsp->Reserved = 0; @@ -7359,13 +7892,13 @@ ipv6_retry: if (!ipv4_set) { struct in_device *idev; - sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->Family = INTERNETWORK; sockaddr_storage->addr4.Port = 0; idev = __in_dev_get_rtnl(netdev); if (!idev) continue; - sockaddr_storage->addr4.IPv4address = + sockaddr_storage->addr4.IPv4Address = idev_ipv4_address(idev); nbytes += sizeof(struct network_interface_info_ioctl_rsp); ipv4_set = true; @@ -7373,9 +7906,9 @@ ipv6_retry: } else { struct inet6_dev *idev6; struct inet6_ifaddr *ifa; - __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6Address; - sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->Family = INTERNETWORKV6; sockaddr_storage->addr6.Port = 0; sockaddr_storage->addr6.FlowInfo = 0; @@ -7439,7 +7972,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, goto err_out; } - neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + neg_rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); neg_rsp->Dialect = cpu_to_le16(conn->dialect); @@ -7485,7 +8018,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, struct smb2_ioctl_rsp *rsp) { struct ksmbd_rpc_command *rpc_resp; - char *data_buf = (char *)&req->Buffer[0]; + char *data_buf = (char *)req + le32_to_cpu(req->InputOffset); int nbytes = 0; rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, @@ -7555,7 +8088,8 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, da.attr = le32_to_cpu(fp->f_ci->m_fattr); ret = ksmbd_vfs_set_dos_attrib_xattr(idmap, - &fp->filp->f_path, &da); + &fp->filp->f_path, + &da, true); if (ret) fp->f_ci->m_fattr = old_fattr; } @@ -7576,8 +8110,8 @@ static int fsctl_request_resume_key(struct ksmbd_work *work, return -ENOENT; memset(key_rsp, 0, sizeof(*key_rsp)); - key_rsp->ResumeKey[0] = req->VolatileFileId; - key_rsp->ResumeKey[1] = req->PersistentFileId; + key_rsp->ResumeKeyU64[0] = req->VolatileFileId; + key_rsp->ResumeKeyU64[1] = req->PersistentFileId; ksmbd_fd_put(work, fp); return 0; @@ -7597,6 +8131,9 @@ int smb2_ioctl(struct ksmbd_work *work) u64 id = KSMBD_NO_FID; struct ksmbd_conn *conn = work->conn; int ret = 0; + char *buffer; + + ksmbd_debug(SMB, "Received smb2 ioctl request\n"); if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); @@ -7615,10 +8152,12 @@ int smb2_ioctl(struct ksmbd_work *work) id = req->VolatileFileId; if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; + ret = -EOPNOTSUPP; goto out; } + buffer = (char *)req + le32_to_cpu(req->InputOffset); + cnt_code = le32_to_cpu(req->CtlCode); ret = smb2_calc_max_out_buf_len(work, 48, le32_to_cpu(req->MaxOutputResponse)); @@ -7633,8 +8172,9 @@ int smb2_ioctl(struct ksmbd_work *work) case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_GET_REFERRALS_EX: /* Not support DFS yet */ + ret = -EOPNOTSUPP; rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; - goto out; + goto out2; case FSCTL_CREATE_OR_GET_OBJECT_ID: { struct file_object_buf_type1_ioctl_rsp *obj_buf; @@ -7676,7 +8216,7 @@ int smb2_ioctl(struct ksmbd_work *work) } ret = fsctl_validate_negotiate_info(conn, - (struct validate_negotiate_info_req *)&req->Buffer[0], + (struct validate_negotiate_info_req *)buffer, (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], in_buf_len); if (ret < 0) @@ -7715,7 +8255,7 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } - if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + if (in_buf_len <= sizeof(struct copychunk_ioctl_req)) { ret = -EINVAL; goto out; } @@ -7729,7 +8269,7 @@ int smb2_ioctl(struct ksmbd_work *work) rsp->VolatileFileId = req->VolatileFileId; rsp->PersistentFileId = req->PersistentFileId; fsctl_copychunk(work, - (struct copychunk_ioctl_req *)&req->Buffer[0], + (struct copychunk_ioctl_req *)buffer, le32_to_cpu(req->CtlCode), le32_to_cpu(req->InputCount), req->VolatileFileId, @@ -7742,8 +8282,7 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } - ret = fsctl_set_sparse(work, id, - (struct file_sparse *)&req->Buffer[0]); + ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer); if (ret < 0) goto out; break; @@ -7766,7 +8305,7 @@ int smb2_ioctl(struct ksmbd_work *work) } zero_data = - (struct file_zero_data_information *)&req->Buffer[0]; + (struct file_zero_data_information *)buffer; off = le64_to_cpu(zero_data->FileOffset); bfz = le64_to_cpu(zero_data->BeyondFinalZero); @@ -7797,7 +8336,7 @@ int smb2_ioctl(struct ksmbd_work *work) } ret = fsctl_query_allocated_ranges(work, id, - (struct file_allocated_range_buffer *)&req->Buffer[0], + (struct file_allocated_range_buffer *)buffer, (struct file_allocated_range_buffer *)&rsp->Buffer[0], out_buf_len / sizeof(struct file_allocated_range_buffer), &nbytes); @@ -7841,7 +8380,7 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } - dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + dup_ext = (struct duplicate_extents_to_file *)buffer; fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, dup_ext->PersistentFileHandle); @@ -7910,9 +8449,9 @@ dup_ext_out: rsp->Reserved = cpu_to_le16(0); rsp->Flags = cpu_to_le32(0); rsp->Reserved2 = cpu_to_le32(0); - inc_rfc1001_len(work->response_buf, 48 + nbytes); - - return 0; + ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_ioctl_rsp) + nbytes); + if (!ret) + return ret; out: if (ret == -EACCES) @@ -7925,8 +8464,10 @@ out: rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; else if (ret < 0 || rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_INVALID_PARAMETER; + +out2: smb2_set_err_rsp(work); - return 0; + return ret; } /** @@ -8036,27 +8577,22 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) goto err_out; } - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - rsp->StructureSize = cpu_to_le16(24); rsp->OplockLevel = rsp_oplevel; rsp->Reserved = 0; rsp->Reserved2 = 0; rsp->VolatileFid = volatile_id; rsp->PersistentFid = persistent_id; - inc_rfc1001_len(work->response_buf, 24); - return; - + ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); + if (ret) { err_out: + smb2_set_err_rsp(work); + } + opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); - opinfo_put(opinfo); ksmbd_fd_put(work, fp); - smb2_set_err_rsp(work); } static int check_lease_state(struct lease *lease, __le32 req_state) @@ -8180,48 +8716,45 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) le32_to_cpu(req->LeaseState)); } - lease_state = lease->state; - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - atomic_dec(&opinfo->breaking_cnt); - wake_up_interruptible_all(&opinfo->oplock_brk); - opinfo_put(opinfo); - if (ret < 0) { rsp->hdr.Status = err; goto err_out; } + lease_state = lease->state; + rsp->StructureSize = cpu_to_le16(36); rsp->Reserved = 0; rsp->Flags = 0; memcpy(rsp->LeaseKey, req->LeaseKey, 16); rsp->LeaseState = lease_state; rsp->LeaseDuration = 0; - inc_rfc1001_len(work->response_buf, 36); - return; - + ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); + if (ret) { err_out: + smb2_set_err_rsp(work); + } + opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); atomic_dec(&opinfo->breaking_cnt); wake_up_interruptible_all(&opinfo->oplock_brk); - opinfo_put(opinfo); - smb2_set_err_rsp(work); } /** * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break * @work: smb work containing oplock/lease break command buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_oplock_break(struct ksmbd_work *work) { struct smb2_oplock_break *req; struct smb2_oplock_break *rsp; + ksmbd_debug(SMB, "Received smb2 oplock break acknowledgment request\n"); + WORK_BUFFERS(work, req, rsp); switch (le16_to_cpu(req->StructureSize)) { @@ -8236,6 +8769,7 @@ int smb2_oplock_break(struct ksmbd_work *work) le16_to_cpu(req->StructureSize)); rsp->hdr.Status = STATUS_INVALID_PARAMETER; smb2_set_err_rsp(work); + return -EINVAL; } return 0; @@ -8245,24 +8779,26 @@ int smb2_oplock_break(struct ksmbd_work *work) * smb2_notify() - handler for smb2 notify request * @work: smb work containing notify command buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_notify(struct ksmbd_work *work) { struct smb2_change_notify_req *req; struct smb2_change_notify_rsp *rsp; + ksmbd_debug(SMB, "Received smb2 notify\n"); + WORK_BUFFERS(work, req, rsp); if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { rsp->hdr.Status = STATUS_INTERNAL_ERROR; smb2_set_err_rsp(work); - return 0; + return -EIO; } smb2_set_err_rsp(work); rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; - return 0; + return -EOPNOTSUPP; } /** @@ -8317,9 +8853,8 @@ int smb2_check_sign_req(struct ksmbd_work *work) iov[0].iov_base = (char *)&hdr->ProtocolId; iov[0].iov_len = len; - if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, - signature)) - return 0; + ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature); if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { pr_err("bad smb2 signature\n"); @@ -8337,48 +8872,24 @@ int smb2_check_sign_req(struct ksmbd_work *work) void smb2_set_sign_rsp(struct ksmbd_work *work) { struct smb2_hdr *hdr; - struct smb2_hdr *req_hdr; char signature[SMB2_HMACSHA256_SIZE]; - struct kvec iov[2]; - size_t len; + struct kvec *iov; int n_vec = 1; - hdr = smb2_get_msg(work->response_buf); - if (work->next_smb2_rsp_hdr_off) - hdr = ksmbd_resp_buf_next(work); - - req_hdr = ksmbd_req_buf_next(work); - - if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(work->response_buf); - if (req_hdr->NextCommand) - len = ALIGN(len, 8); - } else { - len = get_rfc1002_len(work->response_buf) - - work->next_smb2_rsp_hdr_off; - len = ALIGN(len, 8); - } - - if (req_hdr->NextCommand) - hdr->NextCommand = cpu_to_le32(len); - + hdr = ksmbd_resp_buf_curr(work); hdr->Flags |= SMB2_FLAGS_SIGNED; memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (work->aux_payload_sz) { - iov[0].iov_len -= work->aux_payload_sz; - - iov[1].iov_base = work->aux_payload_buf; - iov[1].iov_len = work->aux_payload_sz; + if (hdr->Command == SMB2_READ) { + iov = &work->iov[work->iov_idx - 1]; n_vec++; + } else { + iov = &work->iov[work->iov_idx]; } - if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, - signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); + ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature); + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); } /** @@ -8449,29 +8960,14 @@ int smb3_check_sign_req(struct ksmbd_work *work) void smb3_set_sign_rsp(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *req_hdr, *hdr; + struct smb2_hdr *hdr; struct channel *chann; char signature[SMB2_CMACAES_SIZE]; - struct kvec iov[2]; + struct kvec *iov; int n_vec = 1; - size_t len; char *signing_key; - hdr = smb2_get_msg(work->response_buf); - if (work->next_smb2_rsp_hdr_off) - hdr = ksmbd_resp_buf_next(work); - - req_hdr = ksmbd_req_buf_next(work); - - if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(work->response_buf); - if (req_hdr->NextCommand) - len = ALIGN(len, 8); - } else { - len = get_rfc1002_len(work->response_buf) - - work->next_smb2_rsp_hdr_off; - len = ALIGN(len, 8); - } + hdr = ksmbd_resp_buf_curr(work); if (conn->binding == false && le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { @@ -8487,21 +8983,18 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) if (!signing_key) return; - if (req_hdr->NextCommand) - hdr->NextCommand = cpu_to_le32(len); - hdr->Flags |= SMB2_FLAGS_SIGNED; memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - if (work->aux_payload_sz) { - iov[0].iov_len -= work->aux_payload_sz; - iov[1].iov_base = work->aux_payload_buf; - iov[1].iov_len = work->aux_payload_sz; + + if (hdr->Command == SMB2_READ) { + iov = &work->iov[work->iov_idx - 1]; n_vec++; + } else { + iov = &work->iov[work->iov_idx]; } - if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) + if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, + signature)) memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); } @@ -8568,45 +9061,22 @@ static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) int smb3_encrypt_resp(struct ksmbd_work *work) { - char *buf = work->response_buf; - struct kvec iov[3]; + struct kvec *iov = work->iov; int rc = -ENOMEM; - int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); - - if (ARRAY_SIZE(iov) < rq_nvec) - return -ENOMEM; + void *tr_buf; - work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); - if (!work->tr_buf) + tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, KSMBD_DEFAULT_GFP); + if (!tr_buf) return rc; /* fill transform header */ - fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); + fill_transform_hdr(tr_buf, work->response_buf, work->conn->cipher_type); - iov[0].iov_base = work->tr_buf; + iov[0].iov_base = tr_buf; iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; - buf_size += iov[0].iov_len - 4; - - iov[1].iov_base = buf + 4; - iov[1].iov_len = get_rfc1002_len(buf); - if (work->aux_payload_sz) { - iov[1].iov_len = work->resp_hdr_sz - 4; - - iov[2].iov_base = work->aux_payload_buf; - iov[2].iov_len = work->aux_payload_sz; - buf_size += iov[2].iov_len; - } - buf_size += iov[1].iov_len; - work->resp_hdr_sz = iov[1].iov_len; + work->tr_buf = tr_buf; - rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); - if (rc) - return rc; - - memmove(buf, iov[1].iov_base, iov[1].iov_len); - *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); - - return rc; + return ksmbd_crypt_message(work, iov, work->iov_idx + 1, 1); } bool smb3_is_transform_hdr(void *buf) @@ -8644,6 +9114,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) le64_to_cpu(tr_hdr->SessionId)); return -ECONNABORTED; } + ksmbd_user_session_put(sess); iov[0].iov_base = buf; iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index 2767c08a534a..66cdc8e4a648 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -63,34 +63,19 @@ struct preauth_integrity_info { #define SMB2_SESSION_TIMEOUT (10 * HZ) -struct create_durable_req_v2 { - struct create_context ccontext; - __u8 Name[8]; - __le32 Timeout; - __le32 Flags; - __u8 Reserved[8]; - __u8 CreateGuid[16]; -} __packed; +/* Apple Defined Contexts */ +#define SMB2_CREATE_AAPL "AAPL" -struct create_durable_reconn_v2_req { - struct create_context ccontext; - __u8 Name[8]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - __u8 CreateGuid[16]; - __le32 Flags; -} __packed; +#define DURABLE_HANDLE_MAX_TIMEOUT 300000 struct create_alloc_size_req { - struct create_context ccontext; + struct create_context_hdr ccontext; __u8 Name[8]; __le64 AllocationSize; } __packed; struct create_durable_rsp { - struct create_context ccontext; + struct create_context_hdr ccontext; __u8 Name[8]; union { __u8 Reserved[8]; @@ -98,16 +83,9 @@ struct create_durable_rsp { } Data; } __packed; -struct create_durable_v2_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le32 Timeout; - __le32 Flags; -} __packed; - /* equivalent of the contents of SMB3.1.1 POSIX open context response */ struct create_posix_rsp { - struct create_context ccontext; + struct create_context_hdr ccontext; __u8 Name[16]; __le32 nlink; __le32 reparse_tag; @@ -116,30 +94,8 @@ struct create_posix_rsp { u8 SidBuffer[44]; } __packed; -struct smb2_buffer_desc_v1 { - __le64 offset; - __le32 token; - __le32 length; -} __packed; - #define SMB2_0_IOCTL_IS_FSCTL 0x00000001 -struct smb_sockaddr_in { - __be16 Port; - __be32 IPv4address; - __u8 Reserved[8]; -} __packed; - -struct smb_sockaddr_in6 { - __be16 Port; - __be32 FlowInfo; - __u8 IPv6address[16]; - __be32 ScopeId; -} __packed; - -#define INTERNETWORK 0x0002 -#define INTERNETWORKV6 0x0017 - struct sockaddr_storage_rsp { __le16 Family; union { @@ -148,18 +104,6 @@ struct sockaddr_storage_rsp { }; } __packed; -#define RSS_CAPABLE 0x00000001 -#define RDMA_CAPABLE 0x00000002 - -struct network_interface_info_ioctl_rsp { - __le32 Next; /* next interface. zero if this is last one */ - __le32 IfIndex; - __le32 Capability; /* RSS or RDMA Capable */ - __le32 Reserved; - __le64 LinkSpeed; - char SockAddr_Storage[128]; -} __packed; - struct file_object_buf_type1_ioctl_rsp { __u8 ObjectId[16]; __u8 BirthVolumeId[16]; @@ -167,32 +111,6 @@ struct file_object_buf_type1_ioctl_rsp { __u8 DomainId[16]; } __packed; -struct resume_key_ioctl_rsp { - __u64 ResumeKey[3]; - __le32 ContextLength; - __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ -} __packed; - -struct copychunk_ioctl_req { - __le64 ResumeKey[3]; - __le32 ChunkCount; - __le32 Reserved; - __u8 Chunks[1]; /* array of srv_copychunk */ -} __packed; - -struct srv_copychunk { - __le64 SourceOffset; - __le64 TargetOffset; - __le32 Length; - __le32 Reserved; -} __packed; - -struct copychunk_ioctl_rsp { - __le32 ChunksWritten; - __le32 ChunkBytesWritten; - __le32 TotalBytesWritten; -} __packed; - struct file_sparse { __u8 SetSparse; } __packed; @@ -287,17 +205,6 @@ struct smb2_file_stream_info { char StreamName[]; } __packed; -struct smb2_file_ntwrk_info { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndOfFile; - __le32 Attributes; - __le32 Reserved; -} __packed; - struct smb2_file_standard_info { __le64 AllocationSize; __le64 EndOfFile; @@ -353,7 +260,7 @@ struct smb2_file_attr_tag_info { struct smb2_ea_info_req { __le32 NextEntryOffset; __u8 EaNameLength; - char name[1]; + char name[]; } __packed; /* level 15 Query */ struct smb2_ea_info { @@ -361,18 +268,18 @@ struct smb2_ea_info { __u8 Flags; __u8 EaNameLength; __le16 EaValueLength; - char name[1]; + char name[]; /* optionally followed by value */ } __packed; /* level 15 Query */ struct create_ea_buf_req { - struct create_context ccontext; + struct create_context_hdr ccontext; __u8 Name[8]; struct smb2_ea_info ea; } __packed; struct create_sd_buf_req { - struct create_context ccontext; + struct create_context_hdr ccontext; __u8 Name[8]; struct smb_ntsd ntsd; } __packed; @@ -485,4 +392,14 @@ static inline void *smb2_get_msg(void *buf) return buf + 4; } +#define POSIX_TYPE_FILE 0 +#define POSIX_TYPE_DIR 1 +#define POSIX_TYPE_SYMLINK 2 +#define POSIX_TYPE_CHARDEV 3 +#define POSIX_TYPE_BLKDEV 4 +#define POSIX_TYPE_FIFO 5 +#define POSIX_TYPE_SOCKET 6 + +#define POSIX_FILETYPE_SHIFT 12 + #endif /* _SMB2PDU_H */ diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index c2b75d898852..b23203a1c286 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -9,7 +9,7 @@ #include "smb_common.h" #include "server.h" #include "misc.h" -#include "smbstatus.h" +#include "../common/smb2status.h" #include "connection.h" #include "ksmbd_work.h" #include "mgmt/user_session.h" @@ -18,8 +18,8 @@ #include "mgmt/share_config.h" /*for shortname implementation */ -static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; -#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) +static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (strlen(basechars) - 1) #define MAGIC_CHAR '~' #define PERIOD '.' #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) @@ -158,8 +158,12 @@ int ksmbd_verify_smb_message(struct ksmbd_work *work) */ bool ksmbd_smb_request(struct ksmbd_conn *conn) { - __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); + __le32 *proto; + if (conn->request_buf[0] != 0) + return false; + + proto = (__le32 *)smb2_get_msg(conn->request_buf); if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { pr_err_ratelimited("smb2 compression not support yet"); return false; @@ -319,12 +323,6 @@ static int init_smb1_rsp_hdr(struct ksmbd_work *work) struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; - /* - * Remove 4 byte direct TCP header. - */ - *(__be32 *)work->response_buf = - cpu_to_be32(sizeof(struct smb_hdr) - 4); - rsp_hdr->Command = SMB_COM_NEGOTIATE; *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; rsp_hdr->Flags = SMBFLG_RESPONSE; @@ -360,7 +358,7 @@ static int smb1_check_user_session(struct ksmbd_work *work) static int smb1_allocate_rsp_buf(struct ksmbd_work *work) { work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; if (!work->response_buf) { @@ -372,11 +370,26 @@ static int smb1_allocate_rsp_buf(struct ksmbd_work *work) return 0; } +/** + * set_smb1_rsp_status() - set error type in smb response header + * @work: smb work containing smb response header + * @err: error code to set in response + */ +static void set_smb1_rsp_status(struct ksmbd_work *work, __le32 err) +{ + work->send_no_response = 1; +} + static struct smb_version_ops smb1_server_ops = { .get_cmd_val = get_smb1_cmd_val, .init_rsp_hdr = init_smb1_rsp_hdr, .allocate_rsp_buf = smb1_allocate_rsp_buf, .check_user_session = smb1_check_user_session, + .set_rsp_status = set_smb1_rsp_status, +}; + +static struct smb_version_values smb1_server_values = { + .max_credits = SMB2_MAX_CREDITS, }; static int smb1_negotiate(struct ksmbd_work *work) @@ -390,18 +403,18 @@ static struct smb_version_cmds smb1_server_cmds[1] = { static int init_smb1_server(struct ksmbd_conn *conn) { + conn->vals = &smb1_server_values; conn->ops = &smb1_server_ops; conn->cmds = smb1_server_cmds; conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); return 0; } -int ksmbd_init_smb_server(struct ksmbd_work *work) +int ksmbd_init_smb_server(struct ksmbd_conn *conn) { - struct ksmbd_conn *conn = work->conn; __le32 proto; - proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol; + proto = *(__le32 *)((struct smb_hdr *)conn->request_buf)->Protocol; if (conn->need_neg == false) { if (proto == SMB1_PROTO_NUMBER) return -EINVAL; @@ -448,10 +461,13 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, } ksmbd_kstat.kstat = &kstat; - ksmbd_vfs_fill_dentry_attrs(work, - idmap, - dentry, - &ksmbd_kstat); + rc = ksmbd_vfs_fill_dentry_attrs(work, + idmap, + dentry, + &ksmbd_kstat); + if (rc) + break; + rc = fn(conn, info_level, d_info, &ksmbd_kstat); if (rc) break; @@ -476,7 +492,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, * @shortname: destination short filename * * Return: shortname length or 0 when source long name is '.' or '..' - * TODO: Though this function comforms the restriction of 8.3 Filename spec, + * TODO: Though this function conforms the restriction of 8.3 Filename spec, * but the result is different with Windows 7's one. need to check. */ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, @@ -499,7 +515,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, p = strrchr(longname, '.'); if (p == longname) { /*name starts with a dot*/ - strscpy(extension, "___", strlen("___")); + strscpy(extension, "___", sizeof(extension)); } else { if (p) { p++; @@ -560,10 +576,11 @@ static int smb_handle_negotiate(struct ksmbd_work *work) ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); - /* Add 2 byte bcc and 2 byte DialectIndex. */ - inc_rfc1001_len(work->response_buf, 4); - neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp + 4, + sizeof(struct smb_negotiate_rsp) - 4)) + return -ENOMEM; + neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; neg_rsp->hdr.WordCount = 1; neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); neg_rsp->ByteCount = 0; @@ -633,7 +650,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) * Lookup fp in master fp list, and check desired access and * shared mode between previous open and current open. */ - read_lock(&curr_fp->f_ci->m_lock); + down_read(&curr_fp->f_ci->m_lock); list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { if (file_inode(filp) != file_inode(prev_fp->filp)) continue; @@ -709,7 +726,7 @@ int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) break; } } - read_unlock(&curr_fp->f_ci->m_lock); + up_read(&curr_fp->f_ci->m_lock); return rc; } @@ -719,17 +736,19 @@ bool is_asterisk(char *p) return p && p[0] == '*'; } -int ksmbd_override_fsids(struct ksmbd_work *work) +int __ksmbd_override_fsids(struct ksmbd_work *work, + struct ksmbd_share_config *share) { struct ksmbd_session *sess = work->sess; - struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_user *user = sess->user; struct cred *cred; struct group_info *gi; unsigned int uid; unsigned int gid; + int i; - uid = user_uid(sess->user); - gid = user_gid(sess->user); + uid = user_uid(user); + gid = user_gid(user); if (share->force_uid != KSMBD_SHARE_INVALID_UID) uid = share->force_uid; if (share->force_gid != KSMBD_SHARE_INVALID_GID) @@ -742,11 +761,18 @@ int ksmbd_override_fsids(struct ksmbd_work *work) cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, gid); - gi = groups_alloc(0); + gi = groups_alloc(user->ngroups); if (!gi) { abort_creds(cred); return -ENOMEM; } + + for (i = 0; i < user->ngroups; i++) + gi->gid[i] = make_kgid(&init_user_ns, user->sgid[i]); + + if (user->ngroups) + groups_sort(gi); + set_groups(cred, gi); put_group_info(gi); @@ -755,23 +781,22 @@ int ksmbd_override_fsids(struct ksmbd_work *work) WARN_ON(work->saved_cred); work->saved_cred = override_creds(cred); - if (!work->saved_cred) { - abort_creds(cred); - return -EINVAL; - } return 0; } +int ksmbd_override_fsids(struct ksmbd_work *work) +{ + return __ksmbd_override_fsids(work, work->tcon->share_conf); +} + void ksmbd_revert_fsids(struct ksmbd_work *work) { const struct cred *cred; - WARN_ON(!work->saved_cred); - cred = current_cred(); - revert_creds(work->saved_cred); - put_cred(cred); + cred = revert_creds(work->saved_cred); work->saved_cred = NULL; + put_cred(cred); } __le32 smb_map_generic_desired_access(__le32 daccess) diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index f1092519c0c2..2baf4aa330eb 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -10,7 +10,9 @@ #include "glob.h" #include "nterr.h" +#include "../common/smbglob.h" #include "../common/smb2pdu.h" +#include "../common/fscc.h" #include "smb2pdu.h" /* ksmbd's Specific ERRNO */ @@ -26,18 +28,8 @@ #define SMB311_PROT 6 #define BAD_PROT 0xFFFF -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0" -#define SMB21_VERSION_STRING "2.1" -#define SMB30_VERSION_STRING "3.0" -#define SMB302_VERSION_STRING "3.02" -#define SMB311_VERSION_STRING "3.1.1" - #define SMB_ECHO_INTERVAL (60 * HZ) -#define CIFS_DEFAULT_IOSIZE (64 * 1024) -#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ - #define MAX_STREAM_PROT_LEN 0x00FFFFFF /* Responses when opening a file. */ @@ -46,78 +38,7 @@ #define F_CREATED 2 #define F_OVERWRITTEN 3 -/* - * File Attribute flags - */ -#define ATTR_POSIX_SEMANTICS 0x01000000 -#define ATTR_BACKUP_SEMANTICS 0x02000000 -#define ATTR_DELETE_ON_CLOSE 0x04000000 -#define ATTR_SEQUENTIAL_SCAN 0x08000000 -#define ATTR_RANDOM_ACCESS 0x10000000 -#define ATTR_NO_BUFFERING 0x20000000 -#define ATTR_WRITE_THROUGH 0x80000000 - -/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ -#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ -#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ -#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 -#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 -#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 -#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 -#define FILE_SUPPORTS_HARD_LINKS 0x00400000 -#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 -#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 -#define FILE_READ_ONLY_VOLUME 0x00080000 -#define FILE_NAMED_STREAMS 0x00040000 -#define FILE_SUPPORTS_ENCRYPTION 0x00020000 -#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 -#define FILE_VOLUME_IS_COMPRESSED 0x00008000 -#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 -#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 -#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 -#define FILE_VOLUME_QUOTAS 0x00000020 -#define FILE_FILE_COMPRESSION 0x00000010 -#define FILE_PERSISTENT_ACLS 0x00000008 -#define FILE_UNICODE_ON_DISK 0x00000004 -#define FILE_CASE_PRESERVED_NAMES 0x00000002 -#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 - -#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ -#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ -#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ -#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ -/* with the file can be read */ -#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ -/* with the file can be written */ -#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ -/* the file using system paging I/O */ -#define FILE_DELETE_CHILD 0x00000040 -#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ -/* file can be read */ -#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ -/* file can be written */ -#define DELETE 0x00010000 /* The file can be deleted */ -#define READ_CONTROL 0x00020000 /* The access control list and */ -/* ownership associated with the */ -/* file can be read */ -#define WRITE_DAC 0x00040000 /* The access control list and */ -/* ownership associated with the */ -/* file can be written. */ -#define WRITE_OWNER 0x00080000 /* Ownership information associated */ -/* with the file can be written */ -#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ -/* synchronize with the completion */ -/* of an input/output request */ -#define GENERIC_ALL 0x10000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_READ 0x80000000 -/* In summary - Relevant file */ -/* access flags from CIFS are */ -/* file_read_data, file_write_data */ -/* file_execute, file_read_attributes*/ -/* write_dac, and delete. */ - +/* Combinations of file access permission bits */ #define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ | FILE_READ_ATTRIBUTES \ | DELETE | READ_CONTROL | WRITE_DAC \ @@ -128,14 +49,6 @@ | FILE_WRITE_ATTRIBUTES \ | DELETE | READ_CONTROL | WRITE_DAC \ | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) - -#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ - | READ_CONTROL | SYNCHRONIZE) /* generic flags for file open */ #define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ @@ -156,86 +69,35 @@ FILE_EXECUTE | FILE_DELETE_CHILD | \ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) -#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) -#define SMB_COM_NEGOTIATE 0x72 -#define SMB1_CLIENT_GUID_SIZE (16) +#define SMB_COM_NEGOTIATE 0x72 /* See MS-CIFS 2.2.2.1 */ +/* See MS-CIFS 2.2.3.1 */ #define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ +/* + * See MS-CIFS 2.2.3.1 + * MS-SMB 2.2.3.1 + */ #define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) #define SMBFLG2_EXT_SEC cpu_to_le16(0x800) #define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) #define SMBFLG2_UNICODE cpu_to_le16(0x8000) -struct smb_hdr { - __be32 smb_buf_length; - __u8 Protocol[4]; - __u8 Command; - union { - struct { - __u8 ErrorClass; - __u8 Reserved; - __le16 Error; - } __packed DosError; - __le32 CifsError; - } __packed Status; - __u8 Flags; - __le16 Flags2; /* note: le */ - __le16 PidHigh; - union { - struct { - __le32 SequenceNumber; /* le */ - __u32 Reserved; /* zero */ - } __packed Sequence; - __u8 SecuritySignature[8]; /* le */ - } __packed Signature; - __u8 pad[2]; - __le16 Tid; - __le16 Pid; - __le16 Uid; - __le16 Mid; - __u8 WordCount; -} __packed; - -struct smb_negotiate_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - unsigned char DialectsArray[]; -} __packed; - +/* See MS-CIFS 2.2.4.52.2 */ struct smb_negotiate_rsp { struct smb_hdr hdr; /* wct = 17 */ __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ __le16 ByteCount; } __packed; -struct filesystem_attribute_info { - __le32 Attributes; - __le32 MaxPathNameComponentLength; - __le32 FileSystemNameLen; - __le16 FileSystemName[1]; /* do not have to save this - get subset? */ -} __packed; - -struct filesystem_device_info { - __le32 DeviceType; - __le32 DeviceCharacteristics; -} __packed; /* device info level 0x104 */ - struct filesystem_vol_info { __le64 VolumeCreationTime; __le32 SerialNumber; __le32 VolumeLabelSize; __le16 Reserved; - __le16 VolumeLabel[1]; + __le16 VolumeLabel[]; } __packed; -struct filesystem_info { - __le64 TotalAllocationUnits; - __le64 FreeAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __packed; /* size info, level 0x103 */ - #define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ #define STRING_LENGTH 28 @@ -252,20 +114,6 @@ struct object_id_info { struct fs_extended_info extended_info; } __packed; -struct file_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - char FileName[]; -} __packed; /* level 0x101 FF resp data */ - struct file_names_info { __le32 NextEntryOffset; __u32 FileIndex; @@ -273,39 +121,6 @@ struct file_names_info { char FileName[]; } __packed; /* level 0xc FF resp data */ -struct file_full_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; - char FileName[]; -} __packed; /* level 0x102 FF resp */ - -struct file_both_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - char FileName[]; -} __packed; /* level 0x104 FFrsp data */ - struct file_id_both_directory_info { __le32 NextEntryOffset; __u32 FileIndex; @@ -326,75 +141,6 @@ struct file_id_both_directory_info { char FileName[]; } __packed; -struct file_id_full_dir_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 Reserved; - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ - char FileName[]; -} __packed; /* level 0x105 FF rsp data */ - -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __le16 lock_cmd; - __u32 capabilities; - __u32 max_read_size; - __u32 max_write_size; - __u32 max_trans_size; - __u32 max_credits; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; - size_t create_durable_size; - size_t create_durable_v2_size; - size_t create_mxac_size; - size_t create_disk_id_size; - size_t create_posix_size; -}; - -struct filesystem_posix_info { - /* For undefined recommended transfer size return -1 in that field */ - __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ - __le32 BlockSize; - /* The next three fields are in terms of the block size. - * (above). If block size is unknown, 4096 would be a - * reasonable block size for a server to report. - * Note that returning the blocks/blocksavail removes need - * to make a second call (to QFSInfo level 0x103 to get this info. - * UserBlockAvail is typically less than or equal to BlocksAvail, - * if no distinction is made return the same value in each - */ - __le64 TotalBlocks; - __le64 BlocksAvail; /* bfree */ - __le64 UserBlocksAvail; /* bavail */ - /* For undefined Node fields or FSID return -1 */ - __le64 TotalFileNodes; - __le64 FreeFileNodes; - __le64 FileSysIdentifier; /* fsid */ - /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ - /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ -} __packed; - struct smb_version_ops { u16 (*get_cmd_val)(struct ksmbd_work *swork); int (*init_rsp_hdr)(struct ksmbd_work *swork); @@ -407,7 +153,7 @@ struct smb_version_ops { int (*check_sign_req)(struct ksmbd_work *work); void (*set_sign_rsp)(struct ksmbd_work *work); int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); - int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); + void (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); bool (*is_transform_hdr)(void *buf); int (*decrypt_req)(struct ksmbd_work *work); int (*encrypt_resp)(struct ksmbd_work *work); @@ -427,7 +173,7 @@ bool ksmbd_smb_request(struct ksmbd_conn *conn); int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); -int ksmbd_init_smb_server(struct ksmbd_work *work); +int ksmbd_init_smb_server(struct ksmbd_conn *conn); struct ksmbd_kstat; int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, @@ -447,6 +193,8 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); +int __ksmbd_override_fsids(struct ksmbd_work *work, + struct ksmbd_share_config *share); int ksmbd_override_fsids(struct ksmbd_work *work); void ksmbd_revert_fsids(struct ksmbd_work *work); @@ -455,14 +203,4 @@ unsigned int ksmbd_server_side_copy_max_chunk_size(void); unsigned int ksmbd_server_side_copy_max_total_size(void); bool is_asterisk(char *p); __le32 smb_map_generic_desired_access(__le32 daccess); - -static inline unsigned int get_rfc1002_len(void *buf) -{ - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; -} - -static inline void inc_rfc1001_len(void *buf, int count) -{ - be32_add_cpu((__be32 *)buf, count); -} #endif /* __SMB_COMMON_H__ */ diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index e5e438bf5499..5aa7a66334d9 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -270,6 +270,11 @@ static int sid_to_id(struct mnt_idmap *idmap, return -EIO; } + if (psid->num_subauth == 0) { + pr_err("%s: zero subauthorities!\n", __func__); + return -EIO; + } + if (sidtype == SIDOWNER) { kuid_t uid; uid_t id; @@ -333,7 +338,7 @@ void posix_state_to_acl(struct posix_acl_state *state, pace->e_perm = state->other.allow; } -int init_acl_state(struct posix_acl_state *state, int cnt) +int init_acl_state(struct posix_acl_state *state, u16 cnt) { int alloc; @@ -345,10 +350,10 @@ int init_acl_state(struct posix_acl_state *state, int cnt) */ alloc = sizeof(struct posix_ace_state_array) + cnt * sizeof(struct posix_user_ace_state); - state->users = kzalloc(alloc, GFP_KERNEL); + state->users = kzalloc(alloc, KSMBD_DEFAULT_GFP); if (!state->users) return -ENOMEM; - state->groups = kzalloc(alloc, GFP_KERNEL); + state->groups = kzalloc(alloc, KSMBD_DEFAULT_GFP); if (!state->groups) { kfree(state->users); return -ENOMEM; @@ -368,7 +373,7 @@ static void parse_dacl(struct mnt_idmap *idmap, struct smb_fattr *fattr) { int i, ret; - int num_aces = 0; + u16 num_aces = 0; unsigned int acl_size; char *acl_base; struct smb_ace **ppace; @@ -389,20 +394,18 @@ static void parse_dacl(struct mnt_idmap *idmap, ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), - le32_to_cpu(pdacl->num_aces)); + le16_to_cpu(pdacl->num_aces)); acl_base = (char *)pdacl; acl_size = sizeof(struct smb_acl); - num_aces = le32_to_cpu(pdacl->num_aces); + num_aces = le16_to_cpu(pdacl->num_aces); if (num_aces <= 0) return; - if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) - return; - - ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); - if (!ppace) + if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / + (offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) return; ret = init_acl_state(&acl_state, num_aces); @@ -414,6 +417,13 @@ static void parse_dacl(struct mnt_idmap *idmap, return; } + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), KSMBD_DEFAULT_GFP); + if (!ppace) { + free_acl_state(&default_acl_state); + free_acl_state(&acl_state); + return; + } + /* * reset rwx permissions for user/group/other. * Also, if num_aces is 0 i.e. DACL has no ACEs, @@ -429,6 +439,7 @@ static void parse_dacl(struct mnt_idmap *idmap, offsetof(struct smb_sid, sub_auth); if (end_of_acl - acl_base < acl_size || + ppace[i]->sid.num_subauth == 0 || ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || (end_of_acl - acl_base < acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || @@ -550,7 +561,7 @@ static void parse_dacl(struct mnt_idmap *idmap, if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { fattr->cf_acls = posix_acl_alloc(acl_state.users->n + - acl_state.groups->n + 4, GFP_KERNEL); + acl_state.groups->n + 4, KSMBD_DEFAULT_GFP); if (fattr->cf_acls) { cf_pace = fattr->cf_acls->a_entries; posix_state_to_acl(&acl_state, cf_pace); @@ -564,7 +575,7 @@ static void parse_dacl(struct mnt_idmap *idmap, if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { fattr->cf_dacls = posix_acl_alloc(default_acl_state.users->n + - default_acl_state.groups->n + 4, GFP_KERNEL); + default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP); if (fattr->cf_dacls) { cf_pdace = fattr->cf_dacls->a_entries; posix_state_to_acl(&default_acl_state, cf_pdace); @@ -577,7 +588,7 @@ static void parse_dacl(struct mnt_idmap *idmap, static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, struct smb_ace *pndace, - struct smb_fattr *fattr, u32 *num_aces, + struct smb_fattr *fattr, u16 *num_aces, u16 *size, u32 nt_aces_num) { struct posix_acl_entry *pace; @@ -592,7 +603,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { int flags = 0; - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + sid = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!sid) break; @@ -659,7 +670,7 @@ posix_default_acl: pace = fattr->cf_dacls->a_entries; for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + sid = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!sid) break; @@ -698,7 +709,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, struct smb_fattr *fattr) { struct smb_ace *ntace, *pndace; - int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0; unsigned short size = 0; int i; @@ -725,7 +736,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, set_posix_acl_entries_dacl(idmap, pndace, fattr, &num_aces, &size, nt_num_aces); - pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->num_aces = cpu_to_le16(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } @@ -733,7 +744,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap, struct smb_acl *pndacl, struct smb_fattr *fattr) { struct smb_ace *pace, *pndace; - u32 num_aces = 0; + u16 num_aces = 0; u16 size = 0, ace_size = 0; uid_t uid; const struct smb_sid *sid; @@ -789,7 +800,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap, fattr->cf_mode, 0007); out: - pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->num_aces = cpu_to_le16(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } @@ -804,6 +815,13 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl) return -EINVAL; } + if (!psid->num_subauth) + return 0; + + if (psid->num_subauth > SID_MAX_SUB_AUTHORITIES || + end_of_acl < (char *)psid + 8 + sizeof(__le32) * psid->num_subauth) + return -EINVAL; + return 0; } @@ -845,6 +863,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, pntsd->type = cpu_to_le16(DACL_PRESENT); if (pntsd->osidoffset) { + if (le32_to_cpu(pntsd->osidoffset) < sizeof(struct smb_ntsd)) + return -EINVAL; + rc = parse_sid(owner_sid_ptr, end_of_acl); if (rc) { pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); @@ -860,6 +881,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, } if (pntsd->gsidoffset) { + if (le32_to_cpu(pntsd->gsidoffset) < sizeof(struct smb_ntsd)) + return -EINVAL; + rc = parse_sid(group_sid_ptr, end_of_acl); if (rc) { pr_err("%s: Error %d mapping Owner SID to gid\n", @@ -881,6 +905,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, pntsd->type |= cpu_to_le16(DACL_PROTECTED); if (dacloffset) { + if (dacloffset < sizeof(struct smb_ntsd)) + return -EINVAL; + parse_dacl(idmap, dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, fattr); } @@ -903,7 +930,7 @@ int build_sec_desc(struct mnt_idmap *idmap, gid_t gid; unsigned int sid_type = SIDOWNER; - nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!nowner_sid_ptr) return -ENOMEM; @@ -912,7 +939,7 @@ int build_sec_desc(struct mnt_idmap *idmap, sid_type = SIDUNIX_USER; id_to_sid(uid, sid_type, nowner_sid_ptr); - ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP); if (!ngroup_sid_ptr) { kfree(nowner_sid_ptr); return -ENOMEM; @@ -1003,8 +1030,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct smb_sid owner_sid, group_sid; struct dentry *parent = path->dentry->d_parent; struct mnt_idmap *idmap = mnt_idmap(path->mnt); - int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; - int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; + int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size; + int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size; + unsigned int dacloffset; + size_t dacl_struct_end; + u16 num_aces, ace_cnt = 0; char *aces_base; bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); @@ -1012,15 +1042,18 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, parent, &parent_pntsd); if (pntsd_size <= 0) return -ENOENT; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); - if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { + if (!dacloffset || + check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) || + dacl_struct_end > (size_t)pntsd_size) { rc = -EINVAL; goto free_parent_pntsd; } parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); acl_len = pntsd_size - dacloffset; - num_aces = le32_to_cpu(parent_pdacl->num_aces); + num_aces = le16_to_cpu(parent_pdacl->num_aces); pntsd_type = le16_to_cpu(parent_pntsd->type); pdacl_size = le16_to_cpu(parent_pdacl->size); @@ -1029,7 +1062,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, goto free_parent_pntsd; } - aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, + KSMBD_DEFAULT_GFP); if (!aces_base) { rc = -ENOMEM; goto free_parent_pntsd; @@ -1107,6 +1141,7 @@ pass: struct smb_acl *pdacl; struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + int pntsd_alloc_size; if (parent_pntsd->osidoffset) { powner_sid = (struct smb_sid *)((char *)parent_pntsd + @@ -1119,9 +1154,10 @@ pass: pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); } - pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + - pgroup_sid_size + sizeof(struct smb_acl) + - nt_size, GFP_KERNEL); + pntsd_alloc_size = sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + nt_size; + + pntsd = kzalloc(pntsd_alloc_size, KSMBD_DEFAULT_GFP); if (!pntsd) { rc = -ENOMEM; goto free_aces_base; @@ -1136,6 +1172,27 @@ pass: pntsd->gsidoffset = parent_pntsd->gsidoffset; pntsd->dacloffset = parent_pntsd->dacloffset; + if ((u64)le32_to_cpu(pntsd->osidoffset) + powner_sid_size > + pntsd_alloc_size) { + rc = -EINVAL; + kfree(pntsd); + goto free_aces_base; + } + + if ((u64)le32_to_cpu(pntsd->gsidoffset) + pgroup_sid_size > + pntsd_alloc_size) { + rc = -EINVAL; + kfree(pntsd); + goto free_aces_base; + } + + if ((u64)le32_to_cpu(pntsd->dacloffset) + sizeof(struct smb_acl) + nt_size > + pntsd_alloc_size) { + rc = -EINVAL; + kfree(pntsd); + goto free_aces_base; + } + if (pntsd->osidoffset) { struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + le32_to_cpu(pntsd->osidoffset)); @@ -1156,13 +1213,13 @@ pass: pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); pdacl->revision = cpu_to_le16(2); pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); - pdacl->num_aces = cpu_to_le32(ace_cnt); + pdacl->num_aces = cpu_to_le16(ace_cnt); pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); memcpy(pace, aces_base, nt_size); pntsd_size += sizeof(struct smb_acl) + nt_size; } - ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, pntsd_size); + ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, pntsd_size, false); kfree(pntsd); } @@ -1193,7 +1250,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, struct smb_ntsd *pntsd = NULL; struct smb_acl *pdacl; struct posix_acl *posix_acls; - int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; + int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size; + unsigned int dacl_offset; + size_t dacl_struct_end; struct smb_sid sid; int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); struct smb_ace *ace; @@ -1212,7 +1271,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, dacl_offset = le32_to_cpu(pntsd->dacloffset); if (!dacl_offset || - (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) + check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) || + dacl_struct_end > (size_t)pntsd_size) goto err_out; pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); @@ -1237,7 +1297,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); - for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { if (offsetof(struct smb_ace, access_req) > aces_size) break; ace_size = le16_to_cpu(ace->size); @@ -1258,7 +1318,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); - for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { if (offsetof(struct smb_ace, access_req) > aces_size) break; ace_size = le16_to_cpu(ace->size); @@ -1354,7 +1414,7 @@ err_out: int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check) + bool type_check, bool get_write) { int rc; struct smb_fattr fattr = {{0}}; @@ -1414,13 +1474,13 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { /* Update WinACL in xattr */ ksmbd_vfs_remove_sd_xattrs(idmap, path); - ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, ntsd_len); + ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, ntsd_len, + get_write); } out: posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); - mark_inode_dirty(inode); return rc; } diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h index 49a8c292bd2e..355adaee39b8 100644 --- a/fs/smb/server/smbacl.h +++ b/fs/smb/server/smbacl.h @@ -8,6 +8,7 @@ #ifndef _SMBACL_H #define _SMBACL_H +#include "../common/smbacl.h" #include <linux/fs.h> #include <linux/namei.h> #include <linux/posix_acl.h> @@ -15,32 +16,6 @@ #include "mgmt/tree_connect.h" -#define NUM_AUTHS (6) /* number of authority fields */ -#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ - -/* - * ACE types - see MS-DTYP 2.4.4.1 - */ -enum { - ACCESS_ALLOWED, - ACCESS_DENIED, -}; - -/* - * Security ID types - */ -enum { - SIDOWNER = 1, - SIDGROUP, - SIDCREATOR_OWNER, - SIDCREATOR_GROUP, - SIDUNIX_USER, - SIDUNIX_GROUP, - SIDNFS_USER, - SIDNFS_GROUP, - SIDNFS_MODE, -}; - /* Revision for ACLs */ #define SD_REVISION 1 @@ -62,92 +37,8 @@ enum { #define RM_CONTROL_VALID 0x4000 #define SELF_RELATIVE 0x8000 -/* ACE types - see MS-DTYP 2.4.4.1 */ -#define ACCESS_ALLOWED_ACE_TYPE 0x00 -#define ACCESS_DENIED_ACE_TYPE 0x01 -#define SYSTEM_AUDIT_ACE_TYPE 0x02 -#define SYSTEM_ALARM_ACE_TYPE 0x03 -#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 -#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 -#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 -#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 -#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 -#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 -#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A -#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B -#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C -#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D -#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ -#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F -#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ -#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 -#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 -#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 - -/* ACE flags */ -#define OBJECT_INHERIT_ACE 0x01 -#define CONTAINER_INHERIT_ACE 0x02 -#define NO_PROPAGATE_INHERIT_ACE 0x04 -#define INHERIT_ONLY_ACE 0x08 -#define INHERITED_ACE 0x10 -#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 -#define FAILED_ACCESS_ACE_FLAG 0x80 - -/* - * Maximum size of a string representation of a SID: - * - * The fields are unsigned values in decimal. So: - * - * u8: max 3 bytes in decimal - * u32: max 10 bytes in decimal - * - * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator - * - * For authority field, max is when all 6 values are non-zero and it must be - * represented in hex. So "-0x" + 12 hex digits. - * - * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') - */ -#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) -#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ - -#define DOMAIN_USER_RID_LE cpu_to_le32(513) - struct ksmbd_conn; -struct smb_ntsd { - __le16 revision; /* revision level */ - __le16 type; - __le32 osidoffset; - __le32 gsidoffset; - __le32 sacloffset; - __le32 dacloffset; -} __packed; - -struct smb_sid { - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[NUM_AUTHS]; - __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ -} __packed; - -/* size of a struct cifs_sid, sans sub_auth array */ -#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) - -struct smb_acl { - __le16 revision; /* revision level */ - __le16 size; - __le32 num_aces; -} __packed; - -struct smb_ace { - __u8 type; - __u8 flags; - __le16 size; - __le32 access_req; - struct smb_sid sid; /* ie UUID of user or group who gets these perms */ -} __packed; - struct smb_fattr { kuid_t cf_uid; kgid_t cf_gid; @@ -195,7 +86,7 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, int build_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, __u32 *secdesclen, struct smb_fattr *fattr); -int init_acl_state(struct posix_acl_state *state, int cnt); +int init_acl_state(struct posix_acl_state *state, u16 cnt); void free_acl_state(struct posix_acl_state *state); void posix_state_to_acl(struct posix_acl_state *state, struct posix_acl_entry *pace); @@ -207,7 +98,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, __le32 *pdaccess, int uid); int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check); + bool type_check, bool get_write); void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); void ksmbd_init_domain(u32 *sub_auth); diff --git a/fs/smb/server/smbstatus.h b/fs/smb/server/smbstatus.h deleted file mode 100644 index 8963deb42404..000000000000 --- a/fs/smb/server/smbstatus.h +++ /dev/null @@ -1,1822 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * fs/server/smb2status.h - * - * SMB2 Status code (network error) definitions - * Definitions are from MS-ERREF - * - * Copyright (c) International Business Machines Corp., 2009,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -/* - * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F - * SEV C N <-------Facility--------> <------Error Status Code------> - * - * C is set if "customer defined" error, N bit is reserved and MBZ - */ - -#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) -#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) -#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) -#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) - -struct ntstatus { - /* Facility is the high 12 bits of the following field */ - __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ - __le32 Code; -}; - -#define STATUS_SUCCESS 0x00000000 -#define STATUS_WAIT_0 cpu_to_le32(0x00000000) -#define STATUS_WAIT_1 cpu_to_le32(0x00000001) -#define STATUS_WAIT_2 cpu_to_le32(0x00000002) -#define STATUS_WAIT_3 cpu_to_le32(0x00000003) -#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) -#define STATUS_ABANDONED cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) -#define STATUS_USER_APC cpu_to_le32(0x000000C0) -#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) -#define STATUS_ALERTED cpu_to_le32(0x00000101) -#define STATUS_TIMEOUT cpu_to_le32(0x00000102) -#define STATUS_PENDING cpu_to_le32(0x00000103) -#define STATUS_REPARSE cpu_to_le32(0x00000104) -#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) -#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) -#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) -#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) -#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) -#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) -#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) -#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) -#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) -#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) -#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) -#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) -#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) -#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) -#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) -#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) -#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) -#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) -#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) -#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) -#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) -#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) -#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) -#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) -#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) -#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) -#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) -#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) -#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) -#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) -#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) -#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) -#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) -#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) -#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) -#define DBG_CONTINUE cpu_to_le32(0x00010002) -#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) -#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) -#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) -#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) -#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) -#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) -#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) -#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) -#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) -#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) -#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) -#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) -#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) -#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) -#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) -#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) -#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) -#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) -#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) -#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) -#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) -#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) -#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) -#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) -#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) -#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) -#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) -#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) -#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) -#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) -#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) -#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) -#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) -#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) -#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) -#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) -#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) -#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) -#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) -#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) -#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) -#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) -#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) -#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) -#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) -#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) -#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) -#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) -#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) -#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) -#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) -#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) -#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) -#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) -#define DBG_REPLY_LATER cpu_to_le32(0x40010001) -#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) -#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) -#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) -#define DBG_CONTROL_C cpu_to_le32(0x40010005) -#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) -#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) -#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) -#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) -#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) -#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) -#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) -#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) -#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) -#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) -#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) -#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) -#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) -#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) -#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) -#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) -#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) -#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) -#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ - cpu_to_le32(0x401E0351) -#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) -#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) -#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) -#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) -#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) -#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) -#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) -#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) -#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) -#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) -#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) -#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) -#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) -#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) -#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) -#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) -#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) -#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) -#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) -#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) -#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) -#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) -#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) -#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) -#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) -#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) -#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) -#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) -#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) -#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) -#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) -#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) -#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) -#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) -#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) -#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) -#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) -#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) -#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) -#define STATUS_LONGJUMP cpu_to_le32(0x80000026) -#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) -#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) -#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) -#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) -#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) -#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) -#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) -#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) -#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) -#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) -#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) -#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) -#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) -#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) -#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) -#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) -#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) -#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) -#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) -#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) -#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ - cpu_to_le32(0x801B00EB) -#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) -#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) -#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) -#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) -#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) -#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) -#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) -#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) -#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) -#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) -#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) -#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) -#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) -#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) -#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) -#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) -#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) -#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) -#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) -#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) -#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) -#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) -#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) -#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) -#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) -#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) -#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) -#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) -#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) -#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) -#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) -#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) -#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) -#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) -#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) -#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) -#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) -#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) -#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) -#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) -#define STATUS_UNWIND cpu_to_le32(0xC0000027) -#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) -#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) -#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) -#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) -#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) -#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) -#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) -#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) -#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) -#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) -#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) -#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) -#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) -#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) -#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) -#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) -#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) -#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) -#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) -#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) -#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) -#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) -#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) -#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) -#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) -#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) -#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) -#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) -#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) -#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) -#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) -#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) -#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) -#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) -#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) -#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) -#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) -#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) -#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) -#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) -#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) -#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) -#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) -#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) -#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) -#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) -#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) -#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) -#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) -#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) -#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) -#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) -#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) -#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) -#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) -#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) -#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) -#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) -#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) -#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) -#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) -#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) -#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) -#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) -#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) -#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) -#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) -#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) -#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) -#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) -#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) -#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) -#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) -#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) -#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) -#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) -#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) -#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) -#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) -#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) -#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) -#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) -#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) -#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) -#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) -#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) -#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) -#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) -#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) -#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) -#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) -#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) -#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) -#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) -#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) -#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) -#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) -#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) -#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) -#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) -#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) -#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) -#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) -#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) -#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) -#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) -#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) -#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) -#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) -#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) -#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) -#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) -#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) -#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) -#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) -#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) -#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) -#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) -#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) -#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) -#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) -#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) -#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) -#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) -#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) -#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) -#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) -#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) -#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) -#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) -#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) -#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) -#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) -#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) -#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) -#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) -#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) -#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) -#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) -#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) -#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) -#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) -#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) -#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) -#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) -#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) -#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) -#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) -#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) -#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) -#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) -#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) -#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) -#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) -#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) -#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) -#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) -#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) -#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) -#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) -#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) -#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) -#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) -#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) -#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) -#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) -#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) -#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) -#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) -#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) -#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) -#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) -#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) -#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) -#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) -#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) -#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) -#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) -#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) -#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) -#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) -#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) -#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) -#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) -#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) -#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) -#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) -#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) -#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) -#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) -#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) -#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) -#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) -#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) -#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) -#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) -#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) -#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) -#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) -#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) -#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) -#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) -#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) -#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) -#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) -#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) -#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) -#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) -#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) -#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) -#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) -#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) -#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) -#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) -#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) -#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) -#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) -#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) -#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) -#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) -#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) -#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) -#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) -#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) -#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) -#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) -#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) -#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) -#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) -#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) -#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) -#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) -#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) -#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) -#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) -#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) -#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) -#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) -#define STATUS_NO_LDT cpu_to_le32(0xC0000117) -#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) -#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) -#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) -#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) -#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) -#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) -#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) -#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) -#define STATUS_CANCELLED cpu_to_le32(0xC0000120) -#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) -#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) -#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) -#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) -#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) -#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) -#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) -#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) -#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) -#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) -#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) -#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) -#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) -#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) -#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) -#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) -#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) -#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) -#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) -#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) -#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) -#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) -#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) -#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) -#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) -#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) -#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) -#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) -#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) -#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) -#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) -#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) -#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) -#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) -#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) -#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) -#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) -#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) -#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) -#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) -#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) -#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) -#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) -#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) -#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) -#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) -#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) -#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) -#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) -#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) -#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) -#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) -#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) -#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) -#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) -#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) -#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) -#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) -#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) -#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) -#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) -#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) -#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) -#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) -#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) -#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) -#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) -#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) -#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) -#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) -#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) -#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) -#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) -#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) -#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) -#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) -#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) -#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) -#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) -#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) -#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) -#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) -#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) -#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) -#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) -#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) -#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) -#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) -#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) -#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) -#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) -#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) -#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) -#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) -#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) -#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) -#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) -#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) -#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) -#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) -#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) -#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) -#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) -#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) -#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) -#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) -#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) -#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) -#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) -#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) -#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) -#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) -#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) -#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) -#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) -#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) -#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) -#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) -#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) -#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) -#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) -#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) -#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) -#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) -#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) -#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) -#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) -#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) -#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) -#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) -#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) -#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) -#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) -#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) -#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) -#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) -#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) -#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) -#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) -#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) -#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) -#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) -#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) -#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) -#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) -#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) -#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) -#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) -#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) -#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) -#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) -#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) -#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) -#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) -#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) -#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) -#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) -#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) -#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) -#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) -#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) -#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) -#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) -#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) -#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) -#define STATUS_RETRY cpu_to_le32(0xC000022D) -#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) -#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) -#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) -#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) -#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) -#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) -#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) -#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) -#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) -#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) -#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) -#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) -#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) -#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) -#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) -#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) -#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) -#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) -#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) -#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) -#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) -#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) -#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) -#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) -#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) -#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) -#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) -#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) -#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) -#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) -#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) -#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) -#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) -#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) -#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) -#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) -#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) -#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) -#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) -#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) -#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) -#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) -#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) -#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) -#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) -#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) -#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) -#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) -#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) -#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) -#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) -#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) -#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) -#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) -#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) -#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) -#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) -#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) -#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) -#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) -#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) -#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) -#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) -#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) -#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) -#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) -#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) -#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) -#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) -#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) -#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) -#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) -#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) -#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) -#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) -#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) -#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) -#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) -#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) -#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) -#define STATUS_NO_EFS cpu_to_le32(0xC000028E) -#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) -#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) -#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) -#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) -#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) -#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) -#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) -#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) -#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) -#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) -#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) -#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) -#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) -#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) -#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) -#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) -#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) -#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) -#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) -#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) -#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) -#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) -#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) -#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) -#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) -#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) -#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) -#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) -#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) -#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) -#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) -#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) -#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) -#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) -#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) -#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) -#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) -#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) -#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) -#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) -#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) -#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) -#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) -#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) -#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) -#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) -#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) -#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) -#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) -#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) -#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) -#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) -#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) -#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) -#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) -#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) -#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) -#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) -#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) -#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) -#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) -#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) -#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) -#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) -#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) -#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) -#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) -#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) -#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ - cpu_to_le32(0xC00002DB) -#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) -#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) -#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) -#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) -#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) -#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) -#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) -#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) -#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) -#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) -#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) -#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) -#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) -#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) -#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) -#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) -#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) -#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) -#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) -#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) -#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) -#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) -#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) -#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) -#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) -#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) -#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) -#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) -#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) -#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) -#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) -#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) -#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) -#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) -#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) -#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) -#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) -#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) -#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) -#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) -#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) -#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) -#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) -#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) -#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) -#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) -#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) -#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) -#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) -#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) -#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) -#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) -#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) -#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) -#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) -#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) -#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) -#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) -#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) -#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) -#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) -#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) -#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) -#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) -#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) -#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) -#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) -#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) -#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) -#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) -#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) -#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) -#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) -#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) -#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) -#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) -#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) -#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) -#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) -#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) -#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) -#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) -#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) -#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) -#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) -#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) -#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) -#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) -#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) -#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) -#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) -#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) -#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) -#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) -#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) -#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) -#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) -#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) -#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) -#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) -#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) -#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) -#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) -#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) -#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) -#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) -#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) -#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) -#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) -#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) -#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) -#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) -#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) -#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) -#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) -#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) -#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) -#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) -#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) -#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) -#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ - cpu_to_le32(0xC0000416) -#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) -#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) -#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) -#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) -#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) -#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) -#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) -#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) -#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) -#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) -#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) -#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) -#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) -#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) -#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) -#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) -#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) -#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) -#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) -#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) -#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) -#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) -#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) -#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) -#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) -#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) -#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) -#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) -#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) -#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) -#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) -#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) -#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) -#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) -#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) -#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) -#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) -#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) -#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) -#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) -#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) -#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) -#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) -#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) -#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) -#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070C) -#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070D) -#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070E) -#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) -#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) -#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) -#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) -#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) -#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) -#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) -#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) -#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) -#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) -#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) -#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) -#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) -#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) -#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) -#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) -#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) -#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) -#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) -#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) -#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) -#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) -#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) -#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) -#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) -#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) -#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) -#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) -#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) -#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) -#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) -#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) -#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) -#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) -#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) -#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) -#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) -#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) -#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) -#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ - cpu_to_le32(0xC000A080) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ - cpu_to_le32(0xC000A081) -#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) -#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) -#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) -#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) -#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) -#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) -#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) -#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) -#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) -#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) -#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) -#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) -#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) -#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) -#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) -#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) -#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) -#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) -#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) -#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) -#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) -#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) -#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) -#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) -#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) -#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) -#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) -#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) -#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) -#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) -#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) -#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) -#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) -#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) -#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) -#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) -#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) -#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) -#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) -#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) -#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) -#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) -#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) -#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) -#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) -#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) -#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) -#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) -#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) -#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) -#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) -#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) -#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) -#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) -#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) -#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) -#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) -#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) -#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) -#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) -#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) -#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) -#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) -#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) -#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) -#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) -#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) -#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) -#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) -#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) -#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) -#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) -#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) -#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) -#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) -#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) -#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) -#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) -#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) -#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) -#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) -#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) -#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) -#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) -#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) -#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) -#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) -#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) -#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) -#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) -#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) -#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) -#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) -#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) -#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) -#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) -#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) -#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) -#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) -#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) -#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) -#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) -#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) -#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) -#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) -#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) -#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) -#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) -#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) -#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) -#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) -#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) -#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) -#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) -#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) -#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) -#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) -#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) -#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) -#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) -#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) -#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) -#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) -#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) -#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) -#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) -#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) -#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) -#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) -#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) -#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) -#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) -#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) -#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) -#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) -#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) -#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) -#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) -#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) -#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) -#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) -#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) -#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) -#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) -#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) -#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) -#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) -#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) -#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) -#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) -#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) -#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) -#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) -#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) -#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) -#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) -#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) -#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) -#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) -#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) -#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) -#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) -#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) -#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) -#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) -#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) -#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) -#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) -#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) -#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) -#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) -#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) -#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) -#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) -#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) -#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) -#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) -#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) -#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) -#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) -#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) -#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) -#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) -#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) -#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) -#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) -#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) -#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) -#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) -#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) -#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) -#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) -#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) -#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) -#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) -#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) -#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) -#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) -#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) -#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) -#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) -#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) -#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) -#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) -#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) -#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) -#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) -#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) -#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) -#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) -#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) -#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) -#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) -#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) -#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) -#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) -#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) -#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) -#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) -#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) -#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) -#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) -#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) -#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) -#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) -#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) -#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) -#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) -#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) -#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) -#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) -#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) -#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ - cpu_to_le32(0xC0150012) -#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) -#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) -#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) -#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) -#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) -#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) -#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) -#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ - cpu_to_le32(0xC015001C) -#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) -#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) -#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) -#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) -#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) -#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) -#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) -#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) -#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) -#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) -#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) -#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) -#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) -#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) -#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) -#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) -#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) -#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) -#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) -#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) -#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) -#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) -#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) -#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) -#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) -#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) -#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) -#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) -#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) -#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) -#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) -#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) -#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) -#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) -#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) -#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) -#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ - cpu_to_le32(0xC0190024) -#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) -#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) -#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) -#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) -#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) -#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) -#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) -#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) -#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) -#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) -#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) -#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) -#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) -#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) -#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) -#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) -#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) -#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) -#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) -#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) -#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) -#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) -#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) -#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) -#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) -#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) -#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) -#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) -#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) -#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) -#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) -#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) -#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) -#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ - cpu_to_le32(0xC0190053) -#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) -#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) -#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) -#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) -#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) -#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) -#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) -#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) -#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) -#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) -#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) -#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) -#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) -#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) -#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) -#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) -#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) -#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) -#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) -#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) -#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) -#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) -#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) -#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) -#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) -#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) -#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) -#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) -#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) -#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) -#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) -#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) -#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) -#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) -#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) -#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) -#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) -#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) -#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) -#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) -#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) -#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) -#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) -#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) -#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) -#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) -#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) -#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) -#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) -#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) -#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) -#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) -#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) -#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) -#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) -#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) -#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) -#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) -#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) -#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) -#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) -#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) -#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) -#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) -#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) -#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) -#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) -#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) -#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) -#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) -#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) -#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) -#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) -#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) -#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) -#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) -#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) -#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) -#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) -#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) -#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) -#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) -#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) -#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) -#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) -#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) -#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) -#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) -#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) -#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ - cpu_to_le32(0xC01D0006) -#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ - cpu_to_le32(0xC01D0007) -#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) -#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) -#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) -#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) -#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) -#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) -#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) -#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) -#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) -#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) -#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) -#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) -#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) -#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) -#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) -#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) -#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) -#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) -#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) -#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) -#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) -#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) -#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) -#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0302) -#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) -#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) -#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) -#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) -#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) -#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ - cpu_to_le32(0xC01E0310) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ - cpu_to_le32(0xC01E0311) -#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) -#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) -#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) -#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) -#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) -#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) -#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ - cpu_to_le32(0xC01E031B) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) -#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) -#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) -#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) -#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) -#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) -#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ - cpu_to_le32(0xC01E0325) -#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ - cpu_to_le32(0xC01E0326) -#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ - cpu_to_le32(0xC01E0328) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ - cpu_to_le32(0xC01E0329) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ - cpu_to_le32(0xC01E032E) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) -#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) -#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) -#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) -#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) -#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ - cpu_to_le32(0xC01E0334) -#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) -#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) -#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) -#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) -#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) -#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) -#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) -#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) -#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) -#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) -#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) -#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) -#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ - cpu_to_le32(0xC01E0341) -#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) -#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) -#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ - cpu_to_le32(0xC01E0345) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0346) -#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) -#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) -#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) -#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ - cpu_to_le32(0xC01E034D) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) -#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) -#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ - cpu_to_le32(0xC01E0350) -#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) -#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) -#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) -#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) -#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) -#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ - cpu_to_le32(0xC01E0357) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ - cpu_to_le32(0xC01E0358) -#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) -#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ - cpu_to_le32(0xC01E035A) -#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) -#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) -#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ - cpu_to_le32(0xC01E0400) -#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) -#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) -#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) -#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) -#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) -#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) -#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ - cpu_to_le32(0xC01E051C) -#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) -#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ - cpu_to_le32(0xC01E051F) -#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) -#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ - cpu_to_le32(0xC01E0521) -#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) -#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) -#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) -#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) -#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) -#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) -#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ - cpu_to_le32(0xC01E0506) -#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ - cpu_to_le32(0xC01E0507) -#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0508) -#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) -#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) -#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) -#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ - cpu_to_le32(0xC01E050D) -#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) -#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) -#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) -#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) -#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ - cpu_to_le32(0xC01E0515) -#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) -#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) -#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ - cpu_to_le32(0xC01E0518) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ - cpu_to_le32(0xC01E051A) -#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ - cpu_to_le32(0xC01E051B) -#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) -#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) -#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) -#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) -#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) -#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) -#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ - cpu_to_le32(0xC01E0586) -#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ - cpu_to_le32(0xC01E0587) -#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) -#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) -#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) -#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) -#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ - cpu_to_le32(0xC01E05E1) -#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ - cpu_to_le32(0xC01E05E2) -#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) -#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) -#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ - cpu_to_le32(0xC01E05E5) -#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) -#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) -#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) -#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) -#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) -#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) -#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) -#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) -#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) -#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) -#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) -#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) -#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) -#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) -#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) -#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) -#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) -#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) -#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) -#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) -#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) -#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) -#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) -#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) -#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) -#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) -#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) -#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) -#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) -#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) -#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) -#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) -#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) -#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) -#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) -#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) -#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) -#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) -#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) -#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) -#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) -#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) -#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) -#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) -#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) -#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) -#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) -#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) -#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) -#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) -#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) -#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) -#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) -#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) -#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) -#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) -#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) -#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) -#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) -#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) -#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) -#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) -#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) -#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) -#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) -#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) -#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) -#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) -#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) -#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) -#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) -#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) -#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) -#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) -#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) -#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) -#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) -#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) -#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) -#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) -#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) -#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) -#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) -#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) -#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) -#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) -#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) -#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) -#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) -#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) -#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) -#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) -#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) -#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) -#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) -#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) -#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) -#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) -#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) -#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) -#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) -#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) -#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) -#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) -#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) -#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) -#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) -#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) -#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) -#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) -#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) -#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) -#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) -#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) -#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) -#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) -#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) -#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) -#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) -#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) -#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) -#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) -#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) -#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) -#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) -#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) -#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) -#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) -#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) -#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) -#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) -#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) -#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) -#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) -#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) -#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) -#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) -#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) -#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) -#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) -#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) - -#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) -#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c index b49d47bdafc9..2dbabe2d8005 100644 --- a/fs/smb/server/transport_ipc.c +++ b/fs/smb/server/transport_ipc.c @@ -65,6 +65,7 @@ struct ipc_msg_table_entry { struct hlist_node ipc_table_hlist; void *response; + unsigned int msg_sz; }; static struct delayed_work ipc_timer_work; @@ -74,7 +75,7 @@ static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); static int ksmbd_ipc_heartbeat_request(void); -static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { +static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX + 1] = { [KSMBD_EVENT_UNSPEC] = { .len = 0, }, @@ -119,6 +120,12 @@ static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { }, [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { }, + [KSMBD_EVENT_LOGIN_REQUEST_EXT] = { + .len = sizeof(struct ksmbd_login_request), + }, + [KSMBD_EVENT_LOGIN_RESPONSE_EXT] = { + .len = sizeof(struct ksmbd_login_response_ext), + }, }; static struct genl_ops ksmbd_genl_ops[] = { @@ -186,6 +193,14 @@ static struct genl_ops ksmbd_genl_ops[] = { .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, .doit = handle_generic_event, }, + { + .cmd = KSMBD_EVENT_LOGIN_REQUEST_EXT, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_RESPONSE_EXT, + .doit = handle_generic_event, + }, }; static struct genl_family ksmbd_genl_family = { @@ -197,7 +212,7 @@ static struct genl_family ksmbd_genl_family = { .module = THIS_MODULE, .ops = ksmbd_genl_ops, .n_ops = ARRAY_SIZE(ksmbd_genl_ops), - .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, + .resv_start_op = KSMBD_EVENT_LOGIN_RESPONSE_EXT + 1, }; static void ksmbd_nl_init_fixup(void) @@ -229,7 +244,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) struct ksmbd_ipc_msg *msg; size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); - msg = kvzalloc(msg_sz, GFP_KERNEL); + msg = kvzalloc(msg_sz, KSMBD_DEFAULT_GFP); if (msg) msg->sz = sz; return msg; @@ -248,10 +263,16 @@ static void ipc_msg_handle_free(int handle) static int handle_response(int type, void *payload, size_t sz) { - unsigned int handle = *(unsigned int *)payload; + unsigned int handle; struct ipc_msg_table_entry *entry; int ret = 0; + /* Prevent 4-byte read beyond declared payload size */ + if (sz < sizeof(unsigned int)) + return -EINVAL; + + handle = *(unsigned int *)payload; + ipc_update_last_active(); down_read(&ipc_msg_table_lock); hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { @@ -266,15 +287,17 @@ static int handle_response(int type, void *payload, size_t sz) if (entry->type + 1 != type) { pr_err("Waiting for IPC type %d, got %d. Ignore.\n", entry->type + 1, type); + continue; } - entry->response = kvzalloc(sz, GFP_KERNEL); + entry->response = kvzalloc(sz, KSMBD_DEFAULT_GFP); if (!entry->response) { ret = -ENOMEM; break; } memcpy(entry->response, payload, sz); + entry->msg_sz = sz; wake_up_interruptible(&entry->wait); ret = 0; break; @@ -293,7 +316,11 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) server_conf.signing = req->signing; server_conf.tcp_port = req->tcp_port; server_conf.ipc_timeout = req->ipc_timeout * HZ; - server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; + if (check_mul_overflow(req->deadtime, SMB_ECHO_INTERVAL, + &server_conf.deadtime)) { + ret = -EINVAL; + goto out; + } server_conf.share_fake_fscaps = req->share_fake_fscaps; ksmbd_init_domain(req->sub_auth); @@ -303,19 +330,27 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) init_smb2_max_write_size(req->smb2_max_write); if (req->smb2_max_trans) init_smb2_max_trans_size(req->smb2_max_trans); - if (req->smb2_max_credits) + if (req->smb2_max_credits) { init_smb2_max_credits(req->smb2_max_credits); + server_conf.max_inflight_req = + req->smb2_max_credits; + } if (req->smbd_max_io_size) init_smbd_max_io_size(req->smbd_max_io_size); if (req->max_connections) server_conf.max_connections = req->max_connections; + if (req->max_ip_connections) + server_conf.max_ip_connections = req->max_ip_connections; + ret = ksmbd_set_netbios_name(req->netbios_name); ret |= ksmbd_set_server_string(req->server_string); ret |= ksmbd_set_work_group(req->work_group); + server_conf.bind_interfaces_only = req->bind_interfaces_only; ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), req->ifc_list_sz); +out: if (ret) { pr_err("Server configuration error: %s %s %s\n", req->netbios_name, req->server_string, @@ -403,7 +438,7 @@ static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) return -EPERM; #endif - if (type >= KSMBD_EVENT_MAX) { + if (type > KSMBD_EVENT_MAX) { WARN_ON(1); return -EINVAL; } @@ -428,7 +463,7 @@ static int ipc_msg_send(struct ksmbd_ipc_msg *msg) if (!ksmbd_tools_pid) return ret; - skb = genlmsg_new(msg->sz, GFP_KERNEL); + skb = genlmsg_new(msg->sz, KSMBD_DEFAULT_GFP); if (!skb) return -ENOMEM; @@ -453,6 +488,53 @@ out: return ret; } +static int ipc_validate_msg(struct ipc_msg_table_entry *entry) +{ + unsigned int msg_sz = entry->msg_sz; + + switch (entry->type) { + case KSMBD_EVENT_RPC_REQUEST: + { + struct ksmbd_rpc_command *resp = entry->response; + + msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz; + break; + } + case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST: + { + struct ksmbd_spnego_authen_response *resp = entry->response; + + msg_sz = sizeof(struct ksmbd_spnego_authen_response) + + resp->session_key_len + resp->spnego_blob_len; + break; + } + case KSMBD_EVENT_SHARE_CONFIG_REQUEST: + { + struct ksmbd_share_config_response *resp = entry->response; + + if (resp->payload_sz) { + if (resp->payload_sz < resp->veto_list_sz) + return -EINVAL; + + msg_sz = sizeof(struct ksmbd_share_config_response) + + resp->payload_sz; + } + break; + } + case KSMBD_EVENT_LOGIN_REQUEST_EXT: + { + struct ksmbd_login_response_ext *resp = entry->response; + + if (resp->ngroups) { + msg_sz = sizeof(struct ksmbd_login_response_ext) + + resp->ngroups * sizeof(gid_t); + } + } + } + + return entry->msg_sz != msg_sz ? -EINVAL : 0; +} + static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) { struct ipc_msg_table_entry entry; @@ -471,14 +553,24 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle up_write(&ipc_msg_table_lock); ret = ipc_msg_send(msg); - if (ret) + if (ret) { + down_write(&ipc_msg_table_lock); goto out; + } ret = wait_event_interruptible_timeout(entry.wait, entry.response != NULL, IPC_WAIT_TIMEOUT); -out: + down_write(&ipc_msg_table_lock); + if (entry.response) { + ret = ipc_validate_msg(&entry); + if (ret) { + kvfree(entry.response); + entry.response = NULL; + } + } +out: hash_del(&entry.ipc_table_hlist); up_write(&ipc_msg_table_lock); return entry.response; @@ -523,6 +615,29 @@ struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) return resp; } +struct ksmbd_login_response_ext *ksmbd_ipc_login_request_ext(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_login_request *req; + struct ksmbd_login_response_ext *resp; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_LOGIN_REQUEST_EXT; + req = (struct ksmbd_login_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + struct ksmbd_spnego_authen_response * ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) { @@ -530,6 +645,9 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) struct ksmbd_spnego_authen_request *req; struct ksmbd_spnego_authen_response *resp; + if (blob_len > KSMBD_IPC_MAX_PAYLOAD) + return NULL; + msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + blob_len + 1); if (!msg) @@ -709,10 +827,16 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle struct ksmbd_rpc_command *req; struct ksmbd_rpc_command *resp; + if (payload_sz > KSMBD_IPC_MAX_PAYLOAD) + return NULL; + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); if (!msg) return NULL; + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); msg->type = KSMBD_EVENT_RPC_REQUEST; req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; @@ -721,6 +845,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle req->flags |= KSMBD_RPC_WRITE_METHOD; req->payload_sz = payload_sz; memcpy(req->payload, payload, payload_sz); + up_read(&sess->rpc_lock); resp = ipc_msg_send_request(msg, req->handle); ipc_msg_free(msg); @@ -737,6 +862,9 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) if (!msg) return NULL; + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); msg->type = KSMBD_EVENT_RPC_REQUEST; req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; @@ -744,6 +872,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) req->flags |= rpc_context_flags(sess); req->flags |= KSMBD_RPC_READ_METHOD; req->payload_sz = 0; + up_read(&sess->rpc_lock); resp = ipc_msg_send_request(msg, req->handle); ipc_msg_free(msg); @@ -757,10 +886,16 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle struct ksmbd_rpc_command *req; struct ksmbd_rpc_command *resp; + if (payload_sz > KSMBD_IPC_MAX_PAYLOAD) + return NULL; + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); if (!msg) return NULL; + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); msg->type = KSMBD_EVENT_RPC_REQUEST; req = (struct ksmbd_rpc_command *)msg->payload; req->handle = handle; @@ -769,37 +904,13 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle req->flags |= KSMBD_RPC_IOCTL_METHOD; req->payload_sz = payload_sz; memcpy(req->payload, payload, payload_sz); + up_read(&sess->rpc_lock); resp = ipc_msg_send_request(msg, req->handle); ipc_msg_free(msg); return resp; } -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - req->flags = rpc_context_flags(sess); - req->flags |= KSMBD_RPC_RAP_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - static int __ipc_heartbeat(void) { unsigned long delta; diff --git a/fs/smb/server/transport_ipc.h b/fs/smb/server/transport_ipc.h index 5e5b90a0c187..e51850f1423b 100644 --- a/fs/smb/server/transport_ipc.h +++ b/fs/smb/server/transport_ipc.h @@ -12,6 +12,8 @@ struct ksmbd_login_response * ksmbd_ipc_login_request(const char *account); +struct ksmbd_login_response_ext * +ksmbd_ipc_login_request_ext(const char *account); struct ksmbd_session; struct ksmbd_share_config; @@ -39,8 +41,6 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, void *payload, size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz); void ksmbd_ipc_release(void); void ksmbd_ipc_soft_reset(void); int ksmbd_ipc_init(void); diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index c06efc020bd9..4e7ab8d9314f 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -14,26 +14,35 @@ #include <linux/mempool.h> #include <linux/highmem.h> #include <linux/scatterlist.h> +#include <linux/string_choices.h> #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> #include <rdma/rw.h> +#define __SMBDIRECT_SOCKET_DISCONNECT(__sc) smb_direct_disconnect_rdma_connection(__sc) + #include "glob.h" #include "connection.h" #include "smb_common.h" -#include "smbstatus.h" +#include "../common/smb2status.h" +#include "../common/smbdirect/smbdirect.h" +#include "../common/smbdirect/smbdirect_pdu.h" +#include "../common/smbdirect/smbdirect_socket.h" #include "transport_rdma.h" #define SMB_DIRECT_PORT_IWARP 5445 #define SMB_DIRECT_PORT_INFINIBAND 445 -#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) +#define SMB_DIRECT_VERSION_LE cpu_to_le16(SMBDIRECT_V1) + +/* SMB_DIRECT negotiation timeout (for the server) in seconds */ +#define SMB_DIRECT_NEGOTIATE_TIMEOUT 5 -/* SMB_DIRECT negotiation timeout in seconds */ -#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 +/* The timeout to wait for a keepalive message from peer in seconds */ +#define SMB_DIRECT_KEEPALIVE_SEND_INTERVAL 120 -#define SMB_DIRECT_MAX_SEND_SGES 6 -#define SMB_DIRECT_MAX_RECV_SGES 1 +/* The timeout to wait for a keepalive message from peer in seconds */ +#define SMB_DIRECT_KEEPALIVE_RECV_TIMEOUT 5 /* * Default maximum number of RDMA read/write outstanding on this connection @@ -86,123 +95,17 @@ static struct smb_direct_listener { static struct workqueue_struct *smb_direct_wq; -enum smb_direct_status { - SMB_DIRECT_CS_NEW = 0, - SMB_DIRECT_CS_CONNECTED, - SMB_DIRECT_CS_DISCONNECTING, - SMB_DIRECT_CS_DISCONNECTED, -}; - struct smb_direct_transport { struct ksmbd_transport transport; - enum smb_direct_status status; - bool full_packet_received; - wait_queue_head_t wait_status; - - struct rdma_cm_id *cm_id; - struct ib_cq *send_cq; - struct ib_cq *recv_cq; - struct ib_pd *pd; - struct ib_qp *qp; - - int max_send_size; - int max_recv_size; - int max_fragmented_send_size; - int max_fragmented_recv_size; - int max_rdma_rw_size; - - spinlock_t reassembly_queue_lock; - struct list_head reassembly_queue; - int reassembly_data_length; - int reassembly_queue_length; - int first_entry_offset; - wait_queue_head_t wait_reassembly_queue; - - spinlock_t receive_credit_lock; - int recv_credits; - int count_avail_recvmsg; - int recv_credit_max; - int recv_credit_target; - - spinlock_t recvmsg_queue_lock; - struct list_head recvmsg_queue; - - spinlock_t empty_recvmsg_queue_lock; - struct list_head empty_recvmsg_queue; - - int send_credit_target; - atomic_t send_credits; - spinlock_t lock_new_recv_credits; - int new_recv_credits; - int max_rw_credits; - int pages_per_rw_credit; - atomic_t rw_credits; - - wait_queue_head_t wait_send_credits; - wait_queue_head_t wait_rw_credits; - - mempool_t *sendmsg_mempool; - struct kmem_cache *sendmsg_cache; - mempool_t *recvmsg_mempool; - struct kmem_cache *recvmsg_cache; - - wait_queue_head_t wait_send_pending; - atomic_t send_pending; - - struct delayed_work post_recv_credits_work; - struct work_struct send_immediate_work; - struct work_struct disconnect_work; - - bool negotiation_requested; -}; - -#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) - -enum { - SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, - SMB_DIRECT_MSG_DATA_TRANSFER -}; - -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; - -struct smb_direct_send_ctx { - struct list_head msg_list; - int wr_cnt; - bool need_invalidate_rkey; - unsigned int remote_key; + struct smbdirect_socket socket; }; -struct smb_direct_sendmsg { - struct smb_direct_transport *transport; - struct ib_send_wr wr; - struct list_head list; - int num_sge; - struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; - struct ib_cqe cqe; - u8 packet[]; -}; - -struct smb_direct_recvmsg { - struct smb_direct_transport *transport; - struct list_head list; - int type; - struct ib_sge sge; - struct ib_cqe cqe; - bool first_segment; - u8 packet[]; -}; +#define KSMBD_TRANS(t) (&(t)->transport) +#define SMBD_TRANS(t) (container_of(t, \ + struct smb_direct_transport, transport)) -struct smb_direct_rdma_rw_msg { - struct smb_direct_transport *t; - struct ib_cqe cqe; - int status; - struct completion *completion; - struct list_head list; - struct rdma_rw_ctx rw_ctx; - struct sg_table sgt; - struct scatterlist sg_list[]; -}; +static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; void init_smbd_max_io_size(unsigned int sz) { @@ -210,9 +113,20 @@ void init_smbd_max_io_size(unsigned int sz) smb_direct_max_read_write_size = sz; } -unsigned int get_smbd_max_read_write_size(void) +unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { - return smb_direct_max_read_write_size; + struct smb_direct_transport *t; + struct smbdirect_socket *sc; + struct smbdirect_socket_parameters *sp; + + if (kt->ops != &ksmbd_smb_direct_transport_ops) + return 0; + + t = SMBD_TRANS(kt); + sc = &t->socket; + sp = &sc->parameters; + + return sp->max_read_write_size; } static inline int get_buf_page_count(void *buf, int size) @@ -221,92 +135,65 @@ static inline int get_buf_page_count(void *buf, int size) (uintptr_t)buf / PAGE_SIZE; } -static void smb_direct_destroy_pools(struct smb_direct_transport *transport); +static void smb_direct_destroy_pools(struct smbdirect_socket *sc); static void smb_direct_post_recv_credits(struct work_struct *work); -static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, +static int smb_direct_post_send_data(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx, struct kvec *iov, int niov, int remaining_data_length); -static inline struct smb_direct_transport * -smb_trans_direct_transfort(struct ksmbd_transport *t) -{ - return container_of(t, struct smb_direct_transport, transport); -} - static inline void -*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) +*smbdirect_recv_io_payload(struct smbdirect_recv_io *recvmsg) { return (void *)recvmsg->packet; } -static inline bool is_receive_credit_post_required(int receive_credits, - int avail_recvmsg_count) -{ - return receive_credits <= (smb_direct_receive_credit_max >> 3) && - avail_recvmsg_count >= (receive_credits >> 2); -} - static struct -smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) +smbdirect_recv_io *get_free_recvmsg(struct smbdirect_socket *sc) { - struct smb_direct_recvmsg *recvmsg = NULL; + struct smbdirect_recv_io *recvmsg = NULL; + unsigned long flags; - spin_lock(&t->recvmsg_queue_lock); - if (!list_empty(&t->recvmsg_queue)) { - recvmsg = list_first_entry(&t->recvmsg_queue, - struct smb_direct_recvmsg, + spin_lock_irqsave(&sc->recv_io.free.lock, flags); + if (!list_empty(&sc->recv_io.free.list)) { + recvmsg = list_first_entry(&sc->recv_io.free.list, + struct smbdirect_recv_io, list); list_del(&recvmsg->list); } - spin_unlock(&t->recvmsg_queue_lock); + spin_unlock_irqrestore(&sc->recv_io.free.lock, flags); return recvmsg; } -static void put_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) +static void put_recvmsg(struct smbdirect_socket *sc, + struct smbdirect_recv_io *recvmsg) { - ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); + unsigned long flags; - spin_lock(&t->recvmsg_queue_lock); - list_add(&recvmsg->list, &t->recvmsg_queue); - spin_unlock(&t->recvmsg_queue_lock); -} - -static struct -smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg = NULL; - - spin_lock(&t->empty_recvmsg_queue_lock); - if (!list_empty(&t->empty_recvmsg_queue)) { - recvmsg = list_first_entry(&t->empty_recvmsg_queue, - struct smb_direct_recvmsg, list); - list_del(&recvmsg->list); + if (likely(recvmsg->sge.length != 0)) { + ib_dma_unmap_single(sc->ib.dev, + recvmsg->sge.addr, + recvmsg->sge.length, + DMA_FROM_DEVICE); + recvmsg->sge.length = 0; } - spin_unlock(&t->empty_recvmsg_queue_lock); - return recvmsg; -} -static void put_empty_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); + spin_lock_irqsave(&sc->recv_io.free.lock, flags); + list_add(&recvmsg->list, &sc->recv_io.free.list); + spin_unlock_irqrestore(&sc->recv_io.free.lock, flags); - spin_lock(&t->empty_recvmsg_queue_lock); - list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); - spin_unlock(&t->empty_recvmsg_queue_lock); + queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); } -static void enqueue_reassembly(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg, +static void enqueue_reassembly(struct smbdirect_socket *sc, + struct smbdirect_recv_io *recvmsg, int data_length) { - spin_lock(&t->reassembly_queue_lock); - list_add_tail(&recvmsg->list, &t->reassembly_queue); - t->reassembly_queue_length++; + unsigned long flags; + + spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags); + list_add_tail(&recvmsg->list, &sc->recv_io.reassembly.list); + sc->recv_io.reassembly.queue_length++; /* * Make sure reassembly_data_length is updated after list and * reassembly_queue_length are updated. On the dequeue side @@ -314,92 +201,238 @@ static void enqueue_reassembly(struct smb_direct_transport *t, * if reassembly_queue_length and list is up to date */ virt_wmb(); - t->reassembly_data_length += data_length; - spin_unlock(&t->reassembly_queue_lock); + sc->recv_io.reassembly.data_length += data_length; + spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags); } -static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) +static struct smbdirect_recv_io *get_first_reassembly(struct smbdirect_socket *sc) { - if (!list_empty(&t->reassembly_queue)) - return list_first_entry(&t->reassembly_queue, - struct smb_direct_recvmsg, list); + if (!list_empty(&sc->recv_io.reassembly.list)) + return list_first_entry(&sc->recv_io.reassembly.list, + struct smbdirect_recv_io, list); else return NULL; } +static void smb_direct_disconnect_wake_up_all(struct smbdirect_socket *sc) +{ + /* + * Wake up all waiters in all wait queues + * in order to notice the broken connection. + */ + wake_up_all(&sc->status_wait); + wake_up_all(&sc->send_io.lcredits.wait_queue); + wake_up_all(&sc->send_io.credits.wait_queue); + wake_up_all(&sc->send_io.pending.zero_wait_queue); + wake_up_all(&sc->recv_io.reassembly.wait_queue); + wake_up_all(&sc->rw_io.credits.wait_queue); +} + static void smb_direct_disconnect_rdma_work(struct work_struct *work) { - struct smb_direct_transport *t = - container_of(work, struct smb_direct_transport, - disconnect_work); + struct smbdirect_socket *sc = + container_of(work, struct smbdirect_socket, disconnect_work); + + if (sc->first_error == 0) + sc->first_error = -ECONNABORTED; - if (t->status == SMB_DIRECT_CS_CONNECTED) { - t->status = SMB_DIRECT_CS_DISCONNECTING; - rdma_disconnect(t->cm_id); + /* + * make sure this and other work is not queued again + * but here we don't block and avoid + * disable[_delayed]_work_sync() + */ + disable_work(&sc->disconnect_work); + disable_work(&sc->recv_io.posted.refill_work); + disable_delayed_work(&sc->idle.timer_work); + disable_work(&sc->idle.immediate_work); + + switch (sc->status) { + case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED: + case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING: + case SMBDIRECT_SOCKET_NEGOTIATE_FAILED: + case SMBDIRECT_SOCKET_CONNECTED: + case SMBDIRECT_SOCKET_ERROR: + sc->status = SMBDIRECT_SOCKET_DISCONNECTING; + rdma_disconnect(sc->rdma.cm_id); + break; + + case SMBDIRECT_SOCKET_CREATED: + case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED: + case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING: + case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED: + case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED: + case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING: + case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED: + case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED: + case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING: + case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED: + /* + * rdma_accept() never reached + * RDMA_CM_EVENT_ESTABLISHED + */ + sc->status = SMBDIRECT_SOCKET_DISCONNECTED; + break; + + case SMBDIRECT_SOCKET_DISCONNECTING: + case SMBDIRECT_SOCKET_DISCONNECTED: + case SMBDIRECT_SOCKET_DESTROYED: + break; } + + /* + * Wake up all waiters in all wait queues + * in order to notice the broken connection. + */ + smb_direct_disconnect_wake_up_all(sc); } static void -smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) +smb_direct_disconnect_rdma_connection(struct smbdirect_socket *sc) { - if (t->status == SMB_DIRECT_CS_CONNECTED) - queue_work(smb_direct_wq, &t->disconnect_work); + if (sc->first_error == 0) + sc->first_error = -ECONNABORTED; + + /* + * make sure other work (than disconnect_work) is + * not queued again but here we don't block and avoid + * disable[_delayed]_work_sync() + */ + disable_work(&sc->recv_io.posted.refill_work); + disable_work(&sc->idle.immediate_work); + disable_delayed_work(&sc->idle.timer_work); + + switch (sc->status) { + case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED: + case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED: + case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED: + case SMBDIRECT_SOCKET_NEGOTIATE_FAILED: + case SMBDIRECT_SOCKET_ERROR: + case SMBDIRECT_SOCKET_DISCONNECTING: + case SMBDIRECT_SOCKET_DISCONNECTED: + case SMBDIRECT_SOCKET_DESTROYED: + /* + * Keep the current error status + */ + break; + + case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED: + case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING: + sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED; + break; + + case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED: + case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING: + sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED; + break; + + case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED: + case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING: + sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED; + break; + + case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED: + case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING: + sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED; + break; + + case SMBDIRECT_SOCKET_CREATED: + sc->status = SMBDIRECT_SOCKET_DISCONNECTED; + break; + + case SMBDIRECT_SOCKET_CONNECTED: + sc->status = SMBDIRECT_SOCKET_ERROR; + break; + } + + /* + * Wake up all waiters in all wait queues + * in order to notice the broken connection. + */ + smb_direct_disconnect_wake_up_all(sc); + + queue_work(sc->workqueue, &sc->disconnect_work); } static void smb_direct_send_immediate_work(struct work_struct *work) { - struct smb_direct_transport *t = container_of(work, - struct smb_direct_transport, send_immediate_work); + struct smbdirect_socket *sc = + container_of(work, struct smbdirect_socket, idle.immediate_work); - if (t->status != SMB_DIRECT_CS_CONNECTED) + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) return; - smb_direct_post_send_data(t, NULL, NULL, 0, 0); + smb_direct_post_send_data(sc, NULL, NULL, 0, 0); +} + +static void smb_direct_idle_connection_timer(struct work_struct *work) +{ + struct smbdirect_socket *sc = + container_of(work, struct smbdirect_socket, idle.timer_work.work); + struct smbdirect_socket_parameters *sp = &sc->parameters; + + if (sc->idle.keepalive != SMBDIRECT_KEEPALIVE_NONE) { + smb_direct_disconnect_rdma_connection(sc); + return; + } + + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + return; + + /* + * Now use the keepalive timeout (instead of keepalive interval) + * in order to wait for a response + */ + sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING; + mod_delayed_work(sc->workqueue, &sc->idle.timer_work, + msecs_to_jiffies(sp->keepalive_timeout_msec)); + queue_work(sc->workqueue, &sc->idle.immediate_work); } static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) { struct smb_direct_transport *t; + struct smbdirect_socket *sc; + struct smbdirect_socket_parameters *sp; struct ksmbd_conn *conn; - t = kzalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc(sizeof(*t), KSMBD_DEFAULT_GFP); if (!t) return NULL; + sc = &t->socket; + smbdirect_socket_init(sc); + sp = &sc->parameters; - t->cm_id = cm_id; - cm_id->context = t; - - t->status = SMB_DIRECT_CS_NEW; - init_waitqueue_head(&t->wait_status); + sc->workqueue = smb_direct_wq; - spin_lock_init(&t->reassembly_queue_lock); - INIT_LIST_HEAD(&t->reassembly_queue); - t->reassembly_data_length = 0; - t->reassembly_queue_length = 0; - init_waitqueue_head(&t->wait_reassembly_queue); - init_waitqueue_head(&t->wait_send_credits); - init_waitqueue_head(&t->wait_rw_credits); + INIT_WORK(&sc->disconnect_work, smb_direct_disconnect_rdma_work); - spin_lock_init(&t->receive_credit_lock); - spin_lock_init(&t->recvmsg_queue_lock); - INIT_LIST_HEAD(&t->recvmsg_queue); + sp->negotiate_timeout_msec = SMB_DIRECT_NEGOTIATE_TIMEOUT * 1000; + sp->initiator_depth = SMB_DIRECT_CM_INITIATOR_DEPTH; + sp->responder_resources = 1; + sp->recv_credit_max = smb_direct_receive_credit_max; + sp->send_credit_target = smb_direct_send_credit_target; + sp->max_send_size = smb_direct_max_send_size; + sp->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + sp->max_recv_size = smb_direct_max_receive_size; + sp->max_read_write_size = smb_direct_max_read_write_size; + sp->keepalive_interval_msec = SMB_DIRECT_KEEPALIVE_SEND_INTERVAL * 1000; + sp->keepalive_timeout_msec = SMB_DIRECT_KEEPALIVE_RECV_TIMEOUT * 1000; - spin_lock_init(&t->empty_recvmsg_queue_lock); - INIT_LIST_HEAD(&t->empty_recvmsg_queue); + sc->rdma.cm_id = cm_id; + cm_id->context = sc; - init_waitqueue_head(&t->wait_send_pending); - atomic_set(&t->send_pending, 0); + sc->ib.dev = sc->rdma.cm_id->device; - spin_lock_init(&t->lock_new_recv_credits); - - INIT_DELAYED_WORK(&t->post_recv_credits_work, - smb_direct_post_recv_credits); - INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); - INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); + INIT_DELAYED_WORK(&sc->idle.timer_work, smb_direct_idle_connection_timer); conn = ksmbd_conn_alloc(); if (!conn) goto err; + + down_write(&conn_list_lock); + hash_add(conn_list, &conn->hlist, 0); + up_write(&conn_list_lock); + conn->transport = KSMBD_TRANS(t); KSMBD_TRANS(t)->conn = conn; KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; @@ -409,91 +442,120 @@ err: return NULL; } +static void smb_direct_free_transport(struct ksmbd_transport *kt) +{ + kfree(SMBD_TRANS(kt)); +} + static void free_transport(struct smb_direct_transport *t) { - struct smb_direct_recvmsg *recvmsg; + struct smbdirect_socket *sc = &t->socket; + struct smbdirect_recv_io *recvmsg; - wake_up_interruptible(&t->wait_send_credits); + disable_work_sync(&sc->disconnect_work); + if (sc->status < SMBDIRECT_SOCKET_DISCONNECTING) + smb_direct_disconnect_rdma_work(&sc->disconnect_work); + if (sc->status < SMBDIRECT_SOCKET_DISCONNECTED) + wait_event(sc->status_wait, sc->status == SMBDIRECT_SOCKET_DISCONNECTED); - ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); - wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); + /* + * Wake up all waiters in all wait queues + * in order to notice the broken connection. + * + * Most likely this was already called via + * smb_direct_disconnect_rdma_work(), but call it again... + */ + smb_direct_disconnect_wake_up_all(sc); + + disable_work_sync(&sc->recv_io.posted.refill_work); + disable_delayed_work_sync(&sc->idle.timer_work); + disable_work_sync(&sc->idle.immediate_work); - cancel_work_sync(&t->disconnect_work); - cancel_delayed_work_sync(&t->post_recv_credits_work); - cancel_work_sync(&t->send_immediate_work); + if (sc->rdma.cm_id) + rdma_lock_handler(sc->rdma.cm_id); - if (t->qp) { - ib_drain_qp(t->qp); - ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs); - ib_destroy_qp(t->qp); + if (sc->ib.qp) { + ib_drain_qp(sc->ib.qp); + sc->ib.qp = NULL; + rdma_destroy_qp(sc->rdma.cm_id); } ksmbd_debug(RDMA, "drain the reassembly queue\n"); do { - spin_lock(&t->reassembly_queue_lock); - recvmsg = get_first_reassembly(t); + unsigned long flags; + + spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags); + recvmsg = get_first_reassembly(sc); if (recvmsg) { list_del(&recvmsg->list); - spin_unlock(&t->reassembly_queue_lock); - put_recvmsg(t, recvmsg); + spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags); + put_recvmsg(sc, recvmsg); } else { - spin_unlock(&t->reassembly_queue_lock); + spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags); } } while (recvmsg); - t->reassembly_data_length = 0; - - if (t->send_cq) - ib_free_cq(t->send_cq); - if (t->recv_cq) - ib_free_cq(t->recv_cq); - if (t->pd) - ib_dealloc_pd(t->pd); - if (t->cm_id) - rdma_destroy_id(t->cm_id); - - smb_direct_destroy_pools(t); + sc->recv_io.reassembly.data_length = 0; + + if (sc->ib.send_cq) + ib_free_cq(sc->ib.send_cq); + if (sc->ib.recv_cq) + ib_free_cq(sc->ib.recv_cq); + if (sc->ib.pd) + ib_dealloc_pd(sc->ib.pd); + if (sc->rdma.cm_id) { + rdma_unlock_handler(sc->rdma.cm_id); + rdma_destroy_id(sc->rdma.cm_id); + } + + smb_direct_destroy_pools(sc); ksmbd_conn_free(KSMBD_TRANS(t)->conn); - kfree(t); } -static struct smb_direct_sendmsg -*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) +static struct smbdirect_send_io +*smb_direct_alloc_sendmsg(struct smbdirect_socket *sc) { - struct smb_direct_sendmsg *msg; + struct smbdirect_send_io *msg; - msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + msg = mempool_alloc(sc->send_io.mem.pool, KSMBD_DEFAULT_GFP); if (!msg) return ERR_PTR(-ENOMEM); - msg->transport = t; - INIT_LIST_HEAD(&msg->list); + msg->socket = sc; + INIT_LIST_HEAD(&msg->sibling_list); msg->num_sge = 0; return msg; } -static void smb_direct_free_sendmsg(struct smb_direct_transport *t, - struct smb_direct_sendmsg *msg) +static void smb_direct_free_sendmsg(struct smbdirect_socket *sc, + struct smbdirect_send_io *msg) { int i; + /* + * The list needs to be empty! + * The caller should take care of it. + */ + WARN_ON_ONCE(!list_empty(&msg->sibling_list)); + if (msg->num_sge > 0) { - ib_dma_unmap_single(t->cm_id->device, + ib_dma_unmap_single(sc->ib.dev, msg->sge[0].addr, msg->sge[0].length, DMA_TO_DEVICE); for (i = 1; i < msg->num_sge; i++) - ib_dma_unmap_page(t->cm_id->device, + ib_dma_unmap_page(sc->ib.dev, msg->sge[i].addr, msg->sge[i].length, DMA_TO_DEVICE); } - mempool_free(msg, t->sendmsg_mempool); + mempool_free(msg, sc->send_io.mem.pool); } -static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) +static int smb_direct_check_recvmsg(struct smbdirect_recv_io *recvmsg) { - switch (recvmsg->type) { - case SMB_DIRECT_MSG_DATA_TRANSFER: { - struct smb_direct_data_transfer *req = - (struct smb_direct_data_transfer *)recvmsg->packet; + struct smbdirect_socket *sc = recvmsg->socket; + + switch (sc->recv_io.expected) { + case SMBDIRECT_EXPECT_DATA_TRANSFER: { + struct smbdirect_data_transfer *req = + (struct smbdirect_data_transfer *)recvmsg->packet; struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + le32_to_cpu(req->data_offset)); ksmbd_debug(RDMA, @@ -502,11 +564,11 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) le16_to_cpu(req->credits_requested), req->data_length, req->remaining_data_length, hdr->ProtocolId, hdr->Command); - break; + return 0; } - case SMB_DIRECT_MSG_NEGOTIATE_REQ: { - struct smb_direct_negotiate_req *req = - (struct smb_direct_negotiate_req *)recvmsg->packet; + case SMBDIRECT_EXPECT_NEGOTIATE_REQ: { + struct smbdirect_negotiate_req *req = + (struct smbdirect_negotiate_req *)recvmsg->packet; ksmbd_debug(RDMA, "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", le16_to_cpu(req->min_version), @@ -524,30 +586,35 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) 128 * 1024) return -ECONNABORTED; - break; + return 0; } - default: - return -EINVAL; + case SMBDIRECT_EXPECT_NEGOTIATE_REP: + /* client only */ + break; } - return 0; + + /* This is an internal error */ + return -EINVAL; } static void recv_done(struct ib_cq *cq, struct ib_wc *wc) { - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_transport *t; + struct smbdirect_recv_io *recvmsg; + struct smbdirect_socket *sc; + struct smbdirect_socket_parameters *sp; - recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); - t = recvmsg->transport; + recvmsg = container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe); + sc = recvmsg->socket; + sp = &sc->parameters; if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + put_recvmsg(sc, recvmsg); if (wc->status != IB_WC_WR_FLUSH_ERR) { pr_err("Recv error. status='%s (%d)' opcode=%d\n", ib_wc_status_msg(wc->status), wc->status, wc->opcode); - smb_direct_disconnect_rdma_connection(t); + smb_direct_disconnect_rdma_connection(sc); } - put_empty_recvmsg(t, recvmsg); return; } @@ -558,98 +625,139 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, recvmsg->sge.length, DMA_FROM_DEVICE); - switch (recvmsg->type) { - case SMB_DIRECT_MSG_NEGOTIATE_REQ: - if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { - put_empty_recvmsg(t, recvmsg); + /* + * Reset timer to the keepalive interval in + * order to trigger our next keepalive message. + */ + sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE; + mod_delayed_work(sc->workqueue, &sc->idle.timer_work, + msecs_to_jiffies(sp->keepalive_interval_msec)); + + switch (sc->recv_io.expected) { + case SMBDIRECT_EXPECT_NEGOTIATE_REQ: + if (wc->byte_len < sizeof(struct smbdirect_negotiate_req)) { + put_recvmsg(sc, recvmsg); + smb_direct_disconnect_rdma_connection(sc); return; } - t->negotiation_requested = true; - t->full_packet_received = true; - t->status = SMB_DIRECT_CS_CONNECTED; - enqueue_reassembly(t, recvmsg, 0); - wake_up_interruptible(&t->wait_status); - break; - case SMB_DIRECT_MSG_DATA_TRANSFER: { - struct smb_direct_data_transfer *data_transfer = - (struct smb_direct_data_transfer *)recvmsg->packet; - unsigned int data_length; - int avail_recvmsg_count, receive_credits; + sc->recv_io.reassembly.full_packet_received = true; + /* + * Some drivers (at least mlx5_ib) might post a + * recv completion before RDMA_CM_EVENT_ESTABLISHED, + * we need to adjust our expectation in that case. + */ + if (!sc->first_error && sc->status == SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING) + sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED; + if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_NEEDED)) { + put_recvmsg(sc, recvmsg); + smb_direct_disconnect_rdma_connection(sc); + return; + } + sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING; + enqueue_reassembly(sc, recvmsg, 0); + wake_up(&sc->status_wait); + return; + case SMBDIRECT_EXPECT_DATA_TRANSFER: { + struct smbdirect_data_transfer *data_transfer = + (struct smbdirect_data_transfer *)recvmsg->packet; + u32 remaining_data_length, data_offset, data_length; + u16 old_recv_credit_target; if (wc->byte_len < - offsetof(struct smb_direct_data_transfer, padding)) { - put_empty_recvmsg(t, recvmsg); + offsetof(struct smbdirect_data_transfer, padding)) { + put_recvmsg(sc, recvmsg); + smb_direct_disconnect_rdma_connection(sc); return; } + remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length); data_length = le32_to_cpu(data_transfer->data_length); - if (data_length) { - if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + - (u64)data_length) { - put_empty_recvmsg(t, recvmsg); - return; - } + data_offset = le32_to_cpu(data_transfer->data_offset); + if (wc->byte_len < data_offset || + wc->byte_len < (u64)data_offset + data_length) { + put_recvmsg(sc, recvmsg); + smb_direct_disconnect_rdma_connection(sc); + return; + } + if (remaining_data_length > sp->max_fragmented_recv_size || + data_length > sp->max_fragmented_recv_size || + (u64)remaining_data_length + (u64)data_length > + (u64)sp->max_fragmented_recv_size) { + put_recvmsg(sc, recvmsg); + smb_direct_disconnect_rdma_connection(sc); + return; + } - if (t->full_packet_received) + if (data_length) { + if (sc->recv_io.reassembly.full_packet_received) recvmsg->first_segment = true; if (le32_to_cpu(data_transfer->remaining_data_length)) - t->full_packet_received = false; + sc->recv_io.reassembly.full_packet_received = false; else - t->full_packet_received = true; - - enqueue_reassembly(t, recvmsg, (int)data_length); - wake_up_interruptible(&t->wait_reassembly_queue); - - spin_lock(&t->receive_credit_lock); - receive_credits = --(t->recv_credits); - avail_recvmsg_count = t->count_avail_recvmsg; - spin_unlock(&t->receive_credit_lock); - } else { - put_empty_recvmsg(t, recvmsg); - - spin_lock(&t->receive_credit_lock); - receive_credits = --(t->recv_credits); - avail_recvmsg_count = ++(t->count_avail_recvmsg); - spin_unlock(&t->receive_credit_lock); + sc->recv_io.reassembly.full_packet_received = true; } - t->recv_credit_target = + atomic_dec(&sc->recv_io.posted.count); + atomic_dec(&sc->recv_io.credits.count); + + old_recv_credit_target = sc->recv_io.credits.target; + sc->recv_io.credits.target = le16_to_cpu(data_transfer->credits_requested); + sc->recv_io.credits.target = + min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max); + sc->recv_io.credits.target = + max_t(u16, sc->recv_io.credits.target, 1); atomic_add(le16_to_cpu(data_transfer->credits_granted), - &t->send_credits); + &sc->send_io.credits.count); if (le16_to_cpu(data_transfer->flags) & - SMB_DIRECT_RESPONSE_REQUESTED) - queue_work(smb_direct_wq, &t->send_immediate_work); + SMBDIRECT_FLAG_RESPONSE_REQUESTED) + queue_work(sc->workqueue, &sc->idle.immediate_work); - if (atomic_read(&t->send_credits) > 0) - wake_up_interruptible(&t->wait_send_credits); + if (atomic_read(&sc->send_io.credits.count) > 0) + wake_up(&sc->send_io.credits.wait_queue); - if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) - mod_delayed_work(smb_direct_wq, - &t->post_recv_credits_work, 0); - break; + if (data_length) { + if (sc->recv_io.credits.target > old_recv_credit_target) + queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); + + enqueue_reassembly(sc, recvmsg, (int)data_length); + wake_up(&sc->recv_io.reassembly.wait_queue); + } else + put_recvmsg(sc, recvmsg); + + return; } - default: + case SMBDIRECT_EXPECT_NEGOTIATE_REP: + /* client only */ break; } + + /* + * This is an internal error! + */ + WARN_ON_ONCE(sc->recv_io.expected != SMBDIRECT_EXPECT_DATA_TRANSFER); + put_recvmsg(sc, recvmsg); + smb_direct_disconnect_rdma_connection(sc); } -static int smb_direct_post_recv(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) +static int smb_direct_post_recv(struct smbdirect_socket *sc, + struct smbdirect_recv_io *recvmsg) { + struct smbdirect_socket_parameters *sp = &sc->parameters; struct ib_recv_wr wr; int ret; - recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, - recvmsg->packet, t->max_recv_size, + recvmsg->sge.addr = ib_dma_map_single(sc->ib.dev, + recvmsg->packet, + sp->max_recv_size, DMA_FROM_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); + ret = ib_dma_mapping_error(sc->ib.dev, recvmsg->sge.addr); if (ret) return ret; - recvmsg->sge.length = t->max_recv_size; - recvmsg->sge.lkey = t->pd->local_dma_lkey; + recvmsg->sge.length = sp->max_recv_size; + recvmsg->sge.lkey = sc->ib.pd->local_dma_lkey; recvmsg->cqe.done = recv_done; wr.wr_cqe = &recvmsg->cqe; @@ -657,13 +765,14 @@ static int smb_direct_post_recv(struct smb_direct_transport *t, wr.sg_list = &recvmsg->sge; wr.num_sge = 1; - ret = ib_post_recv(t->qp, &wr, NULL); + ret = ib_post_recv(sc->ib.qp, &wr, NULL); if (ret) { pr_err("Can't post recv: %d\n", ret); - ib_dma_unmap_single(t->cm_id->device, + ib_dma_unmap_single(sc->ib.dev, recvmsg->sge.addr, recvmsg->sge.length, DMA_FROM_DEVICE); - smb_direct_disconnect_rdma_connection(t); + recvmsg->sge.length = 0; + smb_direct_disconnect_rdma_connection(sc); return ret; } return ret; @@ -672,15 +781,16 @@ static int smb_direct_post_recv(struct smb_direct_transport *t, static int smb_direct_read(struct ksmbd_transport *t, char *buf, unsigned int size, int unused) { - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_data_transfer *data_transfer; + struct smbdirect_recv_io *recvmsg; + struct smbdirect_data_transfer *data_transfer; int to_copy, to_read, data_read, offset; u32 data_length, remaining_data_length, data_offset; int rc; - struct smb_direct_transport *st = smb_trans_direct_transfort(t); + struct smb_direct_transport *st = SMBD_TRANS(t); + struct smbdirect_socket *sc = &st->socket; again: - if (st->status != SMB_DIRECT_CS_CONNECTED) { + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { pr_err("disconnected\n"); return -ENOTCONN; } @@ -690,9 +800,10 @@ again: * the only one reading from the front of the queue. The transport * may add more entries to the back of the queue at the same time */ - if (st->reassembly_data_length >= size) { + if (sc->recv_io.reassembly.data_length >= size) { int queue_length; int queue_removed = 0; + unsigned long flags; /* * Need to make sure reassembly_data_length is read before @@ -702,13 +813,13 @@ again: * updated in SOFTIRQ as more data is received */ virt_rmb(); - queue_length = st->reassembly_queue_length; + queue_length = sc->recv_io.reassembly.queue_length; data_read = 0; to_read = size; - offset = st->first_entry_offset; + offset = sc->recv_io.reassembly.first_entry_offset; while (data_read < size) { - recvmsg = get_first_reassembly(st); - data_transfer = smb_direct_recvmsg_payload(recvmsg); + recvmsg = get_first_reassembly(sc); + data_transfer = smbdirect_recv_io_payload(recvmsg); data_length = le32_to_cpu(data_transfer->data_length); remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length); @@ -748,12 +859,12 @@ again: if (queue_length) { list_del(&recvmsg->list); } else { - spin_lock_irq(&st->reassembly_queue_lock); + spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags); list_del(&recvmsg->list); - spin_unlock_irq(&st->reassembly_queue_lock); + spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags); } queue_removed++; - put_recvmsg(st, recvmsg); + put_recvmsg(sc, recvmsg); offset = 0; } else { offset += to_copy; @@ -763,34 +874,24 @@ again: data_read += to_copy; } - spin_lock_irq(&st->reassembly_queue_lock); - st->reassembly_data_length -= data_read; - st->reassembly_queue_length -= queue_removed; - spin_unlock_irq(&st->reassembly_queue_lock); - - spin_lock(&st->receive_credit_lock); - st->count_avail_recvmsg += queue_removed; - if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { - spin_unlock(&st->receive_credit_lock); - mod_delayed_work(smb_direct_wq, - &st->post_recv_credits_work, 0); - } else { - spin_unlock(&st->receive_credit_lock); - } + spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags); + sc->recv_io.reassembly.data_length -= data_read; + sc->recv_io.reassembly.queue_length -= queue_removed; + spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags); - st->first_entry_offset = offset; + sc->recv_io.reassembly.first_entry_offset = offset; ksmbd_debug(RDMA, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", - data_read, st->reassembly_data_length, - st->first_entry_offset); + data_read, sc->recv_io.reassembly.data_length, + sc->recv_io.reassembly.first_entry_offset); read_rfc1002_done: return data_read; } ksmbd_debug(RDMA, "wait_event on more data\n"); - rc = wait_event_interruptible(st->wait_reassembly_queue, - st->reassembly_data_length >= size || - st->status != SMB_DIRECT_CS_CONNECTED); + rc = wait_event_interruptible(sc->recv_io.reassembly.wait_queue, + sc->recv_io.reassembly.data_length >= size || + sc->status != SMBDIRECT_SOCKET_CONNECTED); if (rc) return -EINTR; @@ -799,124 +900,127 @@ read_rfc1002_done: static void smb_direct_post_recv_credits(struct work_struct *work) { - struct smb_direct_transport *t = container_of(work, - struct smb_direct_transport, post_recv_credits_work.work); - struct smb_direct_recvmsg *recvmsg; - int receive_credits, credits = 0; + struct smbdirect_socket *sc = + container_of(work, struct smbdirect_socket, recv_io.posted.refill_work); + struct smbdirect_recv_io *recvmsg; + int credits = 0; int ret; - int use_free = 1; - spin_lock(&t->receive_credit_lock); - receive_credits = t->recv_credits; - spin_unlock(&t->receive_credit_lock); - - if (receive_credits < t->recv_credit_target) { + if (atomic_read(&sc->recv_io.credits.count) < sc->recv_io.credits.target) { while (true) { - if (use_free) - recvmsg = get_free_recvmsg(t); - else - recvmsg = get_empty_recvmsg(t); - if (!recvmsg) { - if (use_free) { - use_free = 0; - continue; - } else { - break; - } - } + recvmsg = get_free_recvmsg(sc); + if (!recvmsg) + break; - recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; recvmsg->first_segment = false; - ret = smb_direct_post_recv(t, recvmsg); + ret = smb_direct_post_recv(sc, recvmsg); if (ret) { pr_err("Can't post recv: %d\n", ret); - put_recvmsg(t, recvmsg); + put_recvmsg(sc, recvmsg); break; } credits++; + + atomic_inc(&sc->recv_io.posted.count); } } - spin_lock(&t->receive_credit_lock); - t->recv_credits += credits; - t->count_avail_recvmsg -= credits; - spin_unlock(&t->receive_credit_lock); - - spin_lock(&t->lock_new_recv_credits); - t->new_recv_credits += credits; - spin_unlock(&t->lock_new_recv_credits); - if (credits) - queue_work(smb_direct_wq, &t->send_immediate_work); + queue_work(sc->workqueue, &sc->idle.immediate_work); } static void send_done(struct ib_cq *cq, struct ib_wc *wc) { - struct smb_direct_sendmsg *sendmsg, *sibling; - struct smb_direct_transport *t; - struct list_head *pos, *prev, *end; + struct smbdirect_send_io *sendmsg, *sibling, *next; + struct smbdirect_socket *sc; + int lcredits = 0; - sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); - t = sendmsg->transport; + sendmsg = container_of(wc->wr_cqe, struct smbdirect_send_io, cqe); + sc = sendmsg->socket; ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", ib_wc_status_msg(wc->status), wc->status, wc->opcode); + /* + * Free possible siblings and then the main send_io + */ + list_for_each_entry_safe(sibling, next, &sendmsg->sibling_list, sibling_list) { + list_del_init(&sibling->sibling_list); + smb_direct_free_sendmsg(sc, sibling); + lcredits += 1; + } + /* Note this frees wc->wr_cqe, but not wc */ + smb_direct_free_sendmsg(sc, sendmsg); + lcredits += 1; + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { pr_err("Send error. status='%s (%d)', opcode=%d\n", ib_wc_status_msg(wc->status), wc->status, wc->opcode); - smb_direct_disconnect_rdma_connection(t); + smb_direct_disconnect_rdma_connection(sc); + return; } - if (atomic_dec_and_test(&t->send_pending)) - wake_up(&t->wait_send_pending); - - /* iterate and free the list of messages in reverse. the list's head - * is invalid. - */ - for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; - prev != end; pos = prev, prev = prev->prev) { - sibling = container_of(pos, struct smb_direct_sendmsg, list); - smb_direct_free_sendmsg(t, sibling); - } + atomic_add(lcredits, &sc->send_io.lcredits.count); + wake_up(&sc->send_io.lcredits.wait_queue); - sibling = container_of(pos, struct smb_direct_sendmsg, list); - smb_direct_free_sendmsg(t, sibling); + if (atomic_dec_and_test(&sc->send_io.pending.count)) + wake_up(&sc->send_io.pending.zero_wait_queue); } -static int manage_credits_prior_sending(struct smb_direct_transport *t) +static int manage_credits_prior_sending(struct smbdirect_socket *sc) { int new_credits; - spin_lock(&t->lock_new_recv_credits); - new_credits = t->new_recv_credits; - t->new_recv_credits = 0; - spin_unlock(&t->lock_new_recv_credits); + if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target) + return 0; + + new_credits = atomic_read(&sc->recv_io.posted.count); + if (new_credits == 0) + return 0; + + new_credits -= atomic_read(&sc->recv_io.credits.count); + if (new_credits <= 0) + return 0; + atomic_add(new_credits, &sc->recv_io.credits.count); return new_credits; } -static int smb_direct_post_send(struct smb_direct_transport *t, +static int manage_keep_alive_before_sending(struct smbdirect_socket *sc) +{ + struct smbdirect_socket_parameters *sp = &sc->parameters; + + if (sc->idle.keepalive == SMBDIRECT_KEEPALIVE_PENDING) { + sc->idle.keepalive = SMBDIRECT_KEEPALIVE_SENT; + /* + * Now use the keepalive timeout (instead of keepalive interval) + * in order to wait for a response + */ + mod_delayed_work(sc->workqueue, &sc->idle.timer_work, + msecs_to_jiffies(sp->keepalive_timeout_msec)); + return 1; + } + return 0; +} + +static int smb_direct_post_send(struct smbdirect_socket *sc, struct ib_send_wr *wr) { int ret; - atomic_inc(&t->send_pending); - ret = ib_post_send(t->qp, wr, NULL); + atomic_inc(&sc->send_io.pending.count); + ret = ib_post_send(sc->ib.qp, wr, NULL); if (ret) { pr_err("failed to post send: %d\n", ret); - if (atomic_dec_and_test(&t->send_pending)) - wake_up(&t->wait_send_pending); - smb_direct_disconnect_rdma_connection(t); + smb_direct_disconnect_rdma_connection(sc); } return ret; } -static void smb_direct_send_ctx_init(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, +static void smb_direct_send_ctx_init(struct smbdirect_send_batch *send_ctx, bool need_invalidate_rkey, unsigned int remote_key) { @@ -926,47 +1030,60 @@ static void smb_direct_send_ctx_init(struct smb_direct_transport *t, send_ctx->remote_key = remote_key; } -static int smb_direct_flush_send_list(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, +static int smb_direct_flush_send_list(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx, bool is_last) { - struct smb_direct_sendmsg *first, *last; + struct smbdirect_send_io *first, *last; int ret; if (list_empty(&send_ctx->msg_list)) return 0; first = list_first_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); + struct smbdirect_send_io, + sibling_list); last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); + struct smbdirect_send_io, + sibling_list); + + if (send_ctx->need_invalidate_rkey) { + first->wr.opcode = IB_WR_SEND_WITH_INV; + first->wr.ex.invalidate_rkey = send_ctx->remote_key; + send_ctx->need_invalidate_rkey = false; + send_ctx->remote_key = 0; + } last->wr.send_flags = IB_SEND_SIGNALED; last->wr.wr_cqe = &last->cqe; - if (is_last && send_ctx->need_invalidate_rkey) { - last->wr.opcode = IB_WR_SEND_WITH_INV; - last->wr.ex.invalidate_rkey = send_ctx->remote_key; - } - ret = smb_direct_post_send(t, &first->wr); - if (!ret) { - smb_direct_send_ctx_init(t, send_ctx, - send_ctx->need_invalidate_rkey, - send_ctx->remote_key); - } else { - atomic_add(send_ctx->wr_cnt, &t->send_credits); - wake_up(&t->wait_send_credits); - list_for_each_entry_safe(first, last, &send_ctx->msg_list, - list) { - smb_direct_free_sendmsg(t, first); + /* + * Remove last from send_ctx->msg_list + * and splice the rest of send_ctx->msg_list + * to last->sibling_list. + * + * send_ctx->msg_list is a valid empty list + * at the end. + */ + list_del_init(&last->sibling_list); + list_splice_tail_init(&send_ctx->msg_list, &last->sibling_list); + send_ctx->wr_cnt = 0; + + ret = smb_direct_post_send(sc, &first->wr); + if (ret) { + struct smbdirect_send_io *sibling, *next; + + list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) { + list_del_init(&sibling->sibling_list); + smb_direct_free_sendmsg(sc, sibling); } + smb_direct_free_sendmsg(sc, last); } + return ret; } -static int wait_for_credits(struct smb_direct_transport *t, +static int wait_for_credits(struct smbdirect_socket *sc, wait_queue_head_t *waitq, atomic_t *total_credits, int needed) { @@ -979,61 +1096,85 @@ static int wait_for_credits(struct smb_direct_transport *t, atomic_add(needed, total_credits); ret = wait_event_interruptible(*waitq, atomic_read(total_credits) >= needed || - t->status != SMB_DIRECT_CS_CONNECTED); + sc->status != SMBDIRECT_SOCKET_CONNECTED); - if (t->status != SMB_DIRECT_CS_CONNECTED) + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) return -ENOTCONN; else if (ret < 0) return ret; } while (true); } -static int wait_for_send_credits(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx) +static int wait_for_send_lcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx) +{ + if (send_ctx && (atomic_read(&sc->send_io.lcredits.count) <= 1)) { + int ret; + + ret = smb_direct_flush_send_list(sc, send_ctx, false); + if (ret) + return ret; + } + + return wait_for_credits(sc, + &sc->send_io.lcredits.wait_queue, + &sc->send_io.lcredits.count, + 1); +} + +static int wait_for_send_credits(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx) { int ret; if (send_ctx && - (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { - ret = smb_direct_flush_send_list(t, send_ctx, false); + (send_ctx->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) { + ret = smb_direct_flush_send_list(sc, send_ctx, false); if (ret) return ret; } - return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1); + return wait_for_credits(sc, &sc->send_io.credits.wait_queue, &sc->send_io.credits.count, 1); } -static int wait_for_rw_credits(struct smb_direct_transport *t, int credits) +static int wait_for_rw_credits(struct smbdirect_socket *sc, int credits) { - return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits); + return wait_for_credits(sc, + &sc->rw_io.credits.wait_queue, + &sc->rw_io.credits.count, + credits); } -static int calc_rw_credits(struct smb_direct_transport *t, +static int calc_rw_credits(struct smbdirect_socket *sc, char *buf, unsigned int len) { return DIV_ROUND_UP(get_buf_page_count(buf, len), - t->pages_per_rw_credit); + sc->rw_io.credits.num_pages); } -static int smb_direct_create_header(struct smb_direct_transport *t, +static int smb_direct_create_header(struct smbdirect_socket *sc, int size, int remaining_data_length, - struct smb_direct_sendmsg **sendmsg_out) + struct smbdirect_send_io **sendmsg_out) { - struct smb_direct_sendmsg *sendmsg; - struct smb_direct_data_transfer *packet; + struct smbdirect_socket_parameters *sp = &sc->parameters; + struct smbdirect_send_io *sendmsg; + struct smbdirect_data_transfer *packet; int header_length; int ret; - sendmsg = smb_direct_alloc_sendmsg(t); + sendmsg = smb_direct_alloc_sendmsg(sc); if (IS_ERR(sendmsg)) return PTR_ERR(sendmsg); /* Fill in the packet header */ - packet = (struct smb_direct_data_transfer *)sendmsg->packet; - packet->credits_requested = cpu_to_le16(t->send_credit_target); - packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + packet = (struct smbdirect_data_transfer *)sendmsg->packet; + packet->credits_requested = cpu_to_le16(sp->send_credit_target); + packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc)); packet->flags = 0; + if (manage_keep_alive_before_sending(sc)) + packet->flags |= cpu_to_le16(SMBDIRECT_FLAG_RESPONSE_REQUESTED); + packet->reserved = 0; if (!size) packet->data_offset = 0; @@ -1052,25 +1193,25 @@ static int smb_direct_create_header(struct smb_direct_transport *t, le32_to_cpu(packet->remaining_data_length)); /* Map the packet to DMA */ - header_length = sizeof(struct smb_direct_data_transfer); + header_length = sizeof(struct smbdirect_data_transfer); /* If this is a packet without payload, don't send padding */ if (!size) header_length = - offsetof(struct smb_direct_data_transfer, padding); + offsetof(struct smbdirect_data_transfer, padding); - sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + sendmsg->sge[0].addr = ib_dma_map_single(sc->ib.dev, (void *)packet, header_length, DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + ret = ib_dma_mapping_error(sc->ib.dev, sendmsg->sge[0].addr); if (ret) { - smb_direct_free_sendmsg(t, sendmsg); + smb_direct_free_sendmsg(sc, sendmsg); return ret; } sendmsg->num_sge = 1; sendmsg->sge[0].length = header_length; - sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + sendmsg->sge[0].lkey = sc->ib.pd->local_dma_lkey; *sendmsg_out = sendmsg; return 0; @@ -1120,14 +1261,14 @@ static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, return ib_dma_map_sg(device, sg_list, npages, dir); } -static int post_sendmsg(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct smb_direct_sendmsg *msg) +static int post_sendmsg(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx, + struct smbdirect_send_io *msg) { int i; for (i = 0; i < msg->num_sge; i++) - ib_dma_sync_single_for_device(t->cm_id->device, + ib_dma_sync_single_for_device(sc->ib.dev, msg->sge[i].addr, msg->sge[i].length, DMA_TO_DEVICE); @@ -1141,65 +1282,67 @@ static int post_sendmsg(struct smb_direct_transport *t, msg->wr.wr_cqe = NULL; msg->wr.send_flags = 0; if (!list_empty(&send_ctx->msg_list)) { - struct smb_direct_sendmsg *last; + struct smbdirect_send_io *last; last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); + struct smbdirect_send_io, + sibling_list); last->wr.next = &msg->wr; } - list_add_tail(&msg->list, &send_ctx->msg_list); + list_add_tail(&msg->sibling_list, &send_ctx->msg_list); send_ctx->wr_cnt++; return 0; } msg->wr.wr_cqe = &msg->cqe; msg->wr.send_flags = IB_SEND_SIGNALED; - return smb_direct_post_send(t, &msg->wr); + return smb_direct_post_send(sc, &msg->wr); } -static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, +static int smb_direct_post_send_data(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx, struct kvec *iov, int niov, int remaining_data_length) { int i, j, ret; - struct smb_direct_sendmsg *msg; + struct smbdirect_send_io *msg; int data_length; - struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; + struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1]; - ret = wait_for_send_credits(t, send_ctx); + ret = wait_for_send_lcredit(sc, send_ctx); if (ret) - return ret; + goto lcredit_failed; + + ret = wait_for_send_credits(sc, send_ctx); + if (ret) + goto credit_failed; data_length = 0; for (i = 0; i < niov; i++) data_length += iov[i].iov_len; - ret = smb_direct_create_header(t, data_length, remaining_data_length, + ret = smb_direct_create_header(sc, data_length, remaining_data_length, &msg); - if (ret) { - atomic_inc(&t->send_credits); - return ret; - } + if (ret) + goto header_failed; for (i = 0; i < niov; i++) { struct ib_sge *sge; int sg_cnt; - sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); - sg_cnt = get_mapped_sg_list(t->cm_id->device, + sg_init_table(sg, SMBDIRECT_SEND_IO_MAX_SGE - 1); + sg_cnt = get_mapped_sg_list(sc->ib.dev, iov[i].iov_base, iov[i].iov_len, - sg, SMB_DIRECT_MAX_SEND_SGES - 1, + sg, SMBDIRECT_SEND_IO_MAX_SGE - 1, DMA_TO_DEVICE); if (sg_cnt <= 0) { pr_err("failed to map buffer\n"); ret = -ENOMEM; goto err; - } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { + } else if (sg_cnt + msg->num_sge > SMBDIRECT_SEND_IO_MAX_SGE) { pr_err("buffer not fitted into sges\n"); ret = -E2BIG; - ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, + ib_dma_unmap_sg(sc->ib.dev, sg, sg_cnt, DMA_TO_DEVICE); goto err; } @@ -1208,18 +1351,22 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t, sge = &msg->sge[msg->num_sge]; sge->addr = sg_dma_address(&sg[j]); sge->length = sg_dma_len(&sg[j]); - sge->lkey = t->pd->local_dma_lkey; + sge->lkey = sc->ib.pd->local_dma_lkey; msg->num_sge++; } } - ret = post_sendmsg(t, send_ctx, msg); + ret = post_sendmsg(sc, send_ctx, msg); if (ret) goto err; return 0; err: - smb_direct_free_sendmsg(t, msg); - atomic_inc(&t->send_credits); + smb_direct_free_sendmsg(sc, msg); +header_failed: + atomic_inc(&sc->send_io.credits.count); +credit_failed: + atomic_inc(&sc->send_io.lcredits.count); +lcredit_failed: return ret; } @@ -1227,81 +1374,133 @@ static int smb_direct_writev(struct ksmbd_transport *t, struct kvec *iov, int niovs, int buflen, bool need_invalidate, unsigned int remote_key) { - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - int remaining_data_length; - int start, i, j; - int max_iov_size = st->max_send_size - - sizeof(struct smb_direct_data_transfer); + struct smb_direct_transport *st = SMBD_TRANS(t); + struct smbdirect_socket *sc = &st->socket; + struct smbdirect_socket_parameters *sp = &sc->parameters; + size_t remaining_data_length; + size_t iov_idx; + size_t iov_ofs; + size_t max_iov_size = sp->max_send_size - + sizeof(struct smbdirect_data_transfer); int ret; - struct kvec vec; - struct smb_direct_send_ctx send_ctx; + struct smbdirect_send_batch send_ctx; + int error = 0; - if (st->status != SMB_DIRECT_CS_CONNECTED) + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) return -ENOTCONN; //FIXME: skip RFC1002 header.. + if (WARN_ON_ONCE(niovs <= 1 || iov[0].iov_len != 4)) + return -EINVAL; buflen -= 4; - iov[0].iov_base += 4; - iov[0].iov_len -= 4; + iov_idx = 1; + iov_ofs = 0; remaining_data_length = buflen; ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); - smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); - start = i = 0; - buflen = 0; - while (true) { - buflen += iov[i].iov_len; - if (buflen > max_iov_size) { - if (i > start) { - remaining_data_length -= - (buflen - iov[i].iov_len); - ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); - if (ret) + smb_direct_send_ctx_init(&send_ctx, need_invalidate, remote_key); + while (remaining_data_length) { + struct kvec vecs[SMBDIRECT_SEND_IO_MAX_SGE - 1]; /* minus smbdirect hdr */ + size_t possible_bytes = max_iov_size; + size_t possible_vecs; + size_t bytes = 0; + size_t nvecs = 0; + + /* + * For the last message remaining_data_length should be + * have been 0 already! + */ + if (WARN_ON_ONCE(iov_idx >= niovs)) { + error = -EINVAL; + goto done; + } + + /* + * We have 2 factors which limit the arguments we pass + * to smb_direct_post_send_data(): + * + * 1. The number of supported sges for the send, + * while one is reserved for the smbdirect header. + * And we currently need one SGE per page. + * 2. The number of negotiated payload bytes per send. + */ + possible_vecs = min_t(size_t, ARRAY_SIZE(vecs), niovs - iov_idx); + + while (iov_idx < niovs && possible_vecs && possible_bytes) { + struct kvec *v = &vecs[nvecs]; + int page_count; + + v->iov_base = ((u8 *)iov[iov_idx].iov_base) + iov_ofs; + v->iov_len = min_t(size_t, + iov[iov_idx].iov_len - iov_ofs, + possible_bytes); + page_count = get_buf_page_count(v->iov_base, v->iov_len); + if (page_count > possible_vecs) { + /* + * If the number of pages in the buffer + * is to much (because we currently require + * one SGE per page), we need to limit the + * length. + * + * We know possible_vecs is at least 1, + * so we always keep the first page. + * + * We need to calculate the number extra + * pages (epages) we can also keep. + * + * We calculate the number of bytes in the + * first page (fplen), this should never be + * larger than v->iov_len because page_count is + * at least 2, but adding a limitation feels + * better. + * + * Then we calculate the number of bytes (elen) + * we can keep for the extra pages. + */ + size_t epages = possible_vecs - 1; + size_t fpofs = offset_in_page(v->iov_base); + size_t fplen = min_t(size_t, PAGE_SIZE - fpofs, v->iov_len); + size_t elen = min_t(size_t, v->iov_len - fplen, epages*PAGE_SIZE); + + v->iov_len = fplen + elen; + page_count = get_buf_page_count(v->iov_base, v->iov_len); + if (WARN_ON_ONCE(page_count > possible_vecs)) { + /* + * Something went wrong in the above + * logic... + */ + error = -EINVAL; goto done; - } else { - /* iov[start] is too big, break it */ - int nvec = (buflen + max_iov_size - 1) / - max_iov_size; - - for (j = 0; j < nvec; j++) { - vec.iov_base = - (char *)iov[start].iov_base + - j * max_iov_size; - vec.iov_len = - min_t(int, max_iov_size, - buflen - max_iov_size * j); - remaining_data_length -= vec.iov_len; - ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, - remaining_data_length); - if (ret) - goto done; } - i++; - if (i == niovs) - break; } - start = i; - buflen = 0; - } else { - i++; - if (i == niovs) { - /* send out all remaining vecs */ - remaining_data_length -= buflen; - ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); - if (ret) - goto done; - break; + possible_vecs -= page_count; + nvecs += 1; + possible_bytes -= v->iov_len; + bytes += v->iov_len; + + iov_ofs += v->iov_len; + if (iov_ofs >= iov[iov_idx].iov_len) { + iov_idx += 1; + iov_ofs = 0; } } + + remaining_data_length -= bytes; + + ret = smb_direct_post_send_data(sc, &send_ctx, + vecs, nvecs, + remaining_data_length); + if (unlikely(ret)) { + error = ret; + goto done; + } } done: - ret = smb_direct_flush_send_list(st, &send_ctx, true); + ret = smb_direct_flush_send_list(sc, &send_ctx, true); + if (unlikely(!ret && error)) + ret = error; /* * As an optimization, we don't wait for individual I/O to finish @@ -1310,16 +1509,22 @@ done: * that means all the I/Os have been out and we are good to return */ - wait_event(st->wait_send_pending, - atomic_read(&st->send_pending) == 0); + wait_event(sc->send_io.pending.zero_wait_queue, + atomic_read(&sc->send_io.pending.count) == 0 || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + if (sc->status != SMBDIRECT_SOCKET_CONNECTED && ret == 0) + ret = -ENOTCONN; + return ret; } static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, - struct smb_direct_rdma_rw_msg *msg, + struct smbdirect_rw_io *msg, enum dma_data_direction dir) { - rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + struct smbdirect_socket *sc = &t->socket; + + rdma_rw_ctx_destroy(&msg->rdma_ctx, sc->ib.qp, sc->ib.qp->port, msg->sgt.sgl, msg->sgt.nents, dir); sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); kfree(msg); @@ -1328,16 +1533,16 @@ static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, enum dma_data_direction dir) { - struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, - struct smb_direct_rdma_rw_msg, cqe); - struct smb_direct_transport *t = msg->t; + struct smbdirect_rw_io *msg = + container_of(wc->wr_cqe, struct smbdirect_rw_io, cqe); + struct smbdirect_socket *sc = msg->socket; if (wc->status != IB_WC_SUCCESS) { - msg->status = -EIO; + msg->error = -EIO; pr_err("read/write error. opcode = %d, status = %s(%d)\n", wc->opcode, ib_wc_status_msg(wc->status), wc->status); if (wc->status != IB_WC_WR_FLUSH_ERR) - smb_direct_disconnect_rdma_connection(t); + smb_direct_disconnect_rdma_connection(sc); } complete(msg->completion); @@ -1355,49 +1560,62 @@ static void write_done(struct ib_cq *cq, struct ib_wc *wc) static int smb_direct_rdma_xmit(struct smb_direct_transport *t, void *buf, int buf_len, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len, bool is_read) { - struct smb_direct_rdma_rw_msg *msg, *next_msg; + struct smbdirect_socket *sc = &t->socket; + struct smbdirect_socket_parameters *sp = &sc->parameters; + struct smbdirect_rw_io *msg, *next_msg; int i, ret; DECLARE_COMPLETION_ONSTACK(completion); struct ib_send_wr *first_wr; LIST_HEAD(msg_list); char *desc_buf; int credits_needed; - unsigned int desc_buf_len; - size_t total_length = 0; + unsigned int desc_buf_len, desc_num = 0; - if (t->status != SMB_DIRECT_CS_CONNECTED) + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) return -ENOTCONN; + if (buf_len > sp->max_read_write_size) + return -EINVAL; + /* calculate needed credits */ credits_needed = 0; desc_buf = buf; for (i = 0; i < desc_len / sizeof(*desc); i++) { + if (!buf_len) + break; + desc_buf_len = le32_to_cpu(desc[i].length); + if (!desc_buf_len) + return -EINVAL; + + if (desc_buf_len > buf_len) { + desc_buf_len = buf_len; + desc[i].length = cpu_to_le32(desc_buf_len); + buf_len = 0; + } - credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len); + credits_needed += calc_rw_credits(sc, desc_buf, desc_buf_len); desc_buf += desc_buf_len; - total_length += desc_buf_len; - if (desc_buf_len == 0 || total_length > buf_len || - total_length > t->max_rdma_rw_size) - return -EINVAL; + buf_len -= desc_buf_len; + desc_num++; } ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n", - is_read ? "read" : "write", buf_len, credits_needed); + str_read_write(is_read), buf_len, credits_needed); - ret = wait_for_rw_credits(t, credits_needed); + ret = wait_for_rw_credits(sc, credits_needed); if (ret < 0) return ret; /* build rdma_rw_ctx for each descriptor */ desc_buf = buf; - for (i = 0; i < desc_len / sizeof(*desc); i++) { - msg = kzalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + - sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + for (i = 0; i < desc_num; i++) { + msg = kzalloc(struct_size(msg, sg_list, SG_CHUNK_SIZE), + KSMBD_DEFAULT_GFP); if (!msg) { ret = -ENOMEM; goto out; @@ -1405,7 +1623,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, desc_buf_len = le32_to_cpu(desc[i].length); - msg->t = t; + msg->socket = sc; msg->cqe.done = is_read ? read_done : write_done; msg->completion = &completion; @@ -1414,20 +1632,16 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, get_buf_page_count(desc_buf, desc_buf_len), msg->sg_list, SG_CHUNK_SIZE); if (ret) { - kfree(msg); ret = -ENOMEM; - goto out; + goto free_msg; } ret = get_sg_list(desc_buf, desc_buf_len, msg->sgt.sgl, msg->sgt.orig_nents); - if (ret < 0) { - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); - goto out; - } + if (ret < 0) + goto free_table; - ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, + ret = rdma_rw_ctx_init(&msg->rdma_ctx, sc->ib.qp, sc->ib.qp->port, msg->sgt.sgl, get_buf_page_count(desc_buf, desc_buf_len), 0, @@ -1436,9 +1650,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); if (ret < 0) { pr_err("failed to init rdma_rw_ctx: %d\n", ret); - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); - goto out; + goto free_table; } list_add_tail(&msg->list, &msg_list); @@ -1448,96 +1660,111 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t, /* concatenate work requests of rdma_rw_ctxs */ first_wr = NULL; list_for_each_entry_reverse(msg, &msg_list, list) { - first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, + first_wr = rdma_rw_ctx_wrs(&msg->rdma_ctx, sc->ib.qp, sc->ib.qp->port, &msg->cqe, first_wr); } - ret = ib_post_send(t->qp, first_wr, NULL); + ret = ib_post_send(sc->ib.qp, first_wr, NULL); if (ret) { pr_err("failed to post send wr for RDMA R/W: %d\n", ret); goto out; } - msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list); + msg = list_last_entry(&msg_list, struct smbdirect_rw_io, list); wait_for_completion(&completion); - ret = msg->status; + ret = msg->error; out: list_for_each_entry_safe(msg, next_msg, &msg_list, list) { list_del(&msg->list); smb_direct_free_rdma_rw_msg(t, msg, is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } - atomic_add(credits_needed, &t->rw_credits); - wake_up(&t->wait_rw_credits); + atomic_add(credits_needed, &sc->rw_io.credits.count); + wake_up(&sc->rw_io.credits.wait_queue); return ret; + +free_table: + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); +free_msg: + kfree(msg); + goto out; } static int smb_direct_rdma_write(struct ksmbd_transport *t, void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len) { - return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + return smb_direct_rdma_xmit(SMBD_TRANS(t), buf, buflen, desc, desc_len, false); } static int smb_direct_rdma_read(struct ksmbd_transport *t, void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len) { - return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + return smb_direct_rdma_xmit(SMBD_TRANS(t), buf, buflen, desc, desc_len, true); } static void smb_direct_disconnect(struct ksmbd_transport *t) { - struct smb_direct_transport *st = smb_trans_direct_transfort(t); + struct smb_direct_transport *st = SMBD_TRANS(t); + struct smbdirect_socket *sc = &st->socket; - ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); + ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", sc->rdma.cm_id); - smb_direct_disconnect_rdma_work(&st->disconnect_work); - wait_event_interruptible(st->wait_status, - st->status == SMB_DIRECT_CS_DISCONNECTED); free_transport(st); } static void smb_direct_shutdown(struct ksmbd_transport *t) { - struct smb_direct_transport *st = smb_trans_direct_transfort(t); + struct smb_direct_transport *st = SMBD_TRANS(t); + struct smbdirect_socket *sc = &st->socket; - ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id); + ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", sc->rdma.cm_id); - smb_direct_disconnect_rdma_work(&st->disconnect_work); + smb_direct_disconnect_rdma_work(&sc->disconnect_work); } static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, struct rdma_cm_event *event) { - struct smb_direct_transport *t = cm_id->context; + struct smbdirect_socket *sc = cm_id->context; ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", cm_id, rdma_event_msg(event->event), event->event); switch (event->event) { case RDMA_CM_EVENT_ESTABLISHED: { - t->status = SMB_DIRECT_CS_CONNECTED; - wake_up_interruptible(&t->wait_status); + /* + * Some drivers (at least mlx5_ib) might post a + * recv completion before RDMA_CM_EVENT_ESTABLISHED, + * we need to adjust our expectation in that case. + * + * As we already started the negotiation, we just + * ignore RDMA_CM_EVENT_ESTABLISHED here. + */ + if (!sc->first_error && sc->status > SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING) + break; + if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)) + break; + sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED; + wake_up(&sc->status_wait); break; } case RDMA_CM_EVENT_DEVICE_REMOVAL: case RDMA_CM_EVENT_DISCONNECTED: { - ib_drain_qp(t->qp); - - t->status = SMB_DIRECT_CS_DISCONNECTED; - wake_up_interruptible(&t->wait_status); - wake_up_interruptible(&t->wait_reassembly_queue); - wake_up(&t->wait_send_credits); + sc->status = SMBDIRECT_SOCKET_DISCONNECTED; + smb_direct_disconnect_rdma_work(&sc->disconnect_work); + if (sc->ib.qp) + ib_drain_qp(sc->ib.qp); break; } case RDMA_CM_EVENT_CONNECT_ERROR: { - t->status = SMB_DIRECT_CS_DISCONNECTED; - wake_up_interruptible(&t->wait_status); + sc->status = SMBDIRECT_SOCKET_DISCONNECTED; + smb_direct_disconnect_rdma_work(&sc->disconnect_work); break; } default: @@ -1551,38 +1778,41 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, static void smb_direct_qpair_handler(struct ib_event *event, void *context) { - struct smb_direct_transport *t = context; + struct smbdirect_socket *sc = context; ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", - t->cm_id, ib_event_msg(event->event), event->event); + sc->rdma.cm_id, ib_event_msg(event->event), event->event); switch (event->event) { case IB_EVENT_CQ_ERR: case IB_EVENT_QP_FATAL: - smb_direct_disconnect_rdma_connection(t); + smb_direct_disconnect_rdma_connection(sc); break; default: break; } } -static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, +static int smb_direct_send_negotiate_response(struct smbdirect_socket *sc, int failed) { - struct smb_direct_sendmsg *sendmsg; - struct smb_direct_negotiate_resp *resp; + struct smbdirect_socket_parameters *sp = &sc->parameters; + struct smbdirect_send_io *sendmsg; + struct smbdirect_negotiate_resp *resp; int ret; - sendmsg = smb_direct_alloc_sendmsg(t); + sendmsg = smb_direct_alloc_sendmsg(sc); if (IS_ERR(sendmsg)) return -ENOMEM; - resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; + resp = (struct smbdirect_negotiate_resp *)sendmsg->packet; if (failed) { memset(resp, 0, sizeof(*resp)); - resp->min_version = cpu_to_le16(0x0100); - resp->max_version = cpu_to_le16(0x0100); + resp->min_version = SMB_DIRECT_VERSION_LE; + resp->max_version = SMB_DIRECT_VERSION_LE; resp->status = STATUS_NOT_SUPPORTED; + + sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED; } else { resp->status = STATUS_SUCCESS; resp->min_version = SMB_DIRECT_VERSION_LE; @@ -1590,57 +1820,65 @@ static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, resp->negotiated_version = SMB_DIRECT_VERSION_LE; resp->reserved = 0; resp->credits_requested = - cpu_to_le16(t->send_credit_target); - resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); - resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); - resp->preferred_send_size = cpu_to_le32(t->max_send_size); - resp->max_receive_size = cpu_to_le32(t->max_recv_size); + cpu_to_le16(sp->send_credit_target); + resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc)); + resp->max_readwrite_size = cpu_to_le32(sp->max_read_write_size); + resp->preferred_send_size = cpu_to_le32(sp->max_send_size); + resp->max_receive_size = cpu_to_le32(sp->max_recv_size); resp->max_fragmented_size = - cpu_to_le32(t->max_fragmented_recv_size); + cpu_to_le32(sp->max_fragmented_recv_size); + + sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER; + sc->status = SMBDIRECT_SOCKET_CONNECTED; } - sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + sendmsg->sge[0].addr = ib_dma_map_single(sc->ib.dev, (void *)resp, sizeof(*resp), DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + ret = ib_dma_mapping_error(sc->ib.dev, sendmsg->sge[0].addr); if (ret) { - smb_direct_free_sendmsg(t, sendmsg); + smb_direct_free_sendmsg(sc, sendmsg); return ret; } sendmsg->num_sge = 1; sendmsg->sge[0].length = sizeof(*resp); - sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + sendmsg->sge[0].lkey = sc->ib.pd->local_dma_lkey; - ret = post_sendmsg(t, NULL, sendmsg); + ret = post_sendmsg(sc, NULL, sendmsg); if (ret) { - smb_direct_free_sendmsg(t, sendmsg); + smb_direct_free_sendmsg(sc, sendmsg); return ret; } - wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); + wait_event(sc->send_io.pending.zero_wait_queue, + atomic_read(&sc->send_io.pending.count) == 0 || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + return -ENOTCONN; + return 0; } -static int smb_direct_accept_client(struct smb_direct_transport *t) +static int smb_direct_accept_client(struct smbdirect_socket *sc) { + struct smbdirect_socket_parameters *sp = &sc->parameters; struct rdma_conn_param conn_param; - struct ib_port_immutable port_immutable; - u32 ird_ord_hdr[2]; + __be32 ird_ord_hdr[2]; int ret; + /* + * smb_direct_handle_connect_request() + * already negotiated sp->initiator_depth + * and sp->responder_resources + */ memset(&conn_param, 0, sizeof(conn_param)); - conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, - SMB_DIRECT_CM_INITIATOR_DEPTH); - conn_param.responder_resources = 0; - - t->cm_id->device->ops.get_port_immutable(t->cm_id->device, - t->cm_id->port_num, - &port_immutable); - if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { - ird_ord_hdr[0] = conn_param.responder_resources; - ird_ord_hdr[1] = 1; + conn_param.initiator_depth = sp->initiator_depth; + conn_param.responder_resources = sp->responder_resources; + + if (sc->rdma.legacy_iwarp) { + ird_ord_hdr[0] = cpu_to_be32(conn_param.responder_resources); + ird_ord_hdr[1] = cpu_to_be32(conn_param.initiator_depth); conn_param.private_data = ird_ord_hdr; conn_param.private_data_len = sizeof(ird_ord_hdr); } else { @@ -1651,7 +1889,17 @@ static int smb_direct_accept_client(struct smb_direct_transport *t) conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; conn_param.flow_control = 0; - ret = rdma_accept(t->cm_id, &conn_param); + /* + * start with the negotiate timeout and SMBDIRECT_KEEPALIVE_PENDING + * so that the timer will cause a disconnect. + */ + sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING; + mod_delayed_work(sc->workqueue, &sc->idle.timer_work, + msecs_to_jiffies(sp->negotiate_timeout_msec)); + + WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED); + sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING; + ret = rdma_accept(sc->rdma.cm_id, &conn_param); if (ret) { pr_err("error at rdma_accept: %d\n", ret); return ret; @@ -1659,358 +1907,459 @@ static int smb_direct_accept_client(struct smb_direct_transport *t) return 0; } -static int smb_direct_prepare_negotiation(struct smb_direct_transport *t) +static int smb_direct_prepare_negotiation(struct smbdirect_socket *sc) { + struct smbdirect_recv_io *recvmsg; + bool recv_posted = false; int ret; - struct smb_direct_recvmsg *recvmsg; - recvmsg = get_free_recvmsg(t); + WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_CREATED); + sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED; + + sc->recv_io.expected = SMBDIRECT_EXPECT_NEGOTIATE_REQ; + + recvmsg = get_free_recvmsg(sc); if (!recvmsg) return -ENOMEM; - recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; - ret = smb_direct_post_recv(t, recvmsg); + ret = smb_direct_post_recv(sc, recvmsg); if (ret) { pr_err("Can't post recv: %d\n", ret); goto out_err; } + recv_posted = true; - t->negotiation_requested = false; - ret = smb_direct_accept_client(t); + ret = smb_direct_accept_client(sc); if (ret) { pr_err("Can't accept client\n"); goto out_err; } - smb_direct_post_recv_credits(&t->post_recv_credits_work.work); return 0; out_err: - put_recvmsg(t, recvmsg); + /* + * If the recv was never posted, return it to the free list. + * If it was posted, leave it alone so disconnect teardown can + * drain the QP and complete it (flush) and the completion path + * will unmap it exactly once. + */ + if (!recv_posted) + put_recvmsg(sc, recvmsg); return ret; } -static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t) +static int smb_direct_init_params(struct smbdirect_socket *sc) { - return min_t(unsigned int, - t->cm_id->device->attrs.max_fast_reg_page_list_len, - 256); -} - -static int smb_direct_init_params(struct smb_direct_transport *t, - struct ib_qp_cap *cap) -{ - struct ib_device *device = t->cm_id->device; - int max_send_sges, max_rw_wrs, max_send_wrs; - unsigned int max_sge_per_wr, wrs_per_credit; + struct smbdirect_socket_parameters *sp = &sc->parameters; + int max_send_sges; + unsigned int maxpages; /* need 3 more sge. because a SMB_DIRECT header, SMB2 header, * SMB2 response could be mapped. */ - t->max_send_size = smb_direct_max_send_size; - max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 3; - if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { - pr_err("max_send_size %d is too large\n", t->max_send_size); - return -EINVAL; - } - - /* Calculate the number of work requests for RDMA R/W. - * The maximum number of pages which can be registered - * with one Memory region can be transferred with one - * R/W credit. And at least 4 work requests for each credit - * are needed for MR registration, RDMA R/W, local & remote - * MR invalidation. - */ - t->max_rdma_rw_size = smb_direct_max_read_write_size; - t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t); - t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size, - (t->pages_per_rw_credit - 1) * - PAGE_SIZE); - - max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge, - device->attrs.max_sge_rd); - max_sge_per_wr = max_t(unsigned int, max_sge_per_wr, - max_send_sges); - wrs_per_credit = max_t(unsigned int, 4, - DIV_ROUND_UP(t->pages_per_rw_credit, - max_sge_per_wr) + 1); - max_rw_wrs = t->max_rw_credits * wrs_per_credit; - - max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; - if (max_send_wrs > device->attrs.max_cqe || - max_send_wrs > device->attrs.max_qp_wr) { - pr_err("consider lowering send_credit_target = %d\n", - smb_direct_send_credit_target); - pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); - return -EINVAL; - } - - if (smb_direct_receive_credit_max > device->attrs.max_cqe || - smb_direct_receive_credit_max > device->attrs.max_qp_wr) { - pr_err("consider lowering receive_credit_max = %d\n", - smb_direct_receive_credit_max); - pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); + max_send_sges = DIV_ROUND_UP(sp->max_send_size, PAGE_SIZE) + 3; + if (max_send_sges > SMBDIRECT_SEND_IO_MAX_SGE) { + pr_err("max_send_size %d is too large\n", sp->max_send_size); return -EINVAL; } - if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { - pr_err("warning: device max_recv_sge = %d too small\n", - device->attrs.max_recv_sge); - return -EINVAL; - } - - t->recv_credits = 0; - t->count_avail_recvmsg = 0; + atomic_set(&sc->send_io.lcredits.count, sp->send_credit_target); - t->recv_credit_max = smb_direct_receive_credit_max; - t->recv_credit_target = 10; - t->new_recv_credits = 0; + maxpages = DIV_ROUND_UP(sp->max_read_write_size, PAGE_SIZE); + sc->rw_io.credits.max = rdma_rw_mr_factor(sc->ib.dev, + sc->rdma.cm_id->port_num, + maxpages); + sc->rw_io.credits.num_pages = DIV_ROUND_UP(maxpages, sc->rw_io.credits.max); + /* add one extra in order to handle unaligned pages */ + sc->rw_io.credits.max += 1; - t->send_credit_target = smb_direct_send_credit_target; - atomic_set(&t->send_credits, 0); - atomic_set(&t->rw_credits, t->max_rw_credits); + sc->recv_io.credits.target = 1; - t->max_send_size = smb_direct_max_send_size; - t->max_recv_size = smb_direct_max_receive_size; - t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + atomic_set(&sc->rw_io.credits.count, sc->rw_io.credits.max); - cap->max_send_wr = max_send_wrs; - cap->max_recv_wr = t->recv_credit_max; - cap->max_send_sge = max_sge_per_wr; - cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; - cap->max_inline_data = 0; - cap->max_rdma_ctxs = t->max_rw_credits; return 0; } -static void smb_direct_destroy_pools(struct smb_direct_transport *t) +static void smb_direct_destroy_pools(struct smbdirect_socket *sc) { - struct smb_direct_recvmsg *recvmsg; + struct smbdirect_recv_io *recvmsg; - while ((recvmsg = get_free_recvmsg(t))) - mempool_free(recvmsg, t->recvmsg_mempool); - while ((recvmsg = get_empty_recvmsg(t))) - mempool_free(recvmsg, t->recvmsg_mempool); + while ((recvmsg = get_free_recvmsg(sc))) + mempool_free(recvmsg, sc->recv_io.mem.pool); - mempool_destroy(t->recvmsg_mempool); - t->recvmsg_mempool = NULL; + mempool_destroy(sc->recv_io.mem.pool); + sc->recv_io.mem.pool = NULL; - kmem_cache_destroy(t->recvmsg_cache); - t->recvmsg_cache = NULL; + kmem_cache_destroy(sc->recv_io.mem.cache); + sc->recv_io.mem.cache = NULL; - mempool_destroy(t->sendmsg_mempool); - t->sendmsg_mempool = NULL; + mempool_destroy(sc->send_io.mem.pool); + sc->send_io.mem.pool = NULL; - kmem_cache_destroy(t->sendmsg_cache); - t->sendmsg_cache = NULL; + kmem_cache_destroy(sc->send_io.mem.cache); + sc->send_io.mem.cache = NULL; } -static int smb_direct_create_pools(struct smb_direct_transport *t) +static int smb_direct_create_pools(struct smbdirect_socket *sc) { + struct smbdirect_socket_parameters *sp = &sc->parameters; char name[80]; int i; - struct smb_direct_recvmsg *recvmsg; + struct smbdirect_recv_io *recvmsg; - snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); - t->sendmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_sendmsg) + - sizeof(struct smb_direct_negotiate_resp), + snprintf(name, sizeof(name), "smbdirect_send_io_pool_%p", sc); + sc->send_io.mem.cache = kmem_cache_create(name, + sizeof(struct smbdirect_send_io) + + sizeof(struct smbdirect_negotiate_resp), 0, SLAB_HWCACHE_ALIGN, NULL); - if (!t->sendmsg_cache) + if (!sc->send_io.mem.cache) return -ENOMEM; - t->sendmsg_mempool = mempool_create(t->send_credit_target, + sc->send_io.mem.pool = mempool_create(sp->send_credit_target, mempool_alloc_slab, mempool_free_slab, - t->sendmsg_cache); - if (!t->sendmsg_mempool) + sc->send_io.mem.cache); + if (!sc->send_io.mem.pool) goto err; - snprintf(name, sizeof(name), "smb_direct_resp_%p", t); - t->recvmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_recvmsg) + - t->max_recv_size, + snprintf(name, sizeof(name), "smbdirect_recv_io_pool_%p", sc); + sc->recv_io.mem.cache = kmem_cache_create(name, + sizeof(struct smbdirect_recv_io) + + sp->max_recv_size, 0, SLAB_HWCACHE_ALIGN, NULL); - if (!t->recvmsg_cache) + if (!sc->recv_io.mem.cache) goto err; - t->recvmsg_mempool = - mempool_create(t->recv_credit_max, mempool_alloc_slab, - mempool_free_slab, t->recvmsg_cache); - if (!t->recvmsg_mempool) + sc->recv_io.mem.pool = + mempool_create(sp->recv_credit_max, mempool_alloc_slab, + mempool_free_slab, sc->recv_io.mem.cache); + if (!sc->recv_io.mem.pool) goto err; - INIT_LIST_HEAD(&t->recvmsg_queue); - - for (i = 0; i < t->recv_credit_max; i++) { - recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + for (i = 0; i < sp->recv_credit_max; i++) { + recvmsg = mempool_alloc(sc->recv_io.mem.pool, KSMBD_DEFAULT_GFP); if (!recvmsg) goto err; - recvmsg->transport = t; - list_add(&recvmsg->list, &t->recvmsg_queue); + recvmsg->socket = sc; + recvmsg->sge.length = 0; + list_add(&recvmsg->list, &sc->recv_io.free.list); } - t->count_avail_recvmsg = t->recv_credit_max; return 0; err: - smb_direct_destroy_pools(t); + smb_direct_destroy_pools(sc); return -ENOMEM; } -static int smb_direct_create_qpair(struct smb_direct_transport *t, - struct ib_qp_cap *cap) +static u32 smb_direct_rdma_rw_send_wrs(struct ib_device *dev, const struct ib_qp_init_attr *attr) +{ + /* + * This could be split out of rdma_rw_init_qp() + * and be a helper function next to rdma_rw_mr_factor() + * + * We can't check unlikely(rdma_rw_force_mr) here, + * but that is most likely 0 anyway. + */ + u32 factor; + + WARN_ON_ONCE(attr->port_num == 0); + + /* + * Each context needs at least one RDMA READ or WRITE WR. + * + * For some hardware we might need more, eventually we should ask the + * HCA driver for a multiplier here. + */ + factor = 1; + + /* + * If the device needs MRs to perform RDMA READ or WRITE operations, + * we'll need two additional MRs for the registrations and the + * invalidation. + */ + if (rdma_protocol_iwarp(dev, attr->port_num) || dev->attrs.max_sgl_rd) + factor += 2; /* inv + reg */ + + return factor * attr->cap.max_rdma_ctxs; +} + +static int smb_direct_create_qpair(struct smbdirect_socket *sc) { + struct smbdirect_socket_parameters *sp = &sc->parameters; int ret; + struct ib_qp_cap qp_cap; struct ib_qp_init_attr qp_attr; - int pages_per_rw; + u32 max_send_wr; + u32 rdma_send_wr; - t->pd = ib_alloc_pd(t->cm_id->device, 0); - if (IS_ERR(t->pd)) { + /* + * Note that {rdma,ib}_create_qp() will call + * rdma_rw_init_qp() if cap->max_rdma_ctxs is not 0. + * It will adjust cap->max_send_wr to the required + * number of additional WRs for the RDMA RW operations. + * It will cap cap->max_send_wr to the device limit. + * + * +1 for ib_drain_qp + */ + qp_cap.max_send_wr = sp->send_credit_target + 1; + qp_cap.max_recv_wr = sp->recv_credit_max + 1; + qp_cap.max_send_sge = SMBDIRECT_SEND_IO_MAX_SGE; + qp_cap.max_recv_sge = SMBDIRECT_RECV_IO_MAX_SGE; + qp_cap.max_inline_data = 0; + qp_cap.max_rdma_ctxs = sc->rw_io.credits.max; + + /* + * Find out the number of max_send_wr + * after rdma_rw_init_qp() adjusted it. + * + * We only do it on a temporary variable, + * as rdma_create_qp() will trigger + * rdma_rw_init_qp() again. + */ + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.cap = qp_cap; + qp_attr.port_num = sc->rdma.cm_id->port_num; + rdma_send_wr = smb_direct_rdma_rw_send_wrs(sc->ib.dev, &qp_attr); + max_send_wr = qp_cap.max_send_wr + rdma_send_wr; + + if (qp_cap.max_send_wr > sc->ib.dev->attrs.max_cqe || + qp_cap.max_send_wr > sc->ib.dev->attrs.max_qp_wr) { + pr_err("Possible CQE overrun: max_send_wr %d\n", + qp_cap.max_send_wr); + pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n", + IB_DEVICE_NAME_MAX, + sc->ib.dev->name, + sc->ib.dev->attrs.max_cqe, + sc->ib.dev->attrs.max_qp_wr); + pr_err("consider lowering send_credit_target = %d\n", + sp->send_credit_target); + return -EINVAL; + } + + if (qp_cap.max_rdma_ctxs && + (max_send_wr >= sc->ib.dev->attrs.max_cqe || + max_send_wr >= sc->ib.dev->attrs.max_qp_wr)) { + pr_err("Possible CQE overrun: rdma_send_wr %d + max_send_wr %d = %d\n", + rdma_send_wr, qp_cap.max_send_wr, max_send_wr); + pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n", + IB_DEVICE_NAME_MAX, + sc->ib.dev->name, + sc->ib.dev->attrs.max_cqe, + sc->ib.dev->attrs.max_qp_wr); + pr_err("consider lowering send_credit_target = %d, max_rdma_ctxs = %d\n", + sp->send_credit_target, qp_cap.max_rdma_ctxs); + return -EINVAL; + } + + if (qp_cap.max_recv_wr > sc->ib.dev->attrs.max_cqe || + qp_cap.max_recv_wr > sc->ib.dev->attrs.max_qp_wr) { + pr_err("Possible CQE overrun: max_recv_wr %d\n", + qp_cap.max_recv_wr); + pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n", + IB_DEVICE_NAME_MAX, + sc->ib.dev->name, + sc->ib.dev->attrs.max_cqe, + sc->ib.dev->attrs.max_qp_wr); + pr_err("consider lowering receive_credit_max = %d\n", + sp->recv_credit_max); + return -EINVAL; + } + + if (qp_cap.max_send_sge > sc->ib.dev->attrs.max_send_sge || + qp_cap.max_recv_sge > sc->ib.dev->attrs.max_recv_sge) { + pr_err("device %.*s max_send_sge/max_recv_sge = %d/%d too small\n", + IB_DEVICE_NAME_MAX, + sc->ib.dev->name, + sc->ib.dev->attrs.max_send_sge, + sc->ib.dev->attrs.max_recv_sge); + return -EINVAL; + } + + sc->ib.pd = ib_alloc_pd(sc->ib.dev, 0); + if (IS_ERR(sc->ib.pd)) { pr_err("Can't create RDMA PD\n"); - ret = PTR_ERR(t->pd); - t->pd = NULL; + ret = PTR_ERR(sc->ib.pd); + sc->ib.pd = NULL; return ret; } - t->send_cq = ib_alloc_cq(t->cm_id->device, t, - smb_direct_send_credit_target + cap->max_rdma_ctxs, - 0, IB_POLL_WORKQUEUE); - if (IS_ERR(t->send_cq)) { + sc->ib.send_cq = ib_alloc_cq_any(sc->ib.dev, sc, + max_send_wr, + IB_POLL_WORKQUEUE); + if (IS_ERR(sc->ib.send_cq)) { pr_err("Can't create RDMA send CQ\n"); - ret = PTR_ERR(t->send_cq); - t->send_cq = NULL; + ret = PTR_ERR(sc->ib.send_cq); + sc->ib.send_cq = NULL; goto err; } - t->recv_cq = ib_alloc_cq(t->cm_id->device, t, - t->recv_credit_max, 0, IB_POLL_WORKQUEUE); - if (IS_ERR(t->recv_cq)) { + sc->ib.recv_cq = ib_alloc_cq_any(sc->ib.dev, sc, + qp_cap.max_recv_wr, + IB_POLL_WORKQUEUE); + if (IS_ERR(sc->ib.recv_cq)) { pr_err("Can't create RDMA recv CQ\n"); - ret = PTR_ERR(t->recv_cq); - t->recv_cq = NULL; + ret = PTR_ERR(sc->ib.recv_cq); + sc->ib.recv_cq = NULL; goto err; } + /* + * We reset completely here! + * As the above use was just temporary + * to calc max_send_wr and rdma_send_wr. + * + * rdma_create_qp() will trigger rdma_rw_init_qp() + * again if max_rdma_ctxs is not 0. + */ memset(&qp_attr, 0, sizeof(qp_attr)); qp_attr.event_handler = smb_direct_qpair_handler; - qp_attr.qp_context = t; - qp_attr.cap = *cap; + qp_attr.qp_context = sc; + qp_attr.cap = qp_cap; qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; qp_attr.qp_type = IB_QPT_RC; - qp_attr.send_cq = t->send_cq; - qp_attr.recv_cq = t->recv_cq; + qp_attr.send_cq = sc->ib.send_cq; + qp_attr.recv_cq = sc->ib.recv_cq; qp_attr.port_num = ~0; - ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); + ret = rdma_create_qp(sc->rdma.cm_id, sc->ib.pd, &qp_attr); if (ret) { pr_err("Can't create RDMA QP: %d\n", ret); goto err; } - t->qp = t->cm_id->qp; - t->cm_id->event_handler = smb_direct_cm_handler; - - pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; - if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) { - ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs, - t->max_rw_credits, IB_MR_TYPE_MEM_REG, - t->pages_per_rw_credit, 0); - if (ret) { - pr_err("failed to init mr pool count %d pages %d\n", - t->max_rw_credits, t->pages_per_rw_credit); - goto err; - } - } + sc->ib.qp = sc->rdma.cm_id->qp; + sc->rdma.cm_id->event_handler = smb_direct_cm_handler; return 0; err: - if (t->qp) { - ib_destroy_qp(t->qp); - t->qp = NULL; + if (sc->ib.qp) { + sc->ib.qp = NULL; + rdma_destroy_qp(sc->rdma.cm_id); } - if (t->recv_cq) { - ib_destroy_cq(t->recv_cq); - t->recv_cq = NULL; + if (sc->ib.recv_cq) { + ib_destroy_cq(sc->ib.recv_cq); + sc->ib.recv_cq = NULL; } - if (t->send_cq) { - ib_destroy_cq(t->send_cq); - t->send_cq = NULL; + if (sc->ib.send_cq) { + ib_destroy_cq(sc->ib.send_cq); + sc->ib.send_cq = NULL; } - if (t->pd) { - ib_dealloc_pd(t->pd); - t->pd = NULL; + if (sc->ib.pd) { + ib_dealloc_pd(sc->ib.pd); + sc->ib.pd = NULL; } return ret; } static int smb_direct_prepare(struct ksmbd_transport *t) { - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_negotiate_req *req; + struct smb_direct_transport *st = SMBD_TRANS(t); + struct smbdirect_socket *sc = &st->socket; + struct smbdirect_socket_parameters *sp = &sc->parameters; + struct smbdirect_recv_io *recvmsg; + struct smbdirect_negotiate_req *req; + unsigned long flags; int ret; + /* + * We are waiting to pass the following states: + * + * SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED + * SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING + * SMBDIRECT_SOCKET_NEGOTIATE_NEEDED + * + * To finally get to SMBDIRECT_SOCKET_NEGOTIATE_RUNNING + * in order to continue below. + * + * Everything else is unexpected and an error. + */ ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); - ret = wait_event_interruptible_timeout(st->wait_status, - st->negotiation_requested || - st->status == SMB_DIRECT_CS_DISCONNECTED, - SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); - if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED) + ret = wait_event_interruptible_timeout(sc->status_wait, + sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED && + sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING && + sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED, + msecs_to_jiffies(sp->negotiate_timeout_msec)); + if (ret <= 0 || sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING) return ret < 0 ? ret : -ETIMEDOUT; - recvmsg = get_first_reassembly(st); + recvmsg = get_first_reassembly(sc); if (!recvmsg) return -ECONNABORTED; ret = smb_direct_check_recvmsg(recvmsg); - if (ret == -ECONNABORTED) - goto out; + if (ret) + goto put; - req = (struct smb_direct_negotiate_req *)recvmsg->packet; - st->max_recv_size = min_t(int, st->max_recv_size, + req = (struct smbdirect_negotiate_req *)recvmsg->packet; + sp->max_recv_size = min_t(int, sp->max_recv_size, le32_to_cpu(req->preferred_send_size)); - st->max_send_size = min_t(int, st->max_send_size, + sp->max_send_size = min_t(int, sp->max_send_size, le32_to_cpu(req->max_receive_size)); - st->max_fragmented_send_size = + sp->max_fragmented_send_size = le32_to_cpu(req->max_fragmented_size); - st->max_fragmented_recv_size = - (st->recv_credit_max * st->max_recv_size) / 2; - - ret = smb_direct_send_negotiate_response(st, ret); -out: - spin_lock_irq(&st->reassembly_queue_lock); - st->reassembly_queue_length--; + sp->max_fragmented_recv_size = + (sp->recv_credit_max * sp->max_recv_size) / 2; + sc->recv_io.credits.target = le16_to_cpu(req->credits_requested); + sc->recv_io.credits.target = min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max); + sc->recv_io.credits.target = max_t(u16, sc->recv_io.credits.target, 1); + +put: + spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags); + sc->recv_io.reassembly.queue_length--; list_del(&recvmsg->list); - spin_unlock_irq(&st->reassembly_queue_lock); - put_recvmsg(st, recvmsg); + spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags); + put_recvmsg(sc, recvmsg); + + if (ret == -ECONNABORTED) + return ret; + + if (ret) + goto respond; + + /* + * We negotiated with success, so we need to refill the recv queue. + * We do that with sc->idle.immediate_work still being disabled + * via smbdirect_socket_init(), so that queue_work(sc->workqueue, + * &sc->idle.immediate_work) in smb_direct_post_recv_credits() + * is a no-op. + * + * The message that grants the credits to the client is + * the negotiate response. + */ + INIT_WORK(&sc->recv_io.posted.refill_work, smb_direct_post_recv_credits); + smb_direct_post_recv_credits(&sc->recv_io.posted.refill_work); + if (unlikely(sc->first_error)) + return sc->first_error; + INIT_WORK(&sc->idle.immediate_work, smb_direct_send_immediate_work); + +respond: + ret = smb_direct_send_negotiate_response(sc, ret); return ret; } -static int smb_direct_connect(struct smb_direct_transport *st) +static int smb_direct_connect(struct smbdirect_socket *sc) { int ret; - struct ib_qp_cap qp_cap; - ret = smb_direct_init_params(st, &qp_cap); + ret = smb_direct_init_params(sc); if (ret) { pr_err("Can't configure RDMA parameters\n"); return ret; } - ret = smb_direct_create_pools(st); + ret = smb_direct_create_pools(sc); if (ret) { pr_err("Can't init RDMA pool: %d\n", ret); return ret; } - ret = smb_direct_create_qpair(st, &qp_cap); + ret = smb_direct_create_qpair(sc); if (ret) { pr_err("Can't accept RDMA client: %d\n", ret); return ret; } - ret = smb_direct_prepare_negotiation(st); + ret = smb_direct_prepare_negotiation(sc); if (ret) { pr_err("Can't negotiate: %d\n", ret); return ret; @@ -2027,9 +2376,15 @@ static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) return true; } -static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) +static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id, + struct rdma_cm_event *event) { struct smb_direct_transport *t; + struct smbdirect_socket *sc; + struct smbdirect_socket_parameters *sp; + struct task_struct *handler; + u8 peer_initiator_depth; + u8 peer_responder_resources; int ret; if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { @@ -2042,16 +2397,79 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) t = alloc_transport(new_cm_id); if (!t) return -ENOMEM; + sc = &t->socket; + sp = &sc->parameters; + + peer_initiator_depth = event->param.conn.initiator_depth; + peer_responder_resources = event->param.conn.responder_resources; + if (rdma_protocol_iwarp(new_cm_id->device, new_cm_id->port_num) && + event->param.conn.private_data_len == 8) { + /* + * Legacy clients with only iWarp MPA v1 support + * need a private blob in order to negotiate + * the IRD/ORD values. + */ + const __be32 *ird_ord_hdr = event->param.conn.private_data; + u32 ird32 = be32_to_cpu(ird_ord_hdr[0]); + u32 ord32 = be32_to_cpu(ird_ord_hdr[1]); + + /* + * cifs.ko sends the legacy IRD/ORD negotiation + * event if iWarp MPA v2 was used. + * + * Here we check that the values match and only + * mark the client as legacy if they don't match. + */ + if ((u32)event->param.conn.initiator_depth != ird32 || + (u32)event->param.conn.responder_resources != ord32) { + /* + * There are broken clients (old cifs.ko) + * using little endian and also + * struct rdma_conn_param only uses u8 + * for initiator_depth and responder_resources, + * so we truncate the value to U8_MAX. + * + * smb_direct_accept_client() will then + * do the real negotiation in order to + * select the minimum between client and + * server. + */ + ird32 = min_t(u32, ird32, U8_MAX); + ord32 = min_t(u32, ord32, U8_MAX); - ret = smb_direct_connect(t); + sc->rdma.legacy_iwarp = true; + peer_initiator_depth = (u8)ird32; + peer_responder_resources = (u8)ord32; + } + } + + /* + * First set what the we as server are able to support + */ + sp->initiator_depth = min_t(u8, sp->initiator_depth, + new_cm_id->device->attrs.max_qp_rd_atom); + + /* + * negotiate the value by using the minimum + * between client and server if the client provided + * non 0 values. + */ + if (peer_initiator_depth != 0) + sp->initiator_depth = min_t(u8, sp->initiator_depth, + peer_initiator_depth); + if (peer_responder_resources != 0) + sp->responder_resources = min_t(u8, sp->responder_resources, + peer_responder_resources); + + ret = smb_direct_connect(sc); if (ret) goto out_err; - KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, "ksmbd:r%u", - smb_direct_port); - if (IS_ERR(KSMBD_TRANS(t)->handler)) { - ret = PTR_ERR(KSMBD_TRANS(t)->handler); + handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:r%u", + smb_direct_port); + if (IS_ERR(handler)) { + ret = PTR_ERR(handler); pr_err("Can't start thread\n"); goto out_err; } @@ -2067,7 +2485,7 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, { switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: { - int ret = smb_direct_handle_connect_request(cm_id); + int ret = smb_direct_handle_connect_request(cm_id, event); if (ret) { pr_err("Can't create transport: %d\n", ret); @@ -2131,11 +2549,10 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) if (ib_dev->node_type != RDMA_NODE_IB_CA) smb_direct_port = SMB_DIRECT_PORT_IWARP; - if (!ib_dev->ops.get_netdev || - !rdma_frwr_is_supported(&ib_dev->attrs)) + if (!rdma_frwr_is_supported(&ib_dev->attrs)) return 0; - smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); + smb_dev = kzalloc(sizeof(*smb_dev), KSMBD_DEFAULT_GFP); if (!smb_dev) return -ENOMEM; smb_dev->ib_dev = ib_dev; @@ -2188,7 +2605,8 @@ int ksmbd_rdma_init(void) * for lack of credits */ smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", - WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU, + 0); if (!smb_direct_wq) return -ENOMEM; @@ -2205,7 +2623,7 @@ int ksmbd_rdma_init(void) return 0; } -void ksmbd_rdma_destroy(void) +void ksmbd_rdma_stop_listening(void) { if (!smb_direct_listener.cm_id) return; @@ -2214,14 +2632,17 @@ void ksmbd_rdma_destroy(void) rdma_destroy_id(smb_direct_listener.cm_id); smb_direct_listener.cm_id = NULL; +} +void ksmbd_rdma_destroy(void) +{ if (smb_direct_wq) { destroy_workqueue(smb_direct_wq); smb_direct_wq = NULL; } } -bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +static bool ksmbd_find_rdma_capable_netdev(struct net_device *netdev) { struct smb_direct_device *smb_dev; int i; @@ -2232,8 +2653,7 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev) for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { struct net_device *ndev; - ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, - i + 1); + ndev = ib_device_get_netdev(smb_dev->ib_dev, i + 1); if (!ndev) continue; @@ -2253,16 +2673,40 @@ out: ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); if (ibdev) { - if (rdma_frwr_is_supported(&ibdev->attrs)) - rdma_capable = true; + rdma_capable = rdma_frwr_is_supported(&ibdev->attrs); ib_device_put(ibdev); } } + ksmbd_debug(RDMA, "netdev(%s) rdma capable : %s\n", + netdev->name, str_true_false(rdma_capable)); + return rdma_capable; } -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { +bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +{ + struct net_device *lower_dev; + struct list_head *iter; + + if (ksmbd_find_rdma_capable_netdev(netdev)) + return true; + + /* check if netdev is bridge or VLAN */ + if (netif_is_bridge_master(netdev) || + netdev->priv_flags & IFF_802_1Q_VLAN) + netdev_for_each_lower_dev(netdev, lower_dev, iter) + if (ksmbd_find_rdma_capable_netdev(lower_dev)) + return true; + + /* check if netdev is IPoIB safely without layer violation */ + if (netdev->type == ARPHRD_INFINIBAND) + return true; + + return false; +} + +static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { .prepare = smb_direct_prepare, .disconnect = smb_direct_disconnect, .shutdown = smb_direct_shutdown, @@ -2270,4 +2714,5 @@ static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { .read = smb_direct_read, .rdma_read = smb_direct_rdma_read, .rdma_write = smb_direct_rdma_write, + .free_transport = smb_direct_free_transport, }; diff --git a/fs/smb/server/transport_rdma.h b/fs/smb/server/transport_rdma.h index 77aee4e5c9dc..3f93c6a9f7e4 100644 --- a/fs/smb/server/transport_rdma.h +++ b/fs/smb/server/transport_rdma.h @@ -11,59 +11,20 @@ #define SMBD_MIN_IOSIZE (512 * 1024) #define SMBD_MAX_IOSIZE (16 * 1024 * 1024) -/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ -struct smb_direct_negotiate_req { - __le16 min_version; - __le16 max_version; - __le16 reserved; - __le16 credits_requested; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ -struct smb_direct_negotiate_resp { - __le16 min_version; - __le16 max_version; - __le16 negotiated_version; - __le16 reserved; - __le16 credits_requested; - __le16 credits_granted; - __le32 status; - __le32 max_readwrite_size; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 - -/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ -struct smb_direct_data_transfer { - __le16 credits_requested; - __le16 credits_granted; - __le16 flags; - __le16 reserved; - __le32 remaining_data_length; - __le32 data_offset; - __le32 data_length; - __le32 padding; - __u8 buffer[]; -} __packed; - #ifdef CONFIG_SMB_SERVER_SMBDIRECT int ksmbd_rdma_init(void); +void ksmbd_rdma_stop_listening(void); void ksmbd_rdma_destroy(void); bool ksmbd_rdma_capable_netdev(struct net_device *netdev); void init_smbd_max_io_size(unsigned int sz); -unsigned int get_smbd_max_read_write_size(void); +unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt); #else static inline int ksmbd_rdma_init(void) { return 0; } -static inline int ksmbd_rdma_destroy(void) { return 0; } +static inline void ksmbd_rdma_stop_listening(void) { } +static inline void ksmbd_rdma_destroy(void) { } static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } static inline void init_smbd_max_io_size(unsigned int sz) { } -static inline unsigned int get_smbd_max_read_write_size(void) { return 0; } +static inline unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { return 0; } #endif #endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index eff7a1d793f0..4bb07937d7ef 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -22,7 +22,6 @@ struct interface { struct socket *ksmbd_socket; struct list_head entry; char *name; - struct mutex sock_release_lock; int state; }; @@ -37,7 +36,7 @@ struct tcp_transport { unsigned int nr_iov; }; -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; +static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops; static void tcp_stop_kthread(struct task_struct *kthread); static struct interface *alloc_iface(char *ifname); @@ -56,27 +55,12 @@ static inline void ksmbd_tcp_reuseaddr(struct socket *sock) sock_set_reuseaddr(sock->sk); } -static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) -{ - lock_sock(sock->sk); - if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) - sock->sk->sk_rcvtimeo = secs * HZ; - else - sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; - release_sock(sock->sk); -} - -static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) -{ - sock_set_sndtimeo(sock->sk, secs); -} - static struct tcp_transport *alloc_transport(struct socket *client_sk) { struct tcp_transport *t; struct ksmbd_conn *conn; - t = kzalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc(sizeof(*t), KSMBD_DEFAULT_GFP); if (!t) return NULL; t->sock = client_sk; @@ -87,23 +71,43 @@ static struct tcp_transport *alloc_transport(struct socket *client_sk) return NULL; } +#if IS_ENABLED(CONFIG_IPV6) + if (client_sk->sk->sk_family == AF_INET6) { + memcpy(&conn->inet6_addr, &client_sk->sk->sk_v6_daddr, 16); + conn->inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr); + } else { + conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr; + conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); + } +#else + conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr; + conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); +#endif + down_write(&conn_list_lock); + hash_add(conn_list, &conn->hlist, conn->inet_hash); + up_write(&conn_list_lock); + conn->transport = KSMBD_TRANS(t); KSMBD_TRANS(t)->conn = conn; KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; return t; } -static void free_transport(struct tcp_transport *t) +static void ksmbd_tcp_free_transport(struct ksmbd_transport *kt) { - kernel_sock_shutdown(t->sock, SHUT_RDWR); - sock_release(t->sock); - t->sock = NULL; + struct tcp_transport *t = TCP_TRANS(kt); - ksmbd_conn_free(KSMBD_TRANS(t)->conn); + sock_release(t->sock); kfree(t->iov); kfree(t); } +static void free_transport(struct tcp_transport *t) +{ + kernel_sock_shutdown(t->sock, SHUT_RDWR); + ksmbd_conn_free(KSMBD_TRANS(t)->conn); +} + /** * kvec_array_init() - initialize a IO vector segment * @new: IO vector to be initialized @@ -151,7 +155,7 @@ static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs return t->iov; /* not big enough -- allocate a new one and release the old */ - new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), KSMBD_DEFAULT_GFP); if (new_iov) { kfree(t->iov); t->iov = new_iov; @@ -160,17 +164,6 @@ static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs return new_iov; } -static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) -{ - switch (sa->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)sa)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); - } - return 0; -} - /** * ksmbd_tcp_new_connection() - create a new tcp session on mount * @client_sk: socket associated with new connection @@ -182,9 +175,9 @@ static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) */ static int ksmbd_tcp_new_connection(struct socket *client_sk) { - struct sockaddr *csin; int rc = 0; struct tcp_transport *t; + struct task_struct *handler; t = alloc_transport(client_sk); if (!t) { @@ -192,27 +185,26 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) return -ENOMEM; } - csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); - if (kernel_getpeername(client_sk, csin) < 0) { - pr_err("client ip resolution failed\n"); - rc = -EINVAL; - goto out_error; - } - - KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, - "ksmbd:%u", - ksmbd_tcp_get_port(csin)); - if (IS_ERR(KSMBD_TRANS(t)->handler)) { +#if IS_ENABLED(CONFIG_IPV6) + if (client_sk->sk->sk_family == AF_INET6) + handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:%pI6c", + &KSMBD_TRANS(t)->conn->inet6_addr); + else + handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:%pI4", + &KSMBD_TRANS(t)->conn->inet_addr); +#else + handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:%pI4", + &KSMBD_TRANS(t)->conn->inet_addr); +#endif + if (IS_ERR(handler)) { pr_err("cannot start conn thread\n"); - rc = PTR_ERR(KSMBD_TRANS(t)->handler); + rc = PTR_ERR(handler); free_transport(t); } return rc; - -out_error: - free_transport(t); - return rc; } /** @@ -225,24 +217,66 @@ static int ksmbd_kthread_fn(void *p) { struct socket *client_sk = NULL; struct interface *iface = (struct interface *)p; - int ret; + struct ksmbd_conn *conn; + int ret, inet_hash; + unsigned int max_ip_conns; while (!kthread_should_stop()) { - mutex_lock(&iface->sock_release_lock); if (!iface->ksmbd_socket) { - mutex_unlock(&iface->sock_release_lock); break; } - ret = kernel_accept(iface->ksmbd_socket, &client_sk, - SOCK_NONBLOCK); - mutex_unlock(&iface->sock_release_lock); - if (ret) { - if (ret == -EAGAIN) - /* check for new connections every 100 msecs */ - schedule_timeout_interruptible(HZ / 10); + ret = kernel_accept(iface->ksmbd_socket, &client_sk, 0); + if (ret == -EINVAL) + break; + if (ret) + continue; + + if (!server_conf.max_ip_connections) + goto skip_max_ip_conns_limit; + + /* + * Limits repeated connections from clients with the same IP. + */ +#if IS_ENABLED(CONFIG_IPV6) + if (client_sk->sk->sk_family == AF_INET6) + inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr); + else + inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); +#else + inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); +#endif + + max_ip_conns = 0; + down_read(&conn_list_lock); + hash_for_each_possible(conn_list, conn, hlist, inet_hash) { +#if IS_ENABLED(CONFIG_IPV6) + if (client_sk->sk->sk_family == AF_INET6) { + if (memcmp(&client_sk->sk->sk_v6_daddr, + &conn->inet6_addr, 16) == 0) + max_ip_conns++; + } else if (inet_sk(client_sk->sk)->inet_daddr == + conn->inet_addr) + max_ip_conns++; +#else + if (inet_sk(client_sk->sk)->inet_daddr == + conn->inet_addr) + max_ip_conns++; +#endif + if (server_conf.max_ip_connections <= max_ip_conns) { + pr_info_ratelimited("Maximum IP connections exceeded (%u/%u)\n", + max_ip_conns, server_conf.max_ip_connections); + ret = -EAGAIN; + break; + } + } + up_read(&conn_list_lock); + if (ret == -EAGAIN) { + /* Per-IP limit hit: release the just-accepted socket. */ + sock_release(client_sk); continue; } +skip_max_ip_conns_limit: if (server_conf.max_connections && atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { pr_info_ratelimited("Limit the maximum number of connections(%u)\n", @@ -364,6 +398,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, * @t: TCP transport instance * @buf: buffer to store read data from socket * @to_read: number of bytes to read from socket + * @max_retries: number of retries if reading from socket fails * * Return: on success return number of bytes read from socket, * otherwise return error number @@ -403,10 +438,6 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket) if (!ksmbd_socket) return; - /* set zero to timeout */ - ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); - ksmbd_tcp_snd_timeout(ksmbd_socket, 0); - ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); if (ret) pr_err("Failed to shutdown socket: %d\n", ret); @@ -415,6 +446,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket) /** * create_socket - create socket for ksmbd/0 + * @iface: interface to bind the created socket to * * Return: 0 on success, error number otherwise */ @@ -426,12 +458,13 @@ static int create_socket(struct interface *iface) struct socket *ksmbd_socket; bool ipv4 = false; - ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); + ret = sock_create_kern(current->nsproxy->net_ns, PF_INET6, SOCK_STREAM, + IPPROTO_TCP, &ksmbd_socket); if (ret) { if (ret != -EAFNOSUPPORT) pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); - ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, - &ksmbd_socket); + ret = sock_create_kern(current->nsproxy->net_ns, PF_INET, + SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); if (ret) { pr_err("Can't create socket for ipv4: %d\n", ret); goto out_clear; @@ -445,6 +478,10 @@ static int create_socket(struct interface *iface) sin6.sin6_family = PF_INET6; sin6.sin6_addr = in6addr_any; sin6.sin6_port = htons(server_conf.tcp_port); + + lock_sock(ksmbd_socket->sk); + ksmbd_socket->sk->sk_ipv6only = false; + release_sock(ksmbd_socket->sk); } ksmbd_tcp_nodelay(ksmbd_socket); @@ -461,19 +498,16 @@ static int create_socket(struct interface *iface) } if (ipv4) - ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, + ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin, sizeof(sin)); else - ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, + ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin6, sizeof(sin6)); if (ret) { pr_err("Failed to bind socket: %d\n", ret); goto out_error; } - ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; - ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; - ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); if (ret) { pr_err("Port listen() error: %d\n", ret); @@ -497,52 +531,60 @@ out_clear: return ret; } +struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name) +{ + struct interface *iface; + + list_for_each_entry(iface, &iface_list, entry) + if (!strcmp(iface->name, netdev_name)) + return iface; + return NULL; +} + static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct interface *iface; - int ret, found = 0; + int ret; switch (event) { case NETDEV_UP: if (netif_is_bridge_port(netdev)) return NOTIFY_OK; - list_for_each_entry(iface, &iface_list, entry) { - if (!strcmp(iface->name, netdev->name)) { - found = 1; - if (iface->state != IFACE_STATE_DOWN) - break; - ret = create_socket(iface); - if (ret) - return NOTIFY_OK; - break; - } + iface = ksmbd_find_netdev_name_iface_list(netdev->name); + if (iface && iface->state == IFACE_STATE_DOWN) { + ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n", + iface->name); + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; } - if (!found && bind_additional_ifaces) { - iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP)); if (!iface) return NOTIFY_OK; + ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n", + iface->name); ret = create_socket(iface); if (ret) break; } break; case NETDEV_DOWN: - list_for_each_entry(iface, &iface_list, entry) { - if (!strcmp(iface->name, netdev->name) && - iface->state == IFACE_STATE_CONFIGURED) { - tcp_stop_kthread(iface->ksmbd_kthread); - iface->ksmbd_kthread = NULL; - mutex_lock(&iface->sock_release_lock); - tcp_destroy_socket(iface->ksmbd_socket); - iface->ksmbd_socket = NULL; - mutex_unlock(&iface->sock_release_lock); - - iface->state = IFACE_STATE_DOWN; - break; - } + iface = ksmbd_find_netdev_name_iface_list(netdev->name); + if (iface && iface->state == IFACE_STATE_CONFIGURED) { + ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n", + iface->name); + kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR); + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + sock_release(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + + iface->state = IFACE_STATE_DOWN; + break; } break; } @@ -593,7 +635,7 @@ static struct interface *alloc_iface(char *ifname) if (!ifname) return NULL; - iface = kzalloc(sizeof(struct interface), GFP_KERNEL); + iface = kzalloc(sizeof(struct interface), KSMBD_DEFAULT_GFP); if (!iface) { kfree(ifname); return NULL; @@ -602,7 +644,6 @@ static struct interface *alloc_iface(char *ifname) iface->name = ifname; iface->state = IFACE_STATE_DOWN; list_add(&iface->entry, &iface_list); - mutex_init(&iface->sock_release_lock); return iface; } @@ -611,22 +652,12 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) int sz = 0; if (!ifc_list_sz) { - struct net_device *netdev; - - rtnl_lock(); - for_each_netdev(&init_net, netdev) { - if (netif_is_bridge_port(netdev)) - continue; - if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) - return -ENOMEM; - } - rtnl_unlock(); bind_additional_ifaces = 1; return 0; } while (ifc_list_sz > 0) { - if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + if (!alloc_iface(kstrdup(ifc_list, KSMBD_DEFAULT_GFP))) return -ENOMEM; sz = strlen(ifc_list); @@ -642,8 +673,9 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) return 0; } -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { +static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { .read = ksmbd_tcp_read, .writev = ksmbd_tcp_writev, .disconnect = ksmbd_tcp_disconnect, + .free_transport = ksmbd_tcp_free_transport, }; diff --git a/fs/smb/server/transport_tcp.h b/fs/smb/server/transport_tcp.h index e338bebe322f..1e51675ee1b2 100644 --- a/fs/smb/server/transport_tcp.h +++ b/fs/smb/server/transport_tcp.h @@ -7,6 +7,8 @@ #define __KSMBD_TRANSPORT_TCP_H__ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); +struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name); +void ksmbd_free_transport(struct ksmbd_transport *kt); int ksmbd_tcp_init(void); void ksmbd_tcp_destroy(void); diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c index 9ae676906ed3..85e6791745ec 100644 --- a/fs/smb/server/unicode.c +++ b/fs/smb/server/unicode.c @@ -8,52 +8,15 @@ */ #include <linux/fs.h> #include <linux/slab.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "glob.h" #include "unicode.h" -#include "uniupr.h" #include "smb_common.h" /* - * smb_utf16_bytes() - how long will a string be after conversion? - * @from: pointer to input string - * @maxbytes: don't go past this many bytes of input string - * @codepage: destination codepage - * - * Walk a utf16le string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - * - * Return: string length after conversion - */ -static int smb_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage) -{ - int i; - int charlen, outlen = 0; - int maxwords = maxbytes / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp; - - for (i = 0; i < maxwords; i++) { - ftmp = get_unaligned_le16(&from[i]); - if (ftmp == 0) - break; - - charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); - if (charlen > 0) - outlen += charlen; - else - outlen++; - } - - return outlen; -} - -/* * cifs_mapchar() - convert a host-endian char to proper char in codepage * @target: where converted character should be copied - * @src_char: 2 byte host-endian source character + * @from: host-endian source string * @cp: codepage to which character should be converted * @mapchar: should character be mapped according to mapchars mount option? * @@ -64,10 +27,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes, * Return: string length after conversion */ static int -cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, +cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, bool mapchar) { int len = 1; + __u16 src_char; + + src_char = *from; if (!mapchar) goto cp_convert; @@ -105,12 +71,66 @@ out: cp_convert: len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); - if (len <= 0) { - *target = '?'; - len = 1; - } + if (len <= 0) + goto surrogate_pair; goto out; + +surrogate_pair: + /* convert SURROGATE_PAIR and IVS */ + if (strcmp(cp->charset, "utf8")) + goto unknown; + len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); + if (len <= 0) + goto unknown; + return len; + +unknown: + *target = '?'; + len = 1; + goto out; +} + +/* + * smb_utf16_bytes() - compute converted string length + * @from: pointer to input string + * @maxbytes: input string length + * @codepage: destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + * + * Return: string length after conversion + */ +static int smb_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i, j; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp[3]; + + for (i = 0; i < maxwords; i++) { + ftmp[0] = get_unaligned_le16(&from[i]); + if (ftmp[0] == 0) + break; + for (j = 1; j <= 2; j++) { + if (i + j < maxwords) + ftmp[j] = get_unaligned_le16(&from[i + j]); + else + ftmp[j] = 0; + } + + charlen = cifs_mapchar(tmp, ftmp, codepage, 0); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; } /* @@ -140,12 +160,12 @@ cp_convert: static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, const struct nls_table *codepage, bool mapchar) { - int i, charlen, safelen; + int i, j, charlen, safelen; int outlen = 0; int nullsize = nls_nullsize(codepage); int fromwords = fromlen / 2; char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp; + __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ /* * because the chars can be of varying widths, we need to take care @@ -156,9 +176,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); for (i = 0; i < fromwords; i++) { - ftmp = get_unaligned_le16(&from[i]); - if (ftmp == 0) + ftmp[0] = get_unaligned_le16(&from[i]); + if (ftmp[0] == 0) break; + for (j = 1; j <= 2; j++) { + if (i + j < fromwords) + ftmp[j] = get_unaligned_le16(&from[i + j]); + else + ftmp[j] = 0; + } /* * check to see if converting this character might make the @@ -173,6 +199,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, /* put converted char into 'to' buffer */ charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); outlen += charlen; + + /* + * charlen (=bytes of UTF-8 for 1 character) + * 4bytes UTF-8(surrogate pair) is charlen=4 + * (4bytes UTF-16 code) + * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 + * (2 UTF-8 pairs divided to 2 UTF-16 pairs) + */ + if (charlen == 4) + i++; + else if (charlen >= 5) + /* 5-6bytes UTF-8 */ + i += 2; } /* properly null-terminate string */ @@ -258,7 +297,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, if (is_unicode) { len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); len += nls_nullsize(codepage); - dst = kmalloc(len, GFP_KERNEL); + dst = kmalloc(len, KSMBD_DEFAULT_GFP); if (!dst) return ERR_PTR(-ENOMEM); ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, @@ -270,7 +309,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen, } else { len = strnlen(src, maxlen); len++; - dst = kmalloc(len, GFP_KERNEL); + dst = kmalloc(len, KSMBD_DEFAULT_GFP); if (!dst) return ERR_PTR(-ENOMEM); strscpy(dst, src, len); @@ -307,6 +346,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, char src_char; __le16 dst_char; wchar_t tmp; + wchar_t wchar_to[6]; /* UTF-16 */ + int ret; + unicode_t u; if (!mapchars) return smb_strtoUTF16(target, source, srclen, cp); @@ -349,11 +391,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, * if no match, use question mark, which at least in * some cases serves as wild card */ - if (charlen < 1) { - dst_char = cpu_to_le16(0x003f); - charlen = 1; + if (charlen > 0) + goto ctoUTF16; + + /* convert SURROGATE_PAIR */ + if (strcmp(cp->charset, "utf8")) + goto unknown; + if (*(source + i) & 0x80) { + charlen = utf8_to_utf32(source + i, 6, &u); + if (charlen < 0) + goto unknown; + } else + goto unknown; + ret = utf8s_to_utf16s(source + i, charlen, + UTF16_LITTLE_ENDIAN, + wchar_to, 6); + if (ret < 0) + goto unknown; + + i += charlen; + dst_char = cpu_to_le16(*wchar_to); + if (charlen <= 3) + /* 1-3bytes UTF-8 to 2bytes UTF-16 */ + put_unaligned(dst_char, &target[j]); + else if (charlen == 4) { + /* + * 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 + * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 + * (charlen=3+4 or 4+4) + */ + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 1)); + j++; + put_unaligned(dst_char, &target[j]); + } else if (charlen >= 5) { + /* 5-6bytes UTF-8 to 6bytes UTF-16 */ + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 1)); + j++; + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 2)); + j++; + put_unaligned(dst_char, &target[j]); } + continue; + +unknown: + dst_char = cpu_to_le16(0x003f); + charlen = 1; } + +ctoUTF16: /* * character may take more than one byte in the source string, * but will take exactly two bytes in the target string diff --git a/fs/smb/server/unicode.h b/fs/smb/server/unicode.h index 076f6034a789..28c7c736f7bb 100644 --- a/fs/smb/server/unicode.h +++ b/fs/smb/server/unicode.h @@ -18,49 +18,14 @@ * This is a compressed table of upper and lower case conversion. * */ -#ifndef _CIFS_UNICODE_H -#define _CIFS_UNICODE_H +#ifndef _SMB_UNICODE_H +#define _SMB_UNICODE_H #include <asm/byteorder.h> #include <linux/types.h> #include <linux/nls.h> #include <linux/unicode.h> - -#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ - -/* - * Windows maps these to the user defined 16 bit Unicode range since they are - * reserved symbols (along with \ and /), otherwise illegal to store - * in filenames in NTFS - */ -#define UNI_ASTERISK ((__u16)('*' + 0xF000)) -#define UNI_QUESTION ((__u16)('?' + 0xF000)) -#define UNI_COLON ((__u16)(':' + 0xF000)) -#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) -#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) -#define UNI_PIPE ((__u16)('|' + 0xF000)) -#define UNI_SLASH ((__u16)('\\' + 0xF000)) - -/* Just define what we want from uniupr.h. We don't want to define the tables - * in each source file. - */ -#ifndef UNICASERANGE_DEFINED -struct UniCaseRange { - wchar_t start; - wchar_t end; - signed char *table; -}; -#endif /* UNICASERANGE_DEFINED */ - -#ifndef UNIUPR_NOUPPER -extern signed char SmbUniUpperTable[512]; -extern const struct UniCaseRange SmbUniUpperRange[]; -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -extern signed char CifsUniLowerTable[512]; -extern const struct UniCaseRange CifsUniLowerRange[]; -#endif /* UNIUPR_NOLOWER */ +#include "../../nls/nls_ucs2_utils.h" #ifdef __KERNEL__ int smb_strtoUTF16(__le16 *to, const char *from, int len, @@ -73,286 +38,4 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); #endif -/* - * UniStrcat: Concatenate the second string to the first - * - * Returns: - * Address of the first string - */ -static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ - - while (*ucs1++) - /*NULL*/; /* To end of first string */ - ucs1--; /* Return to the null */ - while ((*ucs1++ = *ucs2++)) - /*NULL*/; /* copy string 2 over */ - return anchor; -} - -/* - * UniStrchr: Find a character in a string - * - * Returns: - * Address of first occurrence of character in string - * or NULL if the character is not in the string - */ -static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) -{ - while ((*ucs != uc) && *ucs) - ucs++; - - if (*ucs == uc) - return (wchar_t *)ucs; - return NULL; -} - -/* - * UniStrcmp: Compare two strings - * - * Returns: - * < 0: First string is less than second - * = 0: Strings are equal - * > 0: First string is greater than second - */ -static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) -{ - while ((*ucs1 == *ucs2) && *ucs1) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)*ucs2; -} - -/* - * UniStrcpy: Copy a string - */ -static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save the start of result string */ - - while ((*ucs1++ = *ucs2++)) - /*NULL*/; - return anchor; -} - -/* - * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) - */ -static inline size_t UniStrlen(const wchar_t *ucs1) -{ - int i = 0; - - while (*ucs1++) - i++; - return i; -} - -/* - * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a - * string (length limited) - */ -static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) -{ - int i = 0; - - while (*ucs1++) { - i++; - if (i >= maxlen) - break; - } - return i; -} - -/* - * UniStrncat: Concatenate length limited string - */ -static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; /* save pointer to string 1 */ - - while (*ucs1++) - /*NULL*/; - ucs1--; /* point to null terminator of s1 */ - while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ - ucs1++; - ucs2++; - } - *ucs1 = 0; /* Null terminate the result */ - return anchor; -} - -/* - * UniStrncmp: Compare length limited string - */ -static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == *ucs2) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)*ucs2; -} - -/* - * UniStrncmp_le: Compare length limited string - native to little-endian - */ -static inline int -UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); -} - -/* - * UniStrncpy: Copy length limited string with pad - */ -static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = *ucs2++; - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrncpy_le: Copy length limited string with pad to little-endian - */ -static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = __le16_to_cpu(*ucs2++); - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrstr: Find a string in a string - * - * Returns: - * Address of first match found - * NULL if no matching string is found - */ -static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) -{ - const wchar_t *anchor1 = ucs1; - const wchar_t *anchor2 = ucs2; - - while (*ucs1) { - if (*ucs1 == *ucs2) { - /* Partial match found */ - ucs1++; - ucs2++; - } else { - if (!*ucs2) /* Match found */ - return (wchar_t *)anchor1; - ucs1 = ++anchor1; /* No match */ - ucs2 = anchor2; - } - } - - if (!*ucs2) /* Both end together */ - return (wchar_t *)anchor1; /* Match found */ - return NULL; /* No match */ -} - -#ifndef UNIUPR_NOUPPER -/* - * UniToupper: Convert a unicode character to upper case - */ -static inline wchar_t UniToupper(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(SmbUniUpperTable)) { - /* Latin characters */ - return uc + SmbUniUpperTable[uc]; /* Use base tables */ - } - - rp = SmbUniUpperRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - return uc; /* Past last range */ -} - -/* - * UniStrupr: Upper case a unicode string - */ -static inline __le16 *UniStrupr(register __le16 *upin) -{ - register __le16 *up; - - up = upin; - while (*up) { /* For all characters */ - *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); - up++; - } - return upin; /* Return input pointer */ -} -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -/* - * UniTolower: Convert a unicode character to lower case - */ -static inline wchar_t UniTolower(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniLowerTable)) { - /* Latin characters */ - return uc + CifsUniLowerTable[uc]; /* Use base tables */ - } - - rp = CifsUniLowerRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - return uc; /* Past last range */ -} - -/* - * UniStrlwr: Lower case a unicode string - */ -static inline wchar_t *UniStrlwr(register wchar_t *upin) -{ - register wchar_t *up; - - up = upin; - while (*up) { /* For all characters */ - *up = UniTolower(*up); - up++; - } - return upin; /* Return input pointer */ -} - -#endif - -#endif /* _CIFS_UNICODE_H */ +#endif /* _SMB_UNICODE_H */ diff --git a/fs/smb/server/uniupr.h b/fs/smb/server/uniupr.h deleted file mode 100644 index 26583b776897..000000000000 --- a/fs/smb/server/uniupr.h +++ /dev/null @@ -1,268 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Some of the source code in this file came from fs/cifs/uniupr.h - * Copyright (c) International Business Machines Corp., 2000,2002 - * - * uniupr.h - Unicode compressed case ranges - * - */ -#ifndef __KSMBD_UNIUPR_H -#define __KSMBD_UNIUPR_H - -#ifndef UNIUPR_NOUPPER -/* - * Latin upper case - */ -signed char SmbUniUpperTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, /* 060-06f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, - -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ - 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ - 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ - 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ - -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ - 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ -}; - -/* Upper case range - Greek */ -static signed char UniCaseRangeU03a0[47] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 3b0-3bf */ - -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, - -63, -63, -}; - -/* Upper case range - Cyrillic */ -static signed char UniCaseRangeU0430[48] = { - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 430-43f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 440-44f */ - 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, 0, -80, -80, /* 450-45f */ -}; - -/* Upper case range - Extended cyrillic */ -static signed char UniCaseRangeU0490[61] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ - 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -}; - -/* Upper case range - Extended latin and greek */ -static signed char UniCaseRangeU1e00[509] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ - 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ - 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ - 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, - 126, 126, 0, 0, /* 1f70-1f7f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ - 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ - 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ - 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Upper case range - Wide latin */ -static signed char UniCaseRangeUff40[27] = { - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, /* ff40-ff4f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -}; - -/* - * Upper Case Range - */ -const struct UniCaseRange SmbUniUpperRange[] = { - {0x03a0, 0x03ce, UniCaseRangeU03a0}, - {0x0430, 0x045f, UniCaseRangeU0430}, - {0x0490, 0x04cc, UniCaseRangeU0490}, - {0x1e00, 0x1ffc, UniCaseRangeU1e00}, - {0xff40, 0xff5a, UniCaseRangeUff40}, - {0} -}; -#endif - -#ifndef UNIUPR_NOLOWER -/* - * Latin lower case - */ -signed char CifsUniLowerTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 040-04f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, - 0, 0, 0, /* 050-05f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, /* 0c0-0cf */ - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 0, /* 0d0-0df */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ - 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, - 0, /* 170-17f */ - 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, - 0, /* 180-18f */ - 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ - 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ - 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ -}; - -/* Lower case range - Greek */ -static signed char UniCaseRangeL0380[44] = { - 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 390-39f */ - 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* Lower case range - Cyrillic */ -static signed char UniCaseRangeL0400[48] = { - 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 0, 80, 80, /* 400-40f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 410-41f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 420-42f */ -}; - -/* Lower case range - Extended cyrillic */ -static signed char UniCaseRangeL0490[60] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, -}; - -/* Lower case range - Extended latin and greek */ -static signed char UniCaseRangeL1e00[504] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ - 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, - 0, 0, /* 1fc0-1fcf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, - 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Lower case range - Wide latin */ -static signed char UniCaseRangeLff20[27] = { - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, /* ff20-ff2f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* - * Lower Case Range - */ -const struct UniCaseRange CifsUniLowerRange[] = { - {0x0380, 0x03ab, UniCaseRangeL0380}, - {0x0400, 0x042f, UniCaseRangeL0400}, - {0x0490, 0x04cb, UniCaseRangeL0490}, - {0x1e00, 0x1ff7, UniCaseRangeL1e00}, - {0xff20, 0xff3a, UniCaseRangeLff20}, - {0} -}; -#endif - -#endif /* __KSMBD_UNIUPR_H */ diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index d48756a339a5..98b0eb966d91 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -4,6 +4,7 @@ * Copyright (C) 2018 Samsung Electronics Co., Ltd. */ +#include <crypto/sha2.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/filelock.h> @@ -19,6 +20,7 @@ #include <linux/sched/xacct.h> #include <linux/crc32c.h> #include <linux/namei.h> +#include <linux/splice.h> #include "glob.h" #include "oplock.h" @@ -47,28 +49,13 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, i_uid_write(inode, i_uid_read(parent_inode)); } -/** - * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable - */ -int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) -{ - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - if (child->d_parent != parent) { - inode_unlock(d_inode(parent)); - return -ENOENT; - } - - return 0; -} - -static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, - char *pathname, unsigned int flags, - struct path *parent_path, - struct path *path) +static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf, + char *pathname, unsigned int flags, + struct path *path, bool for_remove) { struct qstr last; - struct filename *filename; - struct path *root_share_path = &share_conf->vfs_path; + struct filename *filename __free(putname) = NULL; + const struct path *root_share_path = &share_conf->vfs_path; int err, type; struct dentry *d; @@ -84,48 +71,55 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, return PTR_ERR(filename); err = vfs_path_parent_lookup(filename, flags, - parent_path, &last, &type, + path, &last, &type, root_share_path); - if (err) { - putname(filename); + if (err) return err; - } if (unlikely(type != LAST_NORM)) { - path_put(parent_path); - putname(filename); + path_put(path); return -ENOENT; } - inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT); - d = lookup_one_qstr_excl(&last, parent_path->dentry, 0); - if (IS_ERR(d)) - goto err_out; + if (for_remove) { + err = mnt_want_write(path->mnt); + if (err) { + path_put(path); + return -ENOENT; + } + + d = start_removing_noperm(path->dentry, &last); - if (d_is_negative(d)) { - dput(d); - goto err_out; + if (!IS_ERR(d)) { + dput(path->dentry); + path->dentry = d; + return 0; + } + mnt_drop_write(path->mnt); + path_put(path); + return -ENOENT; } + d = lookup_noperm_unlocked(&last, path->dentry); + if (!IS_ERR(d) && d_is_negative(d)) { + dput(d); + d = ERR_PTR(-ENOENT); + } + if (IS_ERR(d)) { + path_put(path); + return -ENOENT; + } + dput(path->dentry); path->dentry = d; - path->mnt = mntget(parent_path->mnt); if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) { err = follow_down(path, 0); if (err < 0) { path_put(path); - goto err_out; + return -ENOENT; } } - - putname(filename); return 0; - -err_out: - inode_unlock(d_inode(parent_path->dentry)); - path_put(parent_path); - putname(filename); - return -ENOENT; } void ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap, @@ -173,23 +167,16 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) return err; } - err = mnt_want_write(path.mnt); - if (err) - goto out_err; - mode |= S_IFREG; - err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry), - dentry, mode, true); + err = vfs_create(mnt_idmap(path.mnt), dentry, mode, NULL); if (!err) { ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); } else { pr_err("File(%s): creation failed (err:%d)\n", name, err); } - mnt_drop_write(path.mnt); -out_err: - done_path_create(&path, dentry); + end_creating_path(&path, dentry); return err; } @@ -205,8 +192,8 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) { struct mnt_idmap *idmap; struct path path; - struct dentry *dentry; - int err; + struct dentry *dentry, *d; + int err = 0; dentry = ksmbd_vfs_kern_path_create(work, name, LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, @@ -219,36 +206,18 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) return err; } - err = mnt_want_write(path.mnt); - if (err) - goto out_err2; - idmap = mnt_idmap(path.mnt); mode |= S_IFDIR; - err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode); - if (!err && d_unhashed(dentry)) { - struct dentry *d; - - d = lookup_one(idmap, dentry->d_name.name, dentry->d_parent, - dentry->d_name.len); - if (IS_ERR(d)) { - err = PTR_ERR(d); - goto out_err1; - } - if (unlikely(d_is_negative(d))) { - dput(d); - err = -ENOENT; - goto out_err1; - } - - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); - dput(d); - } + d = dentry; + dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode, NULL); + if (IS_ERR(dentry)) + err = PTR_ERR(dentry); + else if (d_is_negative(dentry)) + err = -ENOENT; + if (!err && dentry != d) + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); -out_err1: - mnt_drop_write(path.mnt); -out_err2: - done_path_create(&path, dentry); + end_creating_path(&path, dentry); if (err) pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); return err; @@ -309,6 +278,7 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, if (v_len - *pos < count) count = v_len - *pos; + fp->stream.pos = v_len; memcpy(buf, &stream_buf[*pos], count); @@ -333,22 +303,25 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end, struct file_lock_context *ctx = locks_inode_context(file_inode(filp)); int error = 0; + if (start == end) + return 0; + if (!ctx || list_empty_careful(&ctx->flc_posix)) return 0; spin_lock(&ctx->flc_lock); - list_for_each_entry(flock, &ctx->flc_posix, fl_list) { + for_each_file_lock(flock, &ctx->flc_posix) { /* check conflict locks */ if (flock->fl_end >= start && end >= flock->fl_start) { - if (flock->fl_type == F_RDLCK) { + if (lock_is_read(flock)) { if (type == WRITE) { pr_err("not allow write by shared lock\n"); error = 1; goto out; } - } else if (flock->fl_type == F_WRLCK) { + } else if (lock_is_write(flock)) { /* check owner in lock */ - if (flock->fl_file != filp) { + if (flock->c.flc_file != filp) { error = 1; pr_err("not allow rw access by exclusive lock from other opens\n"); goto out; @@ -364,18 +337,18 @@ out: /** * ksmbd_vfs_read() - vfs helper for smb file read * @work: smb work - * @fid: file id of open file + * @fp: ksmbd file pointer * @count: read byte count * @pos: file pos + * @rbuf: read data buffer * * Return: number of read bytes on success, otherwise error */ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, - loff_t *pos) + loff_t *pos, char *rbuf) { struct file *filp = fp->filp; ssize_t nbytes = 0; - char *rbuf = work->aux_payload_buf; struct inode *inode = file_inode(filp); if (S_ISDIR(inode->i_mode)) @@ -426,10 +399,15 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", *pos, count); + if (*pos >= XATTR_SIZE_MAX) { + pr_err("stream write position %lld is out of bounds\n", *pos); + return -EINVAL; + } + size = *pos + count; if (size > XATTR_SIZE_MAX) { size = XATTR_SIZE_MAX; - count = (*pos + count) - XATTR_SIZE_MAX; + count = XATTR_SIZE_MAX - *pos; } v_len = ksmbd_vfs_getcasexattr(idmap, @@ -444,7 +422,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, } if (v_len < size) { - wbuf = kvzalloc(size, GFP_KERNEL); + wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP); if (!wbuf) { err = -ENOMEM; goto out; @@ -463,11 +441,12 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, fp->stream.name, (void *)stream_buf, size, - 0); + 0, + true); if (err < 0) goto out; - - fp->filp->f_pos = *pos; + else + fp->stream.pos = size; err = 0; out: kvfree(stream_buf); @@ -477,7 +456,7 @@ out: /** * ksmbd_vfs_write() - vfs helper for smb file write * @work: work - * @fid: file id of open file + * @fp: ksmbd file pointer * @buf: buf containing data for writing * @count: read byte count * @pos: file pos @@ -495,7 +474,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, int err = 0; if (work->conn->connection_type) { - if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) || + S_ISDIR(file_inode(fp->filp)->i_mode)) { pr_err("no right to write(%pD)\n", fp->filp); err = -EACCES; goto out; @@ -520,6 +500,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, } } + /* Reserve lease break for parent dir at closing time */ + fp->reserve_lease_break = true; + /* Do we need to break any of a levelII oplock? */ smb_break_all_levII_oplock(work, fp, 1); @@ -545,17 +528,16 @@ out: /** * ksmbd_vfs_getattr() - vfs helper for smb getattr - * @work: work - * @fid: file id of open file - * @attrs: inode attributes - * + * @path: path of dentry + * @stat: pointer to returned kernel stat structure * Return: 0 on success, otherwise error */ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) { int err; - err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + err = vfs_getattr(path, stat, STATX_BASIC_STATS | STATX_BTIME, + AT_STATX_SYNC_AS_STAT); if (err) pr_err("getattr failed, err %d\n", err); return err; @@ -565,6 +547,7 @@ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) * ksmbd_vfs_fsync() - vfs helper for smb fsync * @work: work * @fid: file id of open file + * @p_id: persistent file id * * Return: 0 on success, otherwise error */ @@ -587,7 +570,8 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) /** * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink - * @name: directory or file name that is relative to share + * @work: work + * @path: path of dentry * * Return: 0 on success, otherwise error */ @@ -605,13 +589,9 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) goto out_err; } - err = mnt_want_write(path->mnt); - if (err) - goto out_err; - idmap = mnt_idmap(path->mnt); if (S_ISDIR(d_inode(path->dentry)->i_mode)) { - err = vfs_rmdir(idmap, d_inode(parent), path->dentry); + err = vfs_rmdir(idmap, d_inode(parent), path->dentry, NULL); if (err && err != -ENOTEMPTY) ksmbd_debug(VFS, "rmdir failed, err %d\n", err); } else { @@ -619,7 +599,6 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) if (err) ksmbd_debug(VFS, "unlink failed, err %d\n", err); } - mnt_drop_write(path->mnt); out_err: ksmbd_revert_fsids(work); @@ -628,6 +607,7 @@ out_err: /** * ksmbd_vfs_link() - vfs helper for creating smb hardlink + * @work: work * @oldname: source file name * @newname: hardlink name that is relative to share * @@ -665,19 +645,14 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, goto out3; } - err = mnt_want_write(newpath.mnt); - if (err) - goto out3; - err = vfs_link(oldpath.dentry, mnt_idmap(newpath.mnt), d_inode(newpath.dentry), dentry, NULL); if (err) ksmbd_debug(VFS, "vfs_link failed err %d\n", err); - mnt_drop_write(newpath.mnt); out3: - done_path_create(&newpath, dentry); + end_creating_path(&newpath, dentry); out2: path_put(&oldpath); out1: @@ -688,7 +663,6 @@ out1: int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, char *newname, int flags) { - struct dentry *old_parent, *new_dentry, *trap; struct dentry *old_child = old_path->dentry; struct path new_path; struct qstr new_last; @@ -724,15 +698,16 @@ retry: if (err) goto out2; - trap = lock_rename_child(old_child, new_path.dentry); - - old_parent = dget(old_child->d_parent); - if (d_unhashed(old_child)) { - err = -EINVAL; - goto out3; - } + rd.mnt_idmap = mnt_idmap(old_path->mnt); + rd.old_parent = NULL; + rd.new_parent = new_path.dentry; + rd.flags = flags; + rd.delegated_inode = NULL, + err = start_renaming_dentry(&rd, lookup_flags, old_child, &new_last); + if (err) + goto out_drop_write; - parent_fp = ksmbd_lookup_fd_inode(d_inode(old_child->d_parent)); + parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); if (parent_fp) { if (parent_fp->daccess & FILE_DELETE_LE) { pr_err("parent dir is opened with delete access\n"); @@ -743,50 +718,18 @@ retry: ksmbd_fd_put(work, parent_fp); } - new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, - lookup_flags | LOOKUP_RENAME_TARGET); - if (IS_ERR(new_dentry)) { - err = PTR_ERR(new_dentry); - goto out3; - } - - if (d_is_symlink(new_dentry)) { + if (d_is_symlink(rd.new_dentry)) { err = -EACCES; - goto out4; - } - - if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) { - err = -EEXIST; - goto out4; - } - - if (old_child == trap) { - err = -EINVAL; - goto out4; - } - - if (new_dentry == trap) { - err = -ENOTEMPTY; - goto out4; + goto out3; } - rd.old_mnt_idmap = mnt_idmap(old_path->mnt), - rd.old_dir = d_inode(old_parent), - rd.old_dentry = old_child, - rd.new_mnt_idmap = mnt_idmap(new_path.mnt), - rd.new_dir = new_path.dentry->d_inode, - rd.new_dentry = new_dentry, - rd.flags = flags, - rd.delegated_inode = NULL, err = vfs_rename(&rd); if (err) ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); -out4: - dput(new_dentry); out3: - dput(old_parent); - unlock_rename(old_parent, new_path.dentry); + end_renaming(&rd); +out_drop_write: mnt_drop_write(old_path->mnt); out2: path_put(&new_path); @@ -805,7 +748,7 @@ revert_fsids: /** * ksmbd_vfs_truncate() - vfs helper for smb file truncate * @work: work - * @fid: file id of old file + * @fp: ksmbd file pointer * @size: truncate to given size * * Return: 0 on success, otherwise error @@ -827,7 +770,7 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, if (size < inode->i_size) { err = check_lock_range(filp, size, inode->i_size - 1, WRITE); - } else { + } else if (size > inode->i_size) { err = check_lock_range(filp, inode->i_size, size - 1, WRITE); } @@ -848,7 +791,6 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes * @dentry: dentry of file for listing xattrs * @list: destination buffer - * @size: destination buffer length * * Return: xattr list length on success, otherwise error */ @@ -861,7 +803,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) if (size <= 0) return size; - vlist = kvzalloc(size, GFP_KERNEL); + vlist = kvzalloc(size, KSMBD_DEFAULT_GFP); if (!vlist) return -ENOMEM; @@ -903,7 +845,7 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap, if (xattr_len < 0) return xattr_len; - buf = kmalloc(xattr_len + 1, GFP_KERNEL); + buf = kmalloc(xattr_len + 1, KSMBD_DEFAULT_GFP); if (!buf) return -ENOMEM; @@ -919,23 +861,27 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap, /** * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value * @idmap: idmap of the relevant mount - * @dentry: dentry to set XATTR at + * @path: path of dentry to set XATTR at * @attr_name: xattr name for setxattr * @attr_value: xattr value to set * @attr_size: size of xattr value * @flags: destination buffer length + * @get_write: get write access to a mount * * Return: 0 on success, otherwise error */ int ksmbd_vfs_setxattr(struct mnt_idmap *idmap, const struct path *path, const char *attr_name, - void *attr_value, size_t attr_size, int flags) + void *attr_value, size_t attr_size, int flags, + bool get_write) { int err; - err = mnt_want_write(path->mnt); - if (err) - return err; + if (get_write == true) { + err = mnt_want_write(path->mnt); + if (err) + return err; + } err = vfs_setxattr(idmap, path->dentry, @@ -945,14 +891,15 @@ int ksmbd_vfs_setxattr(struct mnt_idmap *idmap, flags); if (err) ksmbd_debug(VFS, "setxattr failed, err %d\n", err); - mnt_drop_write(path->mnt); + if (get_write == true) + mnt_drop_write(path->mnt); return err; } /** * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options * @filp: file pointer for IO - * @options: smb IO options + * @option: smb IO options */ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) { @@ -1049,16 +996,21 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, } int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, - const struct path *path, char *attr_name) + const struct path *path, char *attr_name, + bool get_write) { int err; - err = mnt_want_write(path->mnt); - if (err) - return err; + if (get_write == true) { + err = mnt_want_write(path->mnt); + if (err) + return err; + } err = vfs_removexattr(idmap, path->dentry, attr_name); - mnt_drop_write(path->mnt); + + if (get_write == true) + mnt_drop_write(path->mnt); return err; } @@ -1074,18 +1026,17 @@ int ksmbd_vfs_unlink(struct file *filp) return err; dir = dget_parent(dentry); - err = ksmbd_vfs_lock_parent(dir, dentry); - if (err) + dentry = start_removing_dentry(dir, dentry); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; - dget(dentry); if (S_ISDIR(d_inode(dentry)->i_mode)) - err = vfs_rmdir(idmap, d_inode(dir), dentry); + err = vfs_rmdir(idmap, d_inode(dir), dentry, NULL); else err = vfs_unlink(idmap, d_inode(dir), dentry, NULL); - dput(dentry); - inode_unlock(d_inode(dir)); + end_removing(dentry); if (err) ksmbd_debug(VFS, "failed to delete, err %d\n", err); out: @@ -1101,9 +1052,10 @@ static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, struct ksmbd_readdir_data *buf; buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - buf->dirent_count++; + if (!is_dot_dotdot(name, namlen)) + buf->dirent_count++; - return buf->dirent_count <= 2; + return !buf->dirent_count; } /** @@ -1123,7 +1075,7 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) readdir_data.dirent_count = 0; err = iterate_dir(fp->filp, &readdir_data.ctx); - if (readdir_data.dirent_count > 2) + if (readdir_data.dirent_count) err = -ENOTEMPTY; else err = 0; @@ -1152,7 +1104,7 @@ static bool __caseless_lookup(struct dir_context *ctx, const char *name, if (cmp < 0) cmp = strncasecmp((char *)buf->private, name, namlen); if (!cmp) { - memcpy((char *)buf->private, name, namlen); + memcpy((char *)buf->private, name, buf->used); buf->dirent_count = 1; return false; } @@ -1164,6 +1116,7 @@ static bool __caseless_lookup(struct dir_context *ctx, const char *name, * @dir: path info * @name: filename to lookup * @namelen: filename length + * @um: &struct unicode_map to use * * Return: 0 on success, otherwise error */ @@ -1192,91 +1145,115 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, return ret; } -/** - * ksmbd_vfs_kern_path_locked() - lookup a file and get path info - * @name: file path that is relative to share - * @flags: lookup flags - * @path: if lookup succeed, return path info - * @caseless: caseless filename lookup - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, - unsigned int flags, struct path *parent_path, - struct path *path, bool caseless) +static +int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath, + unsigned int flags, + struct path *path, bool caseless, bool for_remove) { struct ksmbd_share_config *share_conf = work->tcon->share_conf; + struct path parent_path; + size_t path_len, remain_len; int err; - err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path, - path); - if (!err) - return 0; - - if (caseless) { - char *filepath; - size_t path_len, remain_len; - - filepath = kstrdup(name, GFP_KERNEL); - if (!filepath) - return -ENOMEM; - - path_len = strlen(filepath); - remain_len = path_len; - - *parent_path = share_conf->vfs_path; - path_get(parent_path); +retry: + err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, for_remove); + if (!err || !caseless) + return err; - while (d_can_lookup(parent_path->dentry)) { - char *filename = filepath + path_len - remain_len; - char *next = strchrnul(filename, '/'); - size_t filename_len = next - filename; - bool is_last = !next[0]; + path_len = strlen(filepath); + remain_len = path_len; - if (filename_len == 0) - break; + parent_path = share_conf->vfs_path; + path_get(&parent_path); - err = ksmbd_vfs_lookup_in_dir(parent_path, filename, - filename_len, - work->conn->um); - if (err) - goto out2; + while (d_can_lookup(parent_path.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; - next[0] = '\0'; + if (filename_len == 0) + break; - err = vfs_path_lookup(share_conf->vfs_path.dentry, - share_conf->vfs_path.mnt, - filepath, - flags, - path); - if (err) - goto out2; - else if (is_last) - goto out1; - path_put(parent_path); - *parent_path = *path; - - next[0] = '/'; - remain_len -= filename_len + 1; + err = ksmbd_vfs_lookup_in_dir(&parent_path, filename, + filename_len, + work->conn->um); + path_put(&parent_path); + if (err) + goto out; + if (is_last) { + caseless = false; + goto retry; } + next[0] = '\0'; + + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + filepath, + flags, + &parent_path); + next[0] = '/'; + if (err) + goto out; - err = -EINVAL; -out2: - path_put(parent_path); -out1: - kfree(filepath); + remain_len -= filename_len + 1; } - if (!err) { - err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry); - if (err) { - path_put(path); - path_put(parent_path); - } - } + err = -EINVAL; + path_put(&parent_path); +out: return err; } +/** + * ksmbd_vfs_kern_path() - lookup a file and get path info + * @work: work + * @filepath: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Perform the lookup, possibly crossing over any mount point. + * On return no locks will be held and write-access to filesystem + * won't have been checked. + * Return: 0 if file was found, otherwise error + */ +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath, + unsigned int flags, + struct path *path, bool caseless) +{ + return __ksmbd_vfs_kern_path(work, filepath, flags, path, + caseless, false); +} + +/** + * ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal + * @work: work + * @filepath: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Perform the lookup, but don't cross over any mount point. + * On return the parent of path->dentry will be locked and write-access to + * filesystem will have been gained. + * Return: 0 on if file was found, otherwise error + */ +int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *filepath, + unsigned int flags, + struct path *path, bool caseless) +{ + return __ksmbd_vfs_kern_path(work, filepath, flags, path, + caseless, true); +} + +void ksmbd_vfs_kern_path_end_removing(const struct path *path) +{ + end_removing(path->dentry); + mnt_drop_write(path->mnt); + mntput(path->mnt); +} + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, const char *name, unsigned int flags, @@ -1289,7 +1266,7 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, if (!abs_name) return ERR_PTR(-ENOMEM); - dent = kern_path_create(AT_FDCWD, abs_name, path, flags); + dent = start_creating_path(AT_FDCWD, abs_name, path, flags); kfree(abs_name); return dent; } @@ -1353,7 +1330,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path) ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { - err = ksmbd_vfs_remove_xattr(idmap, path, name); + err = ksmbd_vfs_remove_xattr(idmap, path, name, true); if (err) ksmbd_debug(SMB, "remove xattr failed : %s\n", name); } @@ -1382,7 +1359,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct mnt_idmap *id smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + sizeof(struct xattr_acl_entry) * posix_acls->a_count, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!smb_acl) goto out; @@ -1431,7 +1408,8 @@ out: int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct mnt_idmap *idmap, const struct path *path, - struct smb_ntsd *pntsd, int len) + struct smb_ntsd *pntsd, int len, + bool get_write) { int rc; struct ndr sd_ndr = {0}, acl_ndr = {0}; @@ -1457,11 +1435,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, acl.sd_buf = (char *)pntsd; acl.sd_size = len; - rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - return rc; - } + sha256(acl.sd_buf, acl.sd_size, acl.hash); smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode, ACL_TYPE_ACCESS); @@ -1476,12 +1450,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, goto out; } - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, - acl.posix_acl_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out; - } + sha256(acl_ndr.data, acl_ndr.offset, acl.posix_acl_hash); rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); if (rc) { @@ -1491,7 +1460,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, rc = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_SD, sd_ndr.data, - sd_ndr.offset, 0); + sd_ndr.offset, 0, get_write); if (rc < 0) pr_err("Failed to store XATTR ntacl :%d\n", rc); @@ -1538,11 +1507,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, goto out_free; } - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out_free; - } + sha256(acl_ndr.data, acl_ndr.offset, cmp_hash); if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { pr_err("hash value diff\n"); @@ -1580,7 +1545,8 @@ free_n_data: int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap, const struct path *path, - struct xattr_dos_attrib *da) + struct xattr_dos_attrib *da, + bool get_write) { struct ndr n; int err; @@ -1590,7 +1556,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap, return err; err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE, - (void *)n.data, n.offset, 0); + (void *)n.data, n.offset, 0, get_write); if (err) ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); kfree(n.data); @@ -1623,10 +1589,12 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap, * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format * @p: destination buffer * @ksmbd_kstat: ksmbd kstat wrapper + * + * Returns: pointer to the converted &struct file_directory_info */ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) { - struct file_directory_info *info = (struct file_directory_info *)(*p); + FILE_DIRECTORY_INFO *info = (FILE_DIRECTORY_INFO *)(*p); struct kstat *kstat = ksmbd_kstat->kstat; u64 time; @@ -1656,11 +1624,19 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat) { + struct ksmbd_share_config *share_conf = work->tcon->share_conf; u64 time; int rc; + struct path path = { + .mnt = share_conf->vfs_path.mnt, + .dentry = dentry, + }; - generic_fillattr(idmap, STATX_BASIC_STATS, d_inode(dentry), - ksmbd_kstat->kstat); + rc = vfs_getattr(&path, ksmbd_kstat->kstat, + STATX_BASIC_STATS | STATX_BTIME, + AT_STATX_SYNC_AS_STAT); + if (rc) + return rc; time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); ksmbd_kstat->create_time = time; @@ -1726,7 +1702,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, else type = ":$DATA"; - buf = kasprintf(GFP_KERNEL, "%s%s%s", + buf = kasprintf(KSMBD_DEFAULT_GFP, "%s%s%s", XATTR_NAME_STREAM, stream_name, type); if (!buf) return -ENOMEM; @@ -1794,8 +1770,19 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, if (src_off + len > src_file_size) return -E2BIG; - ret = vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len, 0); + /* + * vfs_copy_file_range does not allow overlapped copying + * within the same file. + */ + if (file_inode(src_fp->filp) == file_inode(dst_fp->filp) && + dst_off + len > src_off && + dst_off < src_off + len) + ret = do_splice_direct(src_fp->filp, &src_off, + dst_fp->filp, &dst_off, + min_t(size_t, len, MAX_RW_COUNT), 0); + else + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, 0); if (ret == -EOPNOTSUPP || ret == -EXDEV) ret = vfs_copy_file_range(src_fp->filp, src_off, dst_fp->filp, dst_off, len, @@ -1811,14 +1798,7 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) { - wait_event(flock->fl_wait, !flock->fl_blocker); -} - -int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) -{ - return wait_event_interruptible_timeout(flock->fl_wait, - !flock->fl_blocker, - timeout); + wait_event(flock->c.flc_wait, !flock->c.flc_blocker); } void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) @@ -1827,7 +1807,7 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) } int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap, - struct path *path) + const struct path *path) { struct posix_acl_state acl_state; struct posix_acl *acls; @@ -1855,17 +1835,13 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap, acl_state.group.allow; acl_state.mask.allow = 0x07; - acls = posix_acl_alloc(6, GFP_KERNEL); + acls = posix_acl_alloc(6, KSMBD_DEFAULT_GFP); if (!acls) { free_acl_state(&acl_state); return -ENOMEM; } posix_state_to_acl(&acl_state, acls->a_entries); - rc = mnt_want_write(path->mnt); - if (rc) - goto out_err; - rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", @@ -1877,16 +1853,14 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap, ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", rc); } - mnt_drop_write(path->mnt); -out_err: free_acl_state(&acl_state); posix_acl_release(acls); return rc; } int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, - struct path *path, struct inode *parent_inode) + const struct path *path, struct inode *parent_inode) { struct posix_acl *acls; struct posix_acl_entry *pace; @@ -1909,10 +1883,6 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, } } - rc = mnt_want_write(path->mnt); - if (rc) - goto out_err; - rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls); if (rc < 0) ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", @@ -1924,9 +1894,7 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", rc); } - mnt_drop_write(path->mnt); -out_err: posix_acl_release(acls); return rc; } diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h index 72f9fb4b48d1..16ca29ee16e5 100644 --- a/fs/smb/server/vfs.h +++ b/fs/smb/server/vfs.h @@ -43,6 +43,7 @@ struct ksmbd_dir_info { char *rptr; int name_len; int out_buf_len; + int num_scan; int num_entry; int data_count; int last_entry_offset; @@ -76,8 +77,8 @@ void ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap, struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); -int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, - size_t count, loff_t *pos); +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, + loff_t *pos, char *rbuf); int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written); @@ -109,14 +110,20 @@ ssize_t ksmbd_vfs_casexattr_len(struct mnt_idmap *idmap, int attr_name_len); int ksmbd_vfs_setxattr(struct mnt_idmap *idmap, const struct path *path, const char *attr_name, - void *attr_value, size_t attr_size, int flags); + void *attr_value, size_t attr_size, int flags, + bool get_write); int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, size_t *xattr_stream_name_size, int s_type); int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, - const struct path *path, char *attr_name); -int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, - unsigned int flags, struct path *parent_path, - struct path *path, bool caseless); + const struct path *path, char *attr_name, + bool get_write); +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, + struct path *path, bool caseless); +int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *name, + unsigned int flags, + struct path *path, bool caseless); +void ksmbd_vfs_kern_path_end_removing(const struct path *path); struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, const char *name, unsigned int flags, @@ -136,7 +143,6 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, struct dentry *dentry, struct ksmbd_kstat *ksmbd_kstat); void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); -int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap, const struct path *path); @@ -144,20 +150,22 @@ int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path) int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, struct mnt_idmap *idmap, const struct path *path, - struct smb_ntsd *pntsd, int len); + struct smb_ntsd *pntsd, int len, + bool get_write); int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, struct mnt_idmap *idmap, struct dentry *dentry, struct smb_ntsd **pntsd); int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap, const struct path *path, - struct xattr_dos_attrib *da); + struct xattr_dos_attrib *da, + bool get_write); int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap, struct dentry *dentry, struct xattr_dos_attrib *da); int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap, - struct path *path); + const struct path *path); int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, - struct path *path, + const struct path *path, struct inode *parent_inode); #endif /* __KSMBD_VFS_H__ */ diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index f41f8d6108ce..6ef116585af6 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -8,6 +8,8 @@ #include <linux/filelock.h> #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/kthread.h> +#include <linux/freezer.h> #include "glob.h" #include "vfs_cache.h" @@ -17,6 +19,7 @@ #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" #include "smb_common.h" +#include "server.h" #define S_DEL_PENDING 1 #define S_DEL_ON_CLS 2 @@ -31,6 +34,10 @@ static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; static struct kmem_cache *filp_cache; +static bool durable_scavenger_running; +static DEFINE_MUTEX(durable_scavenger_lock); +static wait_queue_head_t dh_wq; + void ksmbd_set_fd_limit(unsigned long limit) { limit = min(limit, get_max_files()); @@ -66,14 +73,14 @@ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) return tmp & inode_hash_mask; } -static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) +static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de) { struct hlist_head *head = inode_hashtable + - inode_hash(inode->i_sb, inode->i_ino); + inode_hash(d_inode(de)->i_sb, (unsigned long)de); struct ksmbd_inode *ci = NULL, *ret_ci = NULL; hlist_for_each_entry(ci, head, m_hash) { - if (ci->m_inode == inode) { + if (ci->m_de == de) { if (atomic_inc_not_zero(&ci->m_count)) ret_ci = ci; break; @@ -84,66 +91,89 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) { - return __ksmbd_inode_lookup(file_inode(fp->filp)); + return __ksmbd_inode_lookup(fp->filp->f_path.dentry); } -static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d) { struct ksmbd_inode *ci; read_lock(&inode_hash_lock); - ci = __ksmbd_inode_lookup(inode); + ci = __ksmbd_inode_lookup(d); read_unlock(&inode_hash_lock); + return ci; } -int ksmbd_query_inode_status(struct inode *inode) +int ksmbd_query_inode_status(struct dentry *dentry) { struct ksmbd_inode *ci; int ret = KSMBD_INODE_STATUS_UNKNOWN; read_lock(&inode_hash_lock); - ci = __ksmbd_inode_lookup(inode); - if (ci) { - ret = KSMBD_INODE_STATUS_OK; - if (ci->m_flags & S_DEL_PENDING) - ret = KSMBD_INODE_STATUS_PENDING_DELETE; - atomic_dec(&ci->m_count); - } + ci = __ksmbd_inode_lookup(dentry); read_unlock(&inode_hash_lock); + if (!ci) + return ret; + + down_read(&ci->m_lock); + if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS)) + ret = KSMBD_INODE_STATUS_PENDING_DELETE; + else + ret = KSMBD_INODE_STATUS_OK; + up_read(&ci->m_lock); + + atomic_dec(&ci->m_count); return ret; } bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) { - return (fp->f_ci->m_flags & S_DEL_PENDING); + struct ksmbd_inode *ci = fp->f_ci; + int ret; + + down_read(&ci->m_lock); + ret = (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS)); + up_read(&ci->m_lock); + + return ret; } void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) { - fp->f_ci->m_flags |= S_DEL_PENDING; + struct ksmbd_inode *ci = fp->f_ci; + + down_write(&ci->m_lock); + ci->m_flags |= S_DEL_PENDING; + up_write(&ci->m_lock); } void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) { - fp->f_ci->m_flags &= ~S_DEL_PENDING; + struct ksmbd_inode *ci = fp->f_ci; + + down_write(&ci->m_lock); + ci->m_flags &= ~S_DEL_PENDING; + up_write(&ci->m_lock); } void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info) { - if (ksmbd_stream_fd(fp)) { - fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; - return; - } + struct ksmbd_inode *ci = fp->f_ci; - fp->f_ci->m_flags |= S_DEL_ON_CLS; + down_write(&ci->m_lock); + if (ksmbd_stream_fd(fp)) + ci->m_flags |= S_DEL_ON_CLS_STREAM; + else + ci->m_flags |= S_DEL_ON_CLS; + up_write(&ci->m_lock); } static void ksmbd_inode_hash(struct ksmbd_inode *ci) { struct hlist_head *b = inode_hashtable + - inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); + inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de); hlist_add_head(&ci->m_hash, b); } @@ -157,7 +187,6 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci) static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) { - ci->m_inode = file_inode(fp->filp); atomic_set(&ci->m_count, 1); atomic_set(&ci->op_count, 0); atomic_set(&ci->sop_count, 0); @@ -165,7 +194,8 @@ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) ci->m_fattr = 0; INIT_LIST_HEAD(&ci->m_fp_list); INIT_LIST_HEAD(&ci->m_op_list); - rwlock_init(&ci->m_lock); + init_rwsem(&ci->m_lock); + ci->m_de = fp->filp->f_path.dentry; return 0; } @@ -180,7 +210,7 @@ static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) if (ci) return ci; - ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + ci = kmalloc(sizeof(struct ksmbd_inode), KSMBD_DEFAULT_GFP); if (!ci) return NULL; @@ -209,7 +239,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci) kfree(ci); } -static void ksmbd_inode_put(struct ksmbd_inode *ci) +void ksmbd_inode_put(struct ksmbd_inode *ci) { if (atomic_dec_and_test(&ci->m_count)) ksmbd_inode_free(ci); @@ -249,25 +279,40 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) struct file *filp; filp = fp->filp; - if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { - ci->m_flags &= ~S_DEL_ON_CLS_STREAM; - err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp), - &filp->f_path, - fp->stream.name); - if (err) - pr_err("remove xattr failed : %s\n", - fp->stream.name); + + if (ksmbd_stream_fd(fp)) { + bool remove_stream_xattr = false; + + down_write(&ci->m_lock); + if (ci->m_flags & S_DEL_ON_CLS_STREAM) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + remove_stream_xattr = true; + } + up_write(&ci->m_lock); + + if (remove_stream_xattr) { + err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp), + &filp->f_path, + fp->stream.name, + true); + if (err) + pr_err("remove xattr failed : %s\n", + fp->stream.name); + } } if (atomic_dec_and_test(&ci->m_count)) { - write_lock(&ci->m_lock); + bool do_unlink = false; + + down_write(&ci->m_lock); if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); - write_unlock(&ci->m_lock); - ksmbd_vfs_unlink(filp); - write_lock(&ci->m_lock); + do_unlink = true; } - write_unlock(&ci->m_lock); + up_write(&ci->m_lock); + + if (do_unlink) + ksmbd_vfs_unlink(filp); ksmbd_inode_free(ci); } @@ -278,9 +323,16 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) if (!has_file_id(fp->persistent_id)) return; - write_lock(&global_ft.lock); idr_remove(global_ft.idr, fp->persistent_id); +} + +static void ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + write_lock(&global_ft.lock); + __ksmbd_remove_durable_fd(fp); write_unlock(&global_ft.lock); + if (waitqueue_active(&dh_wq)) + wake_up(&dh_wq); } static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) @@ -288,9 +340,9 @@ static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp if (!has_file_id(fp->volatile_id)) return; - write_lock(&fp->f_ci->m_lock); + down_write(&fp->f_ci->m_lock); list_del_init(&fp->node); - write_unlock(&fp->f_ci->m_lock); + up_write(&fp->f_ci->m_lock); write_lock(&ft->lock); idr_remove(ft->idr, fp->volatile_id); @@ -303,8 +355,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) struct ksmbd_lock *smb_lock, *tmp_lock; fd_limit_close(); - __ksmbd_remove_durable_fd(fp); - __ksmbd_remove_fd(ft, fp); + ksmbd_remove_durable_fd(fp); + if (ft) + __ksmbd_remove_fd(ft, fp); close_id_del_oplock(fp); filp = fp->filp; @@ -333,6 +386,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) { + if (fp->f_state != FP_INITED) + return NULL; + if (!atomic_inc_not_zero(&fp->refcount)) return NULL; return fp; @@ -382,15 +438,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) return 0; ft = &work->sess->file_table; - read_lock(&ft->lock); + write_lock(&ft->lock); fp = idr_find(ft->idr, id); if (fp) { set_close_state_blocked_works(fp); - if (!atomic_dec_and_test(&fp->refcount)) + if (fp->f_state != FP_INITED) fp = NULL; + else { + fp->f_state = FP_CLOSED; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } } - read_unlock(&ft->lock); + write_unlock(&ft->lock); if (!fp) return -EINVAL; @@ -456,11 +517,35 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, return fp; } -struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id) { return __ksmbd_lookup_fd(&global_ft, id); } +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +{ + struct ksmbd_file *fp; + + fp = __ksmbd_lookup_fd(&global_ft, id); + if (fp && (fp->conn || + (fp->durable_scavenger_timeout && + (fp->durable_scavenger_timeout < + jiffies_to_msecs(jiffies))))) { + ksmbd_put_durable_fd(fp); + fp = NULL; + } + + return fp; +} + +void ksmbd_put_durable_fd(struct ksmbd_file *fp) +{ + if (!atomic_dec_and_test(&fp->refcount)) + return; + + __ksmbd_close_fd(NULL, fp); +} + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) { struct ksmbd_file *fp = NULL; @@ -480,26 +565,29 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) return fp; } -struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) +struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry) { struct ksmbd_file *lfp; struct ksmbd_inode *ci; + struct inode *inode = d_inode(dentry); - ci = ksmbd_inode_lookup_by_vfsinode(inode); + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(dentry); + read_unlock(&inode_hash_lock); if (!ci) return NULL; - read_lock(&ci->m_lock); + down_read(&ci->m_lock); list_for_each_entry(lfp, &ci->m_fp_list, node) { if (inode == file_inode(lfp->filp)) { atomic_dec(&ci->m_count); lfp = ksmbd_fp_get(lfp); - read_unlock(&ci->m_lock); + up_read(&ci->m_lock); return lfp; } } atomic_dec(&ci->m_count); - read_unlock(&ci->m_lock); + up_read(&ci->m_lock); return NULL; } @@ -525,7 +613,7 @@ static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, return -EMFILE; } - idr_preload(GFP_KERNEL); + idr_preload(KSMBD_DEFAULT_GFP); write_lock(&ft->lock); ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); if (ret >= 0) { @@ -553,7 +641,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) struct ksmbd_file *fp; int ret; - fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); + fp = kmem_cache_zalloc(filp_cache, KSMBD_DEFAULT_GFP); if (!fp) { pr_err("Failed to allocate memory\n"); return ERR_PTR(-ENOMEM); @@ -570,6 +658,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) fp->tcon = work->tcon; fp->volatile_id = KSMBD_NO_FID; fp->persistent_id = KSMBD_NO_FID; + fp->f_state = FP_NEW; fp->f_ci = ksmbd_inode_get(fp); if (!fp->f_ci) { @@ -591,40 +680,253 @@ err_out: return ERR_PTR(ret); } +void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state) +{ + if (!fp) + return; + + write_lock(&ft->lock); + fp->f_state = state; + write_unlock(&ft->lock); +} + static int __close_file_table_ids(struct ksmbd_file_table *ft, struct ksmbd_tree_connect *tcon, bool (*skip)(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp)) { - unsigned int id; - struct ksmbd_file *fp; - int num = 0; + struct ksmbd_file *fp; + unsigned int id = 0; + int num = 0; + + while (1) { + write_lock(&ft->lock); + fp = idr_get_next(ft->idr, &id); + if (!fp) { + write_unlock(&ft->lock); + break; + } - idr_for_each_entry(ft->idr, fp, id) { - if (skip(tcon, fp)) + if (skip(tcon, fp) || + !atomic_dec_and_test(&fp->refcount)) { + id++; + write_unlock(&ft->lock); continue; + } set_close_state_blocked_works(fp); + idr_remove(ft->idr, fp->volatile_id); + fp->volatile_id = KSMBD_NO_FID; + write_unlock(&ft->lock); + + down_write(&fp->f_ci->m_lock); + list_del_init(&fp->node); + up_write(&fp->f_ci->m_lock); - if (!atomic_dec_and_test(&fp->refcount)) - continue; __ksmbd_close_fd(ft, fp); + num++; + id++; } + return num; } +static inline bool is_reconnectable(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo = opinfo_get(fp); + bool reconn = false; + + if (!opinfo) + return false; + + if (opinfo->op_state != OPLOCK_STATE_NONE) { + opinfo_put(opinfo); + return false; + } + + if (fp->is_resilient || fp->is_persistent) + reconn = true; + else if (fp->is_durable && opinfo->is_lease && + opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + reconn = true; + + else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) + reconn = true; + + opinfo_put(opinfo); + return reconn; +} + static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { return fp->tcon != tcon; } +static bool ksmbd_durable_scavenger_alive(void) +{ + if (!durable_scavenger_running) + return false; + + if (kthread_should_stop()) + return false; + + if (idr_is_empty(global_ft.idr)) + return false; + + return true; +} + +static void ksmbd_scavenger_dispose_dh(struct list_head *head) +{ + while (!list_empty(head)) { + struct ksmbd_file *fp; + + fp = list_first_entry(head, struct ksmbd_file, node); + list_del_init(&fp->node); + __ksmbd_close_fd(NULL, fp); + } +} + +static int ksmbd_durable_scavenger(void *dummy) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + unsigned int min_timeout = 1; + bool found_fp_timeout; + LIST_HEAD(scavenger_list); + unsigned long remaining_jiffies; + + __module_get(THIS_MODULE); + + set_freezable(); + while (ksmbd_durable_scavenger_alive()) { + if (try_to_freeze()) + continue; + + found_fp_timeout = false; + + remaining_jiffies = wait_event_timeout(dh_wq, + ksmbd_durable_scavenger_alive() == false, + __msecs_to_jiffies(min_timeout)); + if (remaining_jiffies) + min_timeout = jiffies_to_msecs(remaining_jiffies); + else + min_timeout = DURABLE_HANDLE_MAX_TIMEOUT; + + write_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!fp->durable_timeout) + continue; + + if (atomic_read(&fp->refcount) > 1 || + fp->conn) + continue; + + found_fp_timeout = true; + if (fp->durable_scavenger_timeout <= + jiffies_to_msecs(jiffies)) { + __ksmbd_remove_durable_fd(fp); + list_add(&fp->node, &scavenger_list); + } else { + unsigned long durable_timeout; + + durable_timeout = + fp->durable_scavenger_timeout - + jiffies_to_msecs(jiffies); + + if (min_timeout > durable_timeout) + min_timeout = durable_timeout; + } + } + write_unlock(&global_ft.lock); + + ksmbd_scavenger_dispose_dh(&scavenger_list); + + if (found_fp_timeout == false) + break; + } + + durable_scavenger_running = false; + + module_put(THIS_MODULE); + + return 0; +} + +void ksmbd_launch_ksmbd_durable_scavenger(void) +{ + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)) + return; + + mutex_lock(&durable_scavenger_lock); + if (durable_scavenger_running == true) { + mutex_unlock(&durable_scavenger_lock); + return; + } + + durable_scavenger_running = true; + + server_conf.dh_task = kthread_run(ksmbd_durable_scavenger, + (void *)NULL, "ksmbd-durable-scavenger"); + if (IS_ERR(server_conf.dh_task)) + pr_err("cannot start conn thread, err : %ld\n", + PTR_ERR(server_conf.dh_task)); + mutex_unlock(&durable_scavenger_lock); +} + +void ksmbd_stop_durable_scavenger(void) +{ + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)) + return; + + mutex_lock(&durable_scavenger_lock); + if (!durable_scavenger_running) { + mutex_unlock(&durable_scavenger_lock); + return; + } + + durable_scavenger_running = false; + if (waitqueue_active(&dh_wq)) + wake_up(&dh_wq); + mutex_unlock(&durable_scavenger_lock); + kthread_stop(server_conf.dh_task); +} + static bool session_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { - return false; + struct ksmbd_inode *ci; + struct oplock_info *op; + struct ksmbd_conn *conn; + + if (!is_reconnectable(fp)) + return false; + + conn = fp->conn; + ci = fp->f_ci; + down_write(&ci->m_lock); + list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { + if (op->conn != conn) + continue; + if (op->conn && atomic_dec_and_test(&op->conn->refcnt)) + kfree(op->conn); + op->conn = NULL; + } + up_write(&ci->m_lock); + + fp->conn = NULL; + fp->tcon = NULL; + fp->volatile_id = KSMBD_NO_FID; + + if (fp->durable_timeout) + fp->durable_scavenger_timeout = + jiffies_to_msecs(jiffies) + fp->durable_timeout; + + return true; } void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) @@ -656,16 +958,81 @@ void ksmbd_free_global_file_table(void) unsigned int id; idr_for_each_entry(global_ft.idr, fp, id) { - __ksmbd_remove_durable_fd(fp); - kmem_cache_free(filp_cache, fp); + ksmbd_remove_durable_fd(fp); + __ksmbd_close_fd(NULL, fp); } - ksmbd_destroy_file_table(&global_ft); + idr_destroy(global_ft.idr); + kfree(global_ft.idr); +} + +int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, + struct ksmbd_file *fp, char *name) +{ + char *pathname, *ab_pathname; + int ret = 0; + + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); + if (!pathname) + return -EACCES; + + ab_pathname = d_path(&fp->filp->f_path, pathname, PATH_MAX); + if (IS_ERR(ab_pathname)) { + kfree(pathname); + return -EACCES; + } + + if (name && strcmp(&ab_pathname[share->path_sz + 1], name)) { + ksmbd_debug(SMB, "invalid name reconnect %s\n", name); + ret = -EINVAL; + } + + kfree(pathname); + + return ret; +} + +int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + struct ksmbd_inode *ci; + struct oplock_info *op; + + if (!fp->is_durable || fp->conn || fp->tcon) { + pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); + return -EBADF; + } + + if (has_file_id(fp->volatile_id)) { + pr_err("Still in use durable fd: %llu\n", fp->volatile_id); + return -EBADF; + } + + fp->conn = work->conn; + fp->tcon = work->tcon; + + ci = fp->f_ci; + down_write(&ci->m_lock); + list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { + if (op->conn) + continue; + op->conn = fp->conn; + atomic_inc(&op->conn->refcnt); + } + up_write(&ci->m_lock); + + fp->f_state = FP_NEW; + __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (!has_file_id(fp->volatile_id)) { + fp->conn = NULL; + fp->tcon = NULL; + return -EBADF; + } + return 0; } int ksmbd_init_file_table(struct ksmbd_file_table *ft) { - ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); + ft->idr = kzalloc(sizeof(struct idr), KSMBD_DEFAULT_GFP); if (!ft->idr) return -ENOMEM; @@ -693,6 +1060,8 @@ int ksmbd_init_file_cache(void) if (!filp_cache) goto out; + init_waitqueue_head(&dh_wq); + return 0; out: diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index fcb13413fa8d..78b506c5ef03 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -14,6 +14,7 @@ #include <linux/workqueue.h> #include "vfs.h" +#include "mgmt/share_config.h" /* Windows style file permissions for extended response */ #define FILE_GENERIC_ALL 0x1F01FF @@ -43,15 +44,16 @@ struct ksmbd_lock { struct stream { char *name; ssize_t size; + loff_t pos; }; struct ksmbd_inode { - rwlock_t m_lock; + struct rw_semaphore m_lock; atomic_t m_count; atomic_t op_count; /* opinfo count for streams */ atomic_t sop_count; - struct inode *m_inode; + struct dentry *m_de; unsigned int m_flags; struct hlist_node m_hash; struct list_head m_fp_list; @@ -60,6 +62,12 @@ struct ksmbd_inode { __le32 m_fattr; }; +enum { + FP_NEW = 0, + FP_INITED, + FP_CLOSED +}; + struct ksmbd_file { struct file *filp; u64 persistent_id; @@ -93,11 +101,19 @@ struct ksmbd_file { struct list_head blocked_works; struct list_head lock_list; - int durable_timeout; + unsigned int durable_timeout; + unsigned int durable_scavenger_timeout; /* if ls is happening on directory, below is valid*/ struct ksmbd_readdir_data readdir_data; int dot_dotdot[2]; + unsigned int f_state; + bool reserve_lease_break; + bool is_durable; + bool is_persistent; + bool is_resilient; + + bool is_posix_ctxt; }; static inline void set_ctx_actor(struct dir_context *ctx, @@ -131,17 +147,25 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, u64 pid); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); +struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); +void ksmbd_inode_put(struct ksmbd_inode *ci); +struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); +void ksmbd_put_durable_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); -struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); +struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); +void ksmbd_launch_ksmbd_durable_scavenger(void); +void ksmbd_stop_durable_scavenger(void); void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); void ksmbd_close_session_fds(struct ksmbd_work *work); int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); int ksmbd_init_global_file_table(void); void ksmbd_free_global_file_table(void); void ksmbd_set_fd_limit(unsigned long limit); +void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state); /* * INODE hash @@ -155,12 +179,15 @@ enum KSMBD_INODE_STATUS { KSMBD_INODE_STATUS_PENDING_DELETE, }; -int ksmbd_query_inode_status(struct inode *inode); +int ksmbd_query_inode_status(struct dentry *dentry); bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info); +int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp); +int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, + struct ksmbd_file *fp, char *name); int ksmbd_init_file_cache(void); void ksmbd_exit_file_cache(void); #endif /* __VFS_CACHE_H__ */ diff --git a/fs/smb/server/xattr.h b/fs/smb/server/xattr.h index 16499ca5c82d..505101a8104c 100644 --- a/fs/smb/server/xattr.h +++ b/fs/smb/server/xattr.h @@ -76,7 +76,7 @@ struct xattr_acl_entry { struct xattr_smb_acl { int count; int next; - struct xattr_acl_entry entries[]; + struct xattr_acl_entry entries[] __counted_by(count); }; /* 64bytes hash in xattr_ntacl is computed with sha256 */ @@ -99,7 +99,7 @@ struct xattr_ntacl { __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ }; -/* DOS ATTRIBUITE XATTR PREFIX */ +/* DOS ATTRIBUTE XATTR PREFIX */ #define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" #define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) #define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) |
