summaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/Kconfig217
-rw-r--r--fs/cifs/Makefile24
-rw-r--r--fs/cifs/asn1.c623
-rw-r--r--fs/cifs/cache.c146
-rw-r--r--fs/cifs/cifs_debug.c876
-rw-r--r--fs/cifs/cifs_debug.h86
-rw-r--r--fs/cifs/cifs_dfs_ref.c386
-rw-r--r--fs/cifs/cifs_fs_sb.h81
-rw-r--r--fs/cifs/cifs_ioctl.h50
-rw-r--r--fs/cifs/cifs_spnego.c248
-rw-r--r--fs/cifs/cifs_spnego.h47
-rw-r--r--fs/cifs/cifs_unicode.c646
-rw-r--r--fs/cifs/cifs_unicode.h419
-rw-r--r--fs/cifs/cifs_uniupr.h253
-rw-r--r--fs/cifs/cifsacl.c1239
-rw-r--r--fs/cifs/cifsacl.h115
-rw-r--r--fs/cifs/cifsencrypt.c877
-rw-r--r--fs/cifs/cifsfs.c1568
-rw-r--r--fs/cifs/cifsfs.h152
-rw-r--r--fs/cifs/cifsglob.h1834
-rw-r--r--fs/cifs/cifspdu.h2769
-rw-r--r--fs/cifs/cifsproto.h566
-rw-r--r--fs/cifs/cifssmb.c6539
-rw-r--r--fs/cifs/connect.c4832
-rw-r--r--fs/cifs/dir.c999
-rw-r--r--fs/cifs/dns_resolve.c99
-rw-r--r--fs/cifs/dns_resolve.h30
-rw-r--r--fs/cifs/export.c67
-rw-r--r--fs/cifs/file.c4292
-rw-r--r--fs/cifs/fscache.c334
-rw-r--r--fs/cifs/fscache.h164
-rw-r--r--fs/cifs/inode.c2574
-rw-r--r--fs/cifs/ioctl.c236
-rw-r--r--fs/cifs/link.c729
-rw-r--r--fs/cifs/misc.c944
-rw-r--r--fs/cifs/netmisc.c1011
-rw-r--r--fs/cifs/nterr.c687
-rw-r--r--fs/cifs/nterr.h563
-rw-r--r--fs/cifs/ntlmssp.h138
-rw-r--r--fs/cifs/readdir.c896
-rw-r--r--fs/cifs/rfc1002pdu.h74
-rw-r--r--fs/cifs/sess.c1480
-rw-r--r--fs/cifs/smb1ops.c1133
-rw-r--r--fs/cifs/smb2file.c287
-rw-r--r--fs/cifs/smb2glob.h64
-rw-r--r--fs/cifs/smb2inode.c299
-rw-r--r--fs/cifs/smb2maperror.c2490
-rw-r--r--fs/cifs/smb2misc.c882
-rw-r--r--fs/cifs/smb2ops.c3909
-rw-r--r--fs/cifs/smb2pdu.c4493
-rw-r--r--fs/cifs/smb2pdu.h1409
-rw-r--r--fs/cifs/smb2proto.h244
-rw-r--r--fs/cifs/smb2status.h1782
-rw-r--r--fs/cifs/smb2transport.c748
-rw-r--r--fs/cifs/smbdirect.c2645
-rw-r--r--fs/cifs/smbdirect.h339
-rw-r--r--fs/cifs/smbencrypt.c222
-rw-r--r--fs/cifs/smberr.h184
-rw-r--r--fs/cifs/smbfsctl.h124
-rw-r--r--fs/cifs/trace.c18
-rw-r--r--fs/cifs/trace.h494
-rw-r--r--fs/cifs/transport.c1321
-rw-r--r--fs/cifs/winucase.c663
-rw-r--r--fs/cifs/xattr.c411
64 files changed, 64071 insertions, 0 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
new file mode 100644
index 000000000..85dadb93c
--- /dev/null
+++ b/fs/cifs/Kconfig
@@ -0,0 +1,217 @@
+config CIFS
+ tristate "SMB3 and CIFS support (advanced network filesystem)"
+ depends on INET
+ select NLS
+ select CRYPTO
+ select CRYPTO_MD4
+ select CRYPTO_MD5
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ select CRYPTO_CMAC
+ select CRYPTO_HMAC
+ select CRYPTO_ARC4
+ select CRYPTO_AEAD2
+ select CRYPTO_CCM
+ select CRYPTO_ECB
+ select CRYPTO_AES
+ select CRYPTO_DES
+ help
+ This is the client VFS module for the SMB3 family of NAS protocols,
+ (including support for the most recent, most secure dialect SMB3.1.1)
+ as well as for earlier dialects such as SMB2.1, SMB2 and the older
+ Common Internet File System (CIFS) protocol. CIFS was the successor
+ to the original dialect, the Server Message Block (SMB) protocol, the
+ native file sharing mechanism for most early PC operating systems.
+
+ The SMB3 protocol is supported by most modern operating systems
+ and NAS appliances (e.g. Samba, Windows 10, Windows Server 2016,
+ MacOS) and even in the cloud (e.g. Microsoft Azure).
+ The older CIFS protocol was included in Windows NT4, 2000 and XP (and
+ later) as well by Samba (which provides excellent CIFS and SMB3
+ server support for Linux and many other operating systems). Use of
+ dialects older than SMB2.1 is often discouraged on public networks.
+ This module also provides limited support for OS/2 and Windows ME
+ and similar very old servers.
+
+ This module provides an advanced network file system client
+ for mounting to SMB3 (and CIFS) compliant servers. It includes
+ support for DFS (hierarchical name space), secure per-user
+ session establishment via Kerberos or NTLM or NTLMv2, RDMA
+ (smbdirect), advanced security features, per-share encryption,
+ directory leases, safe distributed caching (oplock), optional packet
+ signing, Unicode and other internationalization improvements.
+
+ In general, the default dialects, SMB3 and later, enable better
+ performance, security and features, than would be possible with CIFS.
+ Note that when mounting to Samba, due to the CIFS POSIX extensions,
+ CIFS mounts can provide slightly better POSIX compatibility
+ than SMB3 mounts. SMB2/SMB3 mount options are also
+ slightly simpler (compared to CIFS) due to protocol improvements.
+
+ If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y.
+
+config CIFS_STATS2
+ bool "Extended statistics"
+ depends on CIFS
+ help
+ Enabling this option will allow more detailed statistics on SMB
+ request timing to be displayed in /proc/fs/cifs/DebugData and also
+ allow optional logging of slow responses to dmesg (depending on the
+ value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details).
+ These additional statistics may have a minor effect on performance
+ and memory utilization.
+
+ Unless you are a developer or are doing network performance analysis
+ or tuning, say N.
+
+config CIFS_ALLOW_INSECURE_LEGACY
+ bool "Support legacy servers which use less secure dialects"
+ depends on CIFS
+ default y
+ help
+ Modern dialects, SMB2.1 and later (including SMB3 and 3.1.1), have
+ additional security features, including protection against
+ man-in-the-middle attacks and stronger crypto hashes, so the use
+ of legacy dialects (SMB1/CIFS and SMB2.0) is discouraged.
+
+ Disabling this option prevents users from using vers=1.0 or vers=2.0
+ on mounts with cifs.ko
+
+ If unsure, say Y.
+
+config CIFS_WEAK_PW_HASH
+ bool "Support legacy servers which use weaker LANMAN security"
+ depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY
+ help
+ Modern CIFS servers including Samba and most Windows versions
+ (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos)
+ security mechanisms. These hash the password more securely
+ than the mechanisms used in the older LANMAN version of the
+ SMB protocol but LANMAN based authentication is needed to
+ establish sessions with some old SMB servers.
+
+ Enabling this option allows the cifs module to mount to older
+ LANMAN based servers such as OS/2 and Windows 95, but such
+ mounts may be less secure than mounts using NTLM or more recent
+ security mechanisms if you are on a public network. Unless you
+ have a need to access old SMB servers (and are on a private
+ network) you probably want to say N. Even if this support
+ is enabled in the kernel build, LANMAN authentication will not be
+ used automatically. At runtime LANMAN mounts are disabled but
+ can be set to required (or optional) either in
+ /proc/fs/cifs (see fs/cifs/README for more detail) or via an
+ option on the mount command. This support is disabled by
+ default in order to reduce the possibility of a downgrade
+ attack.
+
+ If unsure, say N.
+
+config CIFS_UPCALL
+ bool "Kerberos/SPNEGO advanced session setup"
+ depends on CIFS && KEYS
+ select DNS_RESOLVER
+ help
+ Enables an upcall mechanism for CIFS which accesses userspace helper
+ utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets
+ which are needed to mount to certain secure servers (for which more
+ secure Kerberos authentication is required). If unsure, say Y.
+
+config CIFS_XATTR
+ bool "CIFS extended attributes"
+ depends on CIFS
+ help
+ Extended attributes are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page for details).
+ CIFS maps the name of extended attributes beginning with the user
+ namespace prefix to SMB/CIFS EAs. EAs are stored on Windows
+ servers without the user namespace prefix, but their names are
+ seen by Linux cifs clients prefaced by the user namespace prefix.
+ The system namespace (used by some filesystems to store ACLs) is
+ not supported at this time.
+
+ If unsure, say Y.
+
+config CIFS_POSIX
+ bool "CIFS POSIX Extensions"
+ depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR
+ help
+ Enabling this option will cause the cifs client to attempt to
+ negotiate a newer dialect with servers, such as Samba 3.0.5
+ or later, that optionally can handle more POSIX like (rather
+ than Windows like) file behavior. It also enables
+ support for POSIX ACLs (getfacl and setfacl) to servers
+ (such as Samba 3.10 and later) which can negotiate
+ CIFS POSIX ACL support. If unsure, say N.
+
+config CIFS_ACL
+ bool "Provide CIFS ACL support"
+ depends on CIFS_XATTR && KEYS
+ help
+ Allows fetching CIFS/NTFS ACL from the server. The DACL blob
+ is handed over to the application/caller. See the man
+ page for getcifsacl for more information. If unsure, say Y.
+
+config CIFS_DEBUG
+ bool "Enable CIFS debugging routines"
+ default y
+ depends on CIFS
+ help
+ Enabling this option adds helpful debugging messages to
+ the cifs code which increases the size of the cifs module.
+ If unsure, say Y.
+config CIFS_DEBUG2
+ bool "Enable additional CIFS debugging routines"
+ depends on CIFS_DEBUG
+ help
+ Enabling this option adds a few more debugging routines
+ to the cifs code which slightly increases the size of
+ the cifs module and can cause additional logging of debug
+ messages in some error paths, slowing performance. This
+ option can be turned off unless you are debugging
+ cifs problems. If unsure, say N.
+
+config CIFS_DEBUG_DUMP_KEYS
+ bool "Dump encryption keys for offline decryption (Unsafe)"
+ depends on CIFS_DEBUG
+ help
+ Enabling this will dump the encryption and decryption keys
+ used to communicate on an encrypted share connection on the
+ console. This allows Wireshark to decrypt and dissect
+ encrypted network captures. Enable this carefully.
+ If unsure, say N.
+
+config CIFS_DFS_UPCALL
+ bool "DFS feature support"
+ depends on CIFS && KEYS
+ select DNS_RESOLVER
+ help
+ Distributed File System (DFS) support is used to access shares
+ transparently in an enterprise name space, even if the share
+ moves to a different server. This feature also enables
+ an upcall mechanism for CIFS which contacts userspace helper
+ utilities to provide server name resolution (host names to
+ IP addresses) which is needed for implicit mounts of DFS junction
+ points. If unsure, say Y.
+
+config CIFS_NFSD_EXPORT
+ bool "Allow nfsd to export CIFS file system"
+ depends on CIFS && BROKEN
+ help
+ Allows NFS server to export a CIFS mounted share (nfsd over cifs)
+
+config CIFS_SMB_DIRECT
+ bool "SMB Direct support (Experimental)"
+ depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y
+ help
+ Enables SMB Direct experimental support for SMB 3.0, 3.02 and 3.1.1.
+ SMB Direct allows transferring SMB packets over RDMA. If unsure,
+ say N.
+
+config CIFS_FSCACHE
+ bool "Provide CIFS client caching support"
+ depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
+ help
+ Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
+ to be cached locally on disk through the general filesystem cache
+ manager. If unsure, say N.
+
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
new file mode 100644
index 000000000..85817991e
--- /dev/null
+++ b/fs/cifs/Makefile
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Linux CIFS/SMB2/SMB3 VFS client
+#
+ccflags-y += -I$(src) # needed for trace events
+obj-$(CONFIG_CIFS) += cifs.o
+
+cifs-y := trace.o cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o \
+ inode.o link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \
+ cifs_unicode.o nterr.o cifsencrypt.o \
+ readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o \
+ smb2ops.o smb2maperror.o smb2transport.o \
+ smb2misc.o smb2pdu.o smb2inode.o smb2file.o
+
+cifs-$(CONFIG_CIFS_XATTR) += xattr.o
+cifs-$(CONFIG_CIFS_ACL) += cifsacl.o
+
+cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
+
+cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
+
+cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
+
+cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c
new file mode 100644
index 000000000..4a9b53229
--- /dev/null
+++ b/fs/cifs/asn1.c
@@ -0,0 +1,623 @@
+/*
+ * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
+ * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
+ *
+ * Copyright (c) 2000 RP Internet (www.rpi.net.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifsproto.h"
+
+/*****************************************************************************
+ *
+ * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
+ *
+ *****************************************************************************/
+
+/* Class */
+#define ASN1_UNI 0 /* Universal */
+#define ASN1_APL 1 /* Application */
+#define ASN1_CTX 2 /* Context */
+#define ASN1_PRV 3 /* Private */
+
+/* Tag */
+#define ASN1_EOC 0 /* End Of Contents or N/A */
+#define ASN1_BOL 1 /* Boolean */
+#define ASN1_INT 2 /* Integer */
+#define ASN1_BTS 3 /* Bit String */
+#define ASN1_OTS 4 /* Octet String */
+#define ASN1_NUL 5 /* Null */
+#define ASN1_OJI 6 /* Object Identifier */
+#define ASN1_OJD 7 /* Object Description */
+#define ASN1_EXT 8 /* External */
+#define ASN1_ENUM 10 /* Enumerated */
+#define ASN1_SEQ 16 /* Sequence */
+#define ASN1_SET 17 /* Set */
+#define ASN1_NUMSTR 18 /* Numerical String */
+#define ASN1_PRNSTR 19 /* Printable String */
+#define ASN1_TEXSTR 20 /* Teletext String */
+#define ASN1_VIDSTR 21 /* Video String */
+#define ASN1_IA5STR 22 /* IA5 String */
+#define ASN1_UNITIM 23 /* Universal Time */
+#define ASN1_GENTIM 24 /* General Time */
+#define ASN1_GRASTR 25 /* Graphical String */
+#define ASN1_VISSTR 26 /* Visible String */
+#define ASN1_GENSTR 27 /* General String */
+
+/* Primitive / Constructed methods*/
+#define ASN1_PRI 0 /* Primitive */
+#define ASN1_CON 1 /* Constructed */
+
+/*
+ * Error codes.
+ */
+#define ASN1_ERR_NOERROR 0
+#define ASN1_ERR_DEC_EMPTY 2
+#define ASN1_ERR_DEC_EOC_MISMATCH 3
+#define ASN1_ERR_DEC_LENGTH_MISMATCH 4
+#define ASN1_ERR_DEC_BADVALUE 5
+
+#define SPNEGO_OID_LEN 7
+#define NTLMSSP_OID_LEN 10
+#define KRB5_OID_LEN 7
+#define KRB5U2U_OID_LEN 8
+#define MSKRB5_OID_LEN 7
+static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 };
+static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 };
+static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 };
+static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 };
+static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 };
+
+/*
+ * ASN.1 context.
+ */
+struct asn1_ctx {
+ int error; /* Error condition */
+ unsigned char *pointer; /* Octet just to be decoded */
+ unsigned char *begin; /* First octet */
+ unsigned char *end; /* Octet after last octet */
+};
+
+/*
+ * Octet string (not null terminated)
+ */
+struct asn1_octstr {
+ unsigned char *data;
+ unsigned int len;
+};
+
+static void
+asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
+{
+ ctx->begin = buf;
+ ctx->end = buf + len;
+ ctx->pointer = buf;
+ ctx->error = ASN1_ERR_NOERROR;
+}
+
+static unsigned char
+asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
+{
+ if (ctx->pointer >= ctx->end) {
+ ctx->error = ASN1_ERR_DEC_EMPTY;
+ return 0;
+ }
+ *ch = *(ctx->pointer)++;
+ return 1;
+}
+
+#if 0 /* will be needed later by spnego decoding/encoding of ntlmssp */
+static unsigned char
+asn1_enum_decode(struct asn1_ctx *ctx, __le32 *val)
+{
+ unsigned char ch;
+
+ if (ctx->pointer >= ctx->end) {
+ ctx->error = ASN1_ERR_DEC_EMPTY;
+ return 0;
+ }
+
+ ch = *(ctx->pointer)++; /* ch has 0xa, ptr points to length octet */
+ if ((ch) == ASN1_ENUM) /* if ch value is ENUM, 0xa */
+ *val = *(++(ctx->pointer)); /* value has enum value */
+ else
+ return 0;
+
+ ctx->pointer++;
+ return 1;
+}
+#endif
+
+static unsigned char
+asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
+{
+ unsigned char ch;
+
+ *tag = 0;
+
+ do {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+ *tag <<= 7;
+ *tag |= ch & 0x7F;
+ } while ((ch & 0x80) == 0x80);
+ return 1;
+}
+
+static unsigned char
+asn1_id_decode(struct asn1_ctx *ctx,
+ unsigned int *cls, unsigned int *con, unsigned int *tag)
+{
+ unsigned char ch;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *cls = (ch & 0xC0) >> 6;
+ *con = (ch & 0x20) >> 5;
+ *tag = (ch & 0x1F);
+
+ if (*tag == 0x1F) {
+ if (!asn1_tag_decode(ctx, tag))
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned char
+asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
+{
+ unsigned char ch, cnt;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ if (ch == 0x80)
+ *def = 0;
+ else {
+ *def = 1;
+
+ if (ch < 0x80)
+ *len = ch;
+ else {
+ cnt = (unsigned char) (ch & 0x7F);
+ *len = 0;
+
+ while (cnt > 0) {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+ *len <<= 8;
+ *len |= ch;
+ cnt--;
+ }
+ }
+ }
+
+ /* don't trust len bigger than ctx buffer */
+ if (*len > ctx->end - ctx->pointer)
+ return 0;
+
+ return 1;
+}
+
+static unsigned char
+asn1_header_decode(struct asn1_ctx *ctx,
+ unsigned char **eoc,
+ unsigned int *cls, unsigned int *con, unsigned int *tag)
+{
+ unsigned int def = 0;
+ unsigned int len = 0;
+
+ if (!asn1_id_decode(ctx, cls, con, tag))
+ return 0;
+
+ if (!asn1_length_decode(ctx, &def, &len))
+ return 0;
+
+ /* primitive shall be definite, indefinite shall be constructed */
+ if (*con == ASN1_PRI && !def)
+ return 0;
+
+ if (def)
+ *eoc = ctx->pointer + len;
+ else
+ *eoc = NULL;
+ return 1;
+}
+
+static unsigned char
+asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
+{
+ unsigned char ch;
+
+ if (eoc == NULL) {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ if (ch != 0x00) {
+ ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ if (ch != 0x00) {
+ ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+ return 0;
+ }
+ return 1;
+ } else {
+ if (ctx->pointer != eoc) {
+ ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
+ return 0;
+ }
+ return 1;
+ }
+}
+
+/* static unsigned char asn1_null_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc)
+{
+ ctx->pointer = eoc;
+ return 1;
+}
+
+static unsigned char asn1_long_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc, long *integer)
+{
+ unsigned char ch;
+ unsigned int len;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer = (signed char) ch;
+ len = 1;
+
+ while (ctx->pointer < eoc) {
+ if (++len > sizeof(long)) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer <<= 8;
+ *integer |= ch;
+ }
+ return 1;
+}
+
+static unsigned char asn1_uint_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc,
+ unsigned int *integer)
+{
+ unsigned char ch;
+ unsigned int len;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer = ch;
+ if (ch == 0)
+ len = 0;
+ else
+ len = 1;
+
+ while (ctx->pointer < eoc) {
+ if (++len > sizeof(unsigned int)) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer <<= 8;
+ *integer |= ch;
+ }
+ return 1;
+}
+
+static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc,
+ unsigned long *integer)
+{
+ unsigned char ch;
+ unsigned int len;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer = ch;
+ if (ch == 0)
+ len = 0;
+ else
+ len = 1;
+
+ while (ctx->pointer < eoc) {
+ if (++len > sizeof(unsigned long)) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer <<= 8;
+ *integer |= ch;
+ }
+ return 1;
+}
+
+static unsigned char
+asn1_octets_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc,
+ unsigned char **octets, unsigned int *len)
+{
+ unsigned char *ptr;
+
+ *len = 0;
+
+ *octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC);
+ if (*octets == NULL) {
+ return 0;
+ }
+
+ ptr = *octets;
+ while (ctx->pointer < eoc) {
+ if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
+ kfree(*octets);
+ *octets = NULL;
+ return 0;
+ }
+ (*len)++;
+ }
+ return 1;
+} */
+
+static unsigned char
+asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
+{
+ unsigned char ch;
+
+ *subid = 0;
+
+ do {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *subid <<= 7;
+ *subid |= ch & 0x7F;
+ } while ((ch & 0x80) == 0x80);
+ return 1;
+}
+
+static int
+asn1_oid_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc, unsigned long **oid, unsigned int *len)
+{
+ unsigned long subid;
+ unsigned int size;
+ unsigned long *optr;
+
+ size = eoc - ctx->pointer + 1;
+
+ /* first subid actually encodes first two subids */
+ if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
+ return 0;
+
+ *oid = kmalloc_array(size, sizeof(unsigned long), GFP_ATOMIC);
+ if (*oid == NULL)
+ return 0;
+
+ optr = *oid;
+
+ if (!asn1_subid_decode(ctx, &subid)) {
+ kfree(*oid);
+ *oid = NULL;
+ return 0;
+ }
+
+ if (subid < 40) {
+ optr[0] = 0;
+ optr[1] = subid;
+ } else if (subid < 80) {
+ optr[0] = 1;
+ optr[1] = subid - 40;
+ } else {
+ optr[0] = 2;
+ optr[1] = subid - 80;
+ }
+
+ *len = 2;
+ optr += 2;
+
+ while (ctx->pointer < eoc) {
+ if (++(*len) > size) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ kfree(*oid);
+ *oid = NULL;
+ return 0;
+ }
+
+ if (!asn1_subid_decode(ctx, optr++)) {
+ kfree(*oid);
+ *oid = NULL;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+compare_oid(unsigned long *oid1, unsigned int oid1len,
+ unsigned long *oid2, unsigned int oid2len)
+{
+ unsigned int i;
+
+ if (oid1len != oid2len)
+ return 0;
+ else {
+ for (i = 0; i < oid1len; i++) {
+ if (oid1[i] != oid2[i])
+ return 0;
+ }
+ return 1;
+ }
+}
+
+ /* BB check for endian conversion issues here */
+
+int
+decode_negTokenInit(unsigned char *security_blob, int length,
+ struct TCP_Server_Info *server)
+{
+ struct asn1_ctx ctx;
+ unsigned char *end;
+ unsigned char *sequence_end;
+ unsigned long *oid = NULL;
+ unsigned int cls, con, tag, oidlen, rc;
+
+ /* cifs_dump_mem(" Received SecBlob ", security_blob, length); */
+
+ asn1_open(&ctx, security_blob, length);
+
+ /* GSSAPI header */
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cifs_dbg(FYI, "Error decoding negTokenInit header\n");
+ return 0;
+ } else if ((cls != ASN1_APL) || (con != ASN1_CON)
+ || (tag != ASN1_EOC)) {
+ cifs_dbg(FYI, "cls = %d con = %d tag = %d\n", cls, con, tag);
+ return 0;
+ }
+
+ /* Check for SPNEGO OID -- remember to free obj->oid */
+ rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
+ if (rc) {
+ if ((tag == ASN1_OJI) && (con == ASN1_PRI) &&
+ (cls == ASN1_UNI)) {
+ rc = asn1_oid_decode(&ctx, end, &oid, &oidlen);
+ if (rc) {
+ rc = compare_oid(oid, oidlen, SPNEGO_OID,
+ SPNEGO_OID_LEN);
+ kfree(oid);
+ }
+ } else
+ rc = 0;
+ }
+
+ /* SPNEGO OID not present or garbled -- bail out */
+ if (!rc) {
+ cifs_dbg(FYI, "Error decoding negTokenInit header\n");
+ return 0;
+ }
+
+ /* SPNEGO */
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cifs_dbg(FYI, "Error decoding negTokenInit\n");
+ return 0;
+ } else if ((cls != ASN1_CTX) || (con != ASN1_CON)
+ || (tag != ASN1_EOC)) {
+ cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p exit 0\n",
+ cls, con, tag, end);
+ return 0;
+ }
+
+ /* negTokenInit */
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cifs_dbg(FYI, "Error decoding negTokenInit\n");
+ return 0;
+ } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
+ || (tag != ASN1_SEQ)) {
+ cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p exit 1\n",
+ cls, con, tag, end);
+ return 0;
+ }
+
+ /* sequence */
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cifs_dbg(FYI, "Error decoding 2nd part of negTokenInit\n");
+ return 0;
+ } else if ((cls != ASN1_CTX) || (con != ASN1_CON)
+ || (tag != ASN1_EOC)) {
+ cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p exit 0\n",
+ cls, con, tag, end);
+ return 0;
+ }
+
+ /* sequence of */
+ if (asn1_header_decode
+ (&ctx, &sequence_end, &cls, &con, &tag) == 0) {
+ cifs_dbg(FYI, "Error decoding 2nd part of negTokenInit\n");
+ return 0;
+ } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
+ || (tag != ASN1_SEQ)) {
+ cifs_dbg(FYI, "cls = %d con = %d tag = %d sequence_end = %p exit 1\n",
+ cls, con, tag, sequence_end);
+ return 0;
+ }
+
+ /* list of security mechanisms */
+ while (!asn1_eoc_decode(&ctx, sequence_end)) {
+ rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
+ if (!rc) {
+ cifs_dbg(FYI, "Error decoding negTokenInit hdr exit2\n");
+ return 0;
+ }
+ if ((tag == ASN1_OJI) && (con == ASN1_PRI)) {
+ if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) {
+
+ cifs_dbg(FYI, "OID len = %d oid = 0x%lx 0x%lx 0x%lx 0x%lx\n",
+ oidlen, *oid, *(oid + 1), *(oid + 2),
+ *(oid + 3));
+
+ if (compare_oid(oid, oidlen, MSKRB5_OID,
+ MSKRB5_OID_LEN))
+ server->sec_mskerberos = true;
+ else if (compare_oid(oid, oidlen, KRB5U2U_OID,
+ KRB5U2U_OID_LEN))
+ server->sec_kerberosu2u = true;
+ else if (compare_oid(oid, oidlen, KRB5_OID,
+ KRB5_OID_LEN))
+ server->sec_kerberos = true;
+ else if (compare_oid(oid, oidlen, NTLMSSP_OID,
+ NTLMSSP_OID_LEN))
+ server->sec_ntlmssp = true;
+
+ kfree(oid);
+ }
+ } else {
+ cifs_dbg(FYI, "Should be an oid what is going on?\n");
+ }
+ }
+
+ /*
+ * We currently ignore anything at the end of the SPNEGO blob after
+ * the mechTypes have been parsed, since none of that info is
+ * used at the moment.
+ */
+ return 1;
+}
diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c
new file mode 100644
index 000000000..b7420e605
--- /dev/null
+++ b/fs/cifs/cache.c
@@ -0,0 +1,146 @@
+/*
+ * fs/cifs/cache.c - CIFS filesystem cache index structure definitions
+ *
+ * Copyright (c) 2010 Novell, Inc.
+ * Authors(s): Suresh Jayaraman (sjayaraman@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "fscache.h"
+#include "cifs_debug.h"
+
+/*
+ * CIFS filesystem definition for FS-Cache
+ */
+struct fscache_netfs cifs_fscache_netfs = {
+ .name = "cifs",
+ .version = 0,
+};
+
+/*
+ * Register CIFS for caching with FS-Cache
+ */
+int cifs_fscache_register(void)
+{
+ return fscache_register_netfs(&cifs_fscache_netfs);
+}
+
+/*
+ * Unregister CIFS for caching
+ */
+void cifs_fscache_unregister(void)
+{
+ fscache_unregister_netfs(&cifs_fscache_netfs);
+}
+
+/*
+ * Server object for FS-Cache
+ */
+const struct fscache_cookie_def cifs_fscache_server_index_def = {
+ .name = "CIFS.server",
+ .type = FSCACHE_COOKIE_TYPE_INDEX,
+};
+
+/*
+ * Auxiliary data attached to CIFS superblock within the cache
+ */
+struct cifs_fscache_super_auxdata {
+ u64 resource_id; /* unique server resource id */
+};
+
+char *extract_sharename(const char *treename)
+{
+ const char *src;
+ char *delim, *dst;
+ int len;
+
+ /* skip double chars at the beginning */
+ src = treename + 2;
+
+ /* share name is always preceded by '\\' now */
+ delim = strchr(src, '\\');
+ if (!delim)
+ return ERR_PTR(-EINVAL);
+ delim++;
+ len = strlen(delim);
+
+ /* caller has to free the memory */
+ dst = kstrndup(delim, len, GFP_KERNEL);
+ if (!dst)
+ return ERR_PTR(-ENOMEM);
+
+ return dst;
+}
+
+static enum
+fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
+ const void *data,
+ uint16_t datalen,
+ loff_t object_size)
+{
+ struct cifs_fscache_super_auxdata auxdata;
+ const struct cifs_tcon *tcon = cookie_netfs_data;
+
+ if (datalen != sizeof(auxdata))
+ return FSCACHE_CHECKAUX_OBSOLETE;
+
+ memset(&auxdata, 0, sizeof(auxdata));
+ auxdata.resource_id = tcon->resource_id;
+
+ if (memcmp(data, &auxdata, datalen) != 0)
+ return FSCACHE_CHECKAUX_OBSOLETE;
+
+ return FSCACHE_CHECKAUX_OKAY;
+}
+
+/*
+ * Superblock object for FS-Cache
+ */
+const struct fscache_cookie_def cifs_fscache_super_index_def = {
+ .name = "CIFS.super",
+ .type = FSCACHE_COOKIE_TYPE_INDEX,
+ .check_aux = cifs_fscache_super_check_aux,
+};
+
+static enum
+fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
+ const void *data,
+ uint16_t datalen,
+ loff_t object_size)
+{
+ struct cifs_fscache_inode_auxdata auxdata;
+ struct cifsInodeInfo *cifsi = cookie_netfs_data;
+
+ if (datalen != sizeof(auxdata))
+ return FSCACHE_CHECKAUX_OBSOLETE;
+
+ memset(&auxdata, 0, sizeof(auxdata));
+ auxdata.eof = cifsi->server_eof;
+ auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
+ auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
+ auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
+ auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
+
+ if (memcmp(data, &auxdata, datalen) != 0)
+ return FSCACHE_CHECKAUX_OBSOLETE;
+
+ return FSCACHE_CHECKAUX_OKAY;
+}
+
+const struct fscache_cookie_def cifs_fscache_inode_object_def = {
+ .name = "CIFS.uniqueid",
+ .type = FSCACHE_COOKIE_TYPE_DATAFILE,
+ .check_aux = cifs_fscache_inode_check_aux,
+};
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
new file mode 100644
index 000000000..a85aeff90
--- /dev/null
+++ b/fs/cifs/cifs_debug.c
@@ -0,0 +1,876 @@
+/*
+ * fs/cifs_debug.c
+ *
+ * Copyright (C) International Business Machines Corp., 2000,2005
+ *
+ * Modified by Steve French (sfrench@us.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+#ifdef CONFIG_CIFS_SMB_DIRECT
+#include "smbdirect.h"
+#endif
+
+void
+cifs_dump_mem(char *label, void *data, int length)
+{
+ pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4,
+ data, length, true);
+}
+
+void cifs_dump_detail(void *buf, struct TCP_Server_Info *server)
+{
+#ifdef CONFIG_CIFS_DEBUG2
+ struct smb_hdr *smb = (struct smb_hdr *)buf;
+
+ cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n",
+ smb->Command, smb->Status.CifsError,
+ smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
+ cifs_dbg(VFS, "smb buf %p len %u\n", smb,
+ server->ops->calc_smb_size(smb, server));
+#endif /* CONFIG_CIFS_DEBUG2 */
+}
+
+void cifs_dump_mids(struct TCP_Server_Info *server)
+{
+#ifdef CONFIG_CIFS_DEBUG2
+ struct list_head *tmp;
+ struct mid_q_entry *mid_entry;
+
+ if (server == NULL)
+ return;
+
+ cifs_dbg(VFS, "Dump pending requests:\n");
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
+ mid_entry->mid_state,
+ le16_to_cpu(mid_entry->command),
+ mid_entry->pid,
+ mid_entry->callback_data,
+ mid_entry->mid);
+#ifdef CONFIG_CIFS_STATS2
+ cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n",
+ mid_entry->large_buf,
+ mid_entry->resp_buf,
+ mid_entry->when_received,
+ jiffies);
+#endif /* STATS2 */
+ cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n",
+ mid_entry->multiRsp, mid_entry->multiEnd);
+ if (mid_entry->resp_buf) {
+ cifs_dump_detail(mid_entry->resp_buf, server);
+ cifs_dump_mem("existing buf: ",
+ mid_entry->resp_buf, 62);
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+#endif /* CONFIG_CIFS_DEBUG2 */
+}
+
+#ifdef CONFIG_PROC_FS
+static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
+{
+ __u32 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
+
+ seq_printf(m, "%s Mounts: %d ", tcon->treeName, tcon->tc_count);
+ if (tcon->nativeFileSystem)
+ seq_printf(m, "Type: %s ", tcon->nativeFileSystem);
+ seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x\n\tPathComponentMax: %d Status: %d",
+ le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
+ le32_to_cpu(tcon->fsAttrInfo.Attributes),
+ le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
+ tcon->tidStatus);
+ if (dev_type == FILE_DEVICE_DISK)
+ seq_puts(m, " type: DISK ");
+ else if (dev_type == FILE_DEVICE_CD_ROM)
+ seq_puts(m, " type: CDROM ");
+ else
+ seq_printf(m, " type: %d ", dev_type);
+ if (tcon->seal)
+ seq_printf(m, " Encrypted");
+ if (tcon->nocase)
+ seq_printf(m, " nocase");
+ if (tcon->unix_ext)
+ seq_printf(m, " POSIX Extensions");
+ if (tcon->ses->server->ops->dump_share_caps)
+ tcon->ses->server->ops->dump_share_caps(m, tcon);
+
+ if (tcon->need_reconnect)
+ seq_puts(m, "\tDISCONNECTED ");
+ seq_putc(m, '\n');
+}
+
+static void
+cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
+{
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
+
+ seq_printf(m, "\t\tSpeed: %zu bps\n", iface->speed);
+ seq_puts(m, "\t\tCapabilities: ");
+ if (iface->rdma_capable)
+ seq_puts(m, "rdma ");
+ if (iface->rss_capable)
+ seq_puts(m, "rss ");
+ seq_putc(m, '\n');
+ if (iface->sockaddr.ss_family == AF_INET)
+ seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
+ else if (iface->sockaddr.ss_family == AF_INET6)
+ seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);
+}
+
+static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+{
+ struct list_head *tmp1, *tmp2, *tmp3;
+ struct mid_q_entry *mid_entry;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ int i, j;
+
+ seq_puts(m,
+ "Display Internal CIFS Data Structures for Debugging\n"
+ "---------------------------------------------------\n");
+ seq_printf(m, "CIFS Version %s\n", CIFS_VERSION);
+ seq_printf(m, "Features:");
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ seq_printf(m, " DFS");
+#endif
+#ifdef CONFIG_CIFS_FSCACHE
+ seq_printf(m, ",FSCACHE");
+#endif
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ seq_printf(m, ",SMB_DIRECT");
+#endif
+#ifdef CONFIG_CIFS_STATS2
+ seq_printf(m, ",STATS2");
+#else
+ seq_printf(m, ",STATS");
+#endif
+#ifdef CONFIG_CIFS_DEBUG2
+ seq_printf(m, ",DEBUG2");
+#elif defined(CONFIG_CIFS_DEBUG)
+ seq_printf(m, ",DEBUG");
+#endif
+#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+ seq_printf(m, ",ALLOW_INSECURE_LEGACY");
+#endif
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ seq_printf(m, ",WEAK_PW_HASH");
+#endif
+#ifdef CONFIG_CIFS_POSIX
+ seq_printf(m, ",CIFS_POSIX");
+#endif
+#ifdef CONFIG_CIFS_UPCALL
+ seq_printf(m, ",UPCALL(SPNEGO)");
+#endif
+#ifdef CONFIG_CIFS_XATTR
+ seq_printf(m, ",XATTR");
+#endif
+#ifdef CONFIG_CIFS_ACL
+ seq_printf(m, ",ACL");
+#endif
+ seq_putc(m, '\n');
+ seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
+ seq_printf(m, "Servers:");
+
+ i = 0;
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ if (!server->rdma)
+ goto skip_rdma;
+
+ if (!server->smbd_conn) {
+ seq_printf(m, "\nSMBDirect transport not available");
+ goto skip_rdma;
+ }
+
+ seq_printf(m, "\nSMBDirect (in hex) protocol version: %x "
+ "transport status: %x",
+ server->smbd_conn->protocol,
+ server->smbd_conn->transport_status);
+ seq_printf(m, "\nConn receive_credit_max: %x "
+ "send_credit_target: %x max_send_size: %x",
+ server->smbd_conn->receive_credit_max,
+ server->smbd_conn->send_credit_target,
+ server->smbd_conn->max_send_size);
+ seq_printf(m, "\nConn max_fragmented_recv_size: %x "
+ "max_fragmented_send_size: %x max_receive_size:%x",
+ server->smbd_conn->max_fragmented_recv_size,
+ server->smbd_conn->max_fragmented_send_size,
+ server->smbd_conn->max_receive_size);
+ seq_printf(m, "\nConn keep_alive_interval: %x "
+ "max_readwrite_size: %x rdma_readwrite_threshold: %x",
+ server->smbd_conn->keep_alive_interval,
+ server->smbd_conn->max_readwrite_size,
+ server->smbd_conn->rdma_readwrite_threshold);
+ seq_printf(m, "\nDebug count_get_receive_buffer: %x "
+ "count_put_receive_buffer: %x count_send_empty: %x",
+ server->smbd_conn->count_get_receive_buffer,
+ server->smbd_conn->count_put_receive_buffer,
+ server->smbd_conn->count_send_empty);
+ seq_printf(m, "\nRead Queue count_reassembly_queue: %x "
+ "count_enqueue_reassembly_queue: %x "
+ "count_dequeue_reassembly_queue: %x "
+ "fragment_reassembly_remaining: %x "
+ "reassembly_data_length: %x "
+ "reassembly_queue_length: %x",
+ server->smbd_conn->count_reassembly_queue,
+ server->smbd_conn->count_enqueue_reassembly_queue,
+ server->smbd_conn->count_dequeue_reassembly_queue,
+ server->smbd_conn->fragment_reassembly_remaining,
+ server->smbd_conn->reassembly_data_length,
+ server->smbd_conn->reassembly_queue_length);
+ seq_printf(m, "\nCurrent Credits send_credits: %x "
+ "receive_credits: %x receive_credit_target: %x",
+ atomic_read(&server->smbd_conn->send_credits),
+ atomic_read(&server->smbd_conn->receive_credits),
+ server->smbd_conn->receive_credit_target);
+ seq_printf(m, "\nPending send_pending: %x send_payload_pending:"
+ " %x smbd_send_pending: %x smbd_recv_pending: %x",
+ atomic_read(&server->smbd_conn->send_pending),
+ atomic_read(&server->smbd_conn->send_payload_pending),
+ server->smbd_conn->smbd_send_pending,
+ server->smbd_conn->smbd_recv_pending);
+ seq_printf(m, "\nReceive buffers count_receive_queue: %x "
+ "count_empty_packet_queue: %x",
+ server->smbd_conn->count_receive_queue,
+ server->smbd_conn->count_empty_packet_queue);
+ seq_printf(m, "\nMR responder_resources: %x "
+ "max_frmr_depth: %x mr_type: %x",
+ server->smbd_conn->responder_resources,
+ server->smbd_conn->max_frmr_depth,
+ server->smbd_conn->mr_type);
+ seq_printf(m, "\nMR mr_ready_count: %x mr_used_count: %x",
+ atomic_read(&server->smbd_conn->mr_ready_count),
+ atomic_read(&server->smbd_conn->mr_used_count));
+skip_rdma:
+#endif
+ seq_printf(m, "\nNumber of credits: %d Dialect 0x%x",
+ server->credits, server->dialect);
+ if (server->sign)
+ seq_printf(m, " signed");
+ if (server->posix_ext_supported)
+ seq_printf(m, " posix");
+
+ i++;
+ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifs_ses,
+ smb_ses_list);
+ if ((ses->serverDomain == NULL) ||
+ (ses->serverOS == NULL) ||
+ (ses->serverNOS == NULL)) {
+ seq_printf(m, "\n%d) Name: %s Uses: %d Capability: 0x%x\tSession Status: %d\t",
+ i, ses->serverName, ses->ses_count,
+ ses->capabilities, ses->status);
+ if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
+ seq_printf(m, "Guest\t");
+ else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
+ seq_printf(m, "Anonymous\t");
+ } else {
+ seq_printf(m,
+ "\n%d) Name: %s Domain: %s Uses: %d OS:"
+ " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
+ " session status: %d\t",
+ i, ses->serverName, ses->serverDomain,
+ ses->ses_count, ses->serverOS, ses->serverNOS,
+ ses->capabilities, ses->status);
+ }
+ if (server->rdma)
+ seq_printf(m, "RDMA\n\t");
+ seq_printf(m, "TCP status: %d\n\tLocal Users To "
+ "Server: %d SecMode: 0x%x Req On Wire: %d",
+ server->tcpStatus, server->srv_count,
+ server->sec_mode, in_flight(server));
+
+#ifdef CONFIG_CIFS_STATS2
+ seq_printf(m, " In Send: %d In MaxReq Wait: %d",
+ atomic_read(&server->in_send),
+ atomic_read(&server->num_waiters));
+#endif
+
+ seq_puts(m, "\n\tShares:");
+ j = 0;
+
+ seq_printf(m, "\n\t%d) IPC: ", j);
+ if (ses->tcon_ipc)
+ cifs_debug_tcon(m, ses->tcon_ipc);
+ else
+ seq_puts(m, "none\n");
+
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3, struct cifs_tcon,
+ tcon_list);
+ ++j;
+ seq_printf(m, "\n\t%d) ", j);
+ cifs_debug_tcon(m, tcon);
+ }
+
+ seq_puts(m, "\n\tMIDs:\n");
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp3, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp3, struct mid_q_entry,
+ qhead);
+ seq_printf(m, "\tState: %d com: %d pid:"
+ " %d cbdata: %p mid %llu\n",
+ mid_entry->mid_state,
+ le16_to_cpu(mid_entry->command),
+ mid_entry->pid,
+ mid_entry->callback_data,
+ mid_entry->mid);
+ }
+ spin_unlock(&GlobalMid_Lock);
+
+ spin_lock(&ses->iface_lock);
+ if (ses->iface_count)
+ seq_printf(m, "\n\tServer interfaces: %zu\n",
+ ses->iface_count);
+ for (j = 0; j < ses->iface_count; j++) {
+ seq_printf(m, "\t%d)\n", j);
+ cifs_dump_iface(m, &ses->iface_list[j]);
+ }
+ spin_unlock(&ses->iface_lock);
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ seq_putc(m, '\n');
+
+ /* BB add code to dump additional info such as TCP session info now */
+ return 0;
+}
+
+static ssize_t cifs_stats_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ bool bv;
+ int rc;
+ struct list_head *tmp1, *tmp2, *tmp3;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ rc = kstrtobool_from_user(buffer, count, &bv);
+ if (rc == 0) {
+#ifdef CONFIG_CIFS_STATS2
+ int i;
+
+ atomic_set(&totBufAllocCount, 0);
+ atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
+ atomic_set(&tcpSesReconnectCount, 0);
+ atomic_set(&tconInfoReconnectCount, 0);
+
+ spin_lock(&GlobalMid_Lock);
+ GlobalMaxActiveXid = 0;
+ GlobalCurrentXid = 0;
+ spin_unlock(&GlobalMid_Lock);
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+#ifdef CONFIG_CIFS_STATS2
+ for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++)
+ atomic_set(&server->smb2slowcmd[i], 0);
+#endif /* CONFIG_CIFS_STATS2 */
+ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifs_ses,
+ smb_ses_list);
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3,
+ struct cifs_tcon,
+ tcon_list);
+ atomic_set(&tcon->num_smbs_sent, 0);
+ spin_lock(&tcon->stat_lock);
+ tcon->bytes_read = 0;
+ tcon->bytes_written = 0;
+ spin_unlock(&tcon->stat_lock);
+ if (server->ops->clear_stats)
+ server->ops->clear_stats(tcon);
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ } else {
+ return rc;
+ }
+
+ return count;
+}
+
+static int cifs_stats_proc_show(struct seq_file *m, void *v)
+{
+ int i;
+#ifdef CONFIG_CIFS_STATS2
+ int j;
+#endif /* STATS2 */
+ struct list_head *tmp1, *tmp2, *tmp3;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ seq_printf(m, "Resources in use\nCIFS Session: %d\n",
+ sesInfoAllocCount.counter);
+ seq_printf(m, "Share (unique mount targets): %d\n",
+ tconInfoAllocCount.counter);
+ seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n",
+ bufAllocCount.counter,
+ cifs_min_rcv + tcpSesAllocCount.counter);
+ seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n",
+ smBufAllocCount.counter, cifs_min_small);
+#ifdef CONFIG_CIFS_STATS2
+ seq_printf(m, "Total Large %d Small %d Allocations\n",
+ atomic_read(&totBufAllocCount),
+ atomic_read(&totSmBufAllocCount));
+#endif /* CONFIG_CIFS_STATS2 */
+
+ seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&midCount));
+ seq_printf(m,
+ "\n%d session %d share reconnects\n",
+ tcpSesReconnectCount.counter, tconInfoReconnectCount.counter);
+
+ seq_printf(m,
+ "Total vfs operations: %d maximum at one time: %d\n",
+ GlobalCurrentXid, GlobalMaxActiveXid);
+
+ i = 0;
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+#ifdef CONFIG_CIFS_STATS2
+ for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
+ if (atomic_read(&server->smb2slowcmd[j]))
+ seq_printf(m, "%d slow responses from %s for command %d\n",
+ atomic_read(&server->smb2slowcmd[j]),
+ server->hostname, j);
+#endif /* STATS2 */
+ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifs_ses,
+ smb_ses_list);
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3,
+ struct cifs_tcon,
+ tcon_list);
+ i++;
+ seq_printf(m, "\n%d) %s", i, tcon->treeName);
+ if (tcon->need_reconnect)
+ seq_puts(m, "\tDISCONNECTED ");
+ seq_printf(m, "\nSMBs: %d",
+ atomic_read(&tcon->num_smbs_sent));
+ if (server->ops->print_stats)
+ server->ops->print_stats(m, tcon);
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ seq_putc(m, '\n');
+ return 0;
+}
+
+static int cifs_stats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_stats_proc_show, NULL);
+}
+
+static const struct file_operations cifs_stats_proc_fops = {
+ .open = cifs_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_stats_proc_write,
+};
+
+#ifdef CONFIG_CIFS_SMB_DIRECT
+#define PROC_FILE_DEFINE(name) \
+static ssize_t name##_write(struct file *file, const char __user *buffer, \
+ size_t count, loff_t *ppos) \
+{ \
+ int rc; \
+ rc = kstrtoint_from_user(buffer, count, 10, & name); \
+ if (rc) \
+ return rc; \
+ return count; \
+} \
+static int name##_proc_show(struct seq_file *m, void *v) \
+{ \
+ seq_printf(m, "%d\n", name ); \
+ return 0; \
+} \
+static int name##_open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, name##_proc_show, NULL); \
+} \
+\
+static const struct file_operations cifs_##name##_proc_fops = { \
+ .open = name##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+ .write = name##_write, \
+}
+
+PROC_FILE_DEFINE(rdma_readwrite_threshold);
+PROC_FILE_DEFINE(smbd_max_frmr_depth);
+PROC_FILE_DEFINE(smbd_keep_alive_interval);
+PROC_FILE_DEFINE(smbd_max_receive_size);
+PROC_FILE_DEFINE(smbd_max_fragmented_recv_size);
+PROC_FILE_DEFINE(smbd_max_send_size);
+PROC_FILE_DEFINE(smbd_send_credit_target);
+PROC_FILE_DEFINE(smbd_receive_credit_max);
+#endif
+
+static struct proc_dir_entry *proc_fs_cifs;
+static const struct file_operations cifsFYI_proc_fops;
+static const struct file_operations cifs_lookup_cache_proc_fops;
+static const struct file_operations traceSMB_proc_fops;
+static const struct file_operations cifs_security_flags_proc_fops;
+static const struct file_operations cifs_linux_ext_proc_fops;
+
+void
+cifs_proc_init(void)
+{
+ proc_fs_cifs = proc_mkdir("fs/cifs", NULL);
+ if (proc_fs_cifs == NULL)
+ return;
+
+ proc_create_single("DebugData", 0, proc_fs_cifs,
+ cifs_debug_data_proc_show);
+
+ proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_fops);
+ proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_fops);
+ proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_fops);
+ proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs,
+ &cifs_linux_ext_proc_fops);
+ proc_create("SecurityFlags", 0644, proc_fs_cifs,
+ &cifs_security_flags_proc_fops);
+ proc_create("LookupCacheEnabled", 0644, proc_fs_cifs,
+ &cifs_lookup_cache_proc_fops);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs,
+ &cifs_rdma_readwrite_threshold_proc_fops);
+ proc_create("smbd_max_frmr_depth", 0644, proc_fs_cifs,
+ &cifs_smbd_max_frmr_depth_proc_fops);
+ proc_create("smbd_keep_alive_interval", 0644, proc_fs_cifs,
+ &cifs_smbd_keep_alive_interval_proc_fops);
+ proc_create("smbd_max_receive_size", 0644, proc_fs_cifs,
+ &cifs_smbd_max_receive_size_proc_fops);
+ proc_create("smbd_max_fragmented_recv_size", 0644, proc_fs_cifs,
+ &cifs_smbd_max_fragmented_recv_size_proc_fops);
+ proc_create("smbd_max_send_size", 0644, proc_fs_cifs,
+ &cifs_smbd_max_send_size_proc_fops);
+ proc_create("smbd_send_credit_target", 0644, proc_fs_cifs,
+ &cifs_smbd_send_credit_target_proc_fops);
+ proc_create("smbd_receive_credit_max", 0644, proc_fs_cifs,
+ &cifs_smbd_receive_credit_max_proc_fops);
+#endif
+}
+
+void
+cifs_proc_clean(void)
+{
+ if (proc_fs_cifs == NULL)
+ return;
+
+ remove_proc_entry("DebugData", proc_fs_cifs);
+ remove_proc_entry("cifsFYI", proc_fs_cifs);
+ remove_proc_entry("traceSMB", proc_fs_cifs);
+ remove_proc_entry("Stats", proc_fs_cifs);
+ remove_proc_entry("SecurityFlags", proc_fs_cifs);
+ remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
+ remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs);
+ remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs);
+ remove_proc_entry("smbd_keep_alive_interval", proc_fs_cifs);
+ remove_proc_entry("smbd_max_receive_size", proc_fs_cifs);
+ remove_proc_entry("smbd_max_fragmented_recv_size", proc_fs_cifs);
+ remove_proc_entry("smbd_max_send_size", proc_fs_cifs);
+ remove_proc_entry("smbd_send_credit_target", proc_fs_cifs);
+ remove_proc_entry("smbd_receive_credit_max", proc_fs_cifs);
+#endif
+ remove_proc_entry("fs/cifs", NULL);
+}
+
+static int cifsFYI_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", cifsFYI);
+ return 0;
+}
+
+static int cifsFYI_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifsFYI_proc_show, NULL);
+}
+
+static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char c[2] = { '\0' };
+ bool bv;
+ int rc;
+
+ rc = get_user(c[0], buffer);
+ if (rc)
+ return rc;
+ if (strtobool(c, &bv) == 0)
+ cifsFYI = bv;
+ else if ((c[0] > '1') && (c[0] <= '9'))
+ cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations cifsFYI_proc_fops = {
+ .open = cifsFYI_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifsFYI_proc_write,
+};
+
+static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", linuxExtEnabled);
+ return 0;
+}
+
+static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_linux_ext_proc_show, NULL);
+}
+
+static ssize_t cifs_linux_ext_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ int rc;
+
+ rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static const struct file_operations cifs_linux_ext_proc_fops = {
+ .open = cifs_linux_ext_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_linux_ext_proc_write,
+};
+
+static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", lookupCacheEnabled);
+ return 0;
+}
+
+static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_lookup_cache_proc_show, NULL);
+}
+
+static ssize_t cifs_lookup_cache_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ int rc;
+
+ rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static const struct file_operations cifs_lookup_cache_proc_fops = {
+ .open = cifs_lookup_cache_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_lookup_cache_proc_write,
+};
+
+static int traceSMB_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", traceSMB);
+ return 0;
+}
+
+static int traceSMB_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, traceSMB_proc_show, NULL);
+}
+
+static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ int rc;
+
+ rc = kstrtobool_from_user(buffer, count, &traceSMB);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static const struct file_operations traceSMB_proc_fops = {
+ .open = traceSMB_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = traceSMB_proc_write,
+};
+
+static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "0x%x\n", global_secflags);
+ return 0;
+}
+
+static int cifs_security_flags_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_security_flags_proc_show, NULL);
+}
+
+/*
+ * Ensure that if someone sets a MUST flag, that we disable all other MAY
+ * flags except for the ones corresponding to the given MUST flag. If there are
+ * multiple MUST flags, then try to prefer more secure ones.
+ */
+static void
+cifs_security_flags_handle_must_flags(unsigned int *flags)
+{
+ unsigned int signflags = *flags & CIFSSEC_MUST_SIGN;
+
+ if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
+ *flags = CIFSSEC_MUST_KRB5;
+ else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
+ *flags = CIFSSEC_MUST_NTLMSSP;
+ else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
+ *flags = CIFSSEC_MUST_NTLMV2;
+ else if ((*flags & CIFSSEC_MUST_NTLM) == CIFSSEC_MUST_NTLM)
+ *flags = CIFSSEC_MUST_NTLM;
+ else if (CIFSSEC_MUST_LANMAN &&
+ (*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN)
+ *flags = CIFSSEC_MUST_LANMAN;
+ else if (CIFSSEC_MUST_PLNTXT &&
+ (*flags & CIFSSEC_MUST_PLNTXT) == CIFSSEC_MUST_PLNTXT)
+ *flags = CIFSSEC_MUST_PLNTXT;
+
+ *flags |= signflags;
+}
+
+static ssize_t cifs_security_flags_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ int rc;
+ unsigned int flags;
+ char flags_string[12];
+ bool bv;
+
+ if ((count < 1) || (count > 11))
+ return -EINVAL;
+
+ memset(flags_string, 0, 12);
+
+ if (copy_from_user(flags_string, buffer, count))
+ return -EFAULT;
+
+ if (count < 3) {
+ /* single char or single char followed by null */
+ if (strtobool(flags_string, &bv) == 0) {
+ global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF;
+ return count;
+ } else if (!isdigit(flags_string[0])) {
+ cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
+ flags_string);
+ return -EINVAL;
+ }
+ }
+
+ /* else we have a number */
+ rc = kstrtouint(flags_string, 0, &flags);
+ if (rc) {
+ cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
+ flags_string);
+ return rc;
+ }
+
+ cifs_dbg(FYI, "sec flags 0x%x\n", flags);
+
+ if (flags == 0) {
+ cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string);
+ return -EINVAL;
+ }
+
+ if (flags & ~CIFSSEC_MASK) {
+ cifs_dbg(VFS, "Unsupported security flags: 0x%x\n",
+ flags & ~CIFSSEC_MASK);
+ return -EINVAL;
+ }
+
+ cifs_security_flags_handle_must_flags(&flags);
+
+ /* flags look ok - update the global security flags for cifs module */
+ global_secflags = flags;
+ if (global_secflags & CIFSSEC_MUST_SIGN) {
+ /* requiring signing implies signing is allowed */
+ global_secflags |= CIFSSEC_MAY_SIGN;
+ cifs_dbg(FYI, "packet signing now required\n");
+ } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) {
+ cifs_dbg(FYI, "packet signing disabled\n");
+ }
+ /* BB should we turn on MAY flags for other MUST options? */
+ return count;
+}
+
+static const struct file_operations cifs_security_flags_proc_fops = {
+ .open = cifs_security_flags_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_security_flags_proc_write,
+};
+#else
+inline void cifs_proc_init(void)
+{
+}
+
+inline void cifs_proc_clean(void)
+{
+}
+#endif /* PROC_FS */
diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h
new file mode 100644
index 000000000..f4f3f0853
--- /dev/null
+++ b/fs/cifs/cifs_debug.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2000,2002
+ * Modified by Steve French (sfrench@us.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+#ifndef _H_CIFS_DEBUG
+#define _H_CIFS_DEBUG
+
+void cifs_dump_mem(char *label, void *data, int length);
+void cifs_dump_detail(void *buf, struct TCP_Server_Info *ptcp_info);
+void cifs_dump_mids(struct TCP_Server_Info *);
+extern bool traceSMB; /* flag which enables the function below */
+void dump_smb(void *, int);
+#define CIFS_INFO 0x01
+#define CIFS_RC 0x02
+#define CIFS_TIMER 0x04
+
+#define VFS 1
+#define FYI 2
+extern int cifsFYI;
+#ifdef CONFIG_CIFS_DEBUG2
+#define NOISY 4
+#else
+#define NOISY 0
+#endif
+#define ONCE 8
+
+/*
+ * debug ON
+ * --------
+ */
+#ifdef CONFIG_CIFS_DEBUG
+
+/* information message: e.g., configuration, major event */
+#define cifs_dbg_func(ratefunc, type, fmt, ...) \
+do { \
+ if ((type) & FYI && cifsFYI & CIFS_INFO) { \
+ pr_debug_ ## ratefunc("%s: " \
+ fmt, __FILE__, ##__VA_ARGS__); \
+ } else if ((type) & VFS) { \
+ pr_err_ ## ratefunc("CIFS VFS: " \
+ fmt, ##__VA_ARGS__); \
+ } else if ((type) & NOISY && (NOISY != 0)) { \
+ pr_debug_ ## ratefunc(fmt, ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define cifs_dbg(type, fmt, ...) \
+do { \
+ if ((type) & ONCE) \
+ cifs_dbg_func(once, \
+ type, fmt, ##__VA_ARGS__); \
+ else \
+ cifs_dbg_func(ratelimited, \
+ type, fmt, ##__VA_ARGS__); \
+} while (0)
+
+/*
+ * debug OFF
+ * ---------
+ */
+#else /* _CIFS_DEBUG */
+#define cifs_dbg(type, fmt, ...) \
+do { \
+ if (0) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+#endif
+
+#endif /* _H_CIFS_DEBUG */
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
new file mode 100644
index 000000000..563e2f626
--- /dev/null
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -0,0 +1,386 @@
+/*
+ * Contains the CIFS DFS referral mounting routines used for handling
+ * traversal via DFS junction point
+ *
+ * Copyright (c) 2007 Igor Mammedov
+ * Copyright (C) International Business Machines Corp., 2008
+ * Author(s): Igor Mammedov (niallain@gmail.com)
+ * Steve French (sfrench@us.ibm.com)
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/dcache.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <linux/vfs.h>
+#include <linux/fs.h>
+#include <linux/inet.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifsfs.h"
+#include "dns_resolve.h"
+#include "cifs_debug.h"
+#include "cifs_unicode.h"
+
+static LIST_HEAD(cifs_dfs_automount_list);
+
+static void cifs_dfs_expire_automounts(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cifs_dfs_automount_task,
+ cifs_dfs_expire_automounts);
+static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ;
+
+static void cifs_dfs_expire_automounts(struct work_struct *work)
+{
+ struct list_head *list = &cifs_dfs_automount_list;
+
+ mark_mounts_for_expiry(list);
+ if (!list_empty(list))
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
+}
+
+void cifs_dfs_release_automount_timer(void)
+{
+ BUG_ON(!list_empty(&cifs_dfs_automount_list));
+ cancel_delayed_work_sync(&cifs_dfs_automount_task);
+}
+
+/**
+ * cifs_build_devname - build a devicename from a UNC and optional prepath
+ * @nodename: pointer to UNC string
+ * @prepath: pointer to prefixpath (or NULL if there isn't one)
+ *
+ * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer
+ * big enough to hold the final thing. Copy the UNC from the nodename, and
+ * concatenate the prepath onto the end of it if there is one.
+ *
+ * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
+ * for freeing the returned string.
+ */
+static char *
+cifs_build_devname(char *nodename, const char *prepath)
+{
+ size_t pplen;
+ size_t unclen;
+ char *dev;
+ char *pos;
+
+ /* skip over any preceding delimiters */
+ nodename += strspn(nodename, "\\");
+ if (!*nodename)
+ return ERR_PTR(-EINVAL);
+
+ /* get length of UNC and set pos to last char */
+ unclen = strlen(nodename);
+ pos = nodename + unclen - 1;
+
+ /* trim off any trailing delimiters */
+ while (*pos == '\\') {
+ --pos;
+ --unclen;
+ }
+
+ /* allocate a buffer:
+ * +2 for preceding "//"
+ * +1 for delimiter between UNC and prepath
+ * +1 for trailing NULL
+ */
+ pplen = prepath ? strlen(prepath) : 0;
+ dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ pos = dev;
+ /* add the initial "//" */
+ *pos = '/';
+ ++pos;
+ *pos = '/';
+ ++pos;
+
+ /* copy in the UNC portion from referral */
+ memcpy(pos, nodename, unclen);
+ pos += unclen;
+
+ /* copy the prefixpath remainder (if there is one) */
+ if (pplen) {
+ *pos = '/';
+ ++pos;
+ memcpy(pos, prepath, pplen);
+ pos += pplen;
+ }
+
+ /* NULL terminator */
+ *pos = '\0';
+
+ convert_delimiter(dev, '/');
+ return dev;
+}
+
+
+/**
+ * cifs_compose_mount_options - creates mount options for refferral
+ * @sb_mountdata: parent/root DFS mount options (template)
+ * @fullpath: full path in UNC format
+ * @ref: server's referral
+ * @devname: pointer for saving device name
+ *
+ * creates mount options for submount based on template options sb_mountdata
+ * and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
+ *
+ * Returns: pointer to new mount options or ERR_PTR.
+ * Caller is responcible for freeing retunrned value if it is not error.
+ */
+char *cifs_compose_mount_options(const char *sb_mountdata,
+ const char *fullpath,
+ const struct dfs_info3_param *ref,
+ char **devname)
+{
+ int rc;
+ char *mountdata = NULL;
+ const char *prepath = NULL;
+ int md_len;
+ char *tkn_e;
+ char *srvIP = NULL;
+ char sep = ',';
+ int off, noff;
+
+ if (sb_mountdata == NULL)
+ return ERR_PTR(-EINVAL);
+
+ if (strlen(fullpath) - ref->path_consumed) {
+ prepath = fullpath + ref->path_consumed;
+ /* skip initial delimiter */
+ if (*prepath == '/' || *prepath == '\\')
+ prepath++;
+ }
+
+ *devname = cifs_build_devname(ref->node_name, prepath);
+ if (IS_ERR(*devname)) {
+ rc = PTR_ERR(*devname);
+ *devname = NULL;
+ goto compose_mount_options_err;
+ }
+
+ rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
+ __func__, *devname, rc);
+ goto compose_mount_options_err;
+ }
+
+ /*
+ * In most cases, we'll be building a shorter string than the original,
+ * but we do have to assume that the address in the ip= option may be
+ * much longer than the original. Add the max length of an address
+ * string to the length of the original string to allow for worst case.
+ */
+ md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
+ mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL);
+ if (mountdata == NULL) {
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
+ }
+
+ /* copy all options except of unc,ip,prefixpath */
+ off = 0;
+ if (strncmp(sb_mountdata, "sep=", 4) == 0) {
+ sep = sb_mountdata[4];
+ strncpy(mountdata, sb_mountdata, 5);
+ off += 5;
+ }
+
+ do {
+ tkn_e = strchr(sb_mountdata + off, sep);
+ if (tkn_e == NULL)
+ noff = strlen(sb_mountdata + off);
+ else
+ noff = tkn_e - (sb_mountdata + off) + 1;
+
+ if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
+ off += noff;
+ continue;
+ }
+ if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) {
+ off += noff;
+ continue;
+ }
+ if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
+ off += noff;
+ continue;
+ }
+ strncat(mountdata, sb_mountdata + off, noff);
+ off += noff;
+ } while (tkn_e);
+ strcat(mountdata, sb_mountdata + off);
+ mountdata[md_len] = '\0';
+
+ /* copy new IP and ref share name */
+ if (mountdata[strlen(mountdata) - 1] != sep)
+ strncat(mountdata, &sep, 1);
+ strcat(mountdata, "ip=");
+ strcat(mountdata, srvIP);
+
+ /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
+ /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
+
+compose_mount_options_out:
+ kfree(srvIP);
+ return mountdata;
+
+compose_mount_options_err:
+ kfree(mountdata);
+ mountdata = ERR_PTR(rc);
+ kfree(*devname);
+ *devname = NULL;
+ goto compose_mount_options_out;
+}
+
+/**
+ * cifs_dfs_do_refmount - mounts specified path using provided refferal
+ * @cifs_sb: parent/root superblock
+ * @fullpath: full path in UNC format
+ * @ref: server's referral
+ */
+static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
+ struct cifs_sb_info *cifs_sb,
+ const char *fullpath, const struct dfs_info3_param *ref)
+{
+ struct vfsmount *mnt;
+ char *mountdata;
+ char *devname = NULL;
+
+ /* strip first '\' from fullpath */
+ mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
+ fullpath + 1, ref, &devname);
+
+ if (IS_ERR(mountdata))
+ return (struct vfsmount *)mountdata;
+
+ mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata);
+ kfree(mountdata);
+ kfree(devname);
+ return mnt;
+
+}
+
+static void dump_referral(const struct dfs_info3_param *ref)
+{
+ cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name);
+ cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name);
+ cifs_dbg(FYI, "DFS: fl: %d, srv_type: %d\n",
+ ref->flags, ref->server_type);
+ cifs_dbg(FYI, "DFS: ref_flags: %d, path_consumed: %d\n",
+ ref->ref_flag, ref->path_consumed);
+}
+
+/*
+ * Create a vfsmount that we can automount
+ */
+static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
+{
+ struct dfs_info3_param *referrals = NULL;
+ unsigned int num_referrals = 0;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_ses *ses;
+ char *full_path;
+ unsigned int xid;
+ int i;
+ int rc;
+ struct vfsmount *mnt;
+ struct tcon_link *tlink;
+
+ cifs_dbg(FYI, "in %s\n", __func__);
+ BUG_ON(IS_ROOT(mntpt));
+
+ /*
+ * The MSDFS spec states that paths in DFS referral requests and
+ * responses must be prefixed by a single '\' character instead of
+ * the double backslashes usually used in the UNC. This function
+ * gives us the latter, so we must adjust the result.
+ */
+ mnt = ERR_PTR(-ENOMEM);
+
+ /* always use tree name prefix */
+ full_path = build_path_from_dentry_optional_prefix(mntpt, true);
+ if (full_path == NULL)
+ goto cdda_exit;
+
+ cifs_sb = CIFS_SB(mntpt->d_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ mnt = ERR_CAST(tlink);
+ goto free_full_path;
+ }
+ ses = tlink_tcon(tlink)->ses;
+
+ xid = get_xid();
+ rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,
+ &num_referrals, &referrals,
+ cifs_remap(cifs_sb));
+ free_xid(xid);
+
+ cifs_put_tlink(tlink);
+
+ mnt = ERR_PTR(-ENOENT);
+ for (i = 0; i < num_referrals; i++) {
+ int len;
+ dump_referral(referrals + i);
+ /* connect to a node */
+ len = strlen(referrals[i].node_name);
+ if (len < 2) {
+ cifs_dbg(VFS, "%s: Net Address path too short: %s\n",
+ __func__, referrals[i].node_name);
+ mnt = ERR_PTR(-EINVAL);
+ break;
+ }
+ mnt = cifs_dfs_do_refmount(mntpt, cifs_sb,
+ full_path, referrals + i);
+ cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n",
+ __func__, referrals[i].node_name, mnt);
+ if (!IS_ERR(mnt))
+ goto success;
+ }
+
+ /* no valid submounts were found; return error from get_dfs_path() by
+ * preference */
+ if (rc != 0)
+ mnt = ERR_PTR(rc);
+
+success:
+ free_dfs_info_array(referrals, num_referrals);
+free_full_path:
+ kfree(full_path);
+cdda_exit:
+ cifs_dbg(FYI, "leaving %s\n" , __func__);
+ return mnt;
+}
+
+/*
+ * Attempt to automount the referral
+ */
+struct vfsmount *cifs_dfs_d_automount(struct path *path)
+{
+ struct vfsmount *newmnt;
+
+ cifs_dbg(FYI, "in %s\n", __func__);
+
+ newmnt = cifs_dfs_do_automount(path->dentry);
+ if (IS_ERR(newmnt)) {
+ cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__);
+ return newmnt;
+ }
+
+ mntget(newmnt); /* prevent immediate expiration */
+ mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
+ cifs_dbg(FYI, "leaving %s [ok]\n" , __func__);
+ return newmnt;
+}
+
+const struct inode_operations cifs_dfs_referral_inode_operations = {
+};
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
new file mode 100644
index 000000000..aba2b48d4
--- /dev/null
+++ b/fs/cifs/cifs_fs_sb.h
@@ -0,0 +1,81 @@
+/*
+ * fs/cifs/cifs_fs_sb.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ */
+#include <linux/rbtree.h>
+
+#ifndef _CIFS_FS_SB_H
+#define _CIFS_FS_SB_H
+
+#include <linux/backing-dev.h>
+
+#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */
+#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */
+#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */
+#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */
+#define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */
+#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
+#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible*/
+#define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */
+#define CIFS_MOUNT_NO_BRL 0x100 /* No sending byte range locks to srv */
+#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */
+#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */
+#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */
+#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */
+#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */
+#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/
+#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */
+#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */
+#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */
+#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */
+#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */
+#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of SB_POSIXACL in mnt_cifs_flags */
+#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
+#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
+#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
+#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
+ * root mountable
+ */
+#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */
+#define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */
+
+struct cifs_sb_info {
+ struct rb_root tlink_tree;
+ spinlock_t tlink_tree_lock;
+ struct tcon_link *master_tlink;
+ struct nls_table *local_nls;
+ unsigned int rsize;
+ unsigned int wsize;
+ unsigned long actimeo; /* attribute cache timeout (jiffies) */
+ atomic_t active;
+ kuid_t mnt_uid;
+ kgid_t mnt_gid;
+ kuid_t mnt_backupuid;
+ kgid_t mnt_backupgid;
+ umode_t mnt_file_mode;
+ umode_t mnt_dir_mode;
+ unsigned int mnt_cifs_flags;
+ char *mountdata; /* options received at mount time or via DFS refs */
+ struct delayed_work prune_tlinks;
+ struct rcu_head rcu;
+ char *prepath;
+ /*
+ * Indicate whether serverino option was turned off later
+ * (cifs_autodisable_serverino) in order to match new mounts.
+ */
+ bool mnt_cifs_serverino_autodisabled;
+};
+#endif /* _CIFS_FS_SB_H */
diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
new file mode 100644
index 000000000..57ff0756e
--- /dev/null
+++ b/fs/cifs/cifs_ioctl.h
@@ -0,0 +1,50 @@
+/*
+ * fs/cifs/cifs_ioctl.h
+ *
+ * Structure definitions for io control for cifs/smb3
+ *
+ * Copyright (c) 2015 Steve French <steve.french@primarydata.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ */
+
+struct smb_mnt_fs_info {
+ __u32 version; /* 0001 */
+ __u16 protocol_id;
+ __u16 tcon_flags;
+ __u32 vol_serial_number;
+ __u32 vol_create_time;
+ __u32 share_caps;
+ __u32 share_flags;
+ __u32 sector_flags;
+ __u32 optimal_sector_size;
+ __u32 max_bytes_chunk;
+ __u32 fs_attributes;
+ __u32 max_path_component;
+ __u32 device_type;
+ __u32 device_characteristics;
+ __u32 maximal_access;
+ __u64 cifs_posix_caps;
+} __packed;
+
+struct smb_snapshot_array {
+ __u32 number_of_snapshots;
+ __u32 number_of_snapshots_returned;
+ __u32 snapshot_array_size;
+ /* snapshots[]; */
+} __packed;
+
+#define CIFS_IOCTL_MAGIC 0xCF
+#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
+#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4)
+#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
+#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
new file mode 100644
index 000000000..7f01c6e60
--- /dev/null
+++ b/fs/cifs/cifs_spnego.c
@@ -0,0 +1,248 @@
+/*
+ * fs/cifs/cifs_spnego.c -- SPNEGO upcall management for CIFS
+ *
+ * Copyright (c) 2007 Red Hat, Inc.
+ * Author(s): Jeff Layton (jlayton@redhat.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <keys/user-type.h>
+#include <linux/key-type.h>
+#include <linux/keyctl.h>
+#include <linux/inet.h>
+#include "cifsglob.h"
+#include "cifs_spnego.h"
+#include "cifs_debug.h"
+#include "cifsproto.h"
+static const struct cred *spnego_cred;
+
+/* create a new cifs key */
+static int
+cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
+{
+ char *payload;
+ int ret;
+
+ ret = -ENOMEM;
+ payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
+ if (!payload)
+ goto error;
+
+ /* attach the data */
+ key->payload.data[0] = payload;
+ ret = 0;
+
+error:
+ return ret;
+}
+
+static void
+cifs_spnego_key_destroy(struct key *key)
+{
+ kfree(key->payload.data[0]);
+}
+
+
+/*
+ * keytype for CIFS spnego keys
+ */
+struct key_type cifs_spnego_key_type = {
+ .name = "cifs.spnego",
+ .instantiate = cifs_spnego_key_instantiate,
+ .destroy = cifs_spnego_key_destroy,
+ .describe = user_describe,
+};
+
+/* length of longest version string e.g. strlen("ver=0xFF") */
+#define MAX_VER_STR_LEN 8
+
+/* length of longest security mechanism name, eg in future could have
+ * strlen(";sec=ntlmsspi") */
+#define MAX_MECH_STR_LEN 13
+
+/* strlen of "host=" */
+#define HOST_KEY_LEN 5
+
+/* strlen of ";ip4=" or ";ip6=" */
+#define IP_KEY_LEN 5
+
+/* strlen of ";uid=0x" */
+#define UID_KEY_LEN 7
+
+/* strlen of ";creduid=0x" */
+#define CREDUID_KEY_LEN 11
+
+/* strlen of ";user=" */
+#define USER_KEY_LEN 6
+
+/* strlen of ";pid=0x" */
+#define PID_KEY_LEN 7
+
+/* get a key struct with a SPNEGO security blob, suitable for session setup */
+struct key *
+cifs_get_spnego_key(struct cifs_ses *sesInfo)
+{
+ struct TCP_Server_Info *server = sesInfo->server;
+ struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
+ char *description, *dp;
+ size_t desc_len;
+ struct key *spnego_key;
+ const char *hostname = server->hostname;
+ const struct cred *saved_cred;
+
+ /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
+ host=hostname sec=mechanism uid=0xFF user=username */
+ desc_len = MAX_VER_STR_LEN +
+ HOST_KEY_LEN + strlen(hostname) +
+ IP_KEY_LEN + INET6_ADDRSTRLEN +
+ MAX_MECH_STR_LEN +
+ UID_KEY_LEN + (sizeof(uid_t) * 2) +
+ CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
+ PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
+
+ if (sesInfo->user_name)
+ desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
+
+ spnego_key = ERR_PTR(-ENOMEM);
+ description = kzalloc(desc_len, GFP_KERNEL);
+ if (description == NULL)
+ goto out;
+
+ dp = description;
+ /* start with version and hostname portion of UNC string */
+ spnego_key = ERR_PTR(-EINVAL);
+ sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
+ hostname);
+ dp = description + strlen(description);
+
+ /* add the server address */
+ if (server->dstaddr.ss_family == AF_INET)
+ sprintf(dp, "ip4=%pI4", &sa->sin_addr);
+ else if (server->dstaddr.ss_family == AF_INET6)
+ sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
+ else
+ goto out;
+
+ dp = description + strlen(description);
+
+ /* for now, only sec=krb5 and sec=mskrb5 are valid */
+ if (server->sec_kerberos)
+ sprintf(dp, ";sec=krb5");
+ else if (server->sec_mskerberos)
+ sprintf(dp, ";sec=mskrb5");
+ else {
+ cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
+ sprintf(dp, ";sec=krb5");
+ }
+
+ dp = description + strlen(description);
+ sprintf(dp, ";uid=0x%x",
+ from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
+
+ dp = description + strlen(description);
+ sprintf(dp, ";creduid=0x%x",
+ from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
+
+ if (sesInfo->user_name) {
+ dp = description + strlen(description);
+ sprintf(dp, ";user=%s", sesInfo->user_name);
+ }
+
+ dp = description + strlen(description);
+ sprintf(dp, ";pid=0x%x", current->pid);
+
+ cifs_dbg(FYI, "key description = %s\n", description);
+ saved_cred = override_creds(spnego_cred);
+ spnego_key = request_key(&cifs_spnego_key_type, description, "");
+ revert_creds(saved_cred);
+
+#ifdef CONFIG_CIFS_DEBUG2
+ if (cifsFYI && !IS_ERR(spnego_key)) {
+ struct cifs_spnego_msg *msg = spnego_key->payload.data[0];
+ cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
+ msg->secblob_len + msg->sesskey_len));
+ }
+#endif /* CONFIG_CIFS_DEBUG2 */
+
+out:
+ kfree(description);
+ return spnego_key;
+}
+
+int
+init_cifs_spnego(void)
+{
+ struct cred *cred;
+ struct key *keyring;
+ int ret;
+
+ cifs_dbg(FYI, "Registering the %s key type\n",
+ cifs_spnego_key_type.name);
+
+ /*
+ * Create an override credential set with special thread keyring for
+ * spnego upcalls.
+ */
+
+ cred = prepare_kernel_cred(NULL);
+ if (!cred)
+ return -ENOMEM;
+
+ keyring = keyring_alloc(".cifs_spnego",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto failed_put_cred;
+ }
+
+ ret = register_key_type(&cifs_spnego_key_type);
+ if (ret < 0)
+ goto failed_put_key;
+
+ /*
+ * instruct request_key() to use this special keyring as a cache for
+ * the results it looks up
+ */
+ set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
+ cred->thread_keyring = keyring;
+ cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+ spnego_cred = cred;
+
+ cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
+ return 0;
+
+failed_put_key:
+ key_put(keyring);
+failed_put_cred:
+ put_cred(cred);
+ return ret;
+}
+
+void
+exit_cifs_spnego(void)
+{
+ key_revoke(spnego_cred->thread_keyring);
+ unregister_key_type(&cifs_spnego_key_type);
+ put_cred(spnego_cred);
+ cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
+}
diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h
new file mode 100644
index 000000000..31bef9ee0
--- /dev/null
+++ b/fs/cifs/cifs_spnego.h
@@ -0,0 +1,47 @@
+/*
+ * fs/cifs/cifs_spnego.h -- SPNEGO upcall management for CIFS
+ *
+ * Copyright (c) 2007 Red Hat, Inc.
+ * Author(s): Jeff Layton (jlayton@redhat.com)
+ * Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFS_SPNEGO_H
+#define _CIFS_SPNEGO_H
+
+#define CIFS_SPNEGO_UPCALL_VERSION 2
+
+/*
+ * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION.
+ * The flags field is for future use. The request-key callout should set
+ * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob
+ * and stuff it in the data field.
+ */
+struct cifs_spnego_msg {
+ uint32_t version;
+ uint32_t flags;
+ uint32_t sesskey_len;
+ uint32_t secblob_len;
+ uint8_t data[1];
+};
+
+#ifdef __KERNEL__
+extern struct key_type cifs_spnego_key_type;
+extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo);
+#endif /* KERNEL */
+
+#endif /* _CIFS_SPNEGO_H */
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
new file mode 100644
index 000000000..7932e2055
--- /dev/null
+++ b/fs/cifs/cifs_unicode.c
@@ -0,0 +1,646 @@
+/*
+ * fs/cifs/cifs_unicode.c
+ *
+ * Copyright (c) International Business Machines Corp., 2000,2009
+ * Modified by Steve French (sfrench@us.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "cifs_uniupr.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+
+int cifs_remap(struct cifs_sb_info *cifs_sb)
+{
+ int map_type;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
+ map_type = SFM_MAP_UNI_RSVD;
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+ map_type = SFU_MAP_UNI_RSVD;
+ else
+ map_type = NO_MAP_UNI_RSVD;
+
+ return map_type;
+}
+
+/* Convert character using the SFU - "Services for Unix" remapping range */
+static bool
+convert_sfu_char(const __u16 src_char, char *target)
+{
+ /*
+ * BB: Cannot handle remapping UNI_SLASH until all the calls to
+ * build_path_from_dentry are modified, as they use slash as
+ * separator.
+ */
+ switch (src_char) {
+ case UNI_COLON:
+ *target = ':';
+ break;
+ case UNI_ASTERISK:
+ *target = '*';
+ break;
+ case UNI_QUESTION:
+ *target = '?';
+ break;
+ case UNI_PIPE:
+ *target = '|';
+ break;
+ case UNI_GRTRTHAN:
+ *target = '>';
+ break;
+ case UNI_LESSTHAN:
+ *target = '<';
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+/* Convert character using the SFM - "Services for Mac" remapping range */
+static bool
+convert_sfm_char(const __u16 src_char, char *target)
+{
+ if (src_char >= 0xF001 && src_char <= 0xF01F) {
+ *target = src_char - 0xF000;
+ return true;
+ }
+ switch (src_char) {
+ case SFM_COLON:
+ *target = ':';
+ break;
+ case SFM_DOUBLEQUOTE:
+ *target = '"';
+ break;
+ case SFM_ASTERISK:
+ *target = '*';
+ break;
+ case SFM_QUESTION:
+ *target = '?';
+ break;
+ case SFM_PIPE:
+ *target = '|';
+ break;
+ case SFM_GRTRTHAN:
+ *target = '>';
+ break;
+ case SFM_LESSTHAN:
+ *target = '<';
+ break;
+ case SFM_SPACE:
+ *target = ' ';
+ break;
+ case SFM_PERIOD:
+ *target = '.';
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+/*
+ * 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
+ * @cp - codepage to which character should be converted
+ * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2?
+ *
+ * This function handles the conversion of a single character. It is the
+ * responsibility of the caller to ensure that the target buffer is large
+ * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
+ */
+static int
+cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp,
+ int maptype)
+{
+ int len = 1;
+ __u16 src_char;
+
+ src_char = *from;
+
+ if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target))
+ return len;
+ else if ((maptype == SFU_MAP_UNI_RSVD) &&
+ convert_sfu_char(src_char, target))
+ return len;
+
+ /* if character not one of seven in special remap set */
+ len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
+ if (len <= 0)
+ goto surrogate_pair;
+
+ return len;
+
+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;
+ return len;
+}
+
+/*
+ * cifs_from_utf16 - convert utf16le string to local charset
+ * @to - destination buffer
+ * @from - source buffer
+ * @tolen - destination buffer size (in bytes)
+ * @fromlen - source buffer size (in bytes)
+ * @codepage - codepage to which characters should be converted
+ * @mapchar - should characters be remapped according to the mapchars option?
+ *
+ * Convert a little-endian utf16le string (as sent by the server) to a string
+ * in the provided codepage. The tolen and fromlen parameters are to ensure
+ * that the code doesn't walk off of the end of the buffer (which is always
+ * a danger if the alignment of the source buffer is off). The destination
+ * string is always properly null terminated and fits in the destination
+ * buffer. Returns the length of the destination string in bytes (including
+ * null terminator).
+ *
+ * Note that some windows versions actually send multiword UTF-16 characters
+ * instead of straight UTF16-2. The linux nls routines however aren't able to
+ * deal with those characters properly. In the event that we get some of
+ * those characters, they won't be translated properly.
+ */
+int
+cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
+ const struct nls_table *codepage, int map_type)
+{
+ int i, charlen, safelen;
+ int outlen = 0;
+ int nullsize = nls_nullsize(codepage);
+ int fromwords = fromlen / 2;
+ char tmp[NLS_MAX_CHARSET_SIZE];
+ __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */
+
+ /*
+ * because the chars can be of varying widths, we need to take care
+ * not to overflow the destination buffer when we get close to the
+ * end of it. Until we get to this offset, we don't need to check
+ * for overflow however.
+ */
+ safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
+
+ for (i = 0; i < fromwords; i++) {
+ ftmp[0] = get_unaligned_le16(&from[i]);
+ if (ftmp[0] == 0)
+ break;
+ if (i + 1 < fromwords)
+ ftmp[1] = get_unaligned_le16(&from[i + 1]);
+ else
+ ftmp[1] = 0;
+ if (i + 2 < fromwords)
+ ftmp[2] = get_unaligned_le16(&from[i + 2]);
+ else
+ ftmp[2] = 0;
+
+ /*
+ * check to see if converting this character might make the
+ * conversion bleed into the null terminator
+ */
+ if (outlen >= safelen) {
+ charlen = cifs_mapchar(tmp, ftmp, codepage, map_type);
+ if ((outlen + charlen) > (tolen - nullsize))
+ break;
+ }
+
+ /* put converted char into 'to' buffer */
+ charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type);
+ 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 */
+ for (i = 0; i < nullsize; i++)
+ to[outlen++] = 0;
+
+ return outlen;
+}
+
+/*
+ * NAME: cifs_strtoUTF16()
+ *
+ * FUNCTION: Convert character string to unicode string
+ *
+ */
+int
+cifs_strtoUTF16(__le16 *to, const char *from, int len,
+ const struct nls_table *codepage)
+{
+ int charlen;
+ int i;
+ wchar_t wchar_to; /* needed to quiet sparse */
+
+ /* special case for utf8 to handle no plane0 chars */
+ if (!strcmp(codepage->charset, "utf8")) {
+ /*
+ * convert utf8 -> utf16, we assume we have enough space
+ * as caller should have assumed conversion does not overflow
+ * in destination len is length in wchar_t units (16bits)
+ */
+ i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *) to, len);
+
+ /* if success terminate and exit */
+ if (i >= 0)
+ goto success;
+ /*
+ * if fails fall back to UCS encoding as this
+ * function should not return negative values
+ * currently can fail only if source contains
+ * invalid encoded characters
+ */
+ }
+
+ for (i = 0; len && *from; i++, from += charlen, len -= charlen) {
+ charlen = codepage->char2uni(from, len, &wchar_to);
+ if (charlen < 1) {
+ cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n",
+ *from, charlen);
+ /* A question mark */
+ wchar_to = 0x003f;
+ charlen = 1;
+ }
+ put_unaligned_le16(wchar_to, &to[i]);
+ }
+
+success:
+ put_unaligned_le16(0, &to[i]);
+ return i;
+}
+
+/*
+ * cifs_utf16_bytes - how long will a string be after conversion?
+ * @utf16 - 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.
+ */
+int
+cifs_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[3];
+
+ for (i = 0; i < maxwords; i++) {
+ ftmp[0] = get_unaligned_le16(&from[i]);
+ if (ftmp[0] == 0)
+ break;
+ if (i + 1 < maxwords)
+ ftmp[1] = get_unaligned_le16(&from[i + 1]);
+ else
+ ftmp[1] = 0;
+ if (i + 2 < maxwords)
+ ftmp[2] = get_unaligned_le16(&from[i + 2]);
+ else
+ ftmp[2] = 0;
+
+ charlen = cifs_mapchar(tmp, ftmp, codepage, NO_MAP_UNI_RSVD);
+ outlen += charlen;
+ }
+
+ return outlen;
+}
+
+/*
+ * cifs_strndup_from_utf16 - copy a string from wire format to the local
+ * codepage
+ * @src - source string
+ * @maxlen - don't walk past this many bytes in the source string
+ * @is_unicode - is this a unicode string?
+ * @codepage - destination codepage
+ *
+ * Take a string given by the server, convert it to the local codepage and
+ * put it in a new buffer. Returns a pointer to the new string or NULL on
+ * error.
+ */
+char *
+cifs_strndup_from_utf16(const char *src, const int maxlen,
+ const bool is_unicode, const struct nls_table *codepage)
+{
+ int len;
+ char *dst;
+
+ if (is_unicode) {
+ len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage);
+ len += nls_nullsize(codepage);
+ dst = kmalloc(len, GFP_KERNEL);
+ if (!dst)
+ return NULL;
+ cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage,
+ NO_MAP_UNI_RSVD);
+ } else {
+ dst = kstrndup(src, maxlen, GFP_KERNEL);
+ }
+
+ return dst;
+}
+
+static __le16 convert_to_sfu_char(char src_char)
+{
+ __le16 dest_char;
+
+ switch (src_char) {
+ case ':':
+ dest_char = cpu_to_le16(UNI_COLON);
+ break;
+ case '*':
+ dest_char = cpu_to_le16(UNI_ASTERISK);
+ break;
+ case '?':
+ dest_char = cpu_to_le16(UNI_QUESTION);
+ break;
+ case '<':
+ dest_char = cpu_to_le16(UNI_LESSTHAN);
+ break;
+ case '>':
+ dest_char = cpu_to_le16(UNI_GRTRTHAN);
+ break;
+ case '|':
+ dest_char = cpu_to_le16(UNI_PIPE);
+ break;
+ default:
+ dest_char = 0;
+ }
+
+ return dest_char;
+}
+
+static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
+{
+ __le16 dest_char;
+
+ if (src_char >= 0x01 && src_char <= 0x1F) {
+ dest_char = cpu_to_le16(src_char + 0xF000);
+ return dest_char;
+ }
+ switch (src_char) {
+ case ':':
+ dest_char = cpu_to_le16(SFM_COLON);
+ break;
+ case '"':
+ dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);
+ break;
+ case '*':
+ dest_char = cpu_to_le16(SFM_ASTERISK);
+ break;
+ case '?':
+ dest_char = cpu_to_le16(SFM_QUESTION);
+ break;
+ case '<':
+ dest_char = cpu_to_le16(SFM_LESSTHAN);
+ break;
+ case '>':
+ dest_char = cpu_to_le16(SFM_GRTRTHAN);
+ break;
+ case '|':
+ dest_char = cpu_to_le16(SFM_PIPE);
+ break;
+ case '.':
+ if (end_of_string)
+ dest_char = cpu_to_le16(SFM_PERIOD);
+ else
+ dest_char = 0;
+ break;
+ case ' ':
+ if (end_of_string)
+ dest_char = cpu_to_le16(SFM_SPACE);
+ else
+ dest_char = 0;
+ break;
+ default:
+ dest_char = 0;
+ }
+
+ return dest_char;
+}
+
+/*
+ * Convert 16 bit Unicode pathname to wire format from string in current code
+ * page. Conversion may involve remapping up the six characters that are
+ * only legal in POSIX-like OS (if they are present in the string). Path
+ * names are little endian 16 bit Unicode on the wire
+ */
+int
+cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
+ const struct nls_table *cp, int map_chars)
+{
+ int i, charlen;
+ int j = 0;
+ char src_char;
+ __le16 dst_char;
+ wchar_t tmp;
+ wchar_t *wchar_to; /* UTF-16 */
+ int ret;
+ unicode_t u;
+
+ if (map_chars == NO_MAP_UNI_RSVD)
+ return cifs_strtoUTF16(target, source, PATH_MAX, cp);
+
+ wchar_to = kzalloc(6, GFP_KERNEL);
+
+ for (i = 0; i < srclen; j++) {
+ src_char = source[i];
+ charlen = 1;
+
+ /* check if end of string */
+ if (src_char == 0)
+ goto ctoUTF16_out;
+
+ /* see if we must remap this char */
+ if (map_chars == SFU_MAP_UNI_RSVD)
+ dst_char = convert_to_sfu_char(src_char);
+ else if (map_chars == SFM_MAP_UNI_RSVD) {
+ bool end_of_string;
+
+ /**
+ * Remap spaces and periods found at the end of every
+ * component of the path. The special cases of '.' and
+ * '..' do not need to be dealt with explicitly because
+ * they are addressed in namei.c:link_path_walk().
+ **/
+ if ((i == srclen - 1) || (source[i+1] == '\\'))
+ end_of_string = true;
+ else
+ end_of_string = false;
+
+ dst_char = convert_to_sfm_char(src_char, end_of_string);
+ } else
+ dst_char = 0;
+ /*
+ * FIXME: We can not handle remapping backslash (UNI_SLASH)
+ * until all the calls to build_path_from_dentry are modified,
+ * as they use backslash as separator.
+ */
+ if (dst_char == 0) {
+ charlen = cp->char2uni(source + i, srclen - i, &tmp);
+ dst_char = cpu_to_le16(tmp);
+
+ /*
+ * if no match, use question mark, which at least in
+ * some cases serves as wild card
+ */
+ if (charlen > 0)
+ goto ctoUTF16;
+
+ /* convert SURROGATE_PAIR */
+ if (strcmp(cp->charset, "utf8") || !wchar_to)
+ 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
+ */
+ i += charlen;
+ put_unaligned(dst_char, &target[j]);
+ }
+
+ctoUTF16_out:
+ put_unaligned(0, &target[j]); /* Null terminate target unicode string */
+ kfree(wchar_to);
+ return j;
+}
+
+/*
+ * cifs_local_to_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 - source codepage
+ *
+ * Walk a 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.
+ */
+
+static int
+cifs_local_to_utf16_bytes(const char *from, int len,
+ const struct nls_table *codepage)
+{
+ int charlen;
+ int i;
+ wchar_t wchar_to;
+
+ for (i = 0; len && *from; i++, from += charlen, len -= charlen) {
+ charlen = codepage->char2uni(from, len, &wchar_to);
+ /* Failed conversion defaults to a question mark */
+ if (charlen < 1)
+ charlen = 1;
+ }
+ return 2 * i; /* UTF16 characters are two bytes */
+}
+
+/*
+ * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage
+ * @src - source string
+ * @maxlen - don't walk past this many bytes in the source string
+ * @utf16_len - the length of the allocated string in bytes (including null)
+ * @cp - source codepage
+ * @remap - map special chars
+ *
+ * Take a string convert it from the local codepage to UTF16 and
+ * put it in a new buffer. Returns a pointer to the new string or NULL on
+ * error.
+ */
+__le16 *
+cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len,
+ const struct nls_table *cp, int remap)
+{
+ int len;
+ __le16 *dst;
+
+ len = cifs_local_to_utf16_bytes(src, maxlen, cp);
+ len += 2; /* NULL */
+ dst = kmalloc(len, GFP_KERNEL);
+ if (!dst) {
+ *utf16_len = 0;
+ return NULL;
+ }
+ cifsConvertToUTF16(dst, src, strlen(src), cp, remap);
+ *utf16_len = len;
+ return dst;
+}
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
new file mode 100644
index 000000000..8360b7453
--- /dev/null
+++ b/fs/cifs/cifs_unicode.h
@@ -0,0 +1,419 @@
+/*
+ * cifs_unicode: Unicode kernel case support
+ *
+ * Function:
+ * Convert a unicode character to upper or lower case using
+ * compressed tables.
+ *
+ * Copyright (c) International Business Machines Corp., 2000,2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Notes:
+ * These APIs are based on the C library functions. The semantics
+ * should match the C functions but with expanded size operands.
+ *
+ * The upper/lower functions are based on a table created by mkupr.
+ * This is a compressed table of upper and lower case conversion.
+ *
+ */
+#ifndef _CIFS_UNICODE_H
+#define _CIFS_UNICODE_H
+
+#include <asm/byteorder.h>
+#include <linux/types.h>
+#include <linux/nls.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)
+
+/*
+ * Macs use an older "SFM" mapping of the symbols above. Fortunately it does
+ * not conflict (although almost does) with the mapping above.
+ */
+
+#define SFM_DOUBLEQUOTE ((__u16) 0xF020)
+#define SFM_ASTERISK ((__u16) 0xF021)
+#define SFM_QUESTION ((__u16) 0xF025)
+#define SFM_COLON ((__u16) 0xF022)
+#define SFM_GRTRTHAN ((__u16) 0xF024)
+#define SFM_LESSTHAN ((__u16) 0xF023)
+#define SFM_PIPE ((__u16) 0xF027)
+#define SFM_SLASH ((__u16) 0xF026)
+#define SFM_SPACE ((__u16) 0xF028)
+#define SFM_PERIOD ((__u16) 0xF029)
+
+/*
+ * Mapping mechanism to use when one of the seven reserved characters is
+ * encountered. We can only map using one of the mechanisms at a time
+ * since otherwise readdir could return directory entries which we would
+ * not be able to open
+ *
+ * NO_MAP_UNI_RSVD = do not perform any remapping of the character
+ * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible)
+ * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option)
+ *
+ */
+#define NO_MAP_UNI_RSVD 0
+#define SFM_MAP_UNI_RSVD 1
+#define SFU_MAP_UNI_RSVD 2
+
+/* 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 CifsUniUpperTable[512];
+extern const struct UniCaseRange CifsUniUpperRange[];
+#endif /* UNIUPR_NOUPPER */
+
+#ifndef UNIUPR_NOLOWER
+extern signed char CifsUniLowerTable[512];
+extern const struct UniCaseRange CifsUniLowerRange[];
+#endif /* UNIUPR_NOLOWER */
+
+#ifdef __KERNEL__
+int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
+ const struct nls_table *cp, int map_type);
+int cifs_utf16_bytes(const __le16 *from, int maxbytes,
+ const struct nls_table *codepage);
+int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *);
+char *cifs_strndup_from_utf16(const char *src, const int maxlen,
+ const bool is_unicode,
+ const struct nls_table *codepage);
+extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen,
+ const struct nls_table *cp, int mapChars);
+extern int cifs_remap(struct cifs_sb_info *cifs_sb);
+extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
+ int *utf16_len, const struct nls_table *cp,
+ int remap);
+#endif
+
+wchar_t cifs_toupper(wchar_t in);
+
+/*
+ * UniStrcat: Concatenate the second string to the first
+ *
+ * Returns:
+ * Address of the first string
+ */
+static inline __le16 *
+UniStrcat(__le16 *ucs1, const __le16 *ucs2)
+{
+ __le16 *anchor = ucs1; /* save a pointer to start of ucs1 */
+
+ while (*ucs1++) ; /* To end of first string */
+ ucs1--; /* Return to the null */
+ while ((*ucs1++ = *ucs2++)) ; /* 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++)) ;
+ 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++) ;
+ 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(CifsUniUpperTable)) {
+ /* Latin characters */
+ return uc + CifsUniUpperTable[uc]; /* Use base tables */
+ } else {
+ rp = CifsUniUpperRange; /* 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 */
+ } else {
+ 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 */
diff --git a/fs/cifs/cifs_uniupr.h b/fs/cifs/cifs_uniupr.h
new file mode 100644
index 000000000..0ac7c5a86
--- /dev/null
+++ b/fs/cifs/cifs_uniupr.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000,2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * uniupr.h - Unicode compressed case ranges
+ *
+*/
+
+#ifndef UNIUPR_NOUPPER
+/*
+ * Latin upper case
+ */
+signed char CifsUniUpperTable[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 CifsUniUpperRange[] = {
+ {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
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
new file mode 100644
index 000000000..130bdca9e
--- /dev/null
+++ b/fs/cifs/cifsacl.c
@@ -0,0 +1,1239 @@
+/*
+ * fs/cifs/cifsacl.c
+ *
+ * Copyright (C) International Business Machines Corp., 2007,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Contains the routines for mapping CIFS/NTFS ACLs
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/keyctl.h>
+#include <linux/key-type.h>
+#include <keys/user-type.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsacl.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+/* security id for everyone/world system group */
+static const struct cifs_sid sid_everyone = {
+ 1, 1, {0, 0, 0, 0, 0, 1}, {0} };
+/* security id for Authenticated Users system group */
+static const struct cifs_sid sid_authusers = {
+ 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} };
+/* group users */
+static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
+
+/* S-1-22-1 Unmapped Unix users */
+static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
+ {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-22-2 Unmapped Unix groups */
+static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22},
+ {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/*
+ * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
+ */
+
+/* S-1-5-88 MS NFS and Apple style UID/GID/mode */
+
+/* S-1-5-88-1 Unix uid */
+static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5},
+ {cpu_to_le32(88),
+ cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-5-88-2 Unix gid */
+static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5},
+ {cpu_to_le32(88),
+ cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-5-88-3 Unix mode */
+static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5},
+ {cpu_to_le32(88),
+ cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+static const struct cred *root_cred;
+
+static int
+cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
+{
+ char *payload;
+
+ /*
+ * If the payload is less than or equal to the size of a pointer, then
+ * an allocation here is wasteful. Just copy the data directly to the
+ * payload.value union member instead.
+ *
+ * With this however, you must check the datalen before trying to
+ * dereference payload.data!
+ */
+ if (prep->datalen <= sizeof(key->payload)) {
+ key->payload.data[0] = NULL;
+ memcpy(&key->payload, prep->data, prep->datalen);
+ } else {
+ payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+ key->payload.data[0] = payload;
+ }
+
+ key->datalen = prep->datalen;
+ return 0;
+}
+
+static inline void
+cifs_idmap_key_destroy(struct key *key)
+{
+ if (key->datalen > sizeof(key->payload))
+ kfree(key->payload.data[0]);
+}
+
+static struct key_type cifs_idmap_key_type = {
+ .name = "cifs.idmap",
+ .instantiate = cifs_idmap_key_instantiate,
+ .destroy = cifs_idmap_key_destroy,
+ .describe = user_describe,
+};
+
+static char *
+sid_to_key_str(struct cifs_sid *sidptr, unsigned int type)
+{
+ int i, len;
+ unsigned int saval;
+ char *sidstr, *strptr;
+ unsigned long long id_auth_val;
+
+ /* 3 bytes for prefix */
+ sidstr = kmalloc(3 + SID_STRING_BASE_SIZE +
+ (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth),
+ GFP_KERNEL);
+ if (!sidstr)
+ return sidstr;
+
+ strptr = sidstr;
+ len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g',
+ sidptr->revision);
+ strptr += len;
+
+ /* The authority field is a single 48-bit number */
+ id_auth_val = (unsigned long long)sidptr->authority[5];
+ id_auth_val |= (unsigned long long)sidptr->authority[4] << 8;
+ id_auth_val |= (unsigned long long)sidptr->authority[3] << 16;
+ id_auth_val |= (unsigned long long)sidptr->authority[2] << 24;
+ id_auth_val |= (unsigned long long)sidptr->authority[1] << 32;
+ id_auth_val |= (unsigned long long)sidptr->authority[0] << 48;
+
+ /*
+ * MS-DTYP states that if the authority is >= 2^32, then it should be
+ * expressed as a hex value.
+ */
+ if (id_auth_val <= UINT_MAX)
+ len = sprintf(strptr, "-%llu", id_auth_val);
+ else
+ len = sprintf(strptr, "-0x%llx", id_auth_val);
+
+ strptr += len;
+
+ for (i = 0; i < sidptr->num_subauth; ++i) {
+ saval = le32_to_cpu(sidptr->sub_auth[i]);
+ len = sprintf(strptr, "-%u", saval);
+ strptr += len;
+ }
+
+ return sidstr;
+}
+
+/*
+ * if the two SIDs (roughly equivalent to a UUID for a user or group) are
+ * the same returns zero, if they do not match returns non-zero.
+ */
+static int
+compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
+{
+ int i;
+ int num_subauth, num_sat, num_saw;
+
+ if ((!ctsid) || (!cwsid))
+ return 1;
+
+ /* compare the revision */
+ if (ctsid->revision != cwsid->revision) {
+ if (ctsid->revision > cwsid->revision)
+ return 1;
+ else
+ return -1;
+ }
+
+ /* compare all of the six auth values */
+ for (i = 0; i < NUM_AUTHS; ++i) {
+ if (ctsid->authority[i] != cwsid->authority[i]) {
+ if (ctsid->authority[i] > cwsid->authority[i])
+ return 1;
+ else
+ return -1;
+ }
+ }
+
+ /* compare all of the subauth values if any */
+ num_sat = ctsid->num_subauth;
+ num_saw = cwsid->num_subauth;
+ num_subauth = num_sat < num_saw ? num_sat : num_saw;
+ if (num_subauth) {
+ for (i = 0; i < num_subauth; ++i) {
+ if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
+ if (le32_to_cpu(ctsid->sub_auth[i]) >
+ le32_to_cpu(cwsid->sub_auth[i]))
+ return 1;
+ else
+ return -1;
+ }
+ }
+ }
+
+ return 0; /* sids compare/match */
+}
+
+static bool
+is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group)
+{
+ int i;
+ int num_subauth;
+ const struct cifs_sid *pwell_known_sid;
+
+ if (!psid || (puid == NULL))
+ return false;
+
+ num_subauth = psid->num_subauth;
+
+ /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */
+ if (num_subauth == 2) {
+ if (is_group)
+ pwell_known_sid = &sid_unix_groups;
+ else
+ pwell_known_sid = &sid_unix_users;
+ } else if (num_subauth == 3) {
+ if (is_group)
+ pwell_known_sid = &sid_unix_NFS_groups;
+ else
+ pwell_known_sid = &sid_unix_NFS_users;
+ } else
+ return false;
+
+ /* compare the revision */
+ if (psid->revision != pwell_known_sid->revision)
+ return false;
+
+ /* compare all of the six auth values */
+ for (i = 0; i < NUM_AUTHS; ++i) {
+ if (psid->authority[i] != pwell_known_sid->authority[i]) {
+ cifs_dbg(FYI, "auth %d did not match\n", i);
+ return false;
+ }
+ }
+
+ if (num_subauth == 2) {
+ if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0])
+ return false;
+
+ *puid = le32_to_cpu(psid->sub_auth[1]);
+ } else /* 3 subauths, ie Windows/Mac style */ {
+ *puid = le32_to_cpu(psid->sub_auth[0]);
+ if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) ||
+ (psid->sub_auth[1] != pwell_known_sid->sub_auth[1]))
+ return false;
+
+ *puid = le32_to_cpu(psid->sub_auth[2]);
+ }
+
+ cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid);
+ return true; /* well known sid found, uid returned */
+}
+
+static void
+cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
+{
+ int i;
+
+ dst->revision = src->revision;
+ dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
+ for (i = 0; i < NUM_AUTHS; ++i)
+ dst->authority[i] = src->authority[i];
+ for (i = 0; i < dst->num_subauth; ++i)
+ dst->sub_auth[i] = src->sub_auth[i];
+}
+
+static int
+id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid)
+{
+ int rc;
+ struct key *sidkey;
+ struct cifs_sid *ksid;
+ unsigned int ksid_size;
+ char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */
+ const struct cred *saved_cred;
+
+ rc = snprintf(desc, sizeof(desc), "%ci:%u",
+ sidtype == SIDOWNER ? 'o' : 'g', cid);
+ if (rc >= sizeof(desc))
+ return -EINVAL;
+
+ rc = 0;
+ saved_cred = override_creds(root_cred);
+ sidkey = request_key(&cifs_idmap_key_type, desc, "");
+ if (IS_ERR(sidkey)) {
+ rc = -EINVAL;
+ cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n",
+ __func__, sidtype == SIDOWNER ? 'u' : 'g', cid);
+ goto out_revert_creds;
+ } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
+ rc = -EIO;
+ cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
+ __func__, sidkey->datalen);
+ goto invalidate_key;
+ }
+
+ /*
+ * A sid is usually too large to be embedded in payload.value, but if
+ * there are no subauthorities and the host has 8-byte pointers, then
+ * it could be.
+ */
+ ksid = sidkey->datalen <= sizeof(sidkey->payload) ?
+ (struct cifs_sid *)&sidkey->payload :
+ (struct cifs_sid *)sidkey->payload.data[0];
+
+ ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
+ if (ksid_size > sidkey->datalen) {
+ rc = -EIO;
+ cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n",
+ __func__, sidkey->datalen, ksid_size);
+ goto invalidate_key;
+ }
+
+ cifs_copy_sid(ssid, ksid);
+out_key_put:
+ key_put(sidkey);
+out_revert_creds:
+ revert_creds(saved_cred);
+ return rc;
+
+invalidate_key:
+ key_invalidate(sidkey);
+ goto out_key_put;
+}
+
+static int
+sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
+ struct cifs_fattr *fattr, uint sidtype)
+{
+ int rc;
+ struct key *sidkey;
+ char *sidstr;
+ const struct cred *saved_cred;
+ kuid_t fuid = cifs_sb->mnt_uid;
+ kgid_t fgid = cifs_sb->mnt_gid;
+
+ /*
+ * If we have too many subauthorities, then something is really wrong.
+ * Just return an error.
+ */
+ if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
+ cifs_dbg(FYI, "%s: %u subauthorities is too many!\n",
+ __func__, psid->num_subauth);
+ return -EIO;
+ }
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) {
+ uint32_t unix_id;
+ bool is_group;
+
+ if (sidtype != SIDOWNER)
+ is_group = true;
+ else
+ is_group = false;
+
+ if (is_well_known_sid(psid, &unix_id, is_group) == false)
+ goto try_upcall_to_get_id;
+
+ if (is_group) {
+ kgid_t gid;
+ gid_t id;
+
+ id = (gid_t)unix_id;
+ gid = make_kgid(&init_user_ns, id);
+ if (gid_valid(gid)) {
+ fgid = gid;
+ goto got_valid_id;
+ }
+ } else {
+ kuid_t uid;
+ uid_t id;
+
+ id = (uid_t)unix_id;
+ uid = make_kuid(&init_user_ns, id);
+ if (uid_valid(uid)) {
+ fuid = uid;
+ goto got_valid_id;
+ }
+ }
+ /* If unable to find uid/gid easily from SID try via upcall */
+ }
+
+try_upcall_to_get_id:
+ sidstr = sid_to_key_str(psid, sidtype);
+ if (!sidstr)
+ return -ENOMEM;
+
+ saved_cred = override_creds(root_cred);
+ sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
+ if (IS_ERR(sidkey)) {
+ rc = -EINVAL;
+ cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n",
+ __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g');
+ goto out_revert_creds;
+ }
+
+ /*
+ * FIXME: Here we assume that uid_t and gid_t are same size. It's
+ * probably a safe assumption but might be better to check based on
+ * sidtype.
+ */
+ BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t));
+ if (sidkey->datalen != sizeof(uid_t)) {
+ rc = -EIO;
+ cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
+ __func__, sidkey->datalen);
+ key_invalidate(sidkey);
+ goto out_key_put;
+ }
+
+ if (sidtype == SIDOWNER) {
+ kuid_t uid;
+ uid_t id;
+ memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t));
+ uid = make_kuid(&init_user_ns, id);
+ if (uid_valid(uid))
+ fuid = uid;
+ } else {
+ kgid_t gid;
+ gid_t id;
+ memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t));
+ gid = make_kgid(&init_user_ns, id);
+ if (gid_valid(gid))
+ fgid = gid;
+ }
+
+out_key_put:
+ key_put(sidkey);
+out_revert_creds:
+ revert_creds(saved_cred);
+ kfree(sidstr);
+
+ /*
+ * Note that we return 0 here unconditionally. If the mapping
+ * fails then we just fall back to using the mnt_uid/mnt_gid.
+ */
+got_valid_id:
+ if (sidtype == SIDOWNER)
+ fattr->cf_uid = fuid;
+ else
+ fattr->cf_gid = fgid;
+ return 0;
+}
+
+int
+init_cifs_idmap(void)
+{
+ struct cred *cred;
+ struct key *keyring;
+ int ret;
+
+ cifs_dbg(FYI, "Registering the %s key type\n",
+ cifs_idmap_key_type.name);
+
+ /* create an override credential set with a special thread keyring in
+ * which requests are cached
+ *
+ * this is used to prevent malicious redirections from being installed
+ * with add_key().
+ */
+ cred = prepare_kernel_cred(NULL);
+ if (!cred)
+ return -ENOMEM;
+
+ keyring = keyring_alloc(".cifs_idmap",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto failed_put_cred;
+ }
+
+ ret = register_key_type(&cifs_idmap_key_type);
+ if (ret < 0)
+ goto failed_put_key;
+
+ /* instruct request_key() to use this special keyring as a cache for
+ * the results it looks up */
+ set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
+ cred->thread_keyring = keyring;
+ cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+ root_cred = cred;
+
+ cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring));
+ return 0;
+
+failed_put_key:
+ key_put(keyring);
+failed_put_cred:
+ put_cred(cred);
+ return ret;
+}
+
+void
+exit_cifs_idmap(void)
+{
+ key_revoke(root_cred->thread_keyring);
+ unregister_key_type(&cifs_idmap_key_type);
+ put_cred(root_cred);
+ cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name);
+}
+
+/* copy ntsd, owner sid, and group sid from a security descriptor to another */
+static void copy_sec_desc(const struct cifs_ntsd *pntsd,
+ struct cifs_ntsd *pnntsd, __u32 sidsoffset)
+{
+ struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+
+ /* copy security descriptor control portion */
+ pnntsd->revision = pntsd->revision;
+ pnntsd->type = pntsd->type;
+ pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
+ pnntsd->sacloffset = 0;
+ pnntsd->osidoffset = cpu_to_le32(sidsoffset);
+ pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
+
+ /* copy owner sid */
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->osidoffset));
+ nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
+ cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
+
+ /* copy group sid */
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->gsidoffset));
+ ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
+ sizeof(struct cifs_sid));
+ cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);
+
+ return;
+}
+
+
+/*
+ change posix mode to reflect permissions
+ pmode is the existing mode (we only want to overwrite part of this
+ bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
+*/
+static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
+ umode_t *pbits_to_set)
+{
+ __u32 flags = le32_to_cpu(ace_flags);
+ /* the order of ACEs is important. The canonical order is to begin with
+ DENY entries followed by ALLOW, otherwise an allow entry could be
+ encountered first, making the subsequent deny entry like "dead code"
+ which would be superflous since Windows stops when a match is made
+ for the operation you are trying to perform for your user */
+
+ /* For deny ACEs we change the mask so that subsequent allow access
+ control entries do not turn on the bits we are denying */
+ if (type == ACCESS_DENIED) {
+ if (flags & GENERIC_ALL)
+ *pbits_to_set &= ~S_IRWXUGO;
+
+ if ((flags & GENERIC_WRITE) ||
+ ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
+ *pbits_to_set &= ~S_IWUGO;
+ if ((flags & GENERIC_READ) ||
+ ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
+ *pbits_to_set &= ~S_IRUGO;
+ if ((flags & GENERIC_EXECUTE) ||
+ ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
+ *pbits_to_set &= ~S_IXUGO;
+ return;
+ } else if (type != ACCESS_ALLOWED) {
+ cifs_dbg(VFS, "unknown access control type %d\n", type);
+ return;
+ }
+ /* else ACCESS_ALLOWED type */
+
+ if (flags & GENERIC_ALL) {
+ *pmode |= (S_IRWXUGO & (*pbits_to_set));
+ cifs_dbg(NOISY, "all perms\n");
+ return;
+ }
+ if ((flags & GENERIC_WRITE) ||
+ ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
+ *pmode |= (S_IWUGO & (*pbits_to_set));
+ if ((flags & GENERIC_READ) ||
+ ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
+ *pmode |= (S_IRUGO & (*pbits_to_set));
+ if ((flags & GENERIC_EXECUTE) ||
+ ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
+ *pmode |= (S_IXUGO & (*pbits_to_set));
+
+ cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode);
+ return;
+}
+
+/*
+ Generate access flags to reflect permissions mode is the existing mode.
+ This function is called for every ACE in the DACL whose SID matches
+ with either owner or group or everyone.
+*/
+
+static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
+ __u32 *pace_flags)
+{
+ /* reset access mask */
+ *pace_flags = 0x0;
+
+ /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
+ mode &= bits_to_use;
+
+ /* check for R/W/X UGO since we do not know whose flags
+ is this but we have cleared all the bits sans RWX for
+ either user or group or other as per bits_to_use */
+ if (mode & S_IRUGO)
+ *pace_flags |= SET_FILE_READ_RIGHTS;
+ if (mode & S_IWUGO)
+ *pace_flags |= SET_FILE_WRITE_RIGHTS;
+ if (mode & S_IXUGO)
+ *pace_flags |= SET_FILE_EXEC_RIGHTS;
+
+ cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n",
+ mode, *pace_flags);
+ return;
+}
+
+static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
+ const struct cifs_sid *psid, __u64 nmode, umode_t bits)
+{
+ int i;
+ __u16 size = 0;
+ __u32 access_req = 0;
+
+ pntace->type = ACCESS_ALLOWED;
+ pntace->flags = 0x0;
+ mode_to_access_flags(nmode, bits, &access_req);
+ if (!access_req)
+ access_req = SET_MINIMUM_RIGHTS;
+ pntace->access_req = cpu_to_le32(access_req);
+
+ pntace->sid.revision = psid->revision;
+ pntace->sid.num_subauth = psid->num_subauth;
+ for (i = 0; i < NUM_AUTHS; i++)
+ pntace->sid.authority[i] = psid->authority[i];
+ for (i = 0; i < psid->num_subauth; i++)
+ pntace->sid.sub_auth[i] = psid->sub_auth[i];
+
+ size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
+ pntace->size = cpu_to_le16(size);
+
+ return size;
+}
+
+
+#ifdef CONFIG_CIFS_DEBUG2
+static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
+{
+ int num_subauth;
+
+ /* validate that we do not go past end of acl */
+
+ if (le16_to_cpu(pace->size) < 16) {
+ cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size));
+ return;
+ }
+
+ if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return;
+ }
+
+ num_subauth = pace->sid.num_subauth;
+ if (num_subauth) {
+ int i;
+ cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n",
+ pace->sid.revision, pace->sid.num_subauth, pace->type,
+ pace->flags, le16_to_cpu(pace->size));
+ for (i = 0; i < num_subauth; ++i) {
+ cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n",
+ i, le32_to_cpu(pace->sid.sub_auth[i]));
+ }
+
+ /* BB add length check to make sure that we do not have huge
+ num auths and therefore go off the end */
+ }
+
+ return;
+}
+#endif
+
+
+static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
+ struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
+ struct cifs_fattr *fattr)
+{
+ int i;
+ int num_aces = 0;
+ int acl_size;
+ char *acl_base;
+ struct cifs_ace **ppace;
+
+ /* BB need to add parm so we can store the SID BB */
+
+ if (!pdacl) {
+ /* no DACL in the security descriptor, set
+ all the permissions for user/group/other */
+ fattr->cf_mode |= S_IRWXUGO;
+ return;
+ }
+
+ /* validate that we do not go past end of acl */
+ if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return;
+ }
+
+ cifs_dbg(NOISY, "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));
+
+ /* reset rwx permissions for user/group/other.
+ Also, if num_aces is 0 i.e. DACL has no ACEs,
+ user/group/other have no permissions */
+ fattr->cf_mode &= ~(S_IRWXUGO);
+
+ acl_base = (char *)pdacl;
+ acl_size = sizeof(struct cifs_acl);
+
+ num_aces = le32_to_cpu(pdacl->num_aces);
+ if (num_aces > 0) {
+ umode_t user_mask = S_IRWXU;
+ umode_t group_mask = S_IRWXG;
+ umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO;
+
+ if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *))
+ return;
+ ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *),
+ GFP_KERNEL);
+ if (!ppace)
+ return;
+
+ for (i = 0; i < num_aces; ++i) {
+ ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
+#ifdef CONFIG_CIFS_DEBUG2
+ dump_ace(ppace[i], end_of_acl);
+#endif
+ if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &fattr->cf_mode,
+ &user_mask);
+ if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &fattr->cf_mode,
+ &group_mask);
+ if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &fattr->cf_mode,
+ &other_mask);
+ if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &fattr->cf_mode,
+ &other_mask);
+
+
+/* memcpy((void *)(&(cifscred->aces[i])),
+ (void *)ppace[i],
+ sizeof(struct cifs_ace)); */
+
+ acl_base = (char *)ppace[i];
+ acl_size = le16_to_cpu(ppace[i]->size);
+ }
+
+ kfree(ppace);
+ }
+
+ return;
+}
+
+
+static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
+ struct cifs_sid *pgrpsid, __u64 nmode)
+{
+ u16 size = 0;
+ struct cifs_acl *pnndacl;
+
+ pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
+
+ size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
+ pownersid, nmode, S_IRWXU);
+ size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+ pgrpsid, nmode, S_IRWXG);
+ size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+ &sid_everyone, nmode, S_IRWXO);
+
+ pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
+ pndacl->num_aces = cpu_to_le32(3);
+
+ return 0;
+}
+
+
+static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
+{
+ /* BB need to add parm so we can store the SID BB */
+
+ /* validate that we do not go past end of ACL - sid must be at least 8
+ bytes long (assuming no sub-auths - e.g. the null SID */
+ if (end_of_acl < (char *)psid + 8) {
+ cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_CIFS_DEBUG2
+ if (psid->num_subauth) {
+ int i;
+ cifs_dbg(FYI, "SID revision %d num_auth %d\n",
+ psid->revision, psid->num_subauth);
+
+ for (i = 0; i < psid->num_subauth; i++) {
+ cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n",
+ i, le32_to_cpu(psid->sub_auth[i]));
+ }
+
+ /* BB add length check to make sure that we do not have huge
+ num auths and therefore go off the end */
+ cifs_dbg(FYI, "RID 0x%x\n",
+ le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
+ }
+#endif
+
+ return 0;
+}
+
+
+/* Convert CIFS ACL to POSIX form */
+static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
+ struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)
+{
+ int rc = 0;
+ struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
+ char *end_of_acl = ((char *)pntsd) + acl_len;
+ __u32 dacloffset;
+
+ if (pntsd == NULL)
+ return -EIO;
+
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->osidoffset));
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->gsidoffset));
+ dacloffset = le32_to_cpu(pntsd->dacloffset);
+ dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+ cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
+ pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
+ le32_to_cpu(pntsd->gsidoffset),
+ le32_to_cpu(pntsd->sacloffset), dacloffset);
+/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
+ rc = parse_sid(owner_sid_ptr, end_of_acl);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc);
+ return rc;
+ }
+ rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = parse_sid(group_sid_ptr, end_of_acl);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n",
+ __func__, rc);
+ return rc;
+ }
+ rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n",
+ __func__, rc);
+ return rc;
+ }
+
+ if (dacloffset)
+ parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
+ group_sid_ptr, fattr);
+ else
+ cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
+
+ return rc;
+}
+
+/* Convert permission bits from mode to equivalent CIFS ACL */
+static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+ __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag)
+{
+ int rc = 0;
+ __u32 dacloffset;
+ __u32 ndacloffset;
+ __u32 sidsoffset;
+ struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+ struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
+ struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
+
+ if (nmode != NO_CHANGE_64) { /* chmod */
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->osidoffset));
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->gsidoffset));
+ dacloffset = le32_to_cpu(pntsd->dacloffset);
+ dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+ ndacloffset = sizeof(struct cifs_ntsd);
+ ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
+ ndacl_ptr->revision = dacl_ptr->revision;
+ ndacl_ptr->size = 0;
+ ndacl_ptr->num_aces = 0;
+
+ rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr,
+ nmode);
+ sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
+ /* copy sec desc control portion & owner and group sids */
+ copy_sec_desc(pntsd, pnntsd, sidsoffset);
+ *aclflag = CIFS_ACL_DACL;
+ } else {
+ memcpy(pnntsd, pntsd, secdesclen);
+ if (uid_valid(uid)) { /* chown */
+ uid_t id;
+ owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
+ le32_to_cpu(pnntsd->osidoffset));
+ nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid),
+ GFP_KERNEL);
+ if (!nowner_sid_ptr)
+ return -ENOMEM;
+ id = from_kuid(&init_user_ns, uid);
+ rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n",
+ __func__, rc, id);
+ kfree(nowner_sid_ptr);
+ return rc;
+ }
+ cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr);
+ kfree(nowner_sid_ptr);
+ *aclflag = CIFS_ACL_OWNER;
+ }
+ if (gid_valid(gid)) { /* chgrp */
+ gid_t id;
+ group_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
+ le32_to_cpu(pnntsd->gsidoffset));
+ ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid),
+ GFP_KERNEL);
+ if (!ngroup_sid_ptr)
+ return -ENOMEM;
+ id = from_kgid(&init_user_ns, gid);
+ rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n",
+ __func__, rc, id);
+ kfree(ngroup_sid_ptr);
+ return rc;
+ }
+ cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr);
+ kfree(ngroup_sid_ptr);
+ *aclflag = CIFS_ACL_GROUP;
+ }
+ }
+
+ return rc;
+}
+
+struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
+ const struct cifs_fid *cifsfid, u32 *pacllen)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ unsigned int xid;
+ int rc;
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+
+ if (IS_ERR(tlink))
+ return ERR_CAST(tlink);
+
+ xid = get_xid();
+ rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd,
+ pacllen);
+ free_xid(xid);
+
+ cifs_put_tlink(tlink);
+
+ cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
+ if (rc)
+ return ERR_PTR(rc);
+ return pntsd;
+}
+
+static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
+ const char *path, u32 *pacllen)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ int oplock = 0;
+ unsigned int xid;
+ int rc, create_options = 0;
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+
+ if (IS_ERR(tlink))
+ return ERR_CAST(tlink);
+
+ tcon = tlink_tcon(tlink);
+ xid = get_xid();
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = READ_CONTROL;
+ oparms.create_options = create_options;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (!rc) {
+ rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen);
+ CIFSSMBClose(xid, tcon, fid.netfid);
+ }
+
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+
+ cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
+ if (rc)
+ return ERR_PTR(rc);
+ return pntsd;
+}
+
+/* Retrieve an ACL from the server */
+struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
+ struct inode *inode, const char *path,
+ u32 *pacllen)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ struct cifsFileInfo *open_file = NULL;
+
+ if (inode)
+ open_file = find_readable_file(CIFS_I(inode), true);
+ if (!open_file)
+ return get_cifs_acl_by_path(cifs_sb, path, pacllen);
+
+ pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
+ cifsFileInfo_put(open_file);
+ return pntsd;
+}
+
+ /* Set an ACL on the server */
+int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
+ struct inode *inode, const char *path, int aclflag)
+{
+ int oplock = 0;
+ unsigned int xid;
+ int rc, access_flags, create_options = 0;
+ struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ tcon = tlink_tcon(tlink);
+ xid = get_xid();
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
+ access_flags = WRITE_OWNER;
+ else
+ access_flags = WRITE_DAC;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = access_flags;
+ oparms.create_options = create_options;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc) {
+ cifs_dbg(VFS, "Unable to open file to set ACL\n");
+ goto out;
+ }
+
+ rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag);
+ cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc);
+
+ CIFSSMBClose(xid, tcon, fid.netfid);
+out:
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+/* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */
+int
+cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
+ struct inode *inode, const char *path,
+ const struct cifs_fid *pfid)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ u32 acllen = 0;
+ int rc = 0;
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+ struct smb_version_operations *ops;
+
+ cifs_dbg(NOISY, "converting ACL to mode for %s\n", path);
+
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ ops = tlink_tcon(tlink)->ses->server->ops;
+
+ if (pfid && (ops->get_acl_by_fid))
+ pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen);
+ else if (ops->get_acl)
+ pntsd = ops->get_acl(cifs_sb, inode, path, &acllen);
+ else {
+ cifs_put_tlink(tlink);
+ return -EOPNOTSUPP;
+ }
+ /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
+ if (IS_ERR(pntsd)) {
+ rc = PTR_ERR(pntsd);
+ cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
+ } else {
+ rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr);
+ kfree(pntsd);
+ if (rc)
+ cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc);
+ }
+
+ cifs_put_tlink(tlink);
+
+ return rc;
+}
+
+/* Convert mode bits to an ACL so we can update the ACL on the server */
+int
+id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
+ kuid_t uid, kgid_t gid)
+{
+ int rc = 0;
+ int aclflag = CIFS_ACL_DACL; /* default flag to set */
+ __u32 secdesclen = 0;
+ struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
+ struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+ struct smb_version_operations *ops;
+
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ ops = tlink_tcon(tlink)->ses->server->ops;
+
+ cifs_dbg(NOISY, "set ACL from mode for %s\n", path);
+
+ /* Get the security descriptor */
+
+ if (ops->get_acl == NULL) {
+ cifs_put_tlink(tlink);
+ return -EOPNOTSUPP;
+ }
+
+ pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen);
+ if (IS_ERR(pntsd)) {
+ rc = PTR_ERR(pntsd);
+ cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
+ cifs_put_tlink(tlink);
+ return rc;
+ }
+
+ /*
+ * Add three ACEs for owner, group, everyone getting rid of other ACEs
+ * as chmod disables ACEs and set the security descriptor. Allocate
+ * memory for the smb header, set security descriptor request security
+ * descriptor parameters, and secuirty descriptor itself
+ */
+ secdesclen = max_t(u32, secdesclen, DEFAULT_SEC_DESC_LEN);
+ pnntsd = kmalloc(secdesclen, GFP_KERNEL);
+ if (!pnntsd) {
+ kfree(pntsd);
+ cifs_put_tlink(tlink);
+ return -ENOMEM;
+ }
+
+ rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
+ &aclflag);
+
+ cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
+
+ if (ops->set_acl == NULL)
+ rc = -EOPNOTSUPP;
+
+ if (!rc) {
+ /* Set the security descriptor */
+ rc = ops->set_acl(pnntsd, secdesclen, inode, path, aclflag);
+ cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
+ }
+ cifs_put_tlink(tlink);
+
+ kfree(pnntsd);
+ kfree(pntsd);
+ return rc;
+}
diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h
new file mode 100644
index 000000000..dd95a6fa2
--- /dev/null
+++ b/fs/cifs/cifsacl.h
@@ -0,0 +1,115 @@
+/*
+ * fs/cifs/cifsacl.h
+ *
+ * Copyright (c) International Business Machines Corp., 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSACL_H
+#define _CIFSACL_H
+
+
+#define NUM_AUTHS (6) /* number of authority fields */
+#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */
+
+#define READ_BIT 0x4
+#define WRITE_BIT 0x2
+#define EXEC_BIT 0x1
+
+#define UBITSHIFT 6
+#define GBITSHIFT 3
+
+#define ACCESS_ALLOWED 0
+#define ACCESS_DENIED 1
+
+#define SIDOWNER 1
+#define SIDGROUP 2
+
+/*
+ * Security Descriptor length containing DACL with 3 ACEs (one each for
+ * owner, group and world).
+ */
+#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \
+ sizeof(struct cifs_acl) + \
+ (sizeof(struct cifs_ace) * 3))
+
+/*
+ * 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 */
+
+struct cifs_ntsd {
+ __le16 revision; /* revision level */
+ __le16 type;
+ __le32 osidoffset;
+ __le32 gsidoffset;
+ __le32 sacloffset;
+ __le32 dacloffset;
+} __attribute__((packed));
+
+struct cifs_sid {
+ __u8 revision; /* revision level */
+ __u8 num_subauth;
+ __u8 authority[NUM_AUTHS];
+ __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
+} __attribute__((packed));
+
+/* size of a struct cifs_sid, sans sub_auth array */
+#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
+
+struct cifs_acl {
+ __le16 revision; /* revision level */
+ __le16 size;
+ __le32 num_aces;
+} __attribute__((packed));
+
+struct cifs_ace {
+ __u8 type;
+ __u8 flags;
+ __le16 size;
+ __le32 access_req;
+ struct cifs_sid sid; /* ie UUID of user or group who gets these perms */
+} __attribute__((packed));
+
+/*
+ * Minimum security identifier can be one for system defined Users
+ * and Groups such as NULL SID and World or Built-in accounts such
+ * as Administrator and Guest and consists of
+ * Revision + Num (Sub)Auths + Authority + Domain (one Subauthority)
+ */
+#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */
+
+/*
+ * Minimum security descriptor can be one without any SACL and DACL and can
+ * consist of revision, type, and two sids of minimum size for owner and group
+ */
+#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN))
+
+#endif /* _CIFSACL_H */
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
new file mode 100644
index 000000000..85b31cfa2
--- /dev/null
+++ b/fs/cifs/cifsencrypt.c
@@ -0,0 +1,877 @@
+/*
+ * fs/cifs/cifsencrypt.c
+ *
+ * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP
+ * for more detailed information
+ *
+ * Copyright (C) International Business Machines Corp., 2005,2013
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifs_unicode.h"
+#include "cifsproto.h"
+#include "ntlmssp.h"
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include <linux/highmem.h>
+#include <crypto/skcipher.h>
+#include <crypto/aead.h>
+
+int __cifs_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server, char *signature,
+ struct shash_desc *shash)
+{
+ int i;
+ int rc;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+ int is_smb2 = server->vals->header_preamble_size == 0;
+
+ /* iov[0] is actual data and not the rfc1002 length for SMB2+ */
+ if (is_smb2) {
+ if (iov[0].iov_len <= 4)
+ return -EIO;
+ i = 0;
+ } else {
+ if (n_vec < 2 || iov[0].iov_len != 4)
+ return -EIO;
+ i = 1; /* skip rfc1002 length */
+ }
+
+ for (; i < n_vec; i++) {
+ if (iov[i].iov_len == 0)
+ continue;
+ if (iov[i].iov_base == NULL) {
+ cifs_dbg(VFS, "null iovec entry\n");
+ return -EIO;
+ }
+
+ rc = crypto_shash_update(shash,
+ iov[i].iov_base, iov[i].iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with payload\n",
+ __func__);
+ return rc;
+ }
+ }
+
+ /* now hash over the rq_pages array */
+ for (i = 0; i < rqst->rq_npages; i++) {
+ void *kaddr;
+ unsigned int len, offset;
+
+ rqst_page_get_length(rqst, i, &len, &offset);
+
+ kaddr = (char *) kmap(rqst->rq_pages[i]) + offset;
+
+ rc = crypto_shash_update(shash, kaddr, len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with payload\n",
+ __func__);
+ kunmap(rqst->rq_pages[i]);
+ return rc;
+ }
+
+ kunmap(rqst->rq_pages[i]);
+ }
+
+ rc = crypto_shash_final(shash, signature);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate hash\n", __func__);
+
+ return rc;
+}
+
+/*
+ * Calculate and return the CIFS signature based on the mac key and SMB PDU.
+ * The 16 byte signature must be allocated by the caller. Note we only use the
+ * 1st eight bytes and that the smb header signature field on input contains
+ * the sequence number before this function is called. Also, this function
+ * should be called with the server->srv_mutex held.
+ */
+static int cifs_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server, char *signature)
+{
+ int rc;
+
+ if (!rqst->rq_iov || !signature || !server)
+ return -EINVAL;
+
+ rc = cifs_alloc_hash("md5", &server->secmech.md5,
+ &server->secmech.sdescmd5);
+ if (rc)
+ return -1;
+
+ rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init md5\n", __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_update(&server->secmech.sdescmd5->shash,
+ server->session_key.response, server->session_key.len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
+ return rc;
+ }
+
+ return __cifs_calc_signature(rqst, server, signature,
+ &server->secmech.sdescmd5->shash);
+}
+
+/* must be called with server->srv_mutex held */
+int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number)
+{
+ int rc = 0;
+ char smb_signature[20];
+ struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+
+ if (rqst->rq_iov[0].iov_len != 4 ||
+ rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+ return -EIO;
+
+ if ((cifs_pdu == NULL) || (server == NULL))
+ return -EINVAL;
+
+ if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
+ server->tcpStatus == CifsNeedNegotiate)
+ return rc;
+
+ if (!server->session_estab) {
+ memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
+ return rc;
+ }
+
+ cifs_pdu->Signature.Sequence.SequenceNumber =
+ cpu_to_le32(server->sequence_number);
+ cifs_pdu->Signature.Sequence.Reserved = 0;
+
+ *pexpected_response_sequence_number = ++server->sequence_number;
+ ++server->sequence_number;
+
+ rc = cifs_calc_signature(rqst, server, smb_signature);
+ if (rc)
+ memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
+ else
+ memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
+
+ return rc;
+}
+
+int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence)
+{
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = n_vec };
+
+ return cifs_sign_rqst(&rqst, server, pexpected_response_sequence);
+}
+
+/* must be called with server->srv_mutex held */
+int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number)
+{
+ struct kvec iov[2];
+
+ iov[0].iov_base = cifs_pdu;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = (char *)cifs_pdu + 4;
+ iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length);
+
+ return cifs_sign_smbv(iov, 2, server,
+ pexpected_response_sequence_number);
+}
+
+int cifs_verify_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server,
+ __u32 expected_sequence_number)
+{
+ unsigned int rc;
+ char server_response_sig[8];
+ char what_we_think_sig_should_be[20];
+ struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+
+ if (rqst->rq_iov[0].iov_len != 4 ||
+ rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+ return -EIO;
+
+ if (cifs_pdu == NULL || server == NULL)
+ return -EINVAL;
+
+ if (!server->session_estab)
+ return 0;
+
+ if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) {
+ struct smb_com_lock_req *pSMB =
+ (struct smb_com_lock_req *)cifs_pdu;
+ if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)
+ return 0;
+ }
+
+ /* BB what if signatures are supposed to be on for session but
+ server does not send one? BB */
+
+ /* Do not need to verify session setups with signature "BSRSPYL " */
+ if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0)
+ cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n",
+ cifs_pdu->Command);
+
+ /* save off the origiginal signature so we can modify the smb and check
+ its signature against what the server sent */
+ memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8);
+
+ cifs_pdu->Signature.Sequence.SequenceNumber =
+ cpu_to_le32(expected_sequence_number);
+ cifs_pdu->Signature.Sequence.Reserved = 0;
+
+ mutex_lock(&server->srv_mutex);
+ rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
+ mutex_unlock(&server->srv_mutex);
+
+ if (rc)
+ return rc;
+
+/* cifs_dump_mem("what we think it should be: ",
+ what_we_think_sig_should_be, 16); */
+
+ if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
+ return -EACCES;
+ else
+ return 0;
+
+}
+
+/* first calculate 24 bytes ntlm response and then 16 byte session key */
+int setup_ntlm_response(struct cifs_ses *ses, const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE;
+ char temp_key[CIFS_SESS_KEY_SIZE];
+
+ if (!ses)
+ return -EINVAL;
+
+ ses->auth_key.response = kmalloc(temp_len, GFP_KERNEL);
+ if (!ses->auth_key.response)
+ return -ENOMEM;
+
+ ses->auth_key.len = temp_len;
+
+ rc = SMBNTencrypt(ses->password, ses->server->cryptkey,
+ ses->auth_key.response + CIFS_SESS_KEY_SIZE, nls_cp);
+ if (rc) {
+ cifs_dbg(FYI, "%s Can't generate NTLM response, error: %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = E_md4hash(ses->password, temp_key, nls_cp);
+ if (rc) {
+ cifs_dbg(FYI, "%s Can't generate NT hash, error: %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ rc = mdfour(ses->auth_key.response, temp_key, CIFS_SESS_KEY_SIZE);
+ if (rc)
+ cifs_dbg(FYI, "%s Can't generate NTLM session key, error: %d\n",
+ __func__, rc);
+
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,
+ char *lnm_session_key)
+{
+ int i;
+ int rc;
+ char password_with_pad[CIFS_ENCPWD_SIZE] = {0};
+
+ if (password)
+ strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE);
+
+ if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) {
+ memcpy(lnm_session_key, password_with_pad,
+ CIFS_ENCPWD_SIZE);
+ return 0;
+ }
+
+ /* calculate old style session key */
+ /* calling toupper is less broken than repeatedly
+ calling nls_toupper would be since that will never
+ work for UTF8, but neither handles multibyte code pages
+ but the only alternative would be converting to UCS-16 (Unicode)
+ (using a routine something like UniStrupr) then
+ uppercasing and then converting back from Unicode - which
+ would only worth doing it if we knew it were utf8. Basically
+ utf8 and other multibyte codepages each need their own strupper
+ function since a byte at a time will ont work. */
+
+ for (i = 0; i < CIFS_ENCPWD_SIZE; i++)
+ password_with_pad[i] = toupper(password_with_pad[i]);
+
+ rc = SMBencrypt(password_with_pad, cryptkey, lnm_session_key);
+
+ return rc;
+}
+#endif /* CIFS_WEAK_PW_HASH */
+
+/* Build a proper attribute value/target info pairs blob.
+ * Fill in netbios and dns domain name and workstation name
+ * and client time (total five av pairs and + one end of fields indicator.
+ * Allocate domain name which gets freed when session struct is deallocated.
+ */
+static int
+build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
+{
+ unsigned int dlen;
+ unsigned int size = 2 * sizeof(struct ntlmssp2_name);
+ char *defdmname = "WORKGROUP";
+ unsigned char *blobptr;
+ struct ntlmssp2_name *attrptr;
+
+ if (!ses->domainName) {
+ ses->domainName = kstrdup(defdmname, GFP_KERNEL);
+ if (!ses->domainName)
+ return -ENOMEM;
+ }
+
+ dlen = strlen(ses->domainName);
+
+ /*
+ * The length of this blob is two times the size of a
+ * structure (av pair) which holds name/size
+ * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) +
+ * unicode length of a netbios domain name
+ */
+ ses->auth_key.len = size + 2 * dlen;
+ ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL);
+ if (!ses->auth_key.response) {
+ ses->auth_key.len = 0;
+ return -ENOMEM;
+ }
+
+ blobptr = ses->auth_key.response;
+ attrptr = (struct ntlmssp2_name *) blobptr;
+
+ /*
+ * As defined in MS-NTLM 3.3.2, just this av pair field
+ * is sufficient as part of the temp
+ */
+ attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME);
+ attrptr->length = cpu_to_le16(2 * dlen);
+ blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
+ cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp);
+
+ return 0;
+}
+
+/* Server has provided av pairs/target info in the type 2 challenge
+ * packet and we have plucked it and stored within smb session.
+ * We parse that blob here to find netbios domain name to be used
+ * as part of ntlmv2 authentication (in Target String), if not already
+ * specified on the command line.
+ * If this function returns without any error but without fetching
+ * domain name, authentication may fail against some server but
+ * may not fail against other (those who are not very particular
+ * about target string i.e. for some, just user name might suffice.
+ */
+static int
+find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
+{
+ unsigned int attrsize;
+ unsigned int type;
+ unsigned int onesize = sizeof(struct ntlmssp2_name);
+ unsigned char *blobptr;
+ unsigned char *blobend;
+ struct ntlmssp2_name *attrptr;
+
+ if (!ses->auth_key.len || !ses->auth_key.response)
+ return 0;
+
+ blobptr = ses->auth_key.response;
+ blobend = blobptr + ses->auth_key.len;
+
+ while (blobptr + onesize < blobend) {
+ attrptr = (struct ntlmssp2_name *) blobptr;
+ type = le16_to_cpu(attrptr->type);
+ if (type == NTLMSSP_AV_EOL)
+ break;
+ blobptr += 2; /* advance attr type */
+ attrsize = le16_to_cpu(attrptr->length);
+ blobptr += 2; /* advance attr size */
+ if (blobptr + attrsize > blobend)
+ break;
+ if (type == NTLMSSP_AV_NB_DOMAIN_NAME) {
+ if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN)
+ break;
+ if (!ses->domainName) {
+ ses->domainName =
+ kmalloc(attrsize + 1, GFP_KERNEL);
+ if (!ses->domainName)
+ return -ENOMEM;
+ cifs_from_utf16(ses->domainName,
+ (__le16 *)blobptr, attrsize, attrsize,
+ nls_cp, NO_MAP_UNI_RSVD);
+ break;
+ }
+ }
+ blobptr += attrsize; /* advance attr value */
+ }
+
+ return 0;
+}
+
+/* Server has provided av pairs/target info in the type 2 challenge
+ * packet and we have plucked it and stored within smb session.
+ * We parse that blob here to find the server given timestamp
+ * as part of ntlmv2 authentication (or local current time as
+ * default in case of failure)
+ */
+static __le64
+find_timestamp(struct cifs_ses *ses)
+{
+ unsigned int attrsize;
+ unsigned int type;
+ unsigned int onesize = sizeof(struct ntlmssp2_name);
+ unsigned char *blobptr;
+ unsigned char *blobend;
+ struct ntlmssp2_name *attrptr;
+ struct timespec64 ts;
+
+ if (!ses->auth_key.len || !ses->auth_key.response)
+ return 0;
+
+ blobptr = ses->auth_key.response;
+ blobend = blobptr + ses->auth_key.len;
+
+ while (blobptr + onesize < blobend) {
+ attrptr = (struct ntlmssp2_name *) blobptr;
+ type = le16_to_cpu(attrptr->type);
+ if (type == NTLMSSP_AV_EOL)
+ break;
+ blobptr += 2; /* advance attr type */
+ attrsize = le16_to_cpu(attrptr->length);
+ blobptr += 2; /* advance attr size */
+ if (blobptr + attrsize > blobend)
+ break;
+ if (type == NTLMSSP_AV_TIMESTAMP) {
+ if (attrsize == sizeof(u64))
+ return *((__le64 *)blobptr);
+ }
+ blobptr += attrsize; /* advance attr value */
+ }
+
+ ktime_get_real_ts64(&ts);
+ return cpu_to_le64(cifs_UnixTimeToNT(ts));
+}
+
+static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ int len;
+ char nt_hash[CIFS_NTHASH_SIZE];
+ __le16 *user;
+ wchar_t *domain;
+ wchar_t *server;
+
+ if (!ses->server->secmech.sdeschmacmd5) {
+ cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);
+ return -1;
+ }
+
+ /* calculate md4 hash of password */
+ E_md4hash(ses->password, nt_hash, nls_cp);
+
+ rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,
+ CIFS_NTHASH_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not init hmacmd5\n", __func__);
+ return rc;
+ }
+
+ /* convert ses->user_name to unicode */
+ len = ses->user_name ? strlen(ses->user_name) : 0;
+ user = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if (user == NULL) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ if (len) {
+ len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp);
+ UniStrupr(user);
+ } else {
+ memset(user, '\0', 2);
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ (char *)user, 2 * len);
+ kfree(user);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with user\n", __func__);
+ return rc;
+ }
+
+ /* convert ses->domainName to unicode and uppercase */
+ if (ses->domainName) {
+ len = strlen(ses->domainName);
+
+ domain = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if (domain == NULL) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
+ nls_cp);
+ rc =
+ crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ (char *)domain, 2 * len);
+ kfree(domain);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with domain\n",
+ __func__);
+ return rc;
+ }
+ } else {
+ /* We use ses->serverName if no domain name available */
+ len = strlen(ses->serverName);
+
+ server = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if (server == NULL) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ len = cifs_strtoUTF16((__le16 *)server, ses->serverName, len,
+ nls_cp);
+ rc =
+ crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ (char *)server, 2 * len);
+ kfree(server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with server\n",
+ __func__);
+ return rc;
+ }
+ }
+
+ rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
+ ntlmv2_hash);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
+
+ return rc;
+}
+
+static int
+CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
+{
+ int rc;
+ struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *)
+ (ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+ unsigned int hash_len;
+
+ /* The MD5 hash starts at challenge_key.key */
+ hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE +
+ offsetof(struct ntlmv2_resp, challenge.key[0]));
+
+ if (!ses->server->secmech.sdeschmacmd5) {
+ cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);
+ return -1;
+ }
+
+ rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
+ ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
+ __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not init hmacmd5\n", __func__);
+ return rc;
+ }
+
+ if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
+ memcpy(ntlmv2->challenge.key,
+ ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
+ else
+ memcpy(ntlmv2->challenge.key,
+ ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ ntlmv2->challenge.key, hash_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
+ return rc;
+ }
+
+ /* Note that the MD5 digest over writes anon.challenge_key.key */
+ rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
+ ntlmv2->ntlmv2_hash);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
+
+ return rc;
+}
+
+int
+setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
+{
+ int rc;
+ int baselen;
+ unsigned int tilen;
+ struct ntlmv2_resp *ntlmv2;
+ char ntlmv2_hash[16];
+ unsigned char *tiblob = NULL; /* target info blob */
+ __le64 rsp_timestamp;
+
+ if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
+ if (!ses->domainName) {
+ if (ses->domainAuto) {
+ rc = find_domain_name(ses, nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "error %d finding domain name\n",
+ rc);
+ goto setup_ntlmv2_rsp_ret;
+ }
+ } else {
+ ses->domainName = kstrdup("", GFP_KERNEL);
+ }
+ }
+ } else {
+ rc = build_avpair_blob(ses, nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "error %d building av pair blob\n", rc);
+ goto setup_ntlmv2_rsp_ret;
+ }
+ }
+
+ /* Must be within 5 minutes of the server (or in range +/-2h
+ * in case of Mac OS X), so simply carry over server timestamp
+ * (as Windows 7 does)
+ */
+ rsp_timestamp = find_timestamp(ses);
+
+ baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
+ tilen = ses->auth_key.len;
+ tiblob = ses->auth_key.response;
+
+ ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
+ if (!ses->auth_key.response) {
+ rc = -ENOMEM;
+ ses->auth_key.len = 0;
+ goto setup_ntlmv2_rsp_ret;
+ }
+ ses->auth_key.len += baselen;
+
+ ntlmv2 = (struct ntlmv2_resp *)
+ (ses->auth_key.response + CIFS_SESS_KEY_SIZE);
+ ntlmv2->blob_signature = cpu_to_le32(0x00000101);
+ ntlmv2->reserved = 0;
+ ntlmv2->time = rsp_timestamp;
+
+ get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
+ ntlmv2->reserved2 = 0;
+
+ memcpy(ses->auth_key.response + baselen, tiblob, tilen);
+
+ mutex_lock(&ses->server->srv_mutex);
+
+ rc = cifs_alloc_hash("hmac(md5)",
+ &ses->server->secmech.hmacmd5,
+ &ses->server->secmech.sdeschmacmd5);
+ if (rc) {
+ goto unlock;
+ }
+
+ /* calculate ntlmv2_hash */
+ rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "could not get v2 hash rc %d\n", rc);
+ goto unlock;
+ }
+
+ /* calculate first part of the client response (CR1) */
+ rc = CalcNTLMv2_response(ses, ntlmv2_hash);
+ if (rc) {
+ cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc);
+ goto unlock;
+ }
+
+ /* now calculate the session key for NTLMv2 */
+ rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
+ ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
+ __func__);
+ goto unlock;
+ }
+
+ rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
+ goto unlock;
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
+ ntlmv2->ntlmv2_hash,
+ CIFS_HMAC_MD5_HASH_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
+ goto unlock;
+ }
+
+ rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
+ ses->auth_key.response);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
+
+unlock:
+ mutex_unlock(&ses->server->srv_mutex);
+setup_ntlmv2_rsp_ret:
+ kfree(tiblob);
+
+ return rc;
+}
+
+int
+calc_seckey(struct cifs_ses *ses)
+{
+ int rc;
+ struct crypto_skcipher *tfm_arc4;
+ struct scatterlist sgin, sgout;
+ struct skcipher_request *req;
+ unsigned char *sec_key;
+
+ sec_key = kmalloc(CIFS_SESS_KEY_SIZE, GFP_KERNEL);
+ if (sec_key == NULL)
+ return -ENOMEM;
+
+ get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE);
+
+ tfm_arc4 = crypto_alloc_skcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm_arc4)) {
+ rc = PTR_ERR(tfm_arc4);
+ cifs_dbg(VFS, "could not allocate crypto API arc4\n");
+ goto out;
+ }
+
+ rc = crypto_skcipher_setkey(tfm_arc4, ses->auth_key.response,
+ CIFS_SESS_KEY_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not set response as a key\n",
+ __func__);
+ goto out_free_cipher;
+ }
+
+ req = skcipher_request_alloc(tfm_arc4, GFP_KERNEL);
+ if (!req) {
+ rc = -ENOMEM;
+ cifs_dbg(VFS, "could not allocate crypto API arc4 request\n");
+ goto out_free_cipher;
+ }
+
+ sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE);
+ sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
+
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &sgin, &sgout, CIFS_CPHTXT_SIZE, NULL);
+
+ rc = crypto_skcipher_encrypt(req);
+ skcipher_request_free(req);
+ if (rc) {
+ cifs_dbg(VFS, "could not encrypt session key rc: %d\n", rc);
+ goto out_free_cipher;
+ }
+
+ /* make secondary_key/nonce as session key */
+ memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE);
+ /* and make len as that of session key only */
+ ses->auth_key.len = CIFS_SESS_KEY_SIZE;
+
+out_free_cipher:
+ crypto_free_skcipher(tfm_arc4);
+out:
+ kfree(sec_key);
+ return rc;
+}
+
+void
+cifs_crypto_secmech_release(struct TCP_Server_Info *server)
+{
+ if (server->secmech.cmacaes) {
+ crypto_free_shash(server->secmech.cmacaes);
+ server->secmech.cmacaes = NULL;
+ }
+
+ if (server->secmech.hmacsha256) {
+ crypto_free_shash(server->secmech.hmacsha256);
+ server->secmech.hmacsha256 = NULL;
+ }
+
+ if (server->secmech.md5) {
+ crypto_free_shash(server->secmech.md5);
+ server->secmech.md5 = NULL;
+ }
+
+ if (server->secmech.sha512) {
+ crypto_free_shash(server->secmech.sha512);
+ server->secmech.sha512 = NULL;
+ }
+
+ if (server->secmech.hmacmd5) {
+ crypto_free_shash(server->secmech.hmacmd5);
+ server->secmech.hmacmd5 = NULL;
+ }
+
+ if (server->secmech.ccmaesencrypt) {
+ crypto_free_aead(server->secmech.ccmaesencrypt);
+ server->secmech.ccmaesencrypt = NULL;
+ }
+
+ if (server->secmech.ccmaesdecrypt) {
+ crypto_free_aead(server->secmech.ccmaesdecrypt);
+ server->secmech.ccmaesdecrypt = NULL;
+ }
+
+ kfree(server->secmech.sdesccmacaes);
+ server->secmech.sdesccmacaes = NULL;
+ kfree(server->secmech.sdeschmacsha256);
+ server->secmech.sdeschmacsha256 = NULL;
+ kfree(server->secmech.sdeschmacmd5);
+ server->secmech.sdeschmacmd5 = NULL;
+ kfree(server->secmech.sdescmd5);
+ server->secmech.sdescmd5 = NULL;
+ kfree(server->secmech.sdescsha512);
+ server->secmech.sdescsha512 = NULL;
+}
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
new file mode 100644
index 000000000..52b1524b4
--- /dev/null
+++ b/fs/cifs/cifsfs.c
@@ -0,0 +1,1568 @@
+/*
+ * fs/cifs/cifsfs.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Common Internet FileSystem (CIFS) client
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Note that BB means BUGBUG (ie something to fix eventually) */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/vfs.h>
+#include <linux/mempool.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/namei.h>
+#include <linux/random.h>
+#include <linux/uuid.h>
+#include <linux/xattr.h>
+#include <net/ipv6.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#define DECLARE_GLOBALS_HERE
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include <linux/mm.h>
+#include <linux/key-type.h>
+#include "cifs_spnego.h"
+#include "fscache.h"
+#include "smb2pdu.h"
+
+int cifsFYI = 0;
+bool traceSMB;
+bool enable_oplocks = true;
+bool linuxExtEnabled = true;
+bool lookupCacheEnabled = true;
+bool disable_legacy_dialects; /* false by default */
+unsigned int global_secflags = CIFSSEC_DEF;
+/* unsigned int ntlmv2_support = 0; */
+unsigned int sign_CIFS_PDUs = 1;
+static const struct super_operations cifs_super_ops;
+unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
+module_param(CIFSMaxBufSize, uint, 0444);
+MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header) "
+ "for CIFS requests. "
+ "Default: 16384 Range: 8192 to 130048");
+unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
+module_param(cifs_min_rcv, uint, 0444);
+MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "
+ "1 to 64");
+unsigned int cifs_min_small = 30;
+module_param(cifs_min_small, uint, 0444);
+MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "
+ "Range: 2 to 256");
+unsigned int cifs_max_pending = CIFS_MAX_REQ;
+module_param(cifs_max_pending, uint, 0444);
+MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for "
+ "CIFS/SMB1 dialect (N/A for SMB3) "
+ "Default: 32767 Range: 2 to 32767.");
+module_param(enable_oplocks, bool, 0644);
+MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1");
+
+module_param(disable_legacy_dialects, bool, 0644);
+MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be "
+ "helpful to restrict the ability to "
+ "override the default dialects (SMB2.1, "
+ "SMB3 and SMB3.02) on mount with old "
+ "dialects (CIFS/SMB1 and SMB2) since "
+ "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker"
+ " and less secure. Default: n/N/0");
+
+extern mempool_t *cifs_sm_req_poolp;
+extern mempool_t *cifs_req_poolp;
+extern mempool_t *cifs_mid_poolp;
+
+struct workqueue_struct *cifsiod_wq;
+struct workqueue_struct *cifsoplockd_wq;
+__u32 cifs_lock_secret;
+
+/*
+ * Bumps refcount for cifs super block.
+ * Note that it should be only called if a referece to VFS super block is
+ * already held, e.g. in open-type syscalls context. Otherwise it can race with
+ * atomic_dec_and_test in deactivate_locked_super.
+ */
+void
+cifs_sb_active(struct super_block *sb)
+{
+ struct cifs_sb_info *server = CIFS_SB(sb);
+
+ if (atomic_inc_return(&server->active) == 1)
+ atomic_inc(&sb->s_active);
+}
+
+void
+cifs_sb_deactive(struct super_block *sb)
+{
+ struct cifs_sb_info *server = CIFS_SB(sb);
+
+ if (atomic_dec_and_test(&server->active))
+ deactivate_super(sb);
+}
+
+static int
+cifs_read_super(struct super_block *sb)
+{
+ struct inode *inode;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ int rc = 0;
+
+ cifs_sb = CIFS_SB(sb);
+ tcon = cifs_sb_master_tcon(cifs_sb);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
+ sb->s_flags |= SB_POSIXACL;
+
+ if (tcon->snapshot_time)
+ sb->s_flags |= SB_RDONLY;
+
+ if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files)
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ else
+ sb->s_maxbytes = MAX_NON_LFS;
+
+ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
+ sb->s_time_gran = 100;
+
+ sb->s_magic = CIFS_MAGIC_NUMBER;
+ sb->s_op = &cifs_super_ops;
+ sb->s_xattr = cifs_xattr_handlers;
+ rc = super_setup_bdi(sb);
+ if (rc)
+ goto out_no_root;
+ /* tune readahead according to rsize */
+ sb->s_bdi->ra_pages = cifs_sb->rsize / PAGE_SIZE;
+
+ sb->s_blocksize = CIFS_MAX_MSGSIZE;
+ sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */
+ inode = cifs_root_iget(sb);
+
+ if (IS_ERR(inode)) {
+ rc = PTR_ERR(inode);
+ goto out_no_root;
+ }
+
+ if (tcon->nocase)
+ sb->s_d_op = &cifs_ci_dentry_ops;
+ else
+ sb->s_d_op = &cifs_dentry_ops;
+
+ sb->s_root = d_make_root(inode);
+ if (!sb->s_root) {
+ rc = -ENOMEM;
+ goto out_no_root;
+ }
+
+#ifdef CONFIG_CIFS_NFSD_EXPORT
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ cifs_dbg(FYI, "export ops supported\n");
+ sb->s_export_op = &cifs_export_ops;
+ }
+#endif /* CONFIG_CIFS_NFSD_EXPORT */
+
+ return 0;
+
+out_no_root:
+ cifs_dbg(VFS, "%s: get root inode failed\n", __func__);
+ return rc;
+}
+
+static void cifs_kill_sb(struct super_block *sb)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ kill_anon_super(sb);
+ cifs_umount(cifs_sb);
+}
+
+static int
+cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *sb = dentry->d_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int xid;
+ int rc = 0;
+
+ xid = get_xid();
+
+ if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0)
+ buf->f_namelen =
+ le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
+ else
+ buf->f_namelen = PATH_MAX;
+
+ buf->f_fsid.val[0] = tcon->vol_serial_number;
+ /* are using part of create time for more randomness, see man statfs */
+ buf->f_fsid.val[1] = (int)le64_to_cpu(tcon->vol_create_time);
+
+ buf->f_files = 0; /* undefined */
+ buf->f_ffree = 0; /* unlimited */
+
+ if (server->ops->queryfs)
+ rc = server->ops->queryfs(xid, tcon, buf);
+
+ free_xid(xid);
+ return rc;
+}
+
+static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ if (server->ops->fallocate)
+ return server->ops->fallocate(file, tcon, mode, off, len);
+
+ return -EOPNOTSUPP;
+}
+
+static int cifs_permission(struct inode *inode, int mask)
+{
+ struct cifs_sb_info *cifs_sb;
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
+ if ((mask & MAY_EXEC) && !execute_ok(inode))
+ return -EACCES;
+ else
+ return 0;
+ } else /* file mode might have been restricted at mount time
+ on the client (above and beyond ACL on servers) for
+ servers which do not support setting and viewing mode bits,
+ so allowing client to check permissions is useful */
+ return generic_permission(inode, mask);
+}
+
+static struct kmem_cache *cifs_inode_cachep;
+static struct kmem_cache *cifs_req_cachep;
+static struct kmem_cache *cifs_mid_cachep;
+static struct kmem_cache *cifs_sm_req_cachep;
+mempool_t *cifs_sm_req_poolp;
+mempool_t *cifs_req_poolp;
+mempool_t *cifs_mid_poolp;
+
+static struct inode *
+cifs_alloc_inode(struct super_block *sb)
+{
+ struct cifsInodeInfo *cifs_inode;
+ cifs_inode = kmem_cache_alloc(cifs_inode_cachep, GFP_KERNEL);
+ if (!cifs_inode)
+ return NULL;
+ cifs_inode->cifsAttrs = 0x20; /* default */
+ cifs_inode->time = 0;
+ /*
+ * Until the file is open and we have gotten oplock info back from the
+ * server, can not assume caching of file data or metadata.
+ */
+ cifs_set_oplock_level(cifs_inode, 0);
+ cifs_inode->flags = 0;
+ spin_lock_init(&cifs_inode->writers_lock);
+ cifs_inode->writers = 0;
+ cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
+ cifs_inode->server_eof = 0;
+ cifs_inode->uniqueid = 0;
+ cifs_inode->createtime = 0;
+ cifs_inode->epoch = 0;
+ spin_lock_init(&cifs_inode->open_file_lock);
+ generate_random_uuid(cifs_inode->lease_key);
+
+ /*
+ * Can not set i_flags here - they get immediately overwritten to zero
+ * by the VFS.
+ */
+ /* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
+ INIT_LIST_HEAD(&cifs_inode->openFileList);
+ INIT_LIST_HEAD(&cifs_inode->llist);
+ return &cifs_inode->vfs_inode;
+}
+
+static void cifs_i_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+ kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
+}
+
+static void
+cifs_destroy_inode(struct inode *inode)
+{
+ call_rcu(&inode->i_rcu, cifs_i_callback);
+}
+
+static void
+cifs_evict_inode(struct inode *inode)
+{
+ truncate_inode_pages_final(&inode->i_data);
+ clear_inode(inode);
+ cifs_fscache_release_inode_cookie(inode);
+}
+
+static void
+cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server)
+{
+ struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
+
+ seq_puts(s, ",addr=");
+
+ switch (server->dstaddr.ss_family) {
+ case AF_INET:
+ seq_printf(s, "%pI4", &sa->sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ seq_printf(s, "%pI6", &sa6->sin6_addr.s6_addr);
+ if (sa6->sin6_scope_id)
+ seq_printf(s, "%%%u", sa6->sin6_scope_id);
+ break;
+ default:
+ seq_puts(s, "(unknown)");
+ }
+ if (server->rdma)
+ seq_puts(s, ",rdma");
+}
+
+static void
+cifs_show_security(struct seq_file *s, struct cifs_ses *ses)
+{
+ if (ses->sectype == Unspecified) {
+ if (ses->user_name == NULL)
+ seq_puts(s, ",sec=none");
+ return;
+ }
+
+ seq_puts(s, ",sec=");
+
+ switch (ses->sectype) {
+ case LANMAN:
+ seq_puts(s, "lanman");
+ break;
+ case NTLMv2:
+ seq_puts(s, "ntlmv2");
+ break;
+ case NTLM:
+ seq_puts(s, "ntlm");
+ break;
+ case Kerberos:
+ seq_puts(s, "krb5");
+ break;
+ case RawNTLMSSP:
+ seq_puts(s, "ntlmssp");
+ break;
+ default:
+ /* shouldn't ever happen */
+ seq_puts(s, "unknown");
+ break;
+ }
+
+ if (ses->sign)
+ seq_puts(s, "i");
+}
+
+static void
+cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
+{
+ seq_puts(s, ",cache=");
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
+ seq_puts(s, "strict");
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
+ seq_puts(s, "none");
+ else
+ seq_puts(s, "loose");
+}
+
+static void
+cifs_show_nls(struct seq_file *s, struct nls_table *cur)
+{
+ struct nls_table *def;
+
+ /* Display iocharset= option if it's not default charset */
+ def = load_nls_default();
+ if (def != cur)
+ seq_printf(s, ",iocharset=%s", cur->charset);
+ unload_nls(def);
+}
+
+/*
+ * cifs_show_options() is for displaying mount options in /proc/mounts.
+ * Not all settable options are displayed but most of the important
+ * ones are.
+ */
+static int
+cifs_show_options(struct seq_file *s, struct dentry *root)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct sockaddr *srcaddr;
+ srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr;
+
+ seq_show_option(s, "vers", tcon->ses->server->vals->version_string);
+ cifs_show_security(s, tcon->ses);
+ cifs_show_cache_flavor(s, cifs_sb);
+
+ if (tcon->no_lease)
+ seq_puts(s, ",nolease");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
+ seq_puts(s, ",multiuser");
+ else if (tcon->ses->user_name)
+ seq_show_option(s, "username", tcon->ses->user_name);
+
+ if (tcon->ses->domainName && tcon->ses->domainName[0] != 0)
+ seq_show_option(s, "domain", tcon->ses->domainName);
+
+ if (srcaddr->sa_family != AF_UNSPEC) {
+ struct sockaddr_in *saddr4;
+ struct sockaddr_in6 *saddr6;
+ saddr4 = (struct sockaddr_in *)srcaddr;
+ saddr6 = (struct sockaddr_in6 *)srcaddr;
+ if (srcaddr->sa_family == AF_INET6)
+ seq_printf(s, ",srcaddr=%pI6c",
+ &saddr6->sin6_addr);
+ else if (srcaddr->sa_family == AF_INET)
+ seq_printf(s, ",srcaddr=%pI4",
+ &saddr4->sin_addr.s_addr);
+ else
+ seq_printf(s, ",srcaddr=BAD-AF:%i",
+ (int)(srcaddr->sa_family));
+ }
+
+ seq_printf(s, ",uid=%u",
+ from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid));
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
+ seq_puts(s, ",forceuid");
+ else
+ seq_puts(s, ",noforceuid");
+
+ seq_printf(s, ",gid=%u",
+ from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid));
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
+ seq_puts(s, ",forcegid");
+ else
+ seq_puts(s, ",noforcegid");
+
+ cifs_show_address(s, tcon->ses->server);
+
+ if (!tcon->unix_ext)
+ seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho",
+ cifs_sb->mnt_file_mode,
+ cifs_sb->mnt_dir_mode);
+
+ cifs_show_nls(s, cifs_sb->local_nls);
+
+ if (tcon->seal)
+ seq_puts(s, ",seal");
+ if (tcon->nocase)
+ seq_puts(s, ",nocase");
+ if (tcon->retry)
+ seq_puts(s, ",hard");
+ else
+ seq_puts(s, ",soft");
+ if (tcon->use_persistent)
+ seq_puts(s, ",persistenthandles");
+ else if (tcon->use_resilient)
+ seq_puts(s, ",resilienthandles");
+ if (tcon->posix_extensions)
+ seq_puts(s, ",posix");
+ else if (tcon->unix_ext)
+ seq_puts(s, ",unix");
+ else
+ seq_puts(s, ",nounix");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
+ seq_puts(s, ",posixpaths");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
+ seq_puts(s, ",setuids");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL)
+ seq_puts(s, ",idsfromsid");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
+ seq_puts(s, ",serverino");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
+ seq_puts(s, ",rwpidforward");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL)
+ seq_puts(s, ",forcemand");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ seq_puts(s, ",nouser_xattr");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+ seq_puts(s, ",mapchars");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
+ seq_puts(s, ",mapposix");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
+ seq_puts(s, ",sfu");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ seq_puts(s, ",nobrl");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_HANDLE_CACHE)
+ seq_puts(s, ",nohandlecache");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ seq_puts(s, ",cifsacl");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ seq_puts(s, ",dynperm");
+ if (root->d_sb->s_flags & SB_POSIXACL)
+ seq_puts(s, ",acl");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
+ seq_puts(s, ",mfsymlinks");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE)
+ seq_puts(s, ",fsc");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)
+ seq_puts(s, ",nostrictsync");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
+ seq_puts(s, ",noperm");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID)
+ seq_printf(s, ",backupuid=%u",
+ from_kuid_munged(&init_user_ns,
+ cifs_sb->mnt_backupuid));
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID)
+ seq_printf(s, ",backupgid=%u",
+ from_kgid_munged(&init_user_ns,
+ cifs_sb->mnt_backupgid));
+
+ seq_printf(s, ",rsize=%u", cifs_sb->rsize);
+ seq_printf(s, ",wsize=%u", cifs_sb->wsize);
+ seq_printf(s, ",echo_interval=%lu",
+ tcon->ses->server->echo_interval / HZ);
+ if (tcon->snapshot_time)
+ seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
+ /* convert actimeo and display it in seconds */
+ seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
+
+ return 0;
+}
+
+static void cifs_umount_begin(struct super_block *sb)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_tcon *tcon;
+
+ if (cifs_sb == NULL)
+ return;
+
+ tcon = cifs_sb_master_tcon(cifs_sb);
+
+ spin_lock(&cifs_tcp_ses_lock);
+ if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) {
+ /* we have other mounts to same share or we have
+ already tried to force umount this and woken up
+ all waiting network requests, nothing to do */
+ spin_unlock(&cifs_tcp_ses_lock);
+ return;
+ } else if (tcon->tc_count == 1)
+ tcon->tidStatus = CifsExiting;
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
+ /* cancel_notify_requests(tcon); */
+ if (tcon->ses && tcon->ses->server) {
+ cifs_dbg(FYI, "wake up tasks now - umount begin not complete\n");
+ wake_up_all(&tcon->ses->server->request_q);
+ wake_up_all(&tcon->ses->server->response_q);
+ msleep(1); /* yield */
+ /* we have to kick the requests once more */
+ wake_up_all(&tcon->ses->server->response_q);
+ msleep(1);
+ }
+
+ return;
+}
+
+#ifdef CONFIG_CIFS_STATS2
+static int cifs_show_stats(struct seq_file *s, struct dentry *root)
+{
+ /* BB FIXME */
+ return 0;
+}
+#endif
+
+static int cifs_remount(struct super_block *sb, int *flags, char *data)
+{
+ sync_filesystem(sb);
+ *flags |= SB_NODIRATIME;
+ return 0;
+}
+
+static int cifs_drop_inode(struct inode *inode)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ /* no serverino => unconditional eviction */
+ return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) ||
+ generic_drop_inode(inode);
+}
+
+static const struct super_operations cifs_super_ops = {
+ .statfs = cifs_statfs,
+ .alloc_inode = cifs_alloc_inode,
+ .destroy_inode = cifs_destroy_inode,
+ .drop_inode = cifs_drop_inode,
+ .evict_inode = cifs_evict_inode,
+/* .delete_inode = cifs_delete_inode, */ /* Do not need above
+ function unless later we add lazy close of inodes or unless the
+ kernel forgets to call us with the same number of releases (closes)
+ as opens */
+ .show_options = cifs_show_options,
+ .umount_begin = cifs_umount_begin,
+ .remount_fs = cifs_remount,
+#ifdef CONFIG_CIFS_STATS2
+ .show_stats = cifs_show_stats,
+#endif
+};
+
+/*
+ * Get root dentry from superblock according to prefix path mount option.
+ * Return dentry with refcount + 1 on success and NULL otherwise.
+ */
+static struct dentry *
+cifs_get_root(struct smb_vol *vol, struct super_block *sb)
+{
+ struct dentry *dentry;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ char *full_path = NULL;
+ char *s, *p;
+ char sep;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
+ return dget(sb->s_root);
+
+ full_path = cifs_build_path_to_root(vol, cifs_sb,
+ cifs_sb_master_tcon(cifs_sb), 0);
+ if (full_path == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ cifs_dbg(FYI, "Get root dentry for %s\n", full_path);
+
+ sep = CIFS_DIR_SEP(cifs_sb);
+ dentry = dget(sb->s_root);
+ p = s = full_path;
+
+ do {
+ struct inode *dir = d_inode(dentry);
+ struct dentry *child;
+
+ if (!dir) {
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+ break;
+ }
+ if (!S_ISDIR(dir->i_mode)) {
+ dput(dentry);
+ dentry = ERR_PTR(-ENOTDIR);
+ break;
+ }
+
+ /* skip separators */
+ while (*s == sep)
+ s++;
+ if (!*s)
+ break;
+ p = s++;
+ /* next separator */
+ while (*s && *s != sep)
+ s++;
+
+ child = lookup_one_len_unlocked(p, dentry, s - p);
+ dput(dentry);
+ dentry = child;
+ } while (!IS_ERR(dentry));
+ kfree(full_path);
+ return dentry;
+}
+
+static int cifs_set_super(struct super_block *sb, void *data)
+{
+ struct cifs_mnt_data *mnt_data = data;
+ sb->s_fs_info = mnt_data->cifs_sb;
+ return set_anon_super(sb, NULL);
+}
+
+static struct dentry *
+cifs_smb3_do_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data, bool is_smb3)
+{
+ int rc;
+ struct super_block *sb;
+ struct cifs_sb_info *cifs_sb;
+ struct smb_vol *volume_info;
+ struct cifs_mnt_data mnt_data;
+ struct dentry *root;
+
+ cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags);
+
+ volume_info = cifs_get_volume_info((char *)data, dev_name, is_smb3);
+ if (IS_ERR(volume_info))
+ return ERR_CAST(volume_info);
+
+ cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL);
+ if (cifs_sb == NULL) {
+ root = ERR_PTR(-ENOMEM);
+ goto out_nls;
+ }
+
+ cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL);
+ if (cifs_sb->mountdata == NULL) {
+ root = ERR_PTR(-ENOMEM);
+ goto out_free;
+ }
+
+ rc = cifs_setup_cifs_sb(volume_info, cifs_sb);
+ if (rc) {
+ root = ERR_PTR(rc);
+ goto out_free;
+ }
+
+ rc = cifs_mount(cifs_sb, volume_info);
+ if (rc) {
+ if (!(flags & SB_SILENT))
+ cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n",
+ rc);
+ root = ERR_PTR(rc);
+ goto out_free;
+ }
+
+ mnt_data.vol = volume_info;
+ mnt_data.cifs_sb = cifs_sb;
+ mnt_data.flags = flags;
+
+ /* BB should we make this contingent on mount parm? */
+ flags |= SB_NODIRATIME | SB_NOATIME;
+
+ sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data);
+ if (IS_ERR(sb)) {
+ root = ERR_CAST(sb);
+ cifs_umount(cifs_sb);
+ goto out;
+ }
+
+ if (sb->s_root) {
+ cifs_dbg(FYI, "Use existing superblock\n");
+ cifs_umount(cifs_sb);
+ } else {
+ rc = cifs_read_super(sb);
+ if (rc) {
+ root = ERR_PTR(rc);
+ goto out_super;
+ }
+
+ sb->s_flags |= SB_ACTIVE;
+ }
+
+ root = cifs_get_root(volume_info, sb);
+ if (IS_ERR(root))
+ goto out_super;
+
+ cifs_dbg(FYI, "dentry root is: %p\n", root);
+ goto out;
+
+out_super:
+ deactivate_locked_super(sb);
+ return root;
+out:
+ cifs_cleanup_volume_info(volume_info);
+ return root;
+
+out_free:
+ kfree(cifs_sb->prepath);
+ kfree(cifs_sb->mountdata);
+ kfree(cifs_sb);
+out_nls:
+ unload_nls(volume_info->local_nls);
+ goto out;
+}
+
+static struct dentry *
+smb3_do_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return cifs_smb3_do_mount(fs_type, flags, dev_name, data, true);
+}
+
+static struct dentry *
+cifs_do_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return cifs_smb3_do_mount(fs_type, flags, dev_name, data, false);
+}
+
+static ssize_t
+cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
+{
+ ssize_t rc;
+ struct inode *inode = file_inode(iocb->ki_filp);
+
+ if (iocb->ki_flags & IOCB_DIRECT)
+ return cifs_user_readv(iocb, iter);
+
+ rc = cifs_revalidate_mapping(inode);
+ if (rc)
+ return rc;
+
+ return generic_file_read_iter(iocb, iter);
+}
+
+static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ ssize_t written;
+ int rc;
+
+ if (iocb->ki_filp->f_flags & O_DIRECT) {
+ written = cifs_user_writev(iocb, from);
+ if (written > 0 && CIFS_CACHE_READ(cinode)) {
+ cifs_zap_mapping(inode);
+ cifs_dbg(FYI,
+ "Set no oplock for inode=%p after a write operation\n",
+ inode);
+ cinode->oplock = 0;
+ }
+ return written;
+ }
+
+ written = cifs_get_writer(cinode);
+ if (written)
+ return written;
+
+ written = generic_file_write_iter(iocb, from);
+
+ if (CIFS_CACHE_WRITE(CIFS_I(inode)))
+ goto out;
+
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (rc)
+ cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n",
+ rc, inode);
+
+out:
+ cifs_put_writer(cinode);
+ return written;
+}
+
+static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
+{
+ /*
+ * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
+ * the cached file length
+ */
+ if (whence != SEEK_SET && whence != SEEK_CUR) {
+ int rc;
+ struct inode *inode = file_inode(file);
+
+ /*
+ * We need to be sure that all dirty pages are written and the
+ * server has the newest file length.
+ */
+ if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
+ inode->i_mapping->nrpages != 0) {
+ rc = filemap_fdatawait(inode->i_mapping);
+ if (rc) {
+ mapping_set_error(inode->i_mapping, rc);
+ return rc;
+ }
+ }
+ /*
+ * Some applications poll for the file length in this strange
+ * way so we must seek to end on non-oplocked files by
+ * setting the revalidate time to zero.
+ */
+ CIFS_I(inode)->time = 0;
+
+ rc = cifs_revalidate_file_attr(file);
+ if (rc < 0)
+ return (loff_t)rc;
+ }
+ return generic_file_llseek(file, offset, whence);
+}
+
+static int
+cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv)
+{
+ /*
+ * Note that this is called by vfs setlease with i_lock held to
+ * protect *lease from going away.
+ */
+ struct inode *inode = file_inode(file);
+ struct cifsFileInfo *cfile = file->private_data;
+
+ if (!(S_ISREG(inode->i_mode)))
+ return -EINVAL;
+
+ /* Check if file is oplocked if this is request for new lease */
+ if (arg == F_UNLCK ||
+ ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
+ ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode))))
+ return generic_setlease(file, arg, lease, priv);
+ else if (tlink_tcon(cfile->tlink)->local_lease &&
+ !CIFS_CACHE_READ(CIFS_I(inode)))
+ /*
+ * If the server claims to support oplock on this file, then we
+ * still need to check oplock even if the local_lease mount
+ * option is set, but there are servers which do not support
+ * oplock for which this mount option may be useful if the user
+ * knows that the file won't be changed on the server by anyone
+ * else.
+ */
+ return generic_setlease(file, arg, lease, priv);
+ else
+ return -EAGAIN;
+}
+
+struct file_system_type cifs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "cifs",
+ .mount = cifs_do_mount,
+ .kill_sb = cifs_kill_sb,
+ /* .fs_flags */
+};
+MODULE_ALIAS_FS("cifs");
+
+static struct file_system_type smb3_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "smb3",
+ .mount = smb3_do_mount,
+ .kill_sb = cifs_kill_sb,
+ /* .fs_flags */
+};
+MODULE_ALIAS_FS("smb3");
+MODULE_ALIAS("smb3");
+
+const struct inode_operations cifs_dir_inode_ops = {
+ .create = cifs_create,
+ .atomic_open = cifs_atomic_open,
+ .lookup = cifs_lookup,
+ .getattr = cifs_getattr,
+ .unlink = cifs_unlink,
+ .link = cifs_hardlink,
+ .mkdir = cifs_mkdir,
+ .rmdir = cifs_rmdir,
+ .rename = cifs_rename2,
+ .permission = cifs_permission,
+ .setattr = cifs_setattr,
+ .symlink = cifs_symlink,
+ .mknod = cifs_mknod,
+ .listxattr = cifs_listxattr,
+};
+
+const struct inode_operations cifs_file_inode_ops = {
+ .setattr = cifs_setattr,
+ .getattr = cifs_getattr,
+ .permission = cifs_permission,
+ .listxattr = cifs_listxattr,
+};
+
+const struct inode_operations cifs_symlink_inode_ops = {
+ .get_link = cifs_get_link,
+ .permission = cifs_permission,
+ .listxattr = cifs_listxattr,
+};
+
+static int cifs_clone_file_range(struct file *src_file, loff_t off,
+ struct file *dst_file, loff_t destoff, u64 len)
+{
+ struct inode *src_inode = file_inode(src_file);
+ struct inode *target_inode = file_inode(dst_file);
+ struct cifsFileInfo *smb_file_src = src_file->private_data;
+ struct cifsFileInfo *smb_file_target;
+ struct cifs_tcon *target_tcon;
+ unsigned int xid;
+ int rc;
+
+ cifs_dbg(FYI, "clone range\n");
+
+ xid = get_xid();
+
+ if (!src_file->private_data || !dst_file->private_data) {
+ rc = -EBADF;
+ cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
+ goto out;
+ }
+
+ smb_file_target = dst_file->private_data;
+ target_tcon = tlink_tcon(smb_file_target->tlink);
+
+ /*
+ * Note: cifs case is easier than btrfs since server responsible for
+ * checks for proper open modes and file type and if it wants
+ * server could even support copy of range where source = target
+ */
+ lock_two_nondirectories(target_inode, src_inode);
+
+ if (len == 0)
+ len = src_inode->i_size - off;
+
+ cifs_dbg(FYI, "about to flush pages\n");
+ /* should we flush first and last page first */
+ truncate_inode_pages_range(&target_inode->i_data, destoff,
+ PAGE_ALIGN(destoff + len)-1);
+
+ if (target_tcon->ses->server->ops->duplicate_extents)
+ rc = target_tcon->ses->server->ops->duplicate_extents(xid,
+ smb_file_src, smb_file_target, off, len, destoff);
+ else
+ rc = -EOPNOTSUPP;
+
+ /* force revalidate of size and timestamps of target file now
+ that target is updated on the server */
+ CIFS_I(target_inode)->time = 0;
+ /* although unlocking in the reverse order from locking is not
+ strictly necessary here it is a little cleaner to be consistent */
+ unlock_two_nondirectories(src_inode, target_inode);
+out:
+ free_xid(xid);
+ return rc;
+}
+
+ssize_t cifs_file_copychunk_range(unsigned int xid,
+ struct file *src_file, loff_t off,
+ struct file *dst_file, loff_t destoff,
+ size_t len, unsigned int flags)
+{
+ struct inode *src_inode = file_inode(src_file);
+ struct inode *target_inode = file_inode(dst_file);
+ struct cifsFileInfo *smb_file_src;
+ struct cifsFileInfo *smb_file_target;
+ struct cifs_tcon *src_tcon;
+ struct cifs_tcon *target_tcon;
+ ssize_t rc;
+
+ cifs_dbg(FYI, "copychunk range\n");
+
+ if (src_inode == target_inode) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (!src_file->private_data || !dst_file->private_data) {
+ rc = -EBADF;
+ cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
+ goto out;
+ }
+
+ rc = -EXDEV;
+ smb_file_target = dst_file->private_data;
+ smb_file_src = src_file->private_data;
+ src_tcon = tlink_tcon(smb_file_src->tlink);
+ target_tcon = tlink_tcon(smb_file_target->tlink);
+
+ if (src_tcon->ses != target_tcon->ses) {
+ cifs_dbg(VFS, "source and target of copy not on same server\n");
+ goto out;
+ }
+
+ /*
+ * Note: cifs case is easier than btrfs since server responsible for
+ * checks for proper open modes and file type and if it wants
+ * server could even support copy of range where source = target
+ */
+ lock_two_nondirectories(target_inode, src_inode);
+
+ cifs_dbg(FYI, "about to flush pages\n");
+ /* should we flush first and last page first */
+ truncate_inode_pages(&target_inode->i_data, 0);
+
+ if (target_tcon->ses->server->ops->copychunk_range)
+ rc = target_tcon->ses->server->ops->copychunk_range(xid,
+ smb_file_src, smb_file_target, off, len, destoff);
+ else
+ rc = -EOPNOTSUPP;
+
+ /* force revalidate of size and timestamps of target file now
+ * that target is updated on the server
+ */
+ CIFS_I(target_inode)->time = 0;
+ /* although unlocking in the reverse order from locking is not
+ * strictly necessary here it is a little cleaner to be consistent
+ */
+ unlock_two_nondirectories(src_inode, target_inode);
+
+out:
+ return rc;
+}
+
+/*
+ * Directory operations under CIFS/SMB2/SMB3 are synchronous, so fsync()
+ * is a dummy operation.
+ */
+static int cifs_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ cifs_dbg(FYI, "Sync directory - name: %pD datasync: 0x%x\n",
+ file, datasync);
+
+ return 0;
+}
+
+static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
+ struct file *dst_file, loff_t destoff,
+ size_t len, unsigned int flags)
+{
+ unsigned int xid = get_xid();
+ ssize_t rc;
+
+ rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff,
+ len, flags);
+ free_xid(xid);
+ return rc;
+}
+
+const struct file_operations cifs_file_ops = {
+ .read_iter = cifs_loose_read_iter,
+ .write_iter = cifs_file_write_iter,
+ .open = cifs_open,
+ .release = cifs_close,
+ .lock = cifs_lock,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_mmap,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
+ .llseek = cifs_llseek,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .setlease = cifs_setlease,
+ .fallocate = cifs_fallocate,
+};
+
+const struct file_operations cifs_file_strict_ops = {
+ .read_iter = cifs_strict_readv,
+ .write_iter = cifs_strict_writev,
+ .open = cifs_open,
+ .release = cifs_close,
+ .lock = cifs_lock,
+ .fsync = cifs_strict_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_strict_mmap,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
+ .llseek = cifs_llseek,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .setlease = cifs_setlease,
+ .fallocate = cifs_fallocate,
+};
+
+const struct file_operations cifs_file_direct_ops = {
+ /* BB reevaluate whether they can be done with directio, no cache */
+ .read_iter = cifs_user_readv,
+ .write_iter = cifs_user_writev,
+ .open = cifs_open,
+ .release = cifs_close,
+ .lock = cifs_lock,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_mmap,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .llseek = cifs_llseek,
+ .setlease = cifs_setlease,
+ .fallocate = cifs_fallocate,
+};
+
+const struct file_operations cifs_file_nobrl_ops = {
+ .read_iter = cifs_loose_read_iter,
+ .write_iter = cifs_file_write_iter,
+ .open = cifs_open,
+ .release = cifs_close,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_mmap,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
+ .llseek = cifs_llseek,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .setlease = cifs_setlease,
+ .fallocate = cifs_fallocate,
+};
+
+const struct file_operations cifs_file_strict_nobrl_ops = {
+ .read_iter = cifs_strict_readv,
+ .write_iter = cifs_strict_writev,
+ .open = cifs_open,
+ .release = cifs_close,
+ .fsync = cifs_strict_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_strict_mmap,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
+ .llseek = cifs_llseek,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .setlease = cifs_setlease,
+ .fallocate = cifs_fallocate,
+};
+
+const struct file_operations cifs_file_direct_nobrl_ops = {
+ /* BB reevaluate whether they can be done with directio, no cache */
+ .read_iter = cifs_user_readv,
+ .write_iter = cifs_user_writev,
+ .open = cifs_open,
+ .release = cifs_close,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_mmap,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .llseek = cifs_llseek,
+ .setlease = cifs_setlease,
+ .fallocate = cifs_fallocate,
+};
+
+const struct file_operations cifs_dir_ops = {
+ .iterate_shared = cifs_readdir,
+ .release = cifs_closedir,
+ .read = generic_read_dir,
+ .unlocked_ioctl = cifs_ioctl,
+ .copy_file_range = cifs_copy_file_range,
+ .clone_file_range = cifs_clone_file_range,
+ .llseek = generic_file_llseek,
+ .fsync = cifs_dir_fsync,
+};
+
+static void
+cifs_init_once(void *inode)
+{
+ struct cifsInodeInfo *cifsi = inode;
+
+ inode_init_once(&cifsi->vfs_inode);
+ init_rwsem(&cifsi->lock_sem);
+}
+
+static int __init
+cifs_init_inodecache(void)
+{
+ cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
+ sizeof(struct cifsInodeInfo),
+ 0, (SLAB_RECLAIM_ACCOUNT|
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
+ cifs_init_once);
+ if (cifs_inode_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void
+cifs_destroy_inodecache(void)
+{
+ /*
+ * Make sure all delayed rcu free inodes are flushed before we
+ * destroy cache.
+ */
+ rcu_barrier();
+ kmem_cache_destroy(cifs_inode_cachep);
+}
+
+static int
+cifs_init_request_bufs(void)
+{
+ /*
+ * SMB2 maximum header size is bigger than CIFS one - no problems to
+ * allocate some more bytes for CIFS.
+ */
+ size_t max_hdr_size = MAX_SMB2_HDR_SIZE;
+
+ if (CIFSMaxBufSize < 8192) {
+ /* Buffer size can not be smaller than 2 * PATH_MAX since maximum
+ Unicode path name has to fit in any SMB/CIFS path based frames */
+ CIFSMaxBufSize = 8192;
+ } else if (CIFSMaxBufSize > 1024*127) {
+ CIFSMaxBufSize = 1024 * 127;
+ } else {
+ CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/
+ }
+/*
+ cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n",
+ CIFSMaxBufSize, CIFSMaxBufSize);
+*/
+ cifs_req_cachep = kmem_cache_create_usercopy("cifs_request",
+ CIFSMaxBufSize + max_hdr_size, 0,
+ SLAB_HWCACHE_ALIGN, 0,
+ CIFSMaxBufSize + max_hdr_size,
+ NULL);
+ if (cifs_req_cachep == NULL)
+ return -ENOMEM;
+
+ if (cifs_min_rcv < 1)
+ cifs_min_rcv = 1;
+ else if (cifs_min_rcv > 64) {
+ cifs_min_rcv = 64;
+ cifs_dbg(VFS, "cifs_min_rcv set to maximum (64)\n");
+ }
+
+ cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv,
+ cifs_req_cachep);
+
+ if (cifs_req_poolp == NULL) {
+ kmem_cache_destroy(cifs_req_cachep);
+ return -ENOMEM;
+ }
+ /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and
+ almost all handle based requests (but not write response, nor is it
+ sufficient for path based requests). A smaller size would have
+ been more efficient (compacting multiple slab items on one 4k page)
+ for the case in which debug was on, but this larger size allows
+ more SMBs to use small buffer alloc and is still much more
+ efficient to alloc 1 per page off the slab compared to 17K (5page)
+ alloc of large cifs buffers even when page debugging is on */
+ cifs_sm_req_cachep = kmem_cache_create_usercopy("cifs_small_rq",
+ MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN,
+ 0, MAX_CIFS_SMALL_BUFFER_SIZE, NULL);
+ if (cifs_sm_req_cachep == NULL) {
+ mempool_destroy(cifs_req_poolp);
+ kmem_cache_destroy(cifs_req_cachep);
+ return -ENOMEM;
+ }
+
+ if (cifs_min_small < 2)
+ cifs_min_small = 2;
+ else if (cifs_min_small > 256) {
+ cifs_min_small = 256;
+ cifs_dbg(FYI, "cifs_min_small set to maximum (256)\n");
+ }
+
+ cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small,
+ cifs_sm_req_cachep);
+
+ if (cifs_sm_req_poolp == NULL) {
+ mempool_destroy(cifs_req_poolp);
+ kmem_cache_destroy(cifs_req_cachep);
+ kmem_cache_destroy(cifs_sm_req_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+cifs_destroy_request_bufs(void)
+{
+ mempool_destroy(cifs_req_poolp);
+ kmem_cache_destroy(cifs_req_cachep);
+ mempool_destroy(cifs_sm_req_poolp);
+ kmem_cache_destroy(cifs_sm_req_cachep);
+}
+
+static int
+cifs_init_mids(void)
+{
+ cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids",
+ sizeof(struct mid_q_entry), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (cifs_mid_cachep == NULL)
+ return -ENOMEM;
+
+ /* 3 is a reasonable minimum number of simultaneous operations */
+ cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep);
+ if (cifs_mid_poolp == NULL) {
+ kmem_cache_destroy(cifs_mid_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+cifs_destroy_mids(void)
+{
+ mempool_destroy(cifs_mid_poolp);
+ kmem_cache_destroy(cifs_mid_cachep);
+}
+
+static int __init
+init_cifs(void)
+{
+ int rc = 0;
+ cifs_proc_init();
+ INIT_LIST_HEAD(&cifs_tcp_ses_list);
+#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
+ INIT_LIST_HEAD(&GlobalDnotifyReqList);
+ INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
+#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */
+/*
+ * Initialize Global counters
+ */
+ atomic_set(&sesInfoAllocCount, 0);
+ atomic_set(&tconInfoAllocCount, 0);
+ atomic_set(&tcpSesAllocCount, 0);
+ atomic_set(&tcpSesReconnectCount, 0);
+ atomic_set(&tconInfoReconnectCount, 0);
+
+ atomic_set(&bufAllocCount, 0);
+ atomic_set(&smBufAllocCount, 0);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_set(&totBufAllocCount, 0);
+ atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
+
+ atomic_set(&midCount, 0);
+ GlobalCurrentXid = 0;
+ GlobalTotalActiveXid = 0;
+ GlobalMaxActiveXid = 0;
+ spin_lock_init(&cifs_tcp_ses_lock);
+ spin_lock_init(&GlobalMid_Lock);
+
+ cifs_lock_secret = get_random_u32();
+
+ if (cifs_max_pending < 2) {
+ cifs_max_pending = 2;
+ cifs_dbg(FYI, "cifs_max_pending set to min of 2\n");
+ } else if (cifs_max_pending > CIFS_MAX_REQ) {
+ cifs_max_pending = CIFS_MAX_REQ;
+ cifs_dbg(FYI, "cifs_max_pending set to max of %u\n",
+ CIFS_MAX_REQ);
+ }
+
+ cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ if (!cifsiod_wq) {
+ rc = -ENOMEM;
+ goto out_clean_proc;
+ }
+
+ cifsoplockd_wq = alloc_workqueue("cifsoplockd",
+ WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ if (!cifsoplockd_wq) {
+ rc = -ENOMEM;
+ goto out_destroy_cifsiod_wq;
+ }
+
+ rc = cifs_fscache_register();
+ if (rc)
+ goto out_destroy_cifsoplockd_wq;
+
+ rc = cifs_init_inodecache();
+ if (rc)
+ goto out_unreg_fscache;
+
+ rc = cifs_init_mids();
+ if (rc)
+ goto out_destroy_inodecache;
+
+ rc = cifs_init_request_bufs();
+ if (rc)
+ goto out_destroy_mids;
+
+#ifdef CONFIG_CIFS_UPCALL
+ rc = init_cifs_spnego();
+ if (rc)
+ goto out_destroy_request_bufs;
+#endif /* CONFIG_CIFS_UPCALL */
+
+#ifdef CONFIG_CIFS_ACL
+ rc = init_cifs_idmap();
+ if (rc)
+ goto out_register_key_type;
+#endif /* CONFIG_CIFS_ACL */
+
+ rc = register_filesystem(&cifs_fs_type);
+ if (rc)
+ goto out_init_cifs_idmap;
+
+ rc = register_filesystem(&smb3_fs_type);
+ if (rc) {
+ unregister_filesystem(&cifs_fs_type);
+ goto out_init_cifs_idmap;
+ }
+
+ return 0;
+
+out_init_cifs_idmap:
+#ifdef CONFIG_CIFS_ACL
+ exit_cifs_idmap();
+out_register_key_type:
+#endif
+#ifdef CONFIG_CIFS_UPCALL
+ exit_cifs_spnego();
+out_destroy_request_bufs:
+#endif
+ cifs_destroy_request_bufs();
+out_destroy_mids:
+ cifs_destroy_mids();
+out_destroy_inodecache:
+ cifs_destroy_inodecache();
+out_unreg_fscache:
+ cifs_fscache_unregister();
+out_destroy_cifsoplockd_wq:
+ destroy_workqueue(cifsoplockd_wq);
+out_destroy_cifsiod_wq:
+ destroy_workqueue(cifsiod_wq);
+out_clean_proc:
+ cifs_proc_clean();
+ return rc;
+}
+
+static void __exit
+exit_cifs(void)
+{
+ cifs_dbg(NOISY, "exit_smb3\n");
+ unregister_filesystem(&cifs_fs_type);
+ unregister_filesystem(&smb3_fs_type);
+ cifs_dfs_release_automount_timer();
+#ifdef CONFIG_CIFS_ACL
+ exit_cifs_idmap();
+#endif
+#ifdef CONFIG_CIFS_UPCALL
+ exit_cifs_spnego();
+#endif
+ cifs_destroy_request_bufs();
+ cifs_destroy_mids();
+ cifs_destroy_inodecache();
+ cifs_fscache_unregister();
+ destroy_workqueue(cifsoplockd_wq);
+ destroy_workqueue(cifsiod_wq);
+ cifs_proc_clean();
+}
+
+MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
+MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */
+MODULE_DESCRIPTION
+ ("VFS to access servers complying with the SNIA CIFS Specification "
+ "e.g. Samba and Windows");
+MODULE_VERSION(CIFS_VERSION);
+MODULE_SOFTDEP("pre: arc4");
+MODULE_SOFTDEP("pre: des");
+MODULE_SOFTDEP("pre: ecb");
+MODULE_SOFTDEP("pre: hmac");
+MODULE_SOFTDEP("pre: md4");
+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_init(init_cifs)
+module_exit(exit_cifs)
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
new file mode 100644
index 000000000..f047e8787
--- /dev/null
+++ b/fs/cifs/cifsfs.h
@@ -0,0 +1,152 @@
+/*
+ * fs/cifs/cifsfs.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002, 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSFS_H
+#define _CIFSFS_H
+
+#include <linux/hash.h>
+
+#define ROOT_I 2
+
+/*
+ * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
+ * so that it will fit. We use hash_64 to convert the value to 31 bits, and
+ * then add 1, to ensure that we don't end up with a 0 as the value.
+ */
+static inline ino_t
+cifs_uniqueid_to_ino_t(u64 fileid)
+{
+ if ((sizeof(ino_t)) < (sizeof(u64)))
+ return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1;
+
+ return (ino_t)fileid;
+
+}
+
+static inline void cifs_set_time(struct dentry *dentry, unsigned long time)
+{
+ dentry->d_fsdata = (void *) time;
+}
+
+static inline unsigned long cifs_get_time(struct dentry *dentry)
+{
+ return (unsigned long) dentry->d_fsdata;
+}
+
+extern struct file_system_type cifs_fs_type;
+extern const struct address_space_operations cifs_addr_ops;
+extern const struct address_space_operations cifs_addr_ops_smallbuf;
+
+/* Functions related to super block operations */
+extern void cifs_sb_active(struct super_block *sb);
+extern void cifs_sb_deactive(struct super_block *sb);
+
+/* Functions related to inodes */
+extern const struct inode_operations cifs_dir_inode_ops;
+extern struct inode *cifs_root_iget(struct super_block *);
+extern int cifs_create(struct inode *, struct dentry *, umode_t,
+ bool excl);
+extern int cifs_atomic_open(struct inode *, struct dentry *,
+ struct file *, unsigned, umode_t);
+extern struct dentry *cifs_lookup(struct inode *, struct dentry *,
+ unsigned int);
+extern int cifs_unlink(struct inode *dir, struct dentry *dentry);
+extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
+extern int cifs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+extern int cifs_mkdir(struct inode *, struct dentry *, umode_t);
+extern int cifs_rmdir(struct inode *, struct dentry *);
+extern int cifs_rename2(struct inode *, struct dentry *, struct inode *,
+ struct dentry *, unsigned int);
+extern int cifs_revalidate_file_attr(struct file *filp);
+extern int cifs_revalidate_dentry_attr(struct dentry *);
+extern int cifs_revalidate_file(struct file *filp);
+extern int cifs_revalidate_dentry(struct dentry *);
+extern int cifs_invalidate_mapping(struct inode *inode);
+extern int cifs_revalidate_mapping(struct inode *inode);
+extern int cifs_zap_mapping(struct inode *inode);
+extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
+extern int cifs_setattr(struct dentry *, struct iattr *);
+
+extern const struct inode_operations cifs_file_inode_ops;
+extern const struct inode_operations cifs_symlink_inode_ops;
+extern const struct inode_operations cifs_dfs_referral_inode_operations;
+
+
+/* Functions related to files and directories */
+extern const struct file_operations cifs_file_ops;
+extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */
+extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */
+extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */
+extern const struct file_operations cifs_file_direct_nobrl_ops;
+extern const struct file_operations cifs_file_strict_nobrl_ops;
+extern int cifs_open(struct inode *inode, struct file *file);
+extern int cifs_close(struct inode *inode, struct file *file);
+extern int cifs_closedir(struct inode *inode, struct file *file);
+extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to);
+extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to);
+extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from);
+extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);
+extern int cifs_lock(struct file *, int, struct file_lock *);
+extern int cifs_fsync(struct file *, loff_t, loff_t, int);
+extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);
+extern int cifs_flush(struct file *, fl_owner_t id);
+extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
+extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);
+extern const struct file_operations cifs_dir_ops;
+extern int cifs_dir_open(struct inode *inode, struct file *file);
+extern int cifs_readdir(struct file *file, struct dir_context *ctx);
+
+/* Functions related to dir entries */
+extern const struct dentry_operations cifs_dentry_ops;
+extern const struct dentry_operations cifs_ci_dentry_ops;
+
+#ifdef CONFIG_CIFS_DFS_UPCALL
+extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
+#else
+#define cifs_dfs_d_automount NULL
+#endif
+
+/* Functions related to symlinks */
+extern const char *cifs_get_link(struct dentry *, struct inode *,
+ struct delayed_call *);
+extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
+ const char *symname);
+
+#ifdef CONFIG_CIFS_XATTR
+extern const struct xattr_handler *cifs_xattr_handlers[];
+extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
+#else
+# define cifs_xattr_handlers NULL
+# define cifs_listxattr NULL
+#endif
+
+extern ssize_t cifs_file_copychunk_range(unsigned int xid,
+ struct file *src_file, loff_t off,
+ struct file *dst_file, loff_t destoff,
+ size_t len, unsigned int flags);
+
+extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_CIFS_NFSD_EXPORT
+extern const struct export_operations cifs_export_ops;
+#endif /* CONFIG_CIFS_NFSD_EXPORT */
+
+#define CIFS_VERSION "2.13"
+#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
new file mode 100644
index 000000000..2c632793c
--- /dev/null
+++ b/fs/cifs/cifsglob.h
@@ -0,0 +1,1834 @@
+/*
+ * fs/cifs/cifsglob.h
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ */
+#ifndef _CIFS_GLOB_H
+#define _CIFS_GLOB_H
+
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/slab.h>
+#include <linux/mempool.h>
+#include <linux/workqueue.h>
+#include "cifs_fs_sb.h"
+#include "cifsacl.h"
+#include <crypto/internal/hash.h>
+#include <linux/scatterlist.h>
+#include <uapi/linux/cifs/cifs_mount.h>
+#include "smb2pdu.h"
+
+#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
+
+#define CIFS_PORT 445
+#define RFC1001_PORT 139
+
+/*
+ * The sizes of various internal tables and strings
+ */
+#define MAX_UID_INFO 16
+#define MAX_SES_INFO 2
+#define MAX_TCON_INFO 4
+
+#define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1)
+
+#define CIFS_MIN_RCV_POOL 4
+
+#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */
+/*
+ * default attribute cache timeout (jiffies)
+ */
+#define CIFS_DEF_ACTIMEO (1 * HZ)
+
+/*
+ * max attribute cache timeout (jiffies) - 2^30
+ */
+#define CIFS_MAX_ACTIMEO (1 << 30)
+
+/*
+ * MAX_REQ is the maximum number of requests that WE will send
+ * on one socket concurrently.
+ */
+#define CIFS_MAX_REQ 32767
+
+#define RFC1001_NAME_LEN 15
+#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1)
+
+/* maximum length of ip addr as a string (including ipv6 and sctp) */
+#define SERVER_NAME_LENGTH 80
+#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1)
+
+/* echo interval in seconds */
+#define SMB_ECHO_INTERVAL_MIN 1
+#define SMB_ECHO_INTERVAL_MAX 600
+#define SMB_ECHO_INTERVAL_DEFAULT 60
+
+/* maximum number of PDUs in one compound */
+#define MAX_COMPOUND 5
+
+/*
+ * Default number of credits to keep available for SMB3.
+ * This value is chosen somewhat arbitrarily. The Windows client
+ * defaults to 128 credits, the Windows server allows clients up to
+ * 512 credits (or 8K for later versions), and the NetApp server
+ * does not limit clients at all. Choose a high enough default value
+ * such that the client shouldn't limit performance, but allow mount
+ * to override (until you approach 64K, where we limit credits to 65000
+ * to reduce possibility of seeing more server credit overflow bugs.
+ */
+#define SMB2_MAX_CREDITS_AVAILABLE 32000
+
+#include "cifspdu.h"
+
+#ifndef XATTR_DOS_ATTRIB
+#define XATTR_DOS_ATTRIB "user.DOSATTRIB"
+#endif
+
+/*
+ * CIFS vfs client Status information (based on what we know.)
+ */
+
+/* associated with each tcp and smb session */
+enum statusEnum {
+ CifsNew = 0,
+ CifsGood,
+ CifsExiting,
+ CifsNeedReconnect,
+ CifsNeedNegotiate
+};
+
+enum securityEnum {
+ Unspecified = 0, /* not specified */
+ LANMAN, /* Legacy LANMAN auth */
+ NTLM, /* Legacy NTLM012 auth with NTLM hash */
+ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */
+ RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */
+ Kerberos, /* Kerberos via SPNEGO */
+};
+
+struct session_key {
+ unsigned int len;
+ char *response;
+};
+
+/* crypto security descriptor definition */
+struct sdesc {
+ struct shash_desc shash;
+ char ctx[];
+};
+
+/* crypto hashing related structure/fields, not specific to a sec mech */
+struct cifs_secmech {
+ struct crypto_shash *hmacmd5; /* hmac-md5 hash function */
+ struct crypto_shash *md5; /* md5 hash function */
+ struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */
+ struct crypto_shash *cmacaes; /* block-cipher based MAC function */
+ struct crypto_shash *sha512; /* sha512 hash function */
+ struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */
+ struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */
+ struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */
+ struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */
+ struct sdesc *sdescsha512; /* ctxt to generate smb3.11 signing key */
+ struct crypto_aead *ccmaesencrypt; /* smb3 encryption aead */
+ struct crypto_aead *ccmaesdecrypt; /* smb3 decryption aead */
+};
+
+/* per smb session structure/fields */
+struct ntlmssp_auth {
+ bool sesskey_per_smbsess; /* whether session key is per smb session */
+ __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */
+ __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */
+ unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */
+ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlmssp */
+};
+
+struct cifs_cred {
+ int uid;
+ int gid;
+ int mode;
+ int cecount;
+ struct cifs_sid osid;
+ struct cifs_sid gsid;
+ struct cifs_ntace *ntaces;
+ struct cifs_ace *aces;
+};
+
+/*
+ *****************************************************************
+ * Except the CIFS PDUs themselves all the
+ * globally interesting structs should go here
+ *****************************************************************
+ */
+
+/*
+ * A smb_rqst represents a complete request to be issued to a server. It's
+ * formed by a kvec array, followed by an array of pages. Page data is assumed
+ * to start at the beginning of the first page.
+ */
+struct smb_rqst {
+ struct kvec *rq_iov; /* array of kvecs */
+ unsigned int rq_nvec; /* number of kvecs in array */
+ struct page **rq_pages; /* pointer to array of page ptrs */
+ unsigned int rq_offset; /* the offset to the 1st page */
+ unsigned int rq_npages; /* number pages in array */
+ unsigned int rq_pagesz; /* page size to use */
+ unsigned int rq_tailsz; /* length of last page */
+};
+
+enum smb_version {
+ Smb_1 = 1,
+ Smb_20,
+ Smb_21,
+ Smb_30,
+ Smb_302,
+ Smb_311,
+ Smb_3any,
+ Smb_default,
+ Smb_version_err
+};
+
+struct mid_q_entry;
+struct TCP_Server_Info;
+struct cifsFileInfo;
+struct cifs_ses;
+struct cifs_tcon;
+struct dfs_info3_param;
+struct cifs_fattr;
+struct smb_vol;
+struct cifs_fid;
+struct cifs_readdata;
+struct cifs_writedata;
+struct cifs_io_parms;
+struct cifs_search_info;
+struct cifsInodeInfo;
+struct cifs_open_parms;
+
+struct smb_version_operations {
+ int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
+ struct mid_q_entry *);
+ bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
+ /* setup request: allocate mid, sign message */
+ struct mid_q_entry *(*setup_request)(struct cifs_ses *,
+ struct smb_rqst *);
+ /* setup async request: allocate mid, sign message */
+ struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
+ struct smb_rqst *);
+ /* check response: verify signature, map error */
+ int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
+ bool);
+ void (*add_credits)(struct TCP_Server_Info *, const unsigned int,
+ const int);
+ void (*set_credits)(struct TCP_Server_Info *, const int);
+ int * (*get_credits_field)(struct TCP_Server_Info *, const int);
+ unsigned int (*get_credits)(struct mid_q_entry *);
+ __u64 (*get_next_mid)(struct TCP_Server_Info *);
+ void (*revert_current_mid)(struct TCP_Server_Info *server,
+ const unsigned int val);
+ /* data offset from read response message */
+ unsigned int (*read_data_offset)(char *);
+ /*
+ * Data length from read response message
+ * When in_remaining is true, the returned data length is in
+ * message field DataRemaining for out-of-band data read (e.g through
+ * Memory Registration RDMA write in SMBD).
+ * Otherwise, the returned data length is in message field DataLength.
+ */
+ unsigned int (*read_data_length)(char *, bool in_remaining);
+ /* map smb to linux error */
+ int (*map_error)(char *, bool);
+ /* find mid corresponding to the response message */
+ struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *);
+ void (*dump_detail)(void *buf, struct TCP_Server_Info *ptcp_info);
+ void (*clear_stats)(struct cifs_tcon *);
+ void (*print_stats)(struct seq_file *m, struct cifs_tcon *);
+ void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *);
+ /* verify the message */
+ int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
+ bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
+ int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
+ void (*downgrade_oplock)(struct TCP_Server_Info *server,
+ struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache);
+ /* process transaction2 response */
+ bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *,
+ char *, int);
+ /* check if we need to negotiate */
+ bool (*need_neg)(struct TCP_Server_Info *);
+ /* negotiate to the server */
+ int (*negotiate)(const unsigned int, struct cifs_ses *);
+ /* set negotiated write size */
+ unsigned int (*negotiate_wsize)(struct cifs_tcon *, struct smb_vol *);
+ /* set negotiated read size */
+ unsigned int (*negotiate_rsize)(struct cifs_tcon *, struct smb_vol *);
+ /* setup smb sessionn */
+ int (*sess_setup)(const unsigned int, struct cifs_ses *,
+ const struct nls_table *);
+ /* close smb session */
+ int (*logoff)(const unsigned int, struct cifs_ses *);
+ /* connect to a server share */
+ int (*tree_connect)(const unsigned int, struct cifs_ses *, const char *,
+ struct cifs_tcon *, const struct nls_table *);
+ /* close tree connecion */
+ int (*tree_disconnect)(const unsigned int, struct cifs_tcon *);
+ /* get DFS referrals */
+ int (*get_dfs_refer)(const unsigned int, struct cifs_ses *,
+ const char *, struct dfs_info3_param **,
+ unsigned int *, const struct nls_table *, int);
+ /* informational QFS call */
+ void (*qfs_tcon)(const unsigned int, struct cifs_tcon *);
+ /* check if a path is accessible or not */
+ int (*is_path_accessible)(const unsigned int, struct cifs_tcon *,
+ struct cifs_sb_info *, const char *);
+ /* query path data from the server */
+ int (*query_path_info)(const unsigned int, struct cifs_tcon *,
+ struct cifs_sb_info *, const char *,
+ FILE_ALL_INFO *, bool *, bool *);
+ /* query file data from the server */
+ int (*query_file_info)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *, FILE_ALL_INFO *);
+ /* get server index number */
+ int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
+ struct cifs_sb_info *, const char *,
+ u64 *uniqueid, FILE_ALL_INFO *);
+ /* set size by path */
+ int (*set_path_size)(const unsigned int, struct cifs_tcon *,
+ const char *, __u64, struct cifs_sb_info *, bool);
+ /* set size by file handle */
+ int (*set_file_size)(const unsigned int, struct cifs_tcon *,
+ struct cifsFileInfo *, __u64, bool);
+ /* set attributes */
+ int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
+ const unsigned int);
+ int (*set_compression)(const unsigned int, struct cifs_tcon *,
+ struct cifsFileInfo *);
+ /* check if we can send an echo or nor */
+ bool (*can_echo)(struct TCP_Server_Info *);
+ /* send echo request */
+ int (*echo)(struct TCP_Server_Info *);
+ /* create directory */
+ int (*posix_mkdir)(const unsigned int xid, struct inode *inode,
+ umode_t mode, struct cifs_tcon *tcon,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb);
+ int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *,
+ struct cifs_sb_info *);
+ /* set info on created directory */
+ void (*mkdir_setinfo)(struct inode *, const char *,
+ struct cifs_sb_info *, struct cifs_tcon *,
+ const unsigned int);
+ /* remove directory */
+ int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *,
+ struct cifs_sb_info *);
+ /* unlink file */
+ int (*unlink)(const unsigned int, struct cifs_tcon *, const char *,
+ struct cifs_sb_info *);
+ /* open, rename and delete file */
+ int (*rename_pending_delete)(const char *, struct dentry *,
+ const unsigned int);
+ /* send rename request */
+ int (*rename)(const unsigned int, struct cifs_tcon *, const char *,
+ const char *, struct cifs_sb_info *);
+ /* send create hardlink request */
+ int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
+ const char *, const char *,
+ struct cifs_sb_info *);
+ /* query symlink target */
+ int (*query_symlink)(const unsigned int, struct cifs_tcon *,
+ const char *, char **, struct cifs_sb_info *);
+ /* open a file for non-posix mounts */
+ int (*open)(const unsigned int, struct cifs_open_parms *,
+ __u32 *, FILE_ALL_INFO *);
+ /* set fid protocol-specific info */
+ void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
+ /* close a file */
+ void (*close)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *);
+ /* send a flush request to the server */
+ int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
+ /* async read from the server */
+ int (*async_readv)(struct cifs_readdata *);
+ /* async write to the server */
+ int (*async_writev)(struct cifs_writedata *,
+ void (*release)(struct kref *));
+ /* sync read from the server */
+ int (*sync_read)(const unsigned int, struct cifs_fid *,
+ struct cifs_io_parms *, unsigned int *, char **,
+ int *);
+ /* sync write to the server */
+ int (*sync_write)(const unsigned int, struct cifs_fid *,
+ struct cifs_io_parms *, unsigned int *, struct kvec *,
+ unsigned long);
+ /* open dir, start readdir */
+ int (*query_dir_first)(const unsigned int, struct cifs_tcon *,
+ const char *, struct cifs_sb_info *,
+ struct cifs_fid *, __u16,
+ struct cifs_search_info *);
+ /* continue readdir */
+ int (*query_dir_next)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *,
+ __u16, struct cifs_search_info *srch_inf);
+ /* close dir */
+ int (*close_dir)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *);
+ /* calculate a size of SMB message */
+ unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi);
+ /* check for STATUS_PENDING and process it in a positive case */
+ bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
+ /* check for STATUS_NETWORK_SESSION_EXPIRED */
+ bool (*is_session_expired)(char *);
+ /* send oplock break response */
+ int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
+ struct cifsInodeInfo *);
+ /* query remote filesystem */
+ int (*queryfs)(const unsigned int, struct cifs_tcon *,
+ struct kstatfs *);
+ /* send mandatory brlock to the server */
+ int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64,
+ __u64, __u32, int, int, bool);
+ /* unlock range of mandatory locks */
+ int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *,
+ const unsigned int);
+ /* push brlocks from the cache to the server */
+ int (*push_mand_locks)(struct cifsFileInfo *);
+ /* get lease key of the inode */
+ void (*get_lease_key)(struct inode *, struct cifs_fid *);
+ /* set lease key of the inode */
+ void (*set_lease_key)(struct inode *, struct cifs_fid *);
+ /* generate new lease key */
+ void (*new_lease_key)(struct cifs_fid *);
+ int (*generate_signingkey)(struct cifs_ses *);
+ int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *);
+ int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
+ struct cifsFileInfo *src_file);
+ int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *src_file, void __user *);
+ int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
+ struct cifs_sb_info *, const unsigned char *,
+ char *, unsigned int *);
+ int (*create_mf_symlink)(unsigned int, struct cifs_tcon *,
+ struct cifs_sb_info *, const unsigned char *,
+ char *, unsigned int *);
+ /* if we can do cache read operations */
+ bool (*is_read_op)(__u32);
+ /* set oplock level for the inode */
+ void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int,
+ bool *);
+ /* create lease context buffer for CREATE request */
+ char * (*create_lease_buf)(u8 *lease_key, u8 oplock);
+ /* parse lease context buffer and return oplock/epoch info */
+ __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey);
+ ssize_t (*copychunk_range)(const unsigned int,
+ struct cifsFileInfo *src_file,
+ struct cifsFileInfo *target_file,
+ u64 src_off, u64 len, u64 dest_off);
+ int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src,
+ struct cifsFileInfo *target_file, u64 src_off, u64 len,
+ u64 dest_off);
+ int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
+ ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *,
+ const unsigned char *, const unsigned char *, char *,
+ size_t, struct cifs_sb_info *);
+ int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *,
+ const char *, const void *, const __u16,
+ const struct nls_table *, struct cifs_sb_info *);
+ struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *,
+ const char *, u32 *);
+ struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *,
+ const struct cifs_fid *, u32 *);
+ int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *,
+ int);
+ /* writepages retry size */
+ unsigned int (*wp_retry_size)(struct inode *);
+ /* get mtu credits */
+ int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int,
+ unsigned int *, unsigned int *);
+ /* check if we need to issue closedir */
+ bool (*dir_needs_close)(struct cifsFileInfo *);
+ long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
+ loff_t);
+ /* init transform request - used for encryption for now */
+ int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst,
+ struct smb_rqst *, struct smb_rqst *);
+ int (*is_transform_hdr)(void *buf);
+ int (*receive_transform)(struct TCP_Server_Info *,
+ struct mid_q_entry **, char **, int *);
+ enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
+ enum securityEnum);
+ int (*next_header)(char *);
+};
+
+struct smb_version_values {
+ char *version_string;
+ __u16 protocol_id;
+ __u32 req_capabilities;
+ __u32 large_lock_type;
+ __u32 exclusive_lock_type;
+ __u32 shared_lock_type;
+ __u32 unlock_lock_type;
+ size_t header_preamble_size;
+ size_t header_size;
+ size_t max_header_size;
+ size_t read_rsp_size;
+ __le16 lock_cmd;
+ 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;
+};
+
+#define HEADER_SIZE(server) (server->vals->header_size)
+#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
+
+struct smb_vol {
+ char *username;
+ char *password;
+ char *domainname;
+ char *UNC;
+ char *iocharset; /* local code page for mapping to and from Unicode */
+ char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
+ char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
+ kuid_t cred_uid;
+ kuid_t linux_uid;
+ kgid_t linux_gid;
+ kuid_t backupuid;
+ kgid_t backupgid;
+ umode_t file_mode;
+ umode_t dir_mode;
+ enum securityEnum sectype; /* sectype requested via mnt opts */
+ bool sign; /* was signing requested via mnt opts? */
+ bool retry:1;
+ bool intr:1;
+ bool setuids:1;
+ bool setuidfromacl:1;
+ bool override_uid:1;
+ bool override_gid:1;
+ bool dynperm:1;
+ bool noperm:1;
+ bool no_psx_acl:1; /* set if posix acl support should be disabled */
+ bool cifs_acl:1;
+ bool backupuid_specified; /* mount option backupuid is specified */
+ bool backupgid_specified; /* mount option backupgid is specified */
+ bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
+ bool server_ino:1; /* use inode numbers from server ie UniqueId */
+ bool direct_io:1;
+ bool strict_io:1; /* strict cache behavior */
+ bool remap:1; /* set to remap seven reserved chars in filenames */
+ bool sfu_remap:1; /* remap seven reserved chars ala SFU */
+ bool posix_paths:1; /* unset to not ask for posix pathnames. */
+ bool no_linux_ext:1;
+ bool linux_ext:1;
+ bool sfu_emul:1;
+ bool nullauth:1; /* attempt to authenticate with null user */
+ bool nocase:1; /* request case insensitive filenames */
+ bool nobrl:1; /* disable sending byte range locks to srv */
+ bool nohandlecache:1; /* disable caching dir handles if srvr probs */
+ bool mand_lock:1; /* send mandatory not posix byte range lock reqs */
+ bool seal:1; /* request transport encryption on share */
+ bool nodfs:1; /* Do not request DFS, even if available */
+ bool local_lease:1; /* check leases only on local system, not remote */
+ bool noblocksnd:1;
+ bool noautotune:1;
+ bool nostrictsync:1; /* do not force expensive SMBflush on every sync */
+ bool no_lease:1; /* disable requesting leases */
+ bool fsc:1; /* enable fscache */
+ bool mfsymlinks:1; /* use Minshall+French Symlinks */
+ bool multiuser:1;
+ bool rwpidforward:1; /* pid forward for read/write operations */
+ bool nosharesock:1;
+ bool persistent:1;
+ bool nopersistent:1;
+ bool resilient:1; /* noresilient not required since not fored for CA */
+ bool domainauto:1;
+ bool rdma:1;
+ unsigned int rsize;
+ unsigned int wsize;
+ bool sockopt_tcp_nodelay:1;
+ unsigned long actimeo; /* attribute cache timeout (jiffies) */
+ struct smb_version_operations *ops;
+ struct smb_version_values *vals;
+ char *prepath;
+ struct sockaddr_storage dstaddr; /* destination address */
+ struct sockaddr_storage srcaddr; /* allow binding to a local IP */
+ struct nls_table *local_nls;
+ unsigned int echo_interval; /* echo interval in secs */
+ __u64 snapshot_time; /* needed for timewarp tokens */
+ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
+};
+
+#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
+ CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \
+ CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \
+ CIFS_MOUNT_MAP_SFM_CHR | \
+ CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \
+ CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \
+ CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \
+ CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \
+ CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \
+ CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
+ CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID)
+
+#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \
+ SB_NODEV | SB_SYNCHRONOUS)
+
+struct cifs_mnt_data {
+ struct cifs_sb_info *cifs_sb;
+ struct smb_vol *vol;
+ int flags;
+};
+
+static inline unsigned int
+get_rfc1002_length(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);
+}
+
+struct TCP_Server_Info {
+ struct list_head tcp_ses_list;
+ struct list_head smb_ses_list;
+ int srv_count; /* reference counter */
+ /* 15 character server name + 0x20 16th byte indicating type = srv */
+ char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
+ struct smb_version_operations *ops;
+ struct smb_version_values *vals;
+ enum statusEnum tcpStatus; /* what we think the status is */
+ char *hostname; /* hostname portion of UNC string */
+ struct socket *ssocket;
+ struct sockaddr_storage dstaddr;
+ struct sockaddr_storage srcaddr; /* locally bind to this IP */
+#ifdef CONFIG_NET_NS
+ struct net *net;
+#endif
+ wait_queue_head_t response_q;
+ wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
+ struct list_head pending_mid_q;
+ bool noblocksnd; /* use blocking sendmsg */
+ bool noautotune; /* do not autotune send buf sizes */
+ bool tcp_nodelay;
+ unsigned int credits; /* send no more requests at once */
+ unsigned int max_credits; /* can override large 32000 default at mnt */
+ unsigned int in_flight; /* number of requests on the wire to server */
+ spinlock_t req_lock; /* protect the two values above */
+ struct mutex srv_mutex;
+ struct task_struct *tsk;
+ char server_GUID[16];
+ __u16 sec_mode;
+ bool sign; /* is signing enabled on this connection? */
+ bool session_estab; /* mark when very first sess is established */
+ int echo_credits; /* echo reserved slots */
+ int oplock_credits; /* oplock break reserved slots */
+ bool echoes:1; /* enable echoes */
+ __u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */
+ u16 dialect; /* dialect index that server chose */
+ bool oplocks:1; /* enable oplocks */
+ unsigned int maxReq; /* Clients should submit no more */
+ /* than maxReq distinct unanswered SMBs to the server when using */
+ /* multiplexed reads or writes (for SMB1/CIFS only, not SMB2/SMB3) */
+ unsigned int maxBuf; /* maxBuf specifies the maximum */
+ /* message size the server can send or receive for non-raw SMBs */
+ /* maxBuf is returned by SMB NegotiateProtocol so maxBuf is only 0 */
+ /* when socket is setup (and during reconnect) before NegProt sent */
+ unsigned int max_rw; /* maxRw specifies the maximum */
+ /* message size the server can send or receive for */
+ /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
+ unsigned int capabilities; /* selective disabling of caps by smb sess */
+ int timeAdj; /* Adjust for difference in server time zone in sec */
+ __u64 CurrentMid; /* multiplex id - rotating counter */
+ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
+ /* 16th byte of RFC1001 workstation name is always null */
+ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
+ __u32 sequence_number; /* for signing, protected by srv_mutex */
+ struct session_key session_key;
+ unsigned long lstrp; /* when we got last response from this server */
+ struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */
+#define CIFS_NEGFLAVOR_LANMAN 0 /* wct == 13, LANMAN */
+#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */
+#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */
+ char negflavor; /* NEGOTIATE response flavor */
+ /* extended security flavors that server supports */
+ bool sec_ntlmssp; /* supports NTLMSSP */
+ bool sec_kerberosu2u; /* supports U2U Kerberos */
+ bool sec_kerberos; /* supports plain Kerberos */
+ bool sec_mskerberos; /* supports legacy MS Kerberos */
+ bool large_buf; /* is current buffer large? */
+ /* use SMBD connection instead of socket */
+ bool rdma;
+ /* point to the SMBD connection if RDMA is used instead of socket */
+ struct smbd_connection *smbd_conn;
+ struct delayed_work echo; /* echo ping workqueue job */
+ char *smallbuf; /* pointer to current "small" buffer */
+ char *bigbuf; /* pointer to current "big" buffer */
+ /* Total size of this PDU. Only valid from cifs_demultiplex_thread */
+ unsigned int pdu_size;
+ unsigned int total_read; /* total amount of data read in this pass */
+#ifdef CONFIG_CIFS_FSCACHE
+ struct fscache_cookie *fscache; /* client index cache cookie */
+#endif
+#ifdef CONFIG_CIFS_STATS2
+ atomic_t in_send; /* requests trying to send */
+ atomic_t num_waiters; /* blocked waiting to get in sendrecv */
+ atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
+#endif /* STATS2 */
+ unsigned int max_read;
+ unsigned int max_write;
+ __le16 cipher_type;
+ /* save initital negprot hash */
+ __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
+ bool posix_ext_supported;
+ struct delayed_work reconnect; /* reconnect workqueue job */
+ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
+ unsigned long echo_interval;
+};
+
+static inline unsigned int
+in_flight(struct TCP_Server_Info *server)
+{
+ unsigned int num;
+ spin_lock(&server->req_lock);
+ num = server->in_flight;
+ spin_unlock(&server->req_lock);
+ return num;
+}
+
+static inline bool
+has_credits(struct TCP_Server_Info *server, int *credits)
+{
+ int num;
+ spin_lock(&server->req_lock);
+ num = *credits;
+ spin_unlock(&server->req_lock);
+ return num > 0;
+}
+
+static inline void
+add_credits(struct TCP_Server_Info *server, const unsigned int add,
+ const int optype)
+{
+ server->ops->add_credits(server, add, optype);
+}
+
+static inline void
+add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add,
+ const int optype)
+{
+ if (add) {
+ server->ops->add_credits(server, add, optype);
+ wake_up(&server->request_q);
+ }
+}
+
+static inline void
+set_credits(struct TCP_Server_Info *server, const int val)
+{
+ server->ops->set_credits(server, val);
+}
+
+static inline __le64
+get_next_mid64(struct TCP_Server_Info *server)
+{
+ return cpu_to_le64(server->ops->get_next_mid(server));
+}
+
+static inline __le16
+get_next_mid(struct TCP_Server_Info *server)
+{
+ __u16 mid = server->ops->get_next_mid(server);
+ /*
+ * The value in the SMB header should be little endian for easy
+ * on-the-wire decoding.
+ */
+ return cpu_to_le16(mid);
+}
+
+static inline void
+revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
+{
+ if (server->ops->revert_current_mid)
+ server->ops->revert_current_mid(server, val);
+}
+
+static inline void
+revert_current_mid_from_hdr(struct TCP_Server_Info *server,
+ const struct smb2_sync_hdr *shdr)
+{
+ unsigned int num = le16_to_cpu(shdr->CreditCharge);
+
+ return revert_current_mid(server, num > 0 ? num : 1);
+}
+
+static inline __u16
+get_mid(const struct smb_hdr *smb)
+{
+ return le16_to_cpu(smb->Mid);
+}
+
+static inline bool
+compare_mid(__u16 mid, const struct smb_hdr *smb)
+{
+ return mid == le16_to_cpu(smb->Mid);
+}
+
+/*
+ * When the server supports very large reads and writes via POSIX extensions,
+ * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
+ * including the RFC1001 length.
+ *
+ * Note that this might make for "interesting" allocation problems during
+ * writeback however as we have to allocate an array of pointers for the
+ * pages. A 16M write means ~32kb page array with PAGE_SIZE == 4096.
+ *
+ * For reads, there is a similar problem as we need to allocate an array
+ * of kvecs to handle the receive, though that should only need to be done
+ * once.
+ */
+#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
+
+/*
+ * When the server doesn't allow large posix writes, only allow a rsize/wsize
+ * of 2^17-1 minus the size of the call header. That allows for a read or
+ * write up to the maximum size described by RFC1002.
+ */
+#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
+
+/*
+ * The default wsize is 1M. find_get_pages seems to return a maximum of 256
+ * pages in a single call. With PAGE_SIZE == 4k, this means we can fill
+ * a single wsize request with a single call.
+ */
+#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+
+/*
+ * Windows only supports a max of 60kb reads and 65535 byte writes. Default to
+ * those values when posix extensions aren't in force. In actuality here, we
+ * use 65536 to allow for a write that is a multiple of 4k. Most servers seem
+ * to be ok with the extra byte even though Windows doesn't send writes that
+ * are that large.
+ *
+ * Citation:
+ *
+ * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx
+ */
+#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024)
+#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536)
+
+/*
+ * Macros to allow the TCP_Server_Info->net field and related code to drop out
+ * when CONFIG_NET_NS isn't set.
+ */
+
+#ifdef CONFIG_NET_NS
+
+static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv)
+{
+ return srv->net;
+}
+
+static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
+{
+ srv->net = net;
+}
+
+#else
+
+static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv)
+{
+ return &init_net;
+}
+
+static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
+{
+}
+
+#endif
+
+struct cifs_server_iface {
+ size_t speed;
+ unsigned int rdma_capable : 1;
+ unsigned int rss_capable : 1;
+ struct sockaddr_storage sockaddr;
+};
+
+/*
+ * Session structure. One of these for each uid session with a particular host
+ */
+struct cifs_ses {
+ struct list_head smb_ses_list;
+ struct list_head tcon_list;
+ struct cifs_tcon *tcon_ipc;
+ struct mutex session_mutex;
+ struct TCP_Server_Info *server; /* pointer to server info */
+ int ses_count; /* reference counter */
+ enum statusEnum status;
+ unsigned overrideSecFlg; /* if non-zero override global sec flags */
+ char *serverOS; /* name of operating system underlying server */
+ char *serverNOS; /* name of network operating system of server */
+ char *serverDomain; /* security realm of server */
+ __u64 Suid; /* remote smb uid */
+ kuid_t linux_uid; /* overriding owner of files on the mount */
+ kuid_t cred_uid; /* owner of credentials */
+ unsigned int capabilities;
+ char serverName[SERVER_NAME_LEN_WITH_NULL];
+ char *user_name; /* must not be null except during init of sess
+ and after mount option parsing we fill it */
+ char *domainName;
+ char *password;
+ struct session_key auth_key;
+ struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
+ enum securityEnum sectype; /* what security flavor was specified? */
+ bool sign; /* is signing required? */
+ bool need_reconnect:1; /* connection reset, uid now invalid */
+ bool domainAuto:1;
+ __u16 session_flags;
+ __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
+ __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
+ __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
+ __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
+
+ /*
+ * Network interfaces available on the server this session is
+ * connected to.
+ *
+ * Other channels can be opened by connecting and binding this
+ * session to interfaces from this list.
+ *
+ * iface_lock should be taken when accessing any of these fields
+ */
+ spinlock_t iface_lock;
+ struct cifs_server_iface *iface_list;
+ size_t iface_count;
+ unsigned long iface_last_update; /* jiffies */
+};
+
+static inline bool
+cap_unix(struct cifs_ses *ses)
+{
+ return ses->server->vals->cap_unix & ses->capabilities;
+}
+
+struct cached_fid {
+ bool is_valid:1; /* Do we have a useable root fid */
+ struct kref refcount;
+ struct cifs_fid *fid;
+ struct mutex fid_mutex;
+ struct cifs_tcon *tcon;
+ struct work_struct lease_break;
+};
+
+/*
+ * there is one of these for each connection to a resource on a particular
+ * session
+ */
+struct cifs_tcon {
+ struct list_head tcon_list;
+ int tc_count;
+ struct list_head rlist; /* reconnect list */
+ struct list_head openFileList;
+ spinlock_t open_file_lock; /* protects list above */
+ struct cifs_ses *ses; /* pointer to session associated with */
+ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
+ char *nativeFileSystem;
+ char *password; /* for share-level security */
+ __u32 tid; /* The 4 byte tree id */
+ __u16 Flags; /* optional support bits */
+ enum statusEnum tidStatus;
+ atomic_t num_smbs_sent;
+ union {
+ struct {
+ atomic_t num_writes;
+ atomic_t num_reads;
+ atomic_t num_flushes;
+ atomic_t num_oplock_brks;
+ atomic_t num_opens;
+ atomic_t num_closes;
+ atomic_t num_deletes;
+ atomic_t num_mkdirs;
+ atomic_t num_posixopens;
+ atomic_t num_posixmkdirs;
+ atomic_t num_rmdirs;
+ atomic_t num_renames;
+ atomic_t num_t2renames;
+ atomic_t num_ffirst;
+ atomic_t num_fnext;
+ atomic_t num_fclose;
+ atomic_t num_hardlinks;
+ atomic_t num_symlinks;
+ atomic_t num_locks;
+ atomic_t num_acl_get;
+ atomic_t num_acl_set;
+ } cifs_stats;
+ struct {
+ atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS];
+ atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS];
+ } smb2_stats;
+ } stats;
+ __u64 bytes_read;
+ __u64 bytes_written;
+ spinlock_t stat_lock; /* protects the two fields above */
+ FILE_SYSTEM_DEVICE_INFO fsDevInfo;
+ FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
+ FILE_SYSTEM_UNIX_INFO fsUnixInfo;
+ bool ipc:1; /* set if connection to IPC$ share (always also pipe) */
+ bool pipe:1; /* set if connection to pipe share */
+ bool print:1; /* set if connection to printer share */
+ bool retry:1;
+ bool nocase:1;
+ bool nohandlecache:1; /* if strange server resource prob can turn off */
+ bool seal:1; /* transport encryption for this mounted share */
+ bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol
+ for this mount even if server would support */
+ bool posix_extensions; /* if true SMB3.11 posix extensions enabled */
+ bool local_lease:1; /* check leases (only) on local system not remote */
+ bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */
+ bool broken_sparse_sup; /* if server or share does not support sparse */
+ bool need_reconnect:1; /* connection reset, tid now invalid */
+ bool need_reopen_files:1; /* need to reopen tcon file handles */
+ bool use_resilient:1; /* use resilient instead of durable handles */
+ bool use_persistent:1; /* use persistent instead of durable handles */
+ bool no_lease:1; /* Do not request leases on files or directories */
+ __le32 capabilities;
+ __u32 share_flags;
+ __u32 maximal_access;
+ __u32 vol_serial_number;
+ __le64 vol_create_time;
+ __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */
+ __u32 ss_flags; /* sector size flags */
+ __u32 perf_sector_size; /* best sector size for perf */
+ __u32 max_chunks;
+ __u32 max_bytes_chunk;
+ __u32 max_bytes_copy;
+#ifdef CONFIG_CIFS_FSCACHE
+ u64 resource_id; /* server resource id */
+ struct fscache_cookie *fscache; /* cookie for share */
+#endif
+ struct list_head pending_opens; /* list of incomplete opens */
+ struct cached_fid crfid; /* Cached root fid */
+ /* BB add field for back pointer to sb struct(s)? */
+};
+
+/*
+ * This is a refcounted and timestamped container for a tcon pointer. The
+ * container holds a tcon reference. It is considered safe to free one of
+ * these when the tl_count goes to 0. The tl_time is the time of the last
+ * "get" on the container.
+ */
+struct tcon_link {
+ struct rb_node tl_rbnode;
+ kuid_t tl_uid;
+ unsigned long tl_flags;
+#define TCON_LINK_MASTER 0
+#define TCON_LINK_PENDING 1
+#define TCON_LINK_IN_TREE 2
+ unsigned long tl_time;
+ atomic_t tl_count;
+ struct cifs_tcon *tl_tcon;
+};
+
+extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb);
+extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst);
+
+static inline struct cifs_tcon *
+tlink_tcon(struct tcon_link *tlink)
+{
+ return tlink->tl_tcon;
+}
+
+static inline struct tcon_link *
+cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
+{
+ return cifs_sb->master_tlink;
+}
+
+extern void cifs_put_tlink(struct tcon_link *tlink);
+
+static inline struct tcon_link *
+cifs_get_tlink(struct tcon_link *tlink)
+{
+ if (tlink && !IS_ERR(tlink))
+ atomic_inc(&tlink->tl_count);
+ return tlink;
+}
+
+/* This function is always expected to succeed */
+extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
+
+#define CIFS_OPLOCK_NO_CHANGE 0xfe
+
+struct cifs_pending_open {
+ struct list_head olist;
+ struct tcon_link *tlink;
+ __u8 lease_key[16];
+ __u32 oplock;
+};
+
+/*
+ * This info hangs off the cifsFileInfo structure, pointed to by llist.
+ * This is used to track byte stream locks on the file
+ */
+struct cifsLockInfo {
+ struct list_head llist; /* pointer to next cifsLockInfo */
+ struct list_head blist; /* pointer to locks blocked on this */
+ wait_queue_head_t block_q;
+ __u64 offset;
+ __u64 length;
+ __u32 pid;
+ __u32 type;
+};
+
+/*
+ * One of these for each open instance of a file
+ */
+struct cifs_search_info {
+ loff_t index_of_last_entry;
+ __u16 entries_in_buffer;
+ __u16 info_level;
+ __u32 resume_key;
+ char *ntwrk_buf_start;
+ char *srch_entries_start;
+ char *last_entry;
+ const char *presume_name;
+ unsigned int resume_name_len;
+ bool endOfSearch:1;
+ bool emptyDir:1;
+ bool unicode:1;
+ bool smallBuf:1; /* so we know which buf_release function to call */
+};
+
+struct cifs_open_parms {
+ struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb;
+ int disposition;
+ int desired_access;
+ int create_options;
+ const char *path;
+ struct cifs_fid *fid;
+ umode_t mode;
+ bool reconnect:1;
+};
+
+struct cifs_fid {
+ __u16 netfid;
+ __u64 persistent_fid; /* persist file id for smb2 */
+ __u64 volatile_fid; /* volatile file id for smb2 */
+ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
+ __u8 create_guid[16];
+ struct cifs_pending_open *pending_open;
+ unsigned int epoch;
+ bool purge_cache;
+};
+
+struct cifs_fid_locks {
+ struct list_head llist;
+ struct cifsFileInfo *cfile; /* fid that owns locks */
+ struct list_head locks; /* locks held by fid above */
+};
+
+struct cifsFileInfo {
+ /* following two lists are protected by tcon->open_file_lock */
+ struct list_head tlist; /* pointer to next fid owned by tcon */
+ struct list_head flist; /* next fid (file instance) for this inode */
+ /* lock list below protected by cifsi->lock_sem */
+ struct cifs_fid_locks *llist; /* brlocks held by this fid */
+ kuid_t uid; /* allows finding which FileInfo structure */
+ __u32 pid; /* process id who opened file */
+ struct cifs_fid fid; /* file id from remote */
+ struct list_head rlist; /* reconnect list */
+ /* BB add lock scope info here if needed */ ;
+ /* lock scope id (0 if none) */
+ struct dentry *dentry;
+ struct tcon_link *tlink;
+ unsigned int f_flags;
+ bool invalidHandle:1; /* file closed via session abend */
+ bool oplock_break_cancelled:1;
+ unsigned int oplock_epoch; /* epoch from the lease break */
+ __u32 oplock_level; /* oplock/lease level from the lease break */
+ int count;
+ spinlock_t file_info_lock; /* protects four flag/count fields above */
+ struct mutex fh_mutex; /* prevents reopen race after dead ses*/
+ struct cifs_search_info srch_inf;
+ struct work_struct oplock_break; /* work for oplock breaks */
+};
+
+struct cifs_io_parms {
+ __u16 netfid;
+ __u64 persistent_fid; /* persist file id for smb2 */
+ __u64 volatile_fid; /* volatile file id for smb2 */
+ __u32 pid;
+ __u64 offset;
+ unsigned int length;
+ struct cifs_tcon *tcon;
+};
+
+struct cifs_aio_ctx {
+ struct kref refcount;
+ struct list_head list;
+ struct mutex aio_mutex;
+ struct completion done;
+ struct iov_iter iter;
+ struct kiocb *iocb;
+ struct cifsFileInfo *cfile;
+ struct bio_vec *bv;
+ loff_t pos;
+ unsigned int npages;
+ ssize_t rc;
+ unsigned int len;
+ unsigned int total_len;
+ bool should_dirty;
+};
+
+struct cifs_readdata;
+
+/* asynchronous read support */
+struct cifs_readdata {
+ struct kref refcount;
+ struct list_head list;
+ struct completion done;
+ struct cifsFileInfo *cfile;
+ struct address_space *mapping;
+ struct cifs_aio_ctx *ctx;
+ __u64 offset;
+ unsigned int bytes;
+ unsigned int got_bytes;
+ pid_t pid;
+ int result;
+ struct work_struct work;
+ int (*read_into_pages)(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ unsigned int len);
+ int (*copy_into_pages)(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ struct iov_iter *iter);
+ struct kvec iov[2];
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ struct smbd_mr *mr;
+#endif
+ unsigned int pagesz;
+ unsigned int page_offset;
+ unsigned int tailsz;
+ unsigned int credits;
+ unsigned int nr_pages;
+ struct page **pages;
+};
+
+struct cifs_writedata;
+
+/* asynchronous write support */
+struct cifs_writedata {
+ struct kref refcount;
+ struct list_head list;
+ struct completion done;
+ enum writeback_sync_modes sync_mode;
+ struct work_struct work;
+ struct cifsFileInfo *cfile;
+ struct cifs_aio_ctx *ctx;
+ __u64 offset;
+ pid_t pid;
+ unsigned int bytes;
+ int result;
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ struct smbd_mr *mr;
+#endif
+ unsigned int pagesz;
+ unsigned int page_offset;
+ unsigned int tailsz;
+ unsigned int credits;
+ unsigned int nr_pages;
+ struct page **pages;
+};
+
+/*
+ * Take a reference on the file private data. Must be called with
+ * cfile->file_info_lock held.
+ */
+static inline void
+cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
+{
+ ++cifs_file->count;
+}
+
+struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
+void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr);
+void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
+
+#define CIFS_CACHE_READ_FLG 1
+#define CIFS_CACHE_HANDLE_FLG 2
+#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG)
+#define CIFS_CACHE_WRITE_FLG 4
+#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
+#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)
+
+#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG)
+#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
+#define CIFS_CACHE_WRITE(cinode) (cinode->oplock & CIFS_CACHE_WRITE_FLG)
+
+/*
+ * One of these for each file inode
+ */
+
+struct cifsInodeInfo {
+ bool can_cache_brlcks;
+ struct list_head llist; /* locks helb by this inode */
+ /*
+ * NOTE: Some code paths call down_read(lock_sem) twice, so
+ * we must always use use cifs_down_write() instead of down_write()
+ * for this semaphore to avoid deadlocks.
+ */
+ struct rw_semaphore lock_sem; /* protect the fields above */
+ /* BB add in lists for dirty pages i.e. write caching info for oplock */
+ struct list_head openFileList;
+ spinlock_t open_file_lock; /* protects openFileList */
+ __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
+ unsigned int oplock; /* oplock/lease level we have */
+ unsigned int epoch; /* used to track lease state changes */
+#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
+#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
+#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */
+#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
+#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
+#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
+ unsigned long flags;
+ spinlock_t writers_lock;
+ unsigned int writers; /* Number of writers on this inode */
+ unsigned long time; /* jiffies of last update of inode */
+ u64 server_eof; /* current file size on server -- protected by i_lock */
+ u64 uniqueid; /* server inode number */
+ u64 createtime; /* creation time on server */
+ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
+#ifdef CONFIG_CIFS_FSCACHE
+ struct fscache_cookie *fscache;
+#endif
+ struct inode vfs_inode;
+};
+
+static inline struct cifsInodeInfo *
+CIFS_I(struct inode *inode)
+{
+ return container_of(inode, struct cifsInodeInfo, vfs_inode);
+}
+
+static inline struct cifs_sb_info *
+CIFS_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+static inline struct cifs_sb_info *
+CIFS_FILE_SB(struct file *file)
+{
+ return CIFS_SB(file_inode(file)->i_sb);
+}
+
+static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
+{
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
+ return '/';
+ else
+ return '\\';
+}
+
+static inline void
+convert_delimiter(char *path, char delim)
+{
+ char old_delim, *pos;
+
+ if (delim == '/')
+ old_delim = '\\';
+ else
+ old_delim = '/';
+
+ pos = path;
+ while ((pos = strchr(pos, old_delim)))
+ *pos = delim;
+}
+
+#define cifs_stats_inc atomic_inc
+
+static inline void cifs_stats_bytes_written(struct cifs_tcon *tcon,
+ unsigned int bytes)
+{
+ if (bytes) {
+ spin_lock(&tcon->stat_lock);
+ tcon->bytes_written += bytes;
+ spin_unlock(&tcon->stat_lock);
+ }
+}
+
+static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,
+ unsigned int bytes)
+{
+ spin_lock(&tcon->stat_lock);
+ tcon->bytes_read += bytes;
+ spin_unlock(&tcon->stat_lock);
+}
+
+
+/*
+ * This is the prototype for the mid receive function. This function is for
+ * receiving the rest of the SMB frame, starting with the WordCount (which is
+ * just after the MID in struct smb_hdr). Note:
+ *
+ * - This will be called by cifsd, with no locks held.
+ * - The mid will still be on the pending_mid_q.
+ * - mid->resp_buf will point to the current buffer.
+ *
+ * Returns zero on a successful receive, or an error. The receive state in
+ * the TCP_Server_Info will also be updated.
+ */
+typedef int (mid_receive_t)(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid);
+
+/*
+ * This is the prototype for the mid callback function. This is called once the
+ * mid has been received off of the socket. When creating one, take special
+ * care to avoid deadlocks. Things to bear in mind:
+ *
+ * - it will be called by cifsd, with no locks held
+ * - the mid will be removed from any lists
+ */
+typedef void (mid_callback_t)(struct mid_q_entry *mid);
+
+/*
+ * This is the protopyte for mid handle function. This is called once the mid
+ * has been recognized after decryption of the message.
+ */
+typedef int (mid_handle_t)(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid);
+
+/* one of these for every pending CIFS request to the server */
+struct mid_q_entry {
+ struct list_head qhead; /* mids waiting on reply from this server */
+ struct kref refcount;
+ struct TCP_Server_Info *server; /* server corresponding to this mid */
+ __u64 mid; /* multiplex id */
+ __u16 credits; /* number of credits consumed by this mid */
+ __u32 pid; /* process id */
+ __u32 sequence_number; /* for CIFS signing */
+ unsigned long when_alloc; /* when mid was created */
+#ifdef CONFIG_CIFS_STATS2
+ unsigned long when_sent; /* time when smb send finished */
+ unsigned long when_received; /* when demux complete (taken off wire) */
+#endif
+ mid_receive_t *receive; /* call receive callback */
+ mid_callback_t *callback; /* call completion callback */
+ mid_handle_t *handle; /* call handle mid callback */
+ void *callback_data; /* general purpose pointer for callback */
+ void *resp_buf; /* pointer to received SMB header */
+ unsigned int resp_buf_size;
+ int mid_state; /* wish this were enum but can not pass to wait_event */
+ unsigned int mid_flags;
+ __le16 command; /* smb command code */
+ bool large_buf:1; /* if valid response, is pointer to large buf */
+ bool multiRsp:1; /* multiple trans2 responses for one request */
+ bool multiEnd:1; /* both received */
+ bool decrypted:1; /* decrypted entry */
+};
+
+struct close_cancelled_open {
+ struct cifs_fid fid;
+ struct cifs_tcon *tcon;
+ struct work_struct work;
+};
+
+/* Make code in transport.c a little cleaner by moving
+ update of optional stats into function below */
+#ifdef CONFIG_CIFS_STATS2
+
+static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
+{
+ atomic_inc(&server->in_send);
+}
+
+static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
+{
+ atomic_dec(&server->in_send);
+}
+
+static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
+{
+ atomic_inc(&server->num_waiters);
+}
+
+static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
+{
+ atomic_dec(&server->num_waiters);
+}
+
+static inline void cifs_save_when_sent(struct mid_q_entry *mid)
+{
+ mid->when_sent = jiffies;
+}
+#else
+static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
+{
+}
+static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
+{
+}
+
+static inline void cifs_save_when_sent(struct mid_q_entry *mid)
+{
+}
+#endif
+
+/* for pending dnotify requests */
+struct dir_notify_req {
+ struct list_head lhead;
+ __le16 Pid;
+ __le16 PidHigh;
+ __u16 Mid;
+ __u16 Tid;
+ __u16 Uid;
+ __u16 netfid;
+ __u32 filter; /* CompletionFilter (for multishot) */
+ int multishot;
+ struct file *pfile;
+};
+
+struct dfs_info3_param {
+ int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/
+ int path_consumed;
+ int server_type;
+ int ref_flag;
+ char *path_name;
+ char *node_name;
+};
+
+/*
+ * common struct for holding inode info when searching for or updating an
+ * inode with new info
+ */
+
+#define CIFS_FATTR_DFS_REFERRAL 0x1
+#define CIFS_FATTR_DELETE_PENDING 0x2
+#define CIFS_FATTR_NEED_REVAL 0x4
+#define CIFS_FATTR_INO_COLLISION 0x8
+#define CIFS_FATTR_UNKNOWN_NLINK 0x10
+#define CIFS_FATTR_FAKE_ROOT_INO 0x20
+
+struct cifs_fattr {
+ u32 cf_flags;
+ u32 cf_cifsattrs;
+ u64 cf_uniqueid;
+ u64 cf_eof;
+ u64 cf_bytes;
+ u64 cf_createtime;
+ kuid_t cf_uid;
+ kgid_t cf_gid;
+ umode_t cf_mode;
+ dev_t cf_rdev;
+ unsigned int cf_nlink;
+ unsigned int cf_dtype;
+ struct timespec64 cf_atime;
+ struct timespec64 cf_mtime;
+ struct timespec64 cf_ctime;
+};
+
+static inline void free_dfs_info_param(struct dfs_info3_param *param)
+{
+ if (param) {
+ kfree(param->path_name);
+ kfree(param->node_name);
+ kfree(param);
+ }
+}
+
+static inline void free_dfs_info_array(struct dfs_info3_param *param,
+ int number_of_items)
+{
+ int i;
+ if ((number_of_items == 0) || (param == NULL))
+ return;
+ for (i = 0; i < number_of_items; i++) {
+ kfree(param[i].path_name);
+ kfree(param[i].node_name);
+ }
+ kfree(param);
+}
+
+static inline bool is_interrupt_error(int error)
+{
+ switch (error) {
+ case -EINTR:
+ case -ERESTARTSYS:
+ case -ERESTARTNOHAND:
+ case -ERESTARTNOINTR:
+ return true;
+ }
+ return false;
+}
+
+static inline bool is_retryable_error(int error)
+{
+ if (is_interrupt_error(error) || error == -EAGAIN)
+ return true;
+ return false;
+}
+
+#define MID_FREE 0
+#define MID_REQUEST_ALLOCATED 1
+#define MID_REQUEST_SUBMITTED 2
+#define MID_RESPONSE_RECEIVED 4
+#define MID_RETRY_NEEDED 8 /* session closed while this request out */
+#define MID_RESPONSE_MALFORMED 0x10
+#define MID_SHUTDOWN 0x20
+
+/* Flags */
+#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
+#define MID_DELETED 2 /* Mid has been dequeued/deleted */
+
+/* Types of response buffer returned from SendReceive2 */
+#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
+#define CIFS_SMALL_BUFFER 1
+#define CIFS_LARGE_BUFFER 2
+#define CIFS_IOVEC 4 /* array of response buffers */
+
+/* Type of Request to SendReceive2 */
+#define CIFS_BLOCKING_OP 1 /* operation can block */
+#define CIFS_ASYNC_OP 2 /* do not wait for response */
+#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */
+#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */
+#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */
+#define CIFS_NO_RESP 0x040 /* no response buffer required */
+
+/* Type of request operation */
+#define CIFS_ECHO_OP 0x080 /* echo request */
+#define CIFS_OBREAK_OP 0x0100 /* oplock break request */
+#define CIFS_NEG_OP 0x0200 /* negotiate request */
+#define CIFS_OP_MASK 0x0380 /* mask request type */
+
+#define CIFS_HAS_CREDITS 0x0400 /* already has credits */
+#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
+
+/* Security Flags: indicate type of session setup needed */
+#define CIFSSEC_MAY_SIGN 0x00001
+#define CIFSSEC_MAY_NTLM 0x00002
+#define CIFSSEC_MAY_NTLMV2 0x00004
+#define CIFSSEC_MAY_KRB5 0x00008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFSSEC_MAY_LANMAN 0x00010
+#define CIFSSEC_MAY_PLNTXT 0x00020
+#else
+#define CIFSSEC_MAY_LANMAN 0
+#define CIFSSEC_MAY_PLNTXT 0
+#endif /* weak passwords */
+#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */
+#define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */
+
+#define CIFSSEC_MUST_SIGN 0x01001
+/* note that only one of the following can be set so the
+result of setting MUST flags more than once will be to
+require use of the stronger protocol */
+#define CIFSSEC_MUST_NTLM 0x02002
+#define CIFSSEC_MUST_NTLMV2 0x04004
+#define CIFSSEC_MUST_KRB5 0x08008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFSSEC_MUST_LANMAN 0x10010
+#define CIFSSEC_MUST_PLNTXT 0x20020
+#ifdef CONFIG_CIFS_UPCALL
+#define CIFSSEC_MASK 0xBF0BF /* allows weak security but also krb5 */
+#else
+#define CIFSSEC_MASK 0xB70B7 /* current flags supported if weak */
+#endif /* UPCALL */
+#else /* do not allow weak pw hash */
+#define CIFSSEC_MUST_LANMAN 0
+#define CIFSSEC_MUST_PLNTXT 0
+#ifdef CONFIG_CIFS_UPCALL
+#define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */
+#else
+#define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */
+#endif /* UPCALL */
+#endif /* WEAK_PW_HASH */
+#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
+#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */
+
+#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP)
+#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)
+#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)
+/*
+ *****************************************************************
+ * All constants go here
+ *****************************************************************
+ */
+
+#define UID_HASH (16)
+
+/*
+ * Note that ONE module should define _DECLARE_GLOBALS_HERE to cause the
+ * following to be declared.
+ */
+
+/****************************************************************************
+ * Locking notes. All updates to global variables and lists should be
+ * protected by spinlocks or semaphores.
+ *
+ * Spinlocks
+ * ---------
+ * GlobalMid_Lock protects:
+ * list operations on pending_mid_q and oplockQ
+ * updates to XID counters, multiplex id and SMB sequence numbers
+ * tcp_ses_lock protects:
+ * list operations on tcp and SMB session lists
+ * tcon->open_file_lock protects the list of open files hanging off the tcon
+ * inode->open_file_lock protects the openFileList hanging off the inode
+ * cfile->file_info_lock protects counters and fields in cifs file struct
+ * f_owner.lock protects certain per file struct operations
+ * mapping->page_lock protects certain per page operations
+ *
+ * Note that the cifs_tcon.open_file_lock should be taken before
+ * not after the cifsInodeInfo.open_file_lock
+ *
+ * Semaphores
+ * ----------
+ * sesSem operations on smb session
+ * tconSem operations on tree connection
+ * fh_sem file handle reconnection operations
+ *
+ ****************************************************************************/
+
+#ifdef DECLARE_GLOBALS_HERE
+#define GLOBAL_EXTERN
+#else
+#define GLOBAL_EXTERN extern
+#endif
+
+/*
+ * the list of TCP_Server_Info structures, ie each of the sockets
+ * connecting our client to a distinct server (ip address), is
+ * chained together by cifs_tcp_ses_list. The list of all our SMB
+ * sessions (and from that the tree connections) can be found
+ * by iterating over cifs_tcp_ses_list
+ */
+GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
+
+/*
+ * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
+ * tcp session, and the list of tcon's per smb session. It also protects
+ * the reference counters for the server, smb session, and tcon. Finally,
+ * changes to the tcon->tidStatus should be done while holding this lock.
+ * generally the locks should be taken in order tcp_ses_lock before
+ * tcon->open_file_lock and that before file->file_info_lock since the
+ * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
+ */
+GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
+
+#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
+/* Outstanding dir notify requests */
+GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
+/* DirNotify response queue */
+GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q;
+#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */
+
+/*
+ * Global transaction id (XID) information
+ */
+GLOBAL_EXTERN unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */
+GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */
+GLOBAL_EXTERN unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */
+GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above & list operations */
+ /* on midQ entries */
+/*
+ * Global counters, updated atomically
+ */
+GLOBAL_EXTERN atomic_t sesInfoAllocCount;
+GLOBAL_EXTERN atomic_t tconInfoAllocCount;
+GLOBAL_EXTERN atomic_t tcpSesAllocCount;
+GLOBAL_EXTERN atomic_t tcpSesReconnectCount;
+GLOBAL_EXTERN atomic_t tconInfoReconnectCount;
+
+/* Various Debug counters */
+GLOBAL_EXTERN atomic_t bufAllocCount; /* current number allocated */
+#ifdef CONFIG_CIFS_STATS2
+GLOBAL_EXTERN atomic_t totBufAllocCount; /* total allocated over all time */
+GLOBAL_EXTERN atomic_t totSmBufAllocCount;
+#endif
+GLOBAL_EXTERN atomic_t smBufAllocCount;
+GLOBAL_EXTERN atomic_t midCount;
+
+/* Misc globals */
+extern bool enable_oplocks; /* enable or disable oplocks */
+extern bool lookupCacheEnabled;
+extern unsigned int global_secflags; /* if on, session setup sent
+ with more secure ntlmssp2 challenge/resp */
+extern unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
+extern bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
+extern unsigned int CIFSMaxBufSize; /* max size not including hdr */
+extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
+extern unsigned int cifs_min_small; /* min size of small buf pool */
+extern unsigned int cifs_max_pending; /* MAX requests at once to server*/
+extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */
+
+#ifdef CONFIG_CIFS_ACL
+GLOBAL_EXTERN struct rb_root uidtree;
+GLOBAL_EXTERN struct rb_root gidtree;
+GLOBAL_EXTERN spinlock_t siduidlock;
+GLOBAL_EXTERN spinlock_t sidgidlock;
+GLOBAL_EXTERN struct rb_root siduidtree;
+GLOBAL_EXTERN struct rb_root sidgidtree;
+GLOBAL_EXTERN spinlock_t uidsidlock;
+GLOBAL_EXTERN spinlock_t gidsidlock;
+#endif /* CONFIG_CIFS_ACL */
+
+void cifs_oplock_break(struct work_struct *work);
+void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
+
+extern const struct slow_work_ops cifs_oplock_break_ops;
+extern struct workqueue_struct *cifsiod_wq;
+extern struct workqueue_struct *cifsoplockd_wq;
+extern __u32 cifs_lock_secret;
+
+extern mempool_t *cifs_mid_poolp;
+
+/* Operations for different SMB versions */
+#define SMB1_VERSION_STRING "1.0"
+extern struct smb_version_operations smb1_operations;
+extern struct smb_version_values smb1_values;
+#define SMB20_VERSION_STRING "2.0"
+extern struct smb_version_operations smb20_operations;
+extern struct smb_version_values smb20_values;
+#define SMB21_VERSION_STRING "2.1"
+extern struct smb_version_operations smb21_operations;
+extern struct smb_version_values smb21_values;
+#define SMBDEFAULT_VERSION_STRING "default"
+extern struct smb_version_values smbdefault_values;
+#define SMB3ANY_VERSION_STRING "3"
+extern struct smb_version_values smb3any_values;
+#define SMB30_VERSION_STRING "3.0"
+extern struct smb_version_operations smb30_operations;
+extern struct smb_version_values smb30_values;
+#define SMB302_VERSION_STRING "3.02"
+/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */
+extern struct smb_version_values smb302_values;
+#define SMB311_VERSION_STRING "3.1.1"
+#define ALT_SMB311_VERSION_STRING "3.11"
+extern struct smb_version_operations smb311_operations;
+extern struct smb_version_values smb311_values;
+#endif /* _CIFS_GLOB_H */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
new file mode 100644
index 000000000..1ce733f35
--- /dev/null
+++ b/fs/cifs/cifspdu.h
@@ -0,0 +1,2769 @@
+/*
+ * fs/cifs/cifspdu.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2009
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSPDU_H
+#define _CIFSPDU_H
+
+#include <net/sock.h>
+#include <asm/unaligned.h>
+#include "smbfsctl.h"
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define LANMAN_PROT 0
+#define LANMAN2_PROT 1
+#define CIFS_PROT 2
+#else
+#define CIFS_PROT 0
+#endif
+#define POSIX_PROT (CIFS_PROT+1)
+#define BAD_PROT 0xFFFF
+
+/* SMB command codes:
+ * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses
+ * (ie which include no useful data other than the SMB error code itself).
+ * This can allow us to avoid response buffer allocations and copy in some cases
+ */
+#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */
+#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */
+#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */
+#define SMB_COM_FLUSH 0x05 /* triv req/rsp */
+#define SMB_COM_DELETE 0x06 /* trivial response */
+#define SMB_COM_RENAME 0x07 /* trivial response */
+#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */
+#define SMB_COM_SETATTR 0x09 /* trivial response */
+#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */
+#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/
+#define SMB_COM_ECHO 0x2B /* echo request */
+#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */
+#define SMB_COM_READ_ANDX 0x2E
+#define SMB_COM_WRITE_ANDX 0x2F
+#define SMB_COM_TRANSACTION2 0x32
+#define SMB_COM_TRANSACTION2_SECONDARY 0x33
+#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */
+#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */
+#define SMB_COM_NEGOTIATE 0x72
+#define SMB_COM_SESSION_SETUP_ANDX 0x73
+#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */
+#define SMB_COM_TREE_CONNECT_ANDX 0x75
+#define SMB_COM_NT_TRANSACT 0xA0
+#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1
+#define SMB_COM_NT_CREATE_ANDX 0xA2
+#define SMB_COM_NT_CANCEL 0xA4 /* no response */
+#define SMB_COM_NT_RENAME 0xA5 /* trivial response */
+
+/* Transact2 subcommand codes */
+#define TRANS2_OPEN 0x00
+#define TRANS2_FIND_FIRST 0x01
+#define TRANS2_FIND_NEXT 0x02
+#define TRANS2_QUERY_FS_INFORMATION 0x03
+#define TRANS2_SET_FS_INFORMATION 0x04
+#define TRANS2_QUERY_PATH_INFORMATION 0x05
+#define TRANS2_SET_PATH_INFORMATION 0x06
+#define TRANS2_QUERY_FILE_INFORMATION 0x07
+#define TRANS2_SET_FILE_INFORMATION 0x08
+#define TRANS2_GET_DFS_REFERRAL 0x10
+#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11
+
+/* SMB Transact (Named Pipe) subcommand codes */
+#define TRANS_SET_NMPIPE_STATE 0x0001
+#define TRANS_RAW_READ_NMPIPE 0x0011
+#define TRANS_QUERY_NMPIPE_STATE 0x0021
+#define TRANS_QUERY_NMPIPE_INFO 0x0022
+#define TRANS_PEEK_NMPIPE 0x0023
+#define TRANS_TRANSACT_NMPIPE 0x0026
+#define TRANS_RAW_WRITE_NMPIPE 0x0031
+#define TRANS_READ_NMPIPE 0x0036
+#define TRANS_WRITE_NMPIPE 0x0037
+#define TRANS_WAIT_NMPIPE 0x0053
+#define TRANS_CALL_NMPIPE 0x0054
+
+/* NT Transact subcommand codes */
+#define NT_TRANSACT_CREATE 0x01
+#define NT_TRANSACT_IOCTL 0x02
+#define NT_TRANSACT_SET_SECURITY_DESC 0x03
+#define NT_TRANSACT_NOTIFY_CHANGE 0x04
+#define NT_TRANSACT_RENAME 0x05
+#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06
+#define NT_TRANSACT_GET_USER_QUOTA 0x07
+#define NT_TRANSACT_SET_USER_QUOTA 0x08
+
+#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
+/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */
+/* among the requests (NTCreateX response is bigger with wct of 34) */
+#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */
+#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */
+
+/* internal cifs vfs structures */
+/*****************************************************************
+ * All constants go here
+ *****************************************************************
+ */
+
+/*
+ * Starting value for maximum SMB size negotiation
+ */
+#define CIFS_MAX_MSGSIZE (4*4096)
+
+/*
+ * Size of encrypted user password in bytes
+ */
+#define CIFS_ENCPWD_SIZE (16)
+
+/*
+ * Size of the crypto key returned on the negotiate SMB in bytes
+ */
+#define CIFS_CRYPTO_KEY_SIZE (8)
+
+/*
+ * Size of the ntlm client response
+ */
+#define CIFS_AUTH_RESP_SIZE (24)
+
+/*
+ * Size of the session key (crypto key encrypted with the password
+ */
+#define CIFS_SESS_KEY_SIZE (16)
+
+/*
+ * Size of the smb3 signing key
+ */
+#define SMB3_SIGN_KEY_SIZE (16)
+
+#define CIFS_CLIENT_CHALLENGE_SIZE (8)
+#define CIFS_SERVER_CHALLENGE_SIZE (8)
+#define CIFS_HMAC_MD5_HASH_SIZE (16)
+#define CIFS_CPHTXT_SIZE (16)
+#define CIFS_NTHASH_SIZE (16)
+
+/*
+ * Maximum user name length
+ */
+#define CIFS_UNLEN (20)
+
+/*
+ * Flags on SMB open
+ */
+#define SMBOPEN_WRITE_THROUGH 0x4000
+#define SMBOPEN_DENY_ALL 0x0010
+#define SMBOPEN_DENY_WRITE 0x0020
+#define SMBOPEN_DENY_READ 0x0030
+#define SMBOPEN_DENY_NONE 0x0040
+#define SMBOPEN_READ 0x0000
+#define SMBOPEN_WRITE 0x0001
+#define SMBOPEN_READWRITE 0x0002
+#define SMBOPEN_EXECUTE 0x0003
+
+#define SMBOPEN_OCREATE 0x0010
+#define SMBOPEN_OTRUNC 0x0002
+#define SMBOPEN_OAPPEND 0x0001
+
+/*
+ * SMB flag definitions
+ */
+#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */
+#define SMBFLG_RCV_POSTED 0x02 /* obsolete */
+#define SMBFLG_RSVD 0x04
+#define SMBFLG_CASELESS 0x08 /* all pathnames treated as caseless (off
+ implies case sensitive file handling request) */
+#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */
+#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */
+#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */
+#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */
+
+/*
+ * SMB flag2 definitions
+ */
+#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3)
+ path names in response */
+#define SMBFLG2_KNOWS_EAS cpu_to_le16(2)
+#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4)
+#define SMBFLG2_COMPRESSED (8)
+#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10)
+#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40)
+#define SMBFLG2_REPARSE_PATH (0x400)
+#define SMBFLG2_EXT_SEC cpu_to_le16(0x800)
+#define SMBFLG2_DFS cpu_to_le16(0x1000)
+#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000)
+#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000)
+#define SMBFLG2_UNICODE cpu_to_le16(0x8000)
+
+/*
+ * These are the file access permission bits defined in CIFS for the
+ * NTCreateAndX as well as the level 0x107
+ * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO
+ * responds with the AccessFlags.
+ * The AccessFlags specifies the access permissions a caller has to the
+ * file and can have any suitable combination of the following values:
+ */
+
+#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. */
+
+#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
+#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
+
+#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \
+ | FILE_READ_ATTRIBUTES \
+ | FILE_WRITE_ATTRIBUTES \
+ | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER | SYNCHRONIZE)
+#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | FILE_READ_EA | FILE_WRITE_EA \
+ | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES \
+ | 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)
+
+
+/*
+ * Invalid readdir handle
+ */
+#define CIFS_NO_HANDLE 0xFFFF
+
+#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL
+
+/* IPC$ in ASCII */
+#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24"
+
+/* IPC$ in Unicode */
+#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00"
+
+/* Unicode Null terminate 2 bytes of 0 */
+#define UNICODE_NULL "\x00\x00"
+#define ASCII_NULL 0x00
+
+/*
+ * Server type values (returned on EnumServer API
+ */
+#define CIFS_SV_TYPE_DC 0x00000008
+#define CIFS_SV_TYPE_BACKDC 0x00000010
+
+/*
+ * Alias type flags (From EnumAlias API call
+ */
+#define CIFS_ALIAS_TYPE_FILE 0x0001
+#define CIFS_SHARE_TYPE_FILE 0x0000
+
+/*
+ * File Attribute flags
+ */
+#define ATTR_READONLY 0x0001
+#define ATTR_HIDDEN 0x0002
+#define ATTR_SYSTEM 0x0004
+#define ATTR_VOLUME 0x0008
+#define ATTR_DIRECTORY 0x0010
+#define ATTR_ARCHIVE 0x0020
+#define ATTR_DEVICE 0x0040
+#define ATTR_NORMAL 0x0080
+#define ATTR_TEMPORARY 0x0100
+#define ATTR_SPARSE 0x0200
+#define ATTR_REPARSE 0x0400
+#define ATTR_COMPRESSED 0x0800
+#define ATTR_OFFLINE 0x1000 /* ie file not immediately available -
+ on offline storage */
+#define ATTR_NOT_CONTENT_INDEXED 0x2000
+#define ATTR_ENCRYPTED 0x4000
+#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
+
+/* ShareAccess flags */
+#define FILE_NO_SHARE 0x00000000
+#define FILE_SHARE_READ 0x00000001
+#define FILE_SHARE_WRITE 0x00000002
+#define FILE_SHARE_DELETE 0x00000004
+#define FILE_SHARE_ALL 0x00000007
+
+/* CreateDisposition flags, similar to CreateAction as well */
+#define FILE_SUPERSEDE 0x00000000
+#define FILE_OPEN 0x00000001
+#define FILE_CREATE 0x00000002
+#define FILE_OPEN_IF 0x00000003
+#define FILE_OVERWRITE 0x00000004
+#define FILE_OVERWRITE_IF 0x00000005
+
+/* CreateOptions */
+#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */
+#define CREATE_WRITE_THROUGH 0x00000002
+#define CREATE_SEQUENTIAL 0x00000004
+#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */
+#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */
+#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */
+#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */
+#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */
+#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */
+#define CREATE_NO_EA_KNOWLEDGE 0x00000200
+#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete
+ "open for recovery" flag should
+ be zero in any case */
+#define CREATE_OPEN_FOR_RECOVERY 0x00000400
+#define CREATE_RANDOM_ACCESS 0x00000800
+#define CREATE_DELETE_ON_CLOSE 0x00001000
+#define CREATE_OPEN_BY_ID 0x00002000
+#define CREATE_OPEN_BACKUP_INTENT 0x00004000
+#define CREATE_NO_COMPRESSION 0x00008000
+#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */
+#define OPEN_REPARSE_POINT 0x00200000
+#define OPEN_NO_RECALL 0x00400000
+#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */
+#define CREATE_OPTIONS_MASK 0x007FFFFF
+#define CREATE_OPTION_READONLY 0x10000000
+#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */
+
+/* ImpersonationLevel flags */
+#define SECURITY_ANONYMOUS 0
+#define SECURITY_IDENTIFICATION 1
+#define SECURITY_IMPERSONATION 2
+#define SECURITY_DELEGATION 3
+
+/* SecurityFlags */
+#define SECURITY_CONTEXT_TRACKING 0x01
+#define SECURITY_EFFECTIVE_ONLY 0x02
+
+/*
+ * Default PID value, used in all SMBs where the PID is not important
+ */
+#define CIFS_DFT_PID 0x1234
+
+/*
+ * We use the same routine for Copy and Move SMBs. This flag is used to
+ * distinguish
+ */
+#define CIFS_COPY_OP 1
+#define CIFS_RENAME_OP 2
+
+#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */
+#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */
+
+struct smb_hdr {
+ __be32 smb_buf_length; /* BB length is only two (rarely three) bytes,
+ with one or two byte "type" preceding it that will be
+ zero - we could mask the type byte off */
+ __u8 Protocol[4];
+ __u8 Command;
+ union {
+ struct {
+ __u8 ErrorClass;
+ __u8 Reserved;
+ __le16 Error;
+ } __attribute__((packed)) DosError;
+ __le32 CifsError;
+ } __attribute__((packed)) Status;
+ __u8 Flags;
+ __le16 Flags2; /* note: le */
+ __le16 PidHigh;
+ union {
+ struct {
+ __le32 SequenceNumber; /* le */
+ __u32 Reserved; /* zero */
+ } __attribute__((packed)) Sequence;
+ __u8 SecuritySignature[8]; /* le */
+ } __attribute__((packed)) Signature;
+ __u8 pad[2];
+ __u16 Tid;
+ __le16 Pid;
+ __u16 Uid;
+ __le16 Mid;
+ __u8 WordCount;
+} __attribute__((packed));
+
+/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */
+static inline void *
+BCC(struct smb_hdr *smb)
+{
+ return (void *)smb + sizeof(*smb) + 2 * smb->WordCount;
+}
+
+/* given a pointer to an smb_hdr retrieve the pointer to the byte area */
+#define pByteArea(smb_var) (BCC(smb_var) + 2)
+
+/* get the unconverted ByteCount for a SMB packet and return it */
+static inline __u16
+get_bcc(struct smb_hdr *hdr)
+{
+ __le16 *bc_ptr = (__le16 *)BCC(hdr);
+
+ return get_unaligned_le16(bc_ptr);
+}
+
+/* set the ByteCount for a SMB packet in little-endian */
+static inline void
+put_bcc(__u16 count, struct smb_hdr *hdr)
+{
+ __le16 *bc_ptr = (__le16 *)BCC(hdr);
+
+ put_unaligned_le16(count, bc_ptr);
+}
+
+/*
+ * Computer Name Length (since Netbios name was length 16 with last byte 0x20)
+ * No longer as important, now that TCP names are more commonly used to
+ * resolve hosts.
+ */
+#define CNLEN 15
+
+/*
+ * Share Name Length (SNLEN)
+ * Note: This length was limited by the SMB used to get
+ * the Share info. NetShareEnum only returned 13
+ * chars, including the null termination.
+ * This was removed because it no longer is limiting.
+ */
+
+/*
+ * Comment Length
+ */
+#define MAXCOMMENTLEN 40
+
+/*
+ * The OS/2 maximum path name
+ */
+#define MAX_PATHCONF 256
+
+/*
+ * SMB frame definitions (following must be packed structs)
+ * See the SNIA CIFS Specification for details.
+ *
+ * The Naming convention is the lower case version of the
+ * smb command code name for the struct and this is typedef to the
+ * uppercase version of the same name with the prefix SMB_ removed
+ * for brevity. Although typedefs are not commonly used for
+ * structure definitions in the Linux kernel, their use in the
+ * CIFS standards document, which this code is based on, may
+ * make this one of the cases where typedefs for structures make
+ * sense to improve readability for readers of the standards doc.
+ * Typedefs can always be removed later if they are too distracting
+ * and they are only used for the CIFSs PDUs themselves, not
+ * internal cifs vfs structures
+ *
+ */
+
+typedef struct negotiate_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ unsigned char DialectsArray[1];
+} __attribute__((packed)) NEGOTIATE_REQ;
+
+/* Dialect index is 13 for LANMAN */
+
+#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */
+
+typedef struct lanman_neg_rsp {
+ struct smb_hdr hdr; /* wct = 13 */
+ __le16 DialectIndex;
+ __le16 SecurityMode;
+ __le16 MaxBufSize;
+ __le16 MaxMpxCount;
+ __le16 MaxNumberVcs;
+ __le16 RawMode;
+ __le32 SessionKey;
+ struct {
+ __le16 Time;
+ __le16 Date;
+ } __attribute__((packed)) SrvTime;
+ __le16 ServerTimeZone;
+ __le16 EncryptionKeyLength;
+ __le16 Reserved;
+ __u16 ByteCount;
+ unsigned char EncryptionKey[1];
+} __attribute__((packed)) LANMAN_NEG_RSP;
+
+#define READ_RAW_ENABLE 1
+#define WRITE_RAW_ENABLE 2
+#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE)
+#define SMB1_CLIENT_GUID_SIZE (16)
+typedef struct negotiate_rsp {
+ struct smb_hdr hdr; /* wct = 17 */
+ __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
+ __u8 SecurityMode;
+ __le16 MaxMpxCount;
+ __le16 MaxNumberVcs;
+ __le32 MaxBufferSize;
+ __le32 MaxRawSize;
+ __le32 SessionKey;
+ __le32 Capabilities; /* see below */
+ __le32 SystemTimeLow;
+ __le32 SystemTimeHigh;
+ __le16 ServerTimeZone;
+ __u8 EncryptionKeyLength;
+ __u16 ByteCount;
+ union {
+ unsigned char EncryptionKey[1]; /* cap extended security off */
+ /* followed by Domain name - if extended security is off */
+ /* followed by 16 bytes of server GUID */
+ /* then security blob if cap_extended_security negotiated */
+ struct {
+ unsigned char GUID[SMB1_CLIENT_GUID_SIZE];
+ unsigned char SecurityBlob[1];
+ } __attribute__((packed)) extended_response;
+ } __attribute__((packed)) u;
+} __attribute__((packed)) NEGOTIATE_RSP;
+
+/* SecurityMode bits */
+#define SECMODE_USER 0x01 /* off indicates share level security */
+#define SECMODE_PW_ENCRYPT 0x02
+#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */
+#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */
+
+/* Negotiate response Capabilities */
+#define CAP_RAW_MODE 0x00000001
+#define CAP_MPX_MODE 0x00000002
+#define CAP_UNICODE 0x00000004
+#define CAP_LARGE_FILES 0x00000008
+#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */
+#define CAP_RPC_REMOTE_APIS 0x00000020
+#define CAP_STATUS32 0x00000040
+#define CAP_LEVEL_II_OPLOCKS 0x00000080
+#define CAP_LOCK_AND_READ 0x00000100
+#define CAP_NT_FIND 0x00000200
+#define CAP_DFS 0x00001000
+#define CAP_INFOLEVEL_PASSTHRU 0x00002000
+#define CAP_LARGE_READ_X 0x00004000
+#define CAP_LARGE_WRITE_X 0x00008000
+#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */
+#define CAP_UNIX 0x00800000
+#define CAP_COMPRESSED_DATA 0x02000000
+#define CAP_DYNAMIC_REAUTH 0x20000000
+#define CAP_PERSISTENT_HANDLES 0x40000000
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+typedef union smb_com_session_setup_andx {
+ struct { /* request format */
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 MaxBufferSize;
+ __le16 MaxMpxCount;
+ __le16 VcNumber;
+ __u32 SessionKey;
+ __le16 SecurityBlobLength;
+ __u32 Reserved;
+ __le32 Capabilities; /* see below */
+ __le16 ByteCount;
+ unsigned char SecurityBlob[1]; /* followed by */
+ /* STRING NativeOS */
+ /* STRING NativeLanMan */
+ } __attribute__((packed)) req; /* NTLM request format (with
+ extended security */
+
+ struct { /* request format */
+ struct smb_hdr hdr; /* wct = 13 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 MaxBufferSize;
+ __le16 MaxMpxCount;
+ __le16 VcNumber;
+ __u32 SessionKey;
+ __le16 CaseInsensitivePasswordLength; /* ASCII password len */
+ __le16 CaseSensitivePasswordLength; /* Unicode password length*/
+ __u32 Reserved; /* see below */
+ __le32 Capabilities;
+ __le16 ByteCount;
+ unsigned char CaseInsensitivePassword[1]; /* followed by: */
+ /* unsigned char * CaseSensitivePassword; */
+ /* STRING AccountName */
+ /* STRING PrimaryDomain */
+ /* STRING NativeOS */
+ /* STRING NativeLanMan */
+ } __attribute__((packed)) req_no_secext; /* NTLM request format (without
+ extended security */
+
+ struct { /* default (NTLM) response format */
+ struct smb_hdr hdr; /* wct = 4 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Action; /* see below */
+ __le16 SecurityBlobLength;
+ __u16 ByteCount;
+ unsigned char SecurityBlob[1]; /* followed by */
+/* unsigned char * NativeOS; */
+/* unsigned char * NativeLanMan; */
+/* unsigned char * PrimaryDomain; */
+ } __attribute__((packed)) resp; /* NTLM response
+ (with or without extended sec) */
+
+ struct { /* request format */
+ struct smb_hdr hdr; /* wct = 10 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 MaxBufferSize;
+ __le16 MaxMpxCount;
+ __le16 VcNumber;
+ __u32 SessionKey;
+ __le16 PasswordLength;
+ __u32 Reserved; /* encrypt key len and offset */
+ __le16 ByteCount;
+ unsigned char AccountPassword[1]; /* followed by */
+ /* STRING AccountName */
+ /* STRING PrimaryDomain */
+ /* STRING NativeOS */
+ /* STRING NativeLanMan */
+ } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */
+
+ struct { /* default (NTLM) response format */
+ struct smb_hdr hdr; /* wct = 3 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Action; /* see below */
+ __u16 ByteCount;
+ unsigned char NativeOS[1]; /* followed by */
+/* unsigned char * NativeLanMan; */
+/* unsigned char * PrimaryDomain; */
+ } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */
+} __attribute__((packed)) SESSION_SETUP_ANDX;
+
+/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */
+
+#define NTLMSSP_SERVER_TYPE 1
+#define NTLMSSP_DOMAIN_TYPE 2
+#define NTLMSSP_FQ_DOMAIN_TYPE 3
+#define NTLMSSP_DNS_DOMAIN_TYPE 4
+#define NTLMSSP_DNS_PARENT_TYPE 5
+
+struct ntlmssp2_name {
+ __le16 type;
+ __le16 length;
+/* char name[length]; */
+} __attribute__((packed));
+
+struct ntlmv2_resp {
+ union {
+ char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+ struct {
+ __u8 reserved[8];
+ __u8 key[CIFS_SERVER_CHALLENGE_SIZE];
+ } __attribute__((packed)) challenge;
+ } __attribute__((packed));
+ __le32 blob_signature;
+ __u32 reserved;
+ __le64 time;
+ __u64 client_chal; /* random */
+ __u32 reserved2;
+ /* array of name entries could follow ending in minimum 4 byte struct */
+} __attribute__((packed));
+
+
+#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
+
+/* Capabilities bits (for NTLM SessSetup request) */
+#define CAP_UNICODE 0x00000004
+#define CAP_LARGE_FILES 0x00000008
+#define CAP_NT_SMBS 0x00000010
+#define CAP_STATUS32 0x00000040
+#define CAP_LEVEL_II_OPLOCKS 0x00000080
+#define CAP_NT_FIND 0x00000200 /* reserved should be zero
+ (because NT_SMBs implies the same thing?) */
+#define CAP_BULK_TRANSFER 0x20000000
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/* Action bits */
+#define GUEST_LOGIN 1
+
+typedef struct smb_com_tconx_req {
+ struct smb_hdr hdr; /* wct = 4 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Flags; /* see below */
+ __le16 PasswordLength;
+ __le16 ByteCount;
+ unsigned char Password[1]; /* followed by */
+/* STRING Path *//* \\server\share name */
+ /* STRING Service */
+} __attribute__((packed)) TCONX_REQ;
+
+typedef struct smb_com_tconx_rsp {
+ struct smb_hdr hdr; /* wct = 3 , not extended response */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 OptionalSupport; /* see below */
+ __u16 ByteCount;
+ unsigned char Service[1]; /* always ASCII, not Unicode */
+ /* STRING NativeFileSystem */
+} __attribute__((packed)) TCONX_RSP;
+
+typedef struct smb_com_tconx_rsp_ext {
+ struct smb_hdr hdr; /* wct = 7, extended response */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 OptionalSupport; /* see below */
+ __le32 MaximalShareAccessRights;
+ __le32 GuestMaximalShareAccessRights;
+ __u16 ByteCount;
+ unsigned char Service[1]; /* always ASCII, not Unicode */
+ /* STRING NativeFileSystem */
+} __attribute__((packed)) TCONX_RSP_EXT;
+
+
+/* tree connect Flags */
+#define DISCONNECT_TID 0x0001
+#define TCON_EXTENDED_SIGNATURES 0x0004
+#define TCON_EXTENDED_SECINFO 0x0008
+
+/* OptionalSupport bits */
+#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits
+ (exclusive searches supported) */
+#define SMB_SHARE_IS_IN_DFS 0x0002
+#define SMB_CSC_MASK 0x000C
+/* CSC flags defined as follows */
+#define SMB_CSC_CACHE_MANUAL_REINT 0x0000
+#define SMB_CSC_CACHE_AUTO_REINT 0x0004
+#define SMB_CSC_CACHE_VDO 0x0008
+#define SMB_CSC_NO_CACHING 0x000C
+#define SMB_UNIQUE_FILE_NAME 0x0010
+#define SMB_EXTENDED_SIGNATURES 0x0020
+
+/* services
+ *
+ * A: ie disk
+ * LPT1: ie printer
+ * IPC ie named pipe
+ * COMM
+ * ????? ie any type
+ *
+ */
+
+typedef struct smb_com_echo_req {
+ struct smb_hdr hdr;
+ __le16 EchoCount;
+ __le16 ByteCount;
+ char Data[1];
+} __attribute__((packed)) ECHO_REQ;
+
+typedef struct smb_com_echo_rsp {
+ struct smb_hdr hdr;
+ __le16 SequenceNumber;
+ __le16 ByteCount;
+ char Data[1];
+} __attribute__((packed)) ECHO_RSP;
+
+typedef struct smb_com_logoff_andx_req {
+ struct smb_hdr hdr; /* wct = 2 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __u16 AndXOffset;
+ __u16 ByteCount;
+} __attribute__((packed)) LOGOFF_ANDX_REQ;
+
+typedef struct smb_com_logoff_andx_rsp {
+ struct smb_hdr hdr; /* wct = 2 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __u16 AndXOffset;
+ __u16 ByteCount;
+} __attribute__((packed)) LOGOFF_ANDX_RSP;
+
+typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on
+ tree_connect PDU to effect disconnect */
+ /* tdis is probably simplest SMB PDU */
+ struct {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bcc = 0 */
+ } __attribute__((packed)) req;
+ struct {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bcc = 0 */
+ } __attribute__((packed)) resp;
+} __attribute__((packed)) TREE_DISCONNECT;
+
+typedef struct smb_com_close_req {
+ struct smb_hdr hdr; /* wct = 3 */
+ __u16 FileID;
+ __u32 LastWriteTime; /* should be zero or -1 */
+ __u16 ByteCount; /* 0 */
+} __attribute__((packed)) CLOSE_REQ;
+
+typedef struct smb_com_close_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) CLOSE_RSP;
+
+typedef struct smb_com_flush_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __u16 FileID;
+ __u16 ByteCount; /* 0 */
+} __attribute__((packed)) FLUSH_REQ;
+
+typedef struct smb_com_findclose_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __u16 FileID;
+ __u16 ByteCount; /* 0 */
+} __attribute__((packed)) FINDCLOSE_REQ;
+
+/* OpenFlags */
+#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */
+#define REQ_OPLOCK 0x00000002
+#define REQ_BATCHOPLOCK 0x00000004
+#define REQ_OPENDIRONLY 0x00000008
+#define REQ_EXTENDED_INFO 0x00000010
+
+/* File type */
+#define DISK_TYPE 0x0000
+#define BYTE_PIPE_TYPE 0x0001
+#define MESSAGE_PIPE_TYPE 0x0002
+#define PRINTER_TYPE 0x0003
+#define COMM_DEV_TYPE 0x0004
+#define UNKNOWN_TYPE 0xFFFF
+
+/* Device Type or File Status Flags */
+#define NO_EAS 0x0001
+#define NO_SUBSTREAMS 0x0002
+#define NO_REPARSETAG 0x0004
+/* following flags can apply if pipe */
+#define ICOUNT_MASK 0x00FF
+#define PIPE_READ_MODE 0x0100
+#define NAMED_PIPE_TYPE 0x0400
+#define PIPE_END_POINT 0x4000
+#define BLOCKING_NAMED_PIPE 0x8000
+
+typedef struct smb_com_open_req { /* also handles create */
+ struct smb_hdr hdr; /* wct = 24 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u8 Reserved; /* Must Be Zero */
+ __le16 NameLength;
+ __le32 OpenFlags;
+ __u32 RootDirectoryFid;
+ __le32 DesiredAccess;
+ __le64 AllocationSize;
+ __le32 FileAttributes;
+ __le32 ShareAccess;
+ __le32 CreateDisposition;
+ __le32 CreateOptions;
+ __le32 ImpersonationLevel;
+ __u8 SecurityFlags;
+ __le16 ByteCount;
+ char fileName[1];
+} __attribute__((packed)) OPEN_REQ;
+
+/* open response: oplock levels */
+#define OPLOCK_NONE 0
+#define OPLOCK_EXCLUSIVE 1
+#define OPLOCK_BATCH 2
+#define OPLOCK_READ 3 /* level 2 oplock */
+
+/* open response for CreateAction shifted left */
+#define CIFS_CREATE_ACTION 0x20000 /* file created */
+
+typedef struct smb_com_open_rsp {
+ struct smb_hdr hdr; /* wct = 34 BB */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u8 OplockLevel;
+ __u16 Fid;
+ __le32 CreateAction;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 FileAttributes;
+ __le64 AllocationSize;
+ __le64 EndOfFile;
+ __le16 FileType;
+ __le16 DeviceState;
+ __u8 DirectoryFlag;
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) OPEN_RSP;
+
+typedef struct smb_com_open_rsp_ext {
+ struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u8 OplockLevel;
+ __u16 Fid;
+ __le32 CreateAction;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 FileAttributes;
+ __le64 AllocationSize;
+ __le64 EndOfFile;
+ __le16 FileType;
+ __le16 DeviceState;
+ __u8 DirectoryFlag;
+ __u8 VolumeGUID[16];
+ __u64 FileId; /* note no endian conversion - is opaque UniqueID */
+ __le32 MaximalAccessRights;
+ __le32 GuestMaximalAccessRights;
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) OPEN_RSP_EXT;
+
+
+/* format of legacy open request */
+typedef struct smb_com_openx_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 OpenFlags;
+ __le16 Mode;
+ __le16 Sattr; /* search attributes */
+ __le16 FileAttributes; /* dos attrs */
+ __le32 CreateTime; /* os2 format */
+ __le16 OpenFunction;
+ __le32 EndOfFile;
+ __le32 Timeout;
+ __le32 Reserved;
+ __le16 ByteCount; /* file name follows */
+ char fileName[1];
+} __attribute__((packed)) OPENX_REQ;
+
+typedef struct smb_com_openx_rsp {
+ struct smb_hdr hdr; /* wct = 15 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le16 FileAttributes;
+ __le32 LastWriteTime; /* os2 format */
+ __le32 EndOfFile;
+ __le16 Access;
+ __le16 FileType;
+ __le16 IPCState;
+ __le16 Action;
+ __u32 FileId;
+ __u16 Reserved;
+ __u16 ByteCount;
+} __attribute__((packed)) OPENX_RSP;
+
+/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */
+
+/* Legacy write request for older servers */
+typedef struct smb_com_writex_req {
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __u32 Reserved; /* Timeout */
+ __le16 WriteMode; /* 1 = write through */
+ __le16 Remaining;
+ __le16 Reserved2;
+ __le16 DataLengthLow;
+ __le16 DataOffset;
+ __le16 ByteCount;
+ __u8 Pad; /* BB check for whether padded to DWORD
+ boundary and optimum performance here */
+ char Data[0];
+} __attribute__((packed)) WRITEX_REQ;
+
+typedef struct smb_com_write_req {
+ struct smb_hdr hdr; /* wct = 14 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __u32 Reserved;
+ __le16 WriteMode;
+ __le16 Remaining;
+ __le16 DataLengthHigh;
+ __le16 DataLengthLow;
+ __le16 DataOffset;
+ __le32 OffsetHigh;
+ __le16 ByteCount;
+ __u8 Pad; /* BB check for whether padded to DWORD
+ boundary and optimum performance here */
+ char Data[0];
+} __attribute__((packed)) WRITE_REQ;
+
+typedef struct smb_com_write_rsp {
+ struct smb_hdr hdr; /* wct = 6 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Count;
+ __le16 Remaining;
+ __le16 CountHigh;
+ __u16 Reserved;
+ __u16 ByteCount;
+} __attribute__((packed)) WRITE_RSP;
+
+/* legacy read request for older servers */
+typedef struct smb_com_readx_req {
+ struct smb_hdr hdr; /* wct = 10 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __le16 MaxCount;
+ __le16 MinCount; /* obsolete */
+ __le32 Reserved;
+ __le16 Remaining;
+ __le16 ByteCount;
+} __attribute__((packed)) READX_REQ;
+
+typedef struct smb_com_read_req {
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __le16 MaxCount;
+ __le16 MinCount; /* obsolete */
+ __le32 MaxCountHigh;
+ __le16 Remaining;
+ __le32 OffsetHigh;
+ __le16 ByteCount;
+} __attribute__((packed)) READ_REQ;
+
+typedef struct smb_com_read_rsp {
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Remaining;
+ __le16 DataCompactionMode;
+ __le16 Reserved;
+ __le16 DataLength;
+ __le16 DataOffset;
+ __le16 DataLengthHigh;
+ __u64 Reserved2;
+ __u16 ByteCount;
+ /* read response data immediately follows */
+} __attribute__((packed)) READ_RSP;
+
+typedef struct locking_andx_range {
+ __le16 Pid;
+ __le16 Pad;
+ __le32 OffsetHigh;
+ __le32 OffsetLow;
+ __le32 LengthHigh;
+ __le32 LengthLow;
+} __attribute__((packed)) LOCKING_ANDX_RANGE;
+
+#define LOCKING_ANDX_SHARED_LOCK 0x01
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x02
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04
+#define LOCKING_ANDX_CANCEL_LOCK 0x08
+#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */
+
+typedef struct smb_com_lock_req {
+ struct smb_hdr hdr; /* wct = 8 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __u8 LockType;
+ __u8 OplockLevel;
+ __le32 Timeout;
+ __le16 NumberOfUnlocks;
+ __le16 NumberOfLocks;
+ __le16 ByteCount;
+ LOCKING_ANDX_RANGE Locks[1];
+} __attribute__((packed)) LOCK_REQ;
+
+/* lock type */
+#define CIFS_RDLCK 0
+#define CIFS_WRLCK 1
+#define CIFS_UNLCK 2
+typedef struct cifs_posix_lock {
+ __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */
+ __le16 lock_flags; /* 1 = Wait (only valid for setlock) */
+ __le32 pid;
+ __le64 start;
+ __le64 length;
+ /* BB what about additional owner info to identify network client */
+} __attribute__((packed)) CIFS_POSIX_LOCK;
+
+typedef struct smb_com_lock_rsp {
+ struct smb_hdr hdr; /* wct = 2 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 ByteCount;
+} __attribute__((packed)) LOCK_RSP;
+
+typedef struct smb_com_rename_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __le16 SearchAttributes; /* target file attributes */
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII or Unicode */
+ unsigned char OldFileName[1];
+ /* followed by __u8 BufferFormat2 */
+ /* followed by NewFileName */
+} __attribute__((packed)) RENAME_REQ;
+
+ /* copy request flags */
+#define COPY_MUST_BE_FILE 0x0001
+#define COPY_MUST_BE_DIR 0x0002
+#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */
+#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */
+#define COPY_VERIFY_WRITES 0x0010
+#define COPY_TREE 0x0020
+
+typedef struct smb_com_copy_req {
+ struct smb_hdr hdr; /* wct = 3 */
+ __u16 Tid2;
+ __le16 OpenFunction;
+ __le16 Flags;
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII or Unicode */
+ unsigned char OldFileName[1];
+ /* followed by __u8 BufferFormat2 */
+ /* followed by NewFileName string */
+} __attribute__((packed)) COPY_REQ;
+
+typedef struct smb_com_copy_rsp {
+ struct smb_hdr hdr; /* wct = 1 */
+ __le16 CopyCount; /* number of files copied */
+ __u16 ByteCount; /* may be zero */
+ __u8 BufferFormat; /* 0x04 - only present if errored file follows */
+ unsigned char ErrorFileName[1]; /* only present if error in copy */
+} __attribute__((packed)) COPY_RSP;
+
+#define CREATE_HARD_LINK 0x103
+#define MOVEFILE_COPY_ALLOWED 0x0002
+#define MOVEFILE_REPLACE_EXISTING 0x0001
+
+typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */
+ struct smb_hdr hdr; /* wct = 4 */
+ __le16 SearchAttributes; /* target file attributes */
+ __le16 Flags; /* spec says Information Level */
+ __le32 ClusterCount;
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII or Unicode */
+ unsigned char OldFileName[1];
+ /* followed by __u8 BufferFormat2 */
+ /* followed by NewFileName */
+} __attribute__((packed)) NT_RENAME_REQ;
+
+typedef struct smb_com_rename_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) RENAME_RSP;
+
+typedef struct smb_com_delete_file_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __le16 SearchAttributes;
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char fileName[1];
+} __attribute__((packed)) DELETE_FILE_REQ;
+
+typedef struct smb_com_delete_file_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) DELETE_FILE_RSP;
+
+typedef struct smb_com_delete_directory_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char DirName[1];
+} __attribute__((packed)) DELETE_DIRECTORY_REQ;
+
+typedef struct smb_com_delete_directory_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) DELETE_DIRECTORY_RSP;
+
+typedef struct smb_com_create_directory_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char DirName[1];
+} __attribute__((packed)) CREATE_DIRECTORY_REQ;
+
+typedef struct smb_com_create_directory_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) CREATE_DIRECTORY_RSP;
+
+typedef struct smb_com_query_information_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount; /* 1 + namelen + 1 */
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char FileName[1];
+} __attribute__((packed)) QUERY_INFORMATION_REQ;
+
+typedef struct smb_com_query_information_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ __le16 attr;
+ __le32 last_write_time;
+ __le32 size;
+ __u16 reserved[5];
+ __le16 ByteCount; /* bcc = 0 */
+} __attribute__((packed)) QUERY_INFORMATION_RSP;
+
+typedef struct smb_com_setattr_req {
+ struct smb_hdr hdr; /* wct = 8 */
+ __le16 attr;
+ __le16 time_low;
+ __le16 time_high;
+ __le16 reserved[5]; /* must be zero */
+ __u16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char fileName[1];
+} __attribute__((packed)) SETATTR_REQ;
+
+typedef struct smb_com_setattr_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) SETATTR_RSP;
+
+/* empty wct response to setattr */
+
+/*******************************************************/
+/* NT Transact structure definitions follow */
+/* Currently only ioctl, acl (get security descriptor) */
+/* and notify are implemented */
+/*******************************************************/
+typedef struct smb_com_ntransact_req {
+ struct smb_hdr hdr; /* wct >= 19 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 2 = IOCTL/FSCTL */
+ /* SetupCount words follow then */
+ __le16 ByteCount;
+ __u8 Pad[3];
+ __u8 Parms[0];
+} __attribute__((packed)) NTRANSACT_REQ;
+
+typedef struct smb_com_ntransact_rsp {
+ struct smb_hdr hdr; /* wct = 18 */
+ __u8 Reserved[3];
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 ParameterDisplacement;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __le32 DataDisplacement;
+ __u8 SetupCount; /* 0 */
+ __u16 ByteCount;
+ /* __u8 Pad[3]; */
+ /* parms and data follow */
+} __attribute__((packed)) NTRANSACT_RSP;
+
+/* See MS-SMB 2.2.7.2.1.1 */
+struct srv_copychunk {
+ __le64 SourceOffset;
+ __le64 DestinationOffset;
+ __le32 CopyLength;
+ __u32 Reserved;
+} __packed;
+
+typedef struct smb_com_transaction_ioctl_req {
+ struct smb_hdr hdr; /* wct = 23 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 2 = IOCTL/FSCTL */
+ __le32 FunctionCode;
+ __u16 Fid;
+ __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */
+ __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */
+ __le16 ByteCount;
+ __u8 Pad[3];
+ __u8 Data[1];
+} __attribute__((packed)) TRANSACT_IOCTL_REQ;
+
+typedef struct smb_com_transaction_compr_ioctl_req {
+ struct smb_hdr hdr; /* wct = 23 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 2 = IOCTL/FSCTL */
+ __le32 FunctionCode;
+ __u16 Fid;
+ __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */
+ __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */
+ __le16 ByteCount;
+ __u8 Pad[3];
+ __le16 compression_state; /* See below for valid flags */
+} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ;
+
+/* compression state flags */
+#define COMPRESSION_FORMAT_NONE 0x0000
+#define COMPRESSION_FORMAT_DEFAULT 0x0001
+#define COMPRESSION_FORMAT_LZNT1 0x0002
+
+typedef struct smb_com_transaction_ioctl_rsp {
+ struct smb_hdr hdr; /* wct = 19 */
+ __u8 Reserved[3];
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 ParameterDisplacement;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __le32 DataDisplacement;
+ __u8 SetupCount; /* 1 */
+ __le16 ReturnedDataLen;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACT_IOCTL_RSP;
+
+#define CIFS_ACL_OWNER 1
+#define CIFS_ACL_GROUP 2
+#define CIFS_ACL_DACL 4
+#define CIFS_ACL_SACL 8
+
+typedef struct smb_com_transaction_qsec_req {
+ struct smb_hdr hdr; /* wct = 19 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* no setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */
+ __le16 ByteCount; /* bcc = 3 + 8 */
+ __u8 Pad[3];
+ __u16 Fid;
+ __u16 Reserved2;
+ __le32 AclFlags;
+} __attribute__((packed)) QUERY_SEC_DESC_REQ;
+
+
+typedef struct smb_com_transaction_ssec_req {
+ struct smb_hdr hdr; /* wct = 19 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* no setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 3 = SET_SECURITY_DESC */
+ __le16 ByteCount; /* bcc = 3 + 8 */
+ __u8 Pad[3];
+ __u16 Fid;
+ __u16 Reserved2;
+ __le32 AclFlags;
+} __attribute__((packed)) SET_SEC_DESC_REQ;
+
+typedef struct smb_com_transaction_change_notify_req {
+ struct smb_hdr hdr; /* wct = 23 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand;/* 4 = Change Notify */
+ __le32 CompletionFilter; /* operation to monitor */
+ __u16 Fid;
+ __u8 WatchTree; /* 1 = Monitor subdirectories */
+ __u8 Reserved2;
+ __le16 ByteCount;
+/* __u8 Pad[3];*/
+/* __u8 Data[1];*/
+} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ;
+
+/* BB eventually change to use generic ntransact rsp struct
+ and validation routine */
+typedef struct smb_com_transaction_change_notify_rsp {
+ struct smb_hdr hdr; /* wct = 18 */
+ __u8 Reserved[3];
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 ParameterDisplacement;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __le32 DataDisplacement;
+ __u8 SetupCount; /* 0 */
+ __u16 ByteCount;
+ /* __u8 Pad[3]; */
+} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP;
+/* Completion Filter flags for Notify */
+#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
+#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
+#define FILE_NOTIFY_CHANGE_NAME 0x00000003
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
+#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
+#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
+#define FILE_NOTIFY_CHANGE_EA 0x00000080
+#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
+#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
+#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
+#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+
+#define FILE_ACTION_ADDED 0x00000001
+#define FILE_ACTION_REMOVED 0x00000002
+#define FILE_ACTION_MODIFIED 0x00000003
+#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004
+#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005
+#define FILE_ACTION_ADDED_STREAM 0x00000006
+#define FILE_ACTION_REMOVED_STREAM 0x00000007
+#define FILE_ACTION_MODIFIED_STREAM 0x00000008
+
+/* response contains array of the following structures */
+struct file_notify_information {
+ __le32 NextEntryOffset;
+ __le32 Action;
+ __le32 FileNameLength;
+ __u8 FileName[0];
+} __attribute__((packed));
+
+/* For IO_REPARSE_TAG_SYMLINK */
+struct reparse_symlink_data {
+ __le32 ReparseTag;
+ __le16 ReparseDataLength;
+ __u16 Reserved;
+ __le16 SubstituteNameOffset;
+ __le16 SubstituteNameLength;
+ __le16 PrintNameOffset;
+ __le16 PrintNameLength;
+ __le32 Flags;
+ char PathBuffer[0];
+} __attribute__((packed));
+
+/* For IO_REPARSE_TAG_NFS */
+#define NFS_SPECFILE_LNK 0x00000000014B4E4C
+#define NFS_SPECFILE_CHR 0x0000000000524843
+#define NFS_SPECFILE_BLK 0x00000000004B4C42
+#define NFS_SPECFILE_FIFO 0x000000004F464946
+#define NFS_SPECFILE_SOCK 0x000000004B434F53
+struct reparse_posix_data {
+ __le32 ReparseTag;
+ __le16 ReparseDataLength;
+ __u16 Reserved;
+ __le64 InodeType; /* LNK, FIFO, CHR etc. */
+ char PathBuffer[0];
+} __attribute__((packed));
+
+struct cifs_quota_data {
+ __u32 rsrvd1; /* 0 */
+ __u32 sid_size;
+ __u64 rsrvd2; /* 0 */
+ __u64 space_used;
+ __u64 soft_limit;
+ __u64 hard_limit;
+ char sid[1]; /* variable size? */
+} __attribute__((packed));
+
+/* quota sub commands */
+#define QUOTA_LIST_CONTINUE 0
+#define QUOTA_LIST_START 0x100
+#define QUOTA_FOR_SID 0x101
+
+struct trans2_req {
+ /* struct smb_hdr hdr precedes. Set wct = 14+ */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* 1st setup word - SetupCount words follow */
+ __le16 ByteCount;
+} __attribute__((packed));
+
+struct smb_t2_req {
+ struct smb_hdr hdr;
+ struct trans2_req t2_req;
+} __attribute__((packed));
+
+struct trans2_resp {
+ /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __u16 Reserved;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 ParameterDisplacement;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __le16 DataDisplacement;
+ __u8 SetupCount;
+ __u8 Reserved1;
+ /* SetupWords[SetupCount];
+ __u16 ByteCount;
+ __u16 Reserved2;*/
+ /* data area follows */
+} __attribute__((packed));
+
+struct smb_t2_rsp {
+ struct smb_hdr hdr;
+ struct trans2_resp t2_rsp;
+} __attribute__((packed));
+
+/* PathInfo/FileInfo infolevels */
+#define SMB_INFO_STANDARD 1
+#define SMB_SET_FILE_EA 2
+#define SMB_QUERY_FILE_EA_SIZE 2
+#define SMB_INFO_QUERY_EAS_FROM_LIST 3
+#define SMB_INFO_QUERY_ALL_EAS 4
+#define SMB_INFO_IS_NAME_VALID 6
+#define SMB_QUERY_FILE_BASIC_INFO 0x101
+#define SMB_QUERY_FILE_STANDARD_INFO 0x102
+#define SMB_QUERY_FILE_EA_INFO 0x103
+#define SMB_QUERY_FILE_NAME_INFO 0x104
+#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105
+#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106
+#define SMB_QUERY_FILE_ALL_INFO 0x107
+#define SMB_QUERY_ALT_NAME_INFO 0x108
+#define SMB_QUERY_FILE_STREAM_INFO 0x109
+#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B
+#define SMB_QUERY_FILE_UNIX_BASIC 0x200
+#define SMB_QUERY_FILE_UNIX_LINK 0x201
+#define SMB_QUERY_POSIX_ACL 0x204
+#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */
+#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */
+#define SMB_QUERY_POSIX_PERMISSION 0x207
+#define SMB_QUERY_POSIX_LOCK 0x208
+/* #define SMB_POSIX_OPEN 0x209 */
+/* #define SMB_POSIX_UNLINK 0x20a */
+#define SMB_QUERY_FILE__UNIX_INFO2 0x20b
+#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee
+#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0
+#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */
+#define SMB_QUERY_FILE_POSITION_INFO 0x3f6
+#define SMB_QUERY_FILE_MODE_INFO 0x3f8
+#define SMB_QUERY_FILE_ALGN_INFO 0x3f9
+
+
+#define SMB_SET_FILE_BASIC_INFO 0x101
+#define SMB_SET_FILE_DISPOSITION_INFO 0x102
+#define SMB_SET_FILE_ALLOCATION_INFO 0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO 0x104
+#define SMB_SET_FILE_UNIX_BASIC 0x200
+#define SMB_SET_FILE_UNIX_LINK 0x201
+#define SMB_SET_FILE_UNIX_HLINK 0x203
+#define SMB_SET_POSIX_ACL 0x204
+#define SMB_SET_XATTR 0x205
+#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */
+#define SMB_SET_POSIX_LOCK 0x208
+#define SMB_POSIX_OPEN 0x209
+#define SMB_POSIX_UNLINK 0x20a
+#define SMB_SET_FILE_UNIX_INFO2 0x20b
+#define SMB_SET_FILE_BASIC_INFO2 0x3ec
+#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */
+#define SMB_FILE_ALL_INFO2 0x3fa
+#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb
+#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc
+#define SMB_FILE_MOVE_CLUSTER_INFO 0x407
+#define SMB_FILE_QUOTA_INFO 0x408
+#define SMB_FILE_REPARSEPOINT_INFO 0x409
+#define SMB_FILE_MAXIMUM_INFO 0x40d
+
+/* Find File infolevels */
+#define SMB_FIND_FILE_INFO_STANDARD 0x001
+#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002
+#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003
+#define SMB_FIND_FILE_DIRECTORY_INFO 0x101
+#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_FILE_NAMES_INFO 0x103
+#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105
+#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106
+#define SMB_FIND_FILE_UNIX 0x202
+
+typedef struct smb_com_transaction2_qpi_req {
+ struct smb_hdr hdr; /* wct = 14+ */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __le16 InformationLevel;
+ __u32 Reserved4;
+ char FileName[1];
+} __attribute__((packed)) TRANSACTION2_QPI_REQ;
+
+typedef struct smb_com_transaction2_qpi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word is present for infolevels > 100 */
+} __attribute__((packed)) TRANSACTION2_QPI_RSP;
+
+typedef struct smb_com_transaction2_spi_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 Pad1;
+ __le16 InformationLevel;
+ __u32 Reserved4;
+ char FileName[1];
+} __attribute__((packed)) TRANSACTION2_SPI_REQ;
+
+typedef struct smb_com_transaction2_spi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word is present for infolevels > 100 */
+} __attribute__((packed)) TRANSACTION2_SPI_RSP;
+
+struct set_file_rename {
+ __le32 overwrite; /* 1 = overwrite dest */
+ __u32 root_fid; /* zero */
+ __le32 target_name_len;
+ char target_name[0]; /* Must be unicode */
+} __attribute__((packed));
+
+struct smb_com_transaction2_sfi_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 Pad1;
+ __u16 Fid;
+ __le16 InformationLevel;
+ __u16 Reserved4;
+} __attribute__((packed));
+
+struct smb_com_transaction2_sfi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word reserved -
+ present for infolevels > 100 */
+} __attribute__((packed));
+
+struct smb_t2_qfi_req {
+ struct smb_hdr hdr;
+ struct trans2_req t2;
+ __u8 Pad;
+ __u16 Fid;
+ __le16 InformationLevel;
+} __attribute__((packed));
+
+struct smb_t2_qfi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word reserved -
+ present for infolevels > 100 */
+} __attribute__((packed));
+
+/*
+ * Flags on T2 FINDFIRST and FINDNEXT
+ */
+#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001
+#define CIFS_SEARCH_CLOSE_AT_END 0x0002
+#define CIFS_SEARCH_RETURN_RESUME 0x0004
+#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008
+#define CIFS_SEARCH_BACKUP_SEARCH 0x0010
+
+/*
+ * Size of the resume key on FINDFIRST and FINDNEXT calls
+ */
+#define CIFS_SMB_RESUME_KEY_SIZE 4
+
+typedef struct smb_com_transaction2_ffirst_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_FIND_FIRST */
+ __le16 ByteCount;
+ __u8 Pad;
+ __le16 SearchAttributes;
+ __le16 SearchCount;
+ __le16 SearchFlags;
+ __le16 InformationLevel;
+ __le32 SearchStorageType;
+ char FileName[1];
+} __attribute__((packed)) TRANSACTION2_FFIRST_REQ;
+
+typedef struct smb_com_transaction2_ffirst_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_FFIRST_RSP;
+
+typedef struct smb_com_transaction2_ffirst_rsp_parms {
+ __u16 SearchHandle;
+ __le16 SearchCount;
+ __le16 EndofSearch;
+ __le16 EAErrorOffset;
+ __le16 LastNameOffset;
+} __attribute__((packed)) T2_FFIRST_RSP_PARMS;
+
+typedef struct smb_com_transaction2_fnext_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_FIND_NEXT */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 SearchHandle;
+ __le16 SearchCount;
+ __le16 InformationLevel;
+ __u32 ResumeKey;
+ __le16 SearchFlags;
+ char ResumeFileName[1];
+} __attribute__((packed)) TRANSACTION2_FNEXT_REQ;
+
+typedef struct smb_com_transaction2_fnext_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_FNEXT_RSP;
+
+typedef struct smb_com_transaction2_fnext_rsp_parms {
+ __le16 SearchCount;
+ __le16 EndofSearch;
+ __le16 EAErrorOffset;
+ __le16 LastNameOffset;
+} __attribute__((packed)) T2_FNEXT_RSP_PARMS;
+
+/* QFSInfo Levels */
+#define SMB_INFO_ALLOCATION 1
+#define SMB_INFO_VOLUME 2
+#define SMB_QUERY_FS_VOLUME_INFO 0x102
+#define SMB_QUERY_FS_SIZE_INFO 0x103
+#define SMB_QUERY_FS_DEVICE_INFO 0x104
+#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105
+#define SMB_QUERY_CIFS_UNIX_INFO 0x200
+#define SMB_QUERY_POSIX_FS_INFO 0x201
+#define SMB_QUERY_POSIX_WHO_AM_I 0x202
+#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203
+#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure
+ FILE_SYSTEM__UNIX_INFO to tell
+ whether new NTIOCTL available
+ (0xACE) for WAN friendly SMB
+ operations to be carried */
+#define SMB_QUERY_LABEL_INFO 0x3ea
+#define SMB_QUERY_FS_QUOTA_INFO 0x3ee
+#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef
+#define SMB_QUERY_OBJECTID_INFO 0x3f0
+
+typedef struct smb_com_transaction2_qfsi_req {
+ struct smb_hdr hdr; /* wct = 14+ */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __le16 InformationLevel;
+} __attribute__((packed)) TRANSACTION2_QFSI_REQ;
+
+typedef struct smb_com_transaction_qfsi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u8 Pad; /* may be three bytes? *//* followed by data area */
+} __attribute__((packed)) TRANSACTION2_QFSI_RSP;
+
+typedef struct whoami_rsp_data { /* Query level 0x202 */
+ __u32 flags; /* 0 = Authenticated user 1 = GUEST */
+ __u32 mask; /* which flags bits server understands ie 0x0001 */
+ __u64 unix_user_id;
+ __u64 unix_user_gid;
+ __u32 number_of_supplementary_gids; /* may be zero */
+ __u32 number_of_sids; /* may be zero */
+ __u32 length_of_sid_array; /* in bytes - may be zero */
+ __u32 pad; /* reserved - MBZ */
+ /* __u64 gid_array[0]; */ /* may be empty */
+ /* __u8 * psid_list */ /* may be empty */
+} __attribute__((packed)) WHOAMI_RSP_DATA;
+
+/* SETFSInfo Levels */
+#define SMB_SET_CIFS_UNIX_INFO 0x200
+/* level 0x203 is defined above in list of QFS info levels */
+/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */
+
+/* Level 0x200 request structure follows */
+typedef struct smb_com_transaction2_setfsi_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount; /* 4 */
+ __le16 ParameterOffset;
+ __le16 DataCount; /* 12 */
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 FileNum; /* Parameters start. */
+ __le16 InformationLevel;/* Parameters end. */
+ __le16 ClientUnixMajor; /* Data start. */
+ __le16 ClientUnixMinor;
+ __le64 ClientUnixCap; /* Data end */
+} __attribute__((packed)) TRANSACTION2_SETFSI_REQ;
+
+/* level 0x203 request structure follows */
+typedef struct smb_com_transaction2_setfs_enc_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount; /* 4 */
+ __le16 ParameterOffset;
+ __le16 DataCount; /* 12 */
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 Reserved4; /* Parameters start. */
+ __le16 InformationLevel;/* Parameters end. */
+ /* NTLMSSP Blob, Data start. */
+} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ;
+
+/* response for setfsinfo levels 0x200 and 0x203 */
+typedef struct smb_com_transaction2_setfsi_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_SETFSI_RSP;
+
+typedef struct smb_com_transaction2_get_dfs_refer_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length
+ perhaps?) followed by one byte pad - doesn't
+ seem to matter though */
+ __le16 MaxReferralLevel;
+ char RequestFileName[1];
+} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ;
+
+#define DFS_VERSION cpu_to_le16(0x0003)
+
+/* DFS server target type */
+#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */
+#define DFS_TYPE_ROOT 0x0001
+
+/* Referral Entry Flags */
+#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */
+#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */
+
+typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
+ __le16 VersionNumber; /* must be 3 or 4 */
+ __le16 Size;
+ __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */
+ __le16 ReferralEntryFlags;
+ __le32 TimeToLive;
+ __le16 DfsPathOffset;
+ __le16 DfsAlternatePathOffset;
+ __le16 NetworkAddressOffset; /* offset of the link target */
+ __u8 ServiceSiteGuid[16]; /* MBZ, ignored */
+} __attribute__((packed)) REFERRAL3;
+
+struct get_dfs_referral_rsp {
+ __le16 PathConsumed;
+ __le16 NumberOfReferrals;
+ __le32 DFSFlags;
+ REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
+ /* followed by the strings pointed to by the referral structures */
+} __packed;
+
+typedef struct smb_com_transaction_get_dfs_refer_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u8 Pad;
+ struct get_dfs_referral_rsp dfs_data;
+} __packed TRANSACTION2_GET_DFS_REFER_RSP;
+
+/* DFS Flags */
+#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */
+#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */
+#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */
+
+/*
+ ************************************************************************
+ * All structs for everything above the SMB PDUs themselves
+ * (such as the T2 level specific data) go here
+ ************************************************************************
+ */
+
+/*
+ * Information on a server
+ */
+
+struct serverInfo {
+ char name[16];
+ unsigned char versionMajor;
+ unsigned char versionMinor;
+ unsigned long type;
+ unsigned int commentOffset;
+} __attribute__((packed));
+
+/*
+ * The following structure is the format of the data returned on a NetShareEnum
+ * with level "90" (x5A)
+ */
+
+struct shareInfo {
+ char shareName[13];
+ char pad;
+ unsigned short type;
+ unsigned int commentOffset;
+} __attribute__((packed));
+
+struct aliasInfo {
+ char aliasName[9];
+ char pad;
+ unsigned int commentOffset;
+ unsigned char type[2];
+} __attribute__((packed));
+
+struct aliasInfo92 {
+ int aliasNameOffset;
+ int serverNameOffset;
+ int shareNameOffset;
+} __attribute__((packed));
+
+typedef struct {
+ __le64 TotalAllocationUnits;
+ __le64 FreeAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */
+
+typedef struct {
+ __le32 fsid;
+ __le32 SectorsPerAllocationUnit;
+ __le32 TotalAllocationUnits;
+ __le32 FreeAllocationUnits;
+ __le16 BytesPerSector;
+} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO;
+
+typedef struct {
+ __le16 MajorVersionNumber;
+ __le16 MinorVersionNumber;
+ __le64 Capability;
+} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/
+
+/* Version numbers for CIFS UNIX major and minor. */
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
+/* Linux/Unix extensions capability flags */
+#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */
+#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */
+#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */
+#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */
+#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */
+#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based
+ calls including posix open
+ and posix unlink */
+#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up
+ to 0xFFFF00 */
+#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080
+#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */
+#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */
+#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and
+ QFS PROXY call */
+#ifdef CONFIG_CIFS_POSIX
+/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send
+ LockingX instead of posix locking call on unix sess (and we do not expect
+ LockingX to use different (ie Windows) semantics than posix locking on
+ the same session (if WINE needs to do this later, we can add this cap
+ back in later */
+/* #define CIFS_UNIX_CAP_MASK 0x000000fb */
+#define CIFS_UNIX_CAP_MASK 0x000003db
+#else
+#define CIFS_UNIX_CAP_MASK 0x00000013
+#endif /* CONFIG_CIFS_POSIX */
+
+
+#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */
+
+typedef struct {
+ /* 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 */
+} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO;
+
+/* DeviceType Flags */
+#define FILE_DEVICE_CD_ROM 0x00000002
+#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003
+#define FILE_DEVICE_DFS 0x00000006
+#define FILE_DEVICE_DISK 0x00000007
+#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008
+#define FILE_DEVICE_FILE_SYSTEM 0x00000009
+#define FILE_DEVICE_NAMED_PIPE 0x00000011
+#define FILE_DEVICE_NETWORK 0x00000012
+#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014
+#define FILE_DEVICE_NULL 0x00000015
+#define FILE_DEVICE_PARALLEL_PORT 0x00000016
+#define FILE_DEVICE_PRINTER 0x00000018
+#define FILE_DEVICE_SERIAL_PORT 0x0000001b
+#define FILE_DEVICE_STREAMS 0x0000001e
+#define FILE_DEVICE_TAPE 0x0000001f
+#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020
+#define FILE_DEVICE_VIRTUAL_DISK 0x00000024
+#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028
+
+/* Device Characteristics */
+#define FILE_REMOVABLE_MEDIA 0x00000001
+#define FILE_READ_ONLY_DEVICE 0x00000002
+#define FILE_FLOPPY_DISKETTE 0x00000004
+#define FILE_WRITE_ONCE_MEDIA 0x00000008
+#define FILE_REMOTE_DEVICE 0x00000010
+#define FILE_DEVICE_IS_MOUNTED 0x00000020
+#define FILE_VIRTUAL_VOLUME 0x00000040
+#define FILE_DEVICE_SECURE_OPEN 0x00000100
+#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000
+#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000
+#define FILE_PORTABLE_DEVICE 0x00004000
+#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000
+
+typedef struct {
+ __le32 DeviceType;
+ __le32 DeviceCharacteristics;
+} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
+
+/* minimum includes first three fields, and empty FS Name */
+#define MIN_FS_ATTR_INFO_SIZE 12
+
+
+/* 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
+typedef struct {
+ __le32 Attributes;
+ __le32 MaxPathNameComponentLength;
+ __le32 FileSystemNameLen;
+ char FileSystemName[52]; /* do not have to save this - get subset? */
+} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
+
+/******************************************************************************/
+/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */
+/******************************************************************************/
+typedef struct { /* data block encoding of response to level 263 QPathInfo */
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 Attributes;
+ __u32 Pad1;
+ __le64 AllocationSize;
+ __le64 EndOfFile; /* size ie offset to first free byte in file */
+ __le32 NumberOfLinks; /* hard links */
+ __u8 DeletePending;
+ __u8 Directory;
+ __u16 Pad2;
+ __le64 IndexNumber;
+ __le32 EASize;
+ __le32 AccessFlags;
+ __u64 IndexNumber1;
+ __le64 CurrentByteOffset;
+ __le32 Mode;
+ __le32 AlignmentRequirement;
+ __le32 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */
+
+typedef struct {
+ __le64 AllocationSize;
+ __le64 EndOfFile; /* size ie offset to first free byte in file */
+ __le32 NumberOfLinks; /* hard links */
+ __u8 DeletePending;
+ __u8 Directory;
+ __u16 Pad;
+} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */
+
+
+/* defines for enumerating possible values of the Unix type field below */
+#define UNIX_FILE 0
+#define UNIX_DIR 1
+#define UNIX_SYMLINK 2
+#define UNIX_CHARDEV 3
+#define UNIX_BLOCKDEV 4
+#define UNIX_FIFO 5
+#define UNIX_SOCKET 6
+typedef struct {
+ __le64 EndOfFile;
+ __le64 NumOfBytes;
+ __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */
+ __le64 LastAccessTime;
+ __le64 LastModificationTime;
+ __le64 Uid;
+ __le64 Gid;
+ __le32 Type;
+ __le64 DevMajor;
+ __le64 DevMinor;
+ __le64 UniqueId;
+ __le64 Permissions;
+ __le64 Nlinks;
+} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
+
+typedef struct {
+ char LinkDest[1];
+} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */
+
+/* The following three structures are needed only for
+ setting time to NT4 and some older servers via
+ the primitive DOS time format */
+typedef struct {
+ __u16 Day:5;
+ __u16 Month:4;
+ __u16 Year:7;
+} __attribute__((packed)) SMB_DATE;
+
+typedef struct {
+ __u16 TwoSeconds:5;
+ __u16 Minutes:6;
+ __u16 Hours:5;
+} __attribute__((packed)) SMB_TIME;
+
+typedef struct {
+ __le16 CreationDate; /* SMB Date see above */
+ __le16 CreationTime; /* SMB Time */
+ __le16 LastAccessDate;
+ __le16 LastAccessTime;
+ __le16 LastWriteDate;
+ __le16 LastWriteTime;
+ __le32 DataSize; /* File Size (EOF) */
+ __le32 AllocationSize;
+ __le16 Attributes; /* verify not u32 */
+ __le32 EASize;
+} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
+
+typedef struct {
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 Attributes;
+ __u32 Pad;
+} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */
+
+struct file_allocation_info {
+ __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
+} __attribute__((packed)); /* size used on disk, for level 0x103 for set,
+ 0x105 for query */
+
+struct file_end_of_file_info {
+ __le64 FileSize; /* offset to end of file */
+} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */
+
+struct file_alt_name_info {
+ __u8 alt_name[1];
+} __attribute__((packed)); /* level 0x0108 */
+
+struct file_stream_info {
+ __le32 number_of_streams; /* BB check sizes and verify location */
+ /* followed by info on streams themselves
+ u64 size;
+ u64 allocation_size
+ stream info */
+}; /* level 0x109 */
+
+struct file_compression_info {
+ __le64 compressed_size;
+ __le16 format;
+ __u8 unit_shift;
+ __u8 ch_shift;
+ __u8 cl_shift;
+ __u8 pad[3];
+} __attribute__((packed)); /* level 0x10b */
+
+/* POSIX ACL set/query path info structures */
+#define CIFS_ACL_VERSION 1
+struct cifs_posix_ace { /* access control entry (ACE) */
+ __u8 cifs_e_tag;
+ __u8 cifs_e_perm;
+ __le64 cifs_uid; /* or gid */
+} __attribute__((packed));
+
+struct cifs_posix_acl { /* access conrol list (ACL) */
+ __le16 version;
+ __le16 access_entry_count; /* access ACL - count of entries */
+ __le16 default_entry_count; /* default ACL - count of entries */
+ struct cifs_posix_ace ace_array[0];
+ /* followed by
+ struct cifs_posix_ace default_ace_arraay[] */
+} __attribute__((packed)); /* level 0x204 */
+
+/* types of access control entries already defined in posix_acl.h */
+/* #define CIFS_POSIX_ACL_USER_OBJ 0x01
+#define CIFS_POSIX_ACL_USER 0x02
+#define CIFS_POSIX_ACL_GROUP_OBJ 0x04
+#define CIFS_POSIX_ACL_GROUP 0x08
+#define CIFS_POSIX_ACL_MASK 0x10
+#define CIFS_POSIX_ACL_OTHER 0x20 */
+
+/* types of perms */
+/* #define CIFS_POSIX_ACL_EXECUTE 0x01
+#define CIFS_POSIX_ACL_WRITE 0x02
+#define CIFS_POSIX_ACL_READ 0x04 */
+
+/* end of POSIX ACL definitions */
+
+/* POSIX Open Flags */
+#define SMB_O_RDONLY 0x1
+#define SMB_O_WRONLY 0x2
+#define SMB_O_RDWR 0x4
+#define SMB_O_CREAT 0x10
+#define SMB_O_EXCL 0x20
+#define SMB_O_TRUNC 0x40
+#define SMB_O_APPEND 0x80
+#define SMB_O_SYNC 0x100
+#define SMB_O_DIRECTORY 0x200
+#define SMB_O_NOFOLLOW 0x400
+#define SMB_O_DIRECT 0x800
+
+typedef struct {
+ __le32 OpenFlags; /* same as NT CreateX */
+ __le32 PosixOpenFlags;
+ __le64 Permissions;
+ __le16 Level; /* reply level requested (see QPathInfo levels) */
+} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
+
+typedef struct {
+ __le16 OplockFlags;
+ __u16 Fid;
+ __le32 CreateAction;
+ __le16 ReturnedLevel;
+ __le16 Pad;
+ /* struct following varies based on requested level */
+} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
+
+#define SMB_POSIX_UNLINK_FILE_TARGET 0
+#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1
+
+struct unlink_psx_rq { /* level 0x20a SetPathInfo */
+ __le16 type;
+} __attribute__((packed));
+
+struct file_internal_info {
+ __le64 UniqueId; /* inode number */
+} __attribute__((packed)); /* level 0x3ee */
+
+struct file_mode_info {
+ __le32 Mode;
+} __attribute__((packed)); /* level 0x3f8 */
+
+struct file_attrib_tag {
+ __le32 Attribute;
+ __le32 ReparseTag;
+} __attribute__((packed)); /* level 0x40b */
+
+
+/********************************************************/
+/* FindFirst/FindNext transact2 data buffer formats */
+/********************************************************/
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 ResumeKey; /* as with FileIndex - no need to convert */
+ FILE_UNIX_BASIC_INFO basic;
+ char FileName[1];
+} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
+
+typedef struct {
+ __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 */
+ char FileName[1];
+} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
+
+typedef struct {
+ __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[1];
+} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
+
+typedef struct {
+ __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[12];
+ char FileName[1];
+} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+
+typedef struct {
+ __u32 ResumeKey;
+ __le16 CreationDate; /* SMB Date */
+ __le16 CreationTime; /* SMB Time */
+ __le16 LastAccessDate;
+ __le16 LastAccessTime;
+ __le16 LastWriteDate;
+ __le16 LastWriteTime;
+ __le32 DataSize; /* File Size (EOF) */
+ __le32 AllocationSize;
+ __le16 Attributes; /* verify not u32 */
+ __u8 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
+
+
+struct win_dev {
+ unsigned char type[8]; /* IntxCHR or IntxBLK */
+ __le64 major;
+ __le64 minor;
+} __attribute__((packed));
+
+struct gea {
+ unsigned char name_len;
+ char name[1];
+} __attribute__((packed));
+
+struct gealist {
+ unsigned long list_len;
+ struct gea list[1];
+} __attribute__((packed));
+
+struct fea {
+ unsigned char EA_flags;
+ __u8 name_len;
+ __le16 value_len;
+ char name[1];
+ /* optionally followed by value */
+} __attribute__((packed));
+/* flags for _FEA.fEA */
+#define FEA_NEEDEA 0x80 /* need EA bit */
+
+struct fealist {
+ __le32 list_len;
+ struct fea list[1];
+} __attribute__((packed));
+
+/* used to hold an arbitrary blob of data */
+struct data_blob {
+ __u8 *data;
+ size_t length;
+ void (*free) (struct data_blob *data_blob);
+} __attribute__((packed));
+
+
+#ifdef CONFIG_CIFS_POSIX
+/*
+ For better POSIX semantics from Linux client, (even better
+ than the existing CIFS Unix Extensions) we need updated PDUs for:
+
+ 1) PosixCreateX - to set and return the mode, inode#, device info and
+ perhaps add a CreateDevice - to create Pipes and other special .inodes
+ Also note POSIX open flags
+ 2) Close - to return the last write time to do cache across close
+ more safely
+ 3) FindFirst return unique inode number - what about resume key, two
+ forms short (matches readdir) and full (enough info to cache inodes)
+ 4) Mkdir - set mode
+
+ And under consideration:
+ 5) FindClose2 (return nanosecond timestamp ??)
+ 6) Use nanosecond timestamps throughout all time fields if
+ corresponding attribute flag is set
+ 7) sendfile - handle based copy
+
+ what about fixing 64 bit alignment
+
+ There are also various legacy SMB/CIFS requests used as is
+
+ From existing Lanman and NTLM dialects:
+ --------------------------------------
+ NEGOTIATE
+ SESSION_SETUP_ANDX (BB which?)
+ TREE_CONNECT_ANDX (BB which wct?)
+ TREE_DISCONNECT (BB add volume timestamp on response)
+ LOGOFF_ANDX
+ DELETE (note delete open file behavior)
+ DELETE_DIRECTORY
+ READ_AND_X
+ WRITE_AND_X
+ LOCKING_AND_X (note posix lock semantics)
+ RENAME (note rename across dirs and open file rename posix behaviors)
+ NT_RENAME (for hardlinks) Is this good enough for all features?
+ FIND_CLOSE2
+ TRANSACTION2 (18 cases)
+ SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2
+ (BB verify that never need to set allocation size)
+ SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via
+ Unix ext?)
+
+ COPY (note support for copy across directories) - FUTURE, OPTIONAL
+ setting/getting OS/2 EAs - FUTURE (BB can this handle
+ setting Linux xattrs perfectly) - OPTIONAL
+ dnotify - FUTURE, OPTIONAL
+ quota - FUTURE, OPTIONAL
+
+ Note that various requests implemented for NT interop such as
+ NT_TRANSACT (IOCTL) QueryReparseInfo
+ are unneeded to servers compliant with the CIFS POSIX extensions
+
+ From CIFS Unix Extensions:
+ -------------------------
+ T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks
+ T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2)
+ T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK)
+ T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing
+ inode fields
+ Actually a need QUERY_FILE_UNIX_INFO
+ since has inode num
+ BB what about a) blksize/blkbits/blocks
+ b) i_version
+ c) i_rdev
+ d) notify mask?
+ e) generation
+ f) size_seqcount
+ T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX
+ TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended
+ T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL
+ */
+
+/* xsymlink is a symlink format (used by MacOS) that can be used
+ to save symlink info in a regular file when
+ mounted to operating systems that do not
+ support the cifs Unix extensions or EAs (for xattr
+ based symlinks). For such a file to be recognized
+ as containing symlink data:
+
+ 1) file size must be 1067,
+ 2) signature must begin file data,
+ 3) length field must be set to ASCII representation
+ of a number which is less than or equal to 1024,
+ 4) md5 must match that of the path data */
+
+struct xsymlink {
+ /* 1067 bytes */
+ char signature[4]; /* XSym */ /* not null terminated */
+ char cr0; /* \n */
+/* ASCII representation of length (4 bytes decimal) terminated by \n not null */
+ char length[4];
+ char cr1; /* \n */
+/* md5 of valid subset of path ie path[0] through path[length-1] */
+ __u8 md5[32];
+ char cr2; /* \n */
+/* if room left, then end with \n then 0x20s by convention but not required */
+ char path[1024];
+} __attribute__((packed));
+
+typedef struct file_xattr_info {
+ /* BB do we need another field for flags? BB */
+ __u32 xattr_name_len;
+ __u32 xattr_value_len;
+ char xattr_name[0];
+ /* followed by xattr_value[xattr_value_len], no pad */
+} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info
+ level 0x205 */
+
+/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */
+
+typedef struct file_chattr_info {
+ __le64 mask; /* list of all possible attribute bits */
+ __le64 mode; /* list of actual attribute bits on this inode */
+} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes
+ (chattr, chflags) level 0x206 */
+#endif /* POSIX */
+#endif /* _CIFSPDU_H */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
new file mode 100644
index 000000000..d7ac75ea8
--- /dev/null
+++ b/fs/cifs/cifsproto.h
@@ -0,0 +1,566 @@
+/*
+ * fs/cifs/cifsproto.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _CIFSPROTO_H
+#define _CIFSPROTO_H
+#include <linux/nls.h>
+#include "trace.h"
+
+struct statfs;
+struct smb_vol;
+struct smb_rqst;
+
+/*
+ *****************************************************************
+ * All Prototypes
+ *****************************************************************
+ */
+
+extern struct smb_hdr *cifs_buf_get(void);
+extern void cifs_buf_release(void *);
+extern struct smb_hdr *cifs_small_buf_get(void);
+extern void cifs_small_buf_release(void *);
+extern void free_rsp_buf(int, void *);
+extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
+ unsigned int /* length */);
+extern unsigned int _get_xid(void);
+extern void _free_xid(unsigned int);
+#define get_xid() \
+({ \
+ unsigned int __xid = _get_xid(); \
+ cifs_dbg(FYI, "CIFS VFS: in %s as Xid: %u with uid: %d\n", \
+ __func__, __xid, \
+ from_kuid(&init_user_ns, current_fsuid())); \
+ trace_smb3_enter(__xid, __func__); \
+ __xid; \
+})
+
+#define free_xid(curr_xid) \
+do { \
+ _free_xid(curr_xid); \
+ cifs_dbg(FYI, "CIFS VFS: leaving %s (xid = %u) rc = %d\n", \
+ __func__, curr_xid, (int)rc); \
+ if (rc) \
+ trace_smb3_exit_err(curr_xid, __func__, (int)rc); \
+ else \
+ trace_smb3_exit_done(curr_xid, __func__); \
+} while (0)
+extern int init_cifs_idmap(void);
+extern void exit_cifs_idmap(void);
+extern int init_cifs_spnego(void);
+extern void exit_cifs_spnego(void);
+extern char *build_path_from_dentry(struct dentry *);
+extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry,
+ bool prefix);
+extern char *cifs_build_path_to_root(struct smb_vol *vol,
+ struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon,
+ int add_treename);
+extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
+extern char *cifs_compose_mount_options(const char *sb_mountdata,
+ const char *fullpath, const struct dfs_info3_param *ref,
+ char **devname);
+/* extern void renew_parental_timestamps(struct dentry *direntry);*/
+extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
+ struct TCP_Server_Info *server);
+extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
+extern void cifs_delete_mid(struct mid_q_entry *mid);
+extern void cifs_mid_q_entry_release(struct mid_q_entry *midEntry);
+extern void cifs_wake_up_task(struct mid_q_entry *mid);
+extern int cifs_handle_standard(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid);
+extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
+extern int cifs_call_async(struct TCP_Server_Info *server,
+ struct smb_rqst *rqst,
+ mid_receive_t *receive, mid_callback_t *callback,
+ mid_handle_t *handle, void *cbdata, const int flags);
+extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_rqst *rqst, int *resp_buf_type,
+ const int flags, struct kvec *resp_iov);
+extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
+ const int flags, const int num_rqst,
+ struct smb_rqst *rqst, int *resp_buf_type,
+ struct kvec *resp_iov);
+extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
+ struct smb_hdr * /* input */ ,
+ struct smb_hdr * /* out */ ,
+ int * /* bytes returned */ , const int);
+extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
+ char *in_buf, int flags);
+extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
+ struct smb_rqst *);
+extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
+ struct smb_rqst *);
+extern int cifs_check_receive(struct mid_q_entry *mid,
+ struct TCP_Server_Info *server, bool log_error);
+extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
+ unsigned int size, unsigned int *num,
+ unsigned int *credits);
+extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
+ struct kvec *, int /* nvec to send */,
+ int * /* type of buf returned */, const int flags,
+ struct kvec * /* resp vec */);
+extern int SendReceiveBlockingLock(const unsigned int xid,
+ struct cifs_tcon *ptcon,
+ struct smb_hdr *in_buf ,
+ struct smb_hdr *out_buf,
+ int *bytes_returned);
+extern int cifs_reconnect(struct TCP_Server_Info *server);
+extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
+extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
+extern bool backup_cred(struct cifs_sb_info *);
+extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
+extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
+ unsigned int bytes_written);
+extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
+extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
+extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
+extern int decode_negTokenInit(unsigned char *security_blob, int length,
+ struct TCP_Server_Info *server);
+extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
+extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port);
+extern int map_smb_to_linux_error(char *buf, bool logErr);
+extern void header_assemble(struct smb_hdr *, char /* command */ ,
+ const struct cifs_tcon *, int /* length of
+ fixed section (word count) in two byte units */);
+extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
+ struct cifs_ses *ses,
+ void **request_buf);
+extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
+ enum securityEnum requested);
+extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
+ const struct nls_table *nls_cp);
+extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
+extern u64 cifs_UnixTimeToNT(struct timespec64);
+extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
+ int offset);
+extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
+extern int cifs_get_writer(struct cifsInodeInfo *cinode);
+extern void cifs_put_writer(struct cifsInodeInfo *cinode);
+extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode);
+extern int cifs_unlock_range(struct cifsFileInfo *cfile,
+ struct file_lock *flock, const unsigned int xid);
+extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
+
+extern void cifs_down_write(struct rw_semaphore *sem);
+extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
+ struct file *file,
+ struct tcon_link *tlink,
+ __u32 oplock);
+extern int cifs_posix_open(char *full_path, struct inode **inode,
+ struct super_block *sb, int mode,
+ unsigned int f_flags, __u32 *oplock, __u16 *netfid,
+ unsigned int xid);
+void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr);
+extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
+ FILE_UNIX_BASIC_INFO *info,
+ struct cifs_sb_info *cifs_sb);
+extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
+ struct cifs_sb_info *);
+extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
+extern struct inode *cifs_iget(struct super_block *sb,
+ struct cifs_fattr *fattr);
+
+extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
+ FILE_ALL_INFO *data, struct super_block *sb,
+ int xid, const struct cifs_fid *fid);
+extern int cifs_get_inode_info_unix(struct inode **pinode,
+ const unsigned char *search_path,
+ struct super_block *sb, unsigned int xid);
+extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs,
+ unsigned int xid, char *full_path, __u32 dosattr);
+extern int cifs_rename_pending_delete(const char *full_path,
+ struct dentry *dentry,
+ const unsigned int xid);
+extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr, struct inode *inode,
+ const char *path, const struct cifs_fid *pfid);
+extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64,
+ kuid_t, kgid_t);
+extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *,
+ const char *, u32 *);
+extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *,
+ const struct cifs_fid *, u32 *);
+extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *,
+ const char *, int);
+
+extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);
+extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ unsigned int to_read);
+extern int cifs_read_page_from_socket(struct TCP_Server_Info *server,
+ struct page *page,
+ unsigned int page_offset,
+ unsigned int to_read);
+extern int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
+ struct cifs_sb_info *cifs_sb);
+extern int cifs_match_super(struct super_block *, void *);
+extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info);
+extern struct smb_vol *cifs_get_volume_info(char *mount_data,
+ const char *devname, bool is_smb3);
+extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
+extern void cifs_umount(struct cifs_sb_info *);
+extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
+extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon);
+
+extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
+ __u64 length, __u8 type,
+ struct cifsLockInfo **conf_lock,
+ int rw_check);
+extern void cifs_add_pending_open(struct cifs_fid *fid,
+ struct tcon_link *tlink,
+ struct cifs_pending_open *open);
+extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
+ struct tcon_link *tlink,
+ struct cifs_pending_open *open);
+extern void cifs_del_pending_open(struct cifs_pending_open *open);
+extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
+ int from_reconnect);
+extern void cifs_put_tcon(struct cifs_tcon *tcon);
+
+#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
+extern void cifs_dfs_release_automount_timer(void);
+#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */
+#define cifs_dfs_release_automount_timer() do { } while (0)
+#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */
+
+void cifs_proc_init(void);
+void cifs_proc_clean(void);
+
+extern void cifs_move_llist(struct list_head *source, struct list_head *dest);
+extern void cifs_free_llist(struct list_head *llist);
+extern void cifs_del_lock_waiters(struct cifsLockInfo *lock);
+
+extern int cifs_negotiate_protocol(const unsigned int xid,
+ struct cifs_ses *ses);
+extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
+ struct nls_table *nls_info);
+extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required);
+extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses);
+
+extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
+ const char *tree, struct cifs_tcon *tcon,
+ const struct nls_table *);
+
+extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *searchName, struct cifs_sb_info *cifs_sb,
+ __u16 *searchHandle, __u16 search_flags,
+ struct cifs_search_info *psrch_inf,
+ bool msearch);
+
+extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
+ __u16 searchHandle, __u16 search_flags,
+ struct cifs_search_info *psrch_inf);
+
+extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 search_handle);
+
+extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ u16 netfid, FILE_ALL_INFO *pFindData);
+extern int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *search_Name, FILE_ALL_INFO *data,
+ int legacy /* whether to use old info level */,
+ const struct nls_table *nls_codepage, int remap);
+extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *search_name, FILE_ALL_INFO *data,
+ const struct nls_table *nls_codepage, int remap);
+
+extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ u16 netfid, FILE_UNIX_BASIC_INFO *pFindData);
+extern int CIFSSMBUnixQPathInfo(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const unsigned char *searchName,
+ FILE_UNIX_BASIC_INFO *pFindData,
+ const struct nls_table *nls_codepage, int remap);
+
+extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
+ const char *search_name,
+ struct dfs_info3_param **target_nodes,
+ unsigned int *num_of_nodes,
+ const struct nls_table *nls_codepage, int remap);
+
+extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
+ const char *old_path,
+ const struct nls_table *nls_codepage,
+ unsigned int *num_referrals,
+ struct dfs_info3_param **referrals, int remap);
+extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+ unsigned int *num_of_nodes,
+ struct dfs_info3_param **target_nodes,
+ const struct nls_table *nls_codepage, int remap,
+ const char *searchName, bool is_unicode);
+extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ struct smb_vol *vol);
+extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData);
+extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData);
+extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ __u64 cap);
+
+extern int CIFSSMBQFSAttributeInfo(const unsigned int xid,
+ struct cifs_tcon *tcon);
+extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon);
+extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon);
+extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData);
+
+extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, const FILE_BASIC_INFO *data,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const FILE_BASIC_INFO *data, __u16 fid,
+ __u32 pid_of_opener);
+extern int CIFSSMBSetFileDisposition(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ bool delete_file, __u16 fid,
+ __u32 pid_of_opener);
+#if 0
+extern int CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon,
+ char *fileName, __u16 dos_attributes,
+ const struct nls_table *nls_codepage);
+#endif /* possibly unneeded function */
+extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *file_name, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_allocation);
+extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size,
+ bool set_allocation);
+
+struct cifs_unix_set_info_args {
+ __u64 ctime;
+ __u64 atime;
+ __u64 mtime;
+ __u64 mode;
+ kuid_t uid;
+ kgid_t gid;
+ dev_t device;
+};
+
+extern int CIFSSMBUnixSetFileInfo(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const struct cifs_unix_set_info_args *args,
+ u16 fid, u32 pid_of_opener);
+
+extern int CIFSSMBUnixSetPathInfo(const unsigned int xid,
+ struct cifs_tcon *tcon, const char *file_name,
+ const struct cifs_unix_set_info_args *args,
+ const struct nls_table *nls_codepage,
+ int remap);
+
+extern int CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, __u16 type,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
+extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon,
+ int netfid, const char *target_name,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
+extern int CIFSUnixCreateHardLink(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSUnixCreateSymLink(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage, int remap);
+extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const unsigned char *searchName, char **syminfo,
+ const struct nls_table *nls_codepage, int remap);
+extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
+ __u16 fid, char **symlinkinfo,
+ const struct nls_table *nls_codepage);
+extern int CIFSSMB_set_compression(const unsigned int xid,
+ struct cifs_tcon *tcon, __u16 fid);
+extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms,
+ int *oplock, FILE_ALL_INFO *buf);
+extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, const int disposition,
+ const int access_flags, const int omode,
+ __u16 *netfid, int *pOplock, FILE_ALL_INFO *,
+ const struct nls_table *nls_codepage, int remap);
+extern int CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,
+ u32 posix_flags, __u64 mode, __u16 *netfid,
+ FILE_UNIX_BASIC_INFO *pRetData,
+ __u32 *pOplock, const char *name,
+ const struct nls_table *nls_codepage, int remap);
+extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon,
+ const int smb_file_id);
+
+extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon,
+ const int smb_file_id);
+
+extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, char **buf,
+ int *return_buf_type);
+extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, const char *buf);
+extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, struct kvec *iov, const int nvec);
+extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *search_name, __u64 *inode_number,
+ const struct nls_table *nls_codepage,
+ int remap);
+
+extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 netfid, const __u8 lock_type,
+ const __u32 num_unlock, const __u32 num_lock,
+ LOCKING_ANDX_RANGE *buf);
+extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 netfid, const __u32 netpid, const __u64 len,
+ const __u64 offset, const __u32 numUnlock,
+ const __u32 numLock, const __u8 lockType,
+ const bool waitFlag, const __u8 oplock_level);
+extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 smb_file_id, const __u32 netpid,
+ const loff_t start_offset, const __u64 len,
+ struct file_lock *, const __u16 lock_type,
+ const bool waitFlag);
+extern int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon);
+extern int CIFSSMBEcho(struct TCP_Server_Info *server);
+extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses);
+
+extern struct cifs_ses *sesInfoAlloc(void);
+extern void sesInfoFree(struct cifs_ses *);
+extern struct cifs_tcon *tconInfoAlloc(void);
+extern void tconInfoFree(struct cifs_tcon *);
+
+extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number);
+extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
+ __u32 *);
+extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
+extern int cifs_verify_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server,
+ __u32 expected_sequence_number);
+extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
+ const struct nls_table *);
+extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
+extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
+extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server);
+extern int calc_seckey(struct cifs_ses *);
+extern int generate_smb30signingkey(struct cifs_ses *);
+extern int generate_smb311signingkey(struct cifs_ses *);
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+extern int calc_lanman_hash(const char *password, const char *cryptkey,
+ bool encrypt, char *lnm_session_key);
+#endif /* CIFS_WEAK_PW_HASH */
+#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
+extern int CIFSSMBNotify(const unsigned int xid, struct cifs_tcon *tcon,
+ const int notify_subdirs, const __u16 netfid,
+ __u32 filter, struct file *file, int multishot,
+ const struct nls_table *nls_codepage);
+#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */
+extern int CIFSSMBCopy(unsigned int xid,
+ struct cifs_tcon *source_tcon,
+ const char *fromName,
+ const __u16 target_tid,
+ const char *toName, const int flags,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName,
+ const unsigned char *ea_name, char *EAData,
+ size_t bufsize, struct cifs_sb_info *cifs_sb);
+extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, const char *ea_name,
+ const void *ea_value, const __u16 ea_value_len,
+ const struct nls_table *nls_codepage,
+ struct cifs_sb_info *cifs_sb);
+extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon,
+ __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
+extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16,
+ struct cifs_ntsd *, __u32, int);
+extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName,
+ char *acl_inf, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *fileName,
+ const char *local_acl, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
+ const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
+extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb);
+extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr);
+extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr,
+ const unsigned char *path);
+extern int mdfour(unsigned char *, unsigned char *, int);
+extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
+ const struct nls_table *codepage);
+extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
+ unsigned char *p24);
+
+void cifs_readdata_release(struct kref *refcount);
+int cifs_async_readv(struct cifs_readdata *rdata);
+int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
+
+int cifs_async_writev(struct cifs_writedata *wdata,
+ void (*release)(struct kref *kref));
+void cifs_writev_complete(struct work_struct *work);
+struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
+ work_func_t complete);
+struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages,
+ work_func_t complete);
+void cifs_writedata_release(struct kref *refcount);
+int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const unsigned char *path, char *pbuf,
+ unsigned int *pbytes_read);
+int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const unsigned char *path, char *pbuf,
+ unsigned int *pbytes_written);
+int __cifs_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server, char *signature,
+ struct shash_desc *shash);
+enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
+ enum securityEnum);
+struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
+void cifs_aio_ctx_release(struct kref *refcount);
+int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
+void smb2_cached_lease_break(struct work_struct *work);
+
+int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
+ struct sdesc **sdesc);
+void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
+
+extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
+ unsigned int *len, unsigned int *offset);
+
+#endif /* _CIFSPROTO_H */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
new file mode 100644
index 000000000..cb70f0c6a
--- /dev/null
+++ b/fs/cifs/cifssmb.c
@@ -0,0 +1,6539 @@
+/*
+ * fs/cifs/cifssmb.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2010
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Contains the routines for constructing the SMB PDUs themselves
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */
+ /* These are mostly routines that operate on a pathname, or on a tree id */
+ /* (mounted volume), but there are eight handle based routines which must be */
+ /* treated slightly differently for reconnection purposes since we never */
+ /* want to reuse a stale file handle and only the caller knows the file info */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/task_io_accounting_ops.h>
+#include <linux/uaccess.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsacl.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "fscache.h"
+#include "smbdirect.h"
+
+#ifdef CONFIG_CIFS_POSIX
+static struct {
+ int index;
+ char *name;
+} protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ {LANMAN_PROT, "\2LM1.2X002"},
+ {LANMAN2_PROT, "\2LANMAN2.1"},
+#endif /* weak password hashing for legacy clients */
+ {CIFS_PROT, "\2NT LM 0.12"},
+ {POSIX_PROT, "\2POSIX 2"},
+ {BAD_PROT, "\2"}
+};
+#else
+static struct {
+ int index;
+ char *name;
+} protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ {LANMAN_PROT, "\2LM1.2X002"},
+ {LANMAN2_PROT, "\2LANMAN2.1"},
+#endif /* weak password hashing for legacy clients */
+ {CIFS_PROT, "\2NT LM 0.12"},
+ {BAD_PROT, "\2"}
+};
+#endif
+
+/* define the number of elements in the cifs dialect array */
+#ifdef CONFIG_CIFS_POSIX
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 4
+#else
+#define CIFS_NUM_PROT 2
+#endif /* CIFS_WEAK_PW_HASH */
+#else /* not posix */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 3
+#else
+#define CIFS_NUM_PROT 1
+#endif /* CONFIG_CIFS_WEAK_PW_HASH */
+#endif /* CIFS_POSIX */
+
+/*
+ * Mark as invalid, all open files on tree connections since they
+ * were closed when session to server was lost.
+ */
+void
+cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
+{
+ struct cifsFileInfo *open_file = NULL;
+ struct list_head *tmp;
+ struct list_head *tmp1;
+
+ /* list all files open on tree connection and mark them invalid */
+ spin_lock(&tcon->open_file_lock);
+ list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
+ open_file = list_entry(tmp, struct cifsFileInfo, tlist);
+ open_file->invalidHandle = true;
+ open_file->oplock_break_cancelled = true;
+ }
+ spin_unlock(&tcon->open_file_lock);
+
+ mutex_lock(&tcon->crfid.fid_mutex);
+ tcon->crfid.is_valid = false;
+ memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
+ mutex_unlock(&tcon->crfid.fid_mutex);
+
+ /*
+ * BB Add call to invalidate_inodes(sb) for all superblocks mounted
+ * to this tcon.
+ */
+}
+
+/* reconnect the socket, tcon, and smb session if needed */
+static int
+cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
+{
+ int rc;
+ struct cifs_ses *ses;
+ struct TCP_Server_Info *server;
+ struct nls_table *nls_codepage;
+
+ /*
+ * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
+ * tcp and smb session status done differently for those three - in the
+ * calling routine
+ */
+ if (!tcon)
+ return 0;
+
+ ses = tcon->ses;
+ server = ses->server;
+
+ /*
+ * only tree disconnect, open, and write, (and ulogoff which does not
+ * have tcon) are allowed as we start force umount
+ */
+ if (tcon->tidStatus == CifsExiting) {
+ if (smb_command != SMB_COM_WRITE_ANDX &&
+ smb_command != SMB_COM_OPEN_ANDX &&
+ smb_command != SMB_COM_TREE_DISCONNECT) {
+ cifs_dbg(FYI, "can not send cmd %d while umounting\n",
+ smb_command);
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Give demultiplex thread up to 10 seconds to reconnect, should be
+ * greater than cifs socket timeout which is 7 seconds
+ */
+ while (server->tcpStatus == CifsNeedReconnect) {
+ rc = wait_event_interruptible_timeout(server->response_q,
+ (server->tcpStatus != CifsNeedReconnect),
+ 10 * HZ);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: aborting reconnect due to a received"
+ " signal by the process\n", __func__);
+ return -ERESTARTSYS;
+ }
+
+ /* are we still trying to reconnect? */
+ if (server->tcpStatus != CifsNeedReconnect)
+ break;
+
+ /*
+ * on "soft" mounts we wait once. Hard mounts keep
+ * retrying until process is killed or server comes
+ * back on-line
+ */
+ if (!tcon->retry) {
+ cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
+ return -EHOSTDOWN;
+ }
+ }
+
+ if (!ses->need_reconnect && !tcon->need_reconnect)
+ return 0;
+
+ nls_codepage = load_nls_default();
+
+ /*
+ * need to prevent multiple threads trying to simultaneously
+ * reconnect the same SMB session
+ */
+ mutex_lock(&ses->session_mutex);
+
+ /*
+ * Recheck after acquire mutex. If another thread is negotiating
+ * and the server never sends an answer the socket will be closed
+ * and tcpStatus set to reconnect.
+ */
+ if (server->tcpStatus == CifsNeedReconnect) {
+ rc = -EHOSTDOWN;
+ mutex_unlock(&ses->session_mutex);
+ goto out;
+ }
+
+ rc = cifs_negotiate_protocol(0, ses);
+ if (rc == 0 && ses->need_reconnect)
+ rc = cifs_setup_session(0, ses, nls_codepage);
+
+ /* do we need to reconnect tcon? */
+ if (rc || !tcon->need_reconnect) {
+ mutex_unlock(&ses->session_mutex);
+ goto out;
+ }
+
+ cifs_mark_open_files_invalid(tcon);
+ rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
+ mutex_unlock(&ses->session_mutex);
+ cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
+
+ if (rc) {
+ printk_once(KERN_WARNING "reconnect tcon failed rc = %d\n", rc);
+ goto out;
+ }
+
+ atomic_inc(&tconInfoReconnectCount);
+
+ /* tell server Unix caps we support */
+ if (ses->capabilities & CAP_UNIX)
+ reset_cifs_unix_caps(0, tcon, NULL, NULL);
+
+ /*
+ * Removed call to reopen open files here. It is safer (and faster) to
+ * reopen files one at a time as needed in read and write.
+ *
+ * FIXME: what about file locks? don't we need to reclaim them ASAP?
+ */
+
+out:
+ /*
+ * Check if handle based operation so we know whether we can continue
+ * or not without returning to caller to reset file handle
+ */
+ switch (smb_command) {
+ case SMB_COM_READ_ANDX:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_FIND_CLOSE2:
+ case SMB_COM_LOCKING_ANDX:
+ rc = -EAGAIN;
+ }
+
+ unload_nls(nls_codepage);
+ return rc;
+}
+
+/* Allocate and return pointer to an SMB request buffer, and set basic
+ SMB information in the SMB header. If the return code is zero, this
+ function must have filled in request_buf pointer */
+static int
+small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
+ void **request_buf)
+{
+ int rc;
+
+ rc = cifs_reconnect_tcon(tcon, smb_command);
+ if (rc)
+ return rc;
+
+ *request_buf = cifs_small_buf_get();
+ if (*request_buf == NULL) {
+ /* BB should we add a retry in here if not a writepage? */
+ return -ENOMEM;
+ }
+
+ header_assemble((struct smb_hdr *) *request_buf, smb_command,
+ tcon, wct);
+
+ if (tcon != NULL)
+ cifs_stats_inc(&tcon->num_smbs_sent);
+
+ return 0;
+}
+
+int
+small_smb_init_no_tc(const int smb_command, const int wct,
+ struct cifs_ses *ses, void **request_buf)
+{
+ int rc;
+ struct smb_hdr *buffer;
+
+ rc = small_smb_init(smb_command, wct, NULL, request_buf);
+ if (rc)
+ return rc;
+
+ buffer = (struct smb_hdr *)*request_buf;
+ buffer->Mid = get_next_mid(ses->server);
+ if (ses->capabilities & CAP_UNICODE)
+ buffer->Flags2 |= SMBFLG2_UNICODE;
+ if (ses->capabilities & CAP_STATUS32)
+ buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+
+ /* uid, tid can stay at zero as set in header assemble */
+
+ /* BB add support for turning on the signing when
+ this function is used after 1st of session setup requests */
+
+ return rc;
+}
+
+/* If the return code is zero, this function must fill in request_buf pointer */
+static int
+__smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
+ void **request_buf, void **response_buf)
+{
+ *request_buf = cifs_buf_get();
+ if (*request_buf == NULL) {
+ /* BB should we add a retry in here if not a writepage? */
+ return -ENOMEM;
+ }
+ /* Although the original thought was we needed the response buf for */
+ /* potential retries of smb operations it turns out we can determine */
+ /* from the mid flags when the request buffer can be resent without */
+ /* having to use a second distinct buffer for the response */
+ if (response_buf)
+ *response_buf = *request_buf;
+
+ header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
+ wct);
+
+ if (tcon != NULL)
+ cifs_stats_inc(&tcon->num_smbs_sent);
+
+ return 0;
+}
+
+/* If the return code is zero, this function must fill in request_buf pointer */
+static int
+smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
+ void **request_buf, void **response_buf)
+{
+ int rc;
+
+ rc = cifs_reconnect_tcon(tcon, smb_command);
+ if (rc)
+ return rc;
+
+ return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
+}
+
+static int
+smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon,
+ void **request_buf, void **response_buf)
+{
+ if (tcon->ses->need_reconnect || tcon->need_reconnect)
+ return -EHOSTDOWN;
+
+ return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
+}
+
+static int validate_t2(struct smb_t2_rsp *pSMB)
+{
+ unsigned int total_size;
+
+ /* check for plausible wct */
+ if (pSMB->hdr.WordCount < 10)
+ goto vt2_err;
+
+ /* check for parm and data offset going beyond end of smb */
+ if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
+ get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
+ goto vt2_err;
+
+ total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
+ if (total_size >= 512)
+ goto vt2_err;
+
+ /* check that bcc is at least as big as parms + data, and that it is
+ * less than negotiated smb buffer
+ */
+ total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
+ if (total_size > get_bcc(&pSMB->hdr) ||
+ total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
+ goto vt2_err;
+
+ return 0;
+vt2_err:
+ cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
+ sizeof(struct smb_t2_rsp) + 16);
+ return -EINVAL;
+}
+
+static int
+decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr)
+{
+ int rc = 0;
+ u16 count;
+ char *guid = pSMBr->u.extended_response.GUID;
+ struct TCP_Server_Info *server = ses->server;
+
+ count = get_bcc(&pSMBr->hdr);
+ if (count < SMB1_CLIENT_GUID_SIZE)
+ return -EIO;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ if (server->srv_count > 1) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) {
+ cifs_dbg(FYI, "server UID changed\n");
+ memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE);
+ }
+ } else {
+ spin_unlock(&cifs_tcp_ses_lock);
+ memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE);
+ }
+
+ if (count == SMB1_CLIENT_GUID_SIZE) {
+ server->sec_ntlmssp = true;
+ } else {
+ count -= SMB1_CLIENT_GUID_SIZE;
+ rc = decode_negTokenInit(
+ pSMBr->u.extended_response.SecurityBlob, count, server);
+ if (rc != 1)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int
+cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
+{
+ bool srv_sign_required = server->sec_mode & server->vals->signing_required;
+ bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled;
+ bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN;
+
+ /*
+ * Is signing required by mnt options? If not then check
+ * global_secflags to see if it is there.
+ */
+ if (!mnt_sign_required)
+ mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) ==
+ CIFSSEC_MUST_SIGN);
+
+ /*
+ * If signing is required then it's automatically enabled too,
+ * otherwise, check to see if the secflags allow it.
+ */
+ mnt_sign_enabled = mnt_sign_required ? mnt_sign_required :
+ (global_secflags & CIFSSEC_MAY_SIGN);
+
+ /* If server requires signing, does client allow it? */
+ if (srv_sign_required) {
+ if (!mnt_sign_enabled) {
+ cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!");
+ return -ENOTSUPP;
+ }
+ server->sign = true;
+ }
+
+ /* If client requires signing, does server allow it? */
+ if (mnt_sign_required) {
+ if (!srv_sign_enabled) {
+ cifs_dbg(VFS, "Server does not support signing!");
+ return -ENOTSUPP;
+ }
+ server->sign = true;
+ }
+
+ if (cifs_rdma_enabled(server) && server->sign)
+ cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled");
+
+ return 0;
+}
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+static int
+decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
+{
+ __s16 tmp;
+ struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
+
+ if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT)
+ return -EOPNOTSUPP;
+
+ server->sec_mode = le16_to_cpu(rsp->SecurityMode);
+ server->maxReq = min_t(unsigned int,
+ le16_to_cpu(rsp->MaxMpxCount),
+ cifs_max_pending);
+ set_credits(server, server->maxReq);
+ server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
+ /* even though we do not use raw we might as well set this
+ accurately, in case we ever find a need for it */
+ if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
+ server->max_rw = 0xFF00;
+ server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
+ } else {
+ server->max_rw = 0;/* do not need to use raw anyway */
+ server->capabilities = CAP_MPX_MODE;
+ }
+ tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
+ if (tmp == -1) {
+ /* OS/2 often does not set timezone therefore
+ * we must use server time to calc time zone.
+ * Could deviate slightly from the right zone.
+ * Smallest defined timezone difference is 15 minutes
+ * (i.e. Nepal). Rounding up/down is done to match
+ * this requirement.
+ */
+ int val, seconds, remain, result;
+ struct timespec64 ts;
+ time64_t utc = ktime_get_real_seconds();
+ ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
+ rsp->SrvTime.Time, 0);
+ cifs_dbg(FYI, "SrvTime %lld sec since 1970 (utc: %lld) diff: %lld\n",
+ ts.tv_sec, utc,
+ utc - ts.tv_sec);
+ val = (int)(utc - ts.tv_sec);
+ seconds = abs(val);
+ result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
+ remain = seconds % MIN_TZ_ADJ;
+ if (remain >= (MIN_TZ_ADJ / 2))
+ result += MIN_TZ_ADJ;
+ if (val < 0)
+ result = -result;
+ server->timeAdj = result;
+ } else {
+ server->timeAdj = (int)tmp;
+ server->timeAdj *= 60; /* also in seconds */
+ }
+ cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj);
+
+
+ /* BB get server time for time conversions and add
+ code to use it and timezone since this is not UTC */
+
+ if (rsp->EncryptionKeyLength ==
+ cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
+ memcpy(server->cryptkey, rsp->EncryptionKey,
+ CIFS_CRYPTO_KEY_SIZE);
+ } else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
+ return -EIO; /* need cryptkey unless plain text */
+ }
+
+ cifs_dbg(FYI, "LANMAN negotiated\n");
+ return 0;
+}
+#else
+static inline int
+decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
+{
+ cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n");
+ return -EOPNOTSUPP;
+}
+#endif
+
+static bool
+should_set_ext_sec_flag(enum securityEnum sectype)
+{
+ switch (sectype) {
+ case RawNTLMSSP:
+ case Kerberos:
+ return true;
+ case Unspecified:
+ if (global_secflags &
+ (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP))
+ return true;
+ /* Fallthrough */
+ default:
+ return false;
+ }
+}
+
+int
+CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
+{
+ NEGOTIATE_REQ *pSMB;
+ NEGOTIATE_RSP *pSMBr;
+ int rc = 0;
+ int bytes_returned;
+ int i;
+ struct TCP_Server_Info *server = ses->server;
+ u16 count;
+
+ if (!server) {
+ WARN(1, "%s: server is NULL!\n", __func__);
+ return -EIO;
+ }
+
+ rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
+ (void **) &pSMB, (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Mid = get_next_mid(server);
+ pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
+
+ if (should_set_ext_sec_flag(ses->sectype)) {
+ cifs_dbg(FYI, "Requesting extended security.");
+ pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ }
+
+ count = 0;
+ /*
+ * We know that all the name entries in the protocols array
+ * are short (< 16 bytes anyway) and are NUL terminated.
+ */
+ for (i = 0; i < CIFS_NUM_PROT; i++) {
+ size_t len = strlen(protocols[i].name) + 1;
+
+ memcpy(pSMB->DialectsArray+count, protocols[i].name, len);
+ count += len;
+ }
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc != 0)
+ goto neg_err_exit;
+
+ server->dialect = le16_to_cpu(pSMBr->DialectIndex);
+ cifs_dbg(FYI, "Dialect: %d\n", server->dialect);
+ /* Check wct = 1 error case */
+ if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
+ /* core returns wct = 1, but we do not ask for core - otherwise
+ small wct just comes when dialect index is -1 indicating we
+ could not negotiate a common dialect */
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+ } else if (pSMBr->hdr.WordCount == 13) {
+ server->negflavor = CIFS_NEGFLAVOR_LANMAN;
+ rc = decode_lanman_negprot_rsp(server, pSMBr);
+ goto signing_check;
+ } else if (pSMBr->hdr.WordCount != 17) {
+ /* unknown wct */
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+ }
+ /* else wct == 17, NTLM or better */
+
+ server->sec_mode = pSMBr->SecurityMode;
+ if ((server->sec_mode & SECMODE_USER) == 0)
+ cifs_dbg(FYI, "share mode security\n");
+
+ /* one byte, so no need to convert this or EncryptionKeyLen from
+ little endian */
+ server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount),
+ cifs_max_pending);
+ set_credits(server, server->maxReq);
+ /* probably no need to store and check maxvcs */
+ server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
+ server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
+ cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf);
+ server->capabilities = le32_to_cpu(pSMBr->Capabilities);
+ server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
+ server->timeAdj *= 60;
+
+ if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+ server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
+ memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
+ CIFS_CRYPTO_KEY_SIZE);
+ } else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC ||
+ server->capabilities & CAP_EXTENDED_SECURITY) {
+ server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
+ rc = decode_ext_sec_blob(ses, pSMBr);
+ } else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
+ rc = -EIO; /* no crypt key only if plain text pwd */
+ } else {
+ server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
+ server->capabilities &= ~CAP_EXTENDED_SECURITY;
+ }
+
+signing_check:
+ if (!rc)
+ rc = cifs_enable_signing(server, ses->sign);
+neg_err_exit:
+ cifs_buf_release(pSMB);
+
+ cifs_dbg(FYI, "negprot rc %d\n", rc);
+ return rc;
+}
+
+int
+CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ struct smb_hdr *smb_buffer;
+ int rc = 0;
+
+ cifs_dbg(FYI, "In tree disconnect\n");
+
+ /* BB: do we need to check this? These should never be NULL. */
+ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+ return -EIO;
+
+ /*
+ * No need to return error on this operation if tid invalidated and
+ * closed on server already e.g. due to tcp session crashing. Also,
+ * the tcon is no longer on the list, so no need to take lock before
+ * checking this.
+ */
+ if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
+ return 0;
+
+ rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
+ (void **)&smb_buffer);
+ if (rc)
+ return rc;
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);
+ cifs_small_buf_release(smb_buffer);
+ if (rc)
+ cifs_dbg(FYI, "Tree disconnect failed %d\n", rc);
+
+ /* No need to return error on this operation if tid invalidated and
+ closed on server already e.g. due to tcp session crashing */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+/*
+ * This is a no-op for now. We're not really interested in the reply, but
+ * rather in the fact that the server sent one and that server->lstrp
+ * gets updated.
+ *
+ * FIXME: maybe we should consider checking that the reply matches request?
+ */
+static void
+cifs_echo_callback(struct mid_q_entry *mid)
+{
+ struct TCP_Server_Info *server = mid->callback_data;
+
+ DeleteMidQEntry(mid);
+ add_credits(server, 1, CIFS_ECHO_OP);
+}
+
+int
+CIFSSMBEcho(struct TCP_Server_Info *server)
+{
+ ECHO_REQ *smb;
+ int rc = 0;
+ struct kvec iov[2];
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = 2 };
+
+ cifs_dbg(FYI, "In echo request\n");
+
+ rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
+ if (rc)
+ return rc;
+
+ if (server->capabilities & CAP_UNICODE)
+ smb->hdr.Flags2 |= SMBFLG2_UNICODE;
+
+ /* set up echo request */
+ smb->hdr.Tid = 0xffff;
+ smb->hdr.WordCount = 1;
+ put_unaligned_le16(1, &smb->EchoCount);
+ put_bcc(1, &smb->hdr);
+ smb->Data[0] = 'a';
+ inc_rfc1001_len(smb, 3);
+
+ iov[0].iov_len = 4;
+ iov[0].iov_base = smb;
+ iov[1].iov_len = get_rfc1002_length(smb);
+ iov[1].iov_base = (char *)smb + 4;
+
+ rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
+ server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
+ if (rc)
+ cifs_dbg(FYI, "Echo request failed: %d\n", rc);
+
+ cifs_small_buf_release(smb);
+
+ return rc;
+}
+
+int
+CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
+{
+ LOGOFF_ANDX_REQ *pSMB;
+ int rc = 0;
+
+ cifs_dbg(FYI, "In SMBLogoff for session disconnect\n");
+
+ /*
+ * BB: do we need to check validity of ses and server? They should
+ * always be valid since we have an active reference. If not, that
+ * should probably be a BUG()
+ */
+ if (!ses || !ses->server)
+ return -EIO;
+
+ mutex_lock(&ses->session_mutex);
+ if (ses->need_reconnect)
+ goto session_already_dead; /* no need to send SMBlogoff if uid
+ already closed due to reconnect */
+ rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ return rc;
+ }
+
+ pSMB->hdr.Mid = get_next_mid(ses->server);
+
+ if (ses->server->sign)
+ pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ pSMB->hdr.Uid = ses->Suid;
+
+ pSMB->AndXCommand = 0xFF;
+ rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+session_already_dead:
+ mutex_unlock(&ses->session_mutex);
+
+ /* if session dead then we do not need to do ulogoff,
+ since server closed smb session, no sense reporting
+ error */
+ if (rc == -EAGAIN)
+ rc = 0;
+ return rc;
+}
+
+int
+CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, __u16 type,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ struct unlink_psx_rq *pRqD;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cifs_dbg(FYI, "In POSIX delete\n");
+PsxDelete:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB add path length overrun check */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = 0; /* BB double check this with jra */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+
+ /* Setup pointer to Request Data (inode type) */
+ pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
+ pRqD->type = cpu_to_le16(type);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
+
+ pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
+ pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "Posix delete returned %d\n", rc);
+ cifs_buf_release(pSMB);
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);
+
+ if (rc == -EAGAIN)
+ goto PsxDelete;
+
+ return rc;
+}
+
+int
+CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ DELETE_FILE_REQ *pSMB = NULL;
+ DELETE_FILE_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ int remap = cifs_remap(cifs_sb);
+
+DelFileRetry:
+ rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name,
+ PATH_MAX, cifs_sb->local_nls,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, name, name_len);
+ }
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
+ pSMB->BufferFormat = 0x04;
+ inc_rfc1001_len(pSMB, name_len + 1);
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);
+ if (rc)
+ cifs_dbg(FYI, "Error in RMFile = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto DelFileRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ DELETE_DIRECTORY_REQ *pSMB = NULL;
+ DELETE_DIRECTORY_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ int remap = cifs_remap(cifs_sb);
+
+ cifs_dbg(FYI, "In CIFSSMBRmDir\n");
+RmDirRetry:
+ rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
+ PATH_MAX, cifs_sb->local_nls,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, name, name_len);
+ }
+
+ pSMB->BufferFormat = 0x04;
+ inc_rfc1001_len(pSMB, name_len + 1);
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_rmdirs);
+ if (rc)
+ cifs_dbg(FYI, "Error in RMDir = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto RmDirRetry;
+ return rc;
+}
+
+int
+CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc = 0;
+ CREATE_DIRECTORY_REQ *pSMB = NULL;
+ CREATE_DIRECTORY_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+ int remap = cifs_remap(cifs_sb);
+
+ cifs_dbg(FYI, "In CIFSSMBMkDir\n");
+MkDirRetry:
+ rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
+ PATH_MAX, cifs_sb->local_nls,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, name, name_len);
+ }
+
+ pSMB->BufferFormat = 0x04;
+ inc_rfc1001_len(pSMB, name_len + 1);
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_mkdirs);
+ if (rc)
+ cifs_dbg(FYI, "Error in Mkdir = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto MkDirRetry;
+ return rc;
+}
+
+int
+CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,
+ __u32 posix_flags, __u64 mode, __u16 *netfid,
+ FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock,
+ const char *name, const struct nls_table *nls_codepage,
+ int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+ OPEN_PSX_REQ *pdata;
+ OPEN_PSX_RSP *psx_rsp;
+
+ cifs_dbg(FYI, "In POSIX Create\n");
+PsxCreat:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, name,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, name, name_len);
+ }
+
+ params = 6 + name_len;
+ count = sizeof(OPEN_PSX_REQ);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
+ pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
+ pdata->Permissions = cpu_to_le64(mode);
+ pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
+ pdata->OpenFlags = cpu_to_le32(*pOplock);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Posix create returned %d\n", rc);
+ goto psx_create_err;
+ }
+
+ cifs_dbg(FYI, "copying inode info\n");
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) {
+ rc = -EIO; /* bad smb */
+ goto psx_create_err;
+ }
+
+ /* copy return information to pRetData */
+ psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
+ + le16_to_cpu(pSMBr->t2.DataOffset));
+
+ *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
+ if (netfid)
+ *netfid = psx_rsp->Fid; /* cifs fid stays in le */
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
+ *pOplock |= CIFS_CREATE_ACTION;
+ /* check to make sure response data is there */
+ if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
+ pRetData->Type = cpu_to_le32(-1); /* unknown */
+ cifs_dbg(NOISY, "unknown type\n");
+ } else {
+ if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)
+ + sizeof(FILE_UNIX_BASIC_INFO)) {
+ cifs_dbg(VFS, "Open response data too small\n");
+ pRetData->Type = cpu_to_le32(-1);
+ goto psx_create_err;
+ }
+ memcpy((char *) pRetData,
+ (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
+ sizeof(FILE_UNIX_BASIC_INFO));
+ }
+
+psx_create_err:
+ cifs_buf_release(pSMB);
+
+ if (posix_flags & SMB_O_DIRECTORY)
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs);
+ else
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens);
+
+ if (rc == -EAGAIN)
+ goto PsxCreat;
+
+ return rc;
+}
+
+static __u16 convert_disposition(int disposition)
+{
+ __u16 ofun = 0;
+
+ switch (disposition) {
+ case FILE_SUPERSEDE:
+ ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+ break;
+ case FILE_OPEN:
+ ofun = SMBOPEN_OAPPEND;
+ break;
+ case FILE_CREATE:
+ ofun = SMBOPEN_OCREATE;
+ break;
+ case FILE_OPEN_IF:
+ ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
+ break;
+ case FILE_OVERWRITE:
+ ofun = SMBOPEN_OTRUNC;
+ break;
+ case FILE_OVERWRITE_IF:
+ ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+ break;
+ default:
+ cifs_dbg(FYI, "unknown disposition %d\n", disposition);
+ ofun = SMBOPEN_OAPPEND; /* regular open */
+ }
+ return ofun;
+}
+
+static int
+access_flags_to_smbopen_mode(const int access_flags)
+{
+ int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
+
+ if (masked_flags == GENERIC_READ)
+ return SMBOPEN_READ;
+ else if (masked_flags == GENERIC_WRITE)
+ return SMBOPEN_WRITE;
+
+ /* just go for read/write */
+ return SMBOPEN_READWRITE;
+}
+
+int
+SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, const int openDisposition,
+ const int access_flags, const int create_options, __u16 *netfid,
+ int *pOplock, FILE_ALL_INFO *pfile_info,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = -EACCES;
+ OPENX_REQ *pSMB = NULL;
+ OPENX_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+ __u16 count;
+
+OldOpenRetry:
+ rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ count = 1; /* account for one byte pad to word boundary */
+ name_len =
+ cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1),
+ fileName, PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ count = 0; /* no pad */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ if (*pOplock & REQ_OPLOCK)
+ pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
+ else if (*pOplock & REQ_BATCHOPLOCK)
+ pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
+
+ pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
+ pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
+ pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
+ /* set file as system file if special file such
+ as fifo and server expecting SFU style and
+ no Unix extensions */
+
+ if (create_options & CREATE_OPTION_SPECIAL)
+ pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
+ else /* BB FIXME BB */
+ pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
+
+ if (create_options & CREATE_OPTION_READONLY)
+ pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
+
+ /* BB FIXME BB */
+/* pSMB->CreateOptions = cpu_to_le32(create_options &
+ CREATE_OPTIONS_MASK); */
+ /* BB FIXME END BB */
+
+ pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
+ pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
+ count += name_len;
+ inc_rfc1001_len(pSMB, count);
+
+ pSMB->ByteCount = cpu_to_le16(count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *)pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);
+ if (rc) {
+ cifs_dbg(FYI, "Error in Open = %d\n", rc);
+ } else {
+ /* BB verify if wct == 15 */
+
+/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
+
+ *netfid = pSMBr->Fid; /* cifs fid stays in le */
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ /* BB FIXME BB */
+/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
+ *pOplock |= CIFS_CREATE_ACTION; */
+ /* BB FIXME END */
+
+ if (pfile_info) {
+ pfile_info->CreationTime = 0; /* BB convert CreateTime*/
+ pfile_info->LastAccessTime = 0; /* BB fixme */
+ pfile_info->LastWriteTime = 0; /* BB fixme */
+ pfile_info->ChangeTime = 0; /* BB fixme */
+ pfile_info->Attributes =
+ cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
+ /* the file_info buf is endian converted by caller */
+ pfile_info->AllocationSize =
+ cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
+ pfile_info->EndOfFile = pfile_info->AllocationSize;
+ pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
+ }
+ }
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto OldOpenRetry;
+ return rc;
+}
+
+int
+CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
+ FILE_ALL_INFO *buf)
+{
+ int rc = -EACCES;
+ OPEN_REQ *req = NULL;
+ OPEN_RSP *rsp = NULL;
+ int bytes_returned;
+ int name_len;
+ __u16 count;
+ struct cifs_sb_info *cifs_sb = oparms->cifs_sb;
+ struct cifs_tcon *tcon = oparms->tcon;
+ int remap = cifs_remap(cifs_sb);
+ const struct nls_table *nls = cifs_sb->local_nls;
+ int create_options = oparms->create_options;
+ int desired_access = oparms->desired_access;
+ int disposition = oparms->disposition;
+ const char *path = oparms->path;
+
+openRetry:
+ rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req,
+ (void **)&rsp);
+ if (rc)
+ return rc;
+
+ /* no commands go after this */
+ req->AndXCommand = 0xFF;
+
+ if (req->hdr.Flags2 & SMBFLG2_UNICODE) {
+ /* account for one byte pad to word boundary */
+ count = 1;
+ name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1),
+ path, PATH_MAX, nls, remap);
+ /* trailing null */
+ name_len++;
+ name_len *= 2;
+ req->NameLength = cpu_to_le16(name_len);
+ } else {
+ /* BB improve check for buffer overruns BB */
+ /* no pad */
+ count = 0;
+ name_len = strnlen(path, PATH_MAX);
+ /* trailing null */
+ name_len++;
+ req->NameLength = cpu_to_le16(name_len);
+ strncpy(req->fileName, path, name_len);
+ }
+
+ if (*oplock & REQ_OPLOCK)
+ req->OpenFlags = cpu_to_le32(REQ_OPLOCK);
+ else if (*oplock & REQ_BATCHOPLOCK)
+ req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
+
+ req->DesiredAccess = cpu_to_le32(desired_access);
+ req->AllocationSize = 0;
+
+ /*
+ * Set file as system file if special file such as fifo and server
+ * expecting SFU style and no Unix extensions.
+ */
+ if (create_options & CREATE_OPTION_SPECIAL)
+ req->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
+ else
+ req->FileAttributes = cpu_to_le32(ATTR_NORMAL);
+
+ /*
+ * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case
+ * sensitive checks for other servers such as Samba.
+ */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
+
+ if (create_options & CREATE_OPTION_READONLY)
+ req->FileAttributes |= cpu_to_le32(ATTR_READONLY);
+
+ req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
+ req->CreateDisposition = cpu_to_le32(disposition);
+ req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
+
+ /* BB Expirement with various impersonation levels and verify */
+ req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
+ req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;
+
+ count += name_len;
+ inc_rfc1001_len(req, count);
+
+ req->ByteCount = cpu_to_le16(count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req,
+ (struct smb_hdr *)rsp, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);
+ if (rc) {
+ cifs_dbg(FYI, "Error in Open = %d\n", rc);
+ cifs_buf_release(req);
+ if (rc == -EAGAIN)
+ goto openRetry;
+ return rc;
+ }
+
+ /* 1 byte no need to le_to_cpu */
+ *oplock = rsp->OplockLevel;
+ /* cifs fid stays in le */
+ oparms->fid->netfid = rsp->Fid;
+
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction)
+ *oplock |= CIFS_CREATE_ACTION;
+
+ if (buf) {
+ /* copy from CreationTime to Attributes */
+ memcpy((char *)buf, (char *)&rsp->CreationTime, 36);
+ /* the file_info buf is endian converted by caller */
+ buf->AllocationSize = rsp->AllocationSize;
+ buf->EndOfFile = rsp->EndOfFile;
+ buf->NumberOfLinks = cpu_to_le32(1);
+ buf->DeletePending = 0;
+ }
+
+ cifs_buf_release(req);
+ return rc;
+}
+
+/*
+ * Discard any remaining data in the current SMB. To do this, we borrow the
+ * current bigbuf.
+ */
+int
+cifs_discard_remaining_data(struct TCP_Server_Info *server)
+{
+ unsigned int rfclen = server->pdu_size;
+ int remaining = rfclen + server->vals->header_preamble_size -
+ server->total_read;
+
+ while (remaining > 0) {
+ int length;
+
+ length = cifs_read_from_socket(server, server->bigbuf,
+ min_t(unsigned int, remaining,
+ CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
+ if (length < 0)
+ return length;
+ server->total_read += length;
+ remaining -= length;
+ }
+
+ return 0;
+}
+
+static int
+__cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid,
+ bool malformed)
+{
+ int length;
+
+ length = cifs_discard_remaining_data(server);
+ dequeue_mid(mid, malformed);
+ mid->resp_buf = server->smallbuf;
+ server->smallbuf = NULL;
+ return length;
+}
+
+static int
+cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ struct cifs_readdata *rdata = mid->callback_data;
+
+ return __cifs_readv_discard(server, mid, rdata->result);
+}
+
+int
+cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ int length, len;
+ unsigned int data_offset, data_len;
+ struct cifs_readdata *rdata = mid->callback_data;
+ char *buf = server->smallbuf;
+ unsigned int buflen = server->pdu_size +
+ server->vals->header_preamble_size;
+ bool use_rdma_mr = false;
+
+ cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n",
+ __func__, mid->mid, rdata->offset, rdata->bytes);
+
+ /*
+ * read the rest of READ_RSP header (sans Data array), or whatever we
+ * can if there's not enough data. At this point, we've read down to
+ * the Mid.
+ */
+ len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
+ HEADER_SIZE(server) + 1;
+
+ length = cifs_read_from_socket(server,
+ buf + HEADER_SIZE(server) - 1, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+
+ if (server->ops->is_session_expired &&
+ server->ops->is_session_expired(buf)) {
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -1;
+ }
+
+ if (server->ops->is_status_pending &&
+ server->ops->is_status_pending(buf, server, 0)) {
+ cifs_discard_remaining_data(server);
+ return -1;
+ }
+
+ /* set up first two iov for signature check and to get credits */
+ rdata->iov[0].iov_base = buf;
+ rdata->iov[0].iov_len = server->vals->header_preamble_size;
+ rdata->iov[1].iov_base = buf + server->vals->header_preamble_size;
+ rdata->iov[1].iov_len =
+ server->total_read - server->vals->header_preamble_size;
+ cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
+ rdata->iov[0].iov_base, rdata->iov[0].iov_len);
+ cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
+ rdata->iov[1].iov_base, rdata->iov[1].iov_len);
+
+ /* Was the SMB read successful? */
+ rdata->result = server->ops->map_error(buf, false);
+ if (rdata->result != 0) {
+ cifs_dbg(FYI, "%s: server returned error %d\n",
+ __func__, rdata->result);
+ /* normal error on read response */
+ return __cifs_readv_discard(server, mid, false);
+ }
+
+ /* Is there enough to get to the rest of the READ_RSP header? */
+ if (server->total_read < server->vals->read_rsp_size) {
+ cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n",
+ __func__, server->total_read,
+ server->vals->read_rsp_size);
+ rdata->result = -EIO;
+ return cifs_readv_discard(server, mid);
+ }
+
+ data_offset = server->ops->read_data_offset(buf) +
+ server->vals->header_preamble_size;
+ if (data_offset < server->total_read) {
+ /*
+ * win2k8 sometimes sends an offset of 0 when the read
+ * is beyond the EOF. Treat it as if the data starts just after
+ * the header.
+ */
+ cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
+ __func__, data_offset);
+ data_offset = server->total_read;
+ } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
+ /* data_offset is beyond the end of smallbuf */
+ cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
+ __func__, data_offset);
+ rdata->result = -EIO;
+ return cifs_readv_discard(server, mid);
+ }
+
+ cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n",
+ __func__, server->total_read, data_offset);
+
+ len = data_offset - server->total_read;
+ if (len > 0) {
+ /* read any junk before data into the rest of smallbuf */
+ length = cifs_read_from_socket(server,
+ buf + server->total_read, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+ }
+
+ /* how much data is in the response? */
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ use_rdma_mr = rdata->mr;
+#endif
+ data_len = server->ops->read_data_length(buf, use_rdma_mr);
+ if (!use_rdma_mr && (data_offset + data_len > buflen)) {
+ /* data_len is corrupt -- discard frame */
+ rdata->result = -EIO;
+ return cifs_readv_discard(server, mid);
+ }
+
+ length = rdata->read_into_pages(server, rdata, data_len);
+ if (length < 0)
+ return length;
+
+ server->total_read += length;
+
+ cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n",
+ server->total_read, buflen, data_len);
+
+ /* discard anything left over */
+ if (server->total_read < buflen)
+ return cifs_readv_discard(server, mid);
+
+ dequeue_mid(mid, false);
+ mid->resp_buf = server->smallbuf;
+ server->smallbuf = NULL;
+ return length;
+}
+
+static void
+cifs_readv_callback(struct mid_q_entry *mid)
+{
+ struct cifs_readdata *rdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct smb_rqst rqst = { .rq_iov = rdata->iov,
+ .rq_nvec = 2,
+ .rq_pages = rdata->pages,
+ .rq_npages = rdata->nr_pages,
+ .rq_pagesz = rdata->pagesz,
+ .rq_tailsz = rdata->tailsz };
+
+ cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
+ __func__, mid->mid, mid->mid_state, rdata->result,
+ rdata->bytes);
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ /* result already set, check signature */
+ if (server->sign) {
+ int rc = 0;
+
+ rc = cifs_verify_signature(&rqst, server,
+ mid->sequence_number);
+ if (rc)
+ cifs_dbg(VFS, "SMB signature verification returned error = %d\n",
+ rc);
+ }
+ /* FIXME: should this be counted toward the initiating task? */
+ task_io_account_read(rdata->got_bytes);
+ cifs_stats_bytes_read(tcon, rdata->got_bytes);
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ rdata->result = -EAGAIN;
+ if (server->sign && rdata->got_bytes)
+ /* reset bytes number since we can not check a sign */
+ rdata->got_bytes = 0;
+ /* FIXME: should this be counted toward the initiating task? */
+ task_io_account_read(rdata->got_bytes);
+ cifs_stats_bytes_read(tcon, rdata->got_bytes);
+ break;
+ default:
+ rdata->result = -EIO;
+ }
+
+ queue_work(cifsiod_wq, &rdata->work);
+ DeleteMidQEntry(mid);
+ add_credits(server, 1, 0);
+}
+
+/* cifs_async_readv - send an async write, and set up mid to handle result */
+int
+cifs_async_readv(struct cifs_readdata *rdata)
+{
+ int rc;
+ READ_REQ *smb = NULL;
+ int wct;
+ struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+ struct smb_rqst rqst = { .rq_iov = rdata->iov,
+ .rq_nvec = 2 };
+
+ cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
+ __func__, rdata->offset, rdata->bytes);
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 12;
+ else {
+ wct = 10; /* old style read */
+ if ((rdata->offset >> 32) > 0) {
+ /* can not handle this big offset for old */
+ return -EIO;
+ }
+ }
+
+ rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb);
+ if (rc)
+ return rc;
+
+ smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid);
+ smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
+
+ smb->AndXCommand = 0xFF; /* none */
+ smb->Fid = rdata->cfile->fid.netfid;
+ smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
+ if (wct == 12)
+ smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
+ smb->Remaining = 0;
+ smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF);
+ smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16);
+ if (wct == 12)
+ smb->ByteCount = 0;
+ else {
+ /* old style read */
+ struct smb_com_readx_req *smbr =
+ (struct smb_com_readx_req *)smb;
+ smbr->ByteCount = 0;
+ }
+
+ /* 4 for RFC1001 length + 1 for BCC */
+ rdata->iov[0].iov_base = smb;
+ rdata->iov[0].iov_len = 4;
+ rdata->iov[1].iov_base = (char *)smb + 4;
+ rdata->iov[1].iov_len = get_rfc1002_length(smb);
+
+ kref_get(&rdata->refcount);
+ rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
+ cifs_readv_callback, NULL, rdata, 0);
+
+ if (rc == 0)
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
+ else
+ kref_put(&rdata->refcount, cifs_readdata_release);
+
+ cifs_small_buf_release(smb);
+ return rc;
+}
+
+int
+CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, char **buf, int *pbuf_type)
+{
+ int rc = -EACCES;
+ READ_REQ *pSMB = NULL;
+ READ_RSP *pSMBr = NULL;
+ char *pReadData = NULL;
+ int wct;
+ int resp_buf_type = 0;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ __u32 pid = io_parms->pid;
+ __u16 netfid = io_parms->netfid;
+ __u64 offset = io_parms->offset;
+ struct cifs_tcon *tcon = io_parms->tcon;
+ unsigned int count = io_parms->length;
+
+ cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid);
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 12;
+ else {
+ wct = 10; /* old style read */
+ if ((offset >> 32) > 0) {
+ /* can not handle this big offset for old */
+ return -EIO;
+ }
+ }
+
+ *nbytes = 0;
+ rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ if (wct == 12)
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+
+ pSMB->Remaining = 0;
+ pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
+ pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
+ if (wct == 12)
+ pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
+ else {
+ /* old style read */
+ struct smb_com_readx_req *pSMBW =
+ (struct smb_com_readx_req *)pSMB;
+ pSMBW->ByteCount = 0;
+ }
+
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
+ rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type,
+ CIFS_LOG_ERROR, &rsp_iov);
+ cifs_small_buf_release(pSMB);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
+ pSMBr = (READ_RSP *)rsp_iov.iov_base;
+ if (rc) {
+ cifs_dbg(VFS, "Send error in read = %d\n", rc);
+ } else {
+ int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
+ data_length = data_length << 16;
+ data_length += le16_to_cpu(pSMBr->DataLength);
+ *nbytes = data_length;
+
+ /*check that DataLength would not go beyond end of SMB */
+ if ((data_length > CIFSMaxBufSize)
+ || (data_length > count)) {
+ cifs_dbg(FYI, "bad length %d for count %d\n",
+ data_length, count);
+ rc = -EIO;
+ *nbytes = 0;
+ } else {
+ pReadData = (char *) (&pSMBr->hdr.Protocol) +
+ le16_to_cpu(pSMBr->DataOffset);
+/* if (rc = copy_to_user(buf, pReadData, data_length)) {
+ cifs_dbg(VFS, "Faulting on read rc = %d\n",rc);
+ rc = -EFAULT;
+ }*/ /* can not use copy_to_user when using page cache*/
+ if (*buf)
+ memcpy(*buf, pReadData, data_length);
+ }
+ }
+
+ if (*buf) {
+ free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
+ } else if (resp_buf_type != CIFS_NO_BUFFER) {
+ /* return buffer to caller to free */
+ *buf = rsp_iov.iov_base;
+ if (resp_buf_type == CIFS_SMALL_BUFFER)
+ *pbuf_type = CIFS_SMALL_BUFFER;
+ else if (resp_buf_type == CIFS_LARGE_BUFFER)
+ *pbuf_type = CIFS_LARGE_BUFFER;
+ } /* else no valid buffer on return - leave as null */
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+ return rc;
+}
+
+
+int
+CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, const char *buf)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ WRITE_RSP *pSMBr = NULL;
+ int bytes_returned, wct;
+ __u32 bytes_sent;
+ __u16 byte_count;
+ __u32 pid = io_parms->pid;
+ __u16 netfid = io_parms->netfid;
+ __u64 offset = io_parms->offset;
+ struct cifs_tcon *tcon = io_parms->tcon;
+ unsigned int count = io_parms->length;
+
+ *nbytes = 0;
+
+ /* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/
+ if (tcon->ses == NULL)
+ return -ECONNABORTED;
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 14;
+ else {
+ wct = 12;
+ if ((offset >> 32) > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
+
+ rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ if (wct == 14)
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+
+ pSMB->Reserved = 0xFFFFFFFF;
+ pSMB->WriteMode = 0;
+ pSMB->Remaining = 0;
+
+ /* Can increase buffer size if buffer is big enough in some cases ie we
+ can send more if LARGE_WRITE_X capability returned by the server and if
+ our buffer is big enough or if we convert to iovecs on socket writes
+ and eliminate the copy to the CIFS buffer */
+ if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
+ bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
+ } else {
+ bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
+ & ~0xFF;
+ }
+
+ if (bytes_sent > count)
+ bytes_sent = count;
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+ if (buf)
+ memcpy(pSMB->Data, buf, bytes_sent);
+ else if (count != 0) {
+ /* No buffer */
+ cifs_buf_release(pSMB);
+ return -EINVAL;
+ } /* else setting file size with write of zero bytes */
+ if (wct == 14)
+ byte_count = bytes_sent + 1; /* pad */
+ else /* wct == 12 */
+ byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
+
+ pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
+ pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
+ inc_rfc1001_len(pSMB, byte_count);
+
+ if (wct == 14)
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ else { /* old style write has byte count 4 bytes earlier
+ so 4 bytes pad */
+ struct smb_com_writex_req *pSMBW =
+ (struct smb_com_writex_req *)pSMB;
+ pSMBW->ByteCount = cpu_to_le16(byte_count);
+ }
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in write = %d\n", rc);
+ } else {
+ *nbytes = le16_to_cpu(pSMBr->CountHigh);
+ *nbytes = (*nbytes) << 16;
+ *nbytes += le16_to_cpu(pSMBr->Count);
+
+ /*
+ * Mask off high 16 bits when bytes written as returned by the
+ * server is greater than bytes requested by the client. Some
+ * OS/2 servers are known to set incorrect CountHigh values.
+ */
+ if (*nbytes > count)
+ *nbytes &= 0xFFFF;
+ }
+
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+void
+cifs_writedata_release(struct kref *refcount)
+{
+ struct cifs_writedata *wdata = container_of(refcount,
+ struct cifs_writedata, refcount);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ if (wdata->mr) {
+ smbd_deregister_mr(wdata->mr);
+ wdata->mr = NULL;
+ }
+#endif
+
+ if (wdata->cfile)
+ cifsFileInfo_put(wdata->cfile);
+
+ kvfree(wdata->pages);
+ kfree(wdata);
+}
+
+/*
+ * Write failed with a retryable error. Resend the write request. It's also
+ * possible that the page was redirtied so re-clean the page.
+ */
+static void
+cifs_writev_requeue(struct cifs_writedata *wdata)
+{
+ int i, rc = 0;
+ struct inode *inode = d_inode(wdata->cfile->dentry);
+ struct TCP_Server_Info *server;
+ unsigned int rest_len;
+
+ server = tlink_tcon(wdata->cfile->tlink)->ses->server;
+ i = 0;
+ rest_len = wdata->bytes;
+ do {
+ struct cifs_writedata *wdata2;
+ unsigned int j, nr_pages, wsize, tailsz, cur_len;
+
+ wsize = server->ops->wp_retry_size(inode);
+ if (wsize < rest_len) {
+ nr_pages = wsize / PAGE_SIZE;
+ if (!nr_pages) {
+ rc = -ENOTSUPP;
+ break;
+ }
+ cur_len = nr_pages * PAGE_SIZE;
+ tailsz = PAGE_SIZE;
+ } else {
+ nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE);
+ cur_len = rest_len;
+ tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE;
+ }
+
+ wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete);
+ if (!wdata2) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ for (j = 0; j < nr_pages; j++) {
+ wdata2->pages[j] = wdata->pages[i + j];
+ lock_page(wdata2->pages[j]);
+ clear_page_dirty_for_io(wdata2->pages[j]);
+ }
+
+ wdata2->sync_mode = wdata->sync_mode;
+ wdata2->nr_pages = nr_pages;
+ wdata2->offset = page_offset(wdata2->pages[0]);
+ wdata2->pagesz = PAGE_SIZE;
+ wdata2->tailsz = tailsz;
+ wdata2->bytes = cur_len;
+
+ wdata2->cfile = find_writable_file(CIFS_I(inode), false);
+ if (!wdata2->cfile) {
+ cifs_dbg(VFS, "No writable handle to retry writepages\n");
+ rc = -EBADF;
+ } else {
+ wdata2->pid = wdata2->cfile->pid;
+ rc = server->ops->async_writev(wdata2,
+ cifs_writedata_release);
+ }
+
+ for (j = 0; j < nr_pages; j++) {
+ unlock_page(wdata2->pages[j]);
+ if (rc != 0 && !is_retryable_error(rc)) {
+ SetPageError(wdata2->pages[j]);
+ end_page_writeback(wdata2->pages[j]);
+ put_page(wdata2->pages[j]);
+ }
+ }
+
+ kref_put(&wdata2->refcount, cifs_writedata_release);
+ if (rc) {
+ if (is_retryable_error(rc))
+ continue;
+ i += nr_pages;
+ break;
+ }
+
+ rest_len -= cur_len;
+ i += nr_pages;
+ } while (i < wdata->nr_pages);
+
+ /* cleanup remaining pages from the original wdata */
+ for (; i < wdata->nr_pages; i++) {
+ SetPageError(wdata->pages[i]);
+ end_page_writeback(wdata->pages[i]);
+ put_page(wdata->pages[i]);
+ }
+
+ if (rc != 0 && !is_retryable_error(rc))
+ mapping_set_error(inode->i_mapping, rc);
+ kref_put(&wdata->refcount, cifs_writedata_release);
+}
+
+void
+cifs_writev_complete(struct work_struct *work)
+{
+ struct cifs_writedata *wdata = container_of(work,
+ struct cifs_writedata, work);
+ struct inode *inode = d_inode(wdata->cfile->dentry);
+ int i = 0;
+
+ if (wdata->result == 0) {
+ spin_lock(&inode->i_lock);
+ cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes);
+ spin_unlock(&inode->i_lock);
+ cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink),
+ wdata->bytes);
+ } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN)
+ return cifs_writev_requeue(wdata);
+
+ for (i = 0; i < wdata->nr_pages; i++) {
+ struct page *page = wdata->pages[i];
+ if (wdata->result == -EAGAIN)
+ __set_page_dirty_nobuffers(page);
+ else if (wdata->result < 0)
+ SetPageError(page);
+ end_page_writeback(page);
+ put_page(page);
+ }
+ if (wdata->result != -EAGAIN)
+ mapping_set_error(inode->i_mapping, wdata->result);
+ kref_put(&wdata->refcount, cifs_writedata_release);
+}
+
+struct cifs_writedata *
+cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
+{
+ struct page **pages =
+ kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
+ if (pages)
+ return cifs_writedata_direct_alloc(pages, complete);
+
+ return NULL;
+}
+
+struct cifs_writedata *
+cifs_writedata_direct_alloc(struct page **pages, work_func_t complete)
+{
+ struct cifs_writedata *wdata;
+
+ wdata = kzalloc(sizeof(*wdata), GFP_NOFS);
+ if (wdata != NULL) {
+ wdata->pages = pages;
+ kref_init(&wdata->refcount);
+ INIT_LIST_HEAD(&wdata->list);
+ init_completion(&wdata->done);
+ INIT_WORK(&wdata->work, complete);
+ }
+ return wdata;
+}
+
+/*
+ * Check the mid_state and signature on received buffer (if any), and queue the
+ * workqueue completion task.
+ */
+static void
+cifs_writev_callback(struct mid_q_entry *mid)
+{
+ struct cifs_writedata *wdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ unsigned int written;
+ WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ wdata->result = cifs_check_receive(mid, tcon->ses->server, 0);
+ if (wdata->result != 0)
+ break;
+
+ written = le16_to_cpu(smb->CountHigh);
+ written <<= 16;
+ written += le16_to_cpu(smb->Count);
+ /*
+ * Mask off high 16 bits when bytes written as returned
+ * by the server is greater than bytes requested by the
+ * client. OS/2 servers are known to set incorrect
+ * CountHigh values.
+ */
+ if (written > wdata->bytes)
+ written &= 0xFFFF;
+
+ if (written < wdata->bytes)
+ wdata->result = -ENOSPC;
+ else
+ wdata->bytes = written;
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ wdata->result = -EAGAIN;
+ break;
+ default:
+ wdata->result = -EIO;
+ break;
+ }
+
+ queue_work(cifsiod_wq, &wdata->work);
+ DeleteMidQEntry(mid);
+ add_credits(tcon->ses->server, 1, 0);
+}
+
+/* cifs_async_writev - send an async write, and set up mid to handle result */
+int
+cifs_async_writev(struct cifs_writedata *wdata,
+ void (*release)(struct kref *kref))
+{
+ int rc = -EACCES;
+ WRITE_REQ *smb = NULL;
+ int wct;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ struct kvec iov[2];
+ struct smb_rqst rqst = { };
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES) {
+ wct = 14;
+ } else {
+ wct = 12;
+ if (wdata->offset >> 32 > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
+
+ rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb);
+ if (rc)
+ goto async_writev_out;
+
+ smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
+ smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
+
+ smb->AndXCommand = 0xFF; /* none */
+ smb->Fid = wdata->cfile->fid.netfid;
+ smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF);
+ if (wct == 14)
+ smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32);
+ smb->Reserved = 0xFFFFFFFF;
+ smb->WriteMode = 0;
+ smb->Remaining = 0;
+
+ smb->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+
+ /* 4 for RFC1001 length + 1 for BCC */
+ iov[0].iov_len = 4;
+ iov[0].iov_base = smb;
+ iov[1].iov_len = get_rfc1002_length(smb) + 1;
+ iov[1].iov_base = (char *)smb + 4;
+
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+ rqst.rq_pages = wdata->pages;
+ rqst.rq_npages = wdata->nr_pages;
+ rqst.rq_pagesz = wdata->pagesz;
+ rqst.rq_tailsz = wdata->tailsz;
+
+ cifs_dbg(FYI, "async write at %llu %u bytes\n",
+ wdata->offset, wdata->bytes);
+
+ smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF);
+ smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16);
+
+ if (wct == 14) {
+ inc_rfc1001_len(&smb->hdr, wdata->bytes + 1);
+ put_bcc(wdata->bytes + 1, &smb->hdr);
+ } else {
+ /* wct == 12 */
+ struct smb_com_writex_req *smbw =
+ (struct smb_com_writex_req *)smb;
+ inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5);
+ put_bcc(wdata->bytes + 5, &smbw->hdr);
+ iov[1].iov_len += 4; /* pad bigger by four bytes */
+ }
+
+ kref_get(&wdata->refcount);
+ rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
+ cifs_writev_callback, NULL, wdata, 0);
+
+ if (rc == 0)
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
+ else
+ kref_put(&wdata->refcount, release);
+
+async_writev_out:
+ cifs_small_buf_release(smb);
+ return rc;
+}
+
+int
+CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, struct kvec *iov, int n_vec)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ int wct;
+ int smb_hdr_len;
+ int resp_buf_type = 0;
+ __u32 pid = io_parms->pid;
+ __u16 netfid = io_parms->netfid;
+ __u64 offset = io_parms->offset;
+ struct cifs_tcon *tcon = io_parms->tcon;
+ unsigned int count = io_parms->length;
+ struct kvec rsp_iov;
+
+ *nbytes = 0;
+
+ cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count);
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES) {
+ wct = 14;
+ } else {
+ wct = 12;
+ if ((offset >> 32) > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
+ rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
+
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ if (wct == 14)
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+ pSMB->Reserved = 0xFFFFFFFF;
+ pSMB->WriteMode = 0;
+ pSMB->Remaining = 0;
+
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+
+ pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
+ pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
+ /* header + 1 byte pad */
+ smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1;
+ if (wct == 14)
+ inc_rfc1001_len(pSMB, count + 1);
+ else /* wct == 12 */
+ inc_rfc1001_len(pSMB, count + 5); /* smb data starts later */
+ if (wct == 14)
+ pSMB->ByteCount = cpu_to_le16(count + 1);
+ else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
+ struct smb_com_writex_req *pSMBW =
+ (struct smb_com_writex_req *)pSMB;
+ pSMBW->ByteCount = cpu_to_le16(count + 5);
+ }
+ iov[0].iov_base = pSMB;
+ if (wct == 14)
+ iov[0].iov_len = smb_hdr_len + 4;
+ else /* wct == 12 pad bigger by four bytes */
+ iov[0].iov_len = smb_hdr_len + 8;
+
+ rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0,
+ &rsp_iov);
+ cifs_small_buf_release(pSMB);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
+ if (rc) {
+ cifs_dbg(FYI, "Send error Write2 = %d\n", rc);
+ } else if (resp_buf_type == 0) {
+ /* presumably this can not happen, but best to be safe */
+ rc = -EIO;
+ } else {
+ WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base;
+ *nbytes = le16_to_cpu(pSMBr->CountHigh);
+ *nbytes = (*nbytes) << 16;
+ *nbytes += le16_to_cpu(pSMBr->Count);
+
+ /*
+ * Mask off high 16 bits when bytes written as returned by the
+ * server is greater than bytes requested by the client. OS/2
+ * servers are known to set incorrect CountHigh values.
+ */
+ if (*nbytes > count)
+ *nbytes &= 0xFFFF;
+ }
+
+ free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 netfid, const __u8 lock_type, const __u32 num_unlock,
+ const __u32 num_lock, LOCKING_ANDX_RANGE *buf)
+{
+ int rc = 0;
+ LOCK_REQ *pSMB = NULL;
+ struct kvec iov[2];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+ __u16 count;
+
+ cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n",
+ num_lock, num_unlock);
+
+ rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->Timeout = 0;
+ pSMB->NumberOfLocks = cpu_to_le16(num_lock);
+ pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock);
+ pSMB->LockType = lock_type;
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid; /* netfid stays le */
+
+ count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 -
+ (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
+ rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP,
+ &rsp_iov);
+ cifs_small_buf_release(pSMB);
+ if (rc)
+ cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc);
+
+ return rc;
+}
+
+int
+CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 smb_file_id, const __u32 netpid, const __u64 len,
+ const __u64 offset, const __u32 numUnlock,
+ const __u32 numLock, const __u8 lockType,
+ const bool waitFlag, const __u8 oplock_level)
+{
+ int rc = 0;
+ LOCK_REQ *pSMB = NULL;
+/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
+ int bytes_returned;
+ int flags = 0;
+ __u16 count;
+
+ cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n",
+ (int)waitFlag, numLock);
+ rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
+ /* no response expected */
+ flags = CIFS_ASYNC_OP | CIFS_OBREAK_OP;
+ pSMB->Timeout = 0;
+ } else if (waitFlag) {
+ flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
+ pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
+ } else {
+ pSMB->Timeout = 0;
+ }
+
+ pSMB->NumberOfLocks = cpu_to_le16(numLock);
+ pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
+ pSMB->LockType = lockType;
+ pSMB->OplockLevel = oplock_level;
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = smb_file_id; /* netfid stays le */
+
+ if ((numLock != 0) || (numUnlock != 0)) {
+ pSMB->Locks[0].Pid = cpu_to_le16(netpid);
+ /* BB where to store pid high? */
+ pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
+ pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
+ pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
+ pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
+ count = sizeof(LOCKING_ANDX_RANGE);
+ } else {
+ /* oplock break */
+ count = 0;
+ }
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ if (waitFlag)
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMB, &bytes_returned);
+ else
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags);
+ cifs_small_buf_release(pSMB);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
+ if (rc)
+ cifs_dbg(FYI, "Send error in Lock = %d\n", rc);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+ return rc;
+}
+
+int
+CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 smb_file_id, const __u32 netpid,
+ const loff_t start_offset, const __u64 len,
+ struct file_lock *pLockData, const __u16 lock_type,
+ const bool waitFlag)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
+ struct cifs_posix_lock *parm_data;
+ int rc = 0;
+ int timeout = 0;
+ int bytes_returned = 0;
+ int resp_buf_type = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+
+ cifs_dbg(FYI, "Posix Lock\n");
+
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ count = sizeof(struct cifs_posix_lock);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ if (pLockData)
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+ else
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ parm_data = (struct cifs_posix_lock *)
+ (((char *) &pSMB->hdr.Protocol) + offset);
+
+ parm_data->lock_type = cpu_to_le16(lock_type);
+ if (waitFlag) {
+ timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
+ parm_data->lock_flags = cpu_to_le16(1);
+ pSMB->Timeout = cpu_to_le32(-1);
+ } else
+ pSMB->Timeout = 0;
+
+ parm_data->pid = cpu_to_le32(netpid);
+ parm_data->start = cpu_to_le64(start_offset);
+ parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
+
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = smb_file_id;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned);
+ } else {
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
+ rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
+ &resp_buf_type, timeout, &rsp_iov);
+ pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base;
+ }
+ cifs_small_buf_release(pSMB);
+
+ if (rc) {
+ cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc);
+ } else if (pLockData) {
+ /* lock structure can be returned on get */
+ __u16 data_offset;
+ __u16 data_count;
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) {
+ rc = -EIO; /* bad smb */
+ goto plk_err_exit;
+ }
+ data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ data_count = le16_to_cpu(pSMBr->t2.DataCount);
+ if (data_count < sizeof(struct cifs_posix_lock)) {
+ rc = -EIO;
+ goto plk_err_exit;
+ }
+ parm_data = (struct cifs_posix_lock *)
+ ((char *)&pSMBr->hdr.Protocol + data_offset);
+ if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
+ pLockData->fl_type = F_UNLCK;
+ else {
+ if (parm_data->lock_type ==
+ cpu_to_le16(CIFS_RDLCK))
+ pLockData->fl_type = F_RDLCK;
+ else if (parm_data->lock_type ==
+ cpu_to_le16(CIFS_WRLCK))
+ pLockData->fl_type = F_WRLCK;
+
+ pLockData->fl_start = le64_to_cpu(parm_data->start);
+ pLockData->fl_end = pLockData->fl_start +
+ le64_to_cpu(parm_data->length) - 1;
+ pLockData->fl_pid = -le32_to_cpu(parm_data->pid);
+ }
+ }
+
+plk_err_exit:
+ free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+
+int
+CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
+{
+ int rc = 0;
+ CLOSE_REQ *pSMB = NULL;
+ cifs_dbg(FYI, "In CIFSSMBClose\n");
+
+/* do not retry on dead session on close */
+ rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
+ if (rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMB->FileID = (__u16) smb_file_id;
+ pSMB->LastWriteTime = 0xFFFFFFFF;
+ pSMB->ByteCount = 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_closes);
+ if (rc) {
+ if (rc != -EINTR) {
+ /* EINTR is expected when user ctl-c to kill app */
+ cifs_dbg(VFS, "Send error in Close = %d\n", rc);
+ }
+ }
+
+ /* Since session is dead, file will be closed on server already */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
+{
+ int rc = 0;
+ FLUSH_REQ *pSMB = NULL;
+ cifs_dbg(FYI, "In CIFSSMBFlush\n");
+
+ rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->FileID = (__u16) smb_file_id;
+ pSMB->ByteCount = 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes);
+ if (rc)
+ cifs_dbg(VFS, "Send error in Flush = %d\n", rc);
+
+ return rc;
+}
+
+int
+CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc = 0;
+ RENAME_REQ *pSMB = NULL;
+ RENAME_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+ __u16 count;
+ int remap = cifs_remap(cifs_sb);
+
+ cifs_dbg(FYI, "In CIFSSMBRename\n");
+renameRetry:
+ rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName,
+ from_name, PATH_MAX,
+ cifs_sb->local_nls, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0x04; /* pad */
+ /* protocol requires ASCII signature byte on Unicode string */
+ pSMB->OldFileName[name_len + 1] = 0x00;
+ name_len2 =
+ cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
+ to_name, PATH_MAX, cifs_sb->local_nls,
+ remap);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(from_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, from_name, name_len);
+ name_len2 = strnlen(to_name, PATH_MAX);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ count = 1 /* 1st signature byte */ + name_len + name_len2;
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_renames);
+ if (rc)
+ cifs_dbg(FYI, "Send error in rename = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto renameRetry;
+
+ return rc;
+}
+
+int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
+ int netfid, const char *target_name,
+ const struct nls_table *nls_codepage, int remap)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
+ struct set_file_rename *rename_info;
+ char *data_offset;
+ char dummy_string[30];
+ int rc = 0;
+ int bytes_returned = 0;
+ int len_of_str;
+ __u16 params, param_offset, offset, count, byte_count;
+
+ cifs_dbg(FYI, "Rename to File by handle\n");
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ rename_info = (struct set_file_rename *) data_offset;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ /* construct random name ".cifs_tmp<inodenum><mid>" */
+ rename_info->overwrite = cpu_to_le32(1);
+ rename_info->root_fid = 0;
+ /* unicode only call */
+ if (target_name == NULL) {
+ sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
+ len_of_str =
+ cifsConvertToUTF16((__le16 *)rename_info->target_name,
+ dummy_string, 24, nls_codepage, remap);
+ } else {
+ len_of_str =
+ cifsConvertToUTF16((__le16 *)rename_info->target_name,
+ target_name, PATH_MAX, nls_codepage,
+ remap);
+ }
+ rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
+ count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
+ byte_count += count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->Fid = netfid;
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&pTcon->stats.cifs_stats.num_t2renames);
+ if (rc)
+ cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n",
+ rc);
+
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fromName, const __u16 target_tid, const char *toName,
+ const int flags, const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ COPY_REQ *pSMB = NULL;
+ COPY_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+ __u16 count;
+
+ cifs_dbg(FYI, "In CIFSSMBCopy\n");
+copyRetry:
+ rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->Tid2 = target_tid;
+
+ pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName,
+ fromName, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0x04; /* pad */
+ /* protocol requires ASCII signature byte on Unicode string */
+ pSMB->OldFileName[name_len + 1] = 0x00;
+ name_len2 =
+ cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
+ toName, PATH_MAX, nls_codepage, remap);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, PATH_MAX);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ count = 1 /* 1st signature byte */ + name_len + name_len2;
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n",
+ rc, le16_to_cpu(pSMBr->CopyCount));
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto copyRetry;
+
+ return rc;
+}
+
+int
+CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ char *data_offset;
+ int name_len;
+ int name_len_target;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cifs_dbg(FYI, "In Symlink Unix style\n");
+createSymLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, fromName,
+ /* find define for this maxpathcomponent */
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fromName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len_target =
+ cifsConvertToUTF16((__le16 *) data_offset, toName,
+ /* find define for this maxpathcomponent */
+ PATH_MAX, nls_codepage, remap);
+ name_len_target++; /* trailing null */
+ name_len_target *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len_target = strnlen(toName, PATH_MAX);
+ name_len_target++; /* trailing null */
+ strncpy(data_offset, toName, name_len_target);
+ }
+
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max on data count below from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + name_len_target;
+ pSMB->DataCount = cpu_to_le16(name_len_target);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks);
+ if (rc)
+ cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n",
+ rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto createSymLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ char *data_offset;
+ int name_len;
+ int name_len_target;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cifs_dbg(FYI, "In Create Hard link Unix style\n");
+createHardLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(toName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, toName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len_target =
+ cifsConvertToUTF16((__le16 *) data_offset, fromName,
+ PATH_MAX, nls_codepage, remap);
+ name_len_target++; /* trailing null */
+ name_len_target *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len_target = strnlen(fromName, PATH_MAX);
+ name_len_target++; /* trailing null */
+ strncpy(data_offset, fromName, name_len_target);
+ }
+
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max on data count below from sess*/
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + name_len_target;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->DataCount = cpu_to_le16(name_len_target);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);
+ if (rc)
+ cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n",
+ rc);
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto createHardLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc = 0;
+ NT_RENAME_REQ *pSMB = NULL;
+ RENAME_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+ __u16 count;
+ int remap = cifs_remap(cifs_sb);
+
+ cifs_dbg(FYI, "In CIFSCreateHardLink\n");
+winCreateHardLinkRetry:
+
+ rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
+ pSMB->ClusterCount = 0;
+
+ pSMB->BufferFormat = 0x04;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name,
+ PATH_MAX, cifs_sb->local_nls, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+
+ /* protocol specifies ASCII buffer format (0x04) for unicode */
+ pSMB->OldFileName[name_len] = 0x04;
+ pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
+ name_len2 =
+ cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
+ to_name, PATH_MAX, cifs_sb->local_nls,
+ remap);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(from_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, from_name, name_len);
+ name_len2 = strnlen(to_name, PATH_MAX);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ count = 1 /* string type byte */ + name_len + name_len2;
+ inc_rfc1001_len(pSMB, count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);
+ if (rc)
+ cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto winCreateHardLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName, char **symlinkinfo,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* SMB_QUERY_FILE_UNIX_LINK */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+ char *data_start;
+
+ cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName);
+
+querySymLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName,
+ searchName, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc);
+ } else {
+ /* decode response */
+
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ /* BB also check enough total bytes returned */
+ if (rc || get_bcc(&pSMBr->hdr) < 2)
+ rc = -EIO;
+ else {
+ bool is_unicode;
+ u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+
+ data_start = ((char *) &pSMBr->hdr.Protocol) +
+ le16_to_cpu(pSMBr->t2.DataOffset);
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ is_unicode = true;
+ else
+ is_unicode = false;
+
+ /* BB FIXME investigate remapping reserved chars here */
+ *symlinkinfo = cifs_strndup_from_utf16(data_start,
+ count, is_unicode, nls_codepage);
+ if (!*symlinkinfo)
+ rc = -ENOMEM;
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto querySymLinkRetry;
+ return rc;
+}
+
+/*
+ * Recent Windows versions now create symlinks more frequently
+ * and they use the "reparse point" mechanism below. We can of course
+ * do symlinks nicely to Samba and other servers which support the
+ * CIFS Unix Extensions and we can also do SFU symlinks and "client only"
+ * "MF" symlinks optionally, but for recent Windows we really need to
+ * reenable the code below and fix the cifs_symlink callers to handle this.
+ * In the interim this code has been moved to its own config option so
+ * it is not compiled in by default until callers fixed up and more tested.
+ */
+int
+CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
+ __u16 fid, char **symlinkinfo,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ int bytes_returned;
+ struct smb_com_transaction_ioctl_req *pSMB;
+ struct smb_com_transaction_ioctl_rsp *pSMBr;
+ bool is_unicode;
+ unsigned int sub_len;
+ char *sub_start;
+ struct reparse_symlink_data *reparse_buf;
+ struct reparse_posix_data *posix_buf;
+ __u32 data_offset, data_count;
+ char *end_of_smb;
+
+ cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ /* BB find exact data count max from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4;
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
+ pSMB->IsFsctl = 1; /* FSCTL */
+ pSMB->IsRootFlag = 0;
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
+ goto qreparse_out;
+ }
+
+ data_offset = le32_to_cpu(pSMBr->DataOffset);
+ data_count = le32_to_cpu(pSMBr->DataCount);
+ if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ goto qreparse_out;
+ }
+ if (!data_count || (data_count > 2048)) {
+ rc = -EIO;
+ cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
+ goto qreparse_out;
+ }
+ end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
+ reparse_buf = (struct reparse_symlink_data *)
+ ((char *)&pSMBr->hdr.Protocol + data_offset);
+ if ((char *)reparse_buf >= end_of_smb) {
+ rc = -EIO;
+ goto qreparse_out;
+ }
+ if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) {
+ cifs_dbg(FYI, "NFS style reparse tag\n");
+ posix_buf = (struct reparse_posix_data *)reparse_buf;
+
+ if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) {
+ cifs_dbg(FYI, "unsupported file type 0x%llx\n",
+ le64_to_cpu(posix_buf->InodeType));
+ rc = -EOPNOTSUPP;
+ goto qreparse_out;
+ }
+ is_unicode = true;
+ sub_len = le16_to_cpu(reparse_buf->ReparseDataLength);
+ if (posix_buf->PathBuffer + sub_len > end_of_smb) {
+ cifs_dbg(FYI, "reparse buf beyond SMB\n");
+ rc = -EIO;
+ goto qreparse_out;
+ }
+ *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer,
+ sub_len, is_unicode, nls_codepage);
+ goto qreparse_out;
+ } else if (reparse_buf->ReparseTag !=
+ cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) {
+ rc = -EOPNOTSUPP;
+ goto qreparse_out;
+ }
+
+ /* Reparse tag is NTFS symlink */
+ sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) +
+ reparse_buf->PathBuffer;
+ sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength);
+ if (sub_start + sub_len > end_of_smb) {
+ cifs_dbg(FYI, "reparse buf beyond SMB\n");
+ rc = -EIO;
+ goto qreparse_out;
+ }
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ is_unicode = true;
+ else
+ is_unicode = false;
+
+ /* BB FIXME investigate remapping reserved chars here */
+ *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode,
+ nls_codepage);
+ if (!*symlinkinfo)
+ rc = -ENOMEM;
+qreparse_out:
+ cifs_buf_release(pSMB);
+
+ /*
+ * Note: On -EAGAIN error only caller can retry on handle based calls
+ * since file handle passed in no longer valid.
+ */
+ return rc;
+}
+
+int
+CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
+ __u16 fid)
+{
+ int rc = 0;
+ int bytes_returned;
+ struct smb_com_transaction_compr_ioctl_req *pSMB;
+ struct smb_com_transaction_ioctl_rsp *pSMBr;
+
+ cifs_dbg(FYI, "Set compression for %u\n", fid);
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
+
+ pSMB->TotalParameterCount = 0;
+ pSMB->TotalDataCount = cpu_to_le32(2);
+ pSMB->MaxParameterCount = 0;
+ pSMB->MaxDataCount = 0;
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = cpu_to_le32(2);
+ pSMB->DataOffset =
+ cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req,
+ compression_state) - 4); /* 84 */
+ pSMB->SetupCount = 4;
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
+ pSMB->ParameterCount = 0;
+ pSMB->FunctionCode = cpu_to_le32(FSCTL_SET_COMPRESSION);
+ pSMB->IsFsctl = 1; /* FSCTL */
+ pSMB->IsRootFlag = 0;
+ pSMB->Fid = fid; /* file handle always le */
+ /* 3 byte pad, followed by 2 byte compress state */
+ pSMB->ByteCount = cpu_to_le16(5);
+ inc_rfc1001_len(pSMB, 5);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ /*
+ * Note: On -EAGAIN error only caller can retry on handle based calls
+ * since file handle passed in no longer valid.
+ */
+ return rc;
+}
+
+
+#ifdef CONFIG_CIFS_POSIX
+
+/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
+static void cifs_convert_ace(struct posix_acl_xattr_entry *ace,
+ struct cifs_posix_ace *cifs_ace)
+{
+ /* u8 cifs fields do not need le conversion */
+ ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
+ ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
+ ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
+/*
+ cifs_dbg(FYI, "perm %d tag %d id %d\n",
+ ace->e_perm, ace->e_tag, ace->e_id);
+*/
+
+ return;
+}
+
+/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
+static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
+ const int acl_type, const int size_of_data_area)
+{
+ int size = 0;
+ int i;
+ __u16 count;
+ struct cifs_posix_ace *pACE;
+ struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
+ struct posix_acl_xattr_header *local_acl = (void *)trgt;
+
+ if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
+ return -EOPNOTSUPP;
+
+ if (acl_type == ACL_TYPE_ACCESS) {
+ count = le16_to_cpu(cifs_acl->access_entry_count);
+ pACE = &cifs_acl->ace_array[0];
+ size = sizeof(struct cifs_posix_acl);
+ size += sizeof(struct cifs_posix_ace) * count;
+ /* check if we would go beyond end of SMB */
+ if (size_of_data_area < size) {
+ cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n",
+ size_of_data_area, size);
+ return -EINVAL;
+ }
+ } else if (acl_type == ACL_TYPE_DEFAULT) {
+ count = le16_to_cpu(cifs_acl->access_entry_count);
+ size = sizeof(struct cifs_posix_acl);
+ size += sizeof(struct cifs_posix_ace) * count;
+/* skip past access ACEs to get to default ACEs */
+ pACE = &cifs_acl->ace_array[count];
+ count = le16_to_cpu(cifs_acl->default_entry_count);
+ size += sizeof(struct cifs_posix_ace) * count;
+ /* check if we would go beyond end of SMB */
+ if (size_of_data_area < size)
+ return -EINVAL;
+ } else {
+ /* illegal type */
+ return -EINVAL;
+ }
+
+ size = posix_acl_xattr_size(count);
+ if ((buflen == 0) || (local_acl == NULL)) {
+ /* used to query ACL EA size */
+ } else if (size > buflen) {
+ return -ERANGE;
+ } else /* buffer big enough */ {
+ struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1);
+
+ local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
+ for (i = 0; i < count ; i++) {
+ cifs_convert_ace(&ace[i], pACE);
+ pACE++;
+ }
+ }
+ return size;
+}
+
+static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
+ const struct posix_acl_xattr_entry *local_ace)
+{
+ __u16 rc = 0; /* 0 = ACL converted ok */
+
+ cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
+ cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
+ /* BB is there a better way to handle the large uid? */
+ if (local_ace->e_id == cpu_to_le32(-1)) {
+ /* Probably no need to le convert -1 on any arch but can not hurt */
+ cifs_ace->cifs_uid = cpu_to_le64(-1);
+ } else
+ cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
+/*
+ cifs_dbg(FYI, "perm %d tag %d id %d\n",
+ ace->e_perm, ace->e_tag, ace->e_id);
+*/
+ return rc;
+}
+
+/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
+static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
+ const int buflen, const int acl_type)
+{
+ __u16 rc = 0;
+ struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
+ struct posix_acl_xattr_header *local_acl = (void *)pACL;
+ struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1);
+ int count;
+ int i;
+
+ if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
+ return 0;
+
+ count = posix_acl_xattr_count((size_t)buflen);
+ cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n",
+ count, buflen, le32_to_cpu(local_acl->a_version));
+ if (le32_to_cpu(local_acl->a_version) != 2) {
+ cifs_dbg(FYI, "unknown POSIX ACL version %d\n",
+ le32_to_cpu(local_acl->a_version));
+ return 0;
+ }
+ cifs_acl->version = cpu_to_le16(1);
+ if (acl_type == ACL_TYPE_ACCESS) {
+ cifs_acl->access_entry_count = cpu_to_le16(count);
+ cifs_acl->default_entry_count = cpu_to_le16(0xFFFF);
+ } else if (acl_type == ACL_TYPE_DEFAULT) {
+ cifs_acl->default_entry_count = cpu_to_le16(count);
+ cifs_acl->access_entry_count = cpu_to_le16(0xFFFF);
+ } else {
+ cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);
+ return 0;
+ }
+ for (i = 0; i < count; i++) {
+ rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]);
+ if (rc != 0) {
+ /* ACE not converted */
+ break;
+ }
+ }
+ if (rc == 0) {
+ rc = (__u16)(count * sizeof(struct cifs_posix_ace));
+ rc += sizeof(struct cifs_posix_acl);
+ /* BB add check to make sure ACL does not overflow SMB */
+ }
+ return rc;
+}
+
+int
+CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName,
+ char *acl_inf, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* SMB_QUERY_POSIX_ACL */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName);
+
+queryAclRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName,
+ searchName, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->FileName[name_len] = 0;
+ pSMB->FileName[name_len+1] = 0;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_qpi_req,
+ InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", rc);
+ } else {
+ /* decode response */
+
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ /* BB also check enough total bytes returned */
+ if (rc || get_bcc(&pSMBr->hdr) < 2)
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ rc = cifs_copy_posix_acl(acl_inf,
+ (char *)&pSMBr->hdr.Protocol+data_offset,
+ buflen, acl_type, count);
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto queryAclRetry;
+ return rc;
+}
+
+int
+CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *fileName,
+ const char *local_acl, const int buflen,
+ const int acl_type,
+ const struct nls_table *nls_codepage, int remap)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ char *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count, data_count, param_offset, offset;
+
+ cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
+setAclRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB size from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+
+ /* convert to on the wire format for POSIX ACL */
+ data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
+
+ if (data_count == 0) {
+ rc = -EOPNOTSUPP;
+ goto setACLerrorExit;
+ }
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
+ byte_count = 3 /* pad */ + params + data_count;
+ pSMB->DataCount = cpu_to_le16(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc);
+
+setACLerrorExit:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto setAclRetry;
+ return rc;
+}
+
+/* BB fix tabs in this function FIXME BB */
+int
+CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
+ const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
+{
+ int rc = 0;
+ struct smb_t2_qfi_req *pSMB = NULL;
+ struct smb_t2_qfi_rsp *pSMBr = NULL;
+ int bytes_returned;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In GetExtAttr\n");
+ if (tcon == NULL)
+ return -ENODEV;
+
+GetExtAttrRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2 /* level */ + 2 /* fid */;
+ pSMB->t2.TotalDataCount = 0;
+ pSMB->t2.MaxParameterCount = cpu_to_le16(4);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->t2.MaxDataCount = cpu_to_le16(4000);
+ pSMB->t2.MaxSetupCount = 0;
+ pSMB->t2.Reserved = 0;
+ pSMB->t2.Flags = 0;
+ pSMB->t2.Timeout = 0;
+ pSMB->t2.Reserved2 = 0;
+ pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
+ Fid) - 4);
+ pSMB->t2.DataCount = 0;
+ pSMB->t2.DataOffset = 0;
+ pSMB->t2.SetupCount = 1;
+ pSMB->t2.Reserved3 = 0;
+ pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->t2.TotalParameterCount = cpu_to_le16(params);
+ pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
+ pSMB->Pad = 0;
+ pSMB->Fid = netfid;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->t2.ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "error %d in GetExtAttr\n", rc);
+ } else {
+ /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ /* BB also check enough total bytes returned */
+ if (rc || get_bcc(&pSMBr->hdr) < 2)
+ /* If rc should we check for EOPNOSUPP and
+ disable the srvino flag? or in caller? */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ struct file_chattr_info *pfinfo;
+ /* BB Do we need a cast or hash here ? */
+ if (count != 16) {
+ cifs_dbg(FYI, "Illegal size ret in GetExtAttr\n");
+ rc = -EIO;
+ goto GetExtAttrOut;
+ }
+ pfinfo = (struct file_chattr_info *)
+ (data_offset + (char *) &pSMBr->hdr.Protocol);
+ *pExtAttrBits = le64_to_cpu(pfinfo->mode);
+ *pMask = le64_to_cpu(pfinfo->mask);
+ }
+ }
+GetExtAttrOut:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto GetExtAttrRetry;
+ return rc;
+}
+
+#endif /* CONFIG_POSIX */
+
+#ifdef CONFIG_CIFS_ACL
+/*
+ * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that
+ * all NT TRANSACTS that we init here have total parm and data under about 400
+ * bytes (to fit in small cifs buffer size), which is the case so far, it
+ * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
+ * returned setup area) and MaxParameterCount (returned parms size) must be set
+ * by caller
+ */
+static int
+smb_init_nttransact(const __u16 sub_command, const int setup_count,
+ const int parm_len, struct cifs_tcon *tcon,
+ void **ret_buf)
+{
+ int rc;
+ __u32 temp_offset;
+ struct smb_com_ntransact_req *pSMB;
+
+ rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
+ (void **)&pSMB);
+ if (rc)
+ return rc;
+ *ret_buf = (void *)pSMB;
+ pSMB->Reserved = 0;
+ pSMB->TotalParameterCount = cpu_to_le32(parm_len);
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->DataCount = pSMB->TotalDataCount;
+ temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
+ (setup_count * 2) - 4 /* for rfc1001 length itself */;
+ pSMB->ParameterOffset = cpu_to_le32(temp_offset);
+ pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
+ pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
+ pSMB->SubCommand = cpu_to_le16(sub_command);
+ return 0;
+}
+
+static int
+validate_ntransact(char *buf, char **ppparm, char **ppdata,
+ __u32 *pparmlen, __u32 *pdatalen)
+{
+ char *end_of_smb;
+ __u32 data_count, data_offset, parm_count, parm_offset;
+ struct smb_com_ntransact_rsp *pSMBr;
+ u16 bcc;
+
+ *pdatalen = 0;
+ *pparmlen = 0;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ pSMBr = (struct smb_com_ntransact_rsp *)buf;
+
+ bcc = get_bcc(&pSMBr->hdr);
+ end_of_smb = 2 /* sizeof byte count */ + bcc +
+ (char *)&pSMBr->ByteCount;
+
+ data_offset = le32_to_cpu(pSMBr->DataOffset);
+ data_count = le32_to_cpu(pSMBr->DataCount);
+ parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
+ parm_count = le32_to_cpu(pSMBr->ParameterCount);
+
+ *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
+ *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
+
+ /* should we also check that parm and data areas do not overlap? */
+ if (*ppparm > end_of_smb) {
+ cifs_dbg(FYI, "parms start after end of smb\n");
+ return -EINVAL;
+ } else if (parm_count + *ppparm > end_of_smb) {
+ cifs_dbg(FYI, "parm end after end of smb\n");
+ return -EINVAL;
+ } else if (*ppdata > end_of_smb) {
+ cifs_dbg(FYI, "data starts after end of smb\n");
+ return -EINVAL;
+ } else if (data_count + *ppdata > end_of_smb) {
+ cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n",
+ *ppdata, data_count, (data_count + *ppdata),
+ end_of_smb, pSMBr);
+ return -EINVAL;
+ } else if (parm_count + data_count > bcc) {
+ cifs_dbg(FYI, "parm count and data count larger than SMB\n");
+ return -EINVAL;
+ }
+ *pdatalen = data_count;
+ *pparmlen = parm_count;
+ return 0;
+}
+
+/* Get Security Descriptor (by handle) from remote server for a file or dir */
+int
+CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
+ struct cifs_ntsd **acl_inf, __u32 *pbuflen)
+{
+ int rc = 0;
+ int buf_type = 0;
+ QUERY_SEC_DESC_REQ *pSMB;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+
+ cifs_dbg(FYI, "GetCifsACL\n");
+
+ *pbuflen = 0;
+ *acl_inf = NULL;
+
+ rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
+ 8 /* parm len */, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->MaxParameterCount = cpu_to_le32(4);
+ /* BB TEST with big acls that might need to be e.g. larger than 16K */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
+ CIFS_ACL_DACL);
+ pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
+ inc_rfc1001_len(pSMB, 11);
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
+
+ rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
+ 0, &rsp_iov);
+ cifs_small_buf_release(pSMB);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc);
+ } else { /* decode response */
+ __le32 *parm;
+ __u32 parm_len;
+ __u32 acl_len;
+ struct smb_com_ntransact_rsp *pSMBr;
+ char *pdata;
+
+/* validate_nttransact */
+ rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm,
+ &pdata, &parm_len, pbuflen);
+ if (rc)
+ goto qsec_out;
+ pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base;
+
+ cifs_dbg(FYI, "smb %p parm %p data %p\n",
+ pSMBr, parm, *acl_inf);
+
+ if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
+ rc = -EIO; /* bad smb */
+ *pbuflen = 0;
+ goto qsec_out;
+ }
+
+/* BB check that data area is minimum length and as big as acl_len */
+
+ acl_len = le32_to_cpu(*parm);
+ if (acl_len != *pbuflen) {
+ cifs_dbg(VFS, "acl length %d does not match %d\n",
+ acl_len, *pbuflen);
+ if (*pbuflen > acl_len)
+ *pbuflen = acl_len;
+ }
+
+ /* check if buffer is big enough for the acl
+ header followed by the smallest SID */
+ if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
+ (*pbuflen >= 64 * 1024)) {
+ cifs_dbg(VFS, "bad acl length %d\n", *pbuflen);
+ rc = -EINVAL;
+ *pbuflen = 0;
+ } else {
+ *acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL);
+ if (*acl_inf == NULL) {
+ *pbuflen = 0;
+ rc = -ENOMEM;
+ }
+ }
+ }
+qsec_out:
+ free_rsp_buf(buf_type, rsp_iov.iov_base);
+ return rc;
+}
+
+int
+CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
+ struct cifs_ntsd *pntsd, __u32 acllen, int aclflag)
+{
+ __u16 byte_count, param_count, data_count, param_offset, data_offset;
+ int rc = 0;
+ int bytes_returned = 0;
+ SET_SEC_DESC_REQ *pSMB = NULL;
+ void *pSMBr;
+
+setCifsAclRetry:
+ rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+
+ param_count = 8;
+ param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
+ data_count = acllen;
+ data_offset = param_offset + param_count;
+ byte_count = 3 /* pad */ + param_count;
+
+ pSMB->DataCount = cpu_to_le32(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->MaxParameterCount = cpu_to_le32(4);
+ pSMB->MaxDataCount = cpu_to_le32(16384);
+ pSMB->ParameterCount = cpu_to_le32(param_count);
+ pSMB->ParameterOffset = cpu_to_le32(param_offset);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->DataOffset = cpu_to_le32(data_offset);
+ pSMB->SetupCount = 0;
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
+ pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
+
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->Reserved2 = 0;
+ pSMB->AclFlags = cpu_to_le32(aclflag);
+
+ if (pntsd && acllen) {
+ memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) +
+ data_offset, pntsd, acllen);
+ inc_rfc1001_len(pSMB, byte_count + data_count);
+ } else
+ inc_rfc1001_len(pSMB, byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+ cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n",
+ bytes_returned, rc);
+ if (rc)
+ cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc);
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto setCifsAclRetry;
+
+ return (rc);
+}
+
+#endif /* CONFIG_CIFS_ACL */
+
+/* Legacy Query Path Information call for lookup to old servers such
+ as Win9x/WinME */
+int
+SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *search_name, FILE_ALL_INFO *data,
+ const struct nls_table *nls_codepage, int remap)
+{
+ QUERY_INFORMATION_REQ *pSMB;
+ QUERY_INFORMATION_RSP *pSMBr;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cifs_dbg(FYI, "In SMBQPath path %s\n", search_name);
+QInfRetry:
+ rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName,
+ search_name, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else {
+ name_len = strnlen(search_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, search_name, name_len);
+ }
+ pSMB->BufferFormat = 0x04;
+ name_len++; /* account for buffer type byte */
+ inc_rfc1001_len(pSMB, (__u16)name_len);
+ pSMB->ByteCount = cpu_to_le16(name_len);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc);
+ } else if (data) {
+ struct timespec64 ts;
+ __u32 time = le32_to_cpu(pSMBr->last_write_time);
+
+ /* decode response */
+ /* BB FIXME - add time zone adjustment BB */
+ memset(data, 0, sizeof(FILE_ALL_INFO));
+ ts.tv_nsec = 0;
+ ts.tv_sec = time;
+ /* decode time fields */
+ data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
+ data->LastWriteTime = data->ChangeTime;
+ data->LastAccessTime = 0;
+ data->AllocationSize =
+ cpu_to_le64(le32_to_cpu(pSMBr->size));
+ data->EndOfFile = data->AllocationSize;
+ data->Attributes =
+ cpu_to_le32(le16_to_cpu(pSMBr->attr));
+ } else
+ rc = -EIO; /* bad buffer passed in */
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QInfRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ u16 netfid, FILE_ALL_INFO *pFindData)
+{
+ struct smb_t2_qfi_req *pSMB = NULL;
+ struct smb_t2_qfi_rsp *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ __u16 params, byte_count;
+
+QFileInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2 /* level */ + 2 /* fid */;
+ pSMB->t2.TotalDataCount = 0;
+ pSMB->t2.MaxParameterCount = cpu_to_le16(4);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
+ pSMB->t2.MaxSetupCount = 0;
+ pSMB->t2.Reserved = 0;
+ pSMB->t2.Flags = 0;
+ pSMB->t2.Timeout = 0;
+ pSMB->t2.Reserved2 = 0;
+ pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
+ Fid) - 4);
+ pSMB->t2.DataCount = 0;
+ pSMB->t2.DataOffset = 0;
+ pSMB->t2.SetupCount = 1;
+ pSMB->t2.Reserved3 = 0;
+ pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->t2.TotalParameterCount = cpu_to_le16(params);
+ pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
+ pSMB->Pad = 0;
+ pSMB->Fid = netfid;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->t2.ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QFileInfo = %d", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc) /* BB add auto retry on EOPNOTSUPP? */
+ rc = -EIO;
+ else if (get_bcc(&pSMBr->hdr) < 40)
+ rc = -EIO; /* bad smb */
+ else if (pFindData) {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset, sizeof(FILE_ALL_INFO));
+ } else
+ rc = -ENOMEM;
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QFileInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *search_name, FILE_ALL_INFO *data,
+ int legacy /* old style infolevel */,
+ const struct nls_table *nls_codepage, int remap)
+{
+ /* level 263 SMB_QUERY_FILE_ALL_INFO */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+
+ /* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */
+QPathInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(search_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, search_name, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ if (legacy)
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc) /* BB add auto retry on EOPNOTSUPP? */
+ rc = -EIO;
+ else if (!legacy && get_bcc(&pSMBr->hdr) < 40)
+ rc = -EIO; /* bad smb */
+ else if (legacy && get_bcc(&pSMBr->hdr) < 24)
+ rc = -EIO; /* 24 or 26 expected but we do not read
+ last field */
+ else if (data) {
+ int size;
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+
+ /*
+ * On legacy responses we do not read the last field,
+ * EAsize, fortunately since it varies by subdialect and
+ * also note it differs on Set vs Get, ie two bytes or 4
+ * bytes depending but we don't care here.
+ */
+ if (legacy)
+ size = sizeof(FILE_INFO_STANDARD);
+ else
+ size = sizeof(FILE_ALL_INFO);
+ memcpy((char *) data, (char *) &pSMBr->hdr.Protocol +
+ data_offset, size);
+ } else
+ rc = -ENOMEM;
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QPathInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
+{
+ struct smb_t2_qfi_req *pSMB = NULL;
+ struct smb_t2_qfi_rsp *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ __u16 params, byte_count;
+
+UnixQFileInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2 /* level */ + 2 /* fid */;
+ pSMB->t2.TotalDataCount = 0;
+ pSMB->t2.MaxParameterCount = cpu_to_le16(4);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
+ pSMB->t2.MaxSetupCount = 0;
+ pSMB->t2.Reserved = 0;
+ pSMB->t2.Flags = 0;
+ pSMB->t2.Timeout = 0;
+ pSMB->t2.Reserved2 = 0;
+ pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
+ Fid) - 4);
+ pSMB->t2.DataCount = 0;
+ pSMB->t2.DataOffset = 0;
+ pSMB->t2.SetupCount = 1;
+ pSMB->t2.Reserved3 = 0;
+ pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->t2.TotalParameterCount = cpu_to_le16(params);
+ pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
+ pSMB->Pad = 0;
+ pSMB->Fid = netfid;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->t2.ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in UnixQFileInfo = %d", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) {
+ cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset,
+ sizeof(FILE_UNIX_BASIC_INFO));
+ }
+ }
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto UnixQFileInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName,
+ FILE_UNIX_BASIC_INFO *pFindData,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* SMB_QUERY_FILE_UNIX_BASIC */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned = 0;
+ int name_len;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName);
+UnixQPathInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in UnixQPathInfo = %d", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) {
+ cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset,
+ sizeof(FILE_UNIX_BASIC_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto UnixQPathInfoRetry;
+
+ return rc;
+}
+
+/* xid, tcon, searchName and codepage are input parms, rest are returned */
+int
+CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *searchName, struct cifs_sb_info *cifs_sb,
+ __u16 *pnetfid, __u16 search_flags,
+ struct cifs_search_info *psrch_inf, bool msearch)
+{
+/* level 257 SMB_ */
+ TRANSACTION2_FFIRST_REQ *pSMB = NULL;
+ TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
+ T2_FFIRST_RSP_PARMS *parms;
+ int rc = 0;
+ int bytes_returned = 0;
+ int name_len, remap;
+ __u16 params, byte_count;
+ struct nls_table *nls_codepage;
+
+ cifs_dbg(FYI, "In FindFirst for %s\n", searchName);
+
+findFirstRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ nls_codepage = cifs_sb->local_nls;
+ remap = cifs_remap(cifs_sb);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ /* We can not add the asterik earlier in case
+ it got remapped to 0xF03A as if it were part of the
+ directory name instead of a wildcard */
+ name_len *= 2;
+ if (msearch) {
+ pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
+ pSMB->FileName[name_len+1] = 0;
+ pSMB->FileName[name_len+2] = '*';
+ pSMB->FileName[name_len+3] = 0;
+ name_len += 4; /* now the trailing null */
+ /* null terminate just in case */
+ pSMB->FileName[name_len] = 0;
+ pSMB->FileName[name_len+1] = 0;
+ name_len += 2;
+ }
+ } else { /* BB add check for overrun of SMB buf BB */
+ name_len = strnlen(searchName, PATH_MAX);
+/* BB fix here and in unicode clause above ie
+ if (name_len > buffersize-header)
+ free buffer exit; BB */
+ strncpy(pSMB->FileName, searchName, name_len);
+ if (msearch) {
+ pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
+ pSMB->FileName[name_len+1] = '*';
+ pSMB->FileName[name_len+2] = 0;
+ name_len += 3;
+ }
+ }
+
+ params = 12 + name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(10);
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
+ - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
+ pSMB->SearchFlags = cpu_to_le16(search_flags);
+ pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
+
+ /* BB what should we set StorageType to? Does it matter? BB */
+ pSMB->SearchStorageType = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_ffirst);
+
+ if (rc) {/* BB add logic to retry regular search if Unix search
+ rejected unexpectedly by server */
+ /* BB Add code to handle unsupported level rc */
+ cifs_dbg(FYI, "Error in FindFirst = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ /* BB eventually could optimize out free and realloc of buf */
+ /* for this case */
+ if (rc == -EAGAIN)
+ goto findFirstRetry;
+ } else { /* decode response */
+ /* BB remember to free buffer if error BB */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc == 0) {
+ unsigned int lnoff;
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ psrch_inf->unicode = true;
+ else
+ psrch_inf->unicode = false;
+
+ psrch_inf->ntwrk_buf_start = (char *)pSMBr;
+ psrch_inf->smallBuf = 0;
+ psrch_inf->srch_entries_start =
+ (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.DataOffset);
+ parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.ParameterOffset));
+
+ if (parms->EndofSearch)
+ psrch_inf->endOfSearch = true;
+ else
+ psrch_inf->endOfSearch = false;
+
+ psrch_inf->entries_in_buffer =
+ le16_to_cpu(parms->SearchCount);
+ psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
+ psrch_inf->entries_in_buffer;
+ lnoff = le16_to_cpu(parms->LastNameOffset);
+ if (CIFSMaxBufSize < lnoff) {
+ cifs_dbg(VFS, "ignoring corrupt resume name\n");
+ psrch_inf->last_entry = NULL;
+ return rc;
+ }
+
+ psrch_inf->last_entry = psrch_inf->srch_entries_start +
+ lnoff;
+
+ if (pnetfid)
+ *pnetfid = parms->SearchHandle;
+ } else {
+ cifs_buf_release(pSMB);
+ }
+ }
+
+ return rc;
+}
+
+int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
+ __u16 searchHandle, __u16 search_flags,
+ struct cifs_search_info *psrch_inf)
+{
+ TRANSACTION2_FNEXT_REQ *pSMB = NULL;
+ TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
+ T2_FNEXT_RSP_PARMS *parms;
+ char *response_data;
+ int rc = 0;
+ int bytes_returned;
+ unsigned int name_len;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In FindNext\n");
+
+ if (psrch_inf->endOfSearch)
+ return -ENOENT;
+
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 14; /* includes 2 bytes of null string, converted to LE below*/
+ byte_count = 0;
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(8);
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
+ pSMB->SearchHandle = searchHandle; /* always kept as le */
+ pSMB->SearchCount =
+ cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
+ pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
+ pSMB->ResumeKey = psrch_inf->resume_key;
+ pSMB->SearchFlags = cpu_to_le16(search_flags);
+
+ name_len = psrch_inf->resume_name_len;
+ params += name_len;
+ if (name_len < PATH_MAX) {
+ memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
+ byte_count += name_len;
+ /* 14 byte parm len above enough for 2 byte null terminator */
+ pSMB->ResumeFileName[name_len] = 0;
+ pSMB->ResumeFileName[name_len+1] = 0;
+ } else {
+ rc = -EINVAL;
+ goto FNext2_err_exit;
+ }
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext);
+ if (rc) {
+ if (rc == -EBADF) {
+ psrch_inf->endOfSearch = true;
+ cifs_buf_release(pSMB);
+ rc = 0; /* search probably was closed at end of search*/
+ } else
+ cifs_dbg(FYI, "FindNext returned = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc == 0) {
+ unsigned int lnoff;
+
+ /* BB fixme add lock for file (srch_info) struct here */
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ psrch_inf->unicode = true;
+ else
+ psrch_inf->unicode = false;
+ response_data = (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.ParameterOffset);
+ parms = (T2_FNEXT_RSP_PARMS *)response_data;
+ response_data = (char *)&pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.DataOffset);
+ if (psrch_inf->smallBuf)
+ cifs_small_buf_release(
+ psrch_inf->ntwrk_buf_start);
+ else
+ cifs_buf_release(psrch_inf->ntwrk_buf_start);
+ psrch_inf->srch_entries_start = response_data;
+ psrch_inf->ntwrk_buf_start = (char *)pSMB;
+ psrch_inf->smallBuf = 0;
+ if (parms->EndofSearch)
+ psrch_inf->endOfSearch = true;
+ else
+ psrch_inf->endOfSearch = false;
+ psrch_inf->entries_in_buffer =
+ le16_to_cpu(parms->SearchCount);
+ psrch_inf->index_of_last_entry +=
+ psrch_inf->entries_in_buffer;
+ lnoff = le16_to_cpu(parms->LastNameOffset);
+ if (CIFSMaxBufSize < lnoff) {
+ cifs_dbg(VFS, "ignoring corrupt resume name\n");
+ psrch_inf->last_entry = NULL;
+ return rc;
+ } else
+ psrch_inf->last_entry =
+ psrch_inf->srch_entries_start + lnoff;
+
+/* cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n",
+ psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
+
+ /* BB fixme add unlock here */
+ }
+
+ }
+
+ /* BB On error, should we leave previous search buf (and count and
+ last entry fields) intact or free the previous one? */
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+FNext2_err_exit:
+ if (rc != 0)
+ cifs_buf_release(pSMB);
+ return rc;
+}
+
+int
+CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u16 searchHandle)
+{
+ int rc = 0;
+ FINDCLOSE_REQ *pSMB = NULL;
+
+ cifs_dbg(FYI, "In CIFSSMBFindClose\n");
+ rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
+
+ /* no sense returning error if session restarted
+ as file handle has been closed */
+ if (rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMB->FileID = searchHandle;
+ pSMB->ByteCount = 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ if (rc)
+ cifs_dbg(VFS, "Send error in FindClose = %d\n", rc);
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose);
+
+ /* Since session is dead, search handle closed on server already */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *search_name, __u64 *inode_number,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int name_len, bytes_returned;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name);
+ if (tcon == NULL)
+ return -ENODEV;
+
+GetInodeNumberRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName,
+ search_name, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(search_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, search_name, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc);
+ } else {
+ /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ /* BB also check enough total bytes returned */
+ if (rc || get_bcc(&pSMBr->hdr) < 2)
+ /* If rc should we check for EOPNOSUPP and
+ disable the srvino flag? or in caller? */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ struct file_internal_info *pfinfo;
+ /* BB Do we need a cast or hash here ? */
+ if (count < 8) {
+ cifs_dbg(FYI, "Illegal size ret in QryIntrnlInf\n");
+ rc = -EIO;
+ goto GetInodeNumOut;
+ }
+ pfinfo = (struct file_internal_info *)
+ (data_offset + (char *) &pSMBr->hdr.Protocol);
+ *inode_number = le64_to_cpu(pfinfo->UniqueId);
+ }
+ }
+GetInodeNumOut:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto GetInodeNumberRetry;
+ return rc;
+}
+
+int
+CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
+ const char *search_name, struct dfs_info3_param **target_nodes,
+ unsigned int *num_of_nodes,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* TRANS2_GET_DFS_REFERRAL */
+ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
+ TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+ *num_of_nodes = 0;
+ *target_nodes = NULL;
+
+ cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name);
+ if (ses == NULL || ses->tcon_ipc == NULL)
+ return -ENODEV;
+
+getDFSRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ /* server pointer checked in called function,
+ but should never be null here anyway */
+ pSMB->hdr.Mid = get_next_mid(ses->server);
+ pSMB->hdr.Tid = ses->tcon_ipc->tid;
+ pSMB->hdr.Uid = ses->Suid;
+ if (ses->capabilities & CAP_STATUS32)
+ pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+ if (ses->capabilities & CAP_DFS)
+ pSMB->hdr.Flags2 |= SMBFLG2_DFS;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->RequestFileName,
+ search_name, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(search_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->RequestFileName, search_name, name_len);
+ }
+
+ if (ses->server->sign)
+ pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ pSMB->hdr.Uid = ses->Suid;
+
+ params = 2 /* level */ + name_len /*includes null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = 0;
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
+ byte_count = params + 3 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->MaxReferralLevel = cpu_to_le16(3);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc);
+ goto GetDFSRefExit;
+ }
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ /* BB Also check if enough total bytes returned? */
+ if (rc || get_bcc(&pSMBr->hdr) < 17) {
+ rc = -EIO; /* bad smb */
+ goto GetDFSRefExit;
+ }
+
+ cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d Offset %d\n",
+ get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));
+
+ /* parse returned result into more usable form */
+ rc = parse_dfs_referrals(&pSMBr->dfs_data,
+ le16_to_cpu(pSMBr->t2.DataCount),
+ num_of_nodes, target_nodes, nls_codepage,
+ remap, search_name,
+ (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0);
+
+GetDFSRefExit:
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto getDFSRetry;
+
+ return rc;
+}
+
+/* Query File System Info such as free space to old servers such as Win 9x */
+int
+SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData)
+{
+/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_ALLOC_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "OldQFSInfo\n");
+oldQFSInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < 18)
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ cifs_dbg(FYI, "qfsinf resp BCC: %d Offset %d\n",
+ get_bcc(&pSMBr->hdr), data_offset);
+
+ response_data = (FILE_SYSTEM_ALLOC_INFO *)
+ (((char *) &pSMBr->hdr.Protocol) + data_offset);
+ FSData->f_bsize =
+ le16_to_cpu(response_data->BytesPerSector) *
+ le32_to_cpu(response_data->
+ SectorsPerAllocationUnit);
+ FSData->f_blocks =
+ le32_to_cpu(response_data->TotalAllocationUnits);
+ FSData->f_bfree = FSData->f_bavail =
+ le32_to_cpu(response_data->FreeAllocationUnits);
+ cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n",
+ (unsigned long long)FSData->f_blocks,
+ (unsigned long long)FSData->f_bfree,
+ FSData->f_bsize);
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto oldQFSInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData)
+{
+/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QFSInfo\n");
+QFSInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < 24)
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+
+ response_data =
+ (FILE_SYSTEM_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ FSData->f_bsize =
+ le32_to_cpu(response_data->BytesPerSector) *
+ le32_to_cpu(response_data->
+ SectorsPerAllocationUnit);
+ FSData->f_blocks =
+ le64_to_cpu(response_data->TotalAllocationUnits);
+ FSData->f_bfree = FSData->f_bavail =
+ le64_to_cpu(response_data->FreeAllocationUnits);
+ cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n",
+ (unsigned long long)FSData->f_blocks,
+ (unsigned long long)FSData->f_bfree,
+ FSData->f_bsize);
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon)
+{
+/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QFSAttributeInfo\n");
+QFSAttributeRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < 13) {
+ /* BB also check if enough bytes returned */
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_ATTRIBUTE_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ memcpy(&tcon->fsAttrInfo, response_data,
+ sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSAttributeRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon)
+{
+/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_DEVICE_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QFSDeviceInfo\n");
+QFSDeviceRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) <
+ sizeof(FILE_SYSTEM_DEVICE_INFO))
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_DEVICE_INFO *)
+ (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ memcpy(&tcon->fsDevInfo, response_data,
+ sizeof(FILE_SYSTEM_DEVICE_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSDeviceRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon)
+{
+/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_UNIX_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QFSUnixInfo\n");
+QFSUnixRetry:
+ rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
+ (void **) &pSMB, (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(100);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
+ smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < 13) {
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_UNIX_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ memcpy(&tcon->fsUnixInfo, response_data,
+ sizeof(FILE_SYSTEM_UNIX_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSUnixRetry;
+
+
+ return rc;
+}
+
+int
+CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, __u64 cap)
+{
+/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
+ TRANSACTION2_SETFSI_REQ *pSMB = NULL;
+ TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cifs_dbg(FYI, "In SETFSUnixInfo\n");
+SETFSUnixRetry:
+ /* BB switch to small buf init to save memory */
+ rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
+ (void **) &pSMB, (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 4; /* 2 bytes zero followed by info level. */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
+ - 4;
+ offset = param_offset + params;
+
+ pSMB->MaxParameterCount = cpu_to_le16(4);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(100);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
+ byte_count = 1 /* pad */ + params + 12;
+
+ pSMB->DataCount = cpu_to_le16(12);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+
+ /* Params. */
+ pSMB->FileNum = 0;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
+
+ /* Data. */
+ pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
+ pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
+ pSMB->ClientUnixCap = cpu_to_le64(cap);
+
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc)
+ rc = -EIO; /* bad smb */
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SETFSUnixRetry;
+
+ return rc;
+}
+
+
+
+int
+CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *FSData)
+{
+/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_POSIX_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cifs_dbg(FYI, "In QFSPosixInfo\n");
+QFSPosixRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(100);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
+ smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc);
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || get_bcc(&pSMBr->hdr) < 13) {
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_POSIX_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ FSData->f_bsize =
+ le32_to_cpu(response_data->BlockSize);
+ FSData->f_blocks =
+ le64_to_cpu(response_data->TotalBlocks);
+ FSData->f_bfree =
+ le64_to_cpu(response_data->BlocksAvail);
+ if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
+ FSData->f_bavail = FSData->f_bfree;
+ } else {
+ FSData->f_bavail =
+ le64_to_cpu(response_data->UserBlocksAvail);
+ }
+ if (response_data->TotalFileNodes != cpu_to_le64(-1))
+ FSData->f_files =
+ le64_to_cpu(response_data->TotalFileNodes);
+ if (response_data->FreeFileNodes != cpu_to_le64(-1))
+ FSData->f_ffree =
+ le64_to_cpu(response_data->FreeFileNodes);
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSPosixRetry;
+
+ return rc;
+}
+
+
+/*
+ * We can not use write of zero bytes trick to set file size due to need for
+ * large file support. Also note that this SetPathInfo is preferred to
+ * SetFileInfo based method in next routine which is only needed to work around
+ * a sharing violation bugin Samba which this routine can run into.
+ */
+int
+CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb,
+ bool set_allocation)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ struct file_end_of_file_info *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ int remap = cifs_remap(cifs_sb);
+
+ __u16 params, byte_count, data_count, param_offset, offset;
+
+ cifs_dbg(FYI, "In SetEOF\n");
+SetEOFRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
+ PATH_MAX, cifs_sb->local_nls, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(file_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, file_name, name_len);
+ }
+ params = 6 + name_len;
+ data_count = sizeof(struct file_end_of_file_info);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(4100);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ if (set_allocation) {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
+ } else /* Set File Size */ {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
+ }
+
+ parm_data =
+ (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
+ offset);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + data_count;
+ pSMB->DataCount = cpu_to_le16(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ parm_data->FileSize = cpu_to_le64(size);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetEOFRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size, bool set_allocation)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct file_end_of_file_info *parm_data;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n",
+ (long long)size);
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ count = sizeof(struct file_end_of_file_info);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ parm_data =
+ (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
+ + offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ parm_data->FileSize = cpu_to_le64(size);
+ pSMB->Fid = cfile->fid.netfid;
+ if (set_allocation) {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
+ } else /* Set File Size */ {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
+ }
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n",
+ rc);
+ }
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+/* Some legacy servers such as NT4 require that the file times be set on
+ an open handle, rather than by pathname - this is awkward due to
+ potential access conflicts on the open, but it is unavoidable for these
+ old servers since the only other choice is to go from 100 nanosecond DCE
+ time and resort to the original setpathinfo level which takes the ancient
+ DOS time format with 2 second granularity */
+int
+CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cifs_dbg(FYI, "Set Times (via SetFileInfo)\n");
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *)pSMB +
+ offsetof(struct smb_hdr, Protocol) + offset;
+
+ count = sizeof(FILE_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = fid;
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ if (rc)
+ cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n",
+ rc);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
+ bool delete_file, __u16 fid, __u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n");
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+
+ count = 1;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = fid;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ *data_offset = delete_file ? 1 : 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ if (rc)
+ cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc);
+
+ return rc;
+}
+
+int
+CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, const FILE_BASIC_INFO *data,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ char *data_offset;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cifs_dbg(FYI, "In SetTimes\n");
+
+SetTimesRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+ count = sizeof(FILE_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetTimesRetry;
+
+ return rc;
+}
+
+/* Can not be used to set time stamps yet (due to old DOS time format) */
+/* Can be used to set attributes */
+#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
+ handling it anyway and NT4 was what we thought it would be needed for
+ Do not delete it until we prove whether needed for Win9x though */
+int
+CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon, char *fileName,
+ __u16 dos_attrs, const struct nls_table *nls_codepage)
+{
+ SETATTR_REQ *pSMB = NULL;
+ SETATTR_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cifs_dbg(FYI, "In SetAttrLegacy\n");
+
+SetAttrLgcyRetry:
+ rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ ConvertToUTF16((__le16 *) pSMB->fileName, fileName,
+ PATH_MAX, nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ pSMB->attr = cpu_to_le16(dos_attrs);
+ pSMB->BufferFormat = 0x04;
+ inc_rfc1001_len(pSMB, name_len + 1);
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "Error in LegacySetAttr = %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetAttrLgcyRetry;
+
+ return rc;
+}
+#endif /* temporarily unneeded SetAttr legacy function */
+
+static void
+cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
+ const struct cifs_unix_set_info_args *args)
+{
+ u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64;
+ u64 mode = args->mode;
+
+ if (uid_valid(args->uid))
+ uid = from_kuid(&init_user_ns, args->uid);
+ if (gid_valid(args->gid))
+ gid = from_kgid(&init_user_ns, args->gid);
+
+ /*
+ * Samba server ignores set of file size to zero due to bugs in some
+ * older clients, but we should be precise - we use SetFileSize to
+ * set file size and do not want to truncate file size to zero
+ * accidentally as happened on one Samba server beta by putting
+ * zero instead of -1 here
+ */
+ data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
+ data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
+ data_offset->LastStatusChange = cpu_to_le64(args->ctime);
+ data_offset->LastAccessTime = cpu_to_le64(args->atime);
+ data_offset->LastModificationTime = cpu_to_le64(args->mtime);
+ data_offset->Uid = cpu_to_le64(uid);
+ data_offset->Gid = cpu_to_le64(gid);
+ /* better to leave device as zero when it is */
+ data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
+ data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
+ data_offset->Permissions = cpu_to_le64(mode);
+
+ if (S_ISREG(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FILE);
+ else if (S_ISDIR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_DIR);
+ else if (S_ISLNK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
+ else if (S_ISCHR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
+ else if (S_ISBLK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
+ else if (S_ISFIFO(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FIFO);
+ else if (S_ISSOCK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SOCKET);
+}
+
+int
+CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const struct cifs_unix_set_info_args *args,
+ u16 fid, u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ int rc = 0;
+ u16 params, param_offset, offset, byte_count, count;
+
+ cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n");
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *)pSMB +
+ offsetof(struct smb_hdr, Protocol) + offset;
+
+ count = sizeof(FILE_UNIX_BASIC_INFO);
+
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = fid;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args);
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ cifs_small_buf_release(pSMB);
+ if (rc)
+ cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n",
+ rc);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *file_name,
+ const struct cifs_unix_set_info_args *args,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ FILE_UNIX_BASIC_INFO *data_offset;
+ __u16 params, param_offset, offset, count, byte_count;
+
+ cifs_dbg(FYI, "In SetUID/GID/Mode\n");
+setPermsRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(file_name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, file_name, name_len);
+ }
+
+ params = 6 + name_len;
+ count = sizeof(FILE_UNIX_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ data_offset =
+ (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
+ offset);
+ memset(data_offset, 0, count);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+
+ cifs_fill_unix_set_info(data_offset, args);
+
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc);
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto setPermsRetry;
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_XATTR
+/*
+ * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
+ * function used by listxattr and getxattr type calls. When ea_name is set,
+ * it looks for that attribute name and stuffs that value into the EAData
+ * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
+ * buffer. In both cases, the return value is either the length of the
+ * resulting data or a negative error code. If EAData is a NULL pointer then
+ * the data isn't copied to it, but the length is returned.
+ */
+ssize_t
+CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName, const unsigned char *ea_name,
+ char *EAData, size_t buf_size,
+ struct cifs_sb_info *cifs_sb)
+{
+ /* BB assumes one setup word */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int remap = cifs_remap(cifs_sb);
+ struct nls_table *nls_codepage = cifs_sb->local_nls;
+ int rc = 0;
+ int bytes_returned;
+ int list_len;
+ struct fealist *ea_response_data;
+ struct fea *temp_fea;
+ char *temp_ptr;
+ char *end_of_smb;
+ __u16 params, byte_count, data_offset;
+ unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
+
+ cifs_dbg(FYI, "In Query All EAs path %s\n", searchName);
+QAllEAsRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ list_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ list_len++; /* trailing null */
+ list_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ list_len = strnlen(searchName, PATH_MAX);
+ list_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, list_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc);
+ goto QAllEAsOut;
+ }
+
+
+ /* BB also check enough total bytes returned */
+ /* BB we need to improve the validity checking
+ of these trans2 responses */
+
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc || get_bcc(&pSMBr->hdr) < 4) {
+ rc = -EIO; /* bad smb */
+ goto QAllEAsOut;
+ }
+
+ /* check that length of list is not more than bcc */
+ /* check that each entry does not go beyond length
+ of list */
+ /* check that each element of each entry does not
+ go beyond end of list */
+ /* validate_trans2_offsets() */
+ /* BB check if start of smb + data_offset > &bcc+ bcc */
+
+ data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ ea_response_data = (struct fealist *)
+ (((char *) &pSMBr->hdr.Protocol) + data_offset);
+
+ list_len = le32_to_cpu(ea_response_data->list_len);
+ cifs_dbg(FYI, "ea length %d\n", list_len);
+ if (list_len <= 8) {
+ cifs_dbg(FYI, "empty EA list returned from server\n");
+ /* didn't find the named attribute */
+ if (ea_name)
+ rc = -ENODATA;
+ goto QAllEAsOut;
+ }
+
+ /* make sure list_len doesn't go past end of SMB */
+ end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
+ if ((char *)ea_response_data + list_len > end_of_smb) {
+ cifs_dbg(FYI, "EA list appears to go beyond SMB\n");
+ rc = -EIO;
+ goto QAllEAsOut;
+ }
+
+ /* account for ea list len */
+ list_len -= 4;
+ temp_fea = ea_response_data->list;
+ temp_ptr = (char *)temp_fea;
+ while (list_len > 0) {
+ unsigned int name_len;
+ __u16 value_len;
+
+ list_len -= 4;
+ temp_ptr += 4;
+ /* make sure we can read name_len and value_len */
+ if (list_len < 0) {
+ cifs_dbg(FYI, "EA entry goes beyond length of list\n");
+ rc = -EIO;
+ goto QAllEAsOut;
+ }
+
+ name_len = temp_fea->name_len;
+ value_len = le16_to_cpu(temp_fea->value_len);
+ list_len -= name_len + 1 + value_len;
+ if (list_len < 0) {
+ cifs_dbg(FYI, "EA entry goes beyond length of list\n");
+ rc = -EIO;
+ goto QAllEAsOut;
+ }
+
+ if (ea_name) {
+ if (ea_name_len == name_len &&
+ memcmp(ea_name, temp_ptr, name_len) == 0) {
+ temp_ptr += name_len + 1;
+ rc = value_len;
+ if (buf_size == 0)
+ goto QAllEAsOut;
+ if ((size_t)value_len > buf_size) {
+ rc = -ERANGE;
+ goto QAllEAsOut;
+ }
+ memcpy(EAData, temp_ptr, value_len);
+ goto QAllEAsOut;
+ }
+ } else {
+ /* account for prefix user. and trailing null */
+ rc += (5 + 1 + name_len);
+ if (rc < (int) buf_size) {
+ memcpy(EAData, "user.", 5);
+ EAData += 5;
+ memcpy(EAData, temp_ptr, name_len);
+ EAData += name_len;
+ /* null terminate name */
+ *EAData = 0;
+ ++EAData;
+ } else if (buf_size == 0) {
+ /* skip copy - calc size only */
+ } else {
+ /* stop before overrun buffer */
+ rc = -ERANGE;
+ break;
+ }
+ }
+ temp_ptr += name_len + 1 + value_len;
+ temp_fea = (struct fea *)temp_ptr;
+ }
+
+ /* didn't find the named attribute */
+ if (ea_name)
+ rc = -ENODATA;
+
+QAllEAsOut:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QAllEAsRetry;
+
+ return (ssize_t)rc;
+}
+
+int
+CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, const char *ea_name, const void *ea_value,
+ const __u16 ea_value_len, const struct nls_table *nls_codepage,
+ struct cifs_sb_info *cifs_sb)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ struct fealist *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, byte_count, offset, count;
+ int remap = cifs_remap(cifs_sb);
+
+ cifs_dbg(FYI, "In SetEA\n");
+SetEARetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+
+ /* done calculating parms using name_len of file name,
+ now use name_len to calculate length of ea name
+ we are going to create in the inode xattrs */
+ if (ea_name == NULL)
+ name_len = 0;
+ else
+ name_len = strnlen(ea_name, 255);
+
+ count = sizeof(*parm_data) + ea_value_len + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_EA);
+
+ parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ parm_data->list_len = cpu_to_le32(count);
+ parm_data->list[0].EA_flags = 0;
+ /* we checked above that name len is less than 255 */
+ parm_data->list[0].name_len = (__u8)name_len;
+ /* EA names are always ASCII */
+ if (ea_name)
+ strncpy(parm_data->list[0].name, ea_name, name_len);
+ parm_data->list[0].name[name_len] = 0;
+ parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
+ /* caller ensures that ea_value_len is less than 64K but
+ we need to ensure that it fits within the smb */
+
+ /*BB add length check to see if it would fit in
+ negotiated SMB buffer size BB */
+ /* if (ea_value_len > buffer_size - 512 (enough for header)) */
+ if (ea_value_len)
+ memcpy(parm_data->list[0].name+name_len+1,
+ ea_value, ea_value_len);
+
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ inc_rfc1001_len(pSMB, byte_count);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetEARetry;
+
+ return rc;
+}
+#endif
+
+#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* BB unused temporarily */
+/*
+ * Years ago the kernel added a "dnotify" function for Samba server,
+ * to allow network clients (such as Windows) to display updated
+ * lists of files in directory listings automatically when
+ * files are added by one user when another user has the
+ * same directory open on their desktop. The Linux cifs kernel
+ * client hooked into the kernel side of this interface for
+ * the same reason, but ironically when the VFS moved from
+ * "dnotify" to "inotify" it became harder to plug in Linux
+ * network file system clients (the most obvious use case
+ * for notify interfaces is when multiple users can update
+ * the contents of the same directory - exactly what network
+ * file systems can do) although the server (Samba) could
+ * still use it. For the short term we leave the worker
+ * function ifdeffed out (below) until inotify is fixed
+ * in the VFS to make it easier to plug in network file
+ * system clients. If inotify turns out to be permanently
+ * incompatible for network fs clients, we could instead simply
+ * expose this config flag by adding a future cifs (and smb2) notify ioctl.
+ */
+int CIFSSMBNotify(const unsigned int xid, struct cifs_tcon *tcon,
+ const int notify_subdirs, const __u16 netfid,
+ __u32 filter, struct file *pfile, int multishot,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ struct smb_com_transaction_change_notify_req *pSMB = NULL;
+ struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
+ struct dir_notify_req *dnotify_req;
+ int bytes_returned;
+
+ cifs_dbg(FYI, "In CIFSSMBNotify for file handle %d\n", (int)netfid);
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4; /* single byte does not need le conversion */
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ if (notify_subdirs)
+ pSMB->WatchTree = 1; /* one byte - no le conversion needed */
+ pSMB->Reserved2 = 0;
+ pSMB->CompletionFilter = cpu_to_le32(filter);
+ pSMB->Fid = netfid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *)pSMBr, &bytes_returned,
+ CIFS_ASYNC_OP);
+ if (rc) {
+ cifs_dbg(FYI, "Error in Notify = %d\n", rc);
+ } else {
+ /* Add file to outstanding requests */
+ /* BB change to kmem cache alloc */
+ dnotify_req = kmalloc(
+ sizeof(struct dir_notify_req),
+ GFP_KERNEL);
+ if (dnotify_req) {
+ dnotify_req->Pid = pSMB->hdr.Pid;
+ dnotify_req->PidHigh = pSMB->hdr.PidHigh;
+ dnotify_req->Mid = pSMB->hdr.Mid;
+ dnotify_req->Tid = pSMB->hdr.Tid;
+ dnotify_req->Uid = pSMB->hdr.Uid;
+ dnotify_req->netfid = netfid;
+ dnotify_req->pfile = pfile;
+ dnotify_req->filter = filter;
+ dnotify_req->multishot = multishot;
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&dnotify_req->lhead,
+ &GlobalDnotifyReqList);
+ spin_unlock(&GlobalMid_Lock);
+ } else
+ rc = -ENOMEM;
+ }
+ cifs_buf_release(pSMB);
+ return rc;
+}
+#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
new file mode 100644
index 000000000..b5aba1c89
--- /dev/null
+++ b/fs/cifs/connect.c
@@ -0,0 +1,4832 @@
+/*
+ * fs/cifs/connect.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2011
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/string.h>
+#include <linux/sched/signal.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/ctype.h>
+#include <linux/utsname.h>
+#include <linux/mempool.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+#include <linux/pagevec.h>
+#include <linux/freezer.h>
+#include <linux/namei.h>
+#include <linux/uuid.h>
+#include <linux/uaccess.h>
+#include <asm/processor.h>
+#include <linux/inet.h>
+#include <linux/module.h>
+#include <keys/user-type.h>
+#include <net/ipv6.h>
+#include <linux/parser.h>
+#include <linux/bvec.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "dns_resolve.h"
+#include "ntlmssp.h"
+#include "nterr.h"
+#include "rfc1002pdu.h"
+#include "fscache.h"
+#include "smb2proto.h"
+#include "smbdirect.h"
+
+extern mempool_t *cifs_req_poolp;
+extern bool disable_legacy_dialects;
+
+/* FIXME: should these be tunable? */
+#define TLINK_ERROR_EXPIRE (1 * HZ)
+#define TLINK_IDLE_EXPIRE (600 * HZ)
+
+enum {
+ /* Mount options that take no arguments */
+ Opt_user_xattr, Opt_nouser_xattr,
+ Opt_forceuid, Opt_noforceuid,
+ Opt_forcegid, Opt_noforcegid,
+ Opt_noblocksend, Opt_noautotune, Opt_nolease,
+ Opt_hard, Opt_soft, Opt_perm, Opt_noperm,
+ Opt_mapposix, Opt_nomapposix,
+ Opt_mapchars, Opt_nomapchars, Opt_sfu,
+ Opt_nosfu, Opt_nodfs, Opt_posixpaths,
+ Opt_noposixpaths, Opt_nounix, Opt_unix,
+ Opt_nocase,
+ Opt_brl, Opt_nobrl,
+ Opt_handlecache, Opt_nohandlecache,
+ Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids,
+ Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
+ Opt_nohard, Opt_nosoft,
+ Opt_nointr, Opt_intr,
+ Opt_nostrictsync, Opt_strictsync,
+ Opt_serverino, Opt_noserverino,
+ Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
+ Opt_acl, Opt_noacl, Opt_locallease,
+ Opt_sign, Opt_seal, Opt_noac,
+ Opt_fsc, Opt_mfsymlinks,
+ Opt_multiuser, Opt_sloppy, Opt_nosharesock,
+ Opt_persistent, Opt_nopersistent,
+ Opt_resilient, Opt_noresilient,
+ Opt_domainauto, Opt_rdma,
+
+ /* Mount options which take numeric value */
+ Opt_backupuid, Opt_backupgid, Opt_uid,
+ Opt_cruid, Opt_gid, Opt_file_mode,
+ Opt_dirmode, Opt_port,
+ Opt_rsize, Opt_wsize, Opt_actimeo,
+ Opt_echo_interval, Opt_max_credits,
+ Opt_snapshot,
+
+ /* Mount options which take string value */
+ Opt_user, Opt_pass, Opt_ip,
+ Opt_domain, Opt_srcaddr, Opt_iocharset,
+ Opt_netbiosname, Opt_servern,
+ Opt_ver, Opt_vers, Opt_sec, Opt_cache,
+
+ /* Mount options to be ignored */
+ Opt_ignore,
+
+ /* Options which could be blank */
+ Opt_blank_pass,
+ Opt_blank_user,
+ Opt_blank_ip,
+
+ Opt_err
+};
+
+static const match_table_t cifs_mount_option_tokens = {
+
+ { Opt_user_xattr, "user_xattr" },
+ { Opt_nouser_xattr, "nouser_xattr" },
+ { Opt_forceuid, "forceuid" },
+ { Opt_noforceuid, "noforceuid" },
+ { Opt_forcegid, "forcegid" },
+ { Opt_noforcegid, "noforcegid" },
+ { Opt_noblocksend, "noblocksend" },
+ { Opt_noautotune, "noautotune" },
+ { Opt_nolease, "nolease" },
+ { Opt_hard, "hard" },
+ { Opt_soft, "soft" },
+ { Opt_perm, "perm" },
+ { Opt_noperm, "noperm" },
+ { Opt_mapchars, "mapchars" }, /* SFU style */
+ { Opt_nomapchars, "nomapchars" },
+ { Opt_mapposix, "mapposix" }, /* SFM style */
+ { Opt_nomapposix, "nomapposix" },
+ { Opt_sfu, "sfu" },
+ { Opt_nosfu, "nosfu" },
+ { Opt_nodfs, "nodfs" },
+ { Opt_posixpaths, "posixpaths" },
+ { Opt_noposixpaths, "noposixpaths" },
+ { Opt_nounix, "nounix" },
+ { Opt_nounix, "nolinux" },
+ { Opt_nounix, "noposix" },
+ { Opt_unix, "unix" },
+ { Opt_unix, "linux" },
+ { Opt_unix, "posix" },
+ { Opt_nocase, "nocase" },
+ { Opt_nocase, "ignorecase" },
+ { Opt_brl, "brl" },
+ { Opt_nobrl, "nobrl" },
+ { Opt_handlecache, "handlecache" },
+ { Opt_nohandlecache, "nohandlecache" },
+ { Opt_nobrl, "nolock" },
+ { Opt_forcemandatorylock, "forcemandatorylock" },
+ { Opt_forcemandatorylock, "forcemand" },
+ { Opt_setuids, "setuids" },
+ { Opt_nosetuids, "nosetuids" },
+ { Opt_setuidfromacl, "idsfromsid" },
+ { Opt_dynperm, "dynperm" },
+ { Opt_nodynperm, "nodynperm" },
+ { Opt_nohard, "nohard" },
+ { Opt_nosoft, "nosoft" },
+ { Opt_nointr, "nointr" },
+ { Opt_intr, "intr" },
+ { Opt_nostrictsync, "nostrictsync" },
+ { Opt_strictsync, "strictsync" },
+ { Opt_serverino, "serverino" },
+ { Opt_noserverino, "noserverino" },
+ { Opt_rwpidforward, "rwpidforward" },
+ { Opt_cifsacl, "cifsacl" },
+ { Opt_nocifsacl, "nocifsacl" },
+ { Opt_acl, "acl" },
+ { Opt_noacl, "noacl" },
+ { Opt_locallease, "locallease" },
+ { Opt_sign, "sign" },
+ { Opt_seal, "seal" },
+ { Opt_noac, "noac" },
+ { Opt_fsc, "fsc" },
+ { Opt_mfsymlinks, "mfsymlinks" },
+ { Opt_multiuser, "multiuser" },
+ { Opt_sloppy, "sloppy" },
+ { Opt_nosharesock, "nosharesock" },
+ { Opt_persistent, "persistenthandles"},
+ { Opt_nopersistent, "nopersistenthandles"},
+ { Opt_resilient, "resilienthandles"},
+ { Opt_noresilient, "noresilienthandles"},
+ { Opt_domainauto, "domainauto"},
+ { Opt_rdma, "rdma"},
+
+ { Opt_backupuid, "backupuid=%s" },
+ { Opt_backupgid, "backupgid=%s" },
+ { Opt_uid, "uid=%s" },
+ { Opt_cruid, "cruid=%s" },
+ { Opt_gid, "gid=%s" },
+ { Opt_file_mode, "file_mode=%s" },
+ { Opt_dirmode, "dirmode=%s" },
+ { Opt_dirmode, "dir_mode=%s" },
+ { Opt_port, "port=%s" },
+ { Opt_rsize, "rsize=%s" },
+ { Opt_wsize, "wsize=%s" },
+ { Opt_actimeo, "actimeo=%s" },
+ { Opt_echo_interval, "echo_interval=%s" },
+ { Opt_max_credits, "max_credits=%s" },
+ { Opt_snapshot, "snapshot=%s" },
+
+ { Opt_blank_user, "user=" },
+ { Opt_blank_user, "username=" },
+ { Opt_user, "user=%s" },
+ { Opt_user, "username=%s" },
+ { Opt_blank_pass, "pass=" },
+ { Opt_blank_pass, "password=" },
+ { Opt_pass, "pass=%s" },
+ { Opt_pass, "password=%s" },
+ { Opt_blank_ip, "ip=" },
+ { Opt_blank_ip, "addr=" },
+ { Opt_ip, "ip=%s" },
+ { Opt_ip, "addr=%s" },
+ { Opt_ignore, "unc=%s" },
+ { Opt_ignore, "target=%s" },
+ { Opt_ignore, "path=%s" },
+ { Opt_domain, "dom=%s" },
+ { Opt_domain, "domain=%s" },
+ { Opt_domain, "workgroup=%s" },
+ { Opt_srcaddr, "srcaddr=%s" },
+ { Opt_ignore, "prefixpath=%s" },
+ { Opt_iocharset, "iocharset=%s" },
+ { Opt_netbiosname, "netbiosname=%s" },
+ { Opt_servern, "servern=%s" },
+ { Opt_ver, "ver=%s" },
+ { Opt_vers, "vers=%s" },
+ { Opt_sec, "sec=%s" },
+ { Opt_cache, "cache=%s" },
+
+ { Opt_ignore, "cred" },
+ { Opt_ignore, "credentials" },
+ { Opt_ignore, "cred=%s" },
+ { Opt_ignore, "credentials=%s" },
+ { Opt_ignore, "guest" },
+ { Opt_ignore, "rw" },
+ { Opt_ignore, "ro" },
+ { Opt_ignore, "suid" },
+ { Opt_ignore, "nosuid" },
+ { Opt_ignore, "exec" },
+ { Opt_ignore, "noexec" },
+ { Opt_ignore, "nodev" },
+ { Opt_ignore, "noauto" },
+ { Opt_ignore, "dev" },
+ { Opt_ignore, "mand" },
+ { Opt_ignore, "nomand" },
+ { Opt_ignore, "_netdev" },
+
+ { Opt_err, NULL }
+};
+
+enum {
+ Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
+ Opt_sec_ntlmsspi, Opt_sec_ntlmssp,
+ Opt_ntlm, Opt_sec_ntlmi, Opt_sec_ntlmv2,
+ Opt_sec_ntlmv2i, Opt_sec_lanman,
+ Opt_sec_none,
+
+ Opt_sec_err
+};
+
+static const match_table_t cifs_secflavor_tokens = {
+ { Opt_sec_krb5, "krb5" },
+ { Opt_sec_krb5i, "krb5i" },
+ { Opt_sec_krb5p, "krb5p" },
+ { Opt_sec_ntlmsspi, "ntlmsspi" },
+ { Opt_sec_ntlmssp, "ntlmssp" },
+ { Opt_ntlm, "ntlm" },
+ { Opt_sec_ntlmi, "ntlmi" },
+ { Opt_sec_ntlmv2, "nontlm" },
+ { Opt_sec_ntlmv2, "ntlmv2" },
+ { Opt_sec_ntlmv2i, "ntlmv2i" },
+ { Opt_sec_lanman, "lanman" },
+ { Opt_sec_none, "none" },
+
+ { Opt_sec_err, NULL }
+};
+
+/* cache flavors */
+enum {
+ Opt_cache_loose,
+ Opt_cache_strict,
+ Opt_cache_none,
+ Opt_cache_err
+};
+
+static const match_table_t cifs_cacheflavor_tokens = {
+ { Opt_cache_loose, "loose" },
+ { Opt_cache_strict, "strict" },
+ { Opt_cache_none, "none" },
+ { Opt_cache_err, NULL }
+};
+
+static const match_table_t cifs_smb_version_tokens = {
+ { Smb_1, SMB1_VERSION_STRING },
+ { Smb_20, SMB20_VERSION_STRING},
+ { Smb_21, SMB21_VERSION_STRING },
+ { Smb_30, SMB30_VERSION_STRING },
+ { Smb_302, SMB302_VERSION_STRING },
+ { Smb_311, SMB311_VERSION_STRING },
+ { Smb_311, ALT_SMB311_VERSION_STRING },
+ { Smb_3any, SMB3ANY_VERSION_STRING },
+ { Smb_default, SMBDEFAULT_VERSION_STRING },
+ { Smb_version_err, NULL }
+};
+
+static int ip_connect(struct TCP_Server_Info *server);
+static int generic_ip_connect(struct TCP_Server_Info *server);
+static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
+static void cifs_prune_tlinks(struct work_struct *work);
+static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
+ const char *devname, bool is_smb3);
+
+/*
+ * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
+ * get their ip addresses changed at some point.
+ *
+ * This should be called with server->srv_mutex held.
+ */
+#ifdef CONFIG_CIFS_DFS_UPCALL
+static int reconn_set_ipaddr(struct TCP_Server_Info *server)
+{
+ int rc;
+ int len;
+ char *unc, *ipaddr = NULL;
+
+ if (!server->hostname)
+ return -EINVAL;
+
+ len = strlen(server->hostname) + 3;
+
+ unc = kmalloc(len, GFP_KERNEL);
+ if (!unc) {
+ cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
+ return -ENOMEM;
+ }
+ snprintf(unc, len, "\\\\%s", server->hostname);
+
+ rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
+ kfree(unc);
+
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
+ __func__, server->hostname, rc);
+ return rc;
+ }
+
+ spin_lock(&cifs_tcp_ses_lock);
+ rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr,
+ strlen(ipaddr));
+ spin_unlock(&cifs_tcp_ses_lock);
+ kfree(ipaddr);
+
+ return !rc ? -1 : 0;
+}
+#else
+static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
+{
+ return 0;
+}
+#endif
+
+/*
+ * cifs tcp session reconnection
+ *
+ * mark tcp session as reconnecting so temporarily locked
+ * mark all smb sessions as reconnecting for tcp session
+ * reconnect tcp session
+ * wake up waiters on reconnection? - (not needed currently)
+ */
+int
+cifs_reconnect(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ struct list_head *tmp, *tmp2;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct mid_q_entry *mid_entry;
+ struct list_head retry_list;
+
+ spin_lock(&GlobalMid_Lock);
+ if (server->tcpStatus == CifsExiting) {
+ /* the demux thread will exit normally
+ next time through the loop */
+ spin_unlock(&GlobalMid_Lock);
+ return rc;
+ } else
+ server->tcpStatus = CifsNeedReconnect;
+ spin_unlock(&GlobalMid_Lock);
+ server->maxBuf = 0;
+ server->max_read = 0;
+
+ cifs_dbg(FYI, "Reconnecting tcp session\n");
+ trace_smb3_reconnect(server->CurrentMid, server->hostname);
+
+ /* before reconnecting the tcp session, mark the smb session (uid)
+ and the tid bad so they are not used until reconnected */
+ cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n",
+ __func__);
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+ ses->need_reconnect = true;
+ list_for_each(tmp2, &ses->tcon_list) {
+ tcon = list_entry(tmp2, struct cifs_tcon, tcon_list);
+ tcon->need_reconnect = true;
+ }
+ if (ses->tcon_ipc)
+ ses->tcon_ipc->need_reconnect = true;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ /* do not want to be sending data on a socket we are freeing */
+ cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
+ mutex_lock(&server->srv_mutex);
+ if (server->ssocket) {
+ cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
+ server->ssocket->state, server->ssocket->flags);
+ kernel_sock_shutdown(server->ssocket, SHUT_WR);
+ cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n",
+ server->ssocket->state, server->ssocket->flags);
+ sock_release(server->ssocket);
+ server->ssocket = NULL;
+ }
+ server->sequence_number = 0;
+ server->session_estab = false;
+ kfree(server->session_key.response);
+ server->session_key.response = NULL;
+ server->session_key.len = 0;
+ server->lstrp = jiffies;
+
+ /* mark submitted MIDs for retry and issue callback */
+ INIT_LIST_HEAD(&retry_list);
+ cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
+ mid_entry->mid_state = MID_RETRY_NEEDED;
+ list_move(&mid_entry->qhead, &retry_list);
+ }
+ spin_unlock(&GlobalMid_Lock);
+ mutex_unlock(&server->srv_mutex);
+
+ cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
+ list_for_each_safe(tmp, tmp2, &retry_list) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ list_del_init(&mid_entry->qhead);
+ mid_entry->callback(mid_entry);
+ }
+
+ do {
+ try_to_freeze();
+
+ /* we should try only the port we connected to before */
+ mutex_lock(&server->srv_mutex);
+ if (cifs_rdma_enabled(server))
+ rc = smbd_reconnect(server);
+ else
+ rc = generic_ip_connect(server);
+ if (rc) {
+ cifs_dbg(FYI, "reconnect error %d\n", rc);
+ rc = reconn_set_ipaddr(server);
+ if (rc) {
+ cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
+ __func__, rc);
+ }
+ mutex_unlock(&server->srv_mutex);
+ msleep(3000);
+ } else {
+ atomic_inc(&tcpSesReconnectCount);
+ spin_lock(&GlobalMid_Lock);
+ if (server->tcpStatus != CifsExiting)
+ server->tcpStatus = CifsNeedNegotiate;
+ spin_unlock(&GlobalMid_Lock);
+ mutex_unlock(&server->srv_mutex);
+ }
+ } while (server->tcpStatus == CifsNeedReconnect);
+
+ if (server->tcpStatus == CifsNeedNegotiate)
+ mod_delayed_work(cifsiod_wq, &server->echo, 0);
+
+ return rc;
+}
+
+static void
+cifs_echo_request(struct work_struct *work)
+{
+ int rc;
+ struct TCP_Server_Info *server = container_of(work,
+ struct TCP_Server_Info, echo.work);
+ unsigned long echo_interval;
+
+ /*
+ * If we need to renegotiate, set echo interval to zero to
+ * immediately call echo service where we can renegotiate.
+ */
+ if (server->tcpStatus == CifsNeedNegotiate)
+ echo_interval = 0;
+ else
+ echo_interval = server->echo_interval;
+
+ /*
+ * We cannot send an echo if it is disabled.
+ * Also, no need to ping if we got a response recently.
+ */
+
+ if (server->tcpStatus == CifsNeedReconnect ||
+ server->tcpStatus == CifsExiting ||
+ server->tcpStatus == CifsNew ||
+ (server->ops->can_echo && !server->ops->can_echo(server)) ||
+ time_before(jiffies, server->lstrp + echo_interval - HZ))
+ goto requeue_echo;
+
+ rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
+ if (rc)
+ cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
+ server->hostname);
+
+requeue_echo:
+ queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval);
+}
+
+static bool
+allocate_buffers(struct TCP_Server_Info *server)
+{
+ if (!server->bigbuf) {
+ server->bigbuf = (char *)cifs_buf_get();
+ if (!server->bigbuf) {
+ cifs_dbg(VFS, "No memory for large SMB response\n");
+ msleep(3000);
+ /* retry will check if exiting */
+ return false;
+ }
+ } else if (server->large_buf) {
+ /* we are reusing a dirty large buf, clear its start */
+ memset(server->bigbuf, 0, HEADER_SIZE(server));
+ }
+
+ if (!server->smallbuf) {
+ server->smallbuf = (char *)cifs_small_buf_get();
+ if (!server->smallbuf) {
+ cifs_dbg(VFS, "No memory for SMB response\n");
+ msleep(1000);
+ /* retry will check if exiting */
+ return false;
+ }
+ /* beginning of smb buffer is cleared in our buf_get */
+ } else {
+ /* if existing small buf clear beginning */
+ memset(server->smallbuf, 0, HEADER_SIZE(server));
+ }
+
+ return true;
+}
+
+static bool
+server_unresponsive(struct TCP_Server_Info *server)
+{
+ /*
+ * We need to wait 3 echo intervals to make sure we handle such
+ * situations right:
+ * 1s client sends a normal SMB request
+ * 3s client gets a response
+ * 30s echo workqueue job pops, and decides we got a response recently
+ * and don't need to send another
+ * ...
+ * 65s kernel_recvmsg times out, and we see that we haven't gotten
+ * a response in >60s.
+ */
+ if ((server->tcpStatus == CifsGood ||
+ server->tcpStatus == CifsNeedNegotiate) &&
+ time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
+ cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n",
+ server->hostname, (3 * server->echo_interval) / HZ);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool
+zero_credits(struct TCP_Server_Info *server)
+{
+ int val;
+
+ spin_lock(&server->req_lock);
+ val = server->credits + server->echo_credits + server->oplock_credits;
+ if (server->in_flight == 0 && val == 0) {
+ spin_unlock(&server->req_lock);
+ return true;
+ }
+ spin_unlock(&server->req_lock);
+ return false;
+}
+
+static int
+cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
+{
+ int length = 0;
+ int total_read;
+
+ smb_msg->msg_control = NULL;
+ smb_msg->msg_controllen = 0;
+
+ for (total_read = 0; msg_data_left(smb_msg); total_read += length) {
+ try_to_freeze();
+
+ /* reconnect if no credits and no requests in flight */
+ if (zero_credits(server)) {
+ cifs_reconnect(server);
+ return -ECONNABORTED;
+ }
+
+ if (server_unresponsive(server))
+ return -ECONNABORTED;
+ if (cifs_rdma_enabled(server) && server->smbd_conn)
+ length = smbd_recv(server->smbd_conn, smb_msg);
+ else
+ length = sock_recvmsg(server->ssocket, smb_msg, 0);
+
+ if (server->tcpStatus == CifsExiting)
+ return -ESHUTDOWN;
+
+ if (server->tcpStatus == CifsNeedReconnect) {
+ cifs_reconnect(server);
+ return -ECONNABORTED;
+ }
+
+ if (length == -ERESTARTSYS ||
+ length == -EAGAIN ||
+ length == -EINTR) {
+ /*
+ * Minimum sleep to prevent looping, allowing socket
+ * to clear and app threads to set tcpStatus
+ * CifsNeedReconnect if server hung.
+ */
+ usleep_range(1000, 2000);
+ length = 0;
+ continue;
+ }
+
+ if (length <= 0) {
+ cifs_dbg(FYI, "Received no data or error: %d\n", length);
+ cifs_reconnect(server);
+ return -ECONNABORTED;
+ }
+ }
+ return total_read;
+}
+
+int
+cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ unsigned int to_read)
+{
+ struct msghdr smb_msg;
+ struct kvec iov = {.iov_base = buf, .iov_len = to_read};
+ iov_iter_kvec(&smb_msg.msg_iter, READ | ITER_KVEC, &iov, 1, to_read);
+
+ return cifs_readv_from_socket(server, &smb_msg);
+}
+
+int
+cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
+ unsigned int page_offset, unsigned int to_read)
+{
+ struct msghdr smb_msg;
+ struct bio_vec bv = {
+ .bv_page = page, .bv_len = to_read, .bv_offset = page_offset};
+ iov_iter_bvec(&smb_msg.msg_iter, READ | ITER_BVEC, &bv, 1, to_read);
+ return cifs_readv_from_socket(server, &smb_msg);
+}
+
+static bool
+is_smb_response(struct TCP_Server_Info *server, unsigned char type)
+{
+ /*
+ * The first byte big endian of the length field,
+ * is actually not part of the length but the type
+ * with the most common, zero, as regular data.
+ */
+ switch (type) {
+ case RFC1002_SESSION_MESSAGE:
+ /* Regular SMB response */
+ return true;
+ case RFC1002_SESSION_KEEP_ALIVE:
+ cifs_dbg(FYI, "RFC 1002 session keep alive\n");
+ break;
+ case RFC1002_POSITIVE_SESSION_RESPONSE:
+ cifs_dbg(FYI, "RFC 1002 positive session response\n");
+ break;
+ case RFC1002_NEGATIVE_SESSION_RESPONSE:
+ /*
+ * We get this from Windows 98 instead of an error on
+ * SMB negprot response.
+ */
+ cifs_dbg(FYI, "RFC 1002 negative session response\n");
+ /* give server a second to clean up */
+ msleep(1000);
+ /*
+ * Always try 445 first on reconnect since we get NACK
+ * on some if we ever connected to port 139 (the NACK
+ * is since we do not begin with RFC1001 session
+ * initialize frame).
+ */
+ cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ break;
+ default:
+ cifs_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
+ cifs_reconnect(server);
+ }
+
+ return false;
+}
+
+void
+dequeue_mid(struct mid_q_entry *mid, bool malformed)
+{
+#ifdef CONFIG_CIFS_STATS2
+ mid->when_received = jiffies;
+#endif
+ spin_lock(&GlobalMid_Lock);
+ if (!malformed)
+ mid->mid_state = MID_RESPONSE_RECEIVED;
+ else
+ mid->mid_state = MID_RESPONSE_MALFORMED;
+ /*
+ * Trying to handle/dequeue a mid after the send_recv()
+ * function has finished processing it is a bug.
+ */
+ if (mid->mid_flags & MID_DELETED)
+ printk_once(KERN_WARNING
+ "trying to dequeue a deleted mid\n");
+ else
+ list_del_init(&mid->qhead);
+ spin_unlock(&GlobalMid_Lock);
+}
+
+static void
+handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ char *buf, int malformed)
+{
+ if (server->ops->check_trans2 &&
+ server->ops->check_trans2(mid, server, buf, malformed))
+ return;
+ mid->resp_buf = buf;
+ mid->large_buf = server->large_buf;
+ /* Was previous buf put in mpx struct for multi-rsp? */
+ if (!mid->multiRsp) {
+ /* smb buffer will be freed by user thread */
+ if (server->large_buf)
+ server->bigbuf = NULL;
+ else
+ server->smallbuf = NULL;
+ }
+ dequeue_mid(mid, malformed);
+}
+
+static void clean_demultiplex_info(struct TCP_Server_Info *server)
+{
+ int length;
+
+ /* take it off the list, if it's not already */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_del_init(&server->tcp_ses_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ cancel_delayed_work_sync(&server->echo);
+
+ spin_lock(&GlobalMid_Lock);
+ server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+ wake_up_all(&server->response_q);
+
+ /* check if we have blocked requests that need to free */
+ spin_lock(&server->req_lock);
+ if (server->credits <= 0)
+ server->credits = 1;
+ spin_unlock(&server->req_lock);
+ /*
+ * Although there should not be any requests blocked on this queue it
+ * can not hurt to be paranoid and try to wake up requests that may
+ * haven been blocked when more than 50 at time were on the wire to the
+ * same server - they now will see the session is in exit state and get
+ * out of SendReceive.
+ */
+ wake_up_all(&server->request_q);
+ /* give those requests time to exit */
+ msleep(125);
+ if (cifs_rdma_enabled(server) && server->smbd_conn) {
+ smbd_destroy(server->smbd_conn);
+ server->smbd_conn = NULL;
+ }
+ if (server->ssocket) {
+ sock_release(server->ssocket);
+ server->ssocket = NULL;
+ }
+
+ if (!list_empty(&server->pending_mid_q)) {
+ struct list_head dispose_list;
+ struct mid_q_entry *mid_entry;
+ struct list_head *tmp, *tmp2;
+
+ INIT_LIST_HEAD(&dispose_list);
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid);
+ mid_entry->mid_state = MID_SHUTDOWN;
+ list_move(&mid_entry->qhead, &dispose_list);
+ }
+ spin_unlock(&GlobalMid_Lock);
+
+ /* now walk dispose list and issue callbacks */
+ list_for_each_safe(tmp, tmp2, &dispose_list) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid);
+ list_del_init(&mid_entry->qhead);
+ mid_entry->callback(mid_entry);
+ }
+ /* 1/8th of sec is more than enough time for them to exit */
+ msleep(125);
+ }
+
+ if (!list_empty(&server->pending_mid_q)) {
+ /*
+ * mpx threads have not exited yet give them at least the smb
+ * send timeout time for long ops.
+ *
+ * Due to delays on oplock break requests, we need to wait at
+ * least 45 seconds before giving up on a request getting a
+ * response and going ahead and killing cifsd.
+ */
+ cifs_dbg(FYI, "Wait for exit from demultiplex thread\n");
+ msleep(46000);
+ /*
+ * If threads still have not exited they are probably never
+ * coming home not much else we can do but free the memory.
+ */
+ }
+
+ kfree(server->hostname);
+ kfree(server);
+
+ length = atomic_dec_return(&tcpSesAllocCount);
+ if (length > 0)
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
+}
+
+static int
+standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ int length;
+ char *buf = server->smallbuf;
+ unsigned int pdu_length = server->pdu_size;
+
+ /* make sure this will fit in a large buffer */
+ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
+ server->vals->header_preamble_size) {
+ cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -ECONNABORTED;
+ }
+
+ /* switch to large buffer if too big for a small one */
+ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
+ server->large_buf = true;
+ memcpy(server->bigbuf, buf, server->total_read);
+ buf = server->bigbuf;
+ }
+
+ /* now read the rest */
+ length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
+ pdu_length - HEADER_SIZE(server) + 1
+ + server->vals->header_preamble_size);
+
+ if (length < 0)
+ return length;
+ server->total_read += length;
+
+ dump_smb(buf, server->total_read);
+
+ return cifs_handle_standard(server, mid);
+}
+
+int
+cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
+ int length;
+
+ /*
+ * We know that we received enough to get to the MID as we
+ * checked the pdu_length earlier. Now check to see
+ * if the rest of the header is OK. We borrow the length
+ * var for the rest of the loop to avoid a new stack var.
+ *
+ * 48 bytes is enough to display the header and a little bit
+ * into the payload for debugging purposes.
+ */
+ length = server->ops->check_message(buf, server->total_read, server);
+ if (length != 0)
+ cifs_dump_mem("Bad SMB: ", buf,
+ min_t(unsigned int, server->total_read, 48));
+
+ if (server->ops->is_session_expired &&
+ server->ops->is_session_expired(buf)) {
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -1;
+ }
+
+ if (server->ops->is_status_pending &&
+ server->ops->is_status_pending(buf, server, length))
+ return -1;
+
+ if (!mid)
+ return length;
+
+ handle_mid(mid, server, buf, length);
+ return 0;
+}
+
+static void
+smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
+
+ /*
+ * SMB1 does not use credits.
+ */
+ if (server->vals->header_preamble_size)
+ return;
+
+ if (shdr->CreditRequest) {
+ spin_lock(&server->req_lock);
+ server->credits += le16_to_cpu(shdr->CreditRequest);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ }
+}
+
+
+static int
+cifs_demultiplex_thread(void *p)
+{
+ int i, num_mids, length;
+ struct TCP_Server_Info *server = p;
+ unsigned int pdu_length;
+ unsigned int next_offset;
+ char *buf = NULL;
+ struct task_struct *task_to_wake = NULL;
+ struct mid_q_entry *mids[MAX_COMPOUND];
+ char *bufs[MAX_COMPOUND];
+
+ current->flags |= PF_MEMALLOC;
+ cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current));
+
+ length = atomic_inc_return(&tcpSesAllocCount);
+ if (length > 1)
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
+
+ set_freezable();
+ allow_kernel_signal(SIGKILL);
+ while (server->tcpStatus != CifsExiting) {
+ if (try_to_freeze())
+ continue;
+
+ if (!allocate_buffers(server))
+ continue;
+
+ server->large_buf = false;
+ buf = server->smallbuf;
+ pdu_length = 4; /* enough to get RFC1001 header */
+
+ length = cifs_read_from_socket(server, buf, pdu_length);
+ if (length < 0)
+ continue;
+
+ if (server->vals->header_preamble_size == 0)
+ server->total_read = 0;
+ else
+ server->total_read = length;
+
+ /*
+ * The right amount was read from socket - 4 bytes,
+ * so we can now interpret the length field.
+ */
+ pdu_length = get_rfc1002_length(buf);
+
+ cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
+ if (!is_smb_response(server, buf[0]))
+ continue;
+next_pdu:
+ server->pdu_size = pdu_length;
+
+ /* make sure we have enough to get to the MID */
+ if (server->pdu_size < HEADER_SIZE(server) - 1 -
+ server->vals->header_preamble_size) {
+ cifs_dbg(VFS, "SMB response too short (%u bytes)\n",
+ server->pdu_size);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ continue;
+ }
+
+ /* read down to the MID */
+ length = cifs_read_from_socket(server,
+ buf + server->vals->header_preamble_size,
+ HEADER_SIZE(server) - 1
+ - server->vals->header_preamble_size);
+ if (length < 0)
+ continue;
+ server->total_read += length;
+
+ if (server->ops->next_header) {
+ next_offset = server->ops->next_header(buf);
+ if (next_offset)
+ server->pdu_size = next_offset;
+ }
+
+ memset(mids, 0, sizeof(mids));
+ memset(bufs, 0, sizeof(bufs));
+ num_mids = 0;
+
+ if (server->ops->is_transform_hdr &&
+ server->ops->receive_transform &&
+ server->ops->is_transform_hdr(buf)) {
+ length = server->ops->receive_transform(server,
+ mids,
+ bufs,
+ &num_mids);
+ } else {
+ mids[0] = server->ops->find_mid(server, buf);
+ bufs[0] = buf;
+ num_mids = 1;
+
+ if (!mids[0] || !mids[0]->receive)
+ length = standard_receive3(server, mids[0]);
+ else
+ length = mids[0]->receive(server, mids[0]);
+ }
+
+ if (length < 0) {
+ for (i = 0; i < num_mids; i++)
+ if (mids[i])
+ cifs_mid_q_entry_release(mids[i]);
+ continue;
+ }
+
+ if (server->large_buf)
+ buf = server->bigbuf;
+
+
+ server->lstrp = jiffies;
+
+ for (i = 0; i < num_mids; i++) {
+ if (mids[i] != NULL) {
+ mids[i]->resp_buf_size = server->pdu_size;
+ if ((mids[i]->mid_flags & MID_WAIT_CANCELLED) &&
+ mids[i]->mid_state == MID_RESPONSE_RECEIVED &&
+ server->ops->handle_cancelled_mid)
+ server->ops->handle_cancelled_mid(
+ mids[i]->resp_buf,
+ server);
+
+ if (!mids[i]->multiRsp || mids[i]->multiEnd)
+ mids[i]->callback(mids[i]);
+
+ cifs_mid_q_entry_release(mids[i]);
+ } else if (server->ops->is_oplock_break &&
+ server->ops->is_oplock_break(bufs[i],
+ server)) {
+ smb2_add_credits_from_hdr(bufs[i], server);
+ cifs_dbg(FYI, "Received oplock break\n");
+ } else {
+ cifs_dbg(VFS, "No task to wake, unknown frame "
+ "received! NumMids %d\n",
+ atomic_read(&midCount));
+ cifs_dump_mem("Received Data is: ", bufs[i],
+ HEADER_SIZE(server));
+#ifdef CONFIG_CIFS_DEBUG2
+ if (server->ops->dump_detail)
+ server->ops->dump_detail(bufs[i],
+ server);
+ smb2_add_credits_from_hdr(bufs[i], server);
+ cifs_dump_mids(server);
+#endif /* CIFS_DEBUG2 */
+ }
+ }
+
+ if (pdu_length > server->pdu_size) {
+ if (!allocate_buffers(server))
+ continue;
+ pdu_length -= server->pdu_size;
+ server->total_read = 0;
+ server->large_buf = false;
+ buf = server->smallbuf;
+ goto next_pdu;
+ }
+ } /* end while !EXITING */
+
+ /* buffer usually freed in free_mid - need to free it here on exit */
+ cifs_buf_release(server->bigbuf);
+ if (server->smallbuf) /* no sense logging a debug message if NULL */
+ cifs_small_buf_release(server->smallbuf);
+
+ task_to_wake = xchg(&server->tsk, NULL);
+ clean_demultiplex_info(server);
+
+ /* if server->tsk was NULL then wait for a signal before exiting */
+ if (!task_to_wake) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!signal_pending(current)) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ }
+
+ module_put_and_exit(0);
+}
+
+/* extract the host portion of the UNC string */
+static char *
+extract_hostname(const char *unc)
+{
+ const char *src;
+ char *dst, *delim;
+ unsigned int len;
+
+ /* skip double chars at beginning of string */
+ /* BB: check validity of these bytes? */
+ src = unc + 2;
+
+ /* delimiter between hostname and sharename is always '\\' now */
+ delim = strchr(src, '\\');
+ if (!delim)
+ return ERR_PTR(-EINVAL);
+
+ len = delim - src;
+ dst = kmalloc((len + 1), GFP_KERNEL);
+ if (dst == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+
+ return dst;
+}
+
+static int get_option_ul(substring_t args[], unsigned long *option)
+{
+ int rc;
+ char *string;
+
+ string = match_strdup(args);
+ if (string == NULL)
+ return -ENOMEM;
+ rc = kstrtoul(string, 0, option);
+ kfree(string);
+
+ return rc;
+}
+
+static int get_option_uid(substring_t args[], kuid_t *result)
+{
+ unsigned long value;
+ kuid_t uid;
+ int rc;
+
+ rc = get_option_ul(args, &value);
+ if (rc)
+ return rc;
+
+ uid = make_kuid(current_user_ns(), value);
+ if (!uid_valid(uid))
+ return -EINVAL;
+
+ *result = uid;
+ return 0;
+}
+
+static int get_option_gid(substring_t args[], kgid_t *result)
+{
+ unsigned long value;
+ kgid_t gid;
+ int rc;
+
+ rc = get_option_ul(args, &value);
+ if (rc)
+ return rc;
+
+ gid = make_kgid(current_user_ns(), value);
+ if (!gid_valid(gid))
+ return -EINVAL;
+
+ *result = gid;
+ return 0;
+}
+
+static int cifs_parse_security_flavors(char *value,
+ struct smb_vol *vol)
+{
+
+ substring_t args[MAX_OPT_ARGS];
+
+ /*
+ * With mount options, the last one should win. Reset any existing
+ * settings back to default.
+ */
+ vol->sectype = Unspecified;
+ vol->sign = false;
+
+ switch (match_token(value, cifs_secflavor_tokens, args)) {
+ case Opt_sec_krb5p:
+ cifs_dbg(VFS, "sec=krb5p is not supported!\n");
+ return 1;
+ case Opt_sec_krb5i:
+ vol->sign = true;
+ /* Fallthrough */
+ case Opt_sec_krb5:
+ vol->sectype = Kerberos;
+ break;
+ case Opt_sec_ntlmsspi:
+ vol->sign = true;
+ /* Fallthrough */
+ case Opt_sec_ntlmssp:
+ vol->sectype = RawNTLMSSP;
+ break;
+ case Opt_sec_ntlmi:
+ vol->sign = true;
+ /* Fallthrough */
+ case Opt_ntlm:
+ vol->sectype = NTLM;
+ break;
+ case Opt_sec_ntlmv2i:
+ vol->sign = true;
+ /* Fallthrough */
+ case Opt_sec_ntlmv2:
+ vol->sectype = NTLMv2;
+ break;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ case Opt_sec_lanman:
+ vol->sectype = LANMAN;
+ break;
+#endif
+ case Opt_sec_none:
+ vol->nullauth = 1;
+ break;
+ default:
+ cifs_dbg(VFS, "bad security option: %s\n", value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
+{
+ substring_t args[MAX_OPT_ARGS];
+
+ switch (match_token(value, cifs_cacheflavor_tokens, args)) {
+ case Opt_cache_loose:
+ vol->direct_io = false;
+ vol->strict_io = false;
+ break;
+ case Opt_cache_strict:
+ vol->direct_io = false;
+ vol->strict_io = true;
+ break;
+ case Opt_cache_none:
+ vol->direct_io = true;
+ vol->strict_io = false;
+ break;
+ default:
+ cifs_dbg(VFS, "bad cache= option: %s\n", value);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3)
+{
+ substring_t args[MAX_OPT_ARGS];
+
+ switch (match_token(value, cifs_smb_version_tokens, args)) {
+#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
+ case Smb_1:
+ if (disable_legacy_dialects) {
+ cifs_dbg(VFS, "mount with legacy dialect disabled\n");
+ return 1;
+ }
+ if (is_smb3) {
+ cifs_dbg(VFS, "vers=1.0 (cifs) not permitted when mounting with smb3\n");
+ return 1;
+ }
+ vol->ops = &smb1_operations;
+ vol->vals = &smb1_values;
+ break;
+ case Smb_20:
+ if (disable_legacy_dialects) {
+ cifs_dbg(VFS, "mount with legacy dialect disabled\n");
+ return 1;
+ }
+ if (is_smb3) {
+ cifs_dbg(VFS, "vers=2.0 not permitted when mounting with smb3\n");
+ return 1;
+ }
+ vol->ops = &smb20_operations;
+ vol->vals = &smb20_values;
+ break;
+#else
+ case Smb_1:
+ cifs_dbg(VFS, "vers=1.0 (cifs) mount not permitted when legacy dialects disabled\n");
+ return 1;
+ case Smb_20:
+ cifs_dbg(VFS, "vers=2.0 mount not permitted when legacy dialects disabled\n");
+ return 1;
+#endif /* CIFS_ALLOW_INSECURE_LEGACY */
+ case Smb_21:
+ vol->ops = &smb21_operations;
+ vol->vals = &smb21_values;
+ break;
+ case Smb_30:
+ vol->ops = &smb30_operations;
+ vol->vals = &smb30_values;
+ break;
+ case Smb_302:
+ vol->ops = &smb30_operations; /* currently identical with 3.0 */
+ vol->vals = &smb302_values;
+ break;
+ case Smb_311:
+ vol->ops = &smb311_operations;
+ vol->vals = &smb311_values;
+ break;
+ case Smb_3any:
+ vol->ops = &smb30_operations; /* currently identical with 3.0 */
+ vol->vals = &smb3any_values;
+ break;
+ case Smb_default:
+ vol->ops = &smb30_operations; /* currently identical with 3.0 */
+ vol->vals = &smbdefault_values;
+ break;
+ default:
+ cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Parse a devname into substrings and populate the vol->UNC and vol->prepath
+ * fields with the result. Returns 0 on success and an error otherwise.
+ */
+static int
+cifs_parse_devname(const char *devname, struct smb_vol *vol)
+{
+ char *pos;
+ const char *delims = "/\\";
+ size_t len;
+
+ if (unlikely(!devname || !*devname)) {
+ cifs_dbg(VFS, "Device name not specified.\n");
+ return -EINVAL;
+ }
+
+ /* make sure we have a valid UNC double delimiter prefix */
+ len = strspn(devname, delims);
+ if (len != 2)
+ return -EINVAL;
+
+ /* find delimiter between host and sharename */
+ pos = strpbrk(devname + 2, delims);
+ if (!pos)
+ return -EINVAL;
+
+ /* skip past delimiter */
+ ++pos;
+
+ /* now go until next delimiter or end of string */
+ len = strcspn(pos, delims);
+
+ /* move "pos" up to delimiter or NULL */
+ pos += len;
+ vol->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
+ if (!vol->UNC)
+ return -ENOMEM;
+
+ convert_delimiter(vol->UNC, '\\');
+
+ /* skip any delimiter */
+ if (*pos == '/' || *pos == '\\')
+ pos++;
+
+ /* If pos is NULL then no prepath */
+ if (!*pos)
+ return 0;
+
+ vol->prepath = kstrdup(pos, GFP_KERNEL);
+ if (!vol->prepath)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+cifs_parse_mount_options(const char *mountdata, const char *devname,
+ struct smb_vol *vol, bool is_smb3)
+{
+ char *data, *end;
+ char *mountdata_copy = NULL, *options;
+ unsigned int temp_len, i, j;
+ char separator[2];
+ short int override_uid = -1;
+ short int override_gid = -1;
+ bool uid_specified = false;
+ bool gid_specified = false;
+ bool sloppy = false;
+ char *invalid = NULL;
+ char *nodename = utsname()->nodename;
+ char *string = NULL;
+ char *tmp_end, *value;
+ char delim;
+ bool got_ip = false;
+ bool got_version = false;
+ unsigned short port = 0;
+ struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr;
+
+ separator[0] = ',';
+ separator[1] = 0;
+ delim = separator[0];
+
+ /* ensure we always start with zeroed-out smb_vol */
+ memset(vol, 0, sizeof(*vol));
+
+ /*
+ * does not have to be perfect mapping since field is
+ * informational, only used for servers that do not support
+ * port 445 and it can be overridden at mount time
+ */
+ memset(vol->source_rfc1001_name, 0x20, RFC1001_NAME_LEN);
+ for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++)
+ vol->source_rfc1001_name[i] = toupper(nodename[i]);
+
+ vol->source_rfc1001_name[RFC1001_NAME_LEN] = 0;
+ /* null target name indicates to use *SMBSERVR default called name
+ if we end up sending RFC1001 session initialize */
+ vol->target_rfc1001_name[0] = 0;
+ vol->cred_uid = current_uid();
+ vol->linux_uid = current_uid();
+ vol->linux_gid = current_gid();
+
+ /*
+ * default to SFM style remapping of seven reserved characters
+ * unless user overrides it or we negotiate CIFS POSIX where
+ * it is unnecessary. Can not simultaneously use more than one mapping
+ * since then readdir could list files that open could not open
+ */
+ vol->remap = true;
+
+ /* default to only allowing write access to owner of the mount */
+ vol->dir_mode = vol->file_mode = S_IRUGO | S_IXUGO | S_IWUSR;
+
+ /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
+ /* default is always to request posix paths. */
+ vol->posix_paths = 1;
+ /* default to using server inode numbers where available */
+ vol->server_ino = 1;
+
+ /* default is to use strict cifs caching semantics */
+ vol->strict_io = true;
+
+ vol->actimeo = CIFS_DEF_ACTIMEO;
+
+ /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */
+ vol->ops = &smb30_operations;
+ vol->vals = &smbdefault_values;
+
+ vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
+
+ if (!mountdata)
+ goto cifs_parse_mount_err;
+
+ mountdata_copy = kstrndup(mountdata, PAGE_SIZE, GFP_KERNEL);
+ if (!mountdata_copy)
+ goto cifs_parse_mount_err;
+
+ options = mountdata_copy;
+ end = options + strlen(options);
+
+ if (strncmp(options, "sep=", 4) == 0) {
+ if (options[4] != 0) {
+ separator[0] = options[4];
+ options += 5;
+ } else {
+ cifs_dbg(FYI, "Null separator not allowed\n");
+ }
+ }
+ vol->backupuid_specified = false; /* no backup intent for a user */
+ vol->backupgid_specified = false; /* no backup intent for a group */
+
+ switch (cifs_parse_devname(devname, vol)) {
+ case 0:
+ break;
+ case -ENOMEM:
+ cifs_dbg(VFS, "Unable to allocate memory for devname.\n");
+ goto cifs_parse_mount_err;
+ case -EINVAL:
+ cifs_dbg(VFS, "Malformed UNC in devname.\n");
+ goto cifs_parse_mount_err;
+ default:
+ cifs_dbg(VFS, "Unknown error parsing devname.\n");
+ goto cifs_parse_mount_err;
+ }
+
+ while ((data = strsep(&options, separator)) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+ unsigned long option;
+ int token;
+
+ if (!*data)
+ continue;
+
+ token = match_token(data, cifs_mount_option_tokens, args);
+
+ switch (token) {
+
+ /* Ingnore the following */
+ case Opt_ignore:
+ break;
+
+ /* Boolean values */
+ case Opt_user_xattr:
+ vol->no_xattr = 0;
+ break;
+ case Opt_nouser_xattr:
+ vol->no_xattr = 1;
+ break;
+ case Opt_forceuid:
+ override_uid = 1;
+ break;
+ case Opt_noforceuid:
+ override_uid = 0;
+ break;
+ case Opt_forcegid:
+ override_gid = 1;
+ break;
+ case Opt_noforcegid:
+ override_gid = 0;
+ break;
+ case Opt_noblocksend:
+ vol->noblocksnd = 1;
+ break;
+ case Opt_noautotune:
+ vol->noautotune = 1;
+ break;
+ case Opt_nolease:
+ vol->no_lease = 1;
+ break;
+ case Opt_hard:
+ vol->retry = 1;
+ break;
+ case Opt_soft:
+ vol->retry = 0;
+ break;
+ case Opt_perm:
+ vol->noperm = 0;
+ break;
+ case Opt_noperm:
+ vol->noperm = 1;
+ break;
+ case Opt_mapchars:
+ vol->sfu_remap = true;
+ vol->remap = false; /* disable SFM mapping */
+ break;
+ case Opt_nomapchars:
+ vol->sfu_remap = false;
+ break;
+ case Opt_mapposix:
+ vol->remap = true;
+ vol->sfu_remap = false; /* disable SFU mapping */
+ break;
+ case Opt_nomapposix:
+ vol->remap = false;
+ break;
+ case Opt_sfu:
+ vol->sfu_emul = 1;
+ break;
+ case Opt_nosfu:
+ vol->sfu_emul = 0;
+ break;
+ case Opt_nodfs:
+ vol->nodfs = 1;
+ break;
+ case Opt_posixpaths:
+ vol->posix_paths = 1;
+ break;
+ case Opt_noposixpaths:
+ vol->posix_paths = 0;
+ break;
+ case Opt_nounix:
+ if (vol->linux_ext)
+ cifs_dbg(VFS,
+ "conflicting unix mount options\n");
+ vol->no_linux_ext = 1;
+ break;
+ case Opt_unix:
+ if (vol->no_linux_ext)
+ cifs_dbg(VFS,
+ "conflicting unix mount options\n");
+ vol->linux_ext = 1;
+ break;
+ case Opt_nocase:
+ vol->nocase = 1;
+ break;
+ case Opt_brl:
+ vol->nobrl = 0;
+ break;
+ case Opt_nobrl:
+ vol->nobrl = 1;
+ /*
+ * turn off mandatory locking in mode
+ * if remote locking is turned off since the
+ * local vfs will do advisory
+ */
+ if (vol->file_mode ==
+ (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
+ vol->file_mode = S_IALLUGO;
+ break;
+ case Opt_nohandlecache:
+ vol->nohandlecache = 1;
+ break;
+ case Opt_handlecache:
+ vol->nohandlecache = 0;
+ break;
+ case Opt_forcemandatorylock:
+ vol->mand_lock = 1;
+ break;
+ case Opt_setuids:
+ vol->setuids = 1;
+ break;
+ case Opt_nosetuids:
+ vol->setuids = 0;
+ break;
+ case Opt_setuidfromacl:
+ vol->setuidfromacl = 1;
+ break;
+ case Opt_dynperm:
+ vol->dynperm = true;
+ break;
+ case Opt_nodynperm:
+ vol->dynperm = false;
+ break;
+ case Opt_nohard:
+ vol->retry = 0;
+ break;
+ case Opt_nosoft:
+ vol->retry = 1;
+ break;
+ case Opt_nointr:
+ vol->intr = 0;
+ break;
+ case Opt_intr:
+ vol->intr = 1;
+ break;
+ case Opt_nostrictsync:
+ vol->nostrictsync = 1;
+ break;
+ case Opt_strictsync:
+ vol->nostrictsync = 0;
+ break;
+ case Opt_serverino:
+ vol->server_ino = 1;
+ break;
+ case Opt_noserverino:
+ vol->server_ino = 0;
+ break;
+ case Opt_rwpidforward:
+ vol->rwpidforward = 1;
+ break;
+ case Opt_cifsacl:
+ vol->cifs_acl = 1;
+ break;
+ case Opt_nocifsacl:
+ vol->cifs_acl = 0;
+ break;
+ case Opt_acl:
+ vol->no_psx_acl = 0;
+ break;
+ case Opt_noacl:
+ vol->no_psx_acl = 1;
+ break;
+ case Opt_locallease:
+ vol->local_lease = 1;
+ break;
+ case Opt_sign:
+ vol->sign = true;
+ break;
+ case Opt_seal:
+ /* we do not do the following in secFlags because seal
+ * is a per tree connection (mount) not a per socket
+ * or per-smb connection option in the protocol
+ * vol->secFlg |= CIFSSEC_MUST_SEAL;
+ */
+ vol->seal = 1;
+ break;
+ case Opt_noac:
+ pr_warn("CIFS: Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n");
+ break;
+ case Opt_fsc:
+#ifndef CONFIG_CIFS_FSCACHE
+ cifs_dbg(VFS, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n");
+ goto cifs_parse_mount_err;
+#endif
+ vol->fsc = true;
+ break;
+ case Opt_mfsymlinks:
+ vol->mfsymlinks = true;
+ break;
+ case Opt_multiuser:
+ vol->multiuser = true;
+ break;
+ case Opt_sloppy:
+ sloppy = true;
+ break;
+ case Opt_nosharesock:
+ vol->nosharesock = true;
+ break;
+ case Opt_nopersistent:
+ vol->nopersistent = true;
+ if (vol->persistent) {
+ cifs_dbg(VFS,
+ "persistenthandles mount options conflict\n");
+ goto cifs_parse_mount_err;
+ }
+ break;
+ case Opt_persistent:
+ vol->persistent = true;
+ if ((vol->nopersistent) || (vol->resilient)) {
+ cifs_dbg(VFS,
+ "persistenthandles mount options conflict\n");
+ goto cifs_parse_mount_err;
+ }
+ break;
+ case Opt_resilient:
+ vol->resilient = true;
+ if (vol->persistent) {
+ cifs_dbg(VFS,
+ "persistenthandles mount options conflict\n");
+ goto cifs_parse_mount_err;
+ }
+ break;
+ case Opt_noresilient:
+ vol->resilient = false; /* already the default */
+ break;
+ case Opt_domainauto:
+ vol->domainauto = true;
+ break;
+ case Opt_rdma:
+ vol->rdma = true;
+ break;
+
+ /* Numeric Values */
+ case Opt_backupuid:
+ if (get_option_uid(args, &vol->backupuid)) {
+ cifs_dbg(VFS, "%s: Invalid backupuid value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->backupuid_specified = true;
+ break;
+ case Opt_backupgid:
+ if (get_option_gid(args, &vol->backupgid)) {
+ cifs_dbg(VFS, "%s: Invalid backupgid value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->backupgid_specified = true;
+ break;
+ case Opt_uid:
+ if (get_option_uid(args, &vol->linux_uid)) {
+ cifs_dbg(VFS, "%s: Invalid uid value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ uid_specified = true;
+ break;
+ case Opt_cruid:
+ if (get_option_uid(args, &vol->cred_uid)) {
+ cifs_dbg(VFS, "%s: Invalid cruid value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ break;
+ case Opt_gid:
+ if (get_option_gid(args, &vol->linux_gid)) {
+ cifs_dbg(VFS, "%s: Invalid gid value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ gid_specified = true;
+ break;
+ case Opt_file_mode:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid file_mode value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->file_mode = option;
+ break;
+ case Opt_dirmode:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid dir_mode value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->dir_mode = option;
+ break;
+ case Opt_port:
+ if (get_option_ul(args, &option) ||
+ option > USHRT_MAX) {
+ cifs_dbg(VFS, "%s: Invalid port value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ port = (unsigned short)option;
+ break;
+ case Opt_rsize:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid rsize value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->rsize = option;
+ break;
+ case Opt_wsize:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid wsize value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->wsize = option;
+ break;
+ case Opt_actimeo:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid actimeo value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->actimeo = HZ * option;
+ if (vol->actimeo > CIFS_MAX_ACTIMEO) {
+ cifs_dbg(VFS, "attribute cache timeout too large\n");
+ goto cifs_parse_mount_err;
+ }
+ break;
+ case Opt_echo_interval:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid echo interval value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->echo_interval = option;
+ break;
+ case Opt_snapshot:
+ if (get_option_ul(args, &option)) {
+ cifs_dbg(VFS, "%s: Invalid snapshot time\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->snapshot_time = option;
+ break;
+ case Opt_max_credits:
+ if (get_option_ul(args, &option) || (option < 20) ||
+ (option > 60000)) {
+ cifs_dbg(VFS, "%s: Invalid max_credits value\n",
+ __func__);
+ goto cifs_parse_mount_err;
+ }
+ vol->max_credits = option;
+ break;
+
+ /* String Arguments */
+
+ case Opt_blank_user:
+ /* null user, ie. anonymous authentication */
+ vol->nullauth = 1;
+ vol->username = NULL;
+ break;
+ case Opt_user:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (strnlen(string, CIFS_MAX_USERNAME_LEN) >
+ CIFS_MAX_USERNAME_LEN) {
+ pr_warn("CIFS: username too long\n");
+ goto cifs_parse_mount_err;
+ }
+
+ kfree(vol->username);
+ vol->username = kstrdup(string, GFP_KERNEL);
+ if (!vol->username)
+ goto cifs_parse_mount_err;
+ break;
+ case Opt_blank_pass:
+ /* passwords have to be handled differently
+ * to allow the character used for deliminator
+ * to be passed within them
+ */
+
+ /*
+ * Check if this is a case where the password
+ * starts with a delimiter
+ */
+ tmp_end = strchr(data, '=');
+ tmp_end++;
+ if (!(tmp_end < end && tmp_end[1] == delim)) {
+ /* No it is not. Set the password to NULL */
+ kzfree(vol->password);
+ vol->password = NULL;
+ break;
+ }
+ /* Yes it is. Drop down to Opt_pass below.*/
+ case Opt_pass:
+ /* Obtain the value string */
+ value = strchr(data, '=');
+ value++;
+
+ /* Set tmp_end to end of the string */
+ tmp_end = (char *) value + strlen(value);
+
+ /* Check if following character is the deliminator
+ * If yes, we have encountered a double deliminator
+ * reset the NULL character to the deliminator
+ */
+ if (tmp_end < end && tmp_end[1] == delim) {
+ tmp_end[0] = delim;
+
+ /* Keep iterating until we get to a single
+ * deliminator OR the end
+ */
+ while ((tmp_end = strchr(tmp_end, delim))
+ != NULL && (tmp_end[1] == delim)) {
+ tmp_end = (char *) &tmp_end[2];
+ }
+
+ /* Reset var options to point to next element */
+ if (tmp_end) {
+ tmp_end[0] = '\0';
+ options = (char *) &tmp_end[1];
+ } else
+ /* Reached the end of the mount option
+ * string */
+ options = end;
+ }
+
+ kzfree(vol->password);
+ /* Now build new password string */
+ temp_len = strlen(value);
+ vol->password = kzalloc(temp_len+1, GFP_KERNEL);
+ if (vol->password == NULL) {
+ pr_warn("CIFS: no memory for password\n");
+ goto cifs_parse_mount_err;
+ }
+
+ for (i = 0, j = 0; i < temp_len; i++, j++) {
+ vol->password[j] = value[i];
+ if ((value[i] == delim) &&
+ value[i+1] == delim)
+ /* skip the second deliminator */
+ i++;
+ }
+ vol->password[j] = '\0';
+ break;
+ case Opt_blank_ip:
+ /* FIXME: should this be an error instead? */
+ got_ip = false;
+ break;
+ case Opt_ip:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (!cifs_convert_address(dstaddr, string,
+ strlen(string))) {
+ pr_err("CIFS: bad ip= option (%s).\n", string);
+ goto cifs_parse_mount_err;
+ }
+ got_ip = true;
+ break;
+ case Opt_domain:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (strnlen(string, CIFS_MAX_DOMAINNAME_LEN)
+ == CIFS_MAX_DOMAINNAME_LEN) {
+ pr_warn("CIFS: domain name too long\n");
+ goto cifs_parse_mount_err;
+ }
+
+ kfree(vol->domainname);
+ vol->domainname = kstrdup(string, GFP_KERNEL);
+ if (!vol->domainname) {
+ pr_warn("CIFS: no memory for domainname\n");
+ goto cifs_parse_mount_err;
+ }
+ cifs_dbg(FYI, "Domain name set\n");
+ break;
+ case Opt_srcaddr:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (!cifs_convert_address(
+ (struct sockaddr *)&vol->srcaddr,
+ string, strlen(string))) {
+ pr_warn("CIFS: Could not parse srcaddr: %s\n",
+ string);
+ goto cifs_parse_mount_err;
+ }
+ break;
+ case Opt_iocharset:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (strnlen(string, 1024) >= 65) {
+ pr_warn("CIFS: iocharset name too long.\n");
+ goto cifs_parse_mount_err;
+ }
+
+ if (strncasecmp(string, "default", 7) != 0) {
+ kfree(vol->iocharset);
+ vol->iocharset = kstrdup(string,
+ GFP_KERNEL);
+ if (!vol->iocharset) {
+ pr_warn("CIFS: no memory for charset\n");
+ goto cifs_parse_mount_err;
+ }
+ }
+ /* if iocharset not set then load_nls_default
+ * is used by caller
+ */
+ cifs_dbg(FYI, "iocharset set to %s\n", string);
+ break;
+ case Opt_netbiosname:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ memset(vol->source_rfc1001_name, 0x20,
+ RFC1001_NAME_LEN);
+ /*
+ * FIXME: are there cases in which a comma can
+ * be valid in workstation netbios name (and
+ * need special handling)?
+ */
+ for (i = 0; i < RFC1001_NAME_LEN; i++) {
+ /* don't ucase netbiosname for user */
+ if (string[i] == 0)
+ break;
+ vol->source_rfc1001_name[i] = string[i];
+ }
+ /* The string has 16th byte zero still from
+ * set at top of the function
+ */
+ if (i == RFC1001_NAME_LEN && string[i] != 0)
+ pr_warn("CIFS: netbiosname longer than 15 truncated.\n");
+ break;
+ case Opt_servern:
+ /* servernetbiosname specified override *SMBSERVER */
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ /* last byte, type, is 0x20 for servr type */
+ memset(vol->target_rfc1001_name, 0x20,
+ RFC1001_NAME_LEN_WITH_NULL);
+
+ /* BB are there cases in which a comma can be
+ valid in this workstation netbios name
+ (and need special handling)? */
+
+ /* user or mount helper must uppercase the
+ netbios name */
+ for (i = 0; i < 15; i++) {
+ if (string[i] == 0)
+ break;
+ vol->target_rfc1001_name[i] = string[i];
+ }
+ /* The string has 16th byte zero still from
+ set at top of the function */
+ if (i == RFC1001_NAME_LEN && string[i] != 0)
+ pr_warn("CIFS: server netbiosname longer than 15 truncated.\n");
+ break;
+ case Opt_ver:
+ /* version of mount userspace tools, not dialect */
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ /* If interface changes in mount.cifs bump to new ver */
+ if (strncasecmp(string, "1", 1) == 0) {
+ if (strlen(string) > 1) {
+ pr_warn("Bad mount helper ver=%s. Did "
+ "you want SMB1 (CIFS) dialect "
+ "and mean to type vers=1.0 "
+ "instead?\n", string);
+ goto cifs_parse_mount_err;
+ }
+ /* This is the default */
+ break;
+ }
+ /* For all other value, error */
+ pr_warn("CIFS: Invalid mount helper version specified\n");
+ goto cifs_parse_mount_err;
+ case Opt_vers:
+ /* protocol version (dialect) */
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (cifs_parse_smb_version(string, vol, is_smb3) != 0)
+ goto cifs_parse_mount_err;
+ got_version = true;
+ break;
+ case Opt_sec:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (cifs_parse_security_flavors(string, vol) != 0)
+ goto cifs_parse_mount_err;
+ break;
+ case Opt_cache:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+
+ if (cifs_parse_cache_flavor(string, vol) != 0)
+ goto cifs_parse_mount_err;
+ break;
+ default:
+ /*
+ * An option we don't recognize. Save it off for later
+ * if we haven't already found one
+ */
+ if (!invalid)
+ invalid = data;
+ break;
+ }
+ /* Free up any allocated string */
+ kfree(string);
+ string = NULL;
+ }
+
+ if (!sloppy && invalid) {
+ pr_err("CIFS: Unknown mount option \"%s\"\n", invalid);
+ goto cifs_parse_mount_err;
+ }
+
+ if (vol->rdma && vol->vals->protocol_id < SMB30_PROT_ID) {
+ cifs_dbg(VFS, "SMB Direct requires Version >=3.0\n");
+ goto cifs_parse_mount_err;
+ }
+
+#ifndef CONFIG_KEYS
+ /* Muliuser mounts require CONFIG_KEYS support */
+ if (vol->multiuser) {
+ cifs_dbg(VFS, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n");
+ goto cifs_parse_mount_err;
+ }
+#endif
+ if (!vol->UNC) {
+ cifs_dbg(VFS, "CIFS mount error: No usable UNC path provided in device string!\n");
+ goto cifs_parse_mount_err;
+ }
+
+ /* make sure UNC has a share name */
+ if (!strchr(vol->UNC + 3, '\\')) {
+ cifs_dbg(VFS, "Malformed UNC. Unable to find share name.\n");
+ goto cifs_parse_mount_err;
+ }
+
+ if (!got_ip) {
+ int len;
+ const char *slash;
+
+ /* No ip= option specified? Try to get it from UNC */
+ /* Use the address part of the UNC. */
+ slash = strchr(&vol->UNC[2], '\\');
+ len = slash - &vol->UNC[2];
+ if (!cifs_convert_address(dstaddr, &vol->UNC[2], len)) {
+ pr_err("Unable to determine destination address.\n");
+ goto cifs_parse_mount_err;
+ }
+ }
+
+ /* set the port that we got earlier */
+ cifs_set_port(dstaddr, port);
+
+ if (uid_specified)
+ vol->override_uid = override_uid;
+ else if (override_uid == 1)
+ pr_notice("CIFS: ignoring forceuid mount option specified with no uid= option.\n");
+
+ if (gid_specified)
+ vol->override_gid = override_gid;
+ else if (override_gid == 1)
+ pr_notice("CIFS: ignoring forcegid mount option specified with no gid= option.\n");
+
+ if (got_version == false)
+ pr_warn("No dialect specified on mount. Default has changed to "
+ "a more secure dialect, SMB2.1 or later (e.g. SMB3), from CIFS "
+ "(SMB1). To use the less secure SMB1 dialect to access "
+ "old servers which do not support SMB3 (or SMB2.1) specify vers=1.0"
+ " on mount.\n");
+
+ kfree(mountdata_copy);
+ return 0;
+
+out_nomem:
+ pr_warn("Could not allocate temporary buffer\n");
+cifs_parse_mount_err:
+ kfree(string);
+ kfree(mountdata_copy);
+ return 1;
+}
+
+/** Returns true if srcaddr isn't specified and rhs isn't
+ * specified, or if srcaddr is specified and
+ * matches the IP address of the rhs argument.
+ */
+static bool
+srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)
+{
+ switch (srcaddr->sa_family) {
+ case AF_UNSPEC:
+ return (rhs->sa_family == AF_UNSPEC);
+ case AF_INET: {
+ struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
+ struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
+ return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
+ struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
+ return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr);
+ }
+ default:
+ WARN_ON(1);
+ return false; /* don't expect to be here */
+ }
+}
+
+/*
+ * If no port is specified in addr structure, we try to match with 445 port
+ * and if it fails - with 139 ports. It should be called only if address
+ * families of server and addr are equal.
+ */
+static bool
+match_port(struct TCP_Server_Info *server, struct sockaddr *addr)
+{
+ __be16 port, *sport;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port;
+ port = ((struct sockaddr_in *) addr)->sin_port;
+ break;
+ case AF_INET6:
+ sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port;
+ port = ((struct sockaddr_in6 *) addr)->sin6_port;
+ break;
+ default:
+ WARN_ON(1);
+ return false;
+ }
+
+ if (!port) {
+ port = htons(CIFS_PORT);
+ if (port == *sport)
+ return true;
+
+ port = htons(RFC1001_PORT);
+ }
+
+ return port == *sport;
+}
+
+static bool
+match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
+ struct sockaddr *srcaddr)
+{
+ switch (addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
+ struct sockaddr_in *srv_addr4 =
+ (struct sockaddr_in *)&server->dstaddr;
+
+ if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr)
+ return false;
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
+ struct sockaddr_in6 *srv_addr6 =
+ (struct sockaddr_in6 *)&server->dstaddr;
+
+ if (!ipv6_addr_equal(&addr6->sin6_addr,
+ &srv_addr6->sin6_addr))
+ return false;
+ if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id)
+ return false;
+ break;
+ }
+ default:
+ WARN_ON(1);
+ return false; /* don't expect to be here */
+ }
+
+ if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
+ return false;
+
+ return true;
+}
+
+static bool
+match_security(struct TCP_Server_Info *server, struct smb_vol *vol)
+{
+ /*
+ * The select_sectype function should either return the vol->sectype
+ * that was specified, or "Unspecified" if that sectype was not
+ * compatible with the given NEGOTIATE request.
+ */
+ if (server->ops->select_sectype(server, vol->sectype)
+ == Unspecified)
+ return false;
+
+ /*
+ * Now check if signing mode is acceptable. No need to check
+ * global_secflags at this point since if MUST_SIGN is set then
+ * the server->sign had better be too.
+ */
+ if (vol->sign && !server->sign)
+ return false;
+
+ return true;
+}
+
+static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
+{
+ struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr;
+
+ if (vol->nosharesock)
+ return 0;
+
+ /* BB update this for smb3any and default case */
+ if ((server->vals != vol->vals) || (server->ops != vol->ops))
+ return 0;
+
+ if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
+ return 0;
+
+ if (!match_address(server, addr,
+ (struct sockaddr *)&vol->srcaddr))
+ return 0;
+
+ if (!match_port(server, addr))
+ return 0;
+
+ if (!match_security(server, vol))
+ return 0;
+
+ if (server->echo_interval != vol->echo_interval * HZ)
+ return 0;
+
+ if (server->rdma != vol->rdma)
+ return 0;
+
+ return 1;
+}
+
+static struct TCP_Server_Info *
+cifs_find_tcp_session(struct smb_vol *vol)
+{
+ struct TCP_Server_Info *server;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+ if (!match_server(server, vol))
+ continue;
+
+ ++server->srv_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ cifs_dbg(FYI, "Existing tcp session with server found\n");
+ return server;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+void
+cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
+{
+ struct task_struct *task;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ if (--server->srv_count > 0) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+
+ put_net(cifs_net_ns(server));
+
+ list_del_init(&server->tcp_ses_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ cancel_delayed_work_sync(&server->echo);
+
+ if (from_reconnect)
+ /*
+ * Avoid deadlock here: reconnect work calls
+ * cifs_put_tcp_session() at its end. Need to be sure
+ * that reconnect work does nothing with server pointer after
+ * that step.
+ */
+ cancel_delayed_work(&server->reconnect);
+ else
+ cancel_delayed_work_sync(&server->reconnect);
+
+ spin_lock(&GlobalMid_Lock);
+ server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+
+ cifs_crypto_secmech_release(server);
+ cifs_fscache_release_client_cookie(server);
+
+ kfree(server->session_key.response);
+ server->session_key.response = NULL;
+ server->session_key.len = 0;
+
+ task = xchg(&server->tsk, NULL);
+ if (task)
+ send_sig(SIGKILL, task, 1);
+}
+
+static struct TCP_Server_Info *
+cifs_get_tcp_session(struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *tcp_ses = NULL;
+ int rc;
+
+ cifs_dbg(FYI, "UNC: %s\n", volume_info->UNC);
+
+ /* see if we already have a matching tcp_ses */
+ tcp_ses = cifs_find_tcp_session(volume_info);
+ if (tcp_ses)
+ return tcp_ses;
+
+ tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
+ if (!tcp_ses) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+
+ tcp_ses->ops = volume_info->ops;
+ tcp_ses->vals = volume_info->vals;
+ cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
+ tcp_ses->hostname = extract_hostname(volume_info->UNC);
+ if (IS_ERR(tcp_ses->hostname)) {
+ rc = PTR_ERR(tcp_ses->hostname);
+ goto out_err_crypto_release;
+ }
+
+ tcp_ses->noblocksnd = volume_info->noblocksnd;
+ tcp_ses->noautotune = volume_info->noautotune;
+ tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay;
+ tcp_ses->rdma = volume_info->rdma;
+ tcp_ses->in_flight = 0;
+ tcp_ses->credits = 1;
+ init_waitqueue_head(&tcp_ses->response_q);
+ init_waitqueue_head(&tcp_ses->request_q);
+ INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
+ mutex_init(&tcp_ses->srv_mutex);
+ memcpy(tcp_ses->workstation_RFC1001_name,
+ volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ memcpy(tcp_ses->server_RFC1001_name,
+ volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ tcp_ses->session_estab = false;
+ tcp_ses->sequence_number = 0;
+ tcp_ses->lstrp = jiffies;
+ spin_lock_init(&tcp_ses->req_lock);
+ INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
+ INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
+ INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
+ INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
+ mutex_init(&tcp_ses->reconnect_mutex);
+ memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
+ sizeof(tcp_ses->srcaddr));
+ memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
+ sizeof(tcp_ses->dstaddr));
+ generate_random_uuid(tcp_ses->client_guid);
+ /*
+ * at this point we are the only ones with the pointer
+ * to the struct since the kernel thread not created yet
+ * no need to spinlock this init of tcpStatus or srv_count
+ */
+ tcp_ses->tcpStatus = CifsNew;
+ ++tcp_ses->srv_count;
+
+ if (volume_info->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
+ volume_info->echo_interval <= SMB_ECHO_INTERVAL_MAX)
+ tcp_ses->echo_interval = volume_info->echo_interval * HZ;
+ else
+ tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
+ if (tcp_ses->rdma) {
+#ifndef CONFIG_CIFS_SMB_DIRECT
+ cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
+ rc = -ENOENT;
+ goto out_err_crypto_release;
+#endif
+ tcp_ses->smbd_conn = smbd_get_connection(
+ tcp_ses, (struct sockaddr *)&volume_info->dstaddr);
+ if (tcp_ses->smbd_conn) {
+ cifs_dbg(VFS, "RDMA transport established\n");
+ rc = 0;
+ goto smbd_connected;
+ } else {
+ rc = -ENOENT;
+ goto out_err_crypto_release;
+ }
+ }
+ rc = ip_connect(tcp_ses);
+ if (rc < 0) {
+ cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n");
+ goto out_err_crypto_release;
+ }
+smbd_connected:
+ /*
+ * since we're in a cifs function already, we know that
+ * this will succeed. No need for try_module_get().
+ */
+ __module_get(THIS_MODULE);
+ tcp_ses->tsk = kthread_run(cifs_demultiplex_thread,
+ tcp_ses, "cifsd");
+ if (IS_ERR(tcp_ses->tsk)) {
+ rc = PTR_ERR(tcp_ses->tsk);
+ cifs_dbg(VFS, "error %d create cifsd thread\n", rc);
+ module_put(THIS_MODULE);
+ goto out_err_crypto_release;
+ }
+ tcp_ses->tcpStatus = CifsNeedNegotiate;
+
+ /* thread spawned, put it on the list */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ cifs_fscache_get_client_cookie(tcp_ses);
+
+ /* queue echo request delayed work */
+ queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
+
+ return tcp_ses;
+
+out_err_crypto_release:
+ cifs_crypto_secmech_release(tcp_ses);
+
+ put_net(cifs_net_ns(tcp_ses));
+
+out_err:
+ if (tcp_ses) {
+ if (!IS_ERR(tcp_ses->hostname))
+ kfree(tcp_ses->hostname);
+ if (tcp_ses->ssocket)
+ sock_release(tcp_ses->ssocket);
+ kfree(tcp_ses);
+ }
+ return ERR_PTR(rc);
+}
+
+static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
+{
+ if (vol->sectype != Unspecified &&
+ vol->sectype != ses->sectype)
+ return 0;
+
+ switch (ses->sectype) {
+ case Kerberos:
+ if (!uid_eq(vol->cred_uid, ses->cred_uid))
+ return 0;
+ break;
+ default:
+ /* NULL username means anonymous session */
+ if (ses->user_name == NULL) {
+ if (!vol->nullauth)
+ return 0;
+ break;
+ }
+
+ /* anything else takes username/password */
+ if (strncmp(ses->user_name,
+ vol->username ? vol->username : "",
+ CIFS_MAX_USERNAME_LEN))
+ return 0;
+ if ((vol->username && strlen(vol->username) != 0) &&
+ ses->password != NULL &&
+ strncmp(ses->password,
+ vol->password ? vol->password : "",
+ CIFS_MAX_PASSWORD_LEN))
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * cifs_setup_ipc - helper to setup the IPC tcon for the session
+ *
+ * A new IPC connection is made and stored in the session
+ * tcon_ipc. The IPC tcon has the same lifetime as the session.
+ */
+static int
+cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
+{
+ int rc = 0, xid;
+ struct cifs_tcon *tcon;
+ struct nls_table *nls_codepage;
+ char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
+ bool seal = false;
+
+ /*
+ * If the mount request that resulted in the creation of the
+ * session requires encryption, force IPC to be encrypted too.
+ */
+ if (volume_info->seal) {
+ if (ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
+ seal = true;
+ else {
+ cifs_dbg(VFS,
+ "IPC: server doesn't support encryption\n");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ tcon = tconInfoAlloc();
+ if (tcon == NULL)
+ return -ENOMEM;
+
+ snprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname);
+
+ /* cannot fail */
+ nls_codepage = load_nls_default();
+
+ xid = get_xid();
+ tcon->ses = ses;
+ tcon->ipc = true;
+ tcon->seal = seal;
+ rc = ses->server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage);
+ free_xid(xid);
+
+ if (rc) {
+ cifs_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
+ tconInfoFree(tcon);
+ goto out;
+ }
+
+ cifs_dbg(FYI, "IPC tcon rc = %d ipc tid = %d\n", rc, tcon->tid);
+
+ ses->tcon_ipc = tcon;
+out:
+ unload_nls(nls_codepage);
+ return rc;
+}
+
+/**
+ * cifs_free_ipc - helper to release the session IPC tcon
+ *
+ * Needs to be called everytime a session is destroyed
+ */
+static int
+cifs_free_ipc(struct cifs_ses *ses)
+{
+ int rc = 0, xid;
+ struct cifs_tcon *tcon = ses->tcon_ipc;
+
+ if (tcon == NULL)
+ return 0;
+
+ if (ses->server->ops->tree_disconnect) {
+ xid = get_xid();
+ rc = ses->server->ops->tree_disconnect(xid, tcon);
+ free_xid(xid);
+ }
+
+ if (rc)
+ cifs_dbg(FYI, "failed to disconnect IPC tcon (rc=%d)\n", rc);
+
+ tconInfoFree(tcon);
+ ses->tcon_ipc = NULL;
+ return rc;
+}
+
+static struct cifs_ses *
+cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
+{
+ struct cifs_ses *ses;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ if (ses->status == CifsExiting)
+ continue;
+ if (!match_session(ses, vol))
+ continue;
+ ++ses->ses_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ return ses;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+static void
+cifs_put_smb_ses(struct cifs_ses *ses)
+{
+ unsigned int rc, xid;
+ struct TCP_Server_Info *server = ses->server;
+
+ cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
+
+ spin_lock(&cifs_tcp_ses_lock);
+ if (ses->status == CifsExiting) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+ if (--ses->ses_count > 0) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+ if (ses->status == CifsGood)
+ ses->status = CifsExiting;
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ cifs_free_ipc(ses);
+
+ if (ses->status == CifsExiting && server->ops->logoff) {
+ xid = get_xid();
+ rc = server->ops->logoff(xid, ses);
+ if (rc)
+ cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
+ __func__, rc);
+ _free_xid(xid);
+ }
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_del_init(&ses->smb_ses_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ sesInfoFree(ses);
+ cifs_put_tcp_session(server, 0);
+}
+
+#ifdef CONFIG_KEYS
+
+/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */
+#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1)
+
+/* Populate username and pw fields from keyring if possible */
+static int
+cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
+{
+ int rc = 0;
+ int is_domain = 0;
+ const char *delim, *payload;
+ char *desc;
+ ssize_t len;
+ struct key *key;
+ struct TCP_Server_Info *server = ses->server;
+ struct sockaddr_in *sa;
+ struct sockaddr_in6 *sa6;
+ const struct user_key_payload *upayload;
+
+ desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ /* try to find an address key first */
+ switch (server->dstaddr.ss_family) {
+ case AF_INET:
+ sa = (struct sockaddr_in *)&server->dstaddr;
+ sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ sa6 = (struct sockaddr_in6 *)&server->dstaddr;
+ sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr);
+ break;
+ default:
+ cifs_dbg(FYI, "Bad ss_family (%hu)\n",
+ server->dstaddr.ss_family);
+ rc = -EINVAL;
+ goto out_err;
+ }
+
+ cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
+ key = request_key(&key_type_logon, desc, "");
+ if (IS_ERR(key)) {
+ if (!ses->domainName) {
+ cifs_dbg(FYI, "domainName is NULL\n");
+ rc = PTR_ERR(key);
+ goto out_err;
+ }
+
+ /* didn't work, try to find a domain key */
+ sprintf(desc, "cifs:d:%s", ses->domainName);
+ cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
+ key = request_key(&key_type_logon, desc, "");
+ if (IS_ERR(key)) {
+ rc = PTR_ERR(key);
+ goto out_err;
+ }
+ is_domain = 1;
+ }
+
+ down_read(&key->sem);
+ upayload = user_key_payload_locked(key);
+ if (IS_ERR_OR_NULL(upayload)) {
+ rc = upayload ? PTR_ERR(upayload) : -EINVAL;
+ goto out_key_put;
+ }
+
+ /* find first : in payload */
+ payload = upayload->data;
+ delim = strnchr(payload, upayload->datalen, ':');
+ cifs_dbg(FYI, "payload=%s\n", payload);
+ if (!delim) {
+ cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n",
+ upayload->datalen);
+ rc = -EINVAL;
+ goto out_key_put;
+ }
+
+ len = delim - payload;
+ if (len > CIFS_MAX_USERNAME_LEN || len <= 0) {
+ cifs_dbg(FYI, "Bad value from username search (len=%zd)\n",
+ len);
+ rc = -EINVAL;
+ goto out_key_put;
+ }
+
+ vol->username = kstrndup(payload, len, GFP_KERNEL);
+ if (!vol->username) {
+ cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n",
+ len);
+ rc = -ENOMEM;
+ goto out_key_put;
+ }
+ cifs_dbg(FYI, "%s: username=%s\n", __func__, vol->username);
+
+ len = key->datalen - (len + 1);
+ if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) {
+ cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len);
+ rc = -EINVAL;
+ kfree(vol->username);
+ vol->username = NULL;
+ goto out_key_put;
+ }
+
+ ++delim;
+ vol->password = kstrndup(delim, len, GFP_KERNEL);
+ if (!vol->password) {
+ cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n",
+ len);
+ rc = -ENOMEM;
+ kfree(vol->username);
+ vol->username = NULL;
+ goto out_key_put;
+ }
+
+ /*
+ * If we have a domain key then we must set the domainName in the
+ * for the request.
+ */
+ if (is_domain && ses->domainName) {
+ vol->domainname = kstrndup(ses->domainName,
+ strlen(ses->domainName),
+ GFP_KERNEL);
+ if (!vol->domainname) {
+ cifs_dbg(FYI, "Unable to allocate %zd bytes for "
+ "domain\n", len);
+ rc = -ENOMEM;
+ kfree(vol->username);
+ vol->username = NULL;
+ kzfree(vol->password);
+ vol->password = NULL;
+ goto out_key_put;
+ }
+ }
+
+out_key_put:
+ up_read(&key->sem);
+ key_put(key);
+out_err:
+ kfree(desc);
+ cifs_dbg(FYI, "%s: returning %d\n", __func__, rc);
+ return rc;
+}
+#else /* ! CONFIG_KEYS */
+static inline int
+cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
+ struct cifs_ses *ses __attribute__((unused)))
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_KEYS */
+
+/**
+ * cifs_get_smb_ses - get a session matching @volume_info data from @server
+ *
+ * This function assumes it is being called from cifs_mount() where we
+ * already got a server reference (server refcount +1). See
+ * cifs_get_tcon() for refcount explanations.
+ */
+static struct cifs_ses *
+cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
+{
+ int rc = -ENOMEM;
+ unsigned int xid;
+ struct cifs_ses *ses;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+
+ xid = get_xid();
+
+ ses = cifs_find_smb_ses(server, volume_info);
+ if (ses) {
+ cifs_dbg(FYI, "Existing smb sess found (status=%d)\n",
+ ses->status);
+
+ mutex_lock(&ses->session_mutex);
+ rc = cifs_negotiate_protocol(xid, ses);
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ /* problem -- put our ses reference */
+ cifs_put_smb_ses(ses);
+ free_xid(xid);
+ return ERR_PTR(rc);
+ }
+ if (ses->need_reconnect) {
+ cifs_dbg(FYI, "Session needs reconnect\n");
+ rc = cifs_setup_session(xid, ses,
+ volume_info->local_nls);
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ /* problem -- put our reference */
+ cifs_put_smb_ses(ses);
+ free_xid(xid);
+ return ERR_PTR(rc);
+ }
+ }
+ mutex_unlock(&ses->session_mutex);
+
+ /* existing SMB ses has a server reference already */
+ cifs_put_tcp_session(server, 0);
+ free_xid(xid);
+ return ses;
+ }
+
+ cifs_dbg(FYI, "Existing smb sess not found\n");
+ ses = sesInfoAlloc();
+ if (ses == NULL)
+ goto get_ses_fail;
+
+ /* new SMB session uses our server ref */
+ ses->server = server;
+ if (server->dstaddr.ss_family == AF_INET6)
+ sprintf(ses->serverName, "%pI6", &addr6->sin6_addr);
+ else
+ sprintf(ses->serverName, "%pI4", &addr->sin_addr);
+
+ if (volume_info->username) {
+ ses->user_name = kstrdup(volume_info->username, GFP_KERNEL);
+ if (!ses->user_name)
+ goto get_ses_fail;
+ }
+
+ /* volume_info->password freed at unmount */
+ if (volume_info->password) {
+ ses->password = kstrdup(volume_info->password, GFP_KERNEL);
+ if (!ses->password)
+ goto get_ses_fail;
+ }
+ if (volume_info->domainname) {
+ ses->domainName = kstrdup(volume_info->domainname, GFP_KERNEL);
+ if (!ses->domainName)
+ goto get_ses_fail;
+ }
+ if (volume_info->domainauto)
+ ses->domainAuto = volume_info->domainauto;
+ ses->cred_uid = volume_info->cred_uid;
+ ses->linux_uid = volume_info->linux_uid;
+
+ ses->sectype = volume_info->sectype;
+ ses->sign = volume_info->sign;
+
+ mutex_lock(&ses->session_mutex);
+ rc = cifs_negotiate_protocol(xid, ses);
+ if (!rc)
+ rc = cifs_setup_session(xid, ses, volume_info->local_nls);
+ mutex_unlock(&ses->session_mutex);
+ if (rc)
+ goto get_ses_fail;
+
+ /* success, put it on the list */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_add(&ses->smb_ses_list, &server->smb_ses_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ free_xid(xid);
+
+ cifs_setup_ipc(ses, volume_info);
+
+ return ses;
+
+get_ses_fail:
+ sesInfoFree(ses);
+ free_xid(xid);
+ return ERR_PTR(rc);
+}
+
+static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ if (tcon->tidStatus == CifsExiting)
+ return 0;
+ if (strncmp(tcon->treeName, volume_info->UNC, MAX_TREE_SIZE))
+ return 0;
+ if (tcon->seal != volume_info->seal)
+ return 0;
+ if (tcon->snapshot_time != volume_info->snapshot_time)
+ return 0;
+ if (tcon->no_lease != volume_info->no_lease)
+ return 0;
+ return 1;
+}
+
+static struct cifs_tcon *
+cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
+{
+ struct list_head *tmp;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &ses->tcon_list) {
+ tcon = list_entry(tmp, struct cifs_tcon, tcon_list);
+ if (!match_tcon(tcon, volume_info))
+ continue;
+ ++tcon->tc_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+void
+cifs_put_tcon(struct cifs_tcon *tcon)
+{
+ unsigned int xid;
+ struct cifs_ses *ses;
+
+ /*
+ * IPC tcon share the lifetime of their session and are
+ * destroyed in the session put function
+ */
+ if (tcon == NULL || tcon->ipc)
+ return;
+
+ ses = tcon->ses;
+ cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
+ spin_lock(&cifs_tcp_ses_lock);
+ if (--tcon->tc_count > 0) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+
+ list_del_init(&tcon->tcon_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ xid = get_xid();
+ if (ses->server->ops->tree_disconnect)
+ ses->server->ops->tree_disconnect(xid, tcon);
+ _free_xid(xid);
+
+ cifs_fscache_release_super_cookie(tcon);
+ tconInfoFree(tcon);
+ cifs_put_smb_ses(ses);
+}
+
+/**
+ * cifs_get_tcon - get a tcon matching @volume_info data from @ses
+ *
+ * - tcon refcount is the number of mount points using the tcon.
+ * - ses refcount is the number of tcon using the session.
+ *
+ * 1. This function assumes it is being called from cifs_mount() where
+ * we already got a session reference (ses refcount +1).
+ *
+ * 2. Since we're in the context of adding a mount point, the end
+ * result should be either:
+ *
+ * a) a new tcon already allocated with refcount=1 (1 mount point) and
+ * its session refcount incremented (1 new tcon). This +1 was
+ * already done in (1).
+ *
+ * b) an existing tcon with refcount+1 (add a mount point to it) and
+ * identical ses refcount (no new tcon). Because of (1) we need to
+ * decrement the ses refcount.
+ */
+static struct cifs_tcon *
+cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
+{
+ int rc, xid;
+ struct cifs_tcon *tcon;
+
+ tcon = cifs_find_tcon(ses, volume_info);
+ if (tcon) {
+ /*
+ * tcon has refcount already incremented but we need to
+ * decrement extra ses reference gotten by caller (case b)
+ */
+ cifs_dbg(FYI, "Found match on UNC path\n");
+ cifs_put_smb_ses(ses);
+ return tcon;
+ }
+
+ if (!ses->server->ops->tree_connect) {
+ rc = -ENOSYS;
+ goto out_fail;
+ }
+
+ tcon = tconInfoAlloc();
+ if (tcon == NULL) {
+ rc = -ENOMEM;
+ goto out_fail;
+ }
+
+ if (volume_info->snapshot_time) {
+ if (ses->server->vals->protocol_id == 0) {
+ cifs_dbg(VFS,
+ "Use SMB2 or later for snapshot mount option\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ } else
+ tcon->snapshot_time = volume_info->snapshot_time;
+ }
+
+ tcon->ses = ses;
+ if (volume_info->password) {
+ tcon->password = kstrdup(volume_info->password, GFP_KERNEL);
+ if (!tcon->password) {
+ rc = -ENOMEM;
+ goto out_fail;
+ }
+ }
+
+ if (volume_info->seal) {
+ if (ses->server->vals->protocol_id == 0) {
+ cifs_dbg(VFS,
+ "SMB3 or later required for encryption\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ } else if (tcon->ses->server->capabilities &
+ SMB2_GLOBAL_CAP_ENCRYPTION)
+ tcon->seal = true;
+ else {
+ cifs_dbg(VFS, "Encryption is not supported on share\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ }
+ }
+
+ if (volume_info->linux_ext) {
+ if (ses->server->posix_ext_supported) {
+ tcon->posix_extensions = true;
+ printk_once(KERN_WARNING
+ "SMB3.11 POSIX Extensions are experimental\n");
+ } else {
+ cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions.\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ }
+ }
+
+ /*
+ * BB Do we need to wrap session_mutex around this TCon call and Unix
+ * SetFS as we do on SessSetup and reconnect?
+ */
+ xid = get_xid();
+ rc = ses->server->ops->tree_connect(xid, ses, volume_info->UNC, tcon,
+ volume_info->local_nls);
+ free_xid(xid);
+ cifs_dbg(FYI, "Tcon rc = %d\n", rc);
+ if (rc)
+ goto out_fail;
+
+ if (volume_info->nodfs) {
+ tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
+ cifs_dbg(FYI, "DFS disabled (%d)\n", tcon->Flags);
+ }
+ tcon->use_persistent = false;
+ /* check if SMB2 or later, CIFS does not support persistent handles */
+ if (volume_info->persistent) {
+ if (ses->server->vals->protocol_id == 0) {
+ cifs_dbg(VFS,
+ "SMB3 or later required for persistent handles\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ } else if (ses->server->capabilities &
+ SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
+ tcon->use_persistent = true;
+ else /* persistent handles requested but not supported */ {
+ cifs_dbg(VFS,
+ "Persistent handles not supported on share\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ }
+ } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY)
+ && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
+ && (volume_info->nopersistent == false)) {
+ cifs_dbg(FYI, "enabling persistent handles\n");
+ tcon->use_persistent = true;
+ } else if (volume_info->resilient) {
+ if (ses->server->vals->protocol_id == 0) {
+ cifs_dbg(VFS,
+ "SMB2.1 or later required for resilient handles\n");
+ rc = -EOPNOTSUPP;
+ goto out_fail;
+ }
+ tcon->use_resilient = true;
+ }
+
+ /*
+ * We can have only one retry value for a connection to a share so for
+ * resources mounted more than once to the same server share the last
+ * value passed in for the retry flag is used.
+ */
+ tcon->retry = volume_info->retry;
+ tcon->nocase = volume_info->nocase;
+ tcon->nohandlecache = volume_info->nohandlecache;
+ tcon->local_lease = volume_info->local_lease;
+ tcon->no_lease = volume_info->no_lease;
+ INIT_LIST_HEAD(&tcon->pending_opens);
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_add(&tcon->tcon_list, &ses->tcon_list);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ cifs_fscache_get_super_cookie(tcon);
+
+ return tcon;
+
+out_fail:
+ tconInfoFree(tcon);
+ return ERR_PTR(rc);
+}
+
+void
+cifs_put_tlink(struct tcon_link *tlink)
+{
+ if (!tlink || IS_ERR(tlink))
+ return;
+
+ if (!atomic_dec_and_test(&tlink->tl_count) ||
+ test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) {
+ tlink->tl_time = jiffies;
+ return;
+ }
+
+ if (!IS_ERR(tlink_tcon(tlink)))
+ cifs_put_tcon(tlink_tcon(tlink));
+ kfree(tlink);
+ return;
+}
+
+static int
+compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
+{
+ struct cifs_sb_info *old = CIFS_SB(sb);
+ struct cifs_sb_info *new = mnt_data->cifs_sb;
+ unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK;
+ unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK;
+
+ if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK))
+ return 0;
+
+ if (old->mnt_cifs_serverino_autodisabled)
+ newflags &= ~CIFS_MOUNT_SERVER_INUM;
+
+ if (oldflags != newflags)
+ return 0;
+
+ /*
+ * We want to share sb only if we don't specify an r/wsize or
+ * specified r/wsize is greater than or equal to existing one.
+ */
+ if (new->wsize && new->wsize < old->wsize)
+ return 0;
+
+ if (new->rsize && new->rsize < old->rsize)
+ return 0;
+
+ if (!uid_eq(old->mnt_uid, new->mnt_uid) || !gid_eq(old->mnt_gid, new->mnt_gid))
+ return 0;
+
+ if (old->mnt_file_mode != new->mnt_file_mode ||
+ old->mnt_dir_mode != new->mnt_dir_mode)
+ return 0;
+
+ if (strcmp(old->local_nls->charset, new->local_nls->charset))
+ return 0;
+
+ if (old->actimeo != new->actimeo)
+ return 0;
+
+ return 1;
+}
+
+static int
+match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data)
+{
+ struct cifs_sb_info *old = CIFS_SB(sb);
+ struct cifs_sb_info *new = mnt_data->cifs_sb;
+ bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
+ old->prepath;
+ bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
+ new->prepath;
+
+ if (old_set && new_set && !strcmp(new->prepath, old->prepath))
+ return 1;
+ else if (!old_set && !new_set)
+ return 1;
+
+ return 0;
+}
+
+int
+cifs_match_super(struct super_block *sb, void *data)
+{
+ struct cifs_mnt_data *mnt_data = (struct cifs_mnt_data *)data;
+ struct smb_vol *volume_info;
+ struct cifs_sb_info *cifs_sb;
+ struct TCP_Server_Info *tcp_srv;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+ int rc = 0;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ cifs_sb = CIFS_SB(sb);
+ tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
+ if (tlink == NULL) {
+ /* can not match superblock if tlink were ever null */
+ spin_unlock(&cifs_tcp_ses_lock);
+ return 0;
+ }
+ tcon = tlink_tcon(tlink);
+ ses = tcon->ses;
+ tcp_srv = ses->server;
+
+ volume_info = mnt_data->vol;
+
+ if (!match_server(tcp_srv, volume_info) ||
+ !match_session(ses, volume_info) ||
+ !match_tcon(tcon, volume_info) ||
+ !match_prepath(sb, mnt_data)) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = compare_mount_options(sb, mnt_data);
+out:
+ spin_unlock(&cifs_tcp_ses_lock);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+int
+get_dfs_path(const unsigned int xid, struct cifs_ses *ses, const char *old_path,
+ const struct nls_table *nls_codepage, unsigned int *num_referrals,
+ struct dfs_info3_param **referrals, int remap)
+{
+ int rc = 0;
+
+ if (!ses->server->ops->get_dfs_refer)
+ return -ENOSYS;
+
+ *num_referrals = 0;
+ *referrals = NULL;
+
+ rc = ses->server->ops->get_dfs_refer(xid, ses, old_path,
+ referrals, num_referrals,
+ nls_codepage, remap);
+ return rc;
+}
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key cifs_key[2];
+static struct lock_class_key cifs_slock_key[2];
+
+static inline void
+cifs_reclassify_socket4(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ BUG_ON(!sock_allow_reclassification(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
+ &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
+}
+
+static inline void
+cifs_reclassify_socket6(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ BUG_ON(!sock_allow_reclassification(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
+ &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
+}
+#else
+static inline void
+cifs_reclassify_socket4(struct socket *sock)
+{
+}
+
+static inline void
+cifs_reclassify_socket6(struct socket *sock)
+{
+}
+#endif
+
+/* See RFC1001 section 14 on representation of Netbios names */
+static void rfc1002mangle(char *target, char *source, unsigned int length)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; i < (length); i++) {
+ /* mask a nibble at a time and encode */
+ target[j] = 'A' + (0x0F & (source[i] >> 4));
+ target[j+1] = 'A' + (0x0F & source[i]);
+ j += 2;
+ }
+
+}
+
+static int
+bind_socket(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ if (server->srcaddr.ss_family != AF_UNSPEC) {
+ /* Bind to the specified local IP address */
+ struct socket *socket = server->ssocket;
+ rc = socket->ops->bind(socket,
+ (struct sockaddr *) &server->srcaddr,
+ sizeof(server->srcaddr));
+ if (rc < 0) {
+ struct sockaddr_in *saddr4;
+ struct sockaddr_in6 *saddr6;
+ saddr4 = (struct sockaddr_in *)&server->srcaddr;
+ saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
+ if (saddr6->sin6_family == AF_INET6)
+ cifs_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n",
+ &saddr6->sin6_addr, rc);
+ else
+ cifs_dbg(VFS, "Failed to bind to: %pI4, error: %d\n",
+ &saddr4->sin_addr.s_addr, rc);
+ }
+ }
+ return rc;
+}
+
+static int
+ip_rfc1001_connect(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ /*
+ * some servers require RFC1001 sessinit before sending
+ * negprot - BB check reconnection in case where second
+ * sessinit is sent but no second negprot
+ */
+ struct rfc1002_session_packet *ses_init_buf;
+ struct smb_hdr *smb_buf;
+ ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
+ GFP_KERNEL);
+ if (ses_init_buf) {
+ ses_init_buf->trailer.session_req.called_len = 32;
+
+ if (server->server_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ server->server_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ DEFAULT_CIFS_CALLED_NAME,
+ RFC1001_NAME_LEN_WITH_NULL);
+
+ ses_init_buf->trailer.session_req.calling_len = 32;
+
+ /*
+ * calling name ends in null (byte 16) from old smb
+ * convention.
+ */
+ if (server->workstation_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ server->workstation_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ "LINUX_CIFS_CLNT",
+ RFC1001_NAME_LEN_WITH_NULL);
+
+ ses_init_buf->trailer.session_req.scope1 = 0;
+ ses_init_buf->trailer.session_req.scope2 = 0;
+ smb_buf = (struct smb_hdr *)ses_init_buf;
+
+ /* sizeof RFC1002_SESSION_REQUEST with no scope */
+ smb_buf->smb_buf_length = cpu_to_be32(0x81000044);
+ rc = smb_send(server, smb_buf, 0x44);
+ kfree(ses_init_buf);
+ /*
+ * RFC1001 layer in at least one server
+ * requires very short break before negprot
+ * presumably because not expecting negprot
+ * to follow so fast. This is a simple
+ * solution that works without
+ * complicating the code and causes no
+ * significant slowing down on mount
+ * for everyone else
+ */
+ usleep_range(1000, 2000);
+ }
+ /*
+ * else the negprot may still work without this
+ * even though malloc failed
+ */
+
+ return rc;
+}
+
+static int
+generic_ip_connect(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ __be16 sport;
+ int slen, sfamily;
+ struct socket *socket = server->ssocket;
+ struct sockaddr *saddr;
+
+ saddr = (struct sockaddr *) &server->dstaddr;
+
+ if (server->dstaddr.ss_family == AF_INET6) {
+ sport = ((struct sockaddr_in6 *) saddr)->sin6_port;
+ slen = sizeof(struct sockaddr_in6);
+ sfamily = AF_INET6;
+ } else {
+ sport = ((struct sockaddr_in *) saddr)->sin_port;
+ slen = sizeof(struct sockaddr_in);
+ sfamily = AF_INET;
+ }
+
+ if (socket == NULL) {
+ rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
+ IPPROTO_TCP, &socket, 1);
+ if (rc < 0) {
+ cifs_dbg(VFS, "Error %d creating socket\n", rc);
+ server->ssocket = NULL;
+ return rc;
+ }
+
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cifs_dbg(FYI, "Socket created\n");
+ server->ssocket = socket;
+ socket->sk->sk_allocation = GFP_NOFS;
+ if (sfamily == AF_INET6)
+ cifs_reclassify_socket6(socket);
+ else
+ cifs_reclassify_socket4(socket);
+ }
+
+ rc = bind_socket(server);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Eventually check for other socket options to change from
+ * the default. sock_setsockopt not used because it expects
+ * user space buffer
+ */
+ socket->sk->sk_rcvtimeo = 7 * HZ;
+ socket->sk->sk_sndtimeo = 5 * HZ;
+
+ /* make the bufsizes depend on wsize/rsize and max requests */
+ if (server->noautotune) {
+ if (socket->sk->sk_sndbuf < (200 * 1024))
+ socket->sk->sk_sndbuf = 200 * 1024;
+ if (socket->sk->sk_rcvbuf < (140 * 1024))
+ socket->sk->sk_rcvbuf = 140 * 1024;
+ }
+
+ if (server->tcp_nodelay) {
+ int val = 1;
+ rc = kernel_setsockopt(socket, SOL_TCP, TCP_NODELAY,
+ (char *)&val, sizeof(val));
+ if (rc)
+ cifs_dbg(FYI, "set TCP_NODELAY socket option error %d\n",
+ rc);
+ }
+
+ cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n",
+ socket->sk->sk_sndbuf,
+ socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
+
+ rc = socket->ops->connect(socket, saddr, slen, 0);
+ if (rc < 0) {
+ cifs_dbg(FYI, "Error %d connecting to server\n", rc);
+ sock_release(socket);
+ server->ssocket = NULL;
+ return rc;
+ }
+
+ if (sport == htons(RFC1001_PORT))
+ rc = ip_rfc1001_connect(server);
+
+ return rc;
+}
+
+static int
+ip_connect(struct TCP_Server_Info *server)
+{
+ __be16 *sport;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
+
+ if (server->dstaddr.ss_family == AF_INET6)
+ sport = &addr6->sin6_port;
+ else
+ sport = &addr->sin_port;
+
+ if (*sport == 0) {
+ int rc;
+
+ /* try with 445 port at first */
+ *sport = htons(CIFS_PORT);
+
+ rc = generic_ip_connect(server);
+ if (rc >= 0)
+ return rc;
+
+ /* if it failed, try with 139 port */
+ *sport = htons(RFC1001_PORT);
+ }
+
+ return generic_ip_connect(server);
+}
+
+void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, struct smb_vol *vol_info)
+{
+ /* if we are reconnecting then should we check to see if
+ * any requested capabilities changed locally e.g. via
+ * remount but we can not do much about it here
+ * if they have (even if we could detect it by the following)
+ * Perhaps we could add a backpointer to array of sb from tcon
+ * or if we change to make all sb to same share the same
+ * sb as NFS - then we only have one backpointer to sb.
+ * What if we wanted to mount the server share twice once with
+ * and once without posixacls or posix paths? */
+ __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+ if (vol_info && vol_info->no_linux_ext) {
+ tcon->fsUnixInfo.Capability = 0;
+ tcon->unix_ext = 0; /* Unix Extensions disabled */
+ cifs_dbg(FYI, "Linux protocol extensions disabled\n");
+ return;
+ } else if (vol_info)
+ tcon->unix_ext = 1; /* Unix Extensions supported */
+
+ if (tcon->unix_ext == 0) {
+ cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n");
+ return;
+ }
+
+ if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
+ __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
+ /* check for reconnect case in which we do not
+ want to change the mount behavior if we can avoid it */
+ if (vol_info == NULL) {
+ /* turn off POSIX ACL and PATHNAMES if not set
+ originally at mount time */
+ if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
+ cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+ if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+ if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cifs_dbg(VFS, "POSIXPATH support change\n");
+ cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+ } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+ cifs_dbg(VFS, "possible reconnect error\n");
+ cifs_dbg(VFS, "server disabled POSIX path support\n");
+ }
+ }
+
+ if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
+ cifs_dbg(VFS, "per-share encryption not supported yet\n");
+
+ cap &= CIFS_UNIX_CAP_MASK;
+ if (vol_info && vol_info->no_psx_acl)
+ cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+ else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
+ cifs_dbg(FYI, "negotiated posix acl support\n");
+ if (cifs_sb)
+ cifs_sb->mnt_cifs_flags |=
+ CIFS_MOUNT_POSIXACL;
+ }
+
+ if (vol_info && vol_info->posix_paths == 0)
+ cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+ else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+ cifs_dbg(FYI, "negotiate posix pathnames\n");
+ if (cifs_sb)
+ cifs_sb->mnt_cifs_flags |=
+ CIFS_MOUNT_POSIX_PATHS;
+ }
+
+ cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap);
+#ifdef CONFIG_CIFS_DEBUG2
+ if (cap & CIFS_UNIX_FCNTL_CAP)
+ cifs_dbg(FYI, "FCNTL cap\n");
+ if (cap & CIFS_UNIX_EXTATTR_CAP)
+ cifs_dbg(FYI, "EXTATTR cap\n");
+ if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cifs_dbg(FYI, "POSIX path cap\n");
+ if (cap & CIFS_UNIX_XATTR_CAP)
+ cifs_dbg(FYI, "XATTR cap\n");
+ if (cap & CIFS_UNIX_POSIX_ACL_CAP)
+ cifs_dbg(FYI, "POSIX ACL cap\n");
+ if (cap & CIFS_UNIX_LARGE_READ_CAP)
+ cifs_dbg(FYI, "very large read cap\n");
+ if (cap & CIFS_UNIX_LARGE_WRITE_CAP)
+ cifs_dbg(FYI, "very large write cap\n");
+ if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)
+ cifs_dbg(FYI, "transport encryption cap\n");
+ if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
+ cifs_dbg(FYI, "mandatory transport encryption cap\n");
+#endif /* CIFS_DEBUG2 */
+ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
+ if (vol_info == NULL) {
+ cifs_dbg(FYI, "resetting capabilities failed\n");
+ } else
+ cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n");
+
+ }
+ }
+}
+
+int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
+ struct cifs_sb_info *cifs_sb)
+{
+ INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
+
+ spin_lock_init(&cifs_sb->tlink_tree_lock);
+ cifs_sb->tlink_tree = RB_ROOT;
+
+ /*
+ * Temporarily set r/wsize for matching superblock. If we end up using
+ * new sb then client will later negotiate it downward if needed.
+ */
+ cifs_sb->rsize = pvolume_info->rsize;
+ cifs_sb->wsize = pvolume_info->wsize;
+
+ cifs_sb->mnt_uid = pvolume_info->linux_uid;
+ cifs_sb->mnt_gid = pvolume_info->linux_gid;
+ cifs_sb->mnt_file_mode = pvolume_info->file_mode;
+ cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
+ cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n",
+ cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode);
+
+ cifs_sb->actimeo = pvolume_info->actimeo;
+ cifs_sb->local_nls = pvolume_info->local_nls;
+
+ if (pvolume_info->noperm)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
+ if (pvolume_info->setuids)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+ if (pvolume_info->setuidfromacl)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL;
+ if (pvolume_info->server_ino)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
+ if (pvolume_info->remap)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR;
+ if (pvolume_info->sfu_remap)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
+ if (pvolume_info->no_xattr)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+ if (pvolume_info->sfu_emul)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
+ if (pvolume_info->nobrl)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+ if (pvolume_info->nohandlecache)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE;
+ if (pvolume_info->nostrictsync)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC;
+ if (pvolume_info->mand_lock)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL;
+ if (pvolume_info->rwpidforward)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;
+ if (pvolume_info->cifs_acl)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
+ if (pvolume_info->backupuid_specified) {
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID;
+ cifs_sb->mnt_backupuid = pvolume_info->backupuid;
+ }
+ if (pvolume_info->backupgid_specified) {
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID;
+ cifs_sb->mnt_backupgid = pvolume_info->backupgid;
+ }
+ if (pvolume_info->override_uid)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
+ if (pvolume_info->override_gid)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
+ if (pvolume_info->dynperm)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
+ if (pvolume_info->fsc)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE;
+ if (pvolume_info->multiuser)
+ cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER |
+ CIFS_MOUNT_NO_PERM);
+ if (pvolume_info->strict_io)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO;
+ if (pvolume_info->direct_io) {
+ cifs_dbg(FYI, "mounting share using direct i/o\n");
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
+ }
+ if (pvolume_info->mfsymlinks) {
+ if (pvolume_info->sfu_emul) {
+ /*
+ * Our SFU ("Services for Unix" emulation does not allow
+ * creating symlinks but does allow reading existing SFU
+ * symlinks (it does allow both creating and reading SFU
+ * style mknod and FIFOs though). When "mfsymlinks" and
+ * "sfu" are both enabled at the same time, it allows
+ * reading both types of symlinks, but will only create
+ * them with mfsymlinks format. This allows better
+ * Apple compatibility (probably better for Samba too)
+ * while still recognizing old Windows style symlinks.
+ */
+ cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
+ }
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
+ }
+
+ if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
+ cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n");
+
+ if (pvolume_info->prepath) {
+ cifs_sb->prepath = kstrdup(pvolume_info->prepath, GFP_KERNEL);
+ if (cifs_sb->prepath == NULL)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+cleanup_volume_info_contents(struct smb_vol *volume_info)
+{
+ kfree(volume_info->username);
+ kzfree(volume_info->password);
+ kfree(volume_info->UNC);
+ kfree(volume_info->domainname);
+ kfree(volume_info->iocharset);
+ kfree(volume_info->prepath);
+}
+
+void
+cifs_cleanup_volume_info(struct smb_vol *volume_info)
+{
+ if (!volume_info)
+ return;
+ cleanup_volume_info_contents(volume_info);
+ kfree(volume_info);
+}
+
+
+#ifdef CONFIG_CIFS_DFS_UPCALL
+/*
+ * cifs_build_path_to_root returns full path to root when we do not have an
+ * exiting connection (tcon)
+ */
+static char *
+build_unc_path_to_root(const struct smb_vol *vol,
+ const struct cifs_sb_info *cifs_sb)
+{
+ char *full_path, *pos;
+ unsigned int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0;
+ unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1);
+
+ full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);
+ if (full_path == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ strncpy(full_path, vol->UNC, unc_len);
+ pos = full_path + unc_len;
+
+ if (pplen) {
+ *pos = CIFS_DIR_SEP(cifs_sb);
+ strncpy(pos + 1, vol->prepath, pplen);
+ pos += pplen;
+ }
+
+ *pos = '\0'; /* add trailing null */
+ convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
+ cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);
+ return full_path;
+}
+
+/*
+ * Perform a dfs referral query for a share and (optionally) prefix
+ *
+ * If a referral is found, cifs_sb->mountdata will be (re-)allocated
+ * to a string containing updated options for the submount. Otherwise it
+ * will be left untouched.
+ *
+ * Returns the rc from get_dfs_path to the caller, which can be used to
+ * determine whether there were referrals.
+ */
+static int
+expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb,
+ int check_prefix)
+{
+ int rc;
+ unsigned int num_referrals = 0;
+ struct dfs_info3_param *referrals = NULL;
+ char *full_path = NULL, *ref_path = NULL, *mdata = NULL;
+
+ full_path = build_unc_path_to_root(volume_info, cifs_sb);
+ if (IS_ERR(full_path))
+ return PTR_ERR(full_path);
+
+ /* For DFS paths, skip the first '\' of the UNC */
+ ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1;
+
+ rc = get_dfs_path(xid, ses, ref_path, cifs_sb->local_nls,
+ &num_referrals, &referrals, cifs_remap(cifs_sb));
+
+ if (!rc && num_referrals > 0) {
+ char *fake_devname = NULL;
+
+ mdata = cifs_compose_mount_options(cifs_sb->mountdata,
+ full_path + 1, referrals,
+ &fake_devname);
+
+ free_dfs_info_array(referrals, num_referrals);
+
+ if (IS_ERR(mdata)) {
+ rc = PTR_ERR(mdata);
+ mdata = NULL;
+ } else {
+ cleanup_volume_info_contents(volume_info);
+ rc = cifs_setup_volume_info(volume_info, mdata,
+ fake_devname, false);
+ }
+ kfree(fake_devname);
+ kfree(cifs_sb->mountdata);
+ cifs_sb->mountdata = mdata;
+ }
+ kfree(full_path);
+ return rc;
+}
+#endif
+
+static int
+cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
+ const char *devname, bool is_smb3)
+{
+ int rc = 0;
+
+ if (cifs_parse_mount_options(mount_data, devname, volume_info, is_smb3))
+ return -EINVAL;
+
+ if (volume_info->nullauth) {
+ cifs_dbg(FYI, "Anonymous login\n");
+ kfree(volume_info->username);
+ volume_info->username = NULL;
+ } else if (volume_info->username) {
+ /* BB fixme parse for domain name here */
+ cifs_dbg(FYI, "Username: %s\n", volume_info->username);
+ } else {
+ cifs_dbg(VFS, "No username specified\n");
+ /* In userspace mount helper we can get user name from alternate
+ locations such as env variables and files on disk */
+ return -EINVAL;
+ }
+
+ /* this is needed for ASCII cp to Unicode converts */
+ if (volume_info->iocharset == NULL) {
+ /* load_nls_default cannot return null */
+ volume_info->local_nls = load_nls_default();
+ } else {
+ volume_info->local_nls = load_nls(volume_info->iocharset);
+ if (volume_info->local_nls == NULL) {
+ cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n",
+ volume_info->iocharset);
+ return -ELIBACC;
+ }
+ }
+
+ return rc;
+}
+
+struct smb_vol *
+cifs_get_volume_info(char *mount_data, const char *devname, bool is_smb3)
+{
+ int rc;
+ struct smb_vol *volume_info;
+
+ volume_info = kmalloc(sizeof(struct smb_vol), GFP_KERNEL);
+ if (!volume_info)
+ return ERR_PTR(-ENOMEM);
+
+ rc = cifs_setup_volume_info(volume_info, mount_data, devname, is_smb3);
+ if (rc) {
+ cifs_cleanup_volume_info(volume_info);
+ volume_info = ERR_PTR(rc);
+ }
+
+ return volume_info;
+}
+
+static int
+cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
+ unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ char *full_path)
+{
+ int rc;
+ char *s;
+ char sep, tmp;
+
+ sep = CIFS_DIR_SEP(cifs_sb);
+ s = full_path;
+
+ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
+ while (rc == 0) {
+ /* skip separators */
+ while (*s == sep)
+ s++;
+ if (!*s)
+ break;
+ /* next separator */
+ while (*s && *s != sep)
+ s++;
+
+ /*
+ * temporarily null-terminate the path at the end of
+ * the current component
+ */
+ tmp = *s;
+ *s = 0;
+ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
+ full_path);
+ *s = tmp;
+ }
+ return rc;
+}
+
+int
+cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
+{
+ int rc;
+ unsigned int xid;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ char *full_path;
+ struct tcon_link *tlink;
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ int referral_walks_count = 0;
+#endif
+
+#ifdef CONFIG_CIFS_DFS_UPCALL
+try_mount_again:
+ /* cleanup activities if we're chasing a referral */
+ if (referral_walks_count) {
+ if (tcon)
+ cifs_put_tcon(tcon);
+ else if (ses)
+ cifs_put_smb_ses(ses);
+
+ cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
+
+ free_xid(xid);
+ }
+#endif
+ rc = 0;
+ tcon = NULL;
+ ses = NULL;
+ server = NULL;
+ full_path = NULL;
+ tlink = NULL;
+
+ xid = get_xid();
+
+ /* get a reference to a tcp session */
+ server = cifs_get_tcp_session(volume_info);
+ if (IS_ERR(server)) {
+ rc = PTR_ERR(server);
+ goto out;
+ }
+ if ((volume_info->max_credits < 20) ||
+ (volume_info->max_credits > 60000))
+ server->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
+ else
+ server->max_credits = volume_info->max_credits;
+ /* get a reference to a SMB session */
+ ses = cifs_get_smb_ses(server, volume_info);
+ if (IS_ERR(ses)) {
+ rc = PTR_ERR(ses);
+ ses = NULL;
+ goto mount_fail_check;
+ }
+
+ if ((volume_info->persistent == true) && ((ses->server->capabilities &
+ SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == 0)) {
+ cifs_dbg(VFS, "persistent handles not supported by server\n");
+ rc = -EOPNOTSUPP;
+ goto mount_fail_check;
+ }
+
+ /* search for existing tcon to this server share */
+ tcon = cifs_get_tcon(ses, volume_info);
+ if (IS_ERR(tcon)) {
+ rc = PTR_ERR(tcon);
+ tcon = NULL;
+ if (rc == -EACCES)
+ goto mount_fail_check;
+
+ goto remote_path_check;
+ }
+
+ /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
+ if (tcon->posix_extensions)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
+
+ /* tell server which Unix caps we support */
+ if (cap_unix(tcon->ses)) {
+ /* reset of caps checks mount to see if unix extensions
+ disabled for just this mount */
+ reset_cifs_unix_caps(xid, tcon, cifs_sb, volume_info);
+ if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
+ (le64_to_cpu(tcon->fsUnixInfo.Capability) &
+ CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
+ rc = -EACCES;
+ goto mount_fail_check;
+ }
+ } else
+ tcon->unix_ext = 0; /* server does not support them */
+
+ /* do not care if a following call succeed - informational */
+ if (!tcon->pipe && server->ops->qfs_tcon)
+ server->ops->qfs_tcon(xid, tcon);
+
+ cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info);
+ cifs_sb->rsize = server->ops->negotiate_rsize(tcon, volume_info);
+
+remote_path_check:
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ /*
+ * Perform an unconditional check for whether there are DFS
+ * referrals for this path without prefix, to provide support
+ * for DFS referrals from w2k8 servers which don't seem to respond
+ * with PATH_NOT_COVERED to requests that include the prefix.
+ * Chase the referral if found, otherwise continue normally.
+ */
+ if (referral_walks_count == 0) {
+ int refrc = expand_dfs_referral(xid, ses, volume_info, cifs_sb,
+ false);
+ if (!refrc) {
+ referral_walks_count++;
+ goto try_mount_again;
+ }
+ }
+#endif
+
+ /* check if a whole path is not remote */
+ if (!rc && tcon) {
+ if (!server->ops->is_path_accessible) {
+ rc = -ENOSYS;
+ goto mount_fail_check;
+ }
+ /*
+ * cifs_build_path_to_root works only when we have a valid tcon
+ */
+ full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon,
+ tcon->Flags & SMB_SHARE_IS_IN_DFS);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
+ full_path);
+ if (rc != 0 && rc != -EREMOTE) {
+ kfree(full_path);
+ goto mount_fail_check;
+ }
+
+ if (rc != -EREMOTE) {
+ rc = cifs_are_all_path_components_accessible(server,
+ xid, tcon, cifs_sb,
+ full_path);
+ if (rc != 0) {
+ cifs_dbg(VFS, "cannot query dirs between root and final path, "
+ "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+ rc = 0;
+ }
+ }
+ kfree(full_path);
+ }
+
+ /* get referral if needed */
+ if (rc == -EREMOTE) {
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ if (referral_walks_count > MAX_NESTED_LINKS) {
+ /*
+ * BB: when we implement proper loop detection,
+ * we will remove this check. But now we need it
+ * to prevent an indefinite loop if 'DFS tree' is
+ * misconfigured (i.e. has loops).
+ */
+ rc = -ELOOP;
+ goto mount_fail_check;
+ }
+
+ rc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, true);
+
+ if (!rc) {
+ referral_walks_count++;
+ goto try_mount_again;
+ }
+ goto mount_fail_check;
+#else /* No DFS support, return error on mount */
+ rc = -EOPNOTSUPP;
+#endif
+ }
+
+ if (rc)
+ goto mount_fail_check;
+
+ /* now, hang the tcon off of the superblock */
+ tlink = kzalloc(sizeof *tlink, GFP_KERNEL);
+ if (tlink == NULL) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+
+ tlink->tl_uid = ses->linux_uid;
+ tlink->tl_tcon = tcon;
+ tlink->tl_time = jiffies;
+ set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
+ set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
+
+ cifs_sb->master_tlink = tlink;
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+
+ queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
+ TLINK_IDLE_EXPIRE);
+
+mount_fail_check:
+ /* on error free sesinfo and tcon struct if needed */
+ if (rc) {
+ /* If find_unc succeeded then rc == 0 so we can not end */
+ /* up accidentally freeing someone elses tcon struct */
+ if (tcon)
+ cifs_put_tcon(tcon);
+ else if (ses)
+ cifs_put_smb_ses(ses);
+ else
+ cifs_put_tcp_session(server, 0);
+ }
+
+out:
+ free_xid(xid);
+ return rc;
+}
+
+/*
+ * Issue a TREE_CONNECT request.
+ */
+int
+CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
+ const char *tree, struct cifs_tcon *tcon,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ TCONX_REQ *pSMB;
+ TCONX_RSP *pSMBr;
+ unsigned char *bcc_ptr;
+ int rc = 0;
+ int length;
+ __u16 bytes_left, count;
+
+ if (ses == NULL)
+ return -EIO;
+
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL)
+ return -ENOMEM;
+
+ smb_buffer_response = smb_buffer;
+
+ header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
+ NULL /*no tid */ , 4 /*wct */ );
+
+ smb_buffer->Mid = get_next_mid(ses->server);
+ smb_buffer->Uid = ses->Suid;
+ pSMB = (TCONX_REQ *) smb_buffer;
+ pSMBr = (TCONX_RSP *) smb_buffer_response;
+
+ pSMB->AndXCommand = 0xFF;
+ pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
+ bcc_ptr = &pSMB->Password[0];
+ if (tcon->pipe || (ses->server->sec_mode & SECMODE_USER)) {
+ pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
+ *bcc_ptr = 0; /* password is null byte */
+ bcc_ptr++; /* skip password */
+ /* already aligned so no need to do it below */
+ } else {
+ pSMB->PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+ /* BB FIXME add code to fail this if NTLMv2 or Kerberos
+ specified as required (when that support is added to
+ the vfs in the future) as only NTLM or the much
+ weaker LANMAN (which we do not send by default) is accepted
+ by Samba (not sure whether other servers allow
+ NTLMv2 password here) */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ if ((global_secflags & CIFSSEC_MAY_LANMAN) &&
+ (ses->sectype == LANMAN))
+ calc_lanman_hash(tcon->password, ses->server->cryptkey,
+ ses->server->sec_mode &
+ SECMODE_PW_ENCRYPT ? true : false,
+ bcc_ptr);
+ else
+#endif /* CIFS_WEAK_PW_HASH */
+ rc = SMBNTencrypt(tcon->password, ses->server->cryptkey,
+ bcc_ptr, nls_codepage);
+ if (rc) {
+ cifs_dbg(FYI, "%s Can't generate NTLM rsp. Error: %d\n",
+ __func__, rc);
+ cifs_buf_release(smb_buffer);
+ return rc;
+ }
+
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ if (ses->capabilities & CAP_UNICODE) {
+ /* must align unicode strings */
+ *bcc_ptr = 0; /* null byte password */
+ bcc_ptr++;
+ }
+ }
+
+ if (ses->server->sign)
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ }
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ length =
+ cifs_strtoUTF16((__le16 *) bcc_ptr, tree,
+ 6 /* max utf8 char length in bytes */ *
+ (/* server len*/ + 256 /* share len */), nls_codepage);
+ bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */
+ bcc_ptr += 2; /* skip trailing null */
+ } else { /* ASCII */
+ strcpy(bcc_ptr, tree);
+ bcc_ptr += strlen(tree) + 1;
+ }
+ strcpy(bcc_ptr, "?????");
+ bcc_ptr += strlen("?????");
+ bcc_ptr += 1;
+ count = bcc_ptr - &pSMB->Password[0];
+ pSMB->hdr.smb_buf_length = cpu_to_be32(be32_to_cpu(
+ pSMB->hdr.smb_buf_length) + count);
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
+ 0);
+
+ /* above now done in SendReceive */
+ if (rc == 0) {
+ bool is_unicode;
+
+ tcon->tidStatus = CifsGood;
+ tcon->need_reconnect = false;
+ tcon->tid = smb_buffer_response->Tid;
+ bcc_ptr = pByteArea(smb_buffer_response);
+ bytes_left = get_bcc(smb_buffer_response);
+ length = strnlen(bcc_ptr, bytes_left - 2);
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
+ is_unicode = true;
+ else
+ is_unicode = false;
+
+
+ /* skip service field (NB: this field is always ASCII) */
+ if (length == 3) {
+ if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
+ (bcc_ptr[2] == 'C')) {
+ cifs_dbg(FYI, "IPC connection\n");
+ tcon->ipc = true;
+ tcon->pipe = true;
+ }
+ } else if (length == 2) {
+ if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
+ /* the most common case */
+ cifs_dbg(FYI, "disk share connection\n");
+ }
+ }
+ bcc_ptr += length + 1;
+ bytes_left -= (length + 1);
+ strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
+
+ /* mostly informational -- no need to fail on error here */
+ kfree(tcon->nativeFileSystem);
+ tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr,
+ bytes_left, is_unicode,
+ nls_codepage);
+
+ cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem);
+
+ if ((smb_buffer_response->WordCount == 3) ||
+ (smb_buffer_response->WordCount == 7))
+ /* field is in same location */
+ tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+ else
+ tcon->Flags = 0;
+ cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags);
+ }
+
+ cifs_buf_release(smb_buffer);
+ return rc;
+}
+
+static void delayed_free(struct rcu_head *p)
+{
+ struct cifs_sb_info *sbi = container_of(p, struct cifs_sb_info, rcu);
+ unload_nls(sbi->local_nls);
+ kfree(sbi);
+}
+
+void
+cifs_umount(struct cifs_sb_info *cifs_sb)
+{
+ struct rb_root *root = &cifs_sb->tlink_tree;
+ struct rb_node *node;
+ struct tcon_link *tlink;
+
+ cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
+
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ while ((node = rb_first(root))) {
+ tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+ cifs_get_tlink(tlink);
+ clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
+ rb_erase(node, root);
+
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+ cifs_put_tlink(tlink);
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ }
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+
+ kfree(cifs_sb->mountdata);
+ kfree(cifs_sb->prepath);
+ call_rcu(&cifs_sb->rcu, delayed_free);
+}
+
+int
+cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
+{
+ int rc = 0;
+ struct TCP_Server_Info *server = ses->server;
+
+ if (!server->ops->need_neg || !server->ops->negotiate)
+ return -ENOSYS;
+
+ /* only send once per connect */
+ if (!server->ops->need_neg(server))
+ return 0;
+
+ set_credits(server, 1);
+
+ rc = server->ops->negotiate(xid, ses);
+ if (rc == 0) {
+ spin_lock(&GlobalMid_Lock);
+ if (server->tcpStatus == CifsNeedNegotiate)
+ server->tcpStatus = CifsGood;
+ else
+ rc = -EHOSTDOWN;
+ spin_unlock(&GlobalMid_Lock);
+ }
+
+ return rc;
+}
+
+int
+cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
+ struct nls_table *nls_info)
+{
+ int rc = -ENOSYS;
+ struct TCP_Server_Info *server = ses->server;
+
+ ses->capabilities = server->capabilities;
+ if (linuxExtEnabled == 0)
+ ses->capabilities &= (~server->vals->cap_unix);
+
+ cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
+ server->sec_mode, server->capabilities, server->timeAdj);
+
+ if (ses->auth_key.response) {
+ cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
+ ses->auth_key.response);
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+ ses->auth_key.len = 0;
+ }
+
+ if (server->ops->sess_setup)
+ rc = server->ops->sess_setup(xid, ses, nls_info);
+
+ if (rc)
+ cifs_dbg(VFS, "Send error in SessSetup = %d\n", rc);
+
+ return rc;
+}
+
+static int
+cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses)
+{
+ vol->sectype = ses->sectype;
+
+ /* krb5 is special, since we don't need username or pw */
+ if (vol->sectype == Kerberos)
+ return 0;
+
+ return cifs_set_cifscreds(vol, ses);
+}
+
+static struct cifs_tcon *
+cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
+{
+ int rc;
+ struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon = NULL;
+ struct smb_vol *vol_info;
+
+ vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL);
+ if (vol_info == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ vol_info->local_nls = cifs_sb->local_nls;
+ vol_info->linux_uid = fsuid;
+ vol_info->cred_uid = fsuid;
+ vol_info->UNC = master_tcon->treeName;
+ vol_info->retry = master_tcon->retry;
+ vol_info->nocase = master_tcon->nocase;
+ vol_info->nohandlecache = master_tcon->nohandlecache;
+ vol_info->local_lease = master_tcon->local_lease;
+ vol_info->no_lease = master_tcon->no_lease;
+ vol_info->resilient = master_tcon->use_resilient;
+ vol_info->persistent = master_tcon->use_persistent;
+ vol_info->no_linux_ext = !master_tcon->unix_ext;
+ vol_info->linux_ext = master_tcon->posix_extensions;
+ vol_info->sectype = master_tcon->ses->sectype;
+ vol_info->sign = master_tcon->ses->sign;
+ vol_info->seal = master_tcon->seal;
+
+ rc = cifs_set_vol_auth(vol_info, master_tcon->ses);
+ if (rc) {
+ tcon = ERR_PTR(rc);
+ goto out;
+ }
+
+ /* get a reference for the same TCP session */
+ spin_lock(&cifs_tcp_ses_lock);
+ ++master_tcon->ses->server->srv_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
+ if (IS_ERR(ses)) {
+ tcon = (struct cifs_tcon *)ses;
+ cifs_put_tcp_session(master_tcon->ses->server, 0);
+ goto out;
+ }
+
+ tcon = cifs_get_tcon(ses, vol_info);
+ if (IS_ERR(tcon)) {
+ cifs_put_smb_ses(ses);
+ goto out;
+ }
+
+ if (cap_unix(ses))
+ reset_cifs_unix_caps(0, tcon, NULL, vol_info);
+
+out:
+ kfree(vol_info->username);
+ kzfree(vol_info->password);
+ kfree(vol_info);
+
+ return tcon;
+}
+
+struct cifs_tcon *
+cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
+{
+ return tlink_tcon(cifs_sb_master_tlink(cifs_sb));
+}
+
+/* find and return a tlink with given uid */
+static struct tcon_link *
+tlink_rb_search(struct rb_root *root, kuid_t uid)
+{
+ struct rb_node *node = root->rb_node;
+ struct tcon_link *tlink;
+
+ while (node) {
+ tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+
+ if (uid_gt(tlink->tl_uid, uid))
+ node = node->rb_left;
+ else if (uid_lt(tlink->tl_uid, uid))
+ node = node->rb_right;
+ else
+ return tlink;
+ }
+ return NULL;
+}
+
+/* insert a tcon_link into the tree */
+static void
+tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+ struct tcon_link *tlink;
+
+ while (*new) {
+ tlink = rb_entry(*new, struct tcon_link, tl_rbnode);
+ parent = *new;
+
+ if (uid_gt(tlink->tl_uid, new_tlink->tl_uid))
+ new = &((*new)->rb_left);
+ else
+ new = &((*new)->rb_right);
+ }
+
+ rb_link_node(&new_tlink->tl_rbnode, parent, new);
+ rb_insert_color(&new_tlink->tl_rbnode, root);
+}
+
+/*
+ * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the
+ * current task.
+ *
+ * If the superblock doesn't refer to a multiuser mount, then just return
+ * the master tcon for the mount.
+ *
+ * First, search the rbtree for an existing tcon for this fsuid. If one
+ * exists, then check to see if it's pending construction. If it is then wait
+ * for construction to complete. Once it's no longer pending, check to see if
+ * it failed and either return an error or retry construction, depending on
+ * the timeout.
+ *
+ * If one doesn't exist then insert a new tcon_link struct into the tree and
+ * try to construct a new one.
+ */
+struct tcon_link *
+cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
+{
+ int ret;
+ kuid_t fsuid = current_fsuid();
+ struct tcon_link *tlink, *newtlink;
+
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
+ return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
+
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
+ if (tlink)
+ cifs_get_tlink(tlink);
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+
+ if (tlink == NULL) {
+ newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
+ if (newtlink == NULL)
+ return ERR_PTR(-ENOMEM);
+ newtlink->tl_uid = fsuid;
+ newtlink->tl_tcon = ERR_PTR(-EACCES);
+ set_bit(TCON_LINK_PENDING, &newtlink->tl_flags);
+ set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags);
+ cifs_get_tlink(newtlink);
+
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ /* was one inserted after previous search? */
+ tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
+ if (tlink) {
+ cifs_get_tlink(tlink);
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+ kfree(newtlink);
+ goto wait_for_construction;
+ }
+ tlink = newtlink;
+ tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+ } else {
+wait_for_construction:
+ ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
+ TASK_INTERRUPTIBLE);
+ if (ret) {
+ cifs_put_tlink(tlink);
+ return ERR_PTR(-ERESTARTSYS);
+ }
+
+ /* if it's good, return it */
+ if (!IS_ERR(tlink->tl_tcon))
+ return tlink;
+
+ /* return error if we tried this already recently */
+ if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) {
+ cifs_put_tlink(tlink);
+ return ERR_PTR(-EACCES);
+ }
+
+ if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags))
+ goto wait_for_construction;
+ }
+
+ tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid);
+ clear_bit(TCON_LINK_PENDING, &tlink->tl_flags);
+ wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING);
+
+ if (IS_ERR(tlink->tl_tcon)) {
+ cifs_put_tlink(tlink);
+ return ERR_PTR(-EACCES);
+ }
+
+ return tlink;
+}
+
+/*
+ * periodic workqueue job that scans tcon_tree for a superblock and closes
+ * out tcons.
+ */
+static void
+cifs_prune_tlinks(struct work_struct *work)
+{
+ struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info,
+ prune_tlinks.work);
+ struct rb_root *root = &cifs_sb->tlink_tree;
+ struct rb_node *node;
+ struct rb_node *tmp;
+ struct tcon_link *tlink;
+
+ /*
+ * Because we drop the spinlock in the loop in order to put the tlink
+ * it's not guarded against removal of links from the tree. The only
+ * places that remove entries from the tree are this function and
+ * umounts. Because this function is non-reentrant and is canceled
+ * before umount can proceed, this is safe.
+ */
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ node = rb_first(root);
+ while (node != NULL) {
+ tmp = node;
+ node = rb_next(tmp);
+ tlink = rb_entry(tmp, struct tcon_link, tl_rbnode);
+
+ if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) ||
+ atomic_read(&tlink->tl_count) != 0 ||
+ time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies))
+ continue;
+
+ cifs_get_tlink(tlink);
+ clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
+ rb_erase(tmp, root);
+
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+ cifs_put_tlink(tlink);
+ spin_lock(&cifs_sb->tlink_tree_lock);
+ }
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+
+ queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
+ TLINK_IDLE_EXPIRE);
+}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
new file mode 100644
index 000000000..c7e162c93
--- /dev/null
+++ b/fs/cifs/dir.c
@@ -0,0 +1,999 @@
+/*
+ * fs/cifs/dir.c
+ *
+ * vfs operations that deal with dentries
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2009
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+
+static void
+renew_parental_timestamps(struct dentry *direntry)
+{
+ /* BB check if there is a way to get the kernel to do this or if we
+ really need this */
+ do {
+ cifs_set_time(direntry, jiffies);
+ direntry = direntry->d_parent;
+ } while (!IS_ROOT(direntry));
+}
+
+char *
+cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, int add_treename)
+{
+ int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0;
+ int dfsplen;
+ char *full_path = NULL;
+
+ /* if no prefix path, simply set path to the root of share to "" */
+ if (pplen == 0) {
+ full_path = kzalloc(1, GFP_KERNEL);
+ return full_path;
+ }
+
+ if (add_treename)
+ dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
+ else
+ dfsplen = 0;
+
+ full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL);
+ if (full_path == NULL)
+ return full_path;
+
+ if (dfsplen)
+ strncpy(full_path, tcon->treeName, dfsplen);
+ full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb);
+ strncpy(full_path + dfsplen + 1, vol->prepath, pplen);
+ convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
+ full_path[dfsplen + pplen] = 0; /* add trailing null */
+ return full_path;
+}
+
+/* Note: caller must free return buffer */
+char *
+build_path_from_dentry(struct dentry *direntry)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS;
+
+ return build_path_from_dentry_optional_prefix(direntry,
+ prefix);
+}
+
+char *
+build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix)
+{
+ struct dentry *temp;
+ int namelen;
+ int dfsplen;
+ int pplen = 0;
+ char *full_path;
+ char dirsep;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ unsigned seq;
+
+ dirsep = CIFS_DIR_SEP(cifs_sb);
+ if (prefix)
+ dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
+ else
+ dfsplen = 0;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
+ pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;
+
+cifs_bp_rename_retry:
+ namelen = dfsplen + pplen;
+ seq = read_seqbegin(&rename_lock);
+ rcu_read_lock();
+ for (temp = direntry; !IS_ROOT(temp);) {
+ namelen += (1 + temp->d_name.len);
+ temp = temp->d_parent;
+ if (temp == NULL) {
+ cifs_dbg(VFS, "corrupt dentry\n");
+ rcu_read_unlock();
+ return NULL;
+ }
+ }
+ rcu_read_unlock();
+
+ full_path = kmalloc(namelen+1, GFP_KERNEL);
+ if (full_path == NULL)
+ return full_path;
+ full_path[namelen] = 0; /* trailing null */
+ rcu_read_lock();
+ for (temp = direntry; !IS_ROOT(temp);) {
+ spin_lock(&temp->d_lock);
+ namelen -= 1 + temp->d_name.len;
+ if (namelen < 0) {
+ spin_unlock(&temp->d_lock);
+ break;
+ } else {
+ full_path[namelen] = dirsep;
+ strncpy(full_path + namelen + 1, temp->d_name.name,
+ temp->d_name.len);
+ cifs_dbg(FYI, "name: %s\n", full_path + namelen);
+ }
+ spin_unlock(&temp->d_lock);
+ temp = temp->d_parent;
+ if (temp == NULL) {
+ cifs_dbg(VFS, "corrupt dentry\n");
+ rcu_read_unlock();
+ kfree(full_path);
+ return NULL;
+ }
+ }
+ rcu_read_unlock();
+ if (namelen != dfsplen + pplen || read_seqretry(&rename_lock, seq)) {
+ cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
+ namelen, dfsplen);
+ /* presumably this is only possible if racing with a rename
+ of one of the parent directories (we can not lock the dentries
+ above us to prevent this, but retrying should be harmless) */
+ kfree(full_path);
+ goto cifs_bp_rename_retry;
+ }
+ /* DIR_SEP already set for byte 0 / vs \ but not for
+ subsequent slashes in prepath which currently must
+ be entered the right way - not sure if there is an alternative
+ since the '\' is a valid posix character so we can not switch
+ those safely to '/' if any are found in the middle of the prepath */
+ /* BB test paths to Windows with '/' in the midst of prepath */
+
+ if (pplen) {
+ int i;
+
+ cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
+ memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
+ full_path[dfsplen] = dirsep;
+ for (i = 0; i < pplen-1; i++)
+ if (full_path[dfsplen+1+i] == '/')
+ full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
+ }
+
+ if (dfsplen) {
+ strncpy(full_path, tcon->treeName, dfsplen);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
+ int i;
+ for (i = 0; i < dfsplen; i++) {
+ if (full_path[i] == '\\')
+ full_path[i] = '/';
+ }
+ }
+ }
+ return full_path;
+}
+
+/*
+ * Don't allow path components longer than the server max.
+ * Don't allow the separator character in a path component.
+ * The VFS will not allow "/", but "\" is allowed by posix.
+ */
+static int
+check_name(struct dentry *direntry, struct cifs_tcon *tcon)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+ int i;
+
+ if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength &&
+ direntry->d_name.len >
+ le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength)))
+ return -ENAMETOOLONG;
+
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
+ for (i = 0; i < direntry->d_name.len; i++) {
+ if (direntry->d_name.name[i] == '\\') {
+ cifs_dbg(FYI, "Invalid file name\n");
+ return -EINVAL;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/* Inode operations in similar order to how they appear in Linux file fs.h */
+
+static int
+cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
+ struct tcon_link *tlink, unsigned oflags, umode_t mode,
+ __u32 *oplock, struct cifs_fid *fid)
+{
+ int rc = -ENOENT;
+ int create_options = CREATE_NOT_DIR;
+ int desired_access;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifs_tcon *tcon = tlink_tcon(tlink);
+ char *full_path = NULL;
+ FILE_ALL_INFO *buf = NULL;
+ struct inode *newinode = NULL;
+ int disposition;
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
+
+ *oplock = 0;
+ if (tcon->ses->server->oplocks)
+ *oplock = REQ_OPLOCK;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
+ (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode,
+ oflags, oplock, &fid->netfid, xid);
+ switch (rc) {
+ case 0:
+ if (newinode == NULL) {
+ /* query inode info */
+ goto cifs_create_get_file_info;
+ }
+
+ if (S_ISDIR(newinode->i_mode)) {
+ CIFSSMBClose(xid, tcon, fid->netfid);
+ iput(newinode);
+ rc = -EISDIR;
+ goto out;
+ }
+
+ if (!S_ISREG(newinode->i_mode)) {
+ /*
+ * The server may allow us to open things like
+ * FIFOs, but the client isn't set up to deal
+ * with that. If it's not a regular file, just
+ * close it and proceed as if it were a normal
+ * lookup.
+ */
+ CIFSSMBClose(xid, tcon, fid->netfid);
+ goto cifs_create_get_file_info;
+ }
+ /* success, no need to query */
+ goto cifs_create_set_dentry;
+
+ case -ENOENT:
+ goto cifs_create_get_file_info;
+
+ case -EIO:
+ case -EINVAL:
+ /*
+ * EIO could indicate that (posix open) operation is not
+ * supported, despite what server claimed in capability
+ * negotiation.
+ *
+ * POSIX open in samba versions 3.3.1 and earlier could
+ * incorrectly fail with invalid parameter.
+ */
+ tcon->broken_posix_open = true;
+ break;
+
+ case -EREMOTE:
+ case -EOPNOTSUPP:
+ /*
+ * EREMOTE indicates DFS junction, which is not handled
+ * in posix open. If either that or op not supported
+ * returned, follow the normal lookup.
+ */
+ break;
+
+ default:
+ goto out;
+ }
+ /*
+ * fallthrough to retry, using older open call, this is case
+ * where server does not support this SMB level, and falsely
+ * claims capability (also get here for DFS case which should be
+ * rare for path not covered on files)
+ */
+ }
+
+ desired_access = 0;
+ if (OPEN_FMODE(oflags) & FMODE_READ)
+ desired_access |= GENERIC_READ; /* is this too little? */
+ if (OPEN_FMODE(oflags) & FMODE_WRITE)
+ desired_access |= GENERIC_WRITE;
+
+ disposition = FILE_OVERWRITE_IF;
+ if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ disposition = FILE_CREATE;
+ else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+ disposition = FILE_OVERWRITE_IF;
+ else if ((oflags & O_CREAT) == O_CREAT)
+ disposition = FILE_OPEN_IF;
+ else
+ cifs_dbg(FYI, "Create flag not set in create function\n");
+
+ /*
+ * BB add processing to set equivalent of mode - e.g. via CreateX with
+ * ACLs
+ */
+
+ if (!server->ops->open) {
+ rc = -ENOSYS;
+ goto out;
+ }
+
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (buf == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * if we're not using unix extensions, see if we need to set
+ * ATTR_READONLY on the create call
+ */
+ if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
+ create_options |= CREATE_OPTION_READONLY;
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = desired_access;
+ oparms.create_options = create_options;
+ oparms.disposition = disposition;
+ oparms.path = full_path;
+ oparms.fid = fid;
+ oparms.reconnect = false;
+ oparms.mode = mode;
+ rc = server->ops->open(xid, &oparms, oplock, buf);
+ if (rc) {
+ cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
+ goto out;
+ }
+
+ /*
+ * If Open reported that we actually created a file then we now have to
+ * set the mode if possible.
+ */
+ if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ args.gid = inode->i_gid;
+ else
+ args.gid = current_fsgid();
+ } else {
+ args.uid = INVALID_UID; /* no change */
+ args.gid = INVALID_GID; /* no change */
+ }
+ CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid,
+ current->tgid);
+ } else {
+ /*
+ * BB implement mode setting via Windows security
+ * descriptors e.g.
+ */
+ /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
+
+ /* Could set r/o dos attribute if mode & 0222 == 0 */
+ }
+
+cifs_create_get_file_info:
+ /* server might mask mode so we have to query for it */
+ if (tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
+ xid);
+ else {
+ rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
+ xid, fid);
+ if (newinode) {
+ if (server->ops->set_lease_key)
+ server->ops->set_lease_key(newinode, fid);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ newinode->i_mode = mode;
+ if ((*oplock & CIFS_CREATE_ACTION) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
+ newinode->i_uid = current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ newinode->i_gid = inode->i_gid;
+ else
+ newinode->i_gid = current_fsgid();
+ }
+ }
+ }
+
+cifs_create_set_dentry:
+ if (rc != 0) {
+ cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n",
+ rc);
+ goto out_err;
+ }
+
+ if (S_ISDIR(newinode->i_mode)) {
+ rc = -EISDIR;
+ goto out_err;
+ }
+
+ d_drop(direntry);
+ d_add(direntry, newinode);
+
+out:
+ kfree(buf);
+ kfree(full_path);
+ return rc;
+
+out_err:
+ if (server->ops->close)
+ server->ops->close(xid, tcon, fid);
+ if (newinode)
+ iput(newinode);
+ goto out;
+}
+
+int
+cifs_atomic_open(struct inode *inode, struct dentry *direntry,
+ struct file *file, unsigned oflags, umode_t mode)
+{
+ int rc;
+ unsigned int xid;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
+ __u32 oplock;
+ struct cifsFileInfo *file_info;
+
+ /*
+ * Posix open is only called (at lookup time) for file create now. For
+ * opens (rather than creates), because we do not know if it is a file
+ * or directory yet, and current Samba no longer allows us to do posix
+ * open on dirs, we could end up wasting an open call on what turns out
+ * to be a dir. For file opens, we wait to call posix open till
+ * cifs_open. It could be added to atomic_open in the future but the
+ * performance tradeoff of the extra network request when EISDIR or
+ * EACCES is returned would have to be weighed against the 50% reduction
+ * in network traffic in the other paths.
+ */
+ if (!(oflags & O_CREAT)) {
+ struct dentry *res;
+
+ /*
+ * Check for hashed negative dentry. We have already revalidated
+ * the dentry and it is fine. No need to perform another lookup.
+ */
+ if (!d_in_lookup(direntry))
+ return -ENOENT;
+
+ res = cifs_lookup(inode, direntry, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ return finish_no_open(file, res);
+ }
+
+ xid = get_xid();
+
+ cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
+ inode, direntry, direntry);
+
+ tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ goto out_free_xid;
+ }
+
+ tcon = tlink_tcon(tlink);
+
+ rc = check_name(direntry, tcon);
+ if (rc)
+ goto out;
+
+ server = tcon->ses->server;
+
+ if (server->ops->new_lease_key)
+ server->ops->new_lease_key(&fid);
+
+ cifs_add_pending_open(&fid, tlink, &open);
+
+ rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
+ &oplock, &fid);
+
+ if (rc) {
+ cifs_del_pending_open(&open);
+ goto out;
+ }
+
+ if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ file->f_mode |= FMODE_CREATED;
+
+ rc = finish_open(file, direntry, generic_file_open);
+ if (rc) {
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
+ goto out;
+ }
+
+ if (file->f_flags & O_DIRECT &&
+ CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
+ if (CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ file->f_op = &cifs_file_direct_nobrl_ops;
+ else
+ file->f_op = &cifs_file_direct_ops;
+ }
+
+ file_info = cifs_new_fileinfo(&fid, file, tlink, oplock);
+ if (file_info == NULL) {
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
+ rc = -ENOMEM;
+ }
+
+out:
+ cifs_put_tlink(tlink);
+out_free_xid:
+ free_xid(xid);
+ return rc;
+}
+
+int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,
+ bool excl)
+{
+ int rc;
+ unsigned int xid = get_xid();
+ /*
+ * BB below access is probably too much for mknod to request
+ * but we have to do query and setpathinfo so requesting
+ * less could fail (unless we want to request getatr and setatr
+ * permissions (only). At least for POSIX we do not have to
+ * request so much.
+ */
+ unsigned oflags = O_EXCL | O_CREAT | O_RDWR;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifs_fid fid;
+ __u32 oplock;
+
+ cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
+ inode, direntry, direntry);
+
+ tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
+ rc = PTR_ERR(tlink);
+ if (IS_ERR(tlink))
+ goto out_free_xid;
+
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (server->ops->new_lease_key)
+ server->ops->new_lease_key(&fid);
+
+ rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
+ &oplock, &fid);
+ if (!rc && server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+
+ cifs_put_tlink(tlink);
+out_free_xid:
+ free_xid(xid);
+ return rc;
+}
+
+int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
+ dev_t device_number)
+{
+ int rc = -EPERM;
+ unsigned int xid;
+ int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
+ struct cifs_sb_info *cifs_sb;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct cifs_io_parms io_parms;
+ char *full_path = NULL;
+ struct inode *newinode = NULL;
+ __u32 oplock = 0;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ FILE_ALL_INFO *buf = NULL;
+ unsigned int bytes_written;
+ struct win_dev *pdev;
+ struct kvec iov[2];
+
+ if (!old_valid_dev(device_number))
+ return -EINVAL;
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ tcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto mknod_out;
+ }
+
+ if (tcon->unix_ext) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode & ~current_umask(),
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = device_number,
+ };
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = current_fsuid();
+ args.gid = current_fsgid();
+ } else {
+ args.uid = INVALID_UID; /* no change */
+ args.gid = INVALID_GID; /* no change */
+ }
+ rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (rc)
+ goto mknod_out;
+
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+
+ if (rc == 0)
+ d_instantiate(direntry, newinode);
+ goto mknod_out;
+ }
+
+ if (!S_ISCHR(mode) && !S_ISBLK(mode))
+ goto mknod_out;
+
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
+ goto mknod_out;
+
+
+ cifs_dbg(FYI, "sfu compat create special file\n");
+
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (buf == NULL) {
+ rc = -ENOMEM;
+ goto mknod_out;
+ }
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = GENERIC_WRITE;
+ oparms.create_options = create_options;
+ oparms.disposition = FILE_CREATE;
+ oparms.path = full_path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ if (tcon->ses->server->oplocks)
+ oplock = REQ_OPLOCK;
+ else
+ oplock = 0;
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
+ if (rc)
+ goto mknod_out;
+
+ /*
+ * BB Do not bother to decode buf since no local inode yet to put
+ * timestamps in, but we can reuse it safely.
+ */
+
+ pdev = (struct win_dev *)buf;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = sizeof(struct win_dev);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = sizeof(struct win_dev);
+ if (S_ISCHR(mode)) {
+ memcpy(pdev->type, "IntxCHR", 8);
+ pdev->major = cpu_to_le64(MAJOR(device_number));
+ pdev->minor = cpu_to_le64(MINOR(device_number));
+ rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+ &bytes_written, iov, 1);
+ } else if (S_ISBLK(mode)) {
+ memcpy(pdev->type, "IntxBLK", 8);
+ pdev->major = cpu_to_le64(MAJOR(device_number));
+ pdev->minor = cpu_to_le64(MINOR(device_number));
+ rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+ &bytes_written, iov, 1);
+ }
+ tcon->ses->server->ops->close(xid, tcon, &fid);
+ d_drop(direntry);
+
+ /* FIXME: add code here to set EAs */
+
+mknod_out:
+ kfree(full_path);
+ kfree(buf);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+struct dentry *
+cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
+ unsigned int flags)
+{
+ unsigned int xid;
+ int rc = 0; /* to get around spurious gcc warning, set to zero here */
+ struct cifs_sb_info *cifs_sb;
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ struct inode *newInode = NULL;
+ char *full_path = NULL;
+
+ xid = get_xid();
+
+ cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
+ parent_dir_inode, direntry, direntry);
+
+ /* check whether path exists */
+
+ cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ free_xid(xid);
+ return ERR_CAST(tlink);
+ }
+ pTcon = tlink_tcon(tlink);
+
+ rc = check_name(direntry, pTcon);
+ if (unlikely(rc)) {
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return ERR_PTR(rc);
+ }
+
+ /* can not grab the rename sem here since it would
+ deadlock in the cases (beginning of sys_rename itself)
+ in which we already have the sb rename sem */
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (d_really_is_positive(direntry)) {
+ cifs_dbg(FYI, "non-NULL inode in lookup\n");
+ } else {
+ cifs_dbg(FYI, "NULL inode in lookup\n");
+ }
+ cifs_dbg(FYI, "Full path: %s inode = 0x%p\n",
+ full_path, d_inode(direntry));
+
+ if (pTcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&newInode, full_path,
+ parent_dir_inode->i_sb, xid);
+ } else {
+ rc = cifs_get_inode_info(&newInode, full_path, NULL,
+ parent_dir_inode->i_sb, xid, NULL);
+ }
+
+ if (rc == 0) {
+ /* since paths are not looked up by component - the parent
+ directories are presumed to be good here */
+ renew_parental_timestamps(direntry);
+ } else if (rc == -ENOENT) {
+ cifs_set_time(direntry, jiffies);
+ newInode = NULL;
+ } else {
+ if (rc != -EACCES) {
+ cifs_dbg(FYI, "Unexpected lookup error %d\n", rc);
+ /* We special case check for Access Denied - since that
+ is a common return code */
+ }
+ newInode = ERR_PTR(rc);
+ }
+ kfree(full_path);
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return d_splice_alias(newInode, direntry);
+}
+
+static int
+cifs_d_revalidate(struct dentry *direntry, unsigned int flags)
+{
+ struct inode *inode;
+ int rc;
+
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ if (d_really_is_positive(direntry)) {
+ inode = d_inode(direntry);
+ if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode)))
+ CIFS_I(inode)->time = 0; /* force reval */
+
+ rc = cifs_revalidate_dentry(direntry);
+ if (rc) {
+ cifs_dbg(FYI, "cifs_revalidate_dentry failed with rc=%d", rc);
+ switch (rc) {
+ case -ENOENT:
+ case -ESTALE:
+ /*
+ * Those errors mean the dentry is invalid
+ * (file was deleted or recreated)
+ */
+ return 0;
+ default:
+ /*
+ * Otherwise some unexpected error happened
+ * report it as-is to VFS layer
+ */
+ return rc;
+ }
+ }
+ else {
+ /*
+ * If the inode wasn't known to be a dfs entry when
+ * the dentry was instantiated, such as when created
+ * via ->readdir(), it needs to be set now since the
+ * attributes will have been updated by
+ * cifs_revalidate_dentry().
+ */
+ if (IS_AUTOMOUNT(inode) &&
+ !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) {
+ spin_lock(&direntry->d_lock);
+ direntry->d_flags |= DCACHE_NEED_AUTOMOUNT;
+ spin_unlock(&direntry->d_lock);
+ }
+
+ return 1;
+ }
+ }
+
+ /*
+ * This may be nfsd (or something), anyway, we can't see the
+ * intent of this. So, since this can be for creation, drop it.
+ */
+ if (!flags)
+ return 0;
+
+ /*
+ * Drop the negative dentry, in order to make sure to use the
+ * case sensitive name which is specified by user if this is
+ * for creation.
+ */
+ if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
+ return 0;
+
+ if (time_after(jiffies, cifs_get_time(direntry) + HZ) || !lookupCacheEnabled)
+ return 0;
+
+ return 1;
+}
+
+/* static int cifs_d_delete(struct dentry *direntry)
+{
+ int rc = 0;
+
+ cifs_dbg(FYI, "In cifs d_delete, name = %pd\n", direntry);
+
+ return rc;
+} */
+
+const struct dentry_operations cifs_dentry_ops = {
+ .d_revalidate = cifs_d_revalidate,
+ .d_automount = cifs_dfs_d_automount,
+/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
+};
+
+static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q)
+{
+ struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
+ unsigned long hash;
+ wchar_t c;
+ int i, charlen;
+
+ hash = init_name_hash(dentry);
+ for (i = 0; i < q->len; i += charlen) {
+ charlen = codepage->char2uni(&q->name[i], q->len - i, &c);
+ /* error out if we can't convert the character */
+ if (unlikely(charlen < 0))
+ return charlen;
+ hash = partial_name_hash(cifs_toupper(c), hash);
+ }
+ q->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int cifs_ci_compare(const struct dentry *dentry,
+ unsigned int len, const char *str, const struct qstr *name)
+{
+ struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
+ wchar_t c1, c2;
+ int i, l1, l2;
+
+ /*
+ * We make the assumption here that uppercase characters in the local
+ * codepage are always the same length as their lowercase counterparts.
+ *
+ * If that's ever not the case, then this will fail to match it.
+ */
+ if (name->len != len)
+ return 1;
+
+ for (i = 0; i < len; i += l1) {
+ /* Convert characters in both strings to UTF-16. */
+ l1 = codepage->char2uni(&str[i], len - i, &c1);
+ l2 = codepage->char2uni(&name->name[i], name->len - i, &c2);
+
+ /*
+ * If we can't convert either character, just declare it to
+ * be 1 byte long and compare the original byte.
+ */
+ if (unlikely(l1 < 0 && l2 < 0)) {
+ if (str[i] != name->name[i])
+ return 1;
+ l1 = 1;
+ continue;
+ }
+
+ /*
+ * Here, we again ass|u|me that upper/lowercase versions of
+ * a character are the same length in the local NLS.
+ */
+ if (l1 != l2)
+ return 1;
+
+ /* Now compare uppercase versions of these characters */
+ if (cifs_toupper(c1) != cifs_toupper(c2))
+ return 1;
+ }
+
+ return 0;
+}
+
+const struct dentry_operations cifs_ci_dentry_ops = {
+ .d_revalidate = cifs_d_revalidate,
+ .d_hash = cifs_ci_hash,
+ .d_compare = cifs_ci_compare,
+ .d_automount = cifs_dfs_d_automount,
+};
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
new file mode 100644
index 000000000..7ede73065
--- /dev/null
+++ b/fs/cifs/dns_resolve.c
@@ -0,0 +1,99 @@
+/*
+ * fs/cifs/dns_resolve.c
+ *
+ * Copyright (c) 2007 Igor Mammedov
+ * Author(s): Igor Mammedov (niallain@gmail.com)
+ * Steve French (sfrench@us.ibm.com)
+ * Wang Lei (wang840925@gmail.com)
+ * David Howells (dhowells@redhat.com)
+ *
+ * Contains the CIFS DFS upcall routines used for hostname to
+ * IP address translation.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/dns_resolver.h>
+#include "dns_resolve.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+/**
+ * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
+ * @unc: UNC path specifying the server (with '/' as delimiter)
+ * @ip_addr: Where to return the IP address.
+ *
+ * The IP address will be returned in string form, and the caller is
+ * responsible for freeing it.
+ *
+ * Returns length of result on success, -ve on error.
+ */
+int
+dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+{
+ struct sockaddr_storage ss;
+ const char *hostname, *sep;
+ char *name;
+ int len, rc;
+
+ if (!ip_addr || !unc)
+ return -EINVAL;
+
+ len = strlen(unc);
+ if (len < 3) {
+ cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc);
+ return -EINVAL;
+ }
+
+ /* Discount leading slashes for cifs */
+ len -= 2;
+ hostname = unc + 2;
+
+ /* Search for server name delimiter */
+ sep = memchr(hostname, '/', len);
+ if (sep)
+ len = sep - hostname;
+ else
+ cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n",
+ __func__, unc);
+
+ /* Try to interpret hostname as an IPv4 or IPv6 address */
+ rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len);
+ if (rc > 0)
+ goto name_is_IP_address;
+
+ /* Perform the upcall */
+ rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL);
+ if (rc < 0)
+ cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
+ __func__, len, len, hostname);
+ else
+ cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
+ __func__, len, len, hostname, *ip_addr);
+ return rc;
+
+name_is_IP_address:
+ name = kmalloc(len + 1, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ memcpy(name, hostname, len);
+ name[len] = 0;
+ cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n",
+ __func__, name);
+ *ip_addr = name;
+ return 0;
+}
diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
new file mode 100644
index 000000000..d3f5d27f4
--- /dev/null
+++ b/fs/cifs/dns_resolve.h
@@ -0,0 +1,30 @@
+/*
+ * fs/cifs/dns_resolve.h -- DNS Resolver upcall management for CIFS DFS
+ * Handles host name to IP address resolution
+ *
+ * Copyright (c) International Business Machines Corp., 2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DNS_RESOLVE_H
+#define _DNS_RESOLVE_H
+
+#ifdef __KERNEL__
+extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
+#endif /* KERNEL */
+
+#endif /* _DNS_RESOLVE_H */
diff --git a/fs/cifs/export.c b/fs/cifs/export.c
new file mode 100644
index 000000000..ce8b7f677
--- /dev/null
+++ b/fs/cifs/export.c
@@ -0,0 +1,67 @@
+/*
+ * fs/cifs/export.c
+ *
+ * Copyright (C) International Business Machines Corp., 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Common Internet FileSystem (CIFS) client
+ *
+ * Operations related to support for exporting files via NFSD
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /*
+ * See Documentation/filesystems/nfs/Exporting
+ * and examples in fs/exportfs
+ *
+ * Since cifs is a network file system, an "fsid" must be included for
+ * any nfs exports file entries which refer to cifs paths. In addition
+ * the cifs mount must be mounted with the "serverino" option (ie use stable
+ * server inode numbers instead of locally generated temporary ones).
+ * Although cifs inodes do not use generation numbers (have generation number
+ * of zero) - the inode number alone should be good enough for simple cases
+ * in which users want to export cifs shares with NFS. The decode and encode
+ * could be improved by using a new routine which expects 64 bit inode numbers
+ * instead of the default 32 bit routines in fs/exportfs
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/exportfs.h>
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+
+#ifdef CONFIG_CIFS_NFSD_EXPORT
+static struct dentry *cifs_get_parent(struct dentry *dentry)
+{
+ /* BB need to add code here eventually to enable export via NFSD */
+ cifs_dbg(FYI, "get parent for %p\n", dentry);
+ return ERR_PTR(-EACCES);
+}
+
+const struct export_operations cifs_export_ops = {
+ .get_parent = cifs_get_parent,
+/* Following five export operations are unneeded so far and can default:
+ .get_dentry =
+ .get_name =
+ .find_exported_dentry =
+ .decode_fh =
+ .encode_fs = */
+};
+
+#endif /* CONFIG_CIFS_NFSD_EXPORT */
+
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
new file mode 100644
index 000000000..7b482489b
--- /dev/null
+++ b/fs/cifs/file.c
@@ -0,0 +1,4292 @@
+/*
+ * fs/cifs/file.c
+ *
+ * vfs operations that deal with files
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2010
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/backing-dev.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/pagemap.h>
+#include <linux/pagevec.h>
+#include <linux/writeback.h>
+#include <linux/task_io_accounting_ops.h>
+#include <linux/delay.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "fscache.h"
+#include "smbdirect.h"
+
+static inline int cifs_convert_flags(unsigned int flags)
+{
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ return GENERIC_READ;
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ return GENERIC_WRITE;
+ else if ((flags & O_ACCMODE) == O_RDWR) {
+ /* GENERIC_ALL is too much permission to request
+ can cause unnecessary access denied on create */
+ /* return GENERIC_ALL; */
+ return (GENERIC_READ | GENERIC_WRITE);
+ }
+
+ return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES |
+ FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA |
+ FILE_READ_DATA);
+}
+
+static u32 cifs_posix_convert_flags(unsigned int flags)
+{
+ u32 posix_flags = 0;
+
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ posix_flags = SMB_O_RDONLY;
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ posix_flags = SMB_O_WRONLY;
+ else if ((flags & O_ACCMODE) == O_RDWR)
+ posix_flags = SMB_O_RDWR;
+
+ if (flags & O_CREAT) {
+ posix_flags |= SMB_O_CREAT;
+ if (flags & O_EXCL)
+ posix_flags |= SMB_O_EXCL;
+ } else if (flags & O_EXCL)
+ cifs_dbg(FYI, "Application %s pid %d has incorrectly set O_EXCL flag but not O_CREAT on file open. Ignoring O_EXCL\n",
+ current->comm, current->tgid);
+
+ if (flags & O_TRUNC)
+ posix_flags |= SMB_O_TRUNC;
+ /* be safe and imply O_SYNC for O_DSYNC */
+ if (flags & O_DSYNC)
+ posix_flags |= SMB_O_SYNC;
+ if (flags & O_DIRECTORY)
+ posix_flags |= SMB_O_DIRECTORY;
+ if (flags & O_NOFOLLOW)
+ posix_flags |= SMB_O_NOFOLLOW;
+ if (flags & O_DIRECT)
+ posix_flags |= SMB_O_DIRECT;
+
+ return posix_flags;
+}
+
+static inline int cifs_get_disposition(unsigned int flags)
+{
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ return FILE_CREATE;
+ else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+ return FILE_OVERWRITE_IF;
+ else if ((flags & O_CREAT) == O_CREAT)
+ return FILE_OPEN_IF;
+ else if ((flags & O_TRUNC) == O_TRUNC)
+ return FILE_OVERWRITE;
+ else
+ return FILE_OPEN;
+}
+
+int cifs_posix_open(char *full_path, struct inode **pinode,
+ struct super_block *sb, int mode, unsigned int f_flags,
+ __u32 *poplock, __u16 *pnetfid, unsigned int xid)
+{
+ int rc;
+ FILE_UNIX_BASIC_INFO *presp_data;
+ __u32 posix_flags = 0;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_fattr fattr;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+
+ cifs_dbg(FYI, "posix open %s\n", full_path);
+
+ presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
+ if (presp_data == NULL)
+ return -ENOMEM;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ goto posix_open_ret;
+ }
+
+ tcon = tlink_tcon(tlink);
+ mode &= ~current_umask();
+
+ posix_flags = cifs_posix_convert_flags(f_flags);
+ rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data,
+ poplock, full_path, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ cifs_put_tlink(tlink);
+
+ if (rc)
+ goto posix_open_ret;
+
+ if (presp_data->Type == cpu_to_le32(-1))
+ goto posix_open_ret; /* open ok, caller does qpathinfo */
+
+ if (!pinode)
+ goto posix_open_ret; /* caller does not need info */
+
+ cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);
+
+ /* get new inode and set it up */
+ if (*pinode == NULL) {
+ cifs_fill_uniqueid(sb, &fattr);
+ *pinode = cifs_iget(sb, &fattr);
+ if (!*pinode) {
+ rc = -ENOMEM;
+ goto posix_open_ret;
+ }
+ } else {
+ cifs_revalidate_mapping(*pinode);
+ cifs_fattr_to_inode(*pinode, &fattr);
+ }
+
+posix_open_ret:
+ kfree(presp_data);
+ return rc;
+}
+
+static int
+cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
+ struct cifs_fid *fid, unsigned int xid)
+{
+ int rc;
+ int desired_access;
+ int disposition;
+ int create_options = CREATE_NOT_DIR;
+ FILE_ALL_INFO *buf;
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
+
+ if (!server->ops->open)
+ return -ENOSYS;
+
+ desired_access = cifs_convert_flags(f_flags);
+
+/*********************************************************************
+ * open flag mapping table:
+ *
+ * POSIX Flag CIFS Disposition
+ * ---------- ----------------
+ * O_CREAT FILE_OPEN_IF
+ * O_CREAT | O_EXCL FILE_CREATE
+ * O_CREAT | O_TRUNC FILE_OVERWRITE_IF
+ * O_TRUNC FILE_OVERWRITE
+ * none of the above FILE_OPEN
+ *
+ * Note that there is not a direct match between disposition
+ * FILE_SUPERSEDE (ie create whether or not file exists although
+ * O_CREAT | O_TRUNC is similar but truncates the existing
+ * file rather than creating a new file as FILE_SUPERSEDE does
+ * (which uses the attributes / metadata passed in on open call)
+ *?
+ *? O_SYNC is a reasonable match to CIFS writethrough flag
+ *? and the read write flags match reasonably. O_LARGEFILE
+ *? is irrelevant because largefile support is always used
+ *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY,
+ * O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation
+ *********************************************************************/
+
+ disposition = cifs_get_disposition(f_flags);
+
+ /* BB pass O_SYNC flag through on file attributes .. BB */
+
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
+ if (f_flags & O_SYNC)
+ create_options |= CREATE_WRITE_THROUGH;
+
+ if (f_flags & O_DIRECT)
+ create_options |= CREATE_NO_BUFFER;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = desired_access;
+ oparms.create_options = create_options;
+ oparms.disposition = disposition;
+ oparms.path = full_path;
+ oparms.fid = fid;
+ oparms.reconnect = false;
+
+ rc = server->ops->open(xid, &oparms, oplock, buf);
+
+ if (rc)
+ goto out;
+
+ if (tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb,
+ xid);
+ else
+ rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
+ xid, fid);
+
+ if (rc) {
+ server->ops->close(xid, tcon, fid);
+ if (rc == -ESTALE)
+ rc = -EOPENSTALE;
+ }
+
+out:
+ kfree(buf);
+ return rc;
+}
+
+static bool
+cifs_has_mand_locks(struct cifsInodeInfo *cinode)
+{
+ struct cifs_fid_locks *cur;
+ bool has_locks = false;
+
+ down_read(&cinode->lock_sem);
+ list_for_each_entry(cur, &cinode->llist, llist) {
+ if (!list_empty(&cur->locks)) {
+ has_locks = true;
+ break;
+ }
+ }
+ up_read(&cinode->lock_sem);
+ return has_locks;
+}
+
+void
+cifs_down_write(struct rw_semaphore *sem)
+{
+ while (!down_write_trylock(sem))
+ msleep(10);
+}
+
+struct cifsFileInfo *
+cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
+ struct tcon_link *tlink, __u32 oplock)
+{
+ struct dentry *dentry = file_dentry(file);
+ struct inode *inode = d_inode(dentry);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifsFileInfo *cfile;
+ struct cifs_fid_locks *fdlocks;
+ struct cifs_tcon *tcon = tlink_tcon(tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+ if (cfile == NULL)
+ return cfile;
+
+ fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL);
+ if (!fdlocks) {
+ kfree(cfile);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&fdlocks->locks);
+ fdlocks->cfile = cfile;
+ cfile->llist = fdlocks;
+
+ cfile->count = 1;
+ cfile->pid = current->tgid;
+ cfile->uid = current_fsuid();
+ cfile->dentry = dget(dentry);
+ cfile->f_flags = file->f_flags;
+ cfile->invalidHandle = false;
+ cfile->tlink = cifs_get_tlink(tlink);
+ INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
+ mutex_init(&cfile->fh_mutex);
+ spin_lock_init(&cfile->file_info_lock);
+
+ cifs_sb_active(inode->i_sb);
+
+ /*
+ * If the server returned a read oplock and we have mandatory brlocks,
+ * set oplock level to None.
+ */
+ if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) {
+ cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n");
+ oplock = 0;
+ }
+
+ cifs_down_write(&cinode->lock_sem);
+ list_add(&fdlocks->llist, &cinode->llist);
+ up_write(&cinode->lock_sem);
+
+ spin_lock(&tcon->open_file_lock);
+ if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
+ oplock = fid->pending_open->oplock;
+ list_del(&fid->pending_open->olist);
+
+ fid->purge_cache = false;
+ server->ops->set_fid(cfile, fid, oplock);
+
+ list_add(&cfile->tlist, &tcon->openFileList);
+
+ /* if readable file instance put first in list*/
+ spin_lock(&cinode->open_file_lock);
+ if (file->f_mode & FMODE_READ)
+ list_add(&cfile->flist, &cinode->openFileList);
+ else
+ list_add_tail(&cfile->flist, &cinode->openFileList);
+ spin_unlock(&cinode->open_file_lock);
+ spin_unlock(&tcon->open_file_lock);
+
+ if (fid->purge_cache)
+ cifs_zap_mapping(inode);
+
+ file->private_data = cfile;
+ return cfile;
+}
+
+struct cifsFileInfo *
+cifsFileInfo_get(struct cifsFileInfo *cifs_file)
+{
+ spin_lock(&cifs_file->file_info_lock);
+ cifsFileInfo_get_locked(cifs_file);
+ spin_unlock(&cifs_file->file_info_lock);
+ return cifs_file;
+}
+
+/**
+ * cifsFileInfo_put - release a reference of file priv data
+ *
+ * Always potentially wait for oplock handler. See _cifsFileInfo_put().
+ */
+void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
+{
+ _cifsFileInfo_put(cifs_file, true);
+}
+
+/**
+ * _cifsFileInfo_put - release a reference of file priv data
+ *
+ * This may involve closing the filehandle @cifs_file out on the
+ * server. Must be called without holding tcon->open_file_lock and
+ * cifs_file->file_info_lock.
+ *
+ * If @wait_for_oplock_handler is true and we are releasing the last
+ * reference, wait for any running oplock break handler of the file
+ * and cancel any pending one. If calling this function from the
+ * oplock break handler, you need to pass false.
+ *
+ */
+void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
+{
+ struct inode *inode = d_inode(cifs_file->dentry);
+ struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsLockInfo *li, *tmp;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
+ bool oplock_break_cancelled;
+
+ spin_lock(&tcon->open_file_lock);
+ spin_lock(&cifsi->open_file_lock);
+ spin_lock(&cifs_file->file_info_lock);
+ if (--cifs_file->count > 0) {
+ spin_unlock(&cifs_file->file_info_lock);
+ spin_unlock(&cifsi->open_file_lock);
+ spin_unlock(&tcon->open_file_lock);
+ return;
+ }
+ spin_unlock(&cifs_file->file_info_lock);
+
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ /* store open in pending opens to make sure we don't miss lease break */
+ cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
+
+ /* remove it from the lists */
+ list_del(&cifs_file->flist);
+ list_del(&cifs_file->tlist);
+
+ if (list_empty(&cifsi->openFileList)) {
+ cifs_dbg(FYI, "closing last open instance for inode %p\n",
+ d_inode(cifs_file->dentry));
+ /*
+ * In strict cache mode we need invalidate mapping on the last
+ * close because it may cause a error when we open this file
+ * again and get at least level II oplock.
+ */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
+ set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
+ cifs_set_oplock_level(cifsi, 0);
+ }
+
+ spin_unlock(&cifsi->open_file_lock);
+ spin_unlock(&tcon->open_file_lock);
+
+ oplock_break_cancelled = wait_oplock_handler ?
+ cancel_work_sync(&cifs_file->oplock_break) : false;
+
+ if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int xid;
+
+ xid = get_xid();
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &cifs_file->fid);
+ _free_xid(xid);
+ }
+
+ if (oplock_break_cancelled)
+ cifs_done_oplock_break(cifsi);
+
+ cifs_del_pending_open(&open);
+
+ /*
+ * Delete any outstanding lock records. We'll lose them when the file
+ * is closed anyway.
+ */
+ cifs_down_write(&cifsi->lock_sem);
+ list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
+ list_del(&li->llist);
+ cifs_del_lock_waiters(li);
+ kfree(li);
+ }
+ list_del(&cifs_file->llist->llist);
+ kfree(cifs_file->llist);
+ up_write(&cifsi->lock_sem);
+
+ cifs_put_tlink(cifs_file->tlink);
+ dput(cifs_file->dentry);
+ cifs_sb_deactive(sb);
+ kfree(cifs_file);
+}
+
+int cifs_open(struct inode *inode, struct file *file)
+
+{
+ int rc = -EACCES;
+ unsigned int xid;
+ __u32 oplock;
+ struct cifs_sb_info *cifs_sb;
+ struct TCP_Server_Info *server;
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+ struct cifsFileInfo *cfile = NULL;
+ char *full_path = NULL;
+ bool posix_open_ok = false;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
+
+ xid = get_xid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ free_xid(xid);
+ return PTR_ERR(tlink);
+ }
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ full_path = build_path_from_dentry(file_dentry(file));
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cifs_dbg(FYI, "inode = 0x%p file flags are 0x%x for %s\n",
+ inode, file->f_flags, full_path);
+
+ if (file->f_flags & O_DIRECT &&
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ file->f_op = &cifs_file_direct_nobrl_ops;
+ else
+ file->f_op = &cifs_file_direct_ops;
+ }
+
+ if (server->oplocks)
+ oplock = REQ_OPLOCK;
+ else
+ oplock = 0;
+
+ if (!tcon->broken_posix_open && tcon->unix_ext &&
+ cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ /* can not refresh inode info since size could be stale */
+ rc = cifs_posix_open(full_path, &inode, inode->i_sb,
+ cifs_sb->mnt_file_mode /* ignored */,
+ file->f_flags, &oplock, &fid.netfid, xid);
+ if (rc == 0) {
+ cifs_dbg(FYI, "posix open succeeded\n");
+ posix_open_ok = true;
+ } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
+ if (tcon->ses->serverNOS)
+ cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n",
+ tcon->ses->serverName,
+ tcon->ses->serverNOS);
+ tcon->broken_posix_open = true;
+ } else if ((rc != -EIO) && (rc != -EREMOTE) &&
+ (rc != -EOPNOTSUPP)) /* path not found or net err */
+ goto out;
+ /*
+ * Else fallthrough to retry open the old way on network i/o
+ * or DFS errors.
+ */
+ }
+
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ cifs_add_pending_open(&fid, tlink, &open);
+
+ if (!posix_open_ok) {
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
+ file->f_flags, &oplock, &fid, xid);
+ if (rc) {
+ cifs_del_pending_open(&open);
+ goto out;
+ }
+ }
+
+ cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
+ if (cfile == NULL) {
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cifs_fscache_set_inode_cookie(inode, file);
+
+ if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) {
+ /*
+ * Time to set mode which we can not set earlier due to
+ * problems creating new read-only files.
+ */
+ struct cifs_unix_set_info_args args = {
+ .mode = inode->i_mode,
+ .uid = INVALID_UID, /* no change */
+ .gid = INVALID_GID, /* no change */
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+ CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid,
+ cfile->pid);
+ }
+
+out:
+ kfree(full_path);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static int cifs_push_posix_locks(struct cifsFileInfo *cfile);
+
+/*
+ * Try to reacquire byte range locks that were released when session
+ * to server was lost.
+ */
+static int
+cifs_relock_file(struct cifsFileInfo *cfile)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ int rc = 0;
+
+ down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING);
+ if (cinode->can_cache_brlcks) {
+ /* can cache locks - no need to relock */
+ up_read(&cinode->lock_sem);
+ return rc;
+ }
+
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ rc = cifs_push_posix_locks(cfile);
+ else
+ rc = tcon->ses->server->ops->push_mand_locks(cfile);
+
+ up_read(&cinode->lock_sem);
+ return rc;
+}
+
+static int
+cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
+{
+ int rc = -EACCES;
+ unsigned int xid;
+ __u32 oplock;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifsInodeInfo *cinode;
+ struct inode *inode;
+ char *full_path = NULL;
+ int desired_access;
+ int disposition = FILE_OPEN;
+ int create_options = CREATE_NOT_DIR;
+ struct cifs_open_parms oparms;
+
+ xid = get_xid();
+ mutex_lock(&cfile->fh_mutex);
+ if (!cfile->invalidHandle) {
+ mutex_unlock(&cfile->fh_mutex);
+ rc = 0;
+ free_xid(xid);
+ return rc;
+ }
+
+ inode = d_inode(cfile->dentry);
+ cifs_sb = CIFS_SB(inode->i_sb);
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
+
+ /*
+ * Can not grab rename sem here because various ops, including those
+ * that already have the rename sem can end up causing writepage to get
+ * called and if the server was down that means we end up here, and we
+ * can never tell if the caller already has the rename_sem.
+ */
+ full_path = build_path_from_dentry(cfile->dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ mutex_unlock(&cfile->fh_mutex);
+ free_xid(xid);
+ return rc;
+ }
+
+ cifs_dbg(FYI, "inode = 0x%p file flags 0x%x for %s\n",
+ inode, cfile->f_flags, full_path);
+
+ if (tcon->ses->server->oplocks)
+ oplock = REQ_OPLOCK;
+ else
+ oplock = 0;
+
+ if (tcon->unix_ext && cap_unix(tcon->ses) &&
+ (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ /*
+ * O_CREAT, O_EXCL and O_TRUNC already had their effect on the
+ * original open. Must mask them off for a reopen.
+ */
+ unsigned int oflags = cfile->f_flags &
+ ~(O_CREAT | O_EXCL | O_TRUNC);
+
+ rc = cifs_posix_open(full_path, NULL, inode->i_sb,
+ cifs_sb->mnt_file_mode /* ignored */,
+ oflags, &oplock, &cfile->fid.netfid, xid);
+ if (rc == 0) {
+ cifs_dbg(FYI, "posix reopen succeeded\n");
+ oparms.reconnect = true;
+ goto reopen_success;
+ }
+ /*
+ * fallthrough to retry open the old way on errors, especially
+ * in the reconnect path it is important to retry hard
+ */
+ }
+
+ desired_access = cifs_convert_flags(cfile->f_flags);
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
+ if (cfile->f_flags & O_SYNC)
+ create_options |= CREATE_WRITE_THROUGH;
+
+ if (cfile->f_flags & O_DIRECT)
+ create_options |= CREATE_NO_BUFFER;
+
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &cfile->fid);
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = desired_access;
+ oparms.create_options = create_options;
+ oparms.disposition = disposition;
+ oparms.path = full_path;
+ oparms.fid = &cfile->fid;
+ oparms.reconnect = true;
+
+ /*
+ * Can not refresh inode by passing in file_info buf to be returned by
+ * ops->open and then calling get_inode_info with returned buf since
+ * file might have write behind data that needs to be flushed and server
+ * version of file size can be stale. If we knew for sure that inode was
+ * not dirty locally we could do this.
+ */
+ rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ if (rc == -ENOENT && oparms.reconnect == false) {
+ /* durable handle timeout is expired - open the file again */
+ rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ /* indicate that we need to relock the file */
+ oparms.reconnect = true;
+ }
+
+ if (rc) {
+ mutex_unlock(&cfile->fh_mutex);
+ cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc);
+ cifs_dbg(FYI, "oplock: %d\n", oplock);
+ goto reopen_error_exit;
+ }
+
+reopen_success:
+ cfile->invalidHandle = false;
+ mutex_unlock(&cfile->fh_mutex);
+ cinode = CIFS_I(inode);
+
+ if (can_flush) {
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (!is_interrupt_error(rc))
+ mapping_set_error(inode->i_mapping, rc);
+
+ if (tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&inode, full_path,
+ inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&inode, full_path, NULL,
+ inode->i_sb, xid, NULL);
+ }
+ /*
+ * Else we are writing out data to server already and could deadlock if
+ * we tried to flush data, and since we do not know if we have data that
+ * would invalidate the current end of file on the server we can not go
+ * to the server to get the new inode info.
+ */
+
+ /*
+ * If the server returned a read oplock and we have mandatory brlocks,
+ * set oplock level to None.
+ */
+ if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) {
+ cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n");
+ oplock = 0;
+ }
+
+ server->ops->set_fid(cfile, &cfile->fid, oplock);
+ if (oparms.reconnect)
+ cifs_relock_file(cfile);
+
+reopen_error_exit:
+ kfree(full_path);
+ free_xid(xid);
+ return rc;
+}
+
+int cifs_close(struct inode *inode, struct file *file)
+{
+ if (file->private_data != NULL) {
+ cifsFileInfo_put(file->private_data);
+ file->private_data = NULL;
+ }
+
+ /* return code from the ->release op is always ignored */
+ return 0;
+}
+
+void
+cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
+{
+ struct cifsFileInfo *open_file;
+ struct list_head *tmp;
+ struct list_head *tmp1;
+ struct list_head tmp_list;
+
+ if (!tcon->use_persistent || !tcon->need_reopen_files)
+ return;
+
+ tcon->need_reopen_files = false;
+
+ cifs_dbg(FYI, "Reopen persistent handles");
+ INIT_LIST_HEAD(&tmp_list);
+
+ /* list all files open on tree connection, reopen resilient handles */
+ spin_lock(&tcon->open_file_lock);
+ list_for_each(tmp, &tcon->openFileList) {
+ open_file = list_entry(tmp, struct cifsFileInfo, tlist);
+ if (!open_file->invalidHandle)
+ continue;
+ cifsFileInfo_get(open_file);
+ list_add_tail(&open_file->rlist, &tmp_list);
+ }
+ spin_unlock(&tcon->open_file_lock);
+
+ list_for_each_safe(tmp, tmp1, &tmp_list) {
+ open_file = list_entry(tmp, struct cifsFileInfo, rlist);
+ if (cifs_reopen_file(open_file, false /* do not flush */))
+ tcon->need_reopen_files = true;
+ list_del_init(&open_file->rlist);
+ cifsFileInfo_put(open_file);
+ }
+}
+
+int cifs_closedir(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ unsigned int xid;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ char *buf;
+
+ cifs_dbg(FYI, "Closedir inode = 0x%p\n", inode);
+
+ if (cfile == NULL)
+ return rc;
+
+ xid = get_xid();
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
+
+ cifs_dbg(FYI, "Freeing private data in close dir\n");
+ spin_lock(&cfile->file_info_lock);
+ if (server->ops->dir_needs_close(cfile)) {
+ cfile->invalidHandle = true;
+ spin_unlock(&cfile->file_info_lock);
+ if (server->ops->close_dir)
+ rc = server->ops->close_dir(xid, tcon, &cfile->fid);
+ else
+ rc = -ENOSYS;
+ cifs_dbg(FYI, "Closing uncompleted readdir with rc %d\n", rc);
+ /* not much we can do if it fails anyway, ignore rc */
+ rc = 0;
+ } else
+ spin_unlock(&cfile->file_info_lock);
+
+ buf = cfile->srch_inf.ntwrk_buf_start;
+ if (buf) {
+ cifs_dbg(FYI, "closedir free smb buf in srch struct\n");
+ cfile->srch_inf.ntwrk_buf_start = NULL;
+ if (cfile->srch_inf.smallBuf)
+ cifs_small_buf_release(buf);
+ else
+ cifs_buf_release(buf);
+ }
+
+ cifs_put_tlink(cfile->tlink);
+ kfree(file->private_data);
+ file->private_data = NULL;
+ /* BB can we lock the filestruct while this is going on? */
+ free_xid(xid);
+ return rc;
+}
+
+static struct cifsLockInfo *
+cifs_lock_init(__u64 offset, __u64 length, __u8 type)
+{
+ struct cifsLockInfo *lock =
+ kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
+ if (!lock)
+ return lock;
+ lock->offset = offset;
+ lock->length = length;
+ lock->type = type;
+ lock->pid = current->tgid;
+ INIT_LIST_HEAD(&lock->blist);
+ init_waitqueue_head(&lock->block_q);
+ return lock;
+}
+
+void
+cifs_del_lock_waiters(struct cifsLockInfo *lock)
+{
+ struct cifsLockInfo *li, *tmp;
+ list_for_each_entry_safe(li, tmp, &lock->blist, blist) {
+ list_del_init(&li->blist);
+ wake_up(&li->block_q);
+ }
+}
+
+#define CIFS_LOCK_OP 0
+#define CIFS_READ_OP 1
+#define CIFS_WRITE_OP 2
+
+/* @rw_check : 0 - no op, 1 - read, 2 - write */
+static bool
+cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
+ __u64 length, __u8 type, struct cifsFileInfo *cfile,
+ struct cifsLockInfo **conf_lock, int rw_check)
+{
+ struct cifsLockInfo *li;
+ struct cifsFileInfo *cur_cfile = fdlocks->cfile;
+ struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
+
+ list_for_each_entry(li, &fdlocks->locks, llist) {
+ if (offset + length <= li->offset ||
+ offset >= li->offset + li->length)
+ continue;
+ if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid &&
+ server->ops->compare_fids(cfile, cur_cfile)) {
+ /* shared lock prevents write op through the same fid */
+ if (!(li->type & server->vals->shared_lock_type) ||
+ rw_check != CIFS_WRITE_OP)
+ continue;
+ }
+ if ((type & server->vals->shared_lock_type) &&
+ ((server->ops->compare_fids(cfile, cur_cfile) &&
+ current->tgid == li->pid) || type == li->type))
+ continue;
+ if (conf_lock)
+ *conf_lock = li;
+ return true;
+ }
+ return false;
+}
+
+bool
+cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
+ __u8 type, struct cifsLockInfo **conf_lock,
+ int rw_check)
+{
+ bool rc = false;
+ struct cifs_fid_locks *cur;
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+
+ list_for_each_entry(cur, &cinode->llist, llist) {
+ rc = cifs_find_fid_lock_conflict(cur, offset, length, type,
+ cfile, conf_lock, rw_check);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Check if there is another lock that prevents us to set the lock (mandatory
+ * style). If such a lock exists, update the flock structure with its
+ * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks
+ * or leave it the same if we can't. Returns 0 if we don't need to request to
+ * the server or 1 otherwise.
+ */
+static int
+cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
+ __u8 type, struct file_lock *flock)
+{
+ int rc = 0;
+ struct cifsLockInfo *conf_lock;
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
+ bool exist;
+
+ down_read(&cinode->lock_sem);
+
+ exist = cifs_find_lock_conflict(cfile, offset, length, type,
+ &conf_lock, CIFS_LOCK_OP);
+ if (exist) {
+ flock->fl_start = conf_lock->offset;
+ flock->fl_end = conf_lock->offset + conf_lock->length - 1;
+ flock->fl_pid = conf_lock->pid;
+ if (conf_lock->type & server->vals->shared_lock_type)
+ flock->fl_type = F_RDLCK;
+ else
+ flock->fl_type = F_WRLCK;
+ } else if (!cinode->can_cache_brlcks)
+ rc = 1;
+ else
+ flock->fl_type = F_UNLCK;
+
+ up_read(&cinode->lock_sem);
+ return rc;
+}
+
+static void
+cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ cifs_down_write(&cinode->lock_sem);
+ list_add_tail(&lock->llist, &cfile->llist->locks);
+ up_write(&cinode->lock_sem);
+}
+
+/*
+ * Set the byte-range lock (mandatory style). Returns:
+ * 1) 0, if we set the lock and don't need to request to the server;
+ * 2) 1, if no locks prevent us but we need to request to the server;
+ * 3) -EACCESS, if there is a lock that prevents us and wait is false.
+ */
+static int
+cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
+ bool wait)
+{
+ struct cifsLockInfo *conf_lock;
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ bool exist;
+ int rc = 0;
+
+try_again:
+ exist = false;
+ cifs_down_write(&cinode->lock_sem);
+
+ exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
+ lock->type, &conf_lock, CIFS_LOCK_OP);
+ if (!exist && cinode->can_cache_brlcks) {
+ list_add_tail(&lock->llist, &cfile->llist->locks);
+ up_write(&cinode->lock_sem);
+ return rc;
+ }
+
+ if (!exist)
+ rc = 1;
+ else if (!wait)
+ rc = -EACCES;
+ else {
+ list_add_tail(&lock->blist, &conf_lock->blist);
+ up_write(&cinode->lock_sem);
+ rc = wait_event_interruptible(lock->block_q,
+ (lock->blist.prev == &lock->blist) &&
+ (lock->blist.next == &lock->blist));
+ if (!rc)
+ goto try_again;
+ cifs_down_write(&cinode->lock_sem);
+ list_del_init(&lock->blist);
+ }
+
+ up_write(&cinode->lock_sem);
+ return rc;
+}
+
+/*
+ * Check if there is another lock that prevents us to set the lock (posix
+ * style). If such a lock exists, update the flock structure with its
+ * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks
+ * or leave it the same if we can't. Returns 0 if we don't need to request to
+ * the server or 1 otherwise.
+ */
+static int
+cifs_posix_lock_test(struct file *file, struct file_lock *flock)
+{
+ int rc = 0;
+ struct cifsInodeInfo *cinode = CIFS_I(file_inode(file));
+ unsigned char saved_type = flock->fl_type;
+
+ if ((flock->fl_flags & FL_POSIX) == 0)
+ return 1;
+
+ down_read(&cinode->lock_sem);
+ posix_test_lock(file, flock);
+
+ if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) {
+ flock->fl_type = saved_type;
+ rc = 1;
+ }
+
+ up_read(&cinode->lock_sem);
+ return rc;
+}
+
+/*
+ * Set the byte-range lock (posix style). Returns:
+ * 1) 0, if we set the lock and don't need to request to the server;
+ * 2) 1, if we need to request to the server;
+ * 3) <0, if the error occurs while setting the lock.
+ */
+static int
+cifs_posix_lock_set(struct file *file, struct file_lock *flock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(file_inode(file));
+ int rc = 1;
+
+ if ((flock->fl_flags & FL_POSIX) == 0)
+ return rc;
+
+try_again:
+ cifs_down_write(&cinode->lock_sem);
+ if (!cinode->can_cache_brlcks) {
+ up_write(&cinode->lock_sem);
+ return rc;
+ }
+
+ rc = posix_lock_file(file, flock, NULL);
+ up_write(&cinode->lock_sem);
+ if (rc == FILE_LOCK_DEFERRED) {
+ rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next);
+ if (!rc)
+ goto try_again;
+ posix_unblock_lock(flock);
+ }
+ return rc;
+}
+
+int
+cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
+{
+ unsigned int xid;
+ int rc = 0, stored_rc;
+ struct cifsLockInfo *li, *tmp;
+ struct cifs_tcon *tcon;
+ unsigned int num, max_num, max_buf;
+ LOCKING_ANDX_RANGE *buf, *cur;
+ static const int types[] = {
+ LOCKING_ANDX_LARGE_FILES,
+ LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES
+ };
+ int i;
+
+ xid = get_xid();
+ tcon = tlink_tcon(cfile->tlink);
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it before using.
+ */
+ max_buf = tcon->ses->server->maxBuf;
+ if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) {
+ free_xid(xid);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) >
+ PAGE_SIZE);
+ max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr),
+ PAGE_SIZE);
+ max_num = (max_buf - sizeof(struct smb_hdr)) /
+ sizeof(LOCKING_ANDX_RANGE);
+ buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
+ if (!buf) {
+ free_xid(xid);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < 2; i++) {
+ cur = buf;
+ num = 0;
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
+ if (li->type != types[i])
+ continue;
+ cur->Pid = cpu_to_le16(li->pid);
+ cur->LengthLow = cpu_to_le32((u32)li->length);
+ cur->LengthHigh = cpu_to_le32((u32)(li->length>>32));
+ cur->OffsetLow = cpu_to_le32((u32)li->offset);
+ cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32));
+ if (++num == max_num) {
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->fid.netfid,
+ (__u8)li->type, 0, num,
+ buf);
+ if (stored_rc)
+ rc = stored_rc;
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+
+ if (num) {
+ stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid,
+ (__u8)types[i], 0, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+ }
+
+ kfree(buf);
+ free_xid(xid);
+ return rc;
+}
+
+static __u32
+hash_lockowner(fl_owner_t owner)
+{
+ return cifs_lock_secret ^ hash32_ptr((const void *)owner);
+}
+
+struct lock_to_push {
+ struct list_head llist;
+ __u64 offset;
+ __u64 length;
+ __u32 pid;
+ __u16 netfid;
+ __u8 type;
+};
+
+static int
+cifs_push_posix_locks(struct cifsFileInfo *cfile)
+{
+ struct inode *inode = d_inode(cfile->dentry);
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct file_lock *flock;
+ struct file_lock_context *flctx = inode->i_flctx;
+ unsigned int count = 0, i;
+ int rc = 0, xid, type;
+ struct list_head locks_to_send, *el;
+ struct lock_to_push *lck, *tmp;
+ __u64 length;
+
+ xid = get_xid();
+
+ if (!flctx)
+ goto out;
+
+ spin_lock(&flctx->flc_lock);
+ list_for_each(el, &flctx->flc_posix) {
+ count++;
+ }
+ spin_unlock(&flctx->flc_lock);
+
+ INIT_LIST_HEAD(&locks_to_send);
+
+ /*
+ * Allocating count locks is enough because no FL_POSIX locks can be
+ * added to the list while we are holding cinode->lock_sem that
+ * protects locking operations of this inode.
+ */
+ for (i = 0; i < count; i++) {
+ lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL);
+ if (!lck) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ list_add_tail(&lck->llist, &locks_to_send);
+ }
+
+ el = locks_to_send.next;
+ spin_lock(&flctx->flc_lock);
+ list_for_each_entry(flock, &flctx->flc_posix, fl_list) {
+ if (el == &locks_to_send) {
+ /*
+ * The list ended. We don't have enough allocated
+ * structures - something is really wrong.
+ */
+ cifs_dbg(VFS, "Can't push all brlocks!\n");
+ break;
+ }
+ length = 1 + flock->fl_end - flock->fl_start;
+ if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK)
+ type = CIFS_RDLCK;
+ else
+ type = CIFS_WRLCK;
+ lck = list_entry(el, struct lock_to_push, llist);
+ lck->pid = hash_lockowner(flock->fl_owner);
+ lck->netfid = cfile->fid.netfid;
+ lck->length = length;
+ lck->type = type;
+ lck->offset = flock->fl_start;
+ }
+ spin_unlock(&flctx->flc_lock);
+
+ list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) {
+ int stored_rc;
+
+ stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid,
+ lck->offset, lck->length, NULL,
+ lck->type, 0);
+ if (stored_rc)
+ rc = stored_rc;
+ list_del(&lck->llist);
+ kfree(lck);
+ }
+
+out:
+ free_xid(xid);
+ return rc;
+err_out:
+ list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) {
+ list_del(&lck->llist);
+ kfree(lck);
+ }
+ goto out;
+}
+
+static int
+cifs_push_locks(struct cifsFileInfo *cfile)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ int rc = 0;
+
+ /* we are going to update can_cache_brlcks here - need a write access */
+ cifs_down_write(&cinode->lock_sem);
+ if (!cinode->can_cache_brlcks) {
+ up_write(&cinode->lock_sem);
+ return rc;
+ }
+
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ rc = cifs_push_posix_locks(cfile);
+ else
+ rc = tcon->ses->server->ops->push_mand_locks(cfile);
+
+ cinode->can_cache_brlcks = false;
+ up_write(&cinode->lock_sem);
+ return rc;
+}
+
+static void
+cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock,
+ bool *wait_flag, struct TCP_Server_Info *server)
+{
+ if (flock->fl_flags & FL_POSIX)
+ cifs_dbg(FYI, "Posix\n");
+ if (flock->fl_flags & FL_FLOCK)
+ cifs_dbg(FYI, "Flock\n");
+ if (flock->fl_flags & FL_SLEEP) {
+ cifs_dbg(FYI, "Blocking lock\n");
+ *wait_flag = true;
+ }
+ if (flock->fl_flags & FL_ACCESS)
+ cifs_dbg(FYI, "Process suspended by mandatory locking - not implemented yet\n");
+ if (flock->fl_flags & FL_LEASE)
+ cifs_dbg(FYI, "Lease on file - not implemented yet\n");
+ if (flock->fl_flags &
+ (~(FL_POSIX | FL_FLOCK | FL_SLEEP |
+ FL_ACCESS | FL_LEASE | FL_CLOSE)))
+ cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags);
+
+ *type = server->vals->large_lock_type;
+ if (flock->fl_type == F_WRLCK) {
+ cifs_dbg(FYI, "F_WRLCK\n");
+ *type |= server->vals->exclusive_lock_type;
+ *lock = 1;
+ } else if (flock->fl_type == F_UNLCK) {
+ cifs_dbg(FYI, "F_UNLCK\n");
+ *type |= server->vals->unlock_lock_type;
+ *unlock = 1;
+ /* Check if unlock includes more than one lock range */
+ } else if (flock->fl_type == F_RDLCK) {
+ cifs_dbg(FYI, "F_RDLCK\n");
+ *type |= server->vals->shared_lock_type;
+ *lock = 1;
+ } else if (flock->fl_type == F_EXLCK) {
+ cifs_dbg(FYI, "F_EXLCK\n");
+ *type |= server->vals->exclusive_lock_type;
+ *lock = 1;
+ } else if (flock->fl_type == F_SHLCK) {
+ cifs_dbg(FYI, "F_SHLCK\n");
+ *type |= server->vals->shared_lock_type;
+ *lock = 1;
+ } else
+ cifs_dbg(FYI, "Unknown type of lock\n");
+}
+
+static int
+cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
+ bool wait_flag, bool posix_lck, unsigned int xid)
+{
+ int rc = 0;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ __u16 netfid = cfile->fid.netfid;
+
+ if (posix_lck) {
+ int posix_lock_type;
+
+ rc = cifs_posix_lock_test(file, flock);
+ if (!rc)
+ return rc;
+
+ if (type & server->vals->shared_lock_type)
+ posix_lock_type = CIFS_RDLCK;
+ else
+ posix_lock_type = CIFS_WRLCK;
+ rc = CIFSSMBPosixLock(xid, tcon, netfid,
+ hash_lockowner(flock->fl_owner),
+ flock->fl_start, length, flock,
+ posix_lock_type, wait_flag);
+ return rc;
+ }
+
+ rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock);
+ if (!rc)
+ return rc;
+
+ /* BB we could chain these into one lock request BB */
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type,
+ 1, 0, false);
+ if (rc == 0) {
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type, 0, 1, false);
+ flock->fl_type = F_UNLCK;
+ if (rc != 0)
+ cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n",
+ rc);
+ return 0;
+ }
+
+ if (type & server->vals->shared_lock_type) {
+ flock->fl_type = F_WRLCK;
+ return 0;
+ }
+
+ type &= ~server->vals->exclusive_lock_type;
+
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type | server->vals->shared_lock_type,
+ 1, 0, false);
+ if (rc == 0) {
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type | server->vals->shared_lock_type, 0, 1, false);
+ flock->fl_type = F_RDLCK;
+ if (rc != 0)
+ cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n",
+ rc);
+ } else
+ flock->fl_type = F_WRLCK;
+
+ return 0;
+}
+
+void
+cifs_move_llist(struct list_head *source, struct list_head *dest)
+{
+ struct list_head *li, *tmp;
+ list_for_each_safe(li, tmp, source)
+ list_move(li, dest);
+}
+
+void
+cifs_free_llist(struct list_head *llist)
+{
+ struct cifsLockInfo *li, *tmp;
+ list_for_each_entry_safe(li, tmp, llist, llist) {
+ cifs_del_lock_waiters(li);
+ list_del(&li->llist);
+ kfree(li);
+ }
+}
+
+int
+cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
+ unsigned int xid)
+{
+ int rc = 0, stored_rc;
+ static const int types[] = {
+ LOCKING_ANDX_LARGE_FILES,
+ LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES
+ };
+ unsigned int i;
+ unsigned int max_num, num, max_buf;
+ LOCKING_ANDX_RANGE *buf, *cur;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct cifsLockInfo *li, *tmp;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct list_head tmp_llist;
+
+ INIT_LIST_HEAD(&tmp_llist);
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it before using.
+ */
+ max_buf = tcon->ses->server->maxBuf;
+ if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE)))
+ return -EINVAL;
+
+ BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) >
+ PAGE_SIZE);
+ max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr),
+ PAGE_SIZE);
+ max_num = (max_buf - sizeof(struct smb_hdr)) /
+ sizeof(LOCKING_ANDX_RANGE);
+ buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ cifs_down_write(&cinode->lock_sem);
+ for (i = 0; i < 2; i++) {
+ cur = buf;
+ num = 0;
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
+ if (flock->fl_start > li->offset ||
+ (flock->fl_start + length) <
+ (li->offset + li->length))
+ continue;
+ if (current->tgid != li->pid)
+ continue;
+ if (types[i] != li->type)
+ continue;
+ if (cinode->can_cache_brlcks) {
+ /*
+ * We can cache brlock requests - simply remove
+ * a lock from the file's list.
+ */
+ list_del(&li->llist);
+ cifs_del_lock_waiters(li);
+ kfree(li);
+ continue;
+ }
+ cur->Pid = cpu_to_le16(li->pid);
+ cur->LengthLow = cpu_to_le32((u32)li->length);
+ cur->LengthHigh = cpu_to_le32((u32)(li->length>>32));
+ cur->OffsetLow = cpu_to_le32((u32)li->offset);
+ cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32));
+ /*
+ * We need to save a lock here to let us add it again to
+ * the file's list if the unlock range request fails on
+ * the server.
+ */
+ list_move(&li->llist, &tmp_llist);
+ if (++num == max_num) {
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->fid.netfid,
+ li->type, num, 0, buf);
+ if (stored_rc) {
+ /*
+ * We failed on the unlock range
+ * request - add all locks from the tmp
+ * list to the head of the file's list.
+ */
+ cifs_move_llist(&tmp_llist,
+ &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ /*
+ * The unlock range request succeed -
+ * free the tmp list.
+ */
+ cifs_free_llist(&tmp_llist);
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid,
+ types[i], num, 0, buf);
+ if (stored_rc) {
+ cifs_move_llist(&tmp_llist,
+ &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ cifs_free_llist(&tmp_llist);
+ }
+ }
+
+ up_write(&cinode->lock_sem);
+ kfree(buf);
+ return rc;
+}
+
+static int
+cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
+ bool wait_flag, bool posix_lck, int lock, int unlock,
+ unsigned int xid)
+{
+ int rc = 0;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct inode *inode = d_inode(cfile->dentry);
+
+ if (posix_lck) {
+ int posix_lock_type;
+
+ rc = cifs_posix_lock_set(file, flock);
+ if (!rc || rc < 0)
+ return rc;
+
+ if (type & server->vals->shared_lock_type)
+ posix_lock_type = CIFS_RDLCK;
+ else
+ posix_lock_type = CIFS_WRLCK;
+
+ if (unlock == 1)
+ posix_lock_type = CIFS_UNLCK;
+
+ rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid,
+ hash_lockowner(flock->fl_owner),
+ flock->fl_start, length,
+ NULL, posix_lock_type, wait_flag);
+ goto out;
+ }
+
+ if (lock) {
+ struct cifsLockInfo *lock;
+
+ lock = cifs_lock_init(flock->fl_start, length, type);
+ if (!lock)
+ return -ENOMEM;
+
+ rc = cifs_lock_add_if(cfile, lock, wait_flag);
+ if (rc < 0) {
+ kfree(lock);
+ return rc;
+ }
+ if (!rc)
+ goto out;
+
+ /*
+ * Windows 7 server can delay breaking lease from read to None
+ * if we set a byte-range lock on a file - break it explicitly
+ * before sending the lock to the server to be sure the next
+ * read won't conflict with non-overlapted locks due to
+ * pagereading.
+ */
+ if (!CIFS_CACHE_WRITE(CIFS_I(inode)) &&
+ CIFS_CACHE_READ(CIFS_I(inode))) {
+ cifs_zap_mapping(inode);
+ cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n",
+ inode);
+ CIFS_I(inode)->oplock = 0;
+ }
+
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type, 1, 0, wait_flag);
+ if (rc) {
+ kfree(lock);
+ return rc;
+ }
+
+ cifs_lock_add(cfile, lock);
+ } else if (unlock)
+ rc = server->ops->mand_unlock_range(cfile, flock, xid);
+
+out:
+ if (flock->fl_flags & FL_POSIX) {
+ /*
+ * If this is a request to remove all locks because we
+ * are closing the file, it doesn't matter if the
+ * unlocking failed as both cifs.ko and the SMB server
+ * remove the lock on file close
+ */
+ if (rc) {
+ cifs_dbg(VFS, "%s failed rc=%d\n", __func__, rc);
+ if (!(flock->fl_flags & FL_CLOSE))
+ return rc;
+ }
+ rc = locks_lock_file_wait(file, flock);
+ }
+ return rc;
+}
+
+int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
+{
+ int rc, xid;
+ int lock = 0, unlock = 0;
+ bool wait_flag = false;
+ bool posix_lck = false;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *cinode;
+ struct cifsFileInfo *cfile;
+ __u16 netfid;
+ __u32 type;
+
+ rc = -EACCES;
+ xid = get_xid();
+
+ cifs_dbg(FYI, "Lock parm: 0x%x flockflags: 0x%x flocktype: 0x%x start: %lld end: %lld\n",
+ cmd, flock->fl_flags, flock->fl_type,
+ flock->fl_start, flock->fl_end);
+
+ cfile = (struct cifsFileInfo *)file->private_data;
+ tcon = tlink_tcon(cfile->tlink);
+
+ cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag,
+ tcon->ses->server);
+
+ cifs_sb = CIFS_FILE_SB(file);
+ netfid = cfile->fid.netfid;
+ cinode = CIFS_I(file_inode(file));
+
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ posix_lck = true;
+ /*
+ * BB add code here to normalize offset and length to account for
+ * negative length which we can not accept over the wire.
+ */
+ if (IS_GETLK(cmd)) {
+ rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid);
+ free_xid(xid);
+ return rc;
+ }
+
+ if (!lock && !unlock) {
+ /*
+ * if no lock or unlock then nothing to do since we do not
+ * know what it is
+ */
+ free_xid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock,
+ xid);
+ free_xid(xid);
+ return rc;
+}
+
+/*
+ * update the file size (if needed) after a write. Should be called with
+ * the inode->i_lock held
+ */
+void
+cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
+ unsigned int bytes_written)
+{
+ loff_t end_of_write = offset + bytes_written;
+
+ if (end_of_write > cifsi->server_eof)
+ cifsi->server_eof = end_of_write;
+}
+
+static ssize_t
+cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
+ size_t write_size, loff_t *offset)
+{
+ int rc = 0;
+ unsigned int bytes_written = 0;
+ unsigned int total_written;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ unsigned int xid;
+ struct dentry *dentry = open_file->dentry;
+ struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry));
+ struct cifs_io_parms io_parms;
+
+ cifs_sb = CIFS_SB(dentry->d_sb);
+
+ cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n",
+ write_size, *offset, dentry);
+
+ tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->sync_write)
+ return -ENOSYS;
+
+ xid = get_xid();
+
+ for (total_written = 0; write_size > total_written;
+ total_written += bytes_written) {
+ rc = -EAGAIN;
+ while (rc == -EAGAIN) {
+ struct kvec iov[2];
+ unsigned int len;
+
+ if (open_file->invalidHandle) {
+ /* we could deadlock if we called
+ filemap_fdatawait from here so tell
+ reopen_file not to flush data to
+ server now */
+ rc = cifs_reopen_file(open_file, false);
+ if (rc != 0)
+ break;
+ }
+
+ len = min(server->ops->wp_retry_size(d_inode(dentry)),
+ (unsigned int)write_size - total_written);
+ /* iov[0] is reserved for smb header */
+ iov[1].iov_base = (char *)write_data + total_written;
+ iov[1].iov_len = len;
+ io_parms.pid = pid;
+ io_parms.tcon = tcon;
+ io_parms.offset = *offset;
+ io_parms.length = len;
+ rc = server->ops->sync_write(xid, &open_file->fid,
+ &io_parms, &bytes_written, iov, 1);
+ }
+ if (rc || (bytes_written == 0)) {
+ if (total_written)
+ break;
+ else {
+ free_xid(xid);
+ return rc;
+ }
+ } else {
+ spin_lock(&d_inode(dentry)->i_lock);
+ cifs_update_eof(cifsi, *offset, bytes_written);
+ spin_unlock(&d_inode(dentry)->i_lock);
+ *offset += bytes_written;
+ }
+ }
+
+ cifs_stats_bytes_written(tcon, total_written);
+
+ if (total_written > 0) {
+ spin_lock(&d_inode(dentry)->i_lock);
+ if (*offset > d_inode(dentry)->i_size)
+ i_size_write(d_inode(dentry), *offset);
+ spin_unlock(&d_inode(dentry)->i_lock);
+ }
+ mark_inode_dirty_sync(d_inode(dentry));
+ free_xid(xid);
+ return total_written;
+}
+
+struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
+ bool fsuid_only)
+{
+ struct cifsFileInfo *open_file = NULL;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+
+ /* only filter by fsuid on multiuser mounts */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
+ fsuid_only = false;
+
+ spin_lock(&cifs_inode->open_file_lock);
+ /* we could simply get the first_list_entry since write-only entries
+ are always at the end of the list but since the first entry might
+ have a close pending, we go through the whole list */
+ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+ if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
+ continue;
+ if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
+ if (!open_file->invalidHandle) {
+ /* found a good file */
+ /* lock it so it will not be closed on us */
+ cifsFileInfo_get(open_file);
+ spin_unlock(&cifs_inode->open_file_lock);
+ return open_file;
+ } /* else might as well continue, and look for
+ another, or simply have the caller reopen it
+ again rather than trying to fix this handle */
+ } else /* write only file */
+ break; /* write only files are last so must be done */
+ }
+ spin_unlock(&cifs_inode->open_file_lock);
+ return NULL;
+}
+
+struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
+ bool fsuid_only)
+{
+ struct cifsFileInfo *open_file, *inv_file = NULL;
+ struct cifs_sb_info *cifs_sb;
+ bool any_available = false;
+ int rc;
+ unsigned int refind = 0;
+
+ /* Having a null inode here (because mapping->host was set to zero by
+ the VFS or MM) should not happen but we had reports of on oops (due to
+ it being zero) during stress testcases so we need to check for it */
+
+ if (cifs_inode == NULL) {
+ cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n");
+ dump_stack();
+ return NULL;
+ }
+
+ cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+
+ /* only filter by fsuid on multiuser mounts */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
+ fsuid_only = false;
+
+ spin_lock(&cifs_inode->open_file_lock);
+refind_writable:
+ if (refind > MAX_REOPEN_ATT) {
+ spin_unlock(&cifs_inode->open_file_lock);
+ return NULL;
+ }
+ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+ if (!any_available && open_file->pid != current->tgid)
+ continue;
+ if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
+ continue;
+ if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
+ if (!open_file->invalidHandle) {
+ /* found a good writable file */
+ cifsFileInfo_get(open_file);
+ spin_unlock(&cifs_inode->open_file_lock);
+ return open_file;
+ } else {
+ if (!inv_file)
+ inv_file = open_file;
+ }
+ }
+ }
+ /* couldn't find useable FH with same pid, try any available */
+ if (!any_available) {
+ any_available = true;
+ goto refind_writable;
+ }
+
+ if (inv_file) {
+ any_available = false;
+ cifsFileInfo_get(inv_file);
+ }
+
+ spin_unlock(&cifs_inode->open_file_lock);
+
+ if (inv_file) {
+ rc = cifs_reopen_file(inv_file, false);
+ if (!rc)
+ return inv_file;
+ else {
+ spin_lock(&cifs_inode->open_file_lock);
+ list_move_tail(&inv_file->flist,
+ &cifs_inode->openFileList);
+ spin_unlock(&cifs_inode->open_file_lock);
+ cifsFileInfo_put(inv_file);
+ ++refind;
+ inv_file = NULL;
+ spin_lock(&cifs_inode->open_file_lock);
+ goto refind_writable;
+ }
+ }
+
+ return NULL;
+}
+
+static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
+{
+ struct address_space *mapping = page->mapping;
+ loff_t offset = (loff_t)page->index << PAGE_SHIFT;
+ char *write_data;
+ int rc = -EFAULT;
+ int bytes_written = 0;
+ struct inode *inode;
+ struct cifsFileInfo *open_file;
+
+ if (!mapping || !mapping->host)
+ return -EFAULT;
+
+ inode = page->mapping->host;
+
+ offset += (loff_t)from;
+ write_data = kmap(page);
+ write_data += from;
+
+ if ((to > PAGE_SIZE) || (from > to)) {
+ kunmap(page);
+ return -EIO;
+ }
+
+ /* racing with truncate? */
+ if (offset > mapping->host->i_size) {
+ kunmap(page);
+ return 0; /* don't care */
+ }
+
+ /* check to make sure that we are not extending the file */
+ if (mapping->host->i_size - offset < (loff_t)to)
+ to = (unsigned)(mapping->host->i_size - offset);
+
+ open_file = find_writable_file(CIFS_I(mapping->host), false);
+ if (open_file) {
+ bytes_written = cifs_write(open_file, open_file->pid,
+ write_data, to - from, &offset);
+ cifsFileInfo_put(open_file);
+ /* Does mm or vfs already set times? */
+ inode->i_atime = inode->i_mtime = current_time(inode);
+ if ((bytes_written > 0) && (offset))
+ rc = 0;
+ else if (bytes_written < 0)
+ rc = bytes_written;
+ } else {
+ cifs_dbg(FYI, "No writeable filehandles for inode\n");
+ rc = -EIO;
+ }
+
+ kunmap(page);
+ return rc;
+}
+
+static struct cifs_writedata *
+wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping,
+ pgoff_t end, pgoff_t *index,
+ unsigned int *found_pages)
+{
+ struct cifs_writedata *wdata;
+
+ wdata = cifs_writedata_alloc((unsigned int)tofind,
+ cifs_writev_complete);
+ if (!wdata)
+ return NULL;
+
+ *found_pages = find_get_pages_range_tag(mapping, index, end,
+ PAGECACHE_TAG_DIRTY, tofind, wdata->pages);
+ return wdata;
+}
+
+static unsigned int
+wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages,
+ struct address_space *mapping,
+ struct writeback_control *wbc,
+ pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done)
+{
+ unsigned int nr_pages = 0, i;
+ struct page *page;
+
+ for (i = 0; i < found_pages; i++) {
+ page = wdata->pages[i];
+ /*
+ * At this point we hold neither the i_pages lock nor the
+ * page lock: the page may be truncated or invalidated
+ * (changing page->mapping to NULL), or even swizzled
+ * back from swapper_space to tmpfs file mapping
+ */
+
+ if (nr_pages == 0)
+ lock_page(page);
+ else if (!trylock_page(page))
+ break;
+
+ if (unlikely(page->mapping != mapping)) {
+ unlock_page(page);
+ break;
+ }
+
+ if (!wbc->range_cyclic && page->index > end) {
+ *done = true;
+ unlock_page(page);
+ break;
+ }
+
+ if (*next && (page->index != *next)) {
+ /* Not next consecutive page */
+ unlock_page(page);
+ break;
+ }
+
+ if (wbc->sync_mode != WB_SYNC_NONE)
+ wait_on_page_writeback(page);
+
+ if (PageWriteback(page) ||
+ !clear_page_dirty_for_io(page)) {
+ unlock_page(page);
+ break;
+ }
+
+ /*
+ * This actually clears the dirty bit in the radix tree.
+ * See cifs_writepage() for more commentary.
+ */
+ set_page_writeback(page);
+ if (page_offset(page) >= i_size_read(mapping->host)) {
+ *done = true;
+ unlock_page(page);
+ end_page_writeback(page);
+ break;
+ }
+
+ wdata->pages[i] = page;
+ *next = page->index + 1;
+ ++nr_pages;
+ }
+
+ /* reset index to refind any pages skipped */
+ if (nr_pages == 0)
+ *index = wdata->pages[0]->index + 1;
+
+ /* put any pages we aren't going to use */
+ for (i = nr_pages; i < found_pages; i++) {
+ put_page(wdata->pages[i]);
+ wdata->pages[i] = NULL;
+ }
+
+ return nr_pages;
+}
+
+static int
+wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
+ struct address_space *mapping, struct writeback_control *wbc)
+{
+ int rc = 0;
+ struct TCP_Server_Info *server;
+ unsigned int i;
+
+ wdata->sync_mode = wbc->sync_mode;
+ wdata->nr_pages = nr_pages;
+ wdata->offset = page_offset(wdata->pages[0]);
+ wdata->pagesz = PAGE_SIZE;
+ wdata->tailsz = min(i_size_read(mapping->host) -
+ page_offset(wdata->pages[nr_pages - 1]),
+ (loff_t)PAGE_SIZE);
+ wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz;
+
+ if (wdata->cfile != NULL)
+ cifsFileInfo_put(wdata->cfile);
+ wdata->cfile = find_writable_file(CIFS_I(mapping->host), false);
+ if (!wdata->cfile) {
+ cifs_dbg(VFS, "No writable handles for inode\n");
+ rc = -EBADF;
+ } else {
+ wdata->pid = wdata->cfile->pid;
+ server = tlink_tcon(wdata->cfile->tlink)->ses->server;
+ rc = server->ops->async_writev(wdata, cifs_writedata_release);
+ }
+
+ for (i = 0; i < nr_pages; ++i)
+ unlock_page(wdata->pages[i]);
+
+ return rc;
+}
+
+static int cifs_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb);
+ struct TCP_Server_Info *server;
+ bool done = false, scanned = false, range_whole = false;
+ pgoff_t end, index;
+ struct cifs_writedata *wdata;
+ int rc = 0;
+ int saved_rc = 0;
+
+ /*
+ * If wsize is smaller than the page cache size, default to writing
+ * one page at a time via cifs_writepage
+ */
+ if (cifs_sb->wsize < PAGE_SIZE)
+ return generic_writepages(mapping, wbc);
+
+ if (wbc->range_cyclic) {
+ index = mapping->writeback_index; /* Start from prev offset */
+ end = -1;
+ } else {
+ index = wbc->range_start >> PAGE_SHIFT;
+ end = wbc->range_end >> PAGE_SHIFT;
+ if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
+ range_whole = true;
+ scanned = true;
+ }
+ server = cifs_sb_master_tcon(cifs_sb)->ses->server;
+retry:
+ while (!done && index <= end) {
+ unsigned int i, nr_pages, found_pages, wsize, credits;
+ pgoff_t next = 0, tofind, saved_index = index;
+
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
+ &wsize, &credits);
+ if (rc != 0) {
+ done = true;
+ break;
+ }
+
+ tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1;
+
+ wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index,
+ &found_pages);
+ if (!wdata) {
+ rc = -ENOMEM;
+ done = true;
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ if (found_pages == 0) {
+ kref_put(&wdata->refcount, cifs_writedata_release);
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc,
+ end, &index, &next, &done);
+
+ /* nothing to write? */
+ if (nr_pages == 0) {
+ kref_put(&wdata->refcount, cifs_writedata_release);
+ add_credits_and_wake_if(server, credits, 0);
+ continue;
+ }
+
+ wdata->credits = credits;
+
+ rc = wdata_send_pages(wdata, nr_pages, mapping, wbc);
+
+ /* send failure -- clean up the mess */
+ if (rc != 0) {
+ add_credits_and_wake_if(server, wdata->credits, 0);
+ for (i = 0; i < nr_pages; ++i) {
+ if (is_retryable_error(rc))
+ redirty_page_for_writepage(wbc,
+ wdata->pages[i]);
+ else
+ SetPageError(wdata->pages[i]);
+ end_page_writeback(wdata->pages[i]);
+ put_page(wdata->pages[i]);
+ }
+ if (!is_retryable_error(rc))
+ mapping_set_error(mapping, rc);
+ }
+ kref_put(&wdata->refcount, cifs_writedata_release);
+
+ if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) {
+ index = saved_index;
+ continue;
+ }
+
+ /* Return immediately if we received a signal during writing */
+ if (is_interrupt_error(rc)) {
+ done = true;
+ break;
+ }
+
+ if (rc != 0 && saved_rc == 0)
+ saved_rc = rc;
+
+ wbc->nr_to_write -= nr_pages;
+ if (wbc->nr_to_write <= 0)
+ done = true;
+
+ index = next;
+ }
+
+ if (!scanned && !done) {
+ /*
+ * We hit the last page and there is more work to be done: wrap
+ * back to the start of the file
+ */
+ scanned = true;
+ index = 0;
+ goto retry;
+ }
+
+ if (saved_rc != 0)
+ rc = saved_rc;
+
+ if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
+ mapping->writeback_index = index;
+
+ return rc;
+}
+
+static int
+cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
+{
+ int rc;
+ unsigned int xid;
+
+ xid = get_xid();
+/* BB add check for wbc flags */
+ get_page(page);
+ if (!PageUptodate(page))
+ cifs_dbg(FYI, "ppw - page not up to date\n");
+
+ /*
+ * Set the "writeback" flag, and clear "dirty" in the radix tree.
+ *
+ * A writepage() implementation always needs to do either this,
+ * or re-dirty the page with "redirty_page_for_writepage()" in
+ * the case of a failure.
+ *
+ * Just unlocking the page will cause the radix tree tag-bits
+ * to fail to update with the state of the page correctly.
+ */
+ set_page_writeback(page);
+retry_write:
+ rc = cifs_partialpagewrite(page, 0, PAGE_SIZE);
+ if (is_retryable_error(rc)) {
+ if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN)
+ goto retry_write;
+ redirty_page_for_writepage(wbc, page);
+ } else if (rc != 0) {
+ SetPageError(page);
+ mapping_set_error(page->mapping, rc);
+ } else {
+ SetPageUptodate(page);
+ }
+ end_page_writeback(page);
+ put_page(page);
+ free_xid(xid);
+ return rc;
+}
+
+static int cifs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ int rc = cifs_writepage_locked(page, wbc);
+ unlock_page(page);
+ return rc;
+}
+
+static int cifs_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct page *page, void *fsdata)
+{
+ int rc;
+ struct inode *inode = mapping->host;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb);
+ __u32 pid;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
+ pid = cfile->pid;
+ else
+ pid = current->tgid;
+
+ cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n",
+ page, pos, copied);
+
+ if (PageChecked(page)) {
+ if (copied == len)
+ SetPageUptodate(page);
+ ClearPageChecked(page);
+ } else if (!PageUptodate(page) && copied == PAGE_SIZE)
+ SetPageUptodate(page);
+
+ if (!PageUptodate(page)) {
+ char *page_data;
+ unsigned offset = pos & (PAGE_SIZE - 1);
+ unsigned int xid;
+
+ xid = get_xid();
+ /* this is probably better than directly calling
+ partialpage_write since in this function the file handle is
+ known which we might as well leverage */
+ /* BB check if anything else missing out of ppw
+ such as updating last write time */
+ page_data = kmap(page);
+ rc = cifs_write(cfile, pid, page_data + offset, copied, &pos);
+ /* if (rc < 0) should we set writebehind rc? */
+ kunmap(page);
+
+ free_xid(xid);
+ } else {
+ rc = copied;
+ pos += copied;
+ set_page_dirty(page);
+ }
+
+ if (rc > 0) {
+ spin_lock(&inode->i_lock);
+ if (pos > inode->i_size)
+ i_size_write(inode, pos);
+ spin_unlock(&inode->i_lock);
+ }
+
+ unlock_page(page);
+ put_page(page);
+
+ return rc;
+}
+
+int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
+ int datasync)
+{
+ unsigned int xid;
+ int rc = 0;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifsFileInfo *smbfile = file->private_data;
+ struct inode *inode = file_inode(file);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ rc = file_write_and_wait_range(file, start, end);
+ if (rc)
+ return rc;
+ inode_lock(inode);
+
+ xid = get_xid();
+
+ cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n",
+ file, datasync);
+
+ if (!CIFS_CACHE_READ(CIFS_I(inode))) {
+ rc = cifs_zap_mapping(inode);
+ if (rc) {
+ cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc);
+ rc = 0; /* don't care about it in fsync */
+ }
+ }
+
+ tcon = tlink_tcon(smbfile->tlink);
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
+ server = tcon->ses->server;
+ if (server->ops->flush)
+ rc = server->ops->flush(xid, tcon, &smbfile->fid);
+ else
+ rc = -ENOSYS;
+ }
+
+ free_xid(xid);
+ inode_unlock(inode);
+ return rc;
+}
+
+int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ unsigned int xid;
+ int rc = 0;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifsFileInfo *smbfile = file->private_data;
+ struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
+ struct inode *inode = file->f_mapping->host;
+
+ rc = file_write_and_wait_range(file, start, end);
+ if (rc)
+ return rc;
+ inode_lock(inode);
+
+ xid = get_xid();
+
+ cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n",
+ file, datasync);
+
+ tcon = tlink_tcon(smbfile->tlink);
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
+ server = tcon->ses->server;
+ if (server->ops->flush)
+ rc = server->ops->flush(xid, tcon, &smbfile->fid);
+ else
+ rc = -ENOSYS;
+ }
+
+ free_xid(xid);
+ inode_unlock(inode);
+ return rc;
+}
+
+/*
+ * As file closes, flush all cached write data for this inode checking
+ * for write behind errors.
+ */
+int cifs_flush(struct file *file, fl_owner_t id)
+{
+ struct inode *inode = file_inode(file);
+ int rc = 0;
+
+ if (file->f_mode & FMODE_WRITE)
+ rc = filemap_write_and_wait(inode->i_mapping);
+
+ cifs_dbg(FYI, "Flush inode %p file %p rc %d\n", inode, file, rc);
+
+ return rc;
+}
+
+static int
+cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
+{
+ int rc = 0;
+ unsigned long i;
+
+ for (i = 0; i < num_pages; i++) {
+ pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
+ if (!pages[i]) {
+ /*
+ * save number of pages we have already allocated and
+ * return with ENOMEM error
+ */
+ num_pages = i;
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ if (rc) {
+ for (i = 0; i < num_pages; i++)
+ put_page(pages[i]);
+ }
+ return rc;
+}
+
+static inline
+size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
+{
+ size_t num_pages;
+ size_t clen;
+
+ clen = min_t(const size_t, len, wsize);
+ num_pages = DIV_ROUND_UP(clen, PAGE_SIZE);
+
+ if (cur_len)
+ *cur_len = clen;
+
+ return num_pages;
+}
+
+static void
+cifs_uncached_writedata_release(struct kref *refcount)
+{
+ int i;
+ struct cifs_writedata *wdata = container_of(refcount,
+ struct cifs_writedata, refcount);
+
+ kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release);
+ for (i = 0; i < wdata->nr_pages; i++)
+ put_page(wdata->pages[i]);
+ cifs_writedata_release(refcount);
+}
+
+static void collect_uncached_write_data(struct cifs_aio_ctx *ctx);
+
+static void
+cifs_uncached_writev_complete(struct work_struct *work)
+{
+ struct cifs_writedata *wdata = container_of(work,
+ struct cifs_writedata, work);
+ struct inode *inode = d_inode(wdata->cfile->dentry);
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+
+ spin_lock(&inode->i_lock);
+ cifs_update_eof(cifsi, wdata->offset, wdata->bytes);
+ if (cifsi->server_eof > inode->i_size)
+ i_size_write(inode, cifsi->server_eof);
+ spin_unlock(&inode->i_lock);
+
+ complete(&wdata->done);
+ collect_uncached_write_data(wdata->ctx);
+ /* the below call can possibly free the last ref to aio ctx */
+ kref_put(&wdata->refcount, cifs_uncached_writedata_release);
+}
+
+static int
+wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
+ size_t *len, unsigned long *num_pages)
+{
+ size_t save_len, copied, bytes, cur_len = *len;
+ unsigned long i, nr_pages = *num_pages;
+
+ save_len = cur_len;
+ for (i = 0; i < nr_pages; i++) {
+ bytes = min_t(const size_t, cur_len, PAGE_SIZE);
+ copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from);
+ cur_len -= copied;
+ /*
+ * If we didn't copy as much as we expected, then that
+ * may mean we trod into an unmapped area. Stop copying
+ * at that point. On the next pass through the big
+ * loop, we'll likely end up getting a zero-length
+ * write and bailing out of it.
+ */
+ if (copied < bytes)
+ break;
+ }
+ cur_len = save_len - cur_len;
+ *len = cur_len;
+
+ /*
+ * If we have no data to send, then that probably means that
+ * the copy above failed altogether. That's most likely because
+ * the address in the iovec was bogus. Return -EFAULT and let
+ * the caller free anything we allocated and bail out.
+ */
+ if (!cur_len)
+ return -EFAULT;
+
+ /*
+ * i + 1 now represents the number of pages we actually used in
+ * the copy phase above.
+ */
+ *num_pages = i + 1;
+ return 0;
+}
+
+static int
+cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
+ struct cifsFileInfo *open_file,
+ struct cifs_sb_info *cifs_sb, struct list_head *wdata_list,
+ struct cifs_aio_ctx *ctx)
+{
+ int rc = 0;
+ size_t cur_len;
+ unsigned long nr_pages, num_pages, i;
+ struct cifs_writedata *wdata;
+ struct iov_iter saved_from = *from;
+ loff_t saved_offset = offset;
+ pid_t pid;
+ struct TCP_Server_Info *server;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
+ pid = open_file->pid;
+ else
+ pid = current->tgid;
+
+ server = tlink_tcon(open_file->tlink)->ses->server;
+
+ do {
+ unsigned int wsize, credits;
+
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
+ &wsize, &credits);
+ if (rc)
+ break;
+
+ nr_pages = get_numpages(wsize, len, &cur_len);
+ wdata = cifs_writedata_alloc(nr_pages,
+ cifs_uncached_writev_complete);
+ if (!wdata) {
+ rc = -ENOMEM;
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
+ if (rc) {
+ kfree(wdata);
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ num_pages = nr_pages;
+ rc = wdata_fill_from_iovec(wdata, from, &cur_len, &num_pages);
+ if (rc) {
+ for (i = 0; i < nr_pages; i++)
+ put_page(wdata->pages[i]);
+ kfree(wdata);
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ /*
+ * Bring nr_pages down to the number of pages we actually used,
+ * and free any pages that we didn't use.
+ */
+ for ( ; nr_pages > num_pages; nr_pages--)
+ put_page(wdata->pages[nr_pages - 1]);
+
+ wdata->sync_mode = WB_SYNC_ALL;
+ wdata->nr_pages = nr_pages;
+ wdata->offset = (__u64)offset;
+ wdata->cfile = cifsFileInfo_get(open_file);
+ wdata->pid = pid;
+ wdata->bytes = cur_len;
+ wdata->pagesz = PAGE_SIZE;
+ wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
+ wdata->credits = credits;
+ wdata->ctx = ctx;
+ kref_get(&ctx->refcount);
+
+ if (!wdata->cfile->invalidHandle ||
+ !(rc = cifs_reopen_file(wdata->cfile, false)))
+ rc = server->ops->async_writev(wdata,
+ cifs_uncached_writedata_release);
+ if (rc) {
+ add_credits_and_wake_if(server, wdata->credits, 0);
+ kref_put(&wdata->refcount,
+ cifs_uncached_writedata_release);
+ if (rc == -EAGAIN) {
+ *from = saved_from;
+ iov_iter_advance(from, offset - saved_offset);
+ continue;
+ }
+ break;
+ }
+
+ list_add_tail(&wdata->list, wdata_list);
+ offset += cur_len;
+ len -= cur_len;
+ } while (len > 0);
+
+ return rc;
+}
+
+static void collect_uncached_write_data(struct cifs_aio_ctx *ctx)
+{
+ struct cifs_writedata *wdata, *tmp;
+ struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb;
+ struct dentry *dentry = ctx->cfile->dentry;
+ unsigned int i;
+ int rc;
+
+ tcon = tlink_tcon(ctx->cfile->tlink);
+ cifs_sb = CIFS_SB(dentry->d_sb);
+
+ mutex_lock(&ctx->aio_mutex);
+
+ if (list_empty(&ctx->list)) {
+ mutex_unlock(&ctx->aio_mutex);
+ return;
+ }
+
+ rc = ctx->rc;
+ /*
+ * Wait for and collect replies for any successful sends in order of
+ * increasing offset. Once an error is hit, then return without waiting
+ * for any more replies.
+ */
+restart_loop:
+ list_for_each_entry_safe(wdata, tmp, &ctx->list, list) {
+ if (!rc) {
+ if (!try_wait_for_completion(&wdata->done)) {
+ mutex_unlock(&ctx->aio_mutex);
+ return;
+ }
+
+ if (wdata->result)
+ rc = wdata->result;
+ else
+ ctx->total_len += wdata->bytes;
+
+ /* resend call if it's a retryable error */
+ if (rc == -EAGAIN) {
+ struct list_head tmp_list;
+ struct iov_iter tmp_from = ctx->iter;
+
+ INIT_LIST_HEAD(&tmp_list);
+ list_del_init(&wdata->list);
+
+ iov_iter_advance(&tmp_from,
+ wdata->offset - ctx->pos);
+
+ rc = cifs_write_from_iter(wdata->offset,
+ wdata->bytes, &tmp_from,
+ ctx->cfile, cifs_sb, &tmp_list,
+ ctx);
+
+ list_splice(&tmp_list, &ctx->list);
+
+ kref_put(&wdata->refcount,
+ cifs_uncached_writedata_release);
+ goto restart_loop;
+ }
+ }
+ list_del_init(&wdata->list);
+ kref_put(&wdata->refcount, cifs_uncached_writedata_release);
+ }
+
+ for (i = 0; i < ctx->npages; i++)
+ put_page(ctx->bv[i].bv_page);
+
+ cifs_stats_bytes_written(tcon, ctx->total_len);
+ set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags);
+
+ ctx->rc = (rc == 0) ? ctx->total_len : rc;
+
+ mutex_unlock(&ctx->aio_mutex);
+
+ if (ctx->iocb && ctx->iocb->ki_complete)
+ ctx->iocb->ki_complete(ctx->iocb, ctx->rc, 0);
+ else
+ complete(&ctx->done);
+}
+
+ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct file *file = iocb->ki_filp;
+ ssize_t total_written = 0;
+ struct cifsFileInfo *cfile;
+ struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_aio_ctx *ctx;
+ struct iov_iter saved_from = *from;
+ int rc;
+
+ /*
+ * BB - optimize the way when signing is disabled. We can drop this
+ * extra memory-to-memory copying and use iovec buffers for constructing
+ * write request.
+ */
+
+ rc = generic_write_checks(iocb, from);
+ if (rc <= 0)
+ return rc;
+
+ cifs_sb = CIFS_FILE_SB(file);
+ cfile = file->private_data;
+ tcon = tlink_tcon(cfile->tlink);
+
+ if (!tcon->ses->server->ops->async_writev)
+ return -ENOSYS;
+
+ ctx = cifs_aio_ctx_alloc();
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->cfile = cifsFileInfo_get(cfile);
+
+ if (!is_sync_kiocb(iocb))
+ ctx->iocb = iocb;
+
+ ctx->pos = iocb->ki_pos;
+
+ rc = setup_aio_ctx_iter(ctx, from, WRITE);
+ if (rc) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return rc;
+ }
+
+ /* grab a lock here due to read response handlers can access ctx */
+ mutex_lock(&ctx->aio_mutex);
+
+ rc = cifs_write_from_iter(iocb->ki_pos, ctx->len, &saved_from,
+ cfile, cifs_sb, &ctx->list, ctx);
+
+ /*
+ * If at least one write was successfully sent, then discard any rc
+ * value from the later writes. If the other write succeeds, then
+ * we'll end up returning whatever was written. If it fails, then
+ * we'll get a new rc value from that.
+ */
+ if (!list_empty(&ctx->list))
+ rc = 0;
+
+ mutex_unlock(&ctx->aio_mutex);
+
+ if (rc) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return rc;
+ }
+
+ if (!is_sync_kiocb(iocb)) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return -EIOCBQUEUED;
+ }
+
+ rc = wait_for_completion_killable(&ctx->done);
+ if (rc) {
+ mutex_lock(&ctx->aio_mutex);
+ ctx->rc = rc = -EINTR;
+ total_written = ctx->total_len;
+ mutex_unlock(&ctx->aio_mutex);
+ } else {
+ rc = ctx->rc;
+ total_written = ctx->total_len;
+ }
+
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+
+ if (unlikely(!total_written))
+ return rc;
+
+ iocb->ki_pos += total_written;
+ return total_written;
+}
+
+static ssize_t
+cifs_writev(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct file *file = iocb->ki_filp;
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
+ struct inode *inode = file->f_mapping->host;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
+ ssize_t rc;
+
+ inode_lock(inode);
+ /*
+ * We need to hold the sem to be sure nobody modifies lock list
+ * with a brlock that prevents writing.
+ */
+ down_read(&cinode->lock_sem);
+
+ rc = generic_write_checks(iocb, from);
+ if (rc <= 0)
+ goto out;
+
+ if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from),
+ server->vals->exclusive_lock_type, NULL,
+ CIFS_WRITE_OP))
+ rc = __generic_file_write_iter(iocb, from);
+ else
+ rc = -EACCES;
+out:
+ up_read(&cinode->lock_sem);
+ inode_unlock(inode);
+
+ if (rc > 0)
+ rc = generic_write_sync(iocb, rc);
+ return rc;
+}
+
+ssize_t
+cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)
+ iocb->ki_filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ ssize_t written;
+
+ written = cifs_get_writer(cinode);
+ if (written)
+ return written;
+
+ if (CIFS_CACHE_WRITE(cinode)) {
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))
+ && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) {
+ written = generic_file_write_iter(iocb, from);
+ goto out;
+ }
+ written = cifs_writev(iocb, from);
+ goto out;
+ }
+ /*
+ * For non-oplocked files in strict cache mode we need to write the data
+ * to the server exactly from the pos to pos+len-1 rather than flush all
+ * affected pages because it may cause a error with mandatory locks on
+ * these pages but not on the region from pos to ppos+len-1.
+ */
+ written = cifs_user_writev(iocb, from);
+ if (CIFS_CACHE_READ(cinode)) {
+ /*
+ * We have read level caching and we have just sent a write
+ * request to the server thus making data in the cache stale.
+ * Zap the cache and set oplock/lease level to NONE to avoid
+ * reading stale data from the cache. All subsequent read
+ * operations will read new data from the server.
+ */
+ cifs_zap_mapping(inode);
+ cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n",
+ inode);
+ cinode->oplock = 0;
+ }
+out:
+ cifs_put_writer(cinode);
+ return written;
+}
+
+static struct cifs_readdata *
+cifs_readdata_direct_alloc(struct page **pages, work_func_t complete)
+{
+ struct cifs_readdata *rdata;
+
+ rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
+ if (rdata != NULL) {
+ rdata->pages = pages;
+ kref_init(&rdata->refcount);
+ INIT_LIST_HEAD(&rdata->list);
+ init_completion(&rdata->done);
+ INIT_WORK(&rdata->work, complete);
+ }
+
+ return rdata;
+}
+
+static struct cifs_readdata *
+cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete)
+{
+ struct page **pages =
+ kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
+ struct cifs_readdata *ret = NULL;
+
+ if (pages) {
+ ret = cifs_readdata_direct_alloc(pages, complete);
+ if (!ret)
+ kfree(pages);
+ }
+
+ return ret;
+}
+
+void
+cifs_readdata_release(struct kref *refcount)
+{
+ struct cifs_readdata *rdata = container_of(refcount,
+ struct cifs_readdata, refcount);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ if (rdata->mr) {
+ smbd_deregister_mr(rdata->mr);
+ rdata->mr = NULL;
+ }
+#endif
+ if (rdata->cfile)
+ cifsFileInfo_put(rdata->cfile);
+
+ kvfree(rdata->pages);
+ kfree(rdata);
+}
+
+static int
+cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages)
+{
+ int rc = 0;
+ struct page *page;
+ unsigned int i;
+
+ for (i = 0; i < nr_pages; i++) {
+ page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
+ if (!page) {
+ rc = -ENOMEM;
+ break;
+ }
+ rdata->pages[i] = page;
+ }
+
+ if (rc) {
+ unsigned int nr_page_failed = i;
+
+ for (i = 0; i < nr_page_failed; i++) {
+ put_page(rdata->pages[i]);
+ rdata->pages[i] = NULL;
+ }
+ }
+ return rc;
+}
+
+static void
+cifs_uncached_readdata_release(struct kref *refcount)
+{
+ struct cifs_readdata *rdata = container_of(refcount,
+ struct cifs_readdata, refcount);
+ unsigned int i;
+
+ kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release);
+ for (i = 0; i < rdata->nr_pages; i++) {
+ put_page(rdata->pages[i]);
+ rdata->pages[i] = NULL;
+ }
+ cifs_readdata_release(refcount);
+}
+
+/**
+ * cifs_readdata_to_iov - copy data from pages in response to an iovec
+ * @rdata: the readdata response with list of pages holding data
+ * @iter: destination for our data
+ *
+ * This function copies data from a list of pages in a readdata response into
+ * an array of iovecs. It will first calculate where the data should go
+ * based on the info in the readdata and then copy the data into that spot.
+ */
+static int
+cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
+{
+ size_t remaining = rdata->got_bytes;
+ unsigned int i;
+
+ for (i = 0; i < rdata->nr_pages; i++) {
+ struct page *page = rdata->pages[i];
+ size_t copy = min_t(size_t, remaining, PAGE_SIZE);
+ size_t written;
+
+ if (unlikely(iter->type & ITER_PIPE)) {
+ void *addr = kmap_atomic(page);
+
+ written = copy_to_iter(addr, copy, iter);
+ kunmap_atomic(addr);
+ } else
+ written = copy_page_to_iter(page, 0, copy, iter);
+ remaining -= written;
+ if (written < copy && iov_iter_count(iter) > 0)
+ break;
+ }
+ return remaining ? -EFAULT : 0;
+}
+
+static void collect_uncached_read_data(struct cifs_aio_ctx *ctx);
+
+static void
+cifs_uncached_readv_complete(struct work_struct *work)
+{
+ struct cifs_readdata *rdata = container_of(work,
+ struct cifs_readdata, work);
+
+ complete(&rdata->done);
+ collect_uncached_read_data(rdata->ctx);
+ /* the below call can possibly free the last ref to aio ctx */
+ kref_put(&rdata->refcount, cifs_uncached_readdata_release);
+}
+
+static int
+uncached_fill_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata, struct iov_iter *iter,
+ unsigned int len)
+{
+ int result = 0;
+ unsigned int i;
+ unsigned int nr_pages = rdata->nr_pages;
+ unsigned int page_offset = rdata->page_offset;
+
+ rdata->got_bytes = 0;
+ rdata->tailsz = PAGE_SIZE;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = rdata->pages[i];
+ size_t n;
+ unsigned int segment_size = rdata->pagesz;
+
+ if (i == 0)
+ segment_size -= page_offset;
+ else
+ page_offset = 0;
+
+
+ if (len <= 0) {
+ /* no need to hold page hostage */
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
+ put_page(page);
+ continue;
+ }
+
+ n = len;
+ if (len >= segment_size)
+ /* enough data to fill the page */
+ n = segment_size;
+ else
+ rdata->tailsz = len;
+ len -= n;
+
+ if (iter)
+ result = copy_page_from_iter(
+ page, page_offset, n, iter);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ else if (rdata->mr)
+ result = n;
+#endif
+ else
+ result = cifs_read_page_from_socket(
+ server, page, page_offset, n);
+ if (result < 0)
+ break;
+
+ rdata->got_bytes += result;
+ }
+
+ return rdata->got_bytes > 0 && result != -ECONNABORTED ?
+ rdata->got_bytes : result;
+}
+
+static int
+cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata, unsigned int len)
+{
+ return uncached_fill_pages(server, rdata, NULL, len);
+}
+
+static int
+cifs_uncached_copy_into_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ struct iov_iter *iter)
+{
+ return uncached_fill_pages(server, rdata, iter, iter->count);
+}
+
+static int
+cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
+ struct cifs_sb_info *cifs_sb, struct list_head *rdata_list,
+ struct cifs_aio_ctx *ctx)
+{
+ struct cifs_readdata *rdata;
+ unsigned int npages, rsize, credits;
+ size_t cur_len;
+ int rc;
+ pid_t pid;
+ struct TCP_Server_Info *server;
+
+ server = tlink_tcon(open_file->tlink)->ses->server;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
+ pid = open_file->pid;
+ else
+ pid = current->tgid;
+
+ do {
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
+ &rsize, &credits);
+ if (rc)
+ break;
+
+ cur_len = min_t(const size_t, len, rsize);
+ npages = DIV_ROUND_UP(cur_len, PAGE_SIZE);
+
+ /* allocate a readdata struct */
+ rdata = cifs_readdata_alloc(npages,
+ cifs_uncached_readv_complete);
+ if (!rdata) {
+ add_credits_and_wake_if(server, credits, 0);
+ rc = -ENOMEM;
+ break;
+ }
+
+ rc = cifs_read_allocate_pages(rdata, npages);
+ if (rc)
+ goto error;
+
+ rdata->cfile = cifsFileInfo_get(open_file);
+ rdata->nr_pages = npages;
+ rdata->offset = offset;
+ rdata->bytes = cur_len;
+ rdata->pid = pid;
+ rdata->pagesz = PAGE_SIZE;
+ rdata->tailsz = PAGE_SIZE;
+ rdata->read_into_pages = cifs_uncached_read_into_pages;
+ rdata->copy_into_pages = cifs_uncached_copy_into_pages;
+ rdata->credits = credits;
+ rdata->ctx = ctx;
+ kref_get(&ctx->refcount);
+
+ if (!rdata->cfile->invalidHandle ||
+ !(rc = cifs_reopen_file(rdata->cfile, true)))
+ rc = server->ops->async_readv(rdata);
+error:
+ if (rc) {
+ add_credits_and_wake_if(server, rdata->credits, 0);
+ kref_put(&rdata->refcount,
+ cifs_uncached_readdata_release);
+ if (rc == -EAGAIN)
+ continue;
+ break;
+ }
+
+ list_add_tail(&rdata->list, rdata_list);
+ offset += cur_len;
+ len -= cur_len;
+ } while (len > 0);
+
+ return rc;
+}
+
+static void
+collect_uncached_read_data(struct cifs_aio_ctx *ctx)
+{
+ struct cifs_readdata *rdata, *tmp;
+ struct iov_iter *to = &ctx->iter;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ unsigned int i;
+ int rc;
+
+ tcon = tlink_tcon(ctx->cfile->tlink);
+ cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb);
+
+ mutex_lock(&ctx->aio_mutex);
+
+ if (list_empty(&ctx->list)) {
+ mutex_unlock(&ctx->aio_mutex);
+ return;
+ }
+
+ rc = ctx->rc;
+ /* the loop below should proceed in the order of increasing offsets */
+again:
+ list_for_each_entry_safe(rdata, tmp, &ctx->list, list) {
+ if (!rc) {
+ if (!try_wait_for_completion(&rdata->done)) {
+ mutex_unlock(&ctx->aio_mutex);
+ return;
+ }
+
+ if (rdata->result == -EAGAIN) {
+ /* resend call if it's a retryable error */
+ struct list_head tmp_list;
+ unsigned int got_bytes = rdata->got_bytes;
+
+ list_del_init(&rdata->list);
+ INIT_LIST_HEAD(&tmp_list);
+
+ /*
+ * Got a part of data and then reconnect has
+ * happened -- fill the buffer and continue
+ * reading.
+ */
+ if (got_bytes && got_bytes < rdata->bytes) {
+ rc = cifs_readdata_to_iov(rdata, to);
+ if (rc) {
+ kref_put(&rdata->refcount,
+ cifs_uncached_readdata_release);
+ continue;
+ }
+ }
+
+ rc = cifs_send_async_read(
+ rdata->offset + got_bytes,
+ rdata->bytes - got_bytes,
+ rdata->cfile, cifs_sb,
+ &tmp_list, ctx);
+
+ list_splice(&tmp_list, &ctx->list);
+
+ kref_put(&rdata->refcount,
+ cifs_uncached_readdata_release);
+ goto again;
+ } else if (rdata->result)
+ rc = rdata->result;
+ else
+ rc = cifs_readdata_to_iov(rdata, to);
+
+ /* if there was a short read -- discard anything left */
+ if (rdata->got_bytes && rdata->got_bytes < rdata->bytes)
+ rc = -ENODATA;
+ }
+ list_del_init(&rdata->list);
+ kref_put(&rdata->refcount, cifs_uncached_readdata_release);
+ }
+
+ for (i = 0; i < ctx->npages; i++) {
+ if (ctx->should_dirty)
+ set_page_dirty(ctx->bv[i].bv_page);
+ put_page(ctx->bv[i].bv_page);
+ }
+
+ ctx->total_len = ctx->len - iov_iter_count(to);
+
+ cifs_stats_bytes_read(tcon, ctx->total_len);
+
+ /* mask nodata case */
+ if (rc == -ENODATA)
+ rc = 0;
+
+ ctx->rc = (rc == 0) ? (ssize_t)ctx->total_len : rc;
+
+ mutex_unlock(&ctx->aio_mutex);
+
+ if (ctx->iocb && ctx->iocb->ki_complete)
+ ctx->iocb->ki_complete(ctx->iocb, ctx->rc, 0);
+ else
+ complete(&ctx->done);
+}
+
+ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct file *file = iocb->ki_filp;
+ ssize_t rc;
+ size_t len;
+ ssize_t total_read = 0;
+ loff_t offset = iocb->ki_pos;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ struct cifsFileInfo *cfile;
+ struct cifs_aio_ctx *ctx;
+
+ len = iov_iter_count(to);
+ if (!len)
+ return 0;
+
+ cifs_sb = CIFS_FILE_SB(file);
+ cfile = file->private_data;
+ tcon = tlink_tcon(cfile->tlink);
+
+ if (!tcon->ses->server->ops->async_readv)
+ return -ENOSYS;
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+ cifs_dbg(FYI, "attempting read on write only file instance\n");
+
+ ctx = cifs_aio_ctx_alloc();
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->cfile = cifsFileInfo_get(cfile);
+
+ if (!is_sync_kiocb(iocb))
+ ctx->iocb = iocb;
+
+ if (to->type == ITER_IOVEC)
+ ctx->should_dirty = true;
+
+ rc = setup_aio_ctx_iter(ctx, to, READ);
+ if (rc) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return rc;
+ }
+
+ len = ctx->len;
+
+ /* grab a lock here due to read response handlers can access ctx */
+ mutex_lock(&ctx->aio_mutex);
+
+ rc = cifs_send_async_read(offset, len, cfile, cifs_sb, &ctx->list, ctx);
+
+ /* if at least one read request send succeeded, then reset rc */
+ if (!list_empty(&ctx->list))
+ rc = 0;
+
+ mutex_unlock(&ctx->aio_mutex);
+
+ if (rc) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return rc;
+ }
+
+ if (!is_sync_kiocb(iocb)) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return -EIOCBQUEUED;
+ }
+
+ rc = wait_for_completion_killable(&ctx->done);
+ if (rc) {
+ mutex_lock(&ctx->aio_mutex);
+ ctx->rc = rc = -EINTR;
+ total_read = ctx->total_len;
+ mutex_unlock(&ctx->aio_mutex);
+ } else {
+ rc = ctx->rc;
+ total_read = ctx->total_len;
+ }
+
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+
+ if (total_read) {
+ iocb->ki_pos += total_read;
+ return total_read;
+ }
+ return rc;
+}
+
+ssize_t
+cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct inode *inode = file_inode(iocb->ki_filp);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)
+ iocb->ki_filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ int rc = -EACCES;
+
+ /*
+ * In strict cache mode we need to read from the server all the time
+ * if we don't have level II oplock because the server can delay mtime
+ * change - so we can't make a decision about inode invalidating.
+ * And we can also fail with pagereading if there are mandatory locks
+ * on pages affected by this read but not on the region from pos to
+ * pos+len-1.
+ */
+ if (!CIFS_CACHE_READ(cinode))
+ return cifs_user_readv(iocb, to);
+
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ return generic_file_read_iter(iocb, to);
+
+ /*
+ * We need to hold the sem to be sure nobody modifies lock list
+ * with a brlock that prevents reading.
+ */
+ down_read(&cinode->lock_sem);
+ if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to),
+ tcon->ses->server->vals->shared_lock_type,
+ NULL, CIFS_READ_OP))
+ rc = generic_file_read_iter(iocb, to);
+ up_read(&cinode->lock_sem);
+ return rc;
+}
+
+static ssize_t
+cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
+{
+ int rc = -EACCES;
+ unsigned int bytes_read = 0;
+ unsigned int total_read;
+ unsigned int current_read_size;
+ unsigned int rsize;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ unsigned int xid;
+ char *cur_offset;
+ struct cifsFileInfo *open_file;
+ struct cifs_io_parms io_parms;
+ int buf_type = CIFS_NO_BUFFER;
+ __u32 pid;
+
+ xid = get_xid();
+ cifs_sb = CIFS_FILE_SB(file);
+
+ /* FIXME: set up handlers for larger reads and/or convert to async */
+ rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize);
+
+ if (file->private_data == NULL) {
+ rc = -EBADF;
+ free_xid(xid);
+ return rc;
+ }
+ open_file = file->private_data;
+ tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->sync_read) {
+ free_xid(xid);
+ return -ENOSYS;
+ }
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
+ pid = open_file->pid;
+ else
+ pid = current->tgid;
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+ cifs_dbg(FYI, "attempting read on write only file instance\n");
+
+ for (total_read = 0, cur_offset = read_data; read_size > total_read;
+ total_read += bytes_read, cur_offset += bytes_read) {
+ do {
+ current_read_size = min_t(uint, read_size - total_read,
+ rsize);
+ /*
+ * For windows me and 9x we do not want to request more
+ * than it negotiated since it will refuse the read
+ * then.
+ */
+ if (!(tcon->ses->capabilities &
+ tcon->ses->server->vals->cap_large_files)) {
+ current_read_size = min_t(uint,
+ current_read_size, CIFSMaxBufSize);
+ }
+ if (open_file->invalidHandle) {
+ rc = cifs_reopen_file(open_file, true);
+ if (rc != 0)
+ break;
+ }
+ io_parms.pid = pid;
+ io_parms.tcon = tcon;
+ io_parms.offset = *offset;
+ io_parms.length = current_read_size;
+ rc = server->ops->sync_read(xid, &open_file->fid, &io_parms,
+ &bytes_read, &cur_offset,
+ &buf_type);
+ } while (rc == -EAGAIN);
+
+ if (rc || (bytes_read == 0)) {
+ if (total_read) {
+ break;
+ } else {
+ free_xid(xid);
+ return rc;
+ }
+ } else {
+ cifs_stats_bytes_read(tcon, total_read);
+ *offset += bytes_read;
+ }
+ }
+ free_xid(xid);
+ return total_read;
+}
+
+/*
+ * If the page is mmap'ed into a process' page tables, then we need to make
+ * sure that it doesn't change while being written back.
+ */
+static vm_fault_t
+cifs_page_mkwrite(struct vm_fault *vmf)
+{
+ struct page *page = vmf->page;
+
+ lock_page(page);
+ return VM_FAULT_LOCKED;
+}
+
+static const struct vm_operations_struct cifs_file_vm_ops = {
+ .fault = filemap_fault,
+ .map_pages = filemap_map_pages,
+ .page_mkwrite = cifs_page_mkwrite,
+};
+
+int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int xid, rc = 0;
+ struct inode *inode = file_inode(file);
+
+ xid = get_xid();
+
+ if (!CIFS_CACHE_READ(CIFS_I(inode)))
+ rc = cifs_zap_mapping(inode);
+ if (!rc)
+ rc = generic_file_mmap(file, vma);
+ if (!rc)
+ vma->vm_ops = &cifs_file_vm_ops;
+
+ free_xid(xid);
+ return rc;
+}
+
+int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int rc, xid;
+
+ xid = get_xid();
+
+ rc = cifs_revalidate_file(file);
+ if (rc)
+ cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n",
+ rc);
+ if (!rc)
+ rc = generic_file_mmap(file, vma);
+ if (!rc)
+ vma->vm_ops = &cifs_file_vm_ops;
+
+ free_xid(xid);
+ return rc;
+}
+
+static void
+cifs_readv_complete(struct work_struct *work)
+{
+ unsigned int i, got_bytes;
+ struct cifs_readdata *rdata = container_of(work,
+ struct cifs_readdata, work);
+
+ got_bytes = rdata->got_bytes;
+ for (i = 0; i < rdata->nr_pages; i++) {
+ struct page *page = rdata->pages[i];
+
+ lru_cache_add_file(page);
+
+ if (rdata->result == 0 ||
+ (rdata->result == -EAGAIN && got_bytes)) {
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ }
+
+ unlock_page(page);
+
+ if (rdata->result == 0 ||
+ (rdata->result == -EAGAIN && got_bytes))
+ cifs_readpage_to_fscache(rdata->mapping->host, page);
+
+ got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes);
+
+ put_page(page);
+ rdata->pages[i] = NULL;
+ }
+ kref_put(&rdata->refcount, cifs_readdata_release);
+}
+
+static int
+readpages_fill_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata, struct iov_iter *iter,
+ unsigned int len)
+{
+ int result = 0;
+ unsigned int i;
+ u64 eof;
+ pgoff_t eof_index;
+ unsigned int nr_pages = rdata->nr_pages;
+ unsigned int page_offset = rdata->page_offset;
+
+ /* determine the eof that the server (probably) has */
+ eof = CIFS_I(rdata->mapping->host)->server_eof;
+ eof_index = eof ? (eof - 1) >> PAGE_SHIFT : 0;
+ cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index);
+
+ rdata->got_bytes = 0;
+ rdata->tailsz = PAGE_SIZE;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = rdata->pages[i];
+ unsigned int to_read = rdata->pagesz;
+ size_t n;
+
+ if (i == 0)
+ to_read -= page_offset;
+ else
+ page_offset = 0;
+
+ n = to_read;
+
+ if (len >= to_read) {
+ len -= to_read;
+ } else if (len > 0) {
+ /* enough for partial page, fill and zero the rest */
+ zero_user(page, len + page_offset, to_read - len);
+ n = rdata->tailsz = len;
+ len = 0;
+ } else if (page->index > eof_index) {
+ /*
+ * The VFS will not try to do readahead past the
+ * i_size, but it's possible that we have outstanding
+ * writes with gaps in the middle and the i_size hasn't
+ * caught up yet. Populate those with zeroed out pages
+ * to prevent the VFS from repeatedly attempting to
+ * fill them until the writes are flushed.
+ */
+ zero_user(page, 0, PAGE_SIZE);
+ lru_cache_add_file(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+ put_page(page);
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
+ continue;
+ } else {
+ /* no need to hold page hostage */
+ lru_cache_add_file(page);
+ unlock_page(page);
+ put_page(page);
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
+ continue;
+ }
+
+ if (iter)
+ result = copy_page_from_iter(
+ page, page_offset, n, iter);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ else if (rdata->mr)
+ result = n;
+#endif
+ else
+ result = cifs_read_page_from_socket(
+ server, page, page_offset, n);
+ if (result < 0)
+ break;
+
+ rdata->got_bytes += result;
+ }
+
+ return rdata->got_bytes > 0 && result != -ECONNABORTED ?
+ rdata->got_bytes : result;
+}
+
+static int
+cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata, unsigned int len)
+{
+ return readpages_fill_pages(server, rdata, NULL, len);
+}
+
+static int
+cifs_readpages_copy_into_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ struct iov_iter *iter)
+{
+ return readpages_fill_pages(server, rdata, iter, iter->count);
+}
+
+static int
+readpages_get_pages(struct address_space *mapping, struct list_head *page_list,
+ unsigned int rsize, struct list_head *tmplist,
+ unsigned int *nr_pages, loff_t *offset, unsigned int *bytes)
+{
+ struct page *page, *tpage;
+ unsigned int expected_index;
+ int rc;
+ gfp_t gfp = readahead_gfp_mask(mapping);
+
+ INIT_LIST_HEAD(tmplist);
+
+ page = list_entry(page_list->prev, struct page, lru);
+
+ /*
+ * Lock the page and put it in the cache. Since no one else
+ * should have access to this page, we're safe to simply set
+ * PG_locked without checking it first.
+ */
+ __SetPageLocked(page);
+ rc = add_to_page_cache_locked(page, mapping,
+ page->index, gfp);
+
+ /* give up if we can't stick it in the cache */
+ if (rc) {
+ __ClearPageLocked(page);
+ return rc;
+ }
+
+ /* move first page to the tmplist */
+ *offset = (loff_t)page->index << PAGE_SHIFT;
+ *bytes = PAGE_SIZE;
+ *nr_pages = 1;
+ list_move_tail(&page->lru, tmplist);
+
+ /* now try and add more pages onto the request */
+ expected_index = page->index + 1;
+ list_for_each_entry_safe_reverse(page, tpage, page_list, lru) {
+ /* discontinuity ? */
+ if (page->index != expected_index)
+ break;
+
+ /* would this page push the read over the rsize? */
+ if (*bytes + PAGE_SIZE > rsize)
+ break;
+
+ __SetPageLocked(page);
+ rc = add_to_page_cache_locked(page, mapping, page->index, gfp);
+ if (rc) {
+ __ClearPageLocked(page);
+ break;
+ }
+ list_move_tail(&page->lru, tmplist);
+ (*bytes) += PAGE_SIZE;
+ expected_index++;
+ (*nr_pages)++;
+ }
+ return rc;
+}
+
+static int cifs_readpages(struct file *file, struct address_space *mapping,
+ struct list_head *page_list, unsigned num_pages)
+{
+ int rc;
+ int err = 0;
+ struct list_head tmplist;
+ struct cifsFileInfo *open_file = file->private_data;
+ struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
+ struct TCP_Server_Info *server;
+ pid_t pid;
+
+ /*
+ * Reads as many pages as possible from fscache. Returns -ENOBUFS
+ * immediately if the cookie is negative
+ *
+ * After this point, every page in the list might have PG_fscache set,
+ * so we will need to clean that up off of every page we don't use.
+ */
+ rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list,
+ &num_pages);
+ if (rc == 0)
+ return rc;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
+ pid = open_file->pid;
+ else
+ pid = current->tgid;
+
+ rc = 0;
+ server = tlink_tcon(open_file->tlink)->ses->server;
+
+ cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n",
+ __func__, file, mapping, num_pages);
+
+ /*
+ * Start with the page at end of list and move it to private
+ * list. Do the same with any following pages until we hit
+ * the rsize limit, hit an index discontinuity, or run out of
+ * pages. Issue the async read and then start the loop again
+ * until the list is empty.
+ *
+ * Note that list order is important. The page_list is in
+ * the order of declining indexes. When we put the pages in
+ * the rdata->pages, then we want them in increasing order.
+ */
+ while (!list_empty(page_list) && !err) {
+ unsigned int i, nr_pages, bytes, rsize;
+ loff_t offset;
+ struct page *page, *tpage;
+ struct cifs_readdata *rdata;
+ unsigned credits;
+
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
+ &rsize, &credits);
+ if (rc)
+ break;
+
+ /*
+ * Give up immediately if rsize is too small to read an entire
+ * page. The VFS will fall back to readpage. We should never
+ * reach this point however since we set ra_pages to 0 when the
+ * rsize is smaller than a cache page.
+ */
+ if (unlikely(rsize < PAGE_SIZE)) {
+ add_credits_and_wake_if(server, credits, 0);
+ return 0;
+ }
+
+ nr_pages = 0;
+ err = readpages_get_pages(mapping, page_list, rsize, &tmplist,
+ &nr_pages, &offset, &bytes);
+ if (!nr_pages) {
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete);
+ if (!rdata) {
+ /* best to give up if we're out of mem */
+ list_for_each_entry_safe(page, tpage, &tmplist, lru) {
+ list_del(&page->lru);
+ lru_cache_add_file(page);
+ unlock_page(page);
+ put_page(page);
+ }
+ rc = -ENOMEM;
+ add_credits_and_wake_if(server, credits, 0);
+ break;
+ }
+
+ rdata->cfile = cifsFileInfo_get(open_file);
+ rdata->mapping = mapping;
+ rdata->offset = offset;
+ rdata->bytes = bytes;
+ rdata->pid = pid;
+ rdata->pagesz = PAGE_SIZE;
+ rdata->tailsz = PAGE_SIZE;
+ rdata->read_into_pages = cifs_readpages_read_into_pages;
+ rdata->copy_into_pages = cifs_readpages_copy_into_pages;
+ rdata->credits = credits;
+
+ list_for_each_entry_safe(page, tpage, &tmplist, lru) {
+ list_del(&page->lru);
+ rdata->pages[rdata->nr_pages++] = page;
+ }
+
+ if (!rdata->cfile->invalidHandle ||
+ !(rc = cifs_reopen_file(rdata->cfile, true)))
+ rc = server->ops->async_readv(rdata);
+ if (rc) {
+ add_credits_and_wake_if(server, rdata->credits, 0);
+ for (i = 0; i < rdata->nr_pages; i++) {
+ page = rdata->pages[i];
+ lru_cache_add_file(page);
+ unlock_page(page);
+ put_page(page);
+ }
+ /* Fallback to the readpage in error/reconnect cases */
+ kref_put(&rdata->refcount, cifs_readdata_release);
+ break;
+ }
+
+ kref_put(&rdata->refcount, cifs_readdata_release);
+ }
+
+ /* Any pages that have been shown to fscache but didn't get added to
+ * the pagecache must be uncached before they get returned to the
+ * allocator.
+ */
+ cifs_fscache_readpages_cancel(mapping->host, page_list);
+ return rc;
+}
+
+/*
+ * cifs_readpage_worker must be called with the page pinned
+ */
+static int cifs_readpage_worker(struct file *file, struct page *page,
+ loff_t *poffset)
+{
+ char *read_data;
+ int rc;
+
+ /* Is the page cached? */
+ rc = cifs_readpage_from_fscache(file_inode(file), page);
+ if (rc == 0)
+ goto read_complete;
+
+ read_data = kmap(page);
+ /* for reads over a certain size could initiate async read ahead */
+
+ rc = cifs_read(file, read_data, PAGE_SIZE, poffset);
+
+ if (rc < 0)
+ goto io_error;
+ else
+ cifs_dbg(FYI, "Bytes read %d\n", rc);
+
+ file_inode(file)->i_atime =
+ current_time(file_inode(file));
+
+ if (PAGE_SIZE > rc)
+ memset(read_data + rc, 0, PAGE_SIZE - rc);
+
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+
+ /* send this page to the cache */
+ cifs_readpage_to_fscache(file_inode(file), page);
+
+ rc = 0;
+
+io_error:
+ kunmap(page);
+ unlock_page(page);
+
+read_complete:
+ return rc;
+}
+
+static int cifs_readpage(struct file *file, struct page *page)
+{
+ loff_t offset = (loff_t)page->index << PAGE_SHIFT;
+ int rc = -EACCES;
+ unsigned int xid;
+
+ xid = get_xid();
+
+ if (file->private_data == NULL) {
+ rc = -EBADF;
+ free_xid(xid);
+ return rc;
+ }
+
+ cifs_dbg(FYI, "readpage %p at offset %d 0x%x\n",
+ page, (int)offset, (int)offset);
+
+ rc = cifs_readpage_worker(file, page, &offset);
+
+ free_xid(xid);
+ return rc;
+}
+
+static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
+{
+ struct cifsFileInfo *open_file;
+
+ spin_lock(&cifs_inode->open_file_lock);
+ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+ if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
+ spin_unlock(&cifs_inode->open_file_lock);
+ return 1;
+ }
+ }
+ spin_unlock(&cifs_inode->open_file_lock);
+ return 0;
+}
+
+/* We do not want to update the file size from server for inodes
+ open for write - to avoid races with writepage extending
+ the file - in the future we could consider allowing
+ refreshing the inode only on increases in the file size
+ but this is tricky to do without racing with writebehind
+ page caching in the current Linux kernel design */
+bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
+{
+ if (!cifsInode)
+ return true;
+
+ if (is_inode_writable(cifsInode)) {
+ /* This inode is open for write at least once */
+ struct cifs_sb_info *cifs_sb;
+
+ cifs_sb = CIFS_SB(cifsInode->vfs_inode.i_sb);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+ /* since no page cache to corrupt on directio
+ we can change size safely */
+ return true;
+ }
+
+ if (i_size_read(&cifsInode->vfs_inode) < end_of_file)
+ return true;
+
+ return false;
+ } else
+ return true;
+}
+
+static int cifs_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned flags,
+ struct page **pagep, void **fsdata)
+{
+ int oncethru = 0;
+ pgoff_t index = pos >> PAGE_SHIFT;
+ loff_t offset = pos & (PAGE_SIZE - 1);
+ loff_t page_start = pos & PAGE_MASK;
+ loff_t i_size;
+ struct page *page;
+ int rc = 0;
+
+ cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len);
+
+start:
+ page = grab_cache_page_write_begin(mapping, index, flags);
+ if (!page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (PageUptodate(page))
+ goto out;
+
+ /*
+ * If we write a full page it will be up to date, no need to read from
+ * the server. If the write is short, we'll end up doing a sync write
+ * instead.
+ */
+ if (len == PAGE_SIZE)
+ goto out;
+
+ /*
+ * optimize away the read when we have an oplock, and we're not
+ * expecting to use any of the data we'd be reading in. That
+ * is, when the page lies beyond the EOF, or straddles the EOF
+ * and the write will cover all of the existing data.
+ */
+ if (CIFS_CACHE_READ(CIFS_I(mapping->host))) {
+ i_size = i_size_read(mapping->host);
+ if (page_start >= i_size ||
+ (offset == 0 && (pos + len) >= i_size)) {
+ zero_user_segments(page, 0, offset,
+ offset + len,
+ PAGE_SIZE);
+ /*
+ * PageChecked means that the parts of the page
+ * to which we're not writing are considered up
+ * to date. Once the data is copied to the
+ * page, it can be set uptodate.
+ */
+ SetPageChecked(page);
+ goto out;
+ }
+ }
+
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) {
+ /*
+ * might as well read a page, it is fast enough. If we get
+ * an error, we don't need to return it. cifs_write_end will
+ * do a sync write instead since PG_uptodate isn't set.
+ */
+ cifs_readpage_worker(file, page, &page_start);
+ put_page(page);
+ oncethru = 1;
+ goto start;
+ } else {
+ /* we could try using another file handle if there is one -
+ but how would we lock it to prevent close of that handle
+ racing with this read? In any case
+ this will be written out by write_end so is fine */
+ }
+out:
+ *pagep = page;
+ return rc;
+}
+
+static int cifs_release_page(struct page *page, gfp_t gfp)
+{
+ if (PagePrivate(page))
+ return 0;
+
+ return cifs_fscache_release_page(page, gfp);
+}
+
+static void cifs_invalidate_page(struct page *page, unsigned int offset,
+ unsigned int length)
+{
+ struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host);
+
+ if (offset == 0 && length == PAGE_SIZE)
+ cifs_fscache_invalidate_page(page, &cifsi->vfs_inode);
+}
+
+static int cifs_launder_page(struct page *page)
+{
+ int rc = 0;
+ loff_t range_start = page_offset(page);
+ loff_t range_end = range_start + (loff_t)(PAGE_SIZE - 1);
+ struct writeback_control wbc = {
+ .sync_mode = WB_SYNC_ALL,
+ .nr_to_write = 0,
+ .range_start = range_start,
+ .range_end = range_end,
+ };
+
+ cifs_dbg(FYI, "Launder page: %p\n", page);
+
+ if (clear_page_dirty_for_io(page))
+ rc = cifs_writepage_locked(page, &wbc);
+
+ cifs_fscache_invalidate_page(page, page->mapping->host);
+ return rc;
+}
+
+void cifs_oplock_break(struct work_struct *work)
+{
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
+ oplock_break);
+ struct inode *inode = d_inode(cfile->dentry);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ int rc = 0;
+ bool purge_cache = false;
+
+ wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
+ TASK_UNINTERRUPTIBLE);
+
+ server->ops->downgrade_oplock(server, cinode, cfile->oplock_level,
+ cfile->oplock_epoch, &purge_cache);
+
+ if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&
+ cifs_has_mand_locks(cinode)) {
+ cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n",
+ inode);
+ cinode->oplock = 0;
+ }
+
+ if (inode && S_ISREG(inode->i_mode)) {
+ if (CIFS_CACHE_READ(cinode))
+ break_lease(inode, O_RDONLY);
+ else
+ break_lease(inode, O_WRONLY);
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (!CIFS_CACHE_READ(cinode) || purge_cache) {
+ rc = filemap_fdatawait(inode->i_mapping);
+ mapping_set_error(inode->i_mapping, rc);
+ cifs_zap_mapping(inode);
+ }
+ cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);
+ if (CIFS_CACHE_WRITE(cinode))
+ goto oplock_break_ack;
+ }
+
+ rc = cifs_push_locks(cfile);
+ if (rc)
+ cifs_dbg(VFS, "Push locks rc = %d\n", rc);
+
+oplock_break_ack:
+ /*
+ * releasing stale oplock after recent reconnect of smb session using
+ * a now incorrect file handle is not a data integrity issue but do
+ * not bother sending an oplock release if session to server still is
+ * disconnected since oplock already released by the server
+ */
+ if (!cfile->oplock_break_cancelled) {
+ rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
+ cinode);
+ cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
+ }
+ _cifsFileInfo_put(cfile, false /* do not wait for ourself */);
+ cifs_done_oplock_break(cinode);
+}
+
+/*
+ * The presence of cifs_direct_io() in the address space ops vector
+ * allowes open() O_DIRECT flags which would have failed otherwise.
+ *
+ * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests
+ * so this method should never be called.
+ *
+ * Direct IO is not yet supported in the cached mode.
+ */
+static ssize_t
+cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter)
+{
+ /*
+ * FIXME
+ * Eventually need to support direct IO for non forcedirectio mounts
+ */
+ return -EINVAL;
+}
+
+
+const struct address_space_operations cifs_addr_ops = {
+ .readpage = cifs_readpage,
+ .readpages = cifs_readpages,
+ .writepage = cifs_writepage,
+ .writepages = cifs_writepages,
+ .write_begin = cifs_write_begin,
+ .write_end = cifs_write_end,
+ .set_page_dirty = __set_page_dirty_nobuffers,
+ .releasepage = cifs_release_page,
+ .direct_IO = cifs_direct_io,
+ .invalidatepage = cifs_invalidate_page,
+ .launder_page = cifs_launder_page,
+};
+
+/*
+ * cifs_readpages requires the server to support a buffer large enough to
+ * contain the header plus one complete page of data. Otherwise, we need
+ * to leave cifs_readpages out of the address space operations.
+ */
+const struct address_space_operations cifs_addr_ops_smallbuf = {
+ .readpage = cifs_readpage,
+ .writepage = cifs_writepage,
+ .writepages = cifs_writepages,
+ .write_begin = cifs_write_begin,
+ .write_end = cifs_write_end,
+ .set_page_dirty = __set_page_dirty_nobuffers,
+ .releasepage = cifs_release_page,
+ .invalidatepage = cifs_invalidate_page,
+ .launder_page = cifs_launder_page,
+};
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
new file mode 100644
index 000000000..ea6ace9c2
--- /dev/null
+++ b/fs/cifs/fscache.c
@@ -0,0 +1,334 @@
+/*
+ * fs/cifs/fscache.c - CIFS filesystem cache interface
+ *
+ * Copyright (c) 2010 Novell, Inc.
+ * Author(s): Suresh Jayaraman <sjayaraman@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "fscache.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+
+/*
+ * Key layout of CIFS server cache index object
+ */
+struct cifs_server_key {
+ struct {
+ uint16_t family; /* address family */
+ __be16 port; /* IP port */
+ } hdr;
+ union {
+ struct in_addr ipv4_addr;
+ struct in6_addr ipv6_addr;
+ };
+} __packed;
+
+/*
+ * Get a cookie for a server object keyed by {IPaddress,port,family} tuple
+ */
+void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server)
+{
+ const struct sockaddr *sa = (struct sockaddr *) &server->dstaddr;
+ const struct sockaddr_in *addr = (struct sockaddr_in *) sa;
+ const struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) sa;
+ struct cifs_server_key key;
+ uint16_t key_len = sizeof(key.hdr);
+
+ memset(&key, 0, sizeof(key));
+
+ /*
+ * Should not be a problem as sin_family/sin6_family overlays
+ * sa_family field
+ */
+ key.hdr.family = sa->sa_family;
+ switch (sa->sa_family) {
+ case AF_INET:
+ key.hdr.port = addr->sin_port;
+ key.ipv4_addr = addr->sin_addr;
+ key_len += sizeof(key.ipv4_addr);
+ break;
+
+ case AF_INET6:
+ key.hdr.port = addr6->sin6_port;
+ key.ipv6_addr = addr6->sin6_addr;
+ key_len += sizeof(key.ipv6_addr);
+ break;
+
+ default:
+ cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);
+ server->fscache = NULL;
+ return;
+ }
+
+ server->fscache =
+ fscache_acquire_cookie(cifs_fscache_netfs.primary_index,
+ &cifs_fscache_server_index_def,
+ &key, key_len,
+ NULL, 0,
+ server, 0, true);
+ cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
+ __func__, server, server->fscache);
+}
+
+void cifs_fscache_release_client_cookie(struct TCP_Server_Info *server)
+{
+ cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
+ __func__, server, server->fscache);
+ fscache_relinquish_cookie(server->fscache, NULL, false);
+ server->fscache = NULL;
+}
+
+void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
+{
+ struct TCP_Server_Info *server = tcon->ses->server;
+ char *sharename;
+
+ sharename = extract_sharename(tcon->treeName);
+ if (IS_ERR(sharename)) {
+ cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
+ tcon->fscache = NULL;
+ return;
+ }
+
+ tcon->fscache =
+ fscache_acquire_cookie(server->fscache,
+ &cifs_fscache_super_index_def,
+ sharename, strlen(sharename),
+ &tcon->resource_id, sizeof(tcon->resource_id),
+ tcon, 0, true);
+ kfree(sharename);
+ cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
+ __func__, server->fscache, tcon->fscache);
+}
+
+void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
+{
+ cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache);
+ fscache_relinquish_cookie(tcon->fscache, &tcon->resource_id, false);
+ tcon->fscache = NULL;
+}
+
+static void cifs_fscache_acquire_inode_cookie(struct cifsInodeInfo *cifsi,
+ struct cifs_tcon *tcon)
+{
+ struct cifs_fscache_inode_auxdata auxdata;
+
+ memset(&auxdata, 0, sizeof(auxdata));
+ auxdata.eof = cifsi->server_eof;
+ auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
+ auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
+ auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
+ auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
+
+ cifsi->fscache =
+ fscache_acquire_cookie(tcon->fscache,
+ &cifs_fscache_inode_object_def,
+ &cifsi->uniqueid, sizeof(cifsi->uniqueid),
+ &auxdata, sizeof(auxdata),
+ cifsi, cifsi->vfs_inode.i_size, true);
+}
+
+static void cifs_fscache_enable_inode_cookie(struct inode *inode)
+{
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+
+ if (cifsi->fscache)
+ return;
+
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE))
+ return;
+
+ cifs_fscache_acquire_inode_cookie(cifsi, tcon);
+
+ cifs_dbg(FYI, "%s: got FH cookie (0x%p/0x%p)\n",
+ __func__, tcon->fscache, cifsi->fscache);
+}
+
+void cifs_fscache_release_inode_cookie(struct inode *inode)
+{
+ struct cifs_fscache_inode_auxdata auxdata;
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+
+ if (cifsi->fscache) {
+ memset(&auxdata, 0, sizeof(auxdata));
+ auxdata.eof = cifsi->server_eof;
+ auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
+ auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
+ auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
+ auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
+
+ cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
+ fscache_relinquish_cookie(cifsi->fscache, &auxdata, false);
+ cifsi->fscache = NULL;
+ }
+}
+
+static void cifs_fscache_disable_inode_cookie(struct inode *inode)
+{
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+
+ if (cifsi->fscache) {
+ cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
+ fscache_uncache_all_inode_pages(cifsi->fscache, inode);
+ fscache_relinquish_cookie(cifsi->fscache, NULL, true);
+ cifsi->fscache = NULL;
+ }
+}
+
+void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp)
+{
+ if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
+ cifs_fscache_disable_inode_cookie(inode);
+ else
+ cifs_fscache_enable_inode_cookie(inode);
+}
+
+void cifs_fscache_reset_inode_cookie(struct inode *inode)
+{
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct fscache_cookie *old = cifsi->fscache;
+
+ if (cifsi->fscache) {
+ /* retire the current fscache cache and get a new one */
+ fscache_relinquish_cookie(cifsi->fscache, NULL, true);
+
+ cifs_fscache_acquire_inode_cookie(cifsi, tcon);
+ cifs_dbg(FYI, "%s: new cookie 0x%p oldcookie 0x%p\n",
+ __func__, cifsi->fscache, old);
+ }
+}
+
+int cifs_fscache_release_page(struct page *page, gfp_t gfp)
+{
+ if (PageFsCache(page)) {
+ struct inode *inode = page->mapping->host;
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+
+ cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",
+ __func__, page, cifsi->fscache);
+ if (!fscache_maybe_release_page(cifsi->fscache, page, gfp))
+ return 0;
+ }
+
+ return 1;
+}
+
+static void cifs_readpage_from_fscache_complete(struct page *page, void *ctx,
+ int error)
+{
+ cifs_dbg(FYI, "%s: (0x%p/%d)\n", __func__, page, error);
+ if (!error)
+ SetPageUptodate(page);
+ unlock_page(page);
+}
+
+/*
+ * Retrieve a page from FS-Cache
+ */
+int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)
+{
+ int ret;
+
+ cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n",
+ __func__, CIFS_I(inode)->fscache, page, inode);
+ ret = fscache_read_or_alloc_page(CIFS_I(inode)->fscache, page,
+ cifs_readpage_from_fscache_complete,
+ NULL,
+ GFP_KERNEL);
+ switch (ret) {
+
+ case 0: /* page found in fscache, read submitted */
+ cifs_dbg(FYI, "%s: submitted\n", __func__);
+ return ret;
+ case -ENOBUFS: /* page won't be cached */
+ case -ENODATA: /* page not in cache */
+ cifs_dbg(FYI, "%s: %d\n", __func__, ret);
+ return 1;
+
+ default:
+ cifs_dbg(VFS, "unknown error ret = %d\n", ret);
+ }
+ return ret;
+}
+
+/*
+ * Retrieve a set of pages from FS-Cache
+ */
+int __cifs_readpages_from_fscache(struct inode *inode,
+ struct address_space *mapping,
+ struct list_head *pages,
+ unsigned *nr_pages)
+{
+ int ret;
+
+ cifs_dbg(FYI, "%s: (0x%p/%u/0x%p)\n",
+ __func__, CIFS_I(inode)->fscache, *nr_pages, inode);
+ ret = fscache_read_or_alloc_pages(CIFS_I(inode)->fscache, mapping,
+ pages, nr_pages,
+ cifs_readpage_from_fscache_complete,
+ NULL,
+ mapping_gfp_mask(mapping));
+ switch (ret) {
+ case 0: /* read submitted to the cache for all pages */
+ cifs_dbg(FYI, "%s: submitted\n", __func__);
+ return ret;
+
+ case -ENOBUFS: /* some pages are not cached and can't be */
+ case -ENODATA: /* some pages are not cached */
+ cifs_dbg(FYI, "%s: no page\n", __func__);
+ return 1;
+
+ default:
+ cifs_dbg(FYI, "unknown error ret = %d\n", ret);
+ }
+
+ return ret;
+}
+
+void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)
+{
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+ int ret;
+
+ cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n",
+ __func__, cifsi->fscache, page, inode);
+ ret = fscache_write_page(cifsi->fscache, page,
+ cifsi->vfs_inode.i_size, GFP_KERNEL);
+ if (ret != 0)
+ fscache_uncache_page(cifsi->fscache, page);
+}
+
+void __cifs_fscache_readpages_cancel(struct inode *inode, struct list_head *pages)
+{
+ cifs_dbg(FYI, "%s: (fsc: %p, i: %p)\n",
+ __func__, CIFS_I(inode)->fscache, inode);
+ fscache_readpages_cancel(CIFS_I(inode)->fscache, pages);
+}
+
+void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode)
+{
+ struct cifsInodeInfo *cifsi = CIFS_I(inode);
+ struct fscache_cookie *cookie = cifsi->fscache;
+
+ cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie);
+ fscache_wait_on_page_write(cookie, page);
+ fscache_uncache_page(cookie, page);
+}
diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h
new file mode 100644
index 000000000..8c0862e41
--- /dev/null
+++ b/fs/cifs/fscache.h
@@ -0,0 +1,164 @@
+/*
+ * fs/cifs/fscache.h - CIFS filesystem cache interface definitions
+ *
+ * Copyright (c) 2010 Novell, Inc.
+ * Authors(s): Suresh Jayaraman (sjayaraman@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _CIFS_FSCACHE_H
+#define _CIFS_FSCACHE_H
+
+#include <linux/fscache.h>
+
+#include "cifsglob.h"
+
+#ifdef CONFIG_CIFS_FSCACHE
+
+/*
+ * Auxiliary data attached to CIFS inode within the cache
+ */
+struct cifs_fscache_inode_auxdata {
+ u64 last_write_time_sec;
+ u64 last_change_time_sec;
+ u32 last_write_time_nsec;
+ u32 last_change_time_nsec;
+ u64 eof;
+};
+
+/*
+ * cache.c
+ */
+extern struct fscache_netfs cifs_fscache_netfs;
+extern const struct fscache_cookie_def cifs_fscache_server_index_def;
+extern const struct fscache_cookie_def cifs_fscache_super_index_def;
+extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
+
+extern int cifs_fscache_register(void);
+extern void cifs_fscache_unregister(void);
+extern char *extract_sharename(const char *);
+
+/*
+ * fscache.c
+ */
+extern void cifs_fscache_get_client_cookie(struct TCP_Server_Info *);
+extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *);
+extern void cifs_fscache_get_super_cookie(struct cifs_tcon *);
+extern void cifs_fscache_release_super_cookie(struct cifs_tcon *);
+
+extern void cifs_fscache_release_inode_cookie(struct inode *);
+extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *);
+extern void cifs_fscache_reset_inode_cookie(struct inode *);
+
+extern void __cifs_fscache_invalidate_page(struct page *, struct inode *);
+extern int cifs_fscache_release_page(struct page *page, gfp_t gfp);
+extern int __cifs_readpage_from_fscache(struct inode *, struct page *);
+extern int __cifs_readpages_from_fscache(struct inode *,
+ struct address_space *,
+ struct list_head *,
+ unsigned *);
+extern void __cifs_fscache_readpages_cancel(struct inode *, struct list_head *);
+
+extern void __cifs_readpage_to_fscache(struct inode *, struct page *);
+
+static inline void cifs_fscache_invalidate_page(struct page *page,
+ struct inode *inode)
+{
+ if (PageFsCache(page))
+ __cifs_fscache_invalidate_page(page, inode);
+}
+
+static inline int cifs_readpage_from_fscache(struct inode *inode,
+ struct page *page)
+{
+ if (CIFS_I(inode)->fscache)
+ return __cifs_readpage_from_fscache(inode, page);
+
+ return -ENOBUFS;
+}
+
+static inline int cifs_readpages_from_fscache(struct inode *inode,
+ struct address_space *mapping,
+ struct list_head *pages,
+ unsigned *nr_pages)
+{
+ if (CIFS_I(inode)->fscache)
+ return __cifs_readpages_from_fscache(inode, mapping, pages,
+ nr_pages);
+ return -ENOBUFS;
+}
+
+static inline void cifs_readpage_to_fscache(struct inode *inode,
+ struct page *page)
+{
+ if (PageFsCache(page))
+ __cifs_readpage_to_fscache(inode, page);
+}
+
+static inline void cifs_fscache_readpages_cancel(struct inode *inode,
+ struct list_head *pages)
+{
+ if (CIFS_I(inode)->fscache)
+ return __cifs_fscache_readpages_cancel(inode, pages);
+}
+
+#else /* CONFIG_CIFS_FSCACHE */
+static inline int cifs_fscache_register(void) { return 0; }
+static inline void cifs_fscache_unregister(void) {}
+
+static inline void
+cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {}
+static inline void
+cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) {}
+static inline void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) {}
+static inline void
+cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {}
+
+static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}
+static inline void cifs_fscache_set_inode_cookie(struct inode *inode,
+ struct file *filp) {}
+static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {}
+static inline int cifs_fscache_release_page(struct page *page, gfp_t gfp)
+{
+ return 1; /* May release page */
+}
+
+static inline void cifs_fscache_invalidate_page(struct page *page,
+ struct inode *inode) {}
+static inline int
+cifs_readpage_from_fscache(struct inode *inode, struct page *page)
+{
+ return -ENOBUFS;
+}
+
+static inline int cifs_readpages_from_fscache(struct inode *inode,
+ struct address_space *mapping,
+ struct list_head *pages,
+ unsigned *nr_pages)
+{
+ return -ENOBUFS;
+}
+
+static inline void cifs_readpage_to_fscache(struct inode *inode,
+ struct page *page) {}
+
+static inline void cifs_fscache_readpages_cancel(struct inode *inode,
+ struct list_head *pages)
+{
+}
+
+#endif /* CONFIG_CIFS_FSCACHE */
+
+#endif /* _CIFS_FSCACHE_H */
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
new file mode 100644
index 000000000..d30eb4350
--- /dev/null
+++ b/fs/cifs/inode.c
@@ -0,0 +1,2574 @@
+/*
+ * fs/cifs/inode.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2010
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/freezer.h>
+#include <linux/sched/signal.h>
+#include <linux/wait_bit.h>
+
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "fscache.h"
+
+
+static void cifs_set_ops(struct inode *inode)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_op = &cifs_file_inode_ops;
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ inode->i_fop = &cifs_file_direct_nobrl_ops;
+ else
+ inode->i_fop = &cifs_file_direct_ops;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ inode->i_fop = &cifs_file_strict_nobrl_ops;
+ else
+ inode->i_fop = &cifs_file_strict_ops;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ inode->i_fop = &cifs_file_nobrl_ops;
+ else { /* not direct, send byte range locks */
+ inode->i_fop = &cifs_file_ops;
+ }
+
+ /* check if server can support readpages */
+ if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <
+ PAGE_SIZE + MAX_CIFS_HDR_SIZE)
+ inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ inode->i_data.a_ops = &cifs_addr_ops;
+ break;
+ case S_IFDIR:
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ if (IS_AUTOMOUNT(inode)) {
+ inode->i_op = &cifs_dfs_referral_inode_operations;
+ } else {
+#else /* NO DFS support, treat as a directory */
+ {
+#endif
+ inode->i_op = &cifs_dir_inode_ops;
+ inode->i_fop = &cifs_dir_ops;
+ }
+ break;
+ case S_IFLNK:
+ inode->i_op = &cifs_symlink_inode_ops;
+ break;
+ default:
+ init_special_inode(inode, inode->i_mode, inode->i_rdev);
+ break;
+ }
+}
+
+/* check inode attributes against fattr. If they don't match, tag the
+ * inode for cache invalidation
+ */
+static void
+cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
+{
+ struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+
+ cifs_dbg(FYI, "%s: revalidating inode %llu\n",
+ __func__, cifs_i->uniqueid);
+
+ if (inode->i_state & I_NEW) {
+ cifs_dbg(FYI, "%s: inode %llu is new\n",
+ __func__, cifs_i->uniqueid);
+ return;
+ }
+
+ /* don't bother with revalidation if we have an oplock */
+ if (CIFS_CACHE_READ(cifs_i)) {
+ cifs_dbg(FYI, "%s: inode %llu is oplocked\n",
+ __func__, cifs_i->uniqueid);
+ return;
+ }
+
+ /* revalidate if mtime or size have changed */
+ if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) &&
+ cifs_i->server_eof == fattr->cf_eof) {
+ cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
+ __func__, cifs_i->uniqueid);
+ return;
+ }
+
+ cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n",
+ __func__, cifs_i->uniqueid);
+ set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags);
+}
+
+/*
+ * copy nlink to the inode, unless it wasn't provided. Provide
+ * sane values if we don't have an existing one and none was provided
+ */
+static void
+cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
+{
+ /*
+ * if we're in a situation where we can't trust what we
+ * got from the server (readdir, some non-unix cases)
+ * fake reasonable values
+ */
+ if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) {
+ /* only provide fake values on a new inode */
+ if (inode->i_state & I_NEW) {
+ if (fattr->cf_cifsattrs & ATTR_DIRECTORY)
+ set_nlink(inode, 2);
+ else
+ set_nlink(inode, 1);
+ }
+ return;
+ }
+
+ /* we trust the server, so update it */
+ set_nlink(inode, fattr->cf_nlink);
+}
+
+/* populate an inode with info from a cifs_fattr struct */
+void
+cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
+{
+ struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ cifs_revalidate_cache(inode, fattr);
+
+ spin_lock(&inode->i_lock);
+ inode->i_atime = fattr->cf_atime;
+ inode->i_mtime = fattr->cf_mtime;
+ inode->i_ctime = fattr->cf_ctime;
+ inode->i_rdev = fattr->cf_rdev;
+ cifs_nlink_fattr_to_inode(inode, fattr);
+ inode->i_uid = fattr->cf_uid;
+ inode->i_gid = fattr->cf_gid;
+
+ /* if dynperm is set, don't clobber existing mode */
+ if (inode->i_state & I_NEW ||
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM))
+ inode->i_mode = fattr->cf_mode;
+
+ cifs_i->cifsAttrs = fattr->cf_cifsattrs;
+
+ if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
+ cifs_i->time = 0;
+ else
+ cifs_i->time = jiffies;
+
+ if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
+ set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);
+ else
+ clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);
+
+ cifs_i->server_eof = fattr->cf_eof;
+ /*
+ * Can't safely change the file size here if the client is writing to
+ * it due to potential races.
+ */
+ if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) {
+ i_size_write(inode, fattr->cf_eof);
+
+ /*
+ * i_blocks is not related to (i_size / i_blksize),
+ * but instead 512 byte (2**9) size is required for
+ * calculating num blocks.
+ */
+ inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
+ }
+ spin_unlock(&inode->i_lock);
+
+ if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL)
+ inode->i_flags |= S_AUTOMOUNT;
+ if (inode->i_state & I_NEW)
+ cifs_set_ops(inode);
+}
+
+void
+cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
+ return;
+
+ fattr->cf_uniqueid = iunique(sb, ROOT_I);
+}
+
+/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */
+void
+cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
+ struct cifs_sb_info *cifs_sb)
+{
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_uniqueid = le64_to_cpu(info->UniqueId);
+ fattr->cf_bytes = le64_to_cpu(info->NumOfBytes);
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+
+ fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+ fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime);
+ fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange);
+ /* old POSIX extensions don't get create time */
+
+ fattr->cf_mode = le64_to_cpu(info->Permissions);
+
+ /*
+ * Since we set the inode type below we need to mask off
+ * to avoid strange results if bits set above.
+ */
+ fattr->cf_mode &= ~S_IFMT;
+ switch (le32_to_cpu(info->Type)) {
+ case UNIX_FILE:
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
+ break;
+ case UNIX_SYMLINK:
+ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ break;
+ case UNIX_DIR:
+ fattr->cf_mode |= S_IFDIR;
+ fattr->cf_dtype = DT_DIR;
+ break;
+ case UNIX_CHARDEV:
+ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_dtype = DT_CHR;
+ fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+ le64_to_cpu(info->DevMinor) & MINORMASK);
+ break;
+ case UNIX_BLOCKDEV:
+ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_dtype = DT_BLK;
+ fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+ le64_to_cpu(info->DevMinor) & MINORMASK);
+ break;
+ case UNIX_FIFO:
+ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
+ break;
+ case UNIX_SOCKET:
+ fattr->cf_mode |= S_IFSOCK;
+ fattr->cf_dtype = DT_SOCK;
+ break;
+ default:
+ /* safest to call it a file if we do not know */
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
+ cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type));
+ break;
+ }
+
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) {
+ u64 id = le64_to_cpu(info->Uid);
+ if (id < ((uid_t)-1)) {
+ kuid_t uid = make_kuid(&init_user_ns, id);
+ if (uid_valid(uid))
+ fattr->cf_uid = uid;
+ }
+ }
+
+ fattr->cf_gid = cifs_sb->mnt_gid;
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) {
+ u64 id = le64_to_cpu(info->Gid);
+ if (id < ((gid_t)-1)) {
+ kgid_t gid = make_kgid(&init_user_ns, id);
+ if (gid_valid(gid))
+ fattr->cf_gid = gid;
+ }
+ }
+
+ fattr->cf_nlink = le64_to_cpu(info->Nlinks);
+}
+
+/*
+ * Fill a cifs_fattr struct with fake inode info.
+ *
+ * Needed to setup cifs_fattr data for the directory which is the
+ * junction to the new submount (ie to setup the fake directory
+ * which represents a DFS referral).
+ */
+static void
+cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+
+ cifs_dbg(FYI, "creating fake fattr for DFS referral\n");
+
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ fattr->cf_gid = cifs_sb->mnt_gid;
+ ktime_get_real_ts64(&fattr->cf_mtime);
+ fattr->cf_mtime = timespec64_trunc(fattr->cf_mtime, sb->s_time_gran);
+ fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime;
+ fattr->cf_nlink = 2;
+ fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
+}
+
+static int
+cifs_get_file_info_unix(struct file *filp)
+{
+ int rc;
+ unsigned int xid;
+ FILE_UNIX_BASIC_INFO find_data;
+ struct cifs_fattr fattr;
+ struct inode *inode = file_inode(filp);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsFileInfo *cfile = filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+
+ xid = get_xid();
+ rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);
+ if (!rc) {
+ cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
+ } else if (rc == -EREMOTE) {
+ cifs_create_dfs_fattr(&fattr, inode->i_sb);
+ rc = 0;
+ }
+
+ cifs_fattr_to_inode(inode, &fattr);
+ free_xid(xid);
+ return rc;
+}
+
+int cifs_get_inode_info_unix(struct inode **pinode,
+ const unsigned char *full_path,
+ struct super_block *sb, unsigned int xid)
+{
+ int rc;
+ FILE_UNIX_BASIC_INFO find_data;
+ struct cifs_fattr fattr;
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+
+ cifs_dbg(FYI, "Getting info on %s\n", full_path);
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ /* could have done a find first instead but this returns more info */
+ rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+ cifs_put_tlink(tlink);
+
+ if (!rc) {
+ cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
+ } else if (rc == -EREMOTE) {
+ cifs_create_dfs_fattr(&fattr, sb);
+ rc = 0;
+ } else {
+ return rc;
+ }
+
+ /* check for Minshall+French symlinks */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
+ int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr,
+ full_path);
+ if (tmprc)
+ cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
+ }
+
+ if (*pinode == NULL) {
+ /* get new inode */
+ cifs_fill_uniqueid(sb, &fattr);
+ *pinode = cifs_iget(sb, &fattr);
+ if (!*pinode)
+ rc = -ENOMEM;
+ } else {
+ /* we already have inode, update it */
+
+ /* if uniqueid is different, return error */
+ if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM &&
+ CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) {
+ CIFS_I(*pinode)->time = 0; /* force reval */
+ rc = -ESTALE;
+ goto cgiiu_exit;
+ }
+
+ /* if filetype is different, return error */
+ if (unlikely(((*pinode)->i_mode & S_IFMT) !=
+ (fattr.cf_mode & S_IFMT))) {
+ CIFS_I(*pinode)->time = 0; /* force reval */
+ rc = -ESTALE;
+ goto cgiiu_exit;
+ }
+
+ cifs_fattr_to_inode(*pinode, &fattr);
+ }
+
+cgiiu_exit:
+ return rc;
+}
+
+static int
+cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
+ struct cifs_sb_info *cifs_sb, unsigned int xid)
+{
+ int rc;
+ __u32 oplock;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ char buf[24];
+ unsigned int bytes_read;
+ char *pbuf;
+ int buf_type = CIFS_NO_BUFFER;
+
+ pbuf = buf;
+
+ fattr->cf_mode &= ~S_IFMT;
+
+ if (fattr->cf_eof == 0) {
+ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
+ return 0;
+ } else if (fattr->cf_eof < 8) {
+ fattr->cf_mode |= S_IFREG;
+ fattr->cf_dtype = DT_REG;
+ return -EINVAL; /* EOPNOTSUPP? */
+ }
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = GENERIC_READ;
+ oparms.create_options = CREATE_NOT_DIR;
+ if (backup_cred(cifs_sb))
+ oparms.create_options |= CREATE_OPEN_BACKUP_INTENT;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ if (tcon->ses->server->oplocks)
+ oplock = REQ_OPLOCK;
+ else
+ oplock = 0;
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
+ if (rc) {
+ cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc);
+ cifs_put_tlink(tlink);
+ return rc;
+ }
+
+ /* Read header */
+ io_parms.netfid = fid.netfid;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = 24;
+
+ rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms,
+ &bytes_read, &pbuf, &buf_type);
+ if ((rc == 0) && (bytes_read >= 8)) {
+ if (memcmp("IntxBLK", pbuf, 8) == 0) {
+ cifs_dbg(FYI, "Block device\n");
+ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_dtype = DT_BLK;
+ if (bytes_read == 24) {
+ /* we have enough to decode dev num */
+ __u64 mjr; /* major */
+ __u64 mnr; /* minor */
+ mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
+ mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
+ fattr->cf_rdev = MKDEV(mjr, mnr);
+ }
+ } else if (memcmp("IntxCHR", pbuf, 8) == 0) {
+ cifs_dbg(FYI, "Char device\n");
+ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_dtype = DT_CHR;
+ if (bytes_read == 24) {
+ /* we have enough to decode dev num */
+ __u64 mjr; /* major */
+ __u64 mnr; /* minor */
+ mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
+ mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
+ fattr->cf_rdev = MKDEV(mjr, mnr);
+ }
+ } else if (memcmp("IntxLNK", pbuf, 7) == 0) {
+ cifs_dbg(FYI, "Symlink\n");
+ fattr->cf_mode |= S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ } else {
+ fattr->cf_mode |= S_IFREG; /* file? */
+ fattr->cf_dtype = DT_REG;
+ rc = -EOPNOTSUPP;
+ }
+ } else {
+ fattr->cf_mode |= S_IFREG; /* then it is a file */
+ fattr->cf_dtype = DT_REG;
+ rc = -EOPNOTSUPP; /* or some unknown SFU type */
+ }
+
+ tcon->ses->server->ops->close(xid, tcon, &fid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */
+
+/*
+ * Fetch mode bits as provided by SFU.
+ *
+ * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ?
+ */
+static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
+ struct cifs_sb_info *cifs_sb, unsigned int xid)
+{
+#ifdef CONFIG_CIFS_XATTR
+ ssize_t rc;
+ char ea_value[4];
+ __u32 mode;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ if (tcon->ses->server->ops->query_all_EAs == NULL) {
+ cifs_put_tlink(tlink);
+ return -EOPNOTSUPP;
+ }
+
+ rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path,
+ "SETFILEBITS", ea_value, 4 /* size of buf */,
+ cifs_sb);
+ cifs_put_tlink(tlink);
+ if (rc < 0)
+ return (int)rc;
+ else if (rc > 3) {
+ mode = le32_to_cpu(*((__le32 *)ea_value));
+ fattr->cf_mode &= ~SFBITS_MASK;
+ cifs_dbg(FYI, "special bits 0%o org mode 0%o\n",
+ mode, fattr->cf_mode);
+ fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode;
+ cifs_dbg(FYI, "special mode bits 0%o\n", mode);
+ }
+
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+/* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
+static void
+cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
+ struct super_block *sb, bool adjust_tz,
+ bool symlink)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
+ if (info->DeletePending)
+ fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
+
+ if (info->LastAccessTime)
+ fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+ else {
+ ktime_get_real_ts64(&fattr->cf_atime);
+ fattr->cf_atime = timespec64_trunc(fattr->cf_atime, sb->s_time_gran);
+ }
+
+ fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
+ fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
+
+ if (adjust_tz) {
+ fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
+ fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
+ }
+
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+ fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
+ fattr->cf_createtime = le64_to_cpu(info->CreationTime);
+
+ fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
+
+ if (symlink) {
+ fattr->cf_mode = S_IFLNK;
+ fattr->cf_dtype = DT_LNK;
+ } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+ fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
+ fattr->cf_dtype = DT_DIR;
+ /*
+ * Server can return wrong NumberOfLinks value for directories
+ * when Unix extensions are disabled - fake it.
+ */
+ if (!tcon->unix_ext)
+ fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
+ } else {
+ fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
+ fattr->cf_dtype = DT_REG;
+
+ /* clear write bits if ATTR_READONLY is set */
+ if (fattr->cf_cifsattrs & ATTR_READONLY)
+ fattr->cf_mode &= ~(S_IWUGO);
+
+ /*
+ * Don't accept zero nlink from non-unix servers unless
+ * delete is pending. Instead mark it as unknown.
+ */
+ if ((fattr->cf_nlink < 1) && !tcon->unix_ext &&
+ !info->DeletePending) {
+ cifs_dbg(1, "bogus file nlink value %u\n",
+ fattr->cf_nlink);
+ fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
+ }
+ }
+
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ fattr->cf_gid = cifs_sb->mnt_gid;
+}
+
+static int
+cifs_get_file_info(struct file *filp)
+{
+ int rc;
+ unsigned int xid;
+ FILE_ALL_INFO find_data;
+ struct cifs_fattr fattr;
+ struct inode *inode = file_inode(filp);
+ struct cifsFileInfo *cfile = filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ if (!server->ops->query_file_info)
+ return -ENOSYS;
+
+ xid = get_xid();
+ rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
+ switch (rc) {
+ case 0:
+ cifs_all_info_to_fattr(&fattr, &find_data, inode->i_sb, false,
+ false);
+ break;
+ case -EREMOTE:
+ cifs_create_dfs_fattr(&fattr, inode->i_sb);
+ rc = 0;
+ break;
+ case -EOPNOTSUPP:
+ case -EINVAL:
+ /*
+ * FIXME: legacy server -- fall back to path-based call?
+ * for now, just skip revalidating and mark inode for
+ * immediate reval.
+ */
+ rc = 0;
+ CIFS_I(inode)->time = 0;
+ default:
+ goto cgfi_exit;
+ }
+
+ /*
+ * don't bother with SFU junk here -- just mark inode as needing
+ * revalidation.
+ */
+ fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
+ fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
+ cifs_fattr_to_inode(inode, &fattr);
+cgfi_exit:
+ free_xid(xid);
+ return rc;
+}
+
+/* Simple function to return a 64 bit hash of string. Rarely called */
+static __u64 simple_hashstr(const char *str)
+{
+ const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */
+ __u64 hash = 0;
+
+ while (*str)
+ hash = (hash + (__u64) *str++) * hash_mult;
+
+ return hash;
+}
+
+int
+cifs_get_inode_info(struct inode **inode, const char *full_path,
+ FILE_ALL_INFO *data, struct super_block *sb, int xid,
+ const struct cifs_fid *fid)
+{
+ bool validinum = false;
+ __u16 srchflgs;
+ int rc = 0, tmprc = ENOSYS;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct tcon_link *tlink;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ char *buf = NULL;
+ bool adjust_tz = false;
+ struct cifs_fattr fattr;
+ struct cifs_search_info *srchinf = NULL;
+ bool symlink = false;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ cifs_dbg(FYI, "Getting info on %s\n", full_path);
+
+ if ((data == NULL) && (*inode != NULL)) {
+ if (CIFS_CACHE_READ(CIFS_I(*inode)) &&
+ CIFS_I(*inode)->time != 0) {
+ cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
+ goto cgii_exit;
+ }
+ }
+
+ /* if inode info is not passed, get it from server */
+ if (data == NULL) {
+ if (!server->ops->query_path_info) {
+ rc = -ENOSYS;
+ goto cgii_exit;
+ }
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (buf == NULL) {
+ rc = -ENOMEM;
+ goto cgii_exit;
+ }
+ data = (FILE_ALL_INFO *)buf;
+ rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path,
+ data, &adjust_tz, &symlink);
+ }
+
+ if (!rc) {
+ cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
+ symlink);
+ } else if (rc == -EREMOTE) {
+ cifs_create_dfs_fattr(&fattr, sb);
+ rc = 0;
+ } else if ((rc == -EACCES) && backup_cred(cifs_sb) &&
+ (strcmp(server->vals->version_string, SMB1_VERSION_STRING)
+ == 0)) {
+ /*
+ * For SMB2 and later the backup intent flag is already
+ * sent if needed on open and there is no path based
+ * FindFirst operation to use to retry with
+ */
+
+ srchinf = kzalloc(sizeof(struct cifs_search_info),
+ GFP_KERNEL);
+ if (srchinf == NULL) {
+ rc = -ENOMEM;
+ goto cgii_exit;
+ }
+
+ srchinf->endOfSearch = false;
+ if (tcon->unix_ext)
+ srchinf->info_level = SMB_FIND_FILE_UNIX;
+ else if ((tcon->ses->capabilities &
+ tcon->ses->server->vals->cap_nt_find) == 0)
+ srchinf->info_level = SMB_FIND_FILE_INFO_STANDARD;
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
+ srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
+ else /* no srvino useful for fallback to some netapp */
+ srchinf->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
+
+ srchflgs = CIFS_SEARCH_CLOSE_ALWAYS |
+ CIFS_SEARCH_CLOSE_AT_END |
+ CIFS_SEARCH_BACKUP_SEARCH;
+
+ rc = CIFSFindFirst(xid, tcon, full_path,
+ cifs_sb, NULL, srchflgs, srchinf, false);
+ if (!rc) {
+ data = (FILE_ALL_INFO *)srchinf->srch_entries_start;
+
+ cifs_dir_info_to_fattr(&fattr,
+ (FILE_DIRECTORY_INFO *)data, cifs_sb);
+ fattr.cf_uniqueid = le64_to_cpu(
+ ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId);
+ validinum = true;
+
+ cifs_buf_release(srchinf->ntwrk_buf_start);
+ }
+ kfree(srchinf);
+ if (rc)
+ goto cgii_exit;
+ } else
+ goto cgii_exit;
+
+ /*
+ * If an inode wasn't passed in, then get the inode number
+ *
+ * Is an i_ino of zero legal? Can we use that to check if the server
+ * supports returning inode numbers? Are there other sanity checks we
+ * can use to ensure that the server is really filling in that field?
+ */
+ if (*inode == NULL) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ if (validinum == false) {
+ if (server->ops->get_srv_inum)
+ tmprc = server->ops->get_srv_inum(xid,
+ tcon, cifs_sb, full_path,
+ &fattr.cf_uniqueid, data);
+ if (tmprc) {
+ cifs_dbg(FYI, "GetSrvInodeNum rc %d\n",
+ tmprc);
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
+ cifs_autodisable_serverino(cifs_sb);
+ } else if ((fattr.cf_uniqueid == 0) &&
+ strlen(full_path) == 0) {
+ /* some servers ret bad root ino ie 0 */
+ cifs_dbg(FYI, "Invalid (0) inodenum\n");
+ fattr.cf_flags |=
+ CIFS_FATTR_FAKE_ROOT_INO;
+ fattr.cf_uniqueid =
+ simple_hashstr(tcon->treeName);
+ }
+ }
+ } else
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
+ } else {
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
+ validinum == false && server->ops->get_srv_inum) {
+ /*
+ * Pass a NULL tcon to ensure we don't make a round
+ * trip to the server. This only works for SMB2+.
+ */
+ tmprc = server->ops->get_srv_inum(xid,
+ NULL, cifs_sb, full_path,
+ &fattr.cf_uniqueid, data);
+ if (tmprc)
+ fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ else if ((fattr.cf_uniqueid == 0) &&
+ strlen(full_path) == 0) {
+ /*
+ * Reuse existing root inode num since
+ * inum zero for root causes ls of . and .. to
+ * not be returned
+ */
+ cifs_dbg(FYI, "Srv ret 0 inode num for root\n");
+ fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ }
+ } else
+ fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ }
+
+ /* query for SFU type info if supported and needed */
+ if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid);
+ if (tmprc)
+ cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc);
+ }
+
+#ifdef CONFIG_CIFS_ACL
+ /* fill in 0777 bits from ACL */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, full_path, fid);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n",
+ __func__, rc);
+ goto cgii_exit;
+ }
+ }
+#endif /* CONFIG_CIFS_ACL */
+
+ /* fill in remaining high mode bits e.g. SUID, VTX */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
+ cifs_sfu_mode(&fattr, full_path, cifs_sb, xid);
+
+ /* check for Minshall+French symlinks */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
+ tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr,
+ full_path);
+ if (tmprc)
+ cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
+ }
+
+ if (!*inode) {
+ *inode = cifs_iget(sb, &fattr);
+ if (!*inode)
+ rc = -ENOMEM;
+ } else {
+ /* we already have inode, update it */
+
+ /* if uniqueid is different, return error */
+ if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM &&
+ CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
+ CIFS_I(*inode)->time = 0; /* force reval */
+ rc = -ESTALE;
+ goto cgii_exit;
+ }
+
+ /* if filetype is different, return error */
+ if (unlikely(((*inode)->i_mode & S_IFMT) !=
+ (fattr.cf_mode & S_IFMT))) {
+ CIFS_I(*inode)->time = 0; /* force reval */
+ rc = -ESTALE;
+ goto cgii_exit;
+ }
+
+ cifs_fattr_to_inode(*inode, &fattr);
+ }
+
+cgii_exit:
+ if ((*inode) && ((*inode)->i_ino == 0))
+ cifs_dbg(FYI, "inode number of zero returned\n");
+
+ kfree(buf);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static const struct inode_operations cifs_ipc_inode_ops = {
+ .lookup = cifs_lookup,
+};
+
+static int
+cifs_find_inode(struct inode *inode, void *opaque)
+{
+ struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;
+
+ /* don't match inode with different uniqueid */
+ if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)
+ return 0;
+
+ /* use createtime like an i_generation field */
+ if (CIFS_I(inode)->createtime != fattr->cf_createtime)
+ return 0;
+
+ /* don't match inode of different type */
+ if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
+ return 0;
+
+ /* if it's not a directory or has no dentries, then flag it */
+ if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry))
+ fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
+
+ return 1;
+}
+
+static int
+cifs_init_inode(struct inode *inode, void *opaque)
+{
+ struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;
+
+ CIFS_I(inode)->uniqueid = fattr->cf_uniqueid;
+ CIFS_I(inode)->createtime = fattr->cf_createtime;
+ return 0;
+}
+
+/*
+ * walk dentry list for an inode and report whether it has aliases that
+ * are hashed. We use this to determine if a directory inode can actually
+ * be used.
+ */
+static bool
+inode_has_hashed_dentries(struct inode *inode)
+{
+ struct dentry *dentry;
+
+ spin_lock(&inode->i_lock);
+ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {
+ if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
+ spin_unlock(&inode->i_lock);
+ return true;
+ }
+ }
+ spin_unlock(&inode->i_lock);
+ return false;
+}
+
+/* Given fattrs, get a corresponding inode */
+struct inode *
+cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
+{
+ unsigned long hash;
+ struct inode *inode;
+
+retry_iget5_locked:
+ cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid);
+
+ /* hash down to 32-bits on 32-bit arch */
+ hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
+
+ inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
+ if (inode) {
+ /* was there a potentially problematic inode collision? */
+ if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) {
+ fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
+
+ if (inode_has_hashed_dentries(inode)) {
+ cifs_autodisable_serverino(CIFS_SB(sb));
+ iput(inode);
+ fattr->cf_uniqueid = iunique(sb, ROOT_I);
+ goto retry_iget5_locked;
+ }
+ }
+
+ cifs_fattr_to_inode(inode, fattr);
+ if (sb->s_flags & SB_NOATIME)
+ inode->i_flags |= S_NOATIME | S_NOCMTIME;
+ if (inode->i_state & I_NEW) {
+ inode->i_ino = hash;
+#ifdef CONFIG_CIFS_FSCACHE
+ /* initialize per-inode cache cookie pointer */
+ CIFS_I(inode)->fscache = NULL;
+#endif
+ unlock_new_inode(inode);
+ }
+ }
+
+ return inode;
+}
+
+/* gets root inode */
+struct inode *cifs_root_iget(struct super_block *sb)
+{
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct inode *inode = NULL;
+ long rc;
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ char *path = NULL;
+ int len;
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
+ && cifs_sb->prepath) {
+ len = strlen(cifs_sb->prepath);
+ path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
+ if (path == NULL)
+ return ERR_PTR(-ENOMEM);
+ path[0] = '/';
+ memcpy(path+1, cifs_sb->prepath, len);
+ } else {
+ path = kstrdup("", GFP_KERNEL);
+ if (path == NULL)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ xid = get_xid();
+ if (tcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&inode, path, sb, xid);
+ /* some servers mistakenly claim POSIX support */
+ if (rc != -EOPNOTSUPP)
+ goto iget_no_retry;
+ cifs_dbg(VFS, "server does not support POSIX extensions");
+ tcon->unix_ext = false;
+ }
+
+ convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
+ rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL);
+
+iget_no_retry:
+ if (!inode) {
+ inode = ERR_PTR(rc);
+ goto out;
+ }
+
+#ifdef CONFIG_CIFS_FSCACHE
+ /* populate tcon->resource_id */
+ tcon->resource_id = CIFS_I(inode)->uniqueid;
+#endif
+
+ if (rc && tcon->pipe) {
+ cifs_dbg(FYI, "ipc connection - fake read inode\n");
+ spin_lock(&inode->i_lock);
+ inode->i_mode |= S_IFDIR;
+ set_nlink(inode, 2);
+ inode->i_op = &cifs_ipc_inode_ops;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_uid = cifs_sb->mnt_uid;
+ inode->i_gid = cifs_sb->mnt_gid;
+ spin_unlock(&inode->i_lock);
+ } else if (rc) {
+ iget_failed(inode);
+ inode = ERR_PTR(rc);
+ }
+
+out:
+ kfree(path);
+ free_xid(xid);
+ return inode;
+}
+
+int
+cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
+ char *full_path, __u32 dosattr)
+{
+ bool set_time = false;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct TCP_Server_Info *server;
+ FILE_BASIC_INFO info_buf;
+
+ if (attrs == NULL)
+ return -EINVAL;
+
+ server = cifs_sb_master_tcon(cifs_sb)->ses->server;
+ if (!server->ops->set_file_info)
+ return -ENOSYS;
+
+ info_buf.Pad = 0;
+
+ if (attrs->ia_valid & ATTR_ATIME) {
+ set_time = true;
+ info_buf.LastAccessTime =
+ cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
+ } else
+ info_buf.LastAccessTime = 0;
+
+ if (attrs->ia_valid & ATTR_MTIME) {
+ set_time = true;
+ info_buf.LastWriteTime =
+ cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
+ } else
+ info_buf.LastWriteTime = 0;
+
+ /*
+ * Samba throws this field away, but windows may actually use it.
+ * Do not set ctime unless other time stamps are changed explicitly
+ * (i.e. by utimes()) since we would then have a mix of client and
+ * server times.
+ */
+ if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
+ cifs_dbg(FYI, "CIFS - CTIME changed\n");
+ info_buf.ChangeTime =
+ cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
+ } else
+ info_buf.ChangeTime = 0;
+
+ info_buf.CreationTime = 0; /* don't change */
+ info_buf.Attributes = cpu_to_le32(dosattr);
+
+ return server->ops->set_file_info(inode, full_path, &info_buf, xid);
+}
+
+/*
+ * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
+ * and rename it to a random name that hopefully won't conflict with
+ * anything else.
+ */
+int
+cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,
+ const unsigned int xid)
+{
+ int oplock = 0;
+ int rc;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct inode *inode = d_inode(dentry);
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ __u32 dosattr, origattr;
+ FILE_BASIC_INFO *info_buf = NULL;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ /*
+ * We cannot rename the file if the server doesn't support
+ * CAP_INFOLEVEL_PASSTHRU
+ */
+ if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = DELETE | FILE_WRITE_ATTRIBUTES;
+ oparms.create_options = CREATE_NOT_DIR;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = full_path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc != 0)
+ goto out;
+
+ origattr = cifsInode->cifsAttrs;
+ if (origattr == 0)
+ origattr |= ATTR_NORMAL;
+
+ dosattr = origattr & ~ATTR_READONLY;
+ if (dosattr == 0)
+ dosattr |= ATTR_NORMAL;
+ dosattr |= ATTR_HIDDEN;
+
+ /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */
+ if (dosattr != origattr) {
+ info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL);
+ if (info_buf == NULL) {
+ rc = -ENOMEM;
+ goto out_close;
+ }
+ info_buf->Attributes = cpu_to_le32(dosattr);
+ rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,
+ current->tgid);
+ /* although we would like to mark the file hidden
+ if that fails we will still try to rename it */
+ if (!rc)
+ cifsInode->cifsAttrs = dosattr;
+ else
+ dosattr = origattr; /* since not able to change them */
+ }
+
+ /* rename the file */
+ rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (rc != 0) {
+ rc = -EBUSY;
+ goto undo_setattr;
+ }
+
+ /* try to set DELETE_ON_CLOSE */
+ if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) {
+ rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid,
+ current->tgid);
+ /*
+ * some samba versions return -ENOENT when we try to set the
+ * file disposition here. Likely a samba bug, but work around
+ * it for now. This means that some cifsXXX files may hang
+ * around after they shouldn't.
+ *
+ * BB: remove this hack after more servers have the fix
+ */
+ if (rc == -ENOENT)
+ rc = 0;
+ else if (rc != 0) {
+ rc = -EBUSY;
+ goto undo_rename;
+ }
+ set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);
+ }
+
+out_close:
+ CIFSSMBClose(xid, tcon, fid.netfid);
+out:
+ kfree(info_buf);
+ cifs_put_tlink(tlink);
+ return rc;
+
+ /*
+ * reset everything back to the original state. Don't bother
+ * dealing with errors here since we can't do anything about
+ * them anyway.
+ */
+undo_rename:
+ CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+undo_setattr:
+ if (dosattr != origattr) {
+ info_buf->Attributes = cpu_to_le32(origattr);
+ if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,
+ current->tgid))
+ cifsInode->cifsAttrs = origattr;
+ }
+
+ goto out_close;
+}
+
+/* copied from fs/nfs/dir.c with small changes */
+static void
+cifs_drop_nlink(struct inode *inode)
+{
+ spin_lock(&inode->i_lock);
+ if (inode->i_nlink > 0)
+ drop_nlink(inode);
+ spin_unlock(&inode->i_lock);
+}
+
+/*
+ * If d_inode(dentry) is null (usually meaning the cached dentry
+ * is a negative dentry) then we would attempt a standard SMB delete, but
+ * if that fails we can not attempt the fall back mechanisms on EACCESS
+ * but will return the EACCESS to the caller. Note that the VFS does not call
+ * unlink on negative dentries currently.
+ */
+int cifs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int rc = 0;
+ unsigned int xid;
+ char *full_path = NULL;
+ struct inode *inode = d_inode(dentry);
+ struct cifsInodeInfo *cifs_inode;
+ struct super_block *sb = dir->i_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct iattr *attrs = NULL;
+ __u32 dosattr = 0, origattr = 0;
+
+ cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ xid = get_xid();
+
+ /* Unlink can be called from rename so we can not take the
+ * sb->s_vfs_rename_mutex here */
+ full_path = build_path_from_dentry(dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto unlink_out;
+ }
+
+ if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ rc = CIFSPOSIXDelFile(xid, tcon, full_path,
+ SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ cifs_dbg(FYI, "posix del rc %d\n", rc);
+ if ((rc == 0) || (rc == -ENOENT))
+ goto psx_del_no_retry;
+ }
+
+retry_std_delete:
+ if (!server->ops->unlink) {
+ rc = -ENOSYS;
+ goto psx_del_no_retry;
+ }
+
+ rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);
+
+psx_del_no_retry:
+ if (!rc) {
+ if (inode)
+ cifs_drop_nlink(inode);
+ } else if (rc == -ENOENT) {
+ d_drop(dentry);
+ } else if (rc == -EBUSY) {
+ if (server->ops->rename_pending_delete) {
+ rc = server->ops->rename_pending_delete(full_path,
+ dentry, xid);
+ if (rc == 0)
+ cifs_drop_nlink(inode);
+ }
+ } else if ((rc == -EACCES) && (dosattr == 0) && inode) {
+ attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
+ if (attrs == NULL) {
+ rc = -ENOMEM;
+ goto out_reval;
+ }
+
+ /* try to reset dos attributes */
+ cifs_inode = CIFS_I(inode);
+ origattr = cifs_inode->cifsAttrs;
+ if (origattr == 0)
+ origattr |= ATTR_NORMAL;
+ dosattr = origattr & ~ATTR_READONLY;
+ if (dosattr == 0)
+ dosattr |= ATTR_NORMAL;
+ dosattr |= ATTR_HIDDEN;
+
+ rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
+ if (rc != 0)
+ goto out_reval;
+
+ goto retry_std_delete;
+ }
+
+ /* undo the setattr if we errored out and it's needed */
+ if (rc != 0 && dosattr != 0)
+ cifs_set_file_info(inode, attrs, xid, full_path, origattr);
+
+out_reval:
+ if (inode) {
+ cifs_inode = CIFS_I(inode);
+ cifs_inode->time = 0; /* will force revalidate to get info
+ when needed */
+ inode->i_ctime = current_time(inode);
+ }
+ dir->i_ctime = dir->i_mtime = current_time(dir);
+ cifs_inode = CIFS_I(dir);
+ CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
+unlink_out:
+ kfree(full_path);
+ kfree(attrs);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static int
+cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
+ const char *full_path, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, const unsigned int xid)
+{
+ int rc = 0;
+ struct inode *inode = NULL;
+
+ if (tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
+ xid);
+ else
+ rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
+ xid, NULL);
+
+ if (rc)
+ return rc;
+
+ /*
+ * setting nlink not necessary except in cases where we failed to get it
+ * from the server or was set bogus. Also, since this is a brand new
+ * inode, no need to grab the i_lock before setting the i_nlink.
+ */
+ if (inode->i_nlink < 2)
+ set_nlink(inode, 2);
+ mode &= ~current_umask();
+ /* must turn on setgid bit if parent dir has it */
+ if (parent->i_mode & S_ISGID)
+ mode |= S_ISGID;
+
+ if (tcon->unix_ext) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = current_fsuid();
+ if (parent->i_mode & S_ISGID)
+ args.gid = parent->i_gid;
+ else
+ args.gid = current_fsgid();
+ } else {
+ args.uid = INVALID_UID; /* no change */
+ args.gid = INVALID_GID; /* no change */
+ }
+ CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ } else {
+ struct TCP_Server_Info *server = tcon->ses->server;
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
+ (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo)
+ server->ops->mkdir_setinfo(inode, full_path, cifs_sb,
+ tcon, xid);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ inode->i_mode = (mode | S_IFDIR);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ inode->i_uid = current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ inode->i_gid = parent->i_gid;
+ else
+ inode->i_gid = current_fsgid();
+ }
+ }
+ d_instantiate(dentry, inode);
+ return rc;
+}
+
+static int
+cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode,
+ const char *full_path, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, const unsigned int xid)
+{
+ int rc = 0;
+ u32 oplock = 0;
+ FILE_UNIX_BASIC_INFO *info = NULL;
+ struct inode *newinode = NULL;
+ struct cifs_fattr fattr;
+
+ info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
+ if (info == NULL) {
+ rc = -ENOMEM;
+ goto posix_mkdir_out;
+ }
+
+ mode &= ~current_umask();
+ rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode,
+ NULL /* netfid */, info, &oplock, full_path,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+ if (rc == -EOPNOTSUPP)
+ goto posix_mkdir_out;
+ else if (rc) {
+ cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc);
+ d_drop(dentry);
+ goto posix_mkdir_out;
+ }
+
+ if (info->Type == cpu_to_le32(-1))
+ /* no return info, go query for it */
+ goto posix_mkdir_get_info;
+ /*
+ * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if
+ * need to set uid/gid.
+ */
+
+ cifs_unix_basic_to_fattr(&fattr, info, cifs_sb);
+ cifs_fill_uniqueid(inode->i_sb, &fattr);
+ newinode = cifs_iget(inode->i_sb, &fattr);
+ if (!newinode)
+ goto posix_mkdir_get_info;
+
+ d_instantiate(dentry, newinode);
+
+#ifdef CONFIG_CIFS_DEBUG2
+ cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n",
+ dentry, dentry, newinode);
+
+ if (newinode->i_nlink != 2)
+ cifs_dbg(FYI, "unexpected number of links %d\n",
+ newinode->i_nlink);
+#endif
+
+posix_mkdir_out:
+ kfree(info);
+ return rc;
+posix_mkdir_get_info:
+ rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon,
+ xid);
+ goto posix_mkdir_out;
+}
+
+int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode)
+{
+ int rc = 0;
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ char *full_path;
+
+ cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n",
+ mode, inode);
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto mkdir_out;
+ }
+
+ server = tcon->ses->server;
+
+ if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) {
+ rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path,
+ cifs_sb);
+ d_drop(direntry); /* for time being always refresh inode info */
+ goto mkdir_out;
+ }
+
+ if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb,
+ tcon, xid);
+ if (rc != -EOPNOTSUPP)
+ goto mkdir_out;
+ }
+
+ if (!server->ops->mkdir) {
+ rc = -ENOSYS;
+ goto mkdir_out;
+ }
+
+ /* BB add setting the equivalent of mode via CreateX w/ACLs */
+ rc = server->ops->mkdir(xid, tcon, full_path, cifs_sb);
+ if (rc) {
+ cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc);
+ d_drop(direntry);
+ goto mkdir_out;
+ }
+
+ rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon,
+ xid);
+mkdir_out:
+ /*
+ * Force revalidate to get parent dir info when needed since cached
+ * attributes are invalid now.
+ */
+ CIFS_I(inode)->time = 0;
+ kfree(full_path);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+int cifs_rmdir(struct inode *inode, struct dentry *direntry)
+{
+ int rc = 0;
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ char *full_path = NULL;
+ struct cifsInodeInfo *cifsInode;
+
+ cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode);
+
+ xid = get_xid();
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto rmdir_exit;
+ }
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ goto rmdir_exit;
+ }
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->rmdir) {
+ rc = -ENOSYS;
+ cifs_put_tlink(tlink);
+ goto rmdir_exit;
+ }
+
+ rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
+ cifs_put_tlink(tlink);
+
+ if (!rc) {
+ spin_lock(&d_inode(direntry)->i_lock);
+ i_size_write(d_inode(direntry), 0);
+ clear_nlink(d_inode(direntry));
+ spin_unlock(&d_inode(direntry)->i_lock);
+ }
+
+ cifsInode = CIFS_I(d_inode(direntry));
+ /* force revalidate to go get info when needed */
+ cifsInode->time = 0;
+
+ cifsInode = CIFS_I(inode);
+ /*
+ * Force revalidate to get parent dir info when needed since cached
+ * attributes are invalid now.
+ */
+ cifsInode->time = 0;
+
+ d_inode(direntry)->i_ctime = inode->i_ctime = inode->i_mtime =
+ current_time(inode);
+
+rmdir_exit:
+ kfree(full_path);
+ free_xid(xid);
+ return rc;
+}
+
+static int
+cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
+ const char *from_path, struct dentry *to_dentry,
+ const char *to_path)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ int oplock, rc;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->rename)
+ return -ENOSYS;
+
+ /* try path-based rename first */
+ rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb);
+
+ /*
+ * Don't bother with rename by filehandle unless file is busy and
+ * source. Note that cross directory moves do not work with
+ * rename by filehandle to various Windows servers.
+ */
+ if (rc == 0 || rc != -EBUSY)
+ goto do_rename_exit;
+
+ /* Don't fall back to using SMB on SMB 2+ mount */
+ if (server->vals->protocol_id != 0)
+ goto do_rename_exit;
+
+ /* open-file renames don't work across directories */
+ if (to_dentry->d_parent != from_dentry->d_parent)
+ goto do_rename_exit;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ /* open the file to be renamed -- we need DELETE perms */
+ oparms.desired_access = DELETE;
+ oparms.create_options = CREATE_NOT_DIR;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = from_path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc == 0) {
+ rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid,
+ (const char *) to_dentry->d_name.name,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+ CIFSSMBClose(xid, tcon, fid.netfid);
+ }
+do_rename_exit:
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+int
+cifs_rename2(struct inode *source_dir, struct dentry *source_dentry,
+ struct inode *target_dir, struct dentry *target_dentry,
+ unsigned int flags)
+{
+ char *from_name = NULL;
+ char *to_name = NULL;
+ struct cifs_sb_info *cifs_sb;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
+ FILE_UNIX_BASIC_INFO *info_buf_target;
+ unsigned int xid;
+ int rc, tmprc;
+
+ if (flags & ~RENAME_NOREPLACE)
+ return -EINVAL;
+
+ cifs_sb = CIFS_SB(source_dir->i_sb);
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ /*
+ * we already have the rename sem so we do not need to
+ * grab it again here to protect the path integrity
+ */
+ from_name = build_path_from_dentry(source_dentry);
+ if (from_name == NULL) {
+ rc = -ENOMEM;
+ goto cifs_rename_exit;
+ }
+
+ to_name = build_path_from_dentry(target_dentry);
+ if (to_name == NULL) {
+ rc = -ENOMEM;
+ goto cifs_rename_exit;
+ }
+
+ rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
+ to_name);
+
+ /*
+ * No-replace is the natural behavior for CIFS, so skip unlink hacks.
+ */
+ if (flags & RENAME_NOREPLACE)
+ goto cifs_rename_exit;
+
+ if (rc == -EEXIST && tcon->unix_ext) {
+ /*
+ * Are src and dst hardlinks of same inode? We can only tell
+ * with unix extensions enabled.
+ */
+ info_buf_source =
+ kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO),
+ GFP_KERNEL);
+ if (info_buf_source == NULL) {
+ rc = -ENOMEM;
+ goto cifs_rename_exit;
+ }
+
+ info_buf_target = info_buf_source + 1;
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name,
+ info_buf_source,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (tmprc != 0)
+ goto unlink_target;
+
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name,
+ info_buf_target,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+
+ if (tmprc == 0 && (info_buf_source->UniqueId ==
+ info_buf_target->UniqueId)) {
+ /* same file, POSIX says that this is a noop */
+ rc = 0;
+ goto cifs_rename_exit;
+ }
+ }
+ /*
+ * else ... BB we could add the same check for Windows by
+ * checking the UniqueId via FILE_INTERNAL_INFO
+ */
+
+unlink_target:
+ /* Try unlinking the target dentry if it's not negative */
+ if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) {
+ if (d_is_dir(target_dentry))
+ tmprc = cifs_rmdir(target_dir, target_dentry);
+ else
+ tmprc = cifs_unlink(target_dir, target_dentry);
+ if (tmprc)
+ goto cifs_rename_exit;
+ rc = cifs_do_rename(xid, source_dentry, from_name,
+ target_dentry, to_name);
+ }
+
+ /* force revalidate to go get info when needed */
+ CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
+
+ source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime =
+ target_dir->i_mtime = current_time(source_dir);
+
+cifs_rename_exit:
+ kfree(info_buf_source);
+ kfree(from_name);
+ kfree(to_name);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static bool
+cifs_inode_needs_reval(struct inode *inode)
+{
+ struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ if (cifs_i->time == 0)
+ return true;
+
+ if (CIFS_CACHE_READ(cifs_i))
+ return false;
+
+ if (!lookupCacheEnabled)
+ return true;
+
+ if (!cifs_sb->actimeo)
+ return true;
+
+ if (!time_in_range(jiffies, cifs_i->time,
+ cifs_i->time + cifs_sb->actimeo))
+ return true;
+
+ /* hardlinked files w/ noserverino get "special" treatment */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
+ S_ISREG(inode->i_mode) && inode->i_nlink != 1)
+ return true;
+
+ return false;
+}
+
+/*
+ * Zap the cache. Called when invalid_mapping flag is set.
+ */
+int
+cifs_invalidate_mapping(struct inode *inode)
+{
+ int rc = 0;
+
+ if (inode->i_mapping && inode->i_mapping->nrpages != 0) {
+ rc = invalidate_inode_pages2(inode->i_mapping);
+ if (rc)
+ cifs_dbg(VFS, "%s: could not invalidate inode %p\n",
+ __func__, inode);
+ }
+
+ cifs_fscache_reset_inode_cookie(inode);
+ return rc;
+}
+
+/**
+ * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks
+ * @word: long word containing the bit lock
+ */
+static int
+cifs_wait_bit_killable(struct wait_bit_key *key, int mode)
+{
+ freezable_schedule_unsafe();
+ if (signal_pending_state(mode, current))
+ return -ERESTARTSYS;
+ return 0;
+}
+
+int
+cifs_revalidate_mapping(struct inode *inode)
+{
+ int rc;
+ unsigned long *flags = &CIFS_I(inode)->flags;
+
+ rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable,
+ TASK_KILLABLE);
+ if (rc)
+ return rc;
+
+ if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) {
+ rc = cifs_invalidate_mapping(inode);
+ if (rc)
+ set_bit(CIFS_INO_INVALID_MAPPING, flags);
+ }
+
+ clear_bit_unlock(CIFS_INO_LOCK, flags);
+ smp_mb__after_atomic();
+ wake_up_bit(flags, CIFS_INO_LOCK);
+
+ return rc;
+}
+
+int
+cifs_zap_mapping(struct inode *inode)
+{
+ set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags);
+ return cifs_revalidate_mapping(inode);
+}
+
+int cifs_revalidate_file_attr(struct file *filp)
+{
+ int rc = 0;
+ struct inode *inode = file_inode(filp);
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data;
+
+ if (!cifs_inode_needs_reval(inode))
+ return rc;
+
+ if (tlink_tcon(cfile->tlink)->unix_ext)
+ rc = cifs_get_file_info_unix(filp);
+ else
+ rc = cifs_get_file_info(filp);
+
+ return rc;
+}
+
+int cifs_revalidate_dentry_attr(struct dentry *dentry)
+{
+ unsigned int xid;
+ int rc = 0;
+ struct inode *inode = d_inode(dentry);
+ struct super_block *sb = dentry->d_sb;
+ char *full_path = NULL;
+ int count = 0;
+
+ if (inode == NULL)
+ return -ENOENT;
+
+ if (!cifs_inode_needs_reval(inode))
+ return rc;
+
+ xid = get_xid();
+
+ /* can not safely grab the rename sem here if rename calls revalidate
+ since that would deadlock */
+ full_path = build_path_from_dentry(dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n",
+ full_path, inode, inode->i_count.counter,
+ dentry, cifs_get_time(dentry), jiffies);
+
+again:
+ if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
+ rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
+ else
+ rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
+ xid, NULL);
+ if (rc == -EAGAIN && count++ < 10)
+ goto again;
+out:
+ kfree(full_path);
+ free_xid(xid);
+
+ return rc;
+}
+
+int cifs_revalidate_file(struct file *filp)
+{
+ int rc;
+ struct inode *inode = file_inode(filp);
+
+ rc = cifs_revalidate_file_attr(filp);
+ if (rc)
+ return rc;
+
+ return cifs_revalidate_mapping(inode);
+}
+
+/* revalidate a dentry's inode attributes */
+int cifs_revalidate_dentry(struct dentry *dentry)
+{
+ int rc;
+ struct inode *inode = d_inode(dentry);
+
+ rc = cifs_revalidate_dentry_attr(dentry);
+ if (rc)
+ return rc;
+
+ return cifs_revalidate_mapping(inode);
+}
+
+int cifs_getattr(const struct path *path, struct kstat *stat,
+ u32 request_mask, unsigned int flags)
+{
+ struct dentry *dentry = path->dentry;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct inode *inode = d_inode(dentry);
+ int rc;
+
+ /*
+ * We need to be sure that all dirty pages are written and the server
+ * has actual ctime, mtime and file length.
+ */
+ if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
+ inode->i_mapping->nrpages != 0) {
+ rc = filemap_fdatawait(inode->i_mapping);
+ if (rc) {
+ mapping_set_error(inode->i_mapping, rc);
+ return rc;
+ }
+ }
+
+ rc = cifs_revalidate_dentry_attr(dentry);
+ if (rc)
+ return rc;
+
+ generic_fillattr(inode, stat);
+ stat->blksize = CIFS_MAX_MSGSIZE;
+ stat->ino = CIFS_I(inode)->uniqueid;
+
+ /* old CIFS Unix Extensions doesn't return create time */
+ if (CIFS_I(inode)->createtime) {
+ stat->result_mask |= STATX_BTIME;
+ stat->btime =
+ cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime));
+ }
+
+ stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED);
+ if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED)
+ stat->attributes |= STATX_ATTR_COMPRESSED;
+ if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED)
+ stat->attributes |= STATX_ATTR_ENCRYPTED;
+
+ /*
+ * If on a multiuser mount without unix extensions or cifsacl being
+ * enabled, and the admin hasn't overridden them, set the ownership
+ * to the fsuid/fsgid of the current process.
+ */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) &&
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
+ !tcon->unix_ext) {
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID))
+ stat->uid = current_fsuid();
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID))
+ stat->gid = current_fsgid();
+ }
+ return rc;
+}
+
+static int cifs_truncate_page(struct address_space *mapping, loff_t from)
+{
+ pgoff_t index = from >> PAGE_SHIFT;
+ unsigned offset = from & (PAGE_SIZE - 1);
+ struct page *page;
+ int rc = 0;
+
+ page = grab_cache_page(mapping, index);
+ if (!page)
+ return -ENOMEM;
+
+ zero_user_segment(page, offset, PAGE_SIZE);
+ unlock_page(page);
+ put_page(page);
+ return rc;
+}
+
+static void cifs_setsize(struct inode *inode, loff_t offset)
+{
+ struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+
+ spin_lock(&inode->i_lock);
+ i_size_write(inode, offset);
+ spin_unlock(&inode->i_lock);
+
+ /* Cached inode must be refreshed on truncate */
+ cifs_i->time = 0;
+ truncate_pagecache(inode, offset);
+}
+
+static int
+cifs_set_file_size(struct inode *inode, struct iattr *attrs,
+ unsigned int xid, char *full_path)
+{
+ int rc;
+ struct cifsFileInfo *open_file;
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = NULL;
+ struct cifs_tcon *tcon = NULL;
+ struct TCP_Server_Info *server;
+
+ /*
+ * To avoid spurious oplock breaks from server, in the case of
+ * inodes that we already have open, avoid doing path based
+ * setting of file size if we can do it by handle.
+ * This keeps our caching token (oplock) and avoids timeouts
+ * when the local oplock break takes longer to flush
+ * writebehind data than the SMB timeout for the SetPathInfo
+ * request would allow
+ */
+ open_file = find_writable_file(cifsInode, true);
+ if (open_file) {
+ tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+ if (server->ops->set_file_size)
+ rc = server->ops->set_file_size(xid, tcon, open_file,
+ attrs->ia_size, false);
+ else
+ rc = -ENOSYS;
+ cifsFileInfo_put(open_file);
+ cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc);
+ } else
+ rc = -EINVAL;
+
+ if (!rc)
+ goto set_size_out;
+
+ if (tcon == NULL) {
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+ }
+
+ /*
+ * Set file size by pathname rather than by handle either because no
+ * valid, writeable file handle for it was found or because there was
+ * an error setting it by handle.
+ */
+ if (server->ops->set_path_size)
+ rc = server->ops->set_path_size(xid, tcon, full_path,
+ attrs->ia_size, cifs_sb, false);
+ else
+ rc = -ENOSYS;
+ cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc);
+
+ if (tlink)
+ cifs_put_tlink(tlink);
+
+set_size_out:
+ if (rc == 0) {
+ cifsInode->server_eof = attrs->ia_size;
+ cifs_setsize(inode, attrs->ia_size);
+
+ /*
+ * The man page of truncate says if the size changed,
+ * then the st_ctime and st_mtime fields for the file
+ * are updated.
+ */
+ attrs->ia_ctime = attrs->ia_mtime = current_time(inode);
+ attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME;
+
+ cifs_truncate_page(inode->i_mapping, inode->i_size);
+ }
+
+ return rc;
+}
+
+static int
+cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
+{
+ int rc;
+ unsigned int xid;
+ char *full_path = NULL;
+ struct inode *inode = d_inode(direntry);
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ struct cifs_unix_set_info_args *args = NULL;
+ struct cifsFileInfo *open_file;
+
+ cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n",
+ direntry, attrs->ia_valid);
+
+ xid = get_xid();
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
+ attrs->ia_valid |= ATTR_FORCE;
+
+ rc = setattr_prepare(direntry, attrs);
+ if (rc < 0)
+ goto out;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Attempt to flush data before changing attributes. We need to do
+ * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
+ * ownership or mode then we may also need to do this. Here, we take
+ * the safe way out and just do the flush on all setattr requests. If
+ * the flush returns error, store it to report later and continue.
+ *
+ * BB: This should be smarter. Why bother flushing pages that
+ * will be truncated anyway? Also, should we error out here if
+ * the flush returns error?
+ */
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (is_interrupt_error(rc)) {
+ rc = -ERESTARTSYS;
+ goto out;
+ }
+
+ mapping_set_error(inode->i_mapping, rc);
+ rc = 0;
+
+ if (attrs->ia_valid & ATTR_SIZE) {
+ rc = cifs_set_file_size(inode, attrs, xid, full_path);
+ if (rc != 0)
+ goto out;
+ }
+
+ /* skip mode change if it's just for clearing setuid/setgid */
+ if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ attrs->ia_valid &= ~ATTR_MODE;
+
+ args = kmalloc(sizeof(*args), GFP_KERNEL);
+ if (args == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* set up the struct */
+ if (attrs->ia_valid & ATTR_MODE)
+ args->mode = attrs->ia_mode;
+ else
+ args->mode = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_UID)
+ args->uid = attrs->ia_uid;
+ else
+ args->uid = INVALID_UID; /* no change */
+
+ if (attrs->ia_valid & ATTR_GID)
+ args->gid = attrs->ia_gid;
+ else
+ args->gid = INVALID_GID; /* no change */
+
+ if (attrs->ia_valid & ATTR_ATIME)
+ args->atime = cifs_UnixTimeToNT(attrs->ia_atime);
+ else
+ args->atime = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_MTIME)
+ args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime);
+ else
+ args->mtime = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_CTIME)
+ args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime);
+ else
+ args->ctime = NO_CHANGE_64;
+
+ args->device = 0;
+ open_file = find_writable_file(cifsInode, true);
+ if (open_file) {
+ u16 nfid = open_file->fid.netfid;
+ u32 npid = open_file->pid;
+ pTcon = tlink_tcon(open_file->tlink);
+ rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
+ cifsFileInfo_put(open_file);
+ } else {
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ goto out;
+ }
+ pTcon = tlink_tcon(tlink);
+ rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ cifs_put_tlink(tlink);
+ }
+
+ if (rc)
+ goto out;
+
+ if ((attrs->ia_valid & ATTR_SIZE) &&
+ attrs->ia_size != i_size_read(inode))
+ truncate_setsize(inode, attrs->ia_size);
+
+ setattr_copy(inode, attrs);
+ mark_inode_dirty(inode);
+
+ /* force revalidate when any of these times are set since some
+ of the fs types (eg ext3, fat) do not have fine enough
+ time granularity to match protocol, and we do not have a
+ a way (yet) to query the server fs's time granularity (and
+ whether it rounds times down).
+ */
+ if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME))
+ cifsInode->time = 0;
+out:
+ kfree(args);
+ kfree(full_path);
+ free_xid(xid);
+ return rc;
+}
+
+static int
+cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
+{
+ unsigned int xid;
+ kuid_t uid = INVALID_UID;
+ kgid_t gid = INVALID_GID;
+ struct inode *inode = d_inode(direntry);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ char *full_path = NULL;
+ int rc = -EACCES;
+ __u32 dosattr = 0;
+ __u64 mode = NO_CHANGE_64;
+
+ xid = get_xid();
+
+ cifs_dbg(FYI, "setattr on file %pd attrs->iavalid 0x%x\n",
+ direntry, attrs->ia_valid);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
+ attrs->ia_valid |= ATTR_FORCE;
+
+ rc = setattr_prepare(direntry, attrs);
+ if (rc < 0) {
+ free_xid(xid);
+ return rc;
+ }
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * Attempt to flush data before changing attributes. We need to do
+ * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
+ * ownership or mode then we may also need to do this. Here, we take
+ * the safe way out and just do the flush on all setattr requests. If
+ * the flush returns error, store it to report later and continue.
+ *
+ * BB: This should be smarter. Why bother flushing pages that
+ * will be truncated anyway? Also, should we error out here if
+ * the flush returns error?
+ */
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (is_interrupt_error(rc)) {
+ rc = -ERESTARTSYS;
+ goto cifs_setattr_exit;
+ }
+
+ mapping_set_error(inode->i_mapping, rc);
+ rc = 0;
+
+ if (attrs->ia_valid & ATTR_SIZE) {
+ rc = cifs_set_file_size(inode, attrs, xid, full_path);
+ if (rc != 0)
+ goto cifs_setattr_exit;
+ }
+
+ if (attrs->ia_valid & ATTR_UID)
+ uid = attrs->ia_uid;
+
+ if (attrs->ia_valid & ATTR_GID)
+ gid = attrs->ia_gid;
+
+#ifdef CONFIG_CIFS_ACL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ if (uid_valid(uid) || gid_valid(gid)) {
+ rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64,
+ uid, gid);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Setting id failed with error: %d\n",
+ __func__, rc);
+ goto cifs_setattr_exit;
+ }
+ }
+ } else
+#endif /* CONFIG_CIFS_ACL */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID))
+ attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
+
+ /* skip mode change if it's just for clearing setuid/setgid */
+ if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ attrs->ia_valid &= ~ATTR_MODE;
+
+ if (attrs->ia_valid & ATTR_MODE) {
+ mode = attrs->ia_mode;
+ rc = 0;
+#ifdef CONFIG_CIFS_ACL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ rc = id_mode_to_cifs_acl(inode, full_path, mode,
+ INVALID_UID, INVALID_GID);
+ if (rc) {
+ cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n",
+ __func__, rc);
+ goto cifs_setattr_exit;
+ }
+ } else
+#endif /* CONFIG_CIFS_ACL */
+ if (((mode & S_IWUGO) == 0) &&
+ (cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
+
+ dosattr = cifsInode->cifsAttrs | ATTR_READONLY;
+
+ /* fix up mode if we're not using dynperm */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ attrs->ia_mode = inode->i_mode & ~S_IWUGO;
+ } else if ((mode & S_IWUGO) &&
+ (cifsInode->cifsAttrs & ATTR_READONLY)) {
+
+ dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY;
+ /* Attributes of 0 are ignored */
+ if (dosattr == 0)
+ dosattr |= ATTR_NORMAL;
+
+ /* reset local inode permissions to normal */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
+ attrs->ia_mode &= ~(S_IALLUGO);
+ if (S_ISDIR(inode->i_mode))
+ attrs->ia_mode |=
+ cifs_sb->mnt_dir_mode;
+ else
+ attrs->ia_mode |=
+ cifs_sb->mnt_file_mode;
+ }
+ } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
+ /* ignore mode change - ATTR_READONLY hasn't changed */
+ attrs->ia_valid &= ~ATTR_MODE;
+ }
+ }
+
+ if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) ||
+ ((attrs->ia_valid & ATTR_MODE) && dosattr)) {
+ rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
+ /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */
+
+ /* Even if error on time set, no sense failing the call if
+ the server would set the time to a reasonable value anyway,
+ and this check ensures that we are not being called from
+ sys_utimes in which case we ought to fail the call back to
+ the user when the server rejects the call */
+ if ((rc) && (attrs->ia_valid &
+ (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
+ rc = 0;
+ }
+
+ /* do not need local check to inode_check_ok since the server does
+ that */
+ if (rc)
+ goto cifs_setattr_exit;
+
+ if ((attrs->ia_valid & ATTR_SIZE) &&
+ attrs->ia_size != i_size_read(inode))
+ truncate_setsize(inode, attrs->ia_size);
+
+ setattr_copy(inode, attrs);
+ mark_inode_dirty(inode);
+
+cifs_setattr_exit:
+ kfree(full_path);
+ free_xid(xid);
+ return rc;
+}
+
+int
+cifs_setattr(struct dentry *direntry, struct iattr *attrs)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+ struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);
+ int rc, retries = 0;
+
+ do {
+ if (pTcon->unix_ext)
+ rc = cifs_setattr_unix(direntry, attrs);
+ else
+ rc = cifs_setattr_nounix(direntry, attrs);
+ retries++;
+ } while (is_retryable_error(rc) && retries < 2);
+
+ /* BB: add cifs_setattr_legacy for really old servers */
+ return rc;
+}
+
+#if 0
+void cifs_delete_inode(struct inode *inode)
+{
+ cifs_dbg(FYI, "In cifs_delete_inode, inode = 0x%p\n", inode);
+ /* may have to add back in if and when safe distributed caching of
+ directories added e.g. via FindNotify */
+}
+#endif
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
new file mode 100644
index 000000000..54f32f914
--- /dev/null
+++ b/fs/cifs/ioctl.c
@@ -0,0 +1,236 @@
+/*
+ * fs/cifs/ioctl.c
+ *
+ * vfs operations that deal with io control
+ *
+ * Copyright (C) International Business Machines Corp., 2005,2013
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+#include "cifs_ioctl.h"
+#include <linux/btrfs.h>
+
+static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
+ unsigned long srcfd)
+{
+ int rc;
+ struct fd src_file;
+ struct inode *src_inode;
+
+ cifs_dbg(FYI, "ioctl copychunk range\n");
+ /* the destination must be opened for writing */
+ if (!(dst_file->f_mode & FMODE_WRITE)) {
+ cifs_dbg(FYI, "file target not open for write\n");
+ return -EINVAL;
+ }
+
+ /* check if target volume is readonly and take reference */
+ rc = mnt_want_write_file(dst_file);
+ if (rc) {
+ cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
+ return rc;
+ }
+
+ src_file = fdget(srcfd);
+ if (!src_file.file) {
+ rc = -EBADF;
+ goto out_drop_write;
+ }
+
+ if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
+ rc = -EBADF;
+ cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
+ goto out_fput;
+ }
+
+ src_inode = file_inode(src_file.file);
+ rc = -EINVAL;
+ if (S_ISDIR(src_inode->i_mode))
+ goto out_fput;
+
+ rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0,
+ src_inode->i_size, 0);
+ if (rc > 0)
+ rc = 0;
+out_fput:
+ fdput(src_file);
+out_drop_write:
+ mnt_drop_write_file(dst_file);
+ return rc;
+}
+
+static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
+ void __user *arg)
+{
+ int rc = 0;
+ struct smb_mnt_fs_info *fsinf;
+
+ fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);
+ if (fsinf == NULL)
+ return -ENOMEM;
+
+ fsinf->version = 1;
+ fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
+ fsinf->device_characteristics =
+ le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
+ fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
+ fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);
+ fsinf->max_path_component =
+ le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
+ fsinf->vol_serial_number = tcon->vol_serial_number;
+ fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);
+ fsinf->share_flags = tcon->share_flags;
+ fsinf->share_caps = le32_to_cpu(tcon->capabilities);
+ fsinf->sector_flags = tcon->ss_flags;
+ fsinf->optimal_sector_size = tcon->perf_sector_size;
+ fsinf->max_bytes_chunk = tcon->max_bytes_chunk;
+ fsinf->maximal_access = tcon->maximal_access;
+ fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+ if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))
+ rc = -EFAULT;
+
+ kfree(fsinf);
+ return rc;
+}
+
+long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
+{
+ struct inode *inode = file_inode(filep);
+ int rc = -ENOTTY; /* strange error - but the precedent */
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsFileInfo *pSMBFile = filep->private_data;
+ struct cifs_tcon *tcon;
+ __u64 ExtAttrBits = 0;
+ __u64 caps;
+
+ xid = get_xid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
+ switch (command) {
+ case FS_IOC_GETFLAGS:
+ if (pSMBFile == NULL)
+ break;
+ tcon = tlink_tcon(pSMBFile->tlink);
+ caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
+#ifdef CONFIG_CIFS_POSIX
+ if (CIFS_UNIX_EXTATTR_CAP & caps) {
+ __u64 ExtAttrMask = 0;
+ rc = CIFSGetExtAttr(xid, tcon,
+ pSMBFile->fid.netfid,
+ &ExtAttrBits, &ExtAttrMask);
+ if (rc == 0)
+ rc = put_user(ExtAttrBits &
+ FS_FL_USER_VISIBLE,
+ (int __user *)arg);
+ if (rc != EOPNOTSUPP)
+ break;
+ }
+#endif /* CONFIG_CIFS_POSIX */
+ rc = 0;
+ if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
+ /* add in the compressed bit */
+ ExtAttrBits = FS_COMPR_FL;
+ rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
+ (int __user *)arg);
+ }
+ break;
+ case FS_IOC_SETFLAGS:
+ if (pSMBFile == NULL)
+ break;
+ tcon = tlink_tcon(pSMBFile->tlink);
+ caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+ if (get_user(ExtAttrBits, (int __user *)arg)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /*
+ * if (CIFS_UNIX_EXTATTR_CAP & caps)
+ * rc = CIFSSetExtAttr(xid, tcon,
+ * pSMBFile->fid.netfid,
+ * extAttrBits,
+ * &ExtAttrMask);
+ * if (rc != EOPNOTSUPP)
+ * break;
+ */
+
+ /* Currently only flag we can set is compressed flag */
+ if ((ExtAttrBits & FS_COMPR_FL) == 0)
+ break;
+
+ /* Try to set compress flag */
+ if (tcon->ses->server->ops->set_compression) {
+ rc = tcon->ses->server->ops->set_compression(
+ xid, tcon, pSMBFile);
+ cifs_dbg(FYI, "set compress flag rc %d\n", rc);
+ }
+ break;
+ case CIFS_IOC_COPYCHUNK_FILE:
+ rc = cifs_ioctl_copychunk(xid, filep, arg);
+ break;
+ case CIFS_IOC_SET_INTEGRITY:
+ if (pSMBFile == NULL)
+ break;
+ tcon = tlink_tcon(pSMBFile->tlink);
+ if (tcon->ses->server->ops->set_integrity)
+ rc = tcon->ses->server->ops->set_integrity(xid,
+ tcon, pSMBFile);
+ else
+ rc = -EOPNOTSUPP;
+ break;
+ case CIFS_IOC_GET_MNT_INFO:
+ if (pSMBFile == NULL)
+ break;
+ tcon = tlink_tcon(pSMBFile->tlink);
+ rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
+ break;
+ case CIFS_ENUMERATE_SNAPSHOTS:
+ if (pSMBFile == NULL)
+ break;
+ if (arg == 0) {
+ rc = -EINVAL;
+ goto cifs_ioc_exit;
+ }
+ tcon = tlink_tcon(pSMBFile->tlink);
+ if (tcon->ses->server->ops->enum_snapshots)
+ rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
+ pSMBFile, (void __user *)arg);
+ else
+ rc = -EOPNOTSUPP;
+ break;
+ default:
+ cifs_dbg(FYI, "unsupported ioctl\n");
+ break;
+ }
+cifs_ioc_exit:
+ free_xid(xid);
+ return rc;
+}
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
new file mode 100644
index 000000000..5b1c33d92
--- /dev/null
+++ b/fs/cifs/link.c
@@ -0,0 +1,729 @@
+/*
+ * fs/cifs/link.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/namei.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "smb2proto.h"
+
+/*
+ * M-F Symlink Functions - Begin
+ */
+
+#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
+#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
+#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
+#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
+#define CIFS_MF_SYMLINK_FILE_SIZE \
+ (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
+
+#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
+#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n"
+#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash
+
+static int
+symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
+{
+ int rc;
+ struct crypto_shash *md5 = NULL;
+ struct sdesc *sdescmd5 = NULL;
+
+ rc = cifs_alloc_hash("md5", &md5, &sdescmd5);
+ if (rc)
+ goto symlink_hash_err;
+
+ rc = crypto_shash_init(&sdescmd5->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__);
+ goto symlink_hash_err;
+ }
+ rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
+ goto symlink_hash_err;
+ }
+ rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
+
+symlink_hash_err:
+ cifs_free_hash(&md5, &sdescmd5);
+ return rc;
+}
+
+static int
+parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
+ char **_link_str)
+{
+ int rc;
+ unsigned int link_len;
+ const char *md5_str1;
+ const char *link_str;
+ u8 md5_hash[16];
+ char md5_str2[34];
+
+ if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
+ return -EINVAL;
+
+ md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
+ link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
+
+ rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
+ if (rc != 1)
+ return -EINVAL;
+
+ if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
+ return -EINVAL;
+
+ rc = symlink_hash(link_len, link_str, md5_hash);
+ if (rc) {
+ cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
+ return rc;
+ }
+
+ snprintf(md5_str2, sizeof(md5_str2),
+ CIFS_MF_SYMLINK_MD5_FORMAT,
+ CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
+
+ if (strncmp(md5_str1, md5_str2, 17) != 0)
+ return -EINVAL;
+
+ if (_link_str) {
+ *_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
+ if (!*_link_str)
+ return -ENOMEM;
+ }
+
+ *_link_len = link_len;
+ return 0;
+}
+
+static int
+format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
+{
+ int rc;
+ unsigned int link_len;
+ unsigned int ofs;
+ u8 md5_hash[16];
+
+ if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
+ return -EINVAL;
+
+ link_len = strlen(link_str);
+
+ if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
+ return -ENAMETOOLONG;
+
+ rc = symlink_hash(link_len, link_str, md5_hash);
+ if (rc) {
+ cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
+ return rc;
+ }
+
+ snprintf(buf, buf_len,
+ CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
+ link_len,
+ CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
+
+ ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
+ memcpy(buf + ofs, link_str, link_len);
+
+ ofs += link_len;
+ if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
+ buf[ofs] = '\n';
+ ofs++;
+ }
+
+ while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
+ buf[ofs] = ' ';
+ ofs++;
+ }
+
+ return 0;
+}
+
+bool
+couldbe_mf_symlink(const struct cifs_fattr *fattr)
+{
+ if (!S_ISREG(fattr->cf_mode))
+ /* it's not a symlink */
+ return false;
+
+ if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
+ /* it's not a symlink */
+ return false;
+
+ return true;
+}
+
+static int
+create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *fromName,
+ const char *toName)
+{
+ int rc;
+ u8 *buf;
+ unsigned int bytes_written = 0;
+
+ buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
+ if (rc)
+ goto out;
+
+ if (tcon->ses->server->ops->create_mf_symlink)
+ rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon,
+ cifs_sb, fromName, buf, &bytes_written);
+ else
+ rc = -EOPNOTSUPP;
+
+ if (rc)
+ goto out;
+
+ if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
+ rc = -EIO;
+out:
+ kfree(buf);
+ return rc;
+}
+
+static int
+query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const unsigned char *path,
+ char **symlinkinfo)
+{
+ int rc;
+ u8 *buf = NULL;
+ unsigned int link_len = 0;
+ unsigned int bytes_read = 0;
+
+ buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (tcon->ses->server->ops->query_mf_symlink)
+ rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
+ cifs_sb, path, buf, &bytes_read);
+ else
+ rc = -ENOSYS;
+
+ if (rc)
+ goto out;
+
+ if (bytes_read == 0) { /* not a symlink */
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo);
+out:
+ kfree(buf);
+ return rc;
+}
+
+int
+check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
+ const unsigned char *path)
+{
+ int rc;
+ u8 *buf = NULL;
+ unsigned int link_len = 0;
+ unsigned int bytes_read = 0;
+
+ if (!couldbe_mf_symlink(fattr))
+ /* it's not a symlink */
+ return 0;
+
+ buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (tcon->ses->server->ops->query_mf_symlink)
+ rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
+ cifs_sb, path, buf, &bytes_read);
+ else
+ rc = -ENOSYS;
+
+ if (rc)
+ goto out;
+
+ if (bytes_read == 0) /* not a symlink */
+ goto out;
+
+ rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL);
+ if (rc == -EINVAL) {
+ /* it's not a symlink */
+ rc = 0;
+ goto out;
+ }
+
+ if (rc != 0)
+ goto out;
+
+ /* it is a symlink */
+ fattr->cf_eof = link_len;
+ fattr->cf_mode &= ~S_IFMT;
+ fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+ fattr->cf_dtype = DT_LNK;
+out:
+ kfree(buf);
+ return rc;
+}
+
+/*
+ * SMB 1.0 Protocol specific functions
+ */
+
+int
+cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const unsigned char *path,
+ char *pbuf, unsigned int *pbytes_read)
+{
+ int rc;
+ int oplock = 0;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ int buf_type = CIFS_NO_BUFFER;
+ FILE_ALL_INFO file_info;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = GENERIC_READ;
+ oparms.create_options = CREATE_NOT_DIR;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, &file_info);
+ if (rc)
+ return rc;
+
+ if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
+ rc = -ENOENT;
+ /* it's not a symlink */
+ goto out;
+ }
+
+ io_parms.netfid = fid.netfid;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
+
+ rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
+out:
+ CIFSSMBClose(xid, tcon, fid.netfid);
+ return rc;
+}
+
+int
+cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const unsigned char *path,
+ char *pbuf, unsigned int *pbytes_written)
+{
+ int rc;
+ int oplock = 0;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ int create_options = CREATE_NOT_DIR;
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = GENERIC_WRITE;
+ oparms.create_options = create_options;
+ oparms.disposition = FILE_CREATE;
+ oparms.path = path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc)
+ return rc;
+
+ io_parms.netfid = fid.netfid;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
+
+ rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf);
+ CIFSSMBClose(xid, tcon, fid.netfid);
+ return rc;
+}
+
+/*
+ * SMB 2.1/SMB3 Protocol specific functions
+ */
+int
+smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const unsigned char *path,
+ char *pbuf, unsigned int *pbytes_read)
+{
+ int rc;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ int buf_type = CIFS_NO_BUFFER;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct smb2_file_all_info *pfile_info = NULL;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = GENERIC_READ;
+ oparms.create_options = CREATE_NOT_DIR;
+ if (backup_cred(cifs_sb))
+ oparms.create_options |= CREATE_OPEN_BACKUP_INTENT;
+ oparms.disposition = FILE_OPEN;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (utf16_path == NULL)
+ return -ENOMEM;
+
+ pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+ GFP_KERNEL);
+
+ if (pfile_info == NULL) {
+ kfree(utf16_path);
+ return -ENOMEM;
+ }
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
+ NULL);
+ if (rc)
+ goto qmf_out_open_fail;
+
+ if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
+ /* it's not a symlink */
+ rc = -ENOENT; /* Is there a better rc to return? */
+ goto qmf_out;
+ }
+
+ io_parms.netfid = fid.netfid;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
+ io_parms.persistent_fid = fid.persistent_fid;
+ io_parms.volatile_fid = fid.volatile_fid;
+ rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
+qmf_out:
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+qmf_out_open_fail:
+ kfree(utf16_path);
+ kfree(pfile_info);
+ return rc;
+}
+
+int
+smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const unsigned char *path,
+ char *pbuf, unsigned int *pbytes_written)
+{
+ int rc;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ int create_options = CREATE_NOT_DIR;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct kvec iov[2];
+
+ if (backup_cred(cifs_sb))
+ create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+ cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = GENERIC_WRITE;
+ oparms.create_options = create_options;
+ oparms.disposition = FILE_CREATE;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
+ NULL);
+ if (rc) {
+ kfree(utf16_path);
+ return rc;
+ }
+
+ io_parms.netfid = fid.netfid;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
+ io_parms.persistent_fid = fid.persistent_fid;
+ io_parms.volatile_fid = fid.volatile_fid;
+
+ /* iov[0] is reserved for smb header */
+ iov[1].iov_base = pbuf;
+ iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE;
+
+ rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1);
+
+ /* Make sure we wrote all of the symlink data */
+ if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))
+ rc = -EIO;
+
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+ kfree(utf16_path);
+ return rc;
+}
+
+/*
+ * M-F Symlink Functions - End
+ */
+
+int
+cifs_hardlink(struct dentry *old_file, struct inode *inode,
+ struct dentry *direntry)
+{
+ int rc = -EACCES;
+ unsigned int xid;
+ char *from_name = NULL;
+ char *to_name = NULL;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifsInodeInfo *cifsInode;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ from_name = build_path_from_dentry(old_file);
+ to_name = build_path_from_dentry(direntry);
+ if ((from_name == NULL) || (to_name == NULL)) {
+ rc = -ENOMEM;
+ goto cifs_hl_exit;
+ }
+
+ if (tcon->unix_ext)
+ rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ else {
+ server = tcon->ses->server;
+ if (!server->ops->create_hardlink) {
+ rc = -ENOSYS;
+ goto cifs_hl_exit;
+ }
+ rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,
+ cifs_sb);
+ if ((rc == -EIO) || (rc == -EINVAL))
+ rc = -EOPNOTSUPP;
+ }
+
+ d_drop(direntry); /* force new lookup from server of target */
+
+ /*
+ * if source file is cached (oplocked) revalidate will not go to server
+ * until the file is closed or oplock broken so update nlinks locally
+ */
+ if (d_really_is_positive(old_file)) {
+ cifsInode = CIFS_I(d_inode(old_file));
+ if (rc == 0) {
+ spin_lock(&d_inode(old_file)->i_lock);
+ inc_nlink(d_inode(old_file));
+ spin_unlock(&d_inode(old_file)->i_lock);
+
+ /*
+ * parent dir timestamps will update from srv within a
+ * second, would it really be worth it to set the parent
+ * dir cifs inode time to zero to force revalidate
+ * (faster) for it too?
+ */
+ }
+ /*
+ * if not oplocked will force revalidate to get info on source
+ * file from srv. Note Samba server prior to 4.2 has bug -
+ * not updating src file ctime on hardlinks but Windows servers
+ * handle it properly
+ */
+ cifsInode->time = 0;
+
+ /*
+ * Will update parent dir timestamps from srv within a second.
+ * Would it really be worth it to set the parent dir (cifs
+ * inode) time field to zero to force revalidate on parent
+ * directory faster ie
+ *
+ * CIFS_I(inode)->time = 0;
+ */
+ }
+
+cifs_hl_exit:
+ kfree(from_name);
+ kfree(to_name);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+const char *
+cifs_get_link(struct dentry *direntry, struct inode *inode,
+ struct delayed_call *done)
+{
+ int rc = -ENOMEM;
+ unsigned int xid;
+ char *full_path = NULL;
+ char *target_path = NULL;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = NULL;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+
+ if (!direntry)
+ return ERR_PTR(-ECHILD);
+
+ xid = get_xid();
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ free_xid(xid);
+ return ERR_CAST(tlink);
+ }
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ full_path = build_path_from_dentry(direntry);
+ if (!full_path) {
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
+
+ rc = -EACCES;
+ /*
+ * First try Minshall+French Symlinks, if configured
+ * and fallback to UNIX Extensions Symlinks.
+ */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
+ rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
+ &target_path);
+
+ if (rc != 0 && server->ops->query_symlink)
+ rc = server->ops->query_symlink(xid, tcon, full_path,
+ &target_path, cifs_sb);
+
+ kfree(full_path);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ if (rc != 0) {
+ kfree(target_path);
+ return ERR_PTR(rc);
+ }
+ set_delayed_call(done, kfree_link, target_path);
+ return target_path;
+}
+
+int
+cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
+{
+ int rc = -EOPNOTSUPP;
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ char *full_path = NULL;
+ struct inode *newinode = NULL;
+
+ xid = get_xid();
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ goto symlink_exit;
+ }
+ pTcon = tlink_tcon(tlink);
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto symlink_exit;
+ }
+
+ cifs_dbg(FYI, "Full path: %s\n", full_path);
+ cifs_dbg(FYI, "symname is %s\n", symname);
+
+ /* BB what if DFS and this volume is on different share? BB */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
+ rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
+ else if (pTcon->unix_ext)
+ rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ /* else
+ rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
+ cifs_sb_target->local_nls); */
+
+ if (rc == 0) {
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&newinode, full_path, NULL,
+ inode->i_sb, xid, NULL);
+
+ if (rc != 0) {
+ cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
+ rc);
+ } else {
+ d_instantiate(direntry, newinode);
+ }
+ }
+symlink_exit:
+ kfree(full_path);
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return rc;
+}
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
new file mode 100644
index 000000000..dd67f56ea
--- /dev/null
+++ b/fs/cifs/misc.c
@@ -0,0 +1,944 @@
+/*
+ * fs/cifs/misc.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/mempool.h>
+#include <linux/vmalloc.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "smberr.h"
+#include "nterr.h"
+#include "cifs_unicode.h"
+#include "smb2pdu.h"
+
+extern mempool_t *cifs_sm_req_poolp;
+extern mempool_t *cifs_req_poolp;
+
+/* The xid serves as a useful identifier for each incoming vfs request,
+ in a similar way to the mid which is useful to track each sent smb,
+ and CurrentXid can also provide a running counter (although it
+ will eventually wrap past zero) of the total vfs operations handled
+ since the cifs fs was mounted */
+
+unsigned int
+_get_xid(void)
+{
+ unsigned int xid;
+
+ spin_lock(&GlobalMid_Lock);
+ GlobalTotalActiveXid++;
+
+ /* keep high water mark for number of simultaneous ops in filesystem */
+ if (GlobalTotalActiveXid > GlobalMaxActiveXid)
+ GlobalMaxActiveXid = GlobalTotalActiveXid;
+ if (GlobalTotalActiveXid > 65000)
+ cifs_dbg(FYI, "warning: more than 65000 requests active\n");
+ xid = GlobalCurrentXid++;
+ spin_unlock(&GlobalMid_Lock);
+ return xid;
+}
+
+void
+_free_xid(unsigned int xid)
+{
+ spin_lock(&GlobalMid_Lock);
+ /* if (GlobalTotalActiveXid == 0)
+ BUG(); */
+ GlobalTotalActiveXid--;
+ spin_unlock(&GlobalMid_Lock);
+}
+
+struct cifs_ses *
+sesInfoAlloc(void)
+{
+ struct cifs_ses *ret_buf;
+
+ ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL);
+ if (ret_buf) {
+ atomic_inc(&sesInfoAllocCount);
+ ret_buf->status = CifsNew;
+ ++ret_buf->ses_count;
+ INIT_LIST_HEAD(&ret_buf->smb_ses_list);
+ INIT_LIST_HEAD(&ret_buf->tcon_list);
+ mutex_init(&ret_buf->session_mutex);
+ spin_lock_init(&ret_buf->iface_lock);
+ }
+ return ret_buf;
+}
+
+void
+sesInfoFree(struct cifs_ses *buf_to_free)
+{
+ if (buf_to_free == NULL) {
+ cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");
+ return;
+ }
+
+ atomic_dec(&sesInfoAllocCount);
+ kfree(buf_to_free->serverOS);
+ kfree(buf_to_free->serverDomain);
+ kfree(buf_to_free->serverNOS);
+ kzfree(buf_to_free->password);
+ kfree(buf_to_free->user_name);
+ kfree(buf_to_free->domainName);
+ kzfree(buf_to_free->auth_key.response);
+ kfree(buf_to_free->iface_list);
+ kzfree(buf_to_free);
+}
+
+struct cifs_tcon *
+tconInfoAlloc(void)
+{
+ struct cifs_tcon *ret_buf;
+ ret_buf = kzalloc(sizeof(struct cifs_tcon), GFP_KERNEL);
+ if (ret_buf) {
+ atomic_inc(&tconInfoAllocCount);
+ ret_buf->tidStatus = CifsNew;
+ ++ret_buf->tc_count;
+ INIT_LIST_HEAD(&ret_buf->openFileList);
+ INIT_LIST_HEAD(&ret_buf->tcon_list);
+ spin_lock_init(&ret_buf->open_file_lock);
+ mutex_init(&ret_buf->crfid.fid_mutex);
+ ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid),
+ GFP_KERNEL);
+ spin_lock_init(&ret_buf->stat_lock);
+ }
+ return ret_buf;
+}
+
+void
+tconInfoFree(struct cifs_tcon *buf_to_free)
+{
+ if (buf_to_free == NULL) {
+ cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
+ return;
+ }
+ atomic_dec(&tconInfoAllocCount);
+ kfree(buf_to_free->nativeFileSystem);
+ kzfree(buf_to_free->password);
+ kfree(buf_to_free->crfid.fid);
+ kfree(buf_to_free);
+}
+
+struct smb_hdr *
+cifs_buf_get(void)
+{
+ struct smb_hdr *ret_buf = NULL;
+ /*
+ * SMB2 header is bigger than CIFS one - no problems to clean some
+ * more bytes for CIFS.
+ */
+ size_t buf_size = sizeof(struct smb2_sync_hdr);
+
+ /*
+ * We could use negotiated size instead of max_msgsize -
+ * but it may be more efficient to always alloc same size
+ * albeit slightly larger than necessary and maxbuffersize
+ * defaults to this and can not be bigger.
+ */
+ ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);
+
+ /* clear the first few header bytes */
+ /* for most paths, more is cleared in header_assemble */
+ memset(ret_buf, 0, buf_size + 3);
+ atomic_inc(&bufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&totBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
+
+ return ret_buf;
+}
+
+void
+cifs_buf_release(void *buf_to_free)
+{
+ if (buf_to_free == NULL) {
+ /* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/
+ return;
+ }
+ mempool_free(buf_to_free, cifs_req_poolp);
+
+ atomic_dec(&bufAllocCount);
+ return;
+}
+
+struct smb_hdr *
+cifs_small_buf_get(void)
+{
+ struct smb_hdr *ret_buf = NULL;
+
+/* We could use negotiated size instead of max_msgsize -
+ but it may be more efficient to always alloc same size
+ albeit slightly larger than necessary and maxbuffersize
+ defaults to this and can not be bigger */
+ ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS);
+ /* No need to clear memory here, cleared in header assemble */
+ /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
+ atomic_inc(&smBufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&totSmBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
+
+ return ret_buf;
+}
+
+void
+cifs_small_buf_release(void *buf_to_free)
+{
+
+ if (buf_to_free == NULL) {
+ cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n");
+ return;
+ }
+ mempool_free(buf_to_free, cifs_sm_req_poolp);
+
+ atomic_dec(&smBufAllocCount);
+ return;
+}
+
+void
+free_rsp_buf(int resp_buftype, void *rsp)
+{
+ if (resp_buftype == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(rsp);
+ else if (resp_buftype == CIFS_LARGE_BUFFER)
+ cifs_buf_release(rsp);
+}
+
+/* NB: MID can not be set if treeCon not passed in, in that
+ case it is responsbility of caller to set the mid */
+void
+header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
+ const struct cifs_tcon *treeCon, int word_count
+ /* length of fixed section (word count) in two byte units */)
+{
+ char *temp = (char *) buffer;
+
+ memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
+
+ buffer->smb_buf_length = cpu_to_be32(
+ (2 * word_count) + sizeof(struct smb_hdr) -
+ 4 /* RFC 1001 length field does not count */ +
+ 2 /* for bcc field itself */) ;
+
+ buffer->Protocol[0] = 0xFF;
+ buffer->Protocol[1] = 'S';
+ buffer->Protocol[2] = 'M';
+ buffer->Protocol[3] = 'B';
+ buffer->Command = smb_command;
+ buffer->Flags = 0x00; /* case sensitive */
+ buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
+ buffer->Pid = cpu_to_le16((__u16)current->tgid);
+ buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
+ if (treeCon) {
+ buffer->Tid = treeCon->tid;
+ if (treeCon->ses) {
+ if (treeCon->ses->capabilities & CAP_UNICODE)
+ buffer->Flags2 |= SMBFLG2_UNICODE;
+ if (treeCon->ses->capabilities & CAP_STATUS32)
+ buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+
+ /* Uid is not converted */
+ buffer->Uid = treeCon->ses->Suid;
+ buffer->Mid = get_next_mid(treeCon->ses->server);
+ }
+ if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
+ buffer->Flags2 |= SMBFLG2_DFS;
+ if (treeCon->nocase)
+ buffer->Flags |= SMBFLG_CASELESS;
+ if ((treeCon->ses) && (treeCon->ses->server))
+ if (treeCon->ses->server->sign)
+ buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+ }
+
+/* endian conversion of flags is now done just before sending */
+ buffer->WordCount = (char) word_count;
+ return;
+}
+
+static int
+check_smb_hdr(struct smb_hdr *smb)
+{
+ /* does it have the right SMB "signature" ? */
+ if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) {
+ cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n",
+ *(unsigned int *)smb->Protocol);
+ return 1;
+ }
+
+ /* if it's a response then accept */
+ if (smb->Flags & SMBFLG_RESPONSE)
+ return 0;
+
+ /* only one valid case where server sends us request */
+ if (smb->Command == SMB_COM_LOCKING_ANDX)
+ return 0;
+
+ cifs_dbg(VFS, "Server sent request, not response. mid=%u\n",
+ get_mid(smb));
+ return 1;
+}
+
+int
+checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
+{
+ struct smb_hdr *smb = (struct smb_hdr *)buf;
+ __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
+ __u32 clc_len; /* calculated length */
+ cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
+ total_read, rfclen);
+
+ /* is this frame too small to even get to a BCC? */
+ if (total_read < 2 + sizeof(struct smb_hdr)) {
+ if ((total_read >= sizeof(struct smb_hdr) - 1)
+ && (smb->Status.CifsError != 0)) {
+ /* it's an error return */
+ smb->WordCount = 0;
+ /* some error cases do not return wct and bcc */
+ return 0;
+ } else if ((total_read == sizeof(struct smb_hdr) + 1) &&
+ (smb->WordCount == 0)) {
+ char *tmp = (char *)smb;
+ /* Need to work around a bug in two servers here */
+ /* First, check if the part of bcc they sent was zero */
+ if (tmp[sizeof(struct smb_hdr)] == 0) {
+ /* some servers return only half of bcc
+ * on simple responses (wct, bcc both zero)
+ * in particular have seen this on
+ * ulogoffX and FindClose. This leaves
+ * one byte of bcc potentially unitialized
+ */
+ /* zero rest of bcc */
+ tmp[sizeof(struct smb_hdr)+1] = 0;
+ return 0;
+ }
+ cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n");
+ } else {
+ cifs_dbg(VFS, "Length less than smb header size\n");
+ }
+ return -EIO;
+ }
+
+ /* otherwise, there is enough to get to the BCC */
+ if (check_smb_hdr(smb))
+ return -EIO;
+ clc_len = smbCalcSize(smb, server);
+
+ if (4 + rfclen != total_read) {
+ cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n",
+ rfclen);
+ return -EIO;
+ }
+
+ if (4 + rfclen != clc_len) {
+ __u16 mid = get_mid(smb);
+ /* check if bcc wrapped around for large read responses */
+ if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
+ /* check if lengths match mod 64K */
+ if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
+ return 0; /* bcc wrapped */
+ }
+ cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n",
+ clc_len, 4 + rfclen, mid);
+
+ if (4 + rfclen < clc_len) {
+ cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
+ rfclen, mid);
+ return -EIO;
+ } else if (rfclen > clc_len + 512) {
+ /*
+ * Some servers (Windows XP in particular) send more
+ * data than the lengths in the SMB packet would
+ * indicate on certain calls (byte range locks and
+ * trans2 find first calls in particular). While the
+ * client can handle such a frame by ignoring the
+ * trailing data, we choose limit the amount of extra
+ * data to 512 bytes.
+ */
+ cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n",
+ rfclen, mid);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+bool
+is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
+{
+ struct smb_hdr *buf = (struct smb_hdr *)buffer;
+ struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *pCifsInode;
+ struct cifsFileInfo *netfile;
+
+ cifs_dbg(FYI, "Checking for oplock break or dnotify response\n");
+ if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
+ (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
+ struct smb_com_transaction_change_notify_rsp *pSMBr =
+ (struct smb_com_transaction_change_notify_rsp *)buf;
+ struct file_notify_information *pnotify;
+ __u32 data_offset = 0;
+ size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length);
+
+ if (get_bcc(buf) > sizeof(struct file_notify_information)) {
+ data_offset = le32_to_cpu(pSMBr->DataOffset);
+
+ if (data_offset >
+ len - sizeof(struct file_notify_information)) {
+ cifs_dbg(FYI, "invalid data_offset %u\n",
+ data_offset);
+ return true;
+ }
+ pnotify = (struct file_notify_information *)
+ ((char *)&pSMBr->hdr.Protocol + data_offset);
+ cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n",
+ pnotify->FileName, pnotify->Action);
+ /* cifs_dump_mem("Rcvd notify Data: ",buf,
+ sizeof(struct smb_hdr)+60); */
+ return true;
+ }
+ if (pSMBr->hdr.Status.CifsError) {
+ cifs_dbg(FYI, "notify err 0x%x\n",
+ pSMBr->hdr.Status.CifsError);
+ return true;
+ }
+ return false;
+ }
+ if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
+ return false;
+ if (pSMB->hdr.Flags & SMBFLG_RESPONSE) {
+ /* no sense logging error on invalid handle on oplock
+ break - harmless race between close request and oplock
+ break response is expected from time to time writing out
+ large dirty files cached on the client */
+ if ((NT_STATUS_INVALID_HANDLE) ==
+ le32_to_cpu(pSMB->hdr.Status.CifsError)) {
+ cifs_dbg(FYI, "invalid handle on oplock break\n");
+ return true;
+ } else if (ERRbadfid ==
+ le16_to_cpu(pSMB->hdr.Status.DosError.Error)) {
+ return true;
+ } else {
+ return false; /* on valid oplock brk we get "request" */
+ }
+ }
+ if (pSMB->hdr.WordCount != 8)
+ return false;
+
+ cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n",
+ pSMB->LockType, pSMB->OplockLevel);
+ if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
+ return false;
+
+ /* look up tcon based on tid & uid */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &srv->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+ list_for_each(tmp1, &ses->tcon_list) {
+ tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+ if (tcon->tid != buf->Tid)
+ continue;
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
+ spin_lock(&tcon->open_file_lock);
+ list_for_each(tmp2, &tcon->openFileList) {
+ netfile = list_entry(tmp2, struct cifsFileInfo,
+ tlist);
+ if (pSMB->Fid != netfile->fid.netfid)
+ continue;
+
+ cifs_dbg(FYI, "file id match, oplock break\n");
+ pCifsInode = CIFS_I(d_inode(netfile->dentry));
+
+ set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
+ &pCifsInode->flags);
+
+ netfile->oplock_epoch = 0;
+ netfile->oplock_level = pSMB->OplockLevel;
+ netfile->oplock_break_cancelled = false;
+ cifs_queue_oplock_break(netfile);
+
+ spin_unlock(&tcon->open_file_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+ spin_unlock(&tcon->open_file_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ cifs_dbg(FYI, "No matching file for oplock break\n");
+ return true;
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
+ return true;
+}
+
+void
+dump_smb(void *buf, int smb_buf_length)
+{
+ if (traceSMB == 0)
+ return;
+
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf,
+ smb_buf_length, true);
+}
+
+void
+cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
+{
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
+ cifs_sb->mnt_cifs_serverino_autodisabled = true;
+ cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s. This server doesn't seem to support them properly. Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n",
+ cifs_sb_master_tcon(cifs_sb)->treeName);
+ }
+}
+
+void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
+{
+ oplock &= 0xF;
+
+ if (oplock == OPLOCK_EXCLUSIVE) {
+ cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG;
+ cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
+ &cinode->vfs_inode);
+ } else if (oplock == OPLOCK_READ) {
+ cinode->oplock = CIFS_CACHE_READ_FLG;
+ cifs_dbg(FYI, "Level II Oplock granted on inode %p\n",
+ &cinode->vfs_inode);
+ } else
+ cinode->oplock = 0;
+}
+
+/*
+ * We wait for oplock breaks to be processed before we attempt to perform
+ * writes.
+ */
+int cifs_get_writer(struct cifsInodeInfo *cinode)
+{
+ int rc;
+
+start:
+ rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK,
+ TASK_KILLABLE);
+ if (rc)
+ return rc;
+
+ spin_lock(&cinode->writers_lock);
+ if (!cinode->writers)
+ set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
+ cinode->writers++;
+ /* Check to see if we have started servicing an oplock break */
+ if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) {
+ cinode->writers--;
+ if (cinode->writers == 0) {
+ clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
+ wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS);
+ }
+ spin_unlock(&cinode->writers_lock);
+ goto start;
+ }
+ spin_unlock(&cinode->writers_lock);
+ return 0;
+}
+
+void cifs_put_writer(struct cifsInodeInfo *cinode)
+{
+ spin_lock(&cinode->writers_lock);
+ cinode->writers--;
+ if (cinode->writers == 0) {
+ clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
+ wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS);
+ }
+ spin_unlock(&cinode->writers_lock);
+}
+
+/**
+ * cifs_queue_oplock_break - queue the oplock break handler for cfile
+ *
+ * This function is called from the demultiplex thread when it
+ * receives an oplock break for @cfile.
+ *
+ * Assumes the tcon->open_file_lock is held.
+ * Assumes cfile->file_info_lock is NOT held.
+ */
+void cifs_queue_oplock_break(struct cifsFileInfo *cfile)
+{
+ /*
+ * Bump the handle refcount now while we hold the
+ * open_file_lock to enforce the validity of it for the oplock
+ * break handler. The matching put is done at the end of the
+ * handler.
+ */
+ cifsFileInfo_get(cfile);
+
+ queue_work(cifsoplockd_wq, &cfile->oplock_break);
+}
+
+void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
+{
+ clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
+ wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK);
+}
+
+bool
+backup_cred(struct cifs_sb_info *cifs_sb)
+{
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
+ if (uid_eq(cifs_sb->mnt_backupuid, current_fsuid()))
+ return true;
+ }
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
+ if (in_group_p(cifs_sb->mnt_backupgid))
+ return true;
+ }
+
+ return false;
+}
+
+void
+cifs_del_pending_open(struct cifs_pending_open *open)
+{
+ spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
+ list_del(&open->olist);
+ spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
+}
+
+void
+cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
+ struct cifs_pending_open *open)
+{
+ memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
+ open->oplock = CIFS_OPLOCK_NO_CHANGE;
+ open->tlink = tlink;
+ fid->pending_open = open;
+ list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
+}
+
+void
+cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
+ struct cifs_pending_open *open)
+{
+ spin_lock(&tlink_tcon(tlink)->open_file_lock);
+ cifs_add_pending_open_locked(fid, tlink, open);
+ spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
+}
+
+/* parses DFS refferal V3 structure
+ * caller is responsible for freeing target_nodes
+ * returns:
+ * - on success - 0
+ * - on failure - errno
+ */
+int
+parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+ unsigned int *num_of_nodes,
+ struct dfs_info3_param **target_nodes,
+ const struct nls_table *nls_codepage, int remap,
+ const char *searchName, bool is_unicode)
+{
+ int i, rc = 0;
+ char *data_end;
+ struct dfs_referral_level_3 *ref;
+
+ *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
+
+ if (*num_of_nodes < 1) {
+ cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n",
+ *num_of_nodes);
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ ref = (struct dfs_referral_level_3 *) &(rsp->referrals);
+ if (ref->VersionNumber != cpu_to_le16(3)) {
+ cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
+ le16_to_cpu(ref->VersionNumber));
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ /* get the upper boundary of the resp buffer */
+ data_end = (char *)rsp + rsp_size;
+
+ cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
+ *num_of_nodes, le32_to_cpu(rsp->DFSFlags));
+
+ *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
+ GFP_KERNEL);
+ if (*target_nodes == NULL) {
+ rc = -ENOMEM;
+ goto parse_DFS_referrals_exit;
+ }
+
+ /* collect necessary data from referrals */
+ for (i = 0; i < *num_of_nodes; i++) {
+ char *temp;
+ int max_len;
+ struct dfs_info3_param *node = (*target_nodes)+i;
+
+ node->flags = le32_to_cpu(rsp->DFSFlags);
+ if (is_unicode) {
+ __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
+ GFP_KERNEL);
+ if (tmp == NULL) {
+ rc = -ENOMEM;
+ goto parse_DFS_referrals_exit;
+ }
+ cifsConvertToUTF16((__le16 *) tmp, searchName,
+ PATH_MAX, nls_codepage, remap);
+ node->path_consumed = cifs_utf16_bytes(tmp,
+ le16_to_cpu(rsp->PathConsumed),
+ nls_codepage);
+ kfree(tmp);
+ } else
+ node->path_consumed = le16_to_cpu(rsp->PathConsumed);
+
+ node->server_type = le16_to_cpu(ref->ServerType);
+ node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
+
+ /* copy DfsPath */
+ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
+ max_len = data_end - temp;
+ node->path_name = cifs_strndup_from_utf16(temp, max_len,
+ is_unicode, nls_codepage);
+ if (!node->path_name) {
+ rc = -ENOMEM;
+ goto parse_DFS_referrals_exit;
+ }
+
+ /* copy link target UNC */
+ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
+ max_len = data_end - temp;
+ node->node_name = cifs_strndup_from_utf16(temp, max_len,
+ is_unicode, nls_codepage);
+ if (!node->node_name) {
+ rc = -ENOMEM;
+ goto parse_DFS_referrals_exit;
+ }
+
+ ref++;
+ }
+
+parse_DFS_referrals_exit:
+ if (rc) {
+ free_dfs_info_array(*target_nodes, *num_of_nodes);
+ *target_nodes = NULL;
+ *num_of_nodes = 0;
+ }
+ return rc;
+}
+
+struct cifs_aio_ctx *
+cifs_aio_ctx_alloc(void)
+{
+ struct cifs_aio_ctx *ctx;
+
+ ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ INIT_LIST_HEAD(&ctx->list);
+ mutex_init(&ctx->aio_mutex);
+ init_completion(&ctx->done);
+ kref_init(&ctx->refcount);
+ return ctx;
+}
+
+void
+cifs_aio_ctx_release(struct kref *refcount)
+{
+ struct cifs_aio_ctx *ctx = container_of(refcount,
+ struct cifs_aio_ctx, refcount);
+
+ cifsFileInfo_put(ctx->cfile);
+ kvfree(ctx->bv);
+ kfree(ctx);
+}
+
+#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024)
+
+int
+setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
+{
+ ssize_t rc;
+ unsigned int cur_npages;
+ unsigned int npages = 0;
+ unsigned int i;
+ size_t len;
+ size_t count = iov_iter_count(iter);
+ unsigned int saved_len;
+ size_t start;
+ unsigned int max_pages = iov_iter_npages(iter, INT_MAX);
+ struct page **pages = NULL;
+ struct bio_vec *bv = NULL;
+
+ if (iter->type & ITER_KVEC) {
+ memcpy(&ctx->iter, iter, sizeof(struct iov_iter));
+ ctx->len = count;
+ iov_iter_advance(iter, count);
+ return 0;
+ }
+
+ if (max_pages * sizeof(struct bio_vec) <= CIFS_AIO_KMALLOC_LIMIT)
+ bv = kmalloc_array(max_pages, sizeof(struct bio_vec),
+ GFP_KERNEL);
+
+ if (!bv) {
+ bv = vmalloc(array_size(max_pages, sizeof(struct bio_vec)));
+ if (!bv)
+ return -ENOMEM;
+ }
+
+ if (max_pages * sizeof(struct page *) <= CIFS_AIO_KMALLOC_LIMIT)
+ pages = kmalloc_array(max_pages, sizeof(struct page *),
+ GFP_KERNEL);
+
+ if (!pages) {
+ pages = vmalloc(array_size(max_pages, sizeof(struct page *)));
+ if (!pages) {
+ kvfree(bv);
+ return -ENOMEM;
+ }
+ }
+
+ saved_len = count;
+
+ while (count && npages < max_pages) {
+ rc = iov_iter_get_pages(iter, pages, count, max_pages, &start);
+ if (rc < 0) {
+ cifs_dbg(VFS, "couldn't get user pages (rc=%zd)\n", rc);
+ break;
+ }
+
+ if (rc > count) {
+ cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc,
+ count);
+ break;
+ }
+
+ iov_iter_advance(iter, rc);
+ count -= rc;
+ rc += start;
+ cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE);
+
+ if (npages + cur_npages > max_pages) {
+ cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n",
+ npages + cur_npages, max_pages);
+ break;
+ }
+
+ for (i = 0; i < cur_npages; i++) {
+ len = rc > PAGE_SIZE ? PAGE_SIZE : rc;
+ bv[npages + i].bv_page = pages[i];
+ bv[npages + i].bv_offset = start;
+ bv[npages + i].bv_len = len - start;
+ rc -= len;
+ start = 0;
+ }
+
+ npages += cur_npages;
+ }
+
+ kvfree(pages);
+ ctx->bv = bv;
+ ctx->len = saved_len - count;
+ ctx->npages = npages;
+ iov_iter_bvec(&ctx->iter, ITER_BVEC | rw, ctx->bv, npages, ctx->len);
+ return 0;
+}
+
+/**
+ * cifs_alloc_hash - allocate hash and hash context together
+ *
+ * The caller has to make sure @sdesc is initialized to either NULL or
+ * a valid context. Both can be freed via cifs_free_hash().
+ */
+int
+cifs_alloc_hash(const char *name,
+ struct crypto_shash **shash, struct sdesc **sdesc)
+{
+ int rc = 0;
+ size_t size;
+
+ if (*sdesc != NULL)
+ return 0;
+
+ *shash = crypto_alloc_shash(name, 0, 0);
+ if (IS_ERR(*shash)) {
+ cifs_dbg(VFS, "could not allocate crypto %s\n", name);
+ rc = PTR_ERR(*shash);
+ *shash = NULL;
+ *sdesc = NULL;
+ return rc;
+ }
+
+ size = sizeof(struct shash_desc) + crypto_shash_descsize(*shash);
+ *sdesc = kmalloc(size, GFP_KERNEL);
+ if (*sdesc == NULL) {
+ cifs_dbg(VFS, "no memory left to allocate crypto %s\n", name);
+ crypto_free_shash(*shash);
+ *shash = NULL;
+ return -ENOMEM;
+ }
+
+ (*sdesc)->shash.tfm = *shash;
+ (*sdesc)->shash.flags = 0x0;
+ return 0;
+}
+
+/**
+ * cifs_free_hash - free hash and hash context together
+ *
+ * Freeing a NULL hash or context is safe.
+ */
+void
+cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
+{
+ kfree(*sdesc);
+ *sdesc = NULL;
+ if (*shash)
+ crypto_free_shash(*shash);
+ *shash = NULL;
+}
+
+/**
+ * rqst_page_get_length - obtain the length and offset for a page in smb_rqst
+ * Input: rqst - a smb_rqst, page - a page index for rqst
+ * Output: *len - the length for this page, *offset - the offset for this page
+ */
+void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
+ unsigned int *len, unsigned int *offset)
+{
+ *len = rqst->rq_pagesz;
+ *offset = (page == 0) ? rqst->rq_offset : 0;
+
+ if (rqst->rq_npages == 1 || page == rqst->rq_npages-1)
+ *len = rqst->rq_tailsz;
+ else if (page == 0)
+ *len = rqst->rq_pagesz - rqst->rq_offset;
+}
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
new file mode 100644
index 000000000..66c10121a
--- /dev/null
+++ b/fs/cifs/netmisc.c
@@ -0,0 +1,1011 @@
+/*
+ * fs/cifs/netmisc.c
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Error mapping routines from Samba libsmb/errormap.c
+ * Copyright (C) Andrew Tridgell 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/net.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <asm/div64.h>
+#include <asm/byteorder.h>
+#include <linux/inet.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "smberr.h"
+#include "cifs_debug.h"
+#include "nterr.h"
+
+struct smb_to_posix_error {
+ __u16 smb_err;
+ int posix_code;
+};
+
+static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
+ {ERRbadfunc, -EINVAL},
+ {ERRbadfile, -ENOENT},
+ {ERRbadpath, -ENOTDIR},
+ {ERRnofids, -EMFILE},
+ {ERRnoaccess, -EACCES},
+ {ERRbadfid, -EBADF},
+ {ERRbadmcb, -EIO},
+ {ERRnomem, -EREMOTEIO},
+ {ERRbadmem, -EFAULT},
+ {ERRbadenv, -EFAULT},
+ {ERRbadformat, -EINVAL},
+ {ERRbadaccess, -EACCES},
+ {ERRbaddata, -EIO},
+ {ERRbaddrive, -ENXIO},
+ {ERRremcd, -EACCES},
+ {ERRdiffdevice, -EXDEV},
+ {ERRnofiles, -ENOENT},
+ {ERRwriteprot, -EROFS},
+ {ERRbadshare, -EBUSY},
+ {ERRlock, -EACCES},
+ {ERRunsup, -EINVAL},
+ {ERRnosuchshare, -ENXIO},
+ {ERRfilexists, -EEXIST},
+ {ERRinvparm, -EINVAL},
+ {ERRdiskfull, -ENOSPC},
+ {ERRinvname, -ENOENT},
+ {ERRinvlevel, -EOPNOTSUPP},
+ {ERRdirnotempty, -ENOTEMPTY},
+ {ERRnotlocked, -ENOLCK},
+ {ERRcancelviolation, -ENOLCK},
+ {ERRalreadyexists, -EEXIST},
+ {ERRmoredata, -EOVERFLOW},
+ {ERReasnotsupported, -EOPNOTSUPP},
+ {ErrQuota, -EDQUOT},
+ {ErrNotALink, -ENOLINK},
+ {ERRnetlogonNotStarted, -ENOPROTOOPT},
+ {ERRsymlink, -EOPNOTSUPP},
+ {ErrTooManyLinks, -EMLINK},
+ {0, 0}
+};
+
+static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
+ {ERRerror, -EIO},
+ {ERRbadpw, -EACCES}, /* was EPERM */
+ {ERRbadtype, -EREMOTE},
+ {ERRaccess, -EACCES},
+ {ERRinvtid, -ENXIO},
+ {ERRinvnetname, -ENXIO},
+ {ERRinvdevice, -ENXIO},
+ {ERRqfull, -ENOSPC},
+ {ERRqtoobig, -ENOSPC},
+ {ERRqeof, -EIO},
+ {ERRinvpfid, -EBADF},
+ {ERRsmbcmd, -EBADRQC},
+ {ERRsrverror, -EIO},
+ {ERRbadBID, -EIO},
+ {ERRfilespecs, -EINVAL},
+ {ERRbadLink, -EIO},
+ {ERRbadpermits, -EINVAL},
+ {ERRbadPID, -ESRCH},
+ {ERRsetattrmode, -EINVAL},
+ {ERRpaused, -EHOSTDOWN},
+ {ERRmsgoff, -EHOSTDOWN},
+ {ERRnoroom, -ENOSPC},
+ {ERRrmuns, -EUSERS},
+ {ERRtimeout, -ETIME},
+ {ERRnoresource, -EREMOTEIO},
+ {ERRtoomanyuids, -EUSERS},
+ {ERRbaduid, -EACCES},
+ {ERRusempx, -EIO},
+ {ERRusestd, -EIO},
+ {ERR_NOTIFY_ENUM_DIR, -ENOBUFS},
+ {ERRnoSuchUser, -EACCES},
+/* {ERRaccountexpired, -EACCES},
+ {ERRbadclient, -EACCES},
+ {ERRbadLogonTime, -EACCES},
+ {ERRpasswordExpired, -EACCES},*/
+ {ERRaccountexpired, -EKEYEXPIRED},
+ {ERRbadclient, -EACCES},
+ {ERRbadLogonTime, -EACCES},
+ {ERRpasswordExpired, -EKEYEXPIRED},
+
+ {ERRnosupport, -EINVAL},
+ {0, 0}
+};
+
+/*
+ * Convert a string containing text IPv4 or IPv6 address to binary form.
+ *
+ * Returns 0 on failure.
+ */
+static int
+cifs_inet_pton(const int address_family, const char *cp, int len, void *dst)
+{
+ int ret = 0;
+
+ /* calculate length by finding first slash or NULL */
+ if (address_family == AF_INET)
+ ret = in4_pton(cp, len, dst, '\\', NULL);
+ else if (address_family == AF_INET6)
+ ret = in6_pton(cp, len, dst , '\\', NULL);
+
+ cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n",
+ ret, len, len, cp);
+ if (ret > 0)
+ ret = 1;
+ return ret;
+}
+
+/*
+ * Try to convert a string to an IPv4 address and then attempt to convert
+ * it to an IPv6 address if that fails. Set the family field if either
+ * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to
+ * treat the part following it as a numeric sin6_scope_id.
+ *
+ * Returns 0 on failure.
+ */
+int
+cifs_convert_address(struct sockaddr *dst, const char *src, int len)
+{
+ int rc, alen, slen;
+ const char *pct;
+ char scope_id[13];
+ struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
+
+ /* IPv4 address */
+ if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) {
+ s4->sin_family = AF_INET;
+ return 1;
+ }
+
+ /* attempt to exclude the scope ID from the address part */
+ pct = memchr(src, '%', len);
+ alen = pct ? pct - src : len;
+
+ rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr);
+ if (!rc)
+ return rc;
+
+ s6->sin6_family = AF_INET6;
+ if (pct) {
+ /* grab the scope ID */
+ slen = len - (alen + 1);
+ if (slen <= 0 || slen > 12)
+ return 0;
+ memcpy(scope_id, pct + 1, slen);
+ scope_id[slen] = '\0';
+
+ rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id);
+ rc = (rc == 0) ? 1 : 0;
+ }
+
+ return rc;
+}
+
+void
+cifs_set_port(struct sockaddr *addr, const unsigned short int port)
+{
+ switch (addr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)addr)->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
+ break;
+ }
+}
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+/* NT status -> dos error map */
+static const struct {
+ __u8 dos_class;
+ __u16 dos_code;
+ __u32 ntstatus;
+} ntstatus_to_dos_map[] = {
+ {
+ ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, {
+ ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, {
+ ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, {
+ ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, {
+ ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, {
+ ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, {
+ ERRDOS, 87, NT_STATUS_INVALID_CID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, {
+ ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, {
+ ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, {
+ ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, {
+ ERRDOS, 38, NT_STATUS_END_OF_FILE}, {
+ ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, {
+ ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, {
+ ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
+ during the session setup } */
+ {
+ ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, {
+ ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, {
+ ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, {
+ ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, {
+ ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, {
+ ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, {
+ ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, {
+ ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, {
+ ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, {
+ ERRDOS, 158, NT_STATUS_NOT_LOCKED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, {
+ ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, {
+ ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, {
+ /* mapping changed since shell does lookup on * expects FileNotFound */
+ ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, {
+ ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, {
+ ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, {
+ ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, {
+ ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, {
+ ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, {
+ ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, {
+ ERRDOS, 23, NT_STATUS_DATA_ERROR}, {
+ ERRDOS, 23, NT_STATUS_CRC_ERROR}, {
+ ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, {
+ ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, {
+ ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, {
+ ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, {
+ ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, {
+ ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, {
+ ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, {
+ ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, {
+ ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, {
+ ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, {
+ ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, {
+ ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, {
+ ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, {
+ ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, {
+ ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, {
+ ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, {
+ ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, {
+ ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, {
+ ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
+ during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */
+ ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
+ during the session setup } */
+ {
+ ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, {
+ ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, {
+ ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, {
+ ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, {
+ ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, {
+ ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, {
+ ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, {
+ ERRDOS, 112, NT_STATUS_DISK_FULL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, {
+ ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, {
+ ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, {
+ ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, {
+ ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, {
+ ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, {
+ ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, {
+ ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_INSUFFICIENT_RESOURCES to
+ NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
+ {
+ ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, {
+ ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
+ ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, {
+ ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, {
+ ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, {
+ ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, {
+ ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, {
+ ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, {
+ ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, {
+ ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, {
+ ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, {
+ ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, {
+ ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, {
+ ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, {
+ ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, {
+ ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, {
+ ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, {
+ ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, {
+ ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, {
+ ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, {
+ ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, {
+ ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, {
+ ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, {
+ ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, {
+ ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, {
+ ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, {
+ ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, {
+ ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, {
+ ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, {
+ ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, {
+ ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, {
+ ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, {
+ ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, {
+ ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, {
+ ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, {
+ ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, {
+ ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, {
+ ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, {
+ ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, {
+ ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, {
+ ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, {
+ ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, {
+ ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, {
+ ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, {
+ ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, {
+ ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, {
+ ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, {
+ ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, {
+ ERRDOS, 203, 0xc0000100}, {
+ ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, {
+ ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, {
+ ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, {
+ ERRDOS, 2401, NT_STATUS_FILES_OPEN}, {
+ ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, {
+ ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, {
+ ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, {
+ ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, {
+ ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, {
+ ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, {
+ ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, {
+ ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, {
+ ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, {
+ ERRDOS, 59, NT_STATUS_LINK_FAILED}, {
+ ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, {
+ ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, {
+ ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, {
+ ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, {
+ ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, {
+ ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, {
+ ERRHRD, ERRgeneral, 0xc000016e}, {
+ ERRHRD, ERRgeneral, 0xc000016f}, {
+ ERRHRD, ERRgeneral, 0xc0000170}, {
+ ERRHRD, ERRgeneral, 0xc0000171}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, {
+ ERRHRD, ERRgeneral, 0xc0000179}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, {
+ ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, {
+ ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, {
+ ERRDOS, 19, NT_STATUS_TOO_LATE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_TRUST_SAM_ACCOUNT to
+ NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, {
+ ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, {
+ ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
+ during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, {
+ ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, {
+ ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, {
+ ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, {
+ ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, {
+ ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, {
+ ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, {
+ ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, {
+ ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, {
+ ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, {
+ ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RETRY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, {
+ ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, {
+ ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, {
+ ERRHRD, ERRgeneral, 0xc000024a}, {
+ ERRHRD, ERRgeneral, 0xc000024b}, {
+ ERRHRD, ERRgeneral, 0xc000024c}, {
+ ERRHRD, ERRgeneral, 0xc000024d}, {
+ ERRHRD, ERRgeneral, 0xc000024e}, {
+ ERRHRD, ERRgeneral, 0xc000024f}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, {
+ ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, {
+ ERRHRD, ERRgeneral, 0xc000025d}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, {
+ ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, {
+ ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, {
+ ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, {
+ ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, {
+ ERRDOS, 21, 0xc000026e}, {
+ ERRDOS, 161, 0xc0000281}, {
+ ERRDOS, ERRnoaccess, 0xc000028a}, {
+ ERRDOS, ERRnoaccess, 0xc000028b}, {
+ ERRHRD, ERRgeneral, 0xc000028c}, {
+ ERRDOS, ERRnoaccess, 0xc000028d}, {
+ ERRDOS, ERRnoaccess, 0xc000028e}, {
+ ERRDOS, ERRnoaccess, 0xc000028f}, {
+ ERRDOS, ERRnoaccess, 0xc0000290}, {
+ ERRDOS, ERRbadfunc, 0xc000029c}, {
+ ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, {
+ ERRDOS, ERRinvlevel, 0x007c0001}, {
+ 0, 0, 0 }
+};
+
+/*****************************************************************************
+ Print an error message from the status code
+ *****************************************************************************/
+static void
+cifs_print_status(__u32 status_code)
+{
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) ==
+ (status_code & 0xFFFFFF)) {
+ pr_notice("Status code returned 0x%08x %s\n",
+ status_code, nt_errs[idx].nt_errstr);
+ }
+ idx++;
+ }
+ return;
+}
+
+
+static void
+ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)
+{
+ int i;
+ if (ntstatus == 0) {
+ *eclass = 0;
+ *ecode = 0;
+ return;
+ }
+ for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) {
+ if (ntstatus == ntstatus_to_dos_map[i].ntstatus) {
+ *eclass = ntstatus_to_dos_map[i].dos_class;
+ *ecode = ntstatus_to_dos_map[i].dos_code;
+ return;
+ }
+ }
+ *eclass = ERRHRD;
+ *ecode = ERRgeneral;
+}
+
+int
+map_smb_to_linux_error(char *buf, bool logErr)
+{
+ struct smb_hdr *smb = (struct smb_hdr *)buf;
+ unsigned int i;
+ int rc = -EIO; /* if transport error smb error may not be set */
+ __u8 smberrclass;
+ __u16 smberrcode;
+
+ /* BB if NT Status codes - map NT BB */
+
+ /* old style smb error codes */
+ if (smb->Status.CifsError == 0)
+ return 0;
+
+ if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
+ /* translate the newer STATUS codes to old style SMB errors
+ * and then to POSIX errors */
+ __u32 err = le32_to_cpu(smb->Status.CifsError);
+ if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED)))
+ cifs_print_status(err);
+ else if (cifsFYI & CIFS_RC)
+ cifs_print_status(err);
+ ntstatus_to_dos(err, &smberrclass, &smberrcode);
+ } else {
+ smberrclass = smb->Status.DosError.ErrorClass;
+ smberrcode = le16_to_cpu(smb->Status.DosError.Error);
+ }
+
+ /* old style errors */
+
+ /* DOS class smb error codes - map DOS */
+ if (smberrclass == ERRDOS) {
+ /* 1 byte field no need to byte reverse */
+ for (i = 0;
+ i <
+ sizeof(mapping_table_ERRDOS) /
+ sizeof(struct smb_to_posix_error); i++) {
+ if (mapping_table_ERRDOS[i].smb_err == 0)
+ break;
+ else if (mapping_table_ERRDOS[i].smb_err ==
+ smberrcode) {
+ rc = mapping_table_ERRDOS[i].posix_code;
+ break;
+ }
+ /* else try next error mapping one to see if match */
+ }
+ } else if (smberrclass == ERRSRV) {
+ /* server class of error codes */
+ for (i = 0;
+ i <
+ sizeof(mapping_table_ERRSRV) /
+ sizeof(struct smb_to_posix_error); i++) {
+ if (mapping_table_ERRSRV[i].smb_err == 0)
+ break;
+ else if (mapping_table_ERRSRV[i].smb_err ==
+ smberrcode) {
+ rc = mapping_table_ERRSRV[i].posix_code;
+ break;
+ }
+ /* else try next error mapping to see if match */
+ }
+ }
+ /* else ERRHRD class errors or junk - return EIO */
+
+ cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n",
+ le32_to_cpu(smb->Status.CifsError), rc);
+
+ /* generic corrective action e.g. reconnect SMB session on
+ * ERRbaduid could be added */
+
+ return rc;
+}
+
+/*
+ * calculate the size of the SMB message based on the fixed header
+ * portion, the number of word parameters and the data portion of the message
+ */
+unsigned int
+smbCalcSize(void *buf, struct TCP_Server_Info *server)
+{
+ struct smb_hdr *ptr = (struct smb_hdr *)buf;
+ return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
+ 2 /* size of the bcc field */ + get_bcc(ptr));
+}
+
+/* The following are taken from fs/ntfs/util.c */
+
+#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
+
+/*
+ * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
+ * into Unix UTC (based 1970-01-01, in seconds).
+ */
+struct timespec64
+cifs_NTtimeToUnix(__le64 ntutc)
+{
+ struct timespec64 ts;
+ /* BB what about the timezone? BB */
+
+ /* Subtract the NTFS time offset, then convert to 1s intervals. */
+ s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
+ u64 abs_t;
+
+ /*
+ * Unfortunately can not use normal 64 bit division on 32 bit arch, but
+ * the alternative, do_div, does not work with negative numbers so have
+ * to special case them
+ */
+ if (t < 0) {
+ abs_t = -t;
+ ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100);
+ ts.tv_nsec = -ts.tv_nsec;
+ ts.tv_sec = -abs_t;
+ } else {
+ abs_t = t;
+ ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100;
+ ts.tv_sec = abs_t;
+ }
+
+ return ts;
+}
+
+/* Convert the Unix UTC into NT UTC. */
+u64
+cifs_UnixTimeToNT(struct timespec64 t)
+{
+ /* Convert to 100ns intervals and then add the NTFS time offset. */
+ return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;
+}
+
+static const int total_days_of_prev_months[] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
+{
+ struct timespec64 ts;
+ time64_t sec;
+ int min, days, month, year;
+ u16 date = le16_to_cpu(le_date);
+ u16 time = le16_to_cpu(le_time);
+ SMB_TIME *st = (SMB_TIME *)&time;
+ SMB_DATE *sd = (SMB_DATE *)&date;
+
+ cifs_dbg(FYI, "date %d time %d\n", date, time);
+
+ sec = 2 * st->TwoSeconds;
+ min = st->Minutes;
+ if ((sec > 59) || (min > 59))
+ cifs_dbg(VFS, "illegal time min %d sec %lld\n", min, sec);
+ sec += (min * 60);
+ sec += 60 * 60 * st->Hours;
+ if (st->Hours > 24)
+ cifs_dbg(VFS, "illegal hours %d\n", st->Hours);
+ days = sd->Day;
+ month = sd->Month;
+ if (days < 1 || days > 31 || month < 1 || month > 12) {
+ cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
+ days = clamp(days, 1, 31);
+ month = clamp(month, 1, 12);
+ }
+ month -= 1;
+ days += total_days_of_prev_months[month];
+ days += 3652; /* account for difference in days between 1980 and 1970 */
+ year = sd->Year;
+ days += year * 365;
+ days += (year/4); /* leap year */
+ /* generalized leap year calculation is more complex, ie no leap year
+ for years/100 except for years/400, but since the maximum number for DOS
+ year is 2**7, the last year is 1980+127, which means we need only
+ consider 2 special case years, ie the years 2000 and 2100, and only
+ adjust for the lack of leap year for the year 2100, as 2000 was a
+ leap year (divisable by 400) */
+ if (year >= 120) /* the year 2100 */
+ days = days - 1; /* do not count leap year for the year 2100 */
+
+ /* adjust for leap year where we are still before leap day */
+ if (year != 120)
+ days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0);
+ sec += 24 * 60 * 60 * days;
+
+ ts.tv_sec = sec + offset;
+
+ /* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */
+
+ ts.tv_nsec = 0;
+ return ts;
+}
diff --git a/fs/cifs/nterr.c b/fs/cifs/nterr.c
new file mode 100644
index 000000000..b6023c646
--- /dev/null
+++ b/fs/cifs/nterr.c
@@ -0,0 +1,687 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * RPC Pipe client / server routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* NT error codes - see nterr.h */
+#include <linux/types.h>
+#include <linux/fs.h>
+#include "nterr.h"
+
+const struct nt_err_code_struct nt_errs[] = {
+ {"NT_STATUS_OK", NT_STATUS_OK},
+ {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL},
+ {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED},
+ {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS},
+ {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH},
+ {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION},
+ {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW},
+ {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR},
+ {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA},
+ {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE},
+ {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK},
+ {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC},
+ {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID},
+ {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED},
+ {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER},
+ {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE},
+ {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE},
+ {"NT_STATUS_INVALID_DEVICE_REQUEST",
+ NT_STATUS_INVALID_DEVICE_REQUEST},
+ {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE},
+ {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME},
+ {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA},
+ {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR},
+ {"NT_STATUS_MORE_PROCESSING_REQUIRED",
+ NT_STATUS_MORE_PROCESSING_REQUIRED},
+ {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY},
+ {"NT_STATUS_CONFLICTING_ADDRESSES",
+ NT_STATUS_CONFLICTING_ADDRESSES},
+ {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW},
+ {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM},
+ {"NT_STATUS_UNABLE_TO_DELETE_SECTION",
+ NT_STATUS_UNABLE_TO_DELETE_SECTION},
+ {"NT_STATUS_INVALID_SYSTEM_SERVICE",
+ NT_STATUS_INVALID_SYSTEM_SERVICE},
+ {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION},
+ {"NT_STATUS_INVALID_LOCK_SEQUENCE",
+ NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE},
+ {"NT_STATUS_INVALID_FILE_FOR_SECTION",
+ NT_STATUS_INVALID_FILE_FOR_SECTION},
+ {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED},
+ {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED},
+ {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL},
+ {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH},
+ {"NT_STATUS_NONCONTINUABLE_EXCEPTION",
+ NT_STATUS_NONCONTINUABLE_EXCEPTION},
+ {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION},
+ {"NT_STATUS_UNWIND", NT_STATUS_UNWIND},
+ {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK},
+ {"NT_STATUS_INVALID_UNWIND_TARGET",
+ NT_STATUS_INVALID_UNWIND_TARGET},
+ {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED},
+ {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR},
+ {"NT_STATUS_UNABLE_TO_DECOMMIT_VM",
+ NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+ {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED},
+ {"NT_STATUS_INVALID_PORT_ATTRIBUTES",
+ NT_STATUS_INVALID_PORT_ATTRIBUTES},
+ {"NT_STATUS_PORT_MESSAGE_TOO_LONG",
+ NT_STATUS_PORT_MESSAGE_TOO_LONG},
+ {"NT_STATUS_INVALID_PARAMETER_MIX",
+ NT_STATUS_INVALID_PARAMETER_MIX},
+ {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER},
+ {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR},
+ {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID},
+ {"NT_STATUS_OBJECT_NAME_NOT_FOUND",
+ NT_STATUS_OBJECT_NAME_NOT_FOUND},
+ {"NT_STATUS_OBJECT_NAME_COLLISION",
+ NT_STATUS_OBJECT_NAME_COLLISION},
+ {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE},
+ {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED},
+ {"NT_STATUS_DEVICE_ALREADY_ATTACHED",
+ NT_STATUS_DEVICE_ALREADY_ATTACHED},
+ {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID},
+ {"NT_STATUS_OBJECT_PATH_NOT_FOUND",
+ NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD",
+ NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+ {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN},
+ {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR},
+ {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR},
+ {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR},
+ {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG},
+ {"NT_STATUS_PORT_CONNECTION_REFUSED",
+ NT_STATUS_PORT_CONNECTION_REFUSED},
+ {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE},
+ {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION},
+ {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED},
+ {"NT_STATUS_INVALID_PAGE_PROTECTION",
+ NT_STATUS_INVALID_PAGE_PROTECTION},
+ {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED},
+ {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED",
+ NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET},
+ {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE},
+ {"NT_STATUS_SUSPEND_COUNT_EXCEEDED",
+ NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {"NT_STATUS_THREAD_IS_TERMINATING",
+ NT_STATUS_THREAD_IS_TERMINATING},
+ {"NT_STATUS_BAD_WORKING_SET_LIMIT",
+ NT_STATUS_BAD_WORKING_SET_LIMIT},
+ {"NT_STATUS_INCOMPATIBLE_FILE_MAP",
+ NT_STATUS_INCOMPATIBLE_FILE_MAP},
+ {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION},
+ {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED},
+ {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE},
+ {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE},
+ {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR},
+ {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT},
+ {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED},
+ {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING},
+ {"NT_STATUS_CTL_FILE_NOT_SUPPORTED",
+ NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION},
+ {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH},
+ {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER},
+ {"NT_STATUS_INVALID_PRIMARY_GROUP",
+ NT_STATUS_INVALID_PRIMARY_GROUP},
+ {"NT_STATUS_NO_IMPERSONATION_TOKEN",
+ NT_STATUS_NO_IMPERSONATION_TOKEN},
+ {"NT_STATUS_CANT_DISABLE_MANDATORY",
+ NT_STATUS_CANT_DISABLE_MANDATORY},
+ {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS},
+ {"NT_STATUS_NO_SUCH_LOGON_SESSION",
+ NT_STATUS_NO_SUCH_LOGON_SESSION},
+ {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE},
+ {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD},
+ {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME},
+ {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS},
+ {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER},
+ {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS},
+ {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP},
+ {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP},
+ {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP},
+ {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN},
+ {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD},
+ {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD},
+ {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION},
+ {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE},
+ {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION},
+ {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS},
+ {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION},
+ {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED},
+ {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED},
+ {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED},
+ {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED",
+ NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+ {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED},
+ {"NT_STATUS_INVALID_SUB_AUTHORITY",
+ NT_STATUS_INVALID_SUB_AUTHORITY},
+ {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL},
+ {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID},
+ {"NT_STATUS_INVALID_SECURITY_DESCR",
+ NT_STATUS_INVALID_SECURITY_DESCR},
+ {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND},
+ {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT},
+ {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN},
+ {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL},
+ {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED},
+ {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL},
+ {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED},
+ {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED},
+ {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED",
+ NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED},
+ {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY},
+ {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED},
+ {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL},
+ {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED},
+ {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA},
+ {"NT_STATUS_RESOURCE_DATA_NOT_FOUND",
+ NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+ {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND",
+ NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+ {"NT_STATUS_RESOURCE_NAME_NOT_FOUND",
+ NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+ {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED",
+ NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+ {"NT_STATUS_FLOAT_DENORMAL_OPERAND",
+ NT_STATUS_FLOAT_DENORMAL_OPERAND},
+ {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+ {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT},
+ {"NT_STATUS_FLOAT_INVALID_OPERATION",
+ NT_STATUS_FLOAT_INVALID_OPERATION},
+ {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW},
+ {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK},
+ {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW},
+ {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO",
+ NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+ {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW},
+ {"NT_STATUS_PRIVILEGED_INSTRUCTION",
+ NT_STATUS_PRIVILEGED_INSTRUCTION},
+ {"NT_STATUS_TOO_MANY_PAGING_FILES",
+ NT_STATUS_TOO_MANY_PAGING_FILES},
+ {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID},
+ {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED",
+ NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+ {"NT_STATUS_INSUFFICIENT_RESOURCES",
+ NT_STATUS_INSUFFICIENT_RESOURCES},
+ {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND},
+ {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR},
+ {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED},
+ {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE},
+ {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE},
+ {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED},
+ {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA},
+ {"NT_STATUS_MEDIA_WRITE_PROTECTED",
+ NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY},
+ {"NT_STATUS_INVALID_GROUP_ATTRIBUTES",
+ NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+ {"NT_STATUS_BAD_IMPERSONATION_LEVEL",
+ NT_STATUS_BAD_IMPERSONATION_LEVEL},
+ {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS},
+ {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS},
+ {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE},
+ {"NT_STATUS_BAD_MASTER_BOOT_RECORD",
+ NT_STATUS_BAD_MASTER_BOOT_RECORD},
+ {"NT_STATUS_INSTRUCTION_MISALIGNMENT",
+ NT_STATUS_INSTRUCTION_MISALIGNMENT},
+ {"NT_STATUS_INSTANCE_NOT_AVAILABLE",
+ NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE},
+ {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE},
+ {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY},
+ {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION},
+ {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED},
+ {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING},
+ {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED},
+ {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING},
+ {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE},
+ {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT},
+ {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED},
+ {"NT_STATUS_PROFILING_NOT_STARTED",
+ NT_STATUS_PROFILING_NOT_STARTED},
+ {"NT_STATUS_PROFILING_NOT_STOPPED",
+ NT_STATUS_PROFILING_NOT_STOPPED},
+ {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET},
+ {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY},
+ {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED},
+ {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING},
+ {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME},
+ {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH},
+ {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY},
+ {"NT_STATUS_DEVICE_DOES_NOT_EXIST",
+ NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS},
+ {"NT_STATUS_ADAPTER_HARDWARE_ERROR",
+ NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {"NT_STATUS_INVALID_NETWORK_RESPONSE",
+ NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {"NT_STATUS_UNEXPECTED_NETWORK_ERROR",
+ NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER},
+ {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL},
+ {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE},
+ {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED},
+ {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED},
+ {"NT_STATUS_NETWORK_ACCESS_DENIED",
+ NT_STATUS_NETWORK_ACCESS_DENIED},
+ {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE},
+ {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME},
+ {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES},
+ {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS},
+ {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED},
+ {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED},
+ {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT},
+ {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT},
+ {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE},
+ {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED},
+ {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED",
+ NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {"NT_STATUS_NO_SECURITY_ON_OBJECT",
+ NT_STATUS_NO_SECURITY_ON_OBJECT},
+ {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT},
+ {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY},
+ {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO",
+ NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+ {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF},
+ {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE},
+ {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE},
+ {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE},
+ {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN},
+ {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS},
+ {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED",
+ NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+ {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED},
+ {"NT_STATUS_INVALID_OPLOCK_PROTOCOL",
+ NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {"NT_STATUS_INTERNAL_DB_CORRUPTION",
+ NT_STATUS_INTERNAL_DB_CORRUPTION},
+ {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR},
+ {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED},
+ {"NT_STATUS_BAD_DESCRIPTOR_FORMAT",
+ NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+ {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER},
+ {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR},
+ {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR",
+ NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+ {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR",
+ NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+ {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR",
+ NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+ {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS},
+ {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS},
+ {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1},
+ {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2},
+ {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3},
+ {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4},
+ {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5},
+ {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6},
+ {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7},
+ {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8},
+ {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9},
+ {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10},
+ {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11},
+ {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12},
+ {"NT_STATUS_REDIRECTOR_NOT_STARTED",
+ NT_STATUS_REDIRECTOR_NOT_STARTED},
+ {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED},
+ {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW},
+ {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE},
+ {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE},
+ {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR},
+ {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY},
+ {"NT_STATUS_BAD_LOGON_SESSION_STATE",
+ NT_STATUS_BAD_LOGON_SESSION_STATE},
+ {"NT_STATUS_LOGON_SESSION_COLLISION",
+ NT_STATUS_LOGON_SESSION_COLLISION},
+ {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG},
+ {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN},
+ {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE},
+ {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND},
+ {"NT_STATUS_PROCESS_IS_TERMINATING",
+ NT_STATUS_PROCESS_IS_TERMINATING},
+ {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE},
+ {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION},
+ {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE},
+ {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED},
+ {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT},
+ {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST},
+ {"NT_STATUS_ABIOS_LID_ALREADY_OWNED",
+ NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+ {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER},
+ {"NT_STATUS_ABIOS_INVALID_COMMAND",
+ NT_STATUS_ABIOS_INVALID_COMMAND},
+ {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID},
+ {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE",
+ NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+ {"NT_STATUS_ABIOS_INVALID_SELECTOR",
+ NT_STATUS_ABIOS_INVALID_SELECTOR},
+ {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT},
+ {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE},
+ {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET},
+ {"NT_STATUS_INVALID_LDT_DESCRIPTOR",
+ NT_STATUS_INVALID_LDT_DESCRIPTOR},
+ {"NT_STATUS_INVALID_IMAGE_NE_FORMAT",
+ NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+ {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE},
+ {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE},
+ {"NT_STATUS_MAPPED_FILE_SIZE_ZERO",
+ NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+ {"NT_STATUS_TOO_MANY_OPENED_FILES",
+ NT_STATUS_TOO_MANY_OPENED_FILES},
+ {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED},
+ {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE},
+ {"NT_STATUS_INVALID_COMPUTER_NAME",
+ NT_STATUS_INVALID_COMPUTER_NAME},
+ {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED},
+ {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT},
+ {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP},
+ {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER},
+ {"NT_STATUS_MEMBERS_PRIMARY_GROUP",
+ NT_STATUS_MEMBERS_PRIMARY_GROUP},
+ {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED},
+ {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS},
+ {"NT_STATUS_THREAD_NOT_IN_PROCESS",
+ NT_STATUS_THREAD_NOT_IN_PROCESS},
+ {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE},
+ {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED",
+ NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+ {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT},
+ {"NT_STATUS_INVALID_IMAGE_LE_FORMAT",
+ NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+ {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ},
+ {"NT_STATUS_INVALID_IMAGE_PROTECT",
+ NT_STATUS_INVALID_IMAGE_PROTECT},
+ {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16},
+ {"NT_STATUS_LOGON_SERVER_CONFLICT",
+ NT_STATUS_LOGON_SERVER_CONFLICT},
+ {"NT_STATUS_TIME_DIFFERENCE_AT_DC",
+ NT_STATUS_TIME_DIFFERENCE_AT_DC},
+ {"NT_STATUS_SYNCHRONIZATION_REQUIRED",
+ NT_STATUS_SYNCHRONIZATION_REQUIRED},
+ {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND},
+ {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED},
+ {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED},
+ {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND},
+ {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND},
+ {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT},
+ {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT},
+ {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT},
+ {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES},
+ {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED},
+ {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT},
+ {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION},
+ {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS},
+ {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED},
+ {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE},
+ {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION},
+ {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE},
+ {"NT_STATUS_PAGEFILE_CREATE_FAILED",
+ NT_STATUS_PAGEFILE_CREATE_FAILED},
+ {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE},
+ {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL},
+ {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE},
+ {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT",
+ NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+ {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN},
+ {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT},
+ {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED},
+ {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR},
+ {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME},
+ {"NT_STATUS_SERIAL_NO_DEVICE_INITED",
+ NT_STATUS_SERIAL_NO_DEVICE_INITED},
+ {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS},
+ {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS},
+ {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS},
+ {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS},
+ {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED},
+ {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS},
+ {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG},
+ {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR},
+ {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE},
+ {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS},
+ {"NT_STATUS_LOGON_TYPE_NOT_GRANTED",
+ NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+ {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE},
+ {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED",
+ NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+ {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR",
+ NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+ {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER},
+ {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY",
+ NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+ {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER},
+ {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER},
+ {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER},
+ {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME},
+ {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND",
+ NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+ {"NT_STATUS_FLOPPY_WRONG_CYLINDER",
+ NT_STATUS_FLOPPY_WRONG_CYLINDER},
+ {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+ {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS},
+ {"NT_STATUS_DISK_RECALIBRATE_FAILED",
+ NT_STATUS_DISK_RECALIBRATE_FAILED},
+ {"NT_STATUS_DISK_OPERATION_FAILED",
+ NT_STATUS_DISK_OPERATION_FAILED},
+ {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED},
+ {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY},
+ {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING},
+ {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE},
+ {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH},
+ {"NT_STATUS_DEVICE_NOT_PARTITIONED",
+ NT_STATUS_DEVICE_NOT_PARTITIONED},
+ {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+ {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA",
+ NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+ {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW},
+ {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA},
+ {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER},
+ {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER},
+ {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED},
+ {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE},
+ {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS},
+ {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED",
+ NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+ {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN},
+ {"NT_STATUS_CHILD_MUST_BE_VOLATILE",
+ NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {"NT_STATUS_DEVICE_CONFIGURATION_ERROR",
+ NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+ {"NT_STATUS_DRIVER_INTERNAL_ERROR",
+ NT_STATUS_DRIVER_INTERNAL_ERROR},
+ {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE},
+ {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR},
+ {"NT_STATUS_DEVICE_PROTOCOL_ERROR",
+ NT_STATUS_DEVICE_PROTOCOL_ERROR},
+ {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER},
+ {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL},
+ {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE},
+ {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET},
+ {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+ {"NT_STATUS_TRUSTED_DOMAIN_FAILURE",
+ NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+ {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE",
+ NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+ {"NT_STATUS_EVENTLOG_FILE_CORRUPT",
+ NT_STATUS_EVENTLOG_FILE_CORRUPT},
+ {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START},
+ {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE},
+ {"NT_STATUS_MUTANT_LIMIT_EXCEEDED",
+ NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+ {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED},
+ {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED},
+ {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK},
+ {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT",
+ NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+ {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT},
+ {"NT_STATUS_EVENTLOG_FILE_CHANGED",
+ NT_STATUS_EVENTLOG_FILE_CHANGED},
+ {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
+ NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+ {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
+ NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+ {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
+ NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+ {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT",
+ NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+ {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED},
+ {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY},
+ {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED},
+ {"NT_STATUS_RESOURCE_LANG_NOT_FOUND",
+ NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+ {"NT_STATUS_INSUFF_SERVER_RESOURCES",
+ NT_STATUS_INSUFF_SERVER_RESOURCES},
+ {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE},
+ {"NT_STATUS_INVALID_ADDRESS_COMPONENT",
+ NT_STATUS_INVALID_ADDRESS_COMPONENT},
+ {"NT_STATUS_INVALID_ADDRESS_WILDCARD",
+ NT_STATUS_INVALID_ADDRESS_WILDCARD},
+ {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES},
+ {"NT_STATUS_ADDRESS_ALREADY_EXISTS",
+ NT_STATUS_ADDRESS_ALREADY_EXISTS},
+ {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED},
+ {"NT_STATUS_CONNECTION_DISCONNECTED",
+ NT_STATUS_CONNECTION_DISCONNECTED},
+ {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET},
+ {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES},
+ {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED},
+ {"NT_STATUS_TRANSACTION_TIMED_OUT",
+ NT_STATUS_TRANSACTION_TIMED_OUT},
+ {"NT_STATUS_TRANSACTION_NO_RELEASE",
+ NT_STATUS_TRANSACTION_NO_RELEASE},
+ {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH},
+ {"NT_STATUS_TRANSACTION_RESPONDED",
+ NT_STATUS_TRANSACTION_RESPONDED},
+ {"NT_STATUS_TRANSACTION_INVALID_ID",
+ NT_STATUS_TRANSACTION_INVALID_ID},
+ {"NT_STATUS_TRANSACTION_INVALID_TYPE",
+ NT_STATUS_TRANSACTION_INVALID_TYPE},
+ {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION},
+ {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION},
+ {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE",
+ NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+ {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED},
+ {"NT_STATUS_SYSTEM_PROCESS_TERMINATED",
+ NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+ {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED},
+ {"NT_STATUS_NO_BROWSER_SERVERS_FOUND",
+ NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+ {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR},
+ {"NT_STATUS_DRIVER_CANCEL_TIMEOUT",
+ NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+ {"NT_STATUS_REPLY_MESSAGE_MISMATCH",
+ NT_STATUS_REPLY_MESSAGE_MISMATCH},
+ {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT},
+ {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH",
+ NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+ {"NT_STATUS_LOST_WRITEBEHIND_DATA",
+ NT_STATUS_LOST_WRITEBEHIND_DATA},
+ {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID",
+ NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+ {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE},
+ {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND},
+ {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM},
+ {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE},
+ {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ},
+ {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK},
+ {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID},
+ {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS},
+ {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE},
+ {"NT_STATUS_RETRY", NT_STATUS_RETRY},
+ {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE},
+ {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET},
+ {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND},
+ {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW},
+ {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT},
+ {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+ {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT},
+ {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE},
+ {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED},
+ {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT},
+ {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED",
+ NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+ {"NT_STATUS_ADDRESS_NOT_ASSOCIATED",
+ NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+ {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID},
+ {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE},
+ {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE},
+ {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE},
+ {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE},
+ {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE},
+ {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED},
+ {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED},
+ {"NT_STATUS_BAD_COMPRESSION_BUFFER",
+ NT_STATUS_BAD_COMPRESSION_BUFFER},
+ {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE},
+ {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED},
+ {"NT_STATUS_TIMER_RESOLUTION_NOT_SET",
+ NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+ {"NT_STATUS_CONNECTION_COUNT_LIMIT",
+ NT_STATUS_CONNECTION_COUNT_LIMIT},
+ {"NT_STATUS_LOGIN_TIME_RESTRICTION",
+ NT_STATUS_LOGIN_TIME_RESTRICTION},
+ {"NT_STATUS_LOGIN_WKSTA_RESTRICTION",
+ NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+ {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH},
+ {"NT_STATUS_INSUFFICIENT_LOGON_INFO",
+ NT_STATUS_INSUFFICIENT_LOGON_INFO},
+ {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT},
+ {"NT_STATUS_BAD_SERVICE_ENTRYPOINT",
+ NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+ {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST},
+ {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1},
+ {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2},
+ {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT},
+ {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED},
+ {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE},
+ {"NT_STATUS_LICENSE_QUOTA_EXCEEDED",
+ NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+ {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT},
+ {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT},
+ {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT},
+ {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE},
+ {"NT_STATUS_UNSUPPORTED_COMPRESSION",
+ NT_STATUS_UNSUPPORTED_COMPRESSION},
+ {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE},
+ {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH",
+ NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+ {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND",
+ NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+ {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND",
+ NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+ {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED},
+ {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS},
+ {"NT_STATUS_QUOTA_LIST_INCONSISTENT",
+ NT_STATUS_QUOTA_LIST_INCONSISTENT},
+ {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE},
+ {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
+ {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES},
+ {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED},
+ {NULL, 0}
+};
diff --git a/fs/cifs/nterr.h b/fs/cifs/nterr.h
new file mode 100644
index 000000000..7a0eae5ae
--- /dev/null
+++ b/fs/cifs/nterr.h
@@ -0,0 +1,563 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NT error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+
+#ifndef _NTERR_H
+#define _NTERR_H
+
+struct nt_err_code_struct {
+ char *nt_errstr;
+ __u32 nt_errcode;
+};
+
+extern const struct nt_err_code_struct nt_errs[];
+
+/* Win32 Status codes. */
+#define NT_STATUS_MORE_ENTRIES 0x0105
+#define NT_ERROR_INVALID_PARAMETER 0x0057
+#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a
+#define NT_STATUS_1804 0x070c
+#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c
+
+/*
+ * Win32 Error codes extracted using a loop in smbclient then printing a netmon
+ * sniff to a file.
+ */
+
+#define NT_STATUS_OK 0x0000
+#define NT_STATUS_SOME_UNMAPPED 0x0107
+#define NT_STATUS_BUFFER_OVERFLOW 0x80000005
+#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a
+#define NT_STATUS_MEDIA_CHANGED 0x8000001c
+#define NT_STATUS_END_OF_MEDIA 0x8000001e
+#define NT_STATUS_MEDIA_CHECK 0x80000020
+#define NT_STATUS_NO_DATA_DETECTED 0x8000001c
+#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d
+#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288
+#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288
+#define NT_STATUS_UNSUCCESSFUL 0xC0000000 | 0x0001
+#define NT_STATUS_NOT_IMPLEMENTED 0xC0000000 | 0x0002
+#define NT_STATUS_INVALID_INFO_CLASS 0xC0000000 | 0x0003
+#define NT_STATUS_INFO_LENGTH_MISMATCH 0xC0000000 | 0x0004
+#define NT_STATUS_ACCESS_VIOLATION 0xC0000000 | 0x0005
+#define NT_STATUS_IN_PAGE_ERROR 0xC0000000 | 0x0006
+#define NT_STATUS_PAGEFILE_QUOTA 0xC0000000 | 0x0007
+#define NT_STATUS_INVALID_HANDLE 0xC0000000 | 0x0008
+#define NT_STATUS_BAD_INITIAL_STACK 0xC0000000 | 0x0009
+#define NT_STATUS_BAD_INITIAL_PC 0xC0000000 | 0x000a
+#define NT_STATUS_INVALID_CID 0xC0000000 | 0x000b
+#define NT_STATUS_TIMER_NOT_CANCELED 0xC0000000 | 0x000c
+#define NT_STATUS_INVALID_PARAMETER 0xC0000000 | 0x000d
+#define NT_STATUS_NO_SUCH_DEVICE 0xC0000000 | 0x000e
+#define NT_STATUS_NO_SUCH_FILE 0xC0000000 | 0x000f
+#define NT_STATUS_INVALID_DEVICE_REQUEST 0xC0000000 | 0x0010
+#define NT_STATUS_END_OF_FILE 0xC0000000 | 0x0011
+#define NT_STATUS_WRONG_VOLUME 0xC0000000 | 0x0012
+#define NT_STATUS_NO_MEDIA_IN_DEVICE 0xC0000000 | 0x0013
+#define NT_STATUS_UNRECOGNIZED_MEDIA 0xC0000000 | 0x0014
+#define NT_STATUS_NONEXISTENT_SECTOR 0xC0000000 | 0x0015
+#define NT_STATUS_MORE_PROCESSING_REQUIRED 0xC0000000 | 0x0016
+#define NT_STATUS_NO_MEMORY 0xC0000000 | 0x0017
+#define NT_STATUS_CONFLICTING_ADDRESSES 0xC0000000 | 0x0018
+#define NT_STATUS_NOT_MAPPED_VIEW 0xC0000000 | 0x0019
+#define NT_STATUS_UNABLE_TO_FREE_VM 0x80000000 | 0x001a
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0xC0000000 | 0x001b
+#define NT_STATUS_INVALID_SYSTEM_SERVICE 0xC0000000 | 0x001c
+#define NT_STATUS_ILLEGAL_INSTRUCTION 0xC0000000 | 0x001d
+#define NT_STATUS_INVALID_LOCK_SEQUENCE 0xC0000000 | 0x001e
+#define NT_STATUS_INVALID_VIEW_SIZE 0xC0000000 | 0x001f
+#define NT_STATUS_INVALID_FILE_FOR_SECTION 0xC0000000 | 0x0020
+#define NT_STATUS_ALREADY_COMMITTED 0xC0000000 | 0x0021
+#define NT_STATUS_ACCESS_DENIED 0xC0000000 | 0x0022
+#define NT_STATUS_BUFFER_TOO_SMALL 0xC0000000 | 0x0023
+#define NT_STATUS_OBJECT_TYPE_MISMATCH 0xC0000000 | 0x0024
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0xC0000000 | 0x0025
+#define NT_STATUS_INVALID_DISPOSITION 0xC0000000 | 0x0026
+#define NT_STATUS_UNWIND 0xC0000000 | 0x0027
+#define NT_STATUS_BAD_STACK 0xC0000000 | 0x0028
+#define NT_STATUS_INVALID_UNWIND_TARGET 0xC0000000 | 0x0029
+#define NT_STATUS_NOT_LOCKED 0xC0000000 | 0x002a
+#define NT_STATUS_PARITY_ERROR 0xC0000000 | 0x002b
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0xC0000000 | 0x002c
+#define NT_STATUS_NOT_COMMITTED 0xC0000000 | 0x002d
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0xC0000000 | 0x002e
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0xC0000000 | 0x002f
+#define NT_STATUS_INVALID_PARAMETER_MIX 0xC0000000 | 0x0030
+#define NT_STATUS_INVALID_QUOTA_LOWER 0xC0000000 | 0x0031
+#define NT_STATUS_DISK_CORRUPT_ERROR 0xC0000000 | 0x0032
+#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000000 | 0x0033
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000000 | 0x0034
+#define NT_STATUS_OBJECT_NAME_COLLISION 0xC0000000 | 0x0035
+#define NT_STATUS_HANDLE_NOT_WAITABLE 0xC0000000 | 0x0036
+#define NT_STATUS_PORT_DISCONNECTED 0xC0000000 | 0x0037
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0xC0000000 | 0x0038
+#define NT_STATUS_OBJECT_PATH_INVALID 0xC0000000 | 0x0039
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0xC0000000 | 0x003a
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0xC0000000 | 0x003b
+#define NT_STATUS_DATA_OVERRUN 0xC0000000 | 0x003c
+#define NT_STATUS_DATA_LATE_ERROR 0xC0000000 | 0x003d
+#define NT_STATUS_DATA_ERROR 0xC0000000 | 0x003e
+#define NT_STATUS_CRC_ERROR 0xC0000000 | 0x003f
+#define NT_STATUS_SECTION_TOO_BIG 0xC0000000 | 0x0040
+#define NT_STATUS_PORT_CONNECTION_REFUSED 0xC0000000 | 0x0041
+#define NT_STATUS_INVALID_PORT_HANDLE 0xC0000000 | 0x0042
+#define NT_STATUS_SHARING_VIOLATION 0xC0000000 | 0x0043
+#define NT_STATUS_QUOTA_EXCEEDED 0xC0000000 | 0x0044
+#define NT_STATUS_INVALID_PAGE_PROTECTION 0xC0000000 | 0x0045
+#define NT_STATUS_MUTANT_NOT_OWNED 0xC0000000 | 0x0046
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000000 | 0x0047
+#define NT_STATUS_PORT_ALREADY_SET 0xC0000000 | 0x0048
+#define NT_STATUS_SECTION_NOT_IMAGE 0xC0000000 | 0x0049
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0xC0000000 | 0x004a
+#define NT_STATUS_THREAD_IS_TERMINATING 0xC0000000 | 0x004b
+#define NT_STATUS_BAD_WORKING_SET_LIMIT 0xC0000000 | 0x004c
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0xC0000000 | 0x004d
+#define NT_STATUS_SECTION_PROTECTION 0xC0000000 | 0x004e
+#define NT_STATUS_EAS_NOT_SUPPORTED 0xC0000000 | 0x004f
+#define NT_STATUS_EA_TOO_LARGE 0xC0000000 | 0x0050
+#define NT_STATUS_NONEXISTENT_EA_ENTRY 0xC0000000 | 0x0051
+#define NT_STATUS_NO_EAS_ON_FILE 0xC0000000 | 0x0052
+#define NT_STATUS_EA_CORRUPT_ERROR 0xC0000000 | 0x0053
+#define NT_STATUS_FILE_LOCK_CONFLICT 0xC0000000 | 0x0054
+#define NT_STATUS_LOCK_NOT_GRANTED 0xC0000000 | 0x0055
+#define NT_STATUS_DELETE_PENDING 0xC0000000 | 0x0056
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0xC0000000 | 0x0057
+#define NT_STATUS_UNKNOWN_REVISION 0xC0000000 | 0x0058
+#define NT_STATUS_REVISION_MISMATCH 0xC0000000 | 0x0059
+#define NT_STATUS_INVALID_OWNER 0xC0000000 | 0x005a
+#define NT_STATUS_INVALID_PRIMARY_GROUP 0xC0000000 | 0x005b
+#define NT_STATUS_NO_IMPERSONATION_TOKEN 0xC0000000 | 0x005c
+#define NT_STATUS_CANT_DISABLE_MANDATORY 0xC0000000 | 0x005d
+#define NT_STATUS_NO_LOGON_SERVERS 0xC0000000 | 0x005e
+#define NT_STATUS_NO_SUCH_LOGON_SESSION 0xC0000000 | 0x005f
+#define NT_STATUS_NO_SUCH_PRIVILEGE 0xC0000000 | 0x0060
+#define NT_STATUS_PRIVILEGE_NOT_HELD 0xC0000000 | 0x0061
+#define NT_STATUS_INVALID_ACCOUNT_NAME 0xC0000000 | 0x0062
+#define NT_STATUS_USER_EXISTS 0xC0000000 | 0x0063
+#define NT_STATUS_NO_SUCH_USER 0xC0000000 | 0x0064
+#define NT_STATUS_GROUP_EXISTS 0xC0000000 | 0x0065
+#define NT_STATUS_NO_SUCH_GROUP 0xC0000000 | 0x0066
+#define NT_STATUS_MEMBER_IN_GROUP 0xC0000000 | 0x0067
+#define NT_STATUS_MEMBER_NOT_IN_GROUP 0xC0000000 | 0x0068
+#define NT_STATUS_LAST_ADMIN 0xC0000000 | 0x0069
+#define NT_STATUS_WRONG_PASSWORD 0xC0000000 | 0x006a
+#define NT_STATUS_ILL_FORMED_PASSWORD 0xC0000000 | 0x006b
+#define NT_STATUS_PASSWORD_RESTRICTION 0xC0000000 | 0x006c
+#define NT_STATUS_LOGON_FAILURE 0xC0000000 | 0x006d
+#define NT_STATUS_ACCOUNT_RESTRICTION 0xC0000000 | 0x006e
+#define NT_STATUS_INVALID_LOGON_HOURS 0xC0000000 | 0x006f
+#define NT_STATUS_INVALID_WORKSTATION 0xC0000000 | 0x0070
+#define NT_STATUS_PASSWORD_EXPIRED 0xC0000000 | 0x0071
+#define NT_STATUS_ACCOUNT_DISABLED 0xC0000000 | 0x0072
+#define NT_STATUS_NONE_MAPPED 0xC0000000 | 0x0073
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0xC0000000 | 0x0074
+#define NT_STATUS_LUIDS_EXHAUSTED 0xC0000000 | 0x0075
+#define NT_STATUS_INVALID_SUB_AUTHORITY 0xC0000000 | 0x0076
+#define NT_STATUS_INVALID_ACL 0xC0000000 | 0x0077
+#define NT_STATUS_INVALID_SID 0xC0000000 | 0x0078
+#define NT_STATUS_INVALID_SECURITY_DESCR 0xC0000000 | 0x0079
+#define NT_STATUS_PROCEDURE_NOT_FOUND 0xC0000000 | 0x007a
+#define NT_STATUS_INVALID_IMAGE_FORMAT 0xC0000000 | 0x007b
+#define NT_STATUS_NO_TOKEN 0xC0000000 | 0x007c
+#define NT_STATUS_BAD_INHERITANCE_ACL 0xC0000000 | 0x007d
+#define NT_STATUS_RANGE_NOT_LOCKED 0xC0000000 | 0x007e
+#define NT_STATUS_DISK_FULL 0xC0000000 | 0x007f
+#define NT_STATUS_SERVER_DISABLED 0xC0000000 | 0x0080
+#define NT_STATUS_SERVER_NOT_DISABLED 0xC0000000 | 0x0081
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0xC0000000 | 0x0082
+#define NT_STATUS_GUIDS_EXHAUSTED 0xC0000000 | 0x0083
+#define NT_STATUS_INVALID_ID_AUTHORITY 0xC0000000 | 0x0084
+#define NT_STATUS_AGENTS_EXHAUSTED 0xC0000000 | 0x0085
+#define NT_STATUS_INVALID_VOLUME_LABEL 0xC0000000 | 0x0086
+#define NT_STATUS_SECTION_NOT_EXTENDED 0xC0000000 | 0x0087
+#define NT_STATUS_NOT_MAPPED_DATA 0xC0000000 | 0x0088
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0xC0000000 | 0x0089
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0xC0000000 | 0x008a
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0xC0000000 | 0x008b
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0xC0000000 | 0x008c
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0xC0000000 | 0x008d
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0xC0000000 | 0x008e
+#define NT_STATUS_FLOAT_INEXACT_RESULT 0xC0000000 | 0x008f
+#define NT_STATUS_FLOAT_INVALID_OPERATION 0xC0000000 | 0x0090
+#define NT_STATUS_FLOAT_OVERFLOW 0xC0000000 | 0x0091
+#define NT_STATUS_FLOAT_STACK_CHECK 0xC0000000 | 0x0092
+#define NT_STATUS_FLOAT_UNDERFLOW 0xC0000000 | 0x0093
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000000 | 0x0094
+#define NT_STATUS_INTEGER_OVERFLOW 0xC0000000 | 0x0095
+#define NT_STATUS_PRIVILEGED_INSTRUCTION 0xC0000000 | 0x0096
+#define NT_STATUS_TOO_MANY_PAGING_FILES 0xC0000000 | 0x0097
+#define NT_STATUS_FILE_INVALID 0xC0000000 | 0x0098
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0xC0000000 | 0x0099
+#define NT_STATUS_INSUFFICIENT_RESOURCES 0xC0000000 | 0x009a
+#define NT_STATUS_DFS_EXIT_PATH_FOUND 0xC0000000 | 0x009b
+#define NT_STATUS_DEVICE_DATA_ERROR 0xC0000000 | 0x009c
+#define NT_STATUS_DEVICE_NOT_CONNECTED 0xC0000000 | 0x009d
+#define NT_STATUS_DEVICE_POWER_FAILURE 0xC0000000 | 0x009e
+#define NT_STATUS_FREE_VM_NOT_AT_BASE 0xC0000000 | 0x009f
+#define NT_STATUS_MEMORY_NOT_ALLOCATED 0xC0000000 | 0x00a0
+#define NT_STATUS_WORKING_SET_QUOTA 0xC0000000 | 0x00a1
+#define NT_STATUS_MEDIA_WRITE_PROTECTED 0xC0000000 | 0x00a2
+#define NT_STATUS_DEVICE_NOT_READY 0xC0000000 | 0x00a3
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0xC0000000 | 0x00a4
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0xC0000000 | 0x00a5
+#define NT_STATUS_CANT_OPEN_ANONYMOUS 0xC0000000 | 0x00a6
+#define NT_STATUS_BAD_VALIDATION_CLASS 0xC0000000 | 0x00a7
+#define NT_STATUS_BAD_TOKEN_TYPE 0xC0000000 | 0x00a8
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0xC0000000 | 0x00a9
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0xC0000000 | 0x00aa
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0xC0000000 | 0x00ab
+#define NT_STATUS_PIPE_NOT_AVAILABLE 0xC0000000 | 0x00ac
+#define NT_STATUS_INVALID_PIPE_STATE 0xC0000000 | 0x00ad
+#define NT_STATUS_PIPE_BUSY 0xC0000000 | 0x00ae
+#define NT_STATUS_ILLEGAL_FUNCTION 0xC0000000 | 0x00af
+#define NT_STATUS_PIPE_DISCONNECTED 0xC0000000 | 0x00b0
+#define NT_STATUS_PIPE_CLOSING 0xC0000000 | 0x00b1
+#define NT_STATUS_PIPE_CONNECTED 0xC0000000 | 0x00b2
+#define NT_STATUS_PIPE_LISTENING 0xC0000000 | 0x00b3
+#define NT_STATUS_INVALID_READ_MODE 0xC0000000 | 0x00b4
+#define NT_STATUS_IO_TIMEOUT 0xC0000000 | 0x00b5
+#define NT_STATUS_FILE_FORCED_CLOSED 0xC0000000 | 0x00b6
+#define NT_STATUS_PROFILING_NOT_STARTED 0xC0000000 | 0x00b7
+#define NT_STATUS_PROFILING_NOT_STOPPED 0xC0000000 | 0x00b8
+#define NT_STATUS_COULD_NOT_INTERPRET 0xC0000000 | 0x00b9
+#define NT_STATUS_FILE_IS_A_DIRECTORY 0xC0000000 | 0x00ba
+#define NT_STATUS_NOT_SUPPORTED 0xC0000000 | 0x00bb
+#define NT_STATUS_REMOTE_NOT_LISTENING 0xC0000000 | 0x00bc
+#define NT_STATUS_DUPLICATE_NAME 0xC0000000 | 0x00bd
+#define NT_STATUS_BAD_NETWORK_PATH 0xC0000000 | 0x00be
+#define NT_STATUS_NETWORK_BUSY 0xC0000000 | 0x00bf
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0xC0000000 | 0x00c0
+#define NT_STATUS_TOO_MANY_COMMANDS 0xC0000000 | 0x00c1
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0xC0000000 | 0x00c2
+#define NT_STATUS_INVALID_NETWORK_RESPONSE 0xC0000000 | 0x00c3
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0xC0000000 | 0x00c4
+#define NT_STATUS_BAD_REMOTE_ADAPTER 0xC0000000 | 0x00c5
+#define NT_STATUS_PRINT_QUEUE_FULL 0xC0000000 | 0x00c6
+#define NT_STATUS_NO_SPOOL_SPACE 0xC0000000 | 0x00c7
+#define NT_STATUS_PRINT_CANCELLED 0xC0000000 | 0x00c8
+#define NT_STATUS_NETWORK_NAME_DELETED 0xC0000000 | 0x00c9
+#define NT_STATUS_NETWORK_ACCESS_DENIED 0xC0000000 | 0x00ca
+#define NT_STATUS_BAD_DEVICE_TYPE 0xC0000000 | 0x00cb
+#define NT_STATUS_BAD_NETWORK_NAME 0xC0000000 | 0x00cc
+#define NT_STATUS_TOO_MANY_NAMES 0xC0000000 | 0x00cd
+#define NT_STATUS_TOO_MANY_SESSIONS 0xC0000000 | 0x00ce
+#define NT_STATUS_SHARING_PAUSED 0xC0000000 | 0x00cf
+#define NT_STATUS_REQUEST_NOT_ACCEPTED 0xC0000000 | 0x00d0
+#define NT_STATUS_REDIRECTOR_PAUSED 0xC0000000 | 0x00d1
+#define NT_STATUS_NET_WRITE_FAULT 0xC0000000 | 0x00d2
+#define NT_STATUS_PROFILING_AT_LIMIT 0xC0000000 | 0x00d3
+#define NT_STATUS_NOT_SAME_DEVICE 0xC0000000 | 0x00d4
+#define NT_STATUS_FILE_RENAMED 0xC0000000 | 0x00d5
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0xC0000000 | 0x00d6
+#define NT_STATUS_NO_SECURITY_ON_OBJECT 0xC0000000 | 0x00d7
+#define NT_STATUS_CANT_WAIT 0xC0000000 | 0x00d8
+#define NT_STATUS_PIPE_EMPTY 0xC0000000 | 0x00d9
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0xC0000000 | 0x00da
+#define NT_STATUS_CANT_TERMINATE_SELF 0xC0000000 | 0x00db
+#define NT_STATUS_INVALID_SERVER_STATE 0xC0000000 | 0x00dc
+#define NT_STATUS_INVALID_DOMAIN_STATE 0xC0000000 | 0x00dd
+#define NT_STATUS_INVALID_DOMAIN_ROLE 0xC0000000 | 0x00de
+#define NT_STATUS_NO_SUCH_DOMAIN 0xC0000000 | 0x00df
+#define NT_STATUS_DOMAIN_EXISTS 0xC0000000 | 0x00e0
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0xC0000000 | 0x00e1
+#define NT_STATUS_OPLOCK_NOT_GRANTED 0xC0000000 | 0x00e2
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0xC0000000 | 0x00e3
+#define NT_STATUS_INTERNAL_DB_CORRUPTION 0xC0000000 | 0x00e4
+#define NT_STATUS_INTERNAL_ERROR 0xC0000000 | 0x00e5
+#define NT_STATUS_GENERIC_NOT_MAPPED 0xC0000000 | 0x00e6
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0xC0000000 | 0x00e7
+#define NT_STATUS_INVALID_USER_BUFFER 0xC0000000 | 0x00e8
+#define NT_STATUS_UNEXPECTED_IO_ERROR 0xC0000000 | 0x00e9
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0xC0000000 | 0x00ea
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0xC0000000 | 0x00eb
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0xC0000000 | 0x00ec
+#define NT_STATUS_NOT_LOGON_PROCESS 0xC0000000 | 0x00ed
+#define NT_STATUS_LOGON_SESSION_EXISTS 0xC0000000 | 0x00ee
+#define NT_STATUS_INVALID_PARAMETER_1 0xC0000000 | 0x00ef
+#define NT_STATUS_INVALID_PARAMETER_2 0xC0000000 | 0x00f0
+#define NT_STATUS_INVALID_PARAMETER_3 0xC0000000 | 0x00f1
+#define NT_STATUS_INVALID_PARAMETER_4 0xC0000000 | 0x00f2
+#define NT_STATUS_INVALID_PARAMETER_5 0xC0000000 | 0x00f3
+#define NT_STATUS_INVALID_PARAMETER_6 0xC0000000 | 0x00f4
+#define NT_STATUS_INVALID_PARAMETER_7 0xC0000000 | 0x00f5
+#define NT_STATUS_INVALID_PARAMETER_8 0xC0000000 | 0x00f6
+#define NT_STATUS_INVALID_PARAMETER_9 0xC0000000 | 0x00f7
+#define NT_STATUS_INVALID_PARAMETER_10 0xC0000000 | 0x00f8
+#define NT_STATUS_INVALID_PARAMETER_11 0xC0000000 | 0x00f9
+#define NT_STATUS_INVALID_PARAMETER_12 0xC0000000 | 0x00fa
+#define NT_STATUS_REDIRECTOR_NOT_STARTED 0xC0000000 | 0x00fb
+#define NT_STATUS_REDIRECTOR_STARTED 0xC0000000 | 0x00fc
+#define NT_STATUS_STACK_OVERFLOW 0xC0000000 | 0x00fd
+#define NT_STATUS_NO_SUCH_PACKAGE 0xC0000000 | 0x00fe
+#define NT_STATUS_BAD_FUNCTION_TABLE 0xC0000000 | 0x00ff
+#define NT_STATUS_DIRECTORY_NOT_EMPTY 0xC0000000 | 0x0101
+#define NT_STATUS_FILE_CORRUPT_ERROR 0xC0000000 | 0x0102
+#define NT_STATUS_NOT_A_DIRECTORY 0xC0000000 | 0x0103
+#define NT_STATUS_BAD_LOGON_SESSION_STATE 0xC0000000 | 0x0104
+#define NT_STATUS_LOGON_SESSION_COLLISION 0xC0000000 | 0x0105
+#define NT_STATUS_NAME_TOO_LONG 0xC0000000 | 0x0106
+#define NT_STATUS_FILES_OPEN 0xC0000000 | 0x0107
+#define NT_STATUS_CONNECTION_IN_USE 0xC0000000 | 0x0108
+#define NT_STATUS_MESSAGE_NOT_FOUND 0xC0000000 | 0x0109
+#define NT_STATUS_PROCESS_IS_TERMINATING 0xC0000000 | 0x010a
+#define NT_STATUS_INVALID_LOGON_TYPE 0xC0000000 | 0x010b
+#define NT_STATUS_NO_GUID_TRANSLATION 0xC0000000 | 0x010c
+#define NT_STATUS_CANNOT_IMPERSONATE 0xC0000000 | 0x010d
+#define NT_STATUS_IMAGE_ALREADY_LOADED 0xC0000000 | 0x010e
+#define NT_STATUS_ABIOS_NOT_PRESENT 0xC0000000 | 0x010f
+#define NT_STATUS_ABIOS_LID_NOT_EXIST 0xC0000000 | 0x0110
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0xC0000000 | 0x0111
+#define NT_STATUS_ABIOS_NOT_LID_OWNER 0xC0000000 | 0x0112
+#define NT_STATUS_ABIOS_INVALID_COMMAND 0xC0000000 | 0x0113
+#define NT_STATUS_ABIOS_INVALID_LID 0xC0000000 | 0x0114
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0xC0000000 | 0x0115
+#define NT_STATUS_ABIOS_INVALID_SELECTOR 0xC0000000 | 0x0116
+#define NT_STATUS_NO_LDT 0xC0000000 | 0x0117
+#define NT_STATUS_INVALID_LDT_SIZE 0xC0000000 | 0x0118
+#define NT_STATUS_INVALID_LDT_OFFSET 0xC0000000 | 0x0119
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0xC0000000 | 0x011a
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0xC0000000 | 0x011b
+#define NT_STATUS_RXACT_INVALID_STATE 0xC0000000 | 0x011c
+#define NT_STATUS_RXACT_COMMIT_FAILURE 0xC0000000 | 0x011d
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0xC0000000 | 0x011e
+#define NT_STATUS_TOO_MANY_OPENED_FILES 0xC0000000 | 0x011f
+#define NT_STATUS_CANCELLED 0xC0000000 | 0x0120
+#define NT_STATUS_CANNOT_DELETE 0xC0000000 | 0x0121
+#define NT_STATUS_INVALID_COMPUTER_NAME 0xC0000000 | 0x0122
+#define NT_STATUS_FILE_DELETED 0xC0000000 | 0x0123
+#define NT_STATUS_SPECIAL_ACCOUNT 0xC0000000 | 0x0124
+#define NT_STATUS_SPECIAL_GROUP 0xC0000000 | 0x0125
+#define NT_STATUS_SPECIAL_USER 0xC0000000 | 0x0126
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0xC0000000 | 0x0127
+#define NT_STATUS_FILE_CLOSED 0xC0000000 | 0x0128
+#define NT_STATUS_TOO_MANY_THREADS 0xC0000000 | 0x0129
+#define NT_STATUS_THREAD_NOT_IN_PROCESS 0xC0000000 | 0x012a
+#define NT_STATUS_TOKEN_ALREADY_IN_USE 0xC0000000 | 0x012b
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0xC0000000 | 0x012c
+#define NT_STATUS_COMMITMENT_LIMIT 0xC0000000 | 0x012d
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0xC0000000 | 0x012e
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0xC0000000 | 0x012f
+#define NT_STATUS_INVALID_IMAGE_PROTECT 0xC0000000 | 0x0130
+#define NT_STATUS_INVALID_IMAGE_WIN_16 0xC0000000 | 0x0131
+#define NT_STATUS_LOGON_SERVER_CONFLICT 0xC0000000 | 0x0132
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0xC0000000 | 0x0133
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0xC0000000 | 0x0134
+#define NT_STATUS_DLL_NOT_FOUND 0xC0000000 | 0x0135
+#define NT_STATUS_OPEN_FAILED 0xC0000000 | 0x0136
+#define NT_STATUS_IO_PRIVILEGE_FAILED 0xC0000000 | 0x0137
+#define NT_STATUS_ORDINAL_NOT_FOUND 0xC0000000 | 0x0138
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0139
+#define NT_STATUS_CONTROL_C_EXIT 0xC0000000 | 0x013a
+#define NT_STATUS_LOCAL_DISCONNECT 0xC0000000 | 0x013b
+#define NT_STATUS_REMOTE_DISCONNECT 0xC0000000 | 0x013c
+#define NT_STATUS_REMOTE_RESOURCES 0xC0000000 | 0x013d
+#define NT_STATUS_LINK_FAILED 0xC0000000 | 0x013e
+#define NT_STATUS_LINK_TIMEOUT 0xC0000000 | 0x013f
+#define NT_STATUS_INVALID_CONNECTION 0xC0000000 | 0x0140
+#define NT_STATUS_INVALID_ADDRESS 0xC0000000 | 0x0141
+#define NT_STATUS_DLL_INIT_FAILED 0xC0000000 | 0x0142
+#define NT_STATUS_MISSING_SYSTEMFILE 0xC0000000 | 0x0143
+#define NT_STATUS_UNHANDLED_EXCEPTION 0xC0000000 | 0x0144
+#define NT_STATUS_APP_INIT_FAILURE 0xC0000000 | 0x0145
+#define NT_STATUS_PAGEFILE_CREATE_FAILED 0xC0000000 | 0x0146
+#define NT_STATUS_NO_PAGEFILE 0xC0000000 | 0x0147
+#define NT_STATUS_INVALID_LEVEL 0xC0000000 | 0x0148
+#define NT_STATUS_WRONG_PASSWORD_CORE 0xC0000000 | 0x0149
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0xC0000000 | 0x014a
+#define NT_STATUS_PIPE_BROKEN 0xC0000000 | 0x014b
+#define NT_STATUS_REGISTRY_CORRUPT 0xC0000000 | 0x014c
+#define NT_STATUS_REGISTRY_IO_FAILED 0xC0000000 | 0x014d
+#define NT_STATUS_NO_EVENT_PAIR 0xC0000000 | 0x014e
+#define NT_STATUS_UNRECOGNIZED_VOLUME 0xC0000000 | 0x014f
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0xC0000000 | 0x0150
+#define NT_STATUS_NO_SUCH_ALIAS 0xC0000000 | 0x0151
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0xC0000000 | 0x0152
+#define NT_STATUS_MEMBER_IN_ALIAS 0xC0000000 | 0x0153
+#define NT_STATUS_ALIAS_EXISTS 0xC0000000 | 0x0154
+#define NT_STATUS_LOGON_NOT_GRANTED 0xC0000000 | 0x0155
+#define NT_STATUS_TOO_MANY_SECRETS 0xC0000000 | 0x0156
+#define NT_STATUS_SECRET_TOO_LONG 0xC0000000 | 0x0157
+#define NT_STATUS_INTERNAL_DB_ERROR 0xC0000000 | 0x0158
+#define NT_STATUS_FULLSCREEN_MODE 0xC0000000 | 0x0159
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0xC0000000 | 0x015a
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0xC0000000 | 0x015b
+#define NT_STATUS_NOT_REGISTRY_FILE 0xC0000000 | 0x015c
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x015d
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0xC0000000 | 0x015e
+#define NT_STATUS_FT_MISSING_MEMBER 0xC0000000 | 0x015f
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0xC0000000 | 0x0160
+#define NT_STATUS_ILLEGAL_CHARACTER 0xC0000000 | 0x0161
+#define NT_STATUS_UNMAPPABLE_CHARACTER 0xC0000000 | 0x0162
+#define NT_STATUS_UNDEFINED_CHARACTER 0xC0000000 | 0x0163
+#define NT_STATUS_FLOPPY_VOLUME 0xC0000000 | 0x0164
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0xC0000000 | 0x0165
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0xC0000000 | 0x0166
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0xC0000000 | 0x0167
+#define NT_STATUS_FLOPPY_BAD_REGISTERS 0xC0000000 | 0x0168
+#define NT_STATUS_DISK_RECALIBRATE_FAILED 0xC0000000 | 0x0169
+#define NT_STATUS_DISK_OPERATION_FAILED 0xC0000000 | 0x016a
+#define NT_STATUS_DISK_RESET_FAILED 0xC0000000 | 0x016b
+#define NT_STATUS_SHARED_IRQ_BUSY 0xC0000000 | 0x016c
+#define NT_STATUS_FT_ORPHANING 0xC0000000 | 0x016d
+#define NT_STATUS_PARTITION_FAILURE 0xC0000000 | 0x0172
+#define NT_STATUS_INVALID_BLOCK_LENGTH 0xC0000000 | 0x0173
+#define NT_STATUS_DEVICE_NOT_PARTITIONED 0xC0000000 | 0x0174
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0xC0000000 | 0x0175
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0xC0000000 | 0x0176
+#define NT_STATUS_EOM_OVERFLOW 0xC0000000 | 0x0177
+#define NT_STATUS_NO_MEDIA 0xC0000000 | 0x0178
+#define NT_STATUS_NO_SUCH_MEMBER 0xC0000000 | 0x017a
+#define NT_STATUS_INVALID_MEMBER 0xC0000000 | 0x017b
+#define NT_STATUS_KEY_DELETED 0xC0000000 | 0x017c
+#define NT_STATUS_NO_LOG_SPACE 0xC0000000 | 0x017d
+#define NT_STATUS_TOO_MANY_SIDS 0xC0000000 | 0x017e
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x017f
+#define NT_STATUS_KEY_HAS_CHILDREN 0xC0000000 | 0x0180
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0xC0000000 | 0x0181
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0xC0000000 | 0x0182
+#define NT_STATUS_DRIVER_INTERNAL_ERROR 0xC0000000 | 0x0183
+#define NT_STATUS_INVALID_DEVICE_STATE 0xC0000000 | 0x0184
+#define NT_STATUS_IO_DEVICE_ERROR 0xC0000000 | 0x0185
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0xC0000000 | 0x0186
+#define NT_STATUS_BACKUP_CONTROLLER 0xC0000000 | 0x0187
+#define NT_STATUS_LOG_FILE_FULL 0xC0000000 | 0x0188
+#define NT_STATUS_TOO_LATE 0xC0000000 | 0x0189
+#define NT_STATUS_NO_TRUST_LSA_SECRET 0xC0000000 | 0x018a
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0xC0000000 | 0x018b
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0xC0000000 | 0x018c
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0xC0000000 | 0x018d
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0xC0000000 | 0x018e
+#define NT_STATUS_EVENTLOG_CANT_START 0xC0000000 | 0x018f
+#define NT_STATUS_TRUST_FAILURE 0xC0000000 | 0x0190
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0xC0000000 | 0x0191
+#define NT_STATUS_NETLOGON_NOT_STARTED 0xC0000000 | 0x0192
+#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000000 | 0x0193
+#define NT_STATUS_POSSIBLE_DEADLOCK 0xC0000000 | 0x0194
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0xC0000000 | 0x0195
+#define NT_STATUS_REMOTE_SESSION_LIMIT 0xC0000000 | 0x0196
+#define NT_STATUS_EVENTLOG_FILE_CHANGED 0xC0000000 | 0x0197
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0xC0000000 | 0x0198
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0xC0000000 | 0x0199
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC0000000 | 0x019a
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC0000000 | 0x019b
+#define NT_STATUS_FS_DRIVER_REQUIRED 0xC0000000 | 0x019c
+#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000000 | 0x0202
+#define NT_STATUS_USER_SESSION_DELETED 0xC0000000 | 0x0203
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000000 | 0x0204
+#define NT_STATUS_INSUFF_SERVER_RESOURCES 0xC0000000 | 0x0205
+#define NT_STATUS_INVALID_BUFFER_SIZE 0xC0000000 | 0x0206
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0xC0000000 | 0x0207
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0xC0000000 | 0x0208
+#define NT_STATUS_TOO_MANY_ADDRESSES 0xC0000000 | 0x0209
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0xC0000000 | 0x020a
+#define NT_STATUS_ADDRESS_CLOSED 0xC0000000 | 0x020b
+#define NT_STATUS_CONNECTION_DISCONNECTED 0xC0000000 | 0x020c
+#define NT_STATUS_CONNECTION_RESET 0xC0000000 | 0x020d
+#define NT_STATUS_TOO_MANY_NODES 0xC0000000 | 0x020e
+#define NT_STATUS_TRANSACTION_ABORTED 0xC0000000 | 0x020f
+#define NT_STATUS_TRANSACTION_TIMED_OUT 0xC0000000 | 0x0210
+#define NT_STATUS_TRANSACTION_NO_RELEASE 0xC0000000 | 0x0211
+#define NT_STATUS_TRANSACTION_NO_MATCH 0xC0000000 | 0x0212
+#define NT_STATUS_TRANSACTION_RESPONDED 0xC0000000 | 0x0213
+#define NT_STATUS_TRANSACTION_INVALID_ID 0xC0000000 | 0x0214
+#define NT_STATUS_TRANSACTION_INVALID_TYPE 0xC0000000 | 0x0215
+#define NT_STATUS_NOT_SERVER_SESSION 0xC0000000 | 0x0216
+#define NT_STATUS_NOT_CLIENT_SESSION 0xC0000000 | 0x0217
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0xC0000000 | 0x0218
+#define NT_STATUS_DEBUG_ATTACH_FAILED 0xC0000000 | 0x0219
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0xC0000000 | 0x021a
+#define NT_STATUS_DATA_NOT_ACCEPTED 0xC0000000 | 0x021b
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0xC0000000 | 0x021c
+#define NT_STATUS_VDM_HARD_ERROR 0xC0000000 | 0x021d
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0xC0000000 | 0x021e
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0xC0000000 | 0x021f
+#define NT_STATUS_MAPPED_ALIGNMENT 0xC0000000 | 0x0220
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0xC0000000 | 0x0221
+#define NT_STATUS_LOST_WRITEBEHIND_DATA 0xC0000000 | 0x0222
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0xC0000000 | 0x0223
+#define NT_STATUS_PASSWORD_MUST_CHANGE 0xC0000000 | 0x0224
+#define NT_STATUS_NOT_FOUND 0xC0000000 | 0x0225
+#define NT_STATUS_NOT_TINY_STREAM 0xC0000000 | 0x0226
+#define NT_STATUS_RECOVERY_FAILURE 0xC0000000 | 0x0227
+#define NT_STATUS_STACK_OVERFLOW_READ 0xC0000000 | 0x0228
+#define NT_STATUS_FAIL_CHECK 0xC0000000 | 0x0229
+#define NT_STATUS_DUPLICATE_OBJECTID 0xC0000000 | 0x022a
+#define NT_STATUS_OBJECTID_EXISTS 0xC0000000 | 0x022b
+#define NT_STATUS_CONVERT_TO_LARGE 0xC0000000 | 0x022c
+#define NT_STATUS_RETRY 0xC0000000 | 0x022d
+#define NT_STATUS_FOUND_OUT_OF_SCOPE 0xC0000000 | 0x022e
+#define NT_STATUS_ALLOCATE_BUCKET 0xC0000000 | 0x022f
+#define NT_STATUS_PROPSET_NOT_FOUND 0xC0000000 | 0x0230
+#define NT_STATUS_MARSHALL_OVERFLOW 0xC0000000 | 0x0231
+#define NT_STATUS_INVALID_VARIANT 0xC0000000 | 0x0232
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0xC0000000 | 0x0233
+#define NT_STATUS_ACCOUNT_LOCKED_OUT 0xC0000000 | 0x0234
+#define NT_STATUS_HANDLE_NOT_CLOSABLE 0xC0000000 | 0x0235
+#define NT_STATUS_CONNECTION_REFUSED 0xC0000000 | 0x0236
+#define NT_STATUS_GRACEFUL_DISCONNECT 0xC0000000 | 0x0237
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0xC0000000 | 0x0238
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0xC0000000 | 0x0239
+#define NT_STATUS_CONNECTION_INVALID 0xC0000000 | 0x023a
+#define NT_STATUS_CONNECTION_ACTIVE 0xC0000000 | 0x023b
+#define NT_STATUS_NETWORK_UNREACHABLE 0xC0000000 | 0x023c
+#define NT_STATUS_HOST_UNREACHABLE 0xC0000000 | 0x023d
+#define NT_STATUS_PROTOCOL_UNREACHABLE 0xC0000000 | 0x023e
+#define NT_STATUS_PORT_UNREACHABLE 0xC0000000 | 0x023f
+#define NT_STATUS_REQUEST_ABORTED 0xC0000000 | 0x0240
+#define NT_STATUS_CONNECTION_ABORTED 0xC0000000 | 0x0241
+#define NT_STATUS_BAD_COMPRESSION_BUFFER 0xC0000000 | 0x0242
+#define NT_STATUS_USER_MAPPED_FILE 0xC0000000 | 0x0243
+#define NT_STATUS_AUDIT_FAILED 0xC0000000 | 0x0244
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000000 | 0x0245
+#define NT_STATUS_CONNECTION_COUNT_LIMIT 0xC0000000 | 0x0246
+#define NT_STATUS_LOGIN_TIME_RESTRICTION 0xC0000000 | 0x0247
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0xC0000000 | 0x0248
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0xC0000000 | 0x0249
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0xC0000000 | 0x0250
+#define NT_STATUS_BAD_DLL_ENTRYPOINT 0xC0000000 | 0x0251
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0xC0000000 | 0x0252
+#define NT_STATUS_LPC_REPLY_LOST 0xC0000000 | 0x0253
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 0xC0000000 | 0x0254
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 0xC0000000 | 0x0255
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0xC0000000 | 0x0256
+#define NT_STATUS_PATH_NOT_COVERED 0xC0000000 | 0x0257
+#define NT_STATUS_NO_CALLBACK_ACTIVE 0xC0000000 | 0x0258
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0xC0000000 | 0x0259
+#define NT_STATUS_PWD_TOO_SHORT 0xC0000000 | 0x025a
+#define NT_STATUS_PWD_TOO_RECENT 0xC0000000 | 0x025b
+#define NT_STATUS_PWD_HISTORY_CONFLICT 0xC0000000 | 0x025c
+#define NT_STATUS_PLUGPLAY_NO_DEVICE 0xC0000000 | 0x025e
+#define NT_STATUS_UNSUPPORTED_COMPRESSION 0xC0000000 | 0x025f
+#define NT_STATUS_INVALID_HW_PROFILE 0xC0000000 | 0x0260
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0xC0000000 | 0x0261
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0xC0000000 | 0x0262
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0263
+#define NT_STATUS_RESOURCE_NOT_OWNED 0xC0000000 | 0x0264
+#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266
+#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267
+#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */
+
+#endif /* _NTERR_H */
diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h
new file mode 100644
index 000000000..3079b38f0
--- /dev/null
+++ b/fs/cifs/ntlmssp.h
@@ -0,0 +1,138 @@
+/*
+ * fs/cifs/ntlmssp.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NTLMSSP_SIGNATURE "NTLMSSP"
+/* Message Types */
+#define NtLmNegotiate cpu_to_le32(1)
+#define NtLmChallenge cpu_to_le32(2)
+#define NtLmAuthenticate cpu_to_le32(3)
+#define UnknownMessage cpu_to_le32(8)
+
+/* Negotiate Flags */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */
+#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */
+#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */
+/* define reserved9 0x08 */
+#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */
+#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */
+#define NTLMSSP_NEGOTIATE_DGRAM 0x0040
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */
+/* defined reserved 8 0x0100 */
+#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */
+#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */
+#define NTLMSSP_ANONYMOUS 0x0800
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000
+#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
+#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
+#define NTLMSSP_TARGET_TYPE_SHARE 0x40000
+#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/
+/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */
+#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000
+#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */
+#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000
+/* #define reserved4 0x1000000 */
+#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */
+/* #define reserved3 0x4000000 */
+/* #define reserved2 0x8000000 */
+/* #define reserved1 0x10000000 */
+#define NTLMSSP_NEGOTIATE_128 0x20000000
+#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
+#define NTLMSSP_NEGOTIATE_56 0x80000000
+
+/* Define AV Pair Field IDs */
+enum av_field_type {
+ NTLMSSP_AV_EOL = 0,
+ NTLMSSP_AV_NB_COMPUTER_NAME,
+ NTLMSSP_AV_NB_DOMAIN_NAME,
+ NTLMSSP_AV_DNS_COMPUTER_NAME,
+ NTLMSSP_AV_DNS_DOMAIN_NAME,
+ NTLMSSP_AV_DNS_TREE_NAME,
+ NTLMSSP_AV_FLAGS,
+ NTLMSSP_AV_TIMESTAMP,
+ NTLMSSP_AV_RESTRICTION,
+ NTLMSSP_AV_TARGET_NAME,
+ NTLMSSP_AV_CHANNEL_BINDINGS
+};
+
+/* Although typedefs are not commonly used for structure definitions */
+/* in the Linux kernel, in this particular case they are useful */
+/* to more closely match the standards document for NTLMSSP from */
+/* OpenGroup and to make the code more closely match the standard in */
+/* appearance */
+
+typedef struct _SECURITY_BUFFER {
+ __le16 Length;
+ __le16 MaximumLength;
+ __le32 BufferOffset; /* offset to buffer */
+} __attribute__((packed)) SECURITY_BUFFER;
+
+typedef struct _NEGOTIATE_MESSAGE {
+ __u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
+ __le32 MessageType; /* NtLmNegotiate = 1 */
+ __le32 NegotiateFlags;
+ SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */
+ SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */
+ /* SECURITY_BUFFER for version info not present since we
+ do not set the version is present flag */
+ char DomainString[0];
+ /* followed by WorkstationString */
+} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+
+typedef struct _CHALLENGE_MESSAGE {
+ __u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
+ __le32 MessageType; /* NtLmChallenge = 2 */
+ SECURITY_BUFFER TargetName;
+ __le32 NegotiateFlags;
+ __u8 Challenge[CIFS_CRYPTO_KEY_SIZE];
+ __u8 Reserved[8];
+ SECURITY_BUFFER TargetInfoArray;
+ /* SECURITY_BUFFER for version info not present since we
+ do not set the version is present flag */
+} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+
+typedef struct _AUTHENTICATE_MESSAGE {
+ __u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
+ __le32 MessageType; /* NtLmsAuthenticate = 3 */
+ SECURITY_BUFFER LmChallengeResponse;
+ SECURITY_BUFFER NtChallengeResponse;
+ SECURITY_BUFFER DomainName;
+ SECURITY_BUFFER UserName;
+ SECURITY_BUFFER WorkstationName;
+ SECURITY_BUFFER SessionKey;
+ __le32 NegotiateFlags;
+ /* SECURITY_BUFFER for version info not present since we
+ do not set the version is present flag */
+ char UserString[0];
+} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
+
+/*
+ * Size of the session key (crypto key encrypted with the password
+ */
+
+int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
+void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
+int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
+ struct cifs_ses *ses,
+ const struct nls_table *nls_cp);
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
new file mode 100644
index 000000000..3925a7bfc
--- /dev/null
+++ b/fs/cifs/readdir.c
@@ -0,0 +1,896 @@
+/*
+ * fs/cifs/readdir.c
+ *
+ * Directory search handling
+ *
+ * Copyright (C) International Business Machines Corp., 2004, 2008
+ * Copyright (C) Red Hat, Inc., 2011
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifsfs.h"
+
+/*
+ * To be safe - for UCS to UTF-8 with strings loaded with the rare long
+ * characters alloc more to account for such multibyte target UTF-8
+ * characters.
+ */
+#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2)
+
+#ifdef CONFIG_CIFS_DEBUG2
+static void dump_cifs_file_struct(struct file *file, char *label)
+{
+ struct cifsFileInfo *cf;
+
+ if (file) {
+ cf = file->private_data;
+ if (cf == NULL) {
+ cifs_dbg(FYI, "empty cifs private file data\n");
+ return;
+ }
+ if (cf->invalidHandle)
+ cifs_dbg(FYI, "invalid handle\n");
+ if (cf->srch_inf.endOfSearch)
+ cifs_dbg(FYI, "end of search\n");
+ if (cf->srch_inf.emptyDir)
+ cifs_dbg(FYI, "empty dir\n");
+ }
+}
+#else
+static inline void dump_cifs_file_struct(struct file *file, char *label)
+{
+}
+#endif /* DEBUG2 */
+
+/*
+ * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
+ *
+ * Find the dentry that matches "name". If there isn't one, create one. If it's
+ * a negative dentry or the uniqueid or filetype(mode) changed,
+ * then drop it and recreate it.
+ */
+static void
+cifs_prime_dcache(struct dentry *parent, struct qstr *name,
+ struct cifs_fattr *fattr)
+{
+ struct dentry *dentry, *alias;
+ struct inode *inode;
+ struct super_block *sb = parent->d_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+
+ cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
+
+ dentry = d_hash_and_lookup(parent, name);
+ if (!dentry) {
+ /*
+ * If we know that the inode will need to be revalidated
+ * immediately, then don't create a new dentry for it.
+ * We'll end up doing an on the wire call either way and
+ * this spares us an invalidation.
+ */
+ if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
+ return;
+retry:
+ dentry = d_alloc_parallel(parent, name, &wq);
+ }
+ if (IS_ERR(dentry))
+ return;
+ if (!d_in_lookup(dentry)) {
+ inode = d_inode(dentry);
+ if (inode) {
+ if (d_mountpoint(dentry)) {
+ dput(dentry);
+ return;
+ }
+ /*
+ * If we're generating inode numbers, then we don't
+ * want to clobber the existing one with the one that
+ * the readdir code created.
+ */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
+ fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;
+
+ /* update inode in place
+ * if both i_ino and i_mode didn't change */
+ if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid &&
+ (inode->i_mode & S_IFMT) ==
+ (fattr->cf_mode & S_IFMT)) {
+ cifs_fattr_to_inode(inode, fattr);
+ dput(dentry);
+ return;
+ }
+ }
+ d_invalidate(dentry);
+ dput(dentry);
+ goto retry;
+ } else {
+ inode = cifs_iget(sb, fattr);
+ if (!inode)
+ inode = ERR_PTR(-ENOMEM);
+ alias = d_splice_alias(inode, dentry);
+ d_lookup_done(dentry);
+ if (alias && !IS_ERR(alias))
+ dput(alias);
+ }
+ dput(dentry);
+}
+
+static void
+cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
+{
+ fattr->cf_uid = cifs_sb->mnt_uid;
+ fattr->cf_gid = cifs_sb->mnt_gid;
+
+ if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+ fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
+ fattr->cf_dtype = DT_DIR;
+ } else {
+ fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
+ fattr->cf_dtype = DT_REG;
+ }
+
+ /*
+ * We need to revalidate it further to make a decision about whether it
+ * is a symbolic link, DFS referral or a reparse point with a direct
+ * access like junctions, deduplicated files, NFS symlinks.
+ */
+ if (fattr->cf_cifsattrs & ATTR_REPARSE)
+ fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+
+ /* non-unix readdir doesn't provide nlink */
+ fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
+
+ if (fattr->cf_cifsattrs & ATTR_READONLY)
+ fattr->cf_mode &= ~S_IWUGO;
+
+ /*
+ * We of course don't get ACL info in FIND_FIRST/NEXT results, so
+ * mark it for revalidation so that "ls -l" will look right. It might
+ * be super-slow, but if we don't do this then the ownership of files
+ * may look wrong since the inodes may not have timed out by the time
+ * "ls" does a stat() call on them.
+ */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
+ fattr->cf_cifsattrs & ATTR_SYSTEM) {
+ if (fattr->cf_eof == 0) {
+ fattr->cf_mode &= ~S_IFMT;
+ fattr->cf_mode |= S_IFIFO;
+ fattr->cf_dtype = DT_FIFO;
+ } else {
+ /*
+ * trying to get the type and mode via SFU can be slow,
+ * so just call those regular files for now, and mark
+ * for reval
+ */
+ fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+ }
+ }
+}
+
+void
+cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
+ struct cifs_sb_info *cifs_sb)
+{
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes);
+ fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+ fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
+ fattr->cf_createtime = le64_to_cpu(info->CreationTime);
+ fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+ fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
+ fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
+
+ cifs_fill_common_info(fattr, cifs_sb);
+}
+
+static void
+cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
+ struct cifs_sb_info *cifs_sb)
+{
+ int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
+
+ memset(fattr, 0, sizeof(*fattr));
+ fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
+ info->LastAccessTime, offset);
+ fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
+ info->LastWriteTime, offset);
+ fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
+ info->LastWriteTime, offset);
+
+ fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
+ fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
+ fattr->cf_eof = le32_to_cpu(info->DataSize);
+
+ cifs_fill_common_info(fattr, cifs_sb);
+}
+
+/* BB eventually need to add the following helper function to
+ resolve NT_STATUS_STOPPED_ON_SYMLINK return code when
+ we try to do FindFirst on (NTFS) directory symlinks */
+/*
+int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
+ unsigned int xid)
+{
+ __u16 fid;
+ int len;
+ int oplock = 0;
+ int rc;
+ struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb);
+ char *tmpbuffer;
+
+ rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
+ OPEN_REPARSE_POINT, &fid, &oplock, NULL,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb);
+ if (!rc) {
+ tmpbuffer = kmalloc(maxpath);
+ rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path,
+ tmpbuffer,
+ maxpath -1,
+ fid,
+ cifs_sb->local_nls);
+ if (CIFSSMBClose(xid, ptcon, fid)) {
+ cifs_dbg(FYI, "Error closing temporary reparsepoint open\n");
+ }
+ }
+}
+ */
+
+static int
+initiate_cifs_search(const unsigned int xid, struct file *file)
+{
+ __u16 search_flags;
+ int rc = 0;
+ char *full_path = NULL;
+ struct cifsFileInfo *cifsFile;
+ struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
+ struct tcon_link *tlink = NULL;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+
+ if (file->private_data == NULL) {
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+ if (cifsFile == NULL) {
+ rc = -ENOMEM;
+ goto error_exit;
+ }
+ spin_lock_init(&cifsFile->file_info_lock);
+ file->private_data = cifsFile;
+ cifsFile->tlink = cifs_get_tlink(tlink);
+ tcon = tlink_tcon(tlink);
+ } else {
+ cifsFile = file->private_data;
+ tcon = tlink_tcon(cifsFile->tlink);
+ }
+
+ server = tcon->ses->server;
+
+ if (!server->ops->query_dir_first) {
+ rc = -ENOSYS;
+ goto error_exit;
+ }
+
+ cifsFile->invalidHandle = true;
+ cifsFile->srch_inf.endOfSearch = false;
+
+ full_path = build_path_from_dentry(file_dentry(file));
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto error_exit;
+ }
+
+ cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
+
+ffirst_retry:
+ /* test for Unix extensions */
+ /* but now check for them on the share/mount not on the SMB session */
+ /* if (cap_unix(tcon->ses) { */
+ if (tcon->unix_ext)
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
+ else if ((tcon->ses->capabilities &
+ tcon->ses->server->vals->cap_nt_find) == 0) {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
+ } else /* not srvinos - BB fixme add check for backlevel? */ {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
+ }
+
+ search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
+ if (backup_cred(cifs_sb))
+ search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
+
+ rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
+ &cifsFile->fid, search_flags,
+ &cifsFile->srch_inf);
+
+ if (rc == 0)
+ cifsFile->invalidHandle = false;
+ /* BB add following call to handle readdir on new NTFS symlink errors
+ else if STATUS_STOPPED_ON_SYMLINK
+ call get_symlink_reparse_path and retry with new path */
+ else if ((rc == -EOPNOTSUPP) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
+ cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
+ goto ffirst_retry;
+ }
+error_exit:
+ kfree(full_path);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+/* return length of unicode string in bytes */
+static int cifs_unicode_bytelen(const char *str)
+{
+ int len;
+ const __le16 *ustr = (const __le16 *)str;
+
+ for (len = 0; len <= PATH_MAX; len++) {
+ if (ustr[len] == 0)
+ return len << 1;
+ }
+ cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n");
+ return len << 1;
+}
+
+static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
+{
+ char *new_entry;
+ FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
+
+ if (level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO *pfData;
+ pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
+
+ new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
+ pfData->FileNameLength;
+ } else {
+ u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset);
+
+ if (old_entry + next_offset < old_entry) {
+ cifs_dbg(VFS, "invalid offset %u\n", next_offset);
+ return NULL;
+ }
+ new_entry = old_entry + next_offset;
+ }
+ cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry);
+ /* validate that new_entry is not past end of SMB */
+ if (new_entry >= end_of_smb) {
+ cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n",
+ new_entry, end_of_smb, old_entry);
+ return NULL;
+ } else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
+ (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
+ || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
+ (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) {
+ cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n",
+ new_entry, end_of_smb);
+ return NULL;
+ } else
+ return new_entry;
+
+}
+
+struct cifs_dirent {
+ const char *name;
+ size_t namelen;
+ u32 resume_key;
+ u64 ino;
+};
+
+static void cifs_fill_dirent_unix(struct cifs_dirent *de,
+ const FILE_UNIX_INFO *info, bool is_unicode)
+{
+ de->name = &info->FileName[0];
+ if (is_unicode)
+ de->namelen = cifs_unicode_bytelen(de->name);
+ else
+ de->namelen = strnlen(de->name, PATH_MAX);
+ de->resume_key = info->ResumeKey;
+ de->ino = le64_to_cpu(info->basic.UniqueId);
+}
+
+static void cifs_fill_dirent_dir(struct cifs_dirent *de,
+ const FILE_DIRECTORY_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_full(struct cifs_dirent *de,
+ const FILE_FULL_DIRECTORY_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_search(struct cifs_dirent *de,
+ const SEARCH_ID_FULL_DIR_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+ de->ino = le64_to_cpu(info->UniqueId);
+}
+
+static void cifs_fill_dirent_both(struct cifs_dirent *de,
+ const FILE_BOTH_DIRECTORY_INFO *info)
+{
+ de->name = &info->FileName[0];
+ de->namelen = le32_to_cpu(info->FileNameLength);
+ de->resume_key = info->FileIndex;
+}
+
+static void cifs_fill_dirent_std(struct cifs_dirent *de,
+ const FIND_FILE_STANDARD_INFO *info)
+{
+ de->name = &info->FileName[0];
+ /* one byte length, no endianess conversion */
+ de->namelen = info->FileNameLength;
+ de->resume_key = info->ResumeKey;
+}
+
+static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
+ u16 level, bool is_unicode)
+{
+ memset(de, 0, sizeof(*de));
+
+ switch (level) {
+ case SMB_FIND_FILE_UNIX:
+ cifs_fill_dirent_unix(de, info, is_unicode);
+ break;
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ cifs_fill_dirent_dir(de, info);
+ break;
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ cifs_fill_dirent_full(de, info);
+ break;
+ case SMB_FIND_FILE_ID_FULL_DIR_INFO:
+ cifs_fill_dirent_search(de, info);
+ break;
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ cifs_fill_dirent_both(de, info);
+ break;
+ case SMB_FIND_FILE_INFO_STANDARD:
+ cifs_fill_dirent_std(de, info);
+ break;
+ default:
+ cifs_dbg(FYI, "Unknown findfirst level %d\n", level);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define UNICODE_DOT cpu_to_le16(0x2e)
+
+/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
+static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
+{
+ int rc = 0;
+
+ if (!de->name)
+ return 0;
+
+ if (is_unicode) {
+ __le16 *ufilename = (__le16 *)de->name;
+ if (de->namelen == 2) {
+ /* check for . */
+ if (ufilename[0] == UNICODE_DOT)
+ rc = 1;
+ } else if (de->namelen == 4) {
+ /* check for .. */
+ if (ufilename[0] == UNICODE_DOT &&
+ ufilename[1] == UNICODE_DOT)
+ rc = 2;
+ }
+ } else /* ASCII */ {
+ if (de->namelen == 1) {
+ if (de->name[0] == '.')
+ rc = 1;
+ } else if (de->namelen == 2) {
+ if (de->name[0] == '.' && de->name[1] == '.')
+ rc = 2;
+ }
+ }
+
+ return rc;
+}
+
+/* Check if directory that we are searching has changed so we can decide
+ whether we can use the cached search results from the previous search */
+static int is_dir_changed(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
+
+ if (cifsInfo->time == 0)
+ return 1; /* directory was changed, perhaps due to unlink */
+ else
+ return 0;
+
+}
+
+static int cifs_save_resume_key(const char *current_entry,
+ struct cifsFileInfo *file_info)
+{
+ struct cifs_dirent de;
+ int rc;
+
+ rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
+ file_info->srch_inf.unicode);
+ if (!rc) {
+ file_info->srch_inf.presume_name = de.name;
+ file_info->srch_inf.resume_name_len = de.namelen;
+ file_info->srch_inf.resume_key = de.resume_key;
+ }
+ return rc;
+}
+
+/*
+ * Find the corresponding entry in the search. Note that the SMB server returns
+ * search entries for . and .. which complicates logic here if we choose to
+ * parse for them and we do not assume that they are located in the findfirst
+ * return buffer. We start counting in the buffer with entry 2 and increment for
+ * every entry (do not increment for . or .. entry).
+ */
+static int
+find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
+ struct file *file, char **current_entry, int *num_to_ret)
+{
+ __u16 search_flags;
+ int rc = 0;
+ int pos_in_buf = 0;
+ loff_t first_entry_in_buffer;
+ loff_t index_to_find = pos;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ /* check if index in the buffer */
+
+ if (!server->ops->query_dir_first || !server->ops->query_dir_next)
+ return -ENOSYS;
+
+ if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
+ return -ENOENT;
+
+ *current_entry = NULL;
+ first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
+ cfile->srch_inf.entries_in_buffer;
+
+ /*
+ * If first entry in buf is zero then is first buffer
+ * in search response data which means it is likely . and ..
+ * will be in this buffer, although some servers do not return
+ * . and .. for the root of a drive and for those we need
+ * to start two entries earlier.
+ */
+
+ dump_cifs_file_struct(file, "In fce ");
+ if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
+ is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
+ /* close and restart search */
+ cifs_dbg(FYI, "search backing up - close and restart search\n");
+ spin_lock(&cfile->file_info_lock);
+ if (server->ops->dir_needs_close(cfile)) {
+ cfile->invalidHandle = true;
+ spin_unlock(&cfile->file_info_lock);
+ if (server->ops->close_dir)
+ server->ops->close_dir(xid, tcon, &cfile->fid);
+ } else
+ spin_unlock(&cfile->file_info_lock);
+ if (cfile->srch_inf.ntwrk_buf_start) {
+ cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
+ if (cfile->srch_inf.smallBuf)
+ cifs_small_buf_release(cfile->srch_inf.
+ ntwrk_buf_start);
+ else
+ cifs_buf_release(cfile->srch_inf.
+ ntwrk_buf_start);
+ cfile->srch_inf.ntwrk_buf_start = NULL;
+ }
+ rc = initiate_cifs_search(xid, file);
+ if (rc) {
+ cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
+ rc);
+ return rc;
+ }
+ /* FindFirst/Next set last_entry to NULL on malformed reply */
+ if (cfile->srch_inf.last_entry)
+ cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
+ }
+
+ search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
+ if (backup_cred(cifs_sb))
+ search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
+
+ while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
+ (rc == 0) && !cfile->srch_inf.endOfSearch) {
+ cifs_dbg(FYI, "calling findnext2\n");
+ rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
+ search_flags,
+ &cfile->srch_inf);
+ /* FindFirst/Next set last_entry to NULL on malformed reply */
+ if (cfile->srch_inf.last_entry)
+ cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
+ if (rc)
+ return -ENOENT;
+ }
+ if (index_to_find < cfile->srch_inf.index_of_last_entry) {
+ /* we found the buffer that contains the entry */
+ /* scan and find it */
+ int i;
+ char *cur_ent;
+ char *end_of_smb;
+
+ if (cfile->srch_inf.ntwrk_buf_start == NULL) {
+ cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n");
+ return -EIO;
+ }
+
+ end_of_smb = cfile->srch_inf.ntwrk_buf_start +
+ server->ops->calc_smb_size(
+ cfile->srch_inf.ntwrk_buf_start,
+ server);
+
+ cur_ent = cfile->srch_inf.srch_entries_start;
+ first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
+ - cfile->srch_inf.entries_in_buffer;
+ pos_in_buf = index_to_find - first_entry_in_buffer;
+ cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf);
+
+ for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
+ /* go entry by entry figuring out which is first */
+ cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
+ cfile->srch_inf.info_level);
+ }
+ if ((cur_ent == NULL) && (i < pos_in_buf)) {
+ /* BB fixme - check if we should flag this error */
+ cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n",
+ pos_in_buf, index_to_find, rc);
+ }
+ rc = 0;
+ *current_entry = cur_ent;
+ } else {
+ cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");
+ return 0;
+ }
+
+ if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
+ cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n");
+ *num_to_ret = 0;
+ } else
+ *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
+
+ return rc;
+}
+
+static int cifs_filldir(char *find_entry, struct file *file,
+ struct dir_context *ctx,
+ char *scratch_buf, unsigned int max_len)
+{
+ struct cifsFileInfo *file_info = file->private_data;
+ struct super_block *sb = file_inode(file)->i_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_dirent de = { NULL, };
+ struct cifs_fattr fattr;
+ struct qstr name;
+ int rc = 0;
+ ino_t ino;
+
+ rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
+ file_info->srch_inf.unicode);
+ if (rc)
+ return rc;
+
+ if (de.namelen > max_len) {
+ cifs_dbg(VFS, "bad search response length %zd past smb end\n",
+ de.namelen);
+ return -EINVAL;
+ }
+
+ /* skip . and .. since we added them first */
+ if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
+ return 0;
+
+ if (file_info->srch_inf.unicode) {
+ struct nls_table *nlt = cifs_sb->local_nls;
+ int map_type;
+
+ map_type = cifs_remap(cifs_sb);
+ name.name = scratch_buf;
+ name.len =
+ cifs_from_utf16((char *)name.name, (__le16 *)de.name,
+ UNICODE_NAME_MAX,
+ min_t(size_t, de.namelen,
+ (size_t)max_len), nlt, map_type);
+ name.len -= nls_nullsize(nlt);
+ } else {
+ name.name = de.name;
+ name.len = de.namelen;
+ }
+
+ switch (file_info->srch_inf.info_level) {
+ case SMB_FIND_FILE_UNIX:
+ cifs_unix_basic_to_fattr(&fattr,
+ &((FILE_UNIX_INFO *)find_entry)->basic,
+ cifs_sb);
+ break;
+ case SMB_FIND_FILE_INFO_STANDARD:
+ cifs_std_info_to_fattr(&fattr,
+ (FIND_FILE_STANDARD_INFO *)find_entry,
+ cifs_sb);
+ break;
+ default:
+ cifs_dir_info_to_fattr(&fattr,
+ (FILE_DIRECTORY_INFO *)find_entry,
+ cifs_sb);
+ break;
+ }
+
+ if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
+ fattr.cf_uniqueid = de.ino;
+ } else {
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
+ cifs_autodisable_serverino(cifs_sb);
+ }
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
+ couldbe_mf_symlink(&fattr))
+ /*
+ * trying to get the type and mode can be slow,
+ * so just call those regular files for now, and mark
+ * for reval
+ */
+ fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
+
+ cifs_prime_dcache(file_dentry(file), &name, &fattr);
+
+ ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
+ return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype);
+}
+
+
+int cifs_readdir(struct file *file, struct dir_context *ctx)
+{
+ int rc = 0;
+ unsigned int xid;
+ int i;
+ struct cifs_tcon *tcon;
+ struct cifsFileInfo *cifsFile = NULL;
+ char *current_entry;
+ int num_to_fill = 0;
+ char *tmp_buf = NULL;
+ char *end_of_smb;
+ unsigned int max_len;
+
+ xid = get_xid();
+
+ /*
+ * Ensure FindFirst doesn't fail before doing filldir() for '.' and
+ * '..'. Otherwise we won't be able to notify VFS in case of failure.
+ */
+ if (file->private_data == NULL) {
+ rc = initiate_cifs_search(xid, file);
+ cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
+ if (rc)
+ goto rddir2_exit;
+ }
+
+ if (!dir_emit_dots(file, ctx))
+ goto rddir2_exit;
+
+ /* 1) If search is active,
+ is in current search buffer?
+ if it before then restart search
+ if after then keep searching till find it */
+
+ cifsFile = file->private_data;
+ if (cifsFile->srch_inf.endOfSearch) {
+ if (cifsFile->srch_inf.emptyDir) {
+ cifs_dbg(FYI, "End of search, empty dir\n");
+ rc = 0;
+ goto rddir2_exit;
+ }
+ } /* else {
+ cifsFile->invalidHandle = true;
+ tcon->ses->server->close(xid, tcon, &cifsFile->fid);
+ } */
+
+ tcon = tlink_tcon(cifsFile->tlink);
+ rc = find_cifs_entry(xid, tcon, ctx->pos, file, &current_entry,
+ &num_to_fill);
+ if (rc) {
+ cifs_dbg(FYI, "fce error %d\n", rc);
+ goto rddir2_exit;
+ } else if (current_entry != NULL) {
+ cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
+ } else {
+ cifs_dbg(FYI, "could not find entry\n");
+ goto rddir2_exit;
+ }
+ cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
+ num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
+ max_len = tcon->ses->server->ops->calc_smb_size(
+ cifsFile->srch_inf.ntwrk_buf_start,
+ tcon->ses->server);
+ end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
+
+ tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
+ if (tmp_buf == NULL) {
+ rc = -ENOMEM;
+ goto rddir2_exit;
+ }
+
+ for (i = 0; i < num_to_fill; i++) {
+ if (current_entry == NULL) {
+ /* evaluate whether this case is an error */
+ cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n",
+ num_to_fill, i);
+ break;
+ }
+ /*
+ * if buggy server returns . and .. late do we want to
+ * check for that here?
+ */
+ *tmp_buf = 0;
+ rc = cifs_filldir(current_entry, file, ctx,
+ tmp_buf, max_len);
+ if (rc) {
+ if (rc > 0)
+ rc = 0;
+ break;
+ }
+
+ ctx->pos++;
+ if (ctx->pos ==
+ cifsFile->srch_inf.index_of_last_entry) {
+ cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
+ ctx->pos, tmp_buf);
+ cifs_save_resume_key(current_entry, cifsFile);
+ break;
+ } else
+ current_entry =
+ nxt_dir_entry(current_entry, end_of_smb,
+ cifsFile->srch_inf.info_level);
+ }
+ kfree(tmp_buf);
+
+rddir2_exit:
+ free_xid(xid);
+ return rc;
+}
diff --git a/fs/cifs/rfc1002pdu.h b/fs/cifs/rfc1002pdu.h
new file mode 100644
index 000000000..8b69fcceb
--- /dev/null
+++ b/fs/cifs/rfc1002pdu.h
@@ -0,0 +1,74 @@
+/*
+ * fs/cifs/rfc1002pdu.h
+ *
+ * Protocol Data Unit definitions for RFC 1001/1002 support
+ *
+ * Copyright (c) International Business Machines Corp., 2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */
+
+ /* RFC 1002 session packet types */
+#define RFC1002_SESSION_MESSAGE 0x00
+#define RFC1002_SESSION_REQUEST 0x81
+#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82
+#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83
+#define RFC1002_RETARGET_SESSION_RESPONSE 0x84
+#define RFC1002_SESSION_KEEP_ALIVE 0x85
+
+ /* RFC 1002 flags (only one defined */
+#define RFC1002_LENGTH_EXTEND 0x80 /* high order bit of length (ie +64K) */
+
+struct rfc1002_session_packet {
+ __u8 type;
+ __u8 flags;
+ __u16 length;
+ union {
+ struct {
+ __u8 called_len;
+ __u8 called_name[32];
+ __u8 scope1; /* null */
+ __u8 calling_len;
+ __u8 calling_name[32];
+ __u8 scope2; /* null */
+ } __attribute__((packed)) session_req;
+ struct {
+ __u32 retarget_ip_addr;
+ __u16 port;
+ } __attribute__((packed)) retarget_resp;
+ __u8 neg_ses_resp_error_code;
+ /* POSITIVE_SESSION_RESPONSE packet does not include trailer.
+ SESSION_KEEP_ALIVE packet also does not include a trailer.
+ Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */
+ } __attribute__((packed)) trailer;
+} __attribute__((packed));
+
+/* Negative Session Response error codes */
+#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */
+#define RFC1002_NOT_LISTENING_CALLING 0x81 /* not listening on calling name */
+#define RFC1002_NOT_PRESENT 0x82 /* called name not present */
+#define RFC1002_INSUFFICIENT_RESOURCE 0x83
+#define RFC1002_UNSPECIFIED_ERROR 0x8F
+
+/* RFC 1002 Datagram service packets are not defined here as they
+are not needed for the network filesystem client unless we plan on
+implementing broadcast resolution of the server ip address (from
+server netbios name). Currently server names are resolved only via DNS
+(tcp name) or ip address or an /etc/hosts equivalent mapping to ip address.*/
+
+#define DEFAULT_CIFS_CALLED_NAME "*SMBSERVER "
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
new file mode 100644
index 000000000..0113dba28
--- /dev/null
+++ b/fs/cifs/sess.c
@@ -0,0 +1,1480 @@
+/*
+ * fs/cifs/sess.c
+ *
+ * SMB/CIFS session setup handling routines
+ *
+ * Copyright (c) International Business Machines Corp., 2006, 2009
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "ntlmssp.h"
+#include "nterr.h"
+#include <linux/utsname.h>
+#include <linux/slab.h>
+#include "cifs_spnego.h"
+
+static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
+{
+ __u32 capabilities = 0;
+
+ /* init fields common to all four types of SessSetup */
+ /* Note that offsets for first seven fields in req struct are same */
+ /* in CIFS Specs so does not matter which of 3 forms of struct */
+ /* that we use in next few lines */
+ /* Note that header is initialized to zero in header_assemble */
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
+ CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
+ USHRT_MAX));
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+ pSMB->req.VcNumber = cpu_to_le16(1);
+
+ /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
+
+ /* BB verify whether signing required on neg or just on auth frame
+ (and NTLM case) */
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+
+ if (ses->server->sign)
+ pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ if (ses->capabilities & CAP_UNIX)
+ capabilities |= CAP_UNIX;
+
+ return capabilities;
+}
+
+static void
+unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* Copy OS version */
+ bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32,
+ nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release,
+ 32, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* trailing null */
+
+ bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 32, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* trailing null */
+
+ *pbcc_area = bcc_ptr;
+}
+
+static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* copy domain */
+ if (ses->domainName == NULL) {
+ /* Sending null domain better than using a bogus domain name (as
+ we did briefly in 2.6.18) since server will use its default */
+ *bcc_ptr = 0;
+ *(bcc_ptr+1) = 0;
+ bytes_ret = 0;
+ } else
+ bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName,
+ CIFS_MAX_DOMAINNAME_LEN, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* account for null terminator */
+
+ *pbcc_area = bcc_ptr;
+}
+
+
+static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* BB FIXME add check that strings total less
+ than 335 or will need to send them as arrays */
+
+ /* unicode strings, must be word aligned before the call */
+/* if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ } */
+ /* copy user */
+ if (ses->user_name == NULL) {
+ /* null user mount */
+ *bcc_ptr = 0;
+ *(bcc_ptr+1) = 0;
+ } else {
+ bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name,
+ CIFS_MAX_USERNAME_LEN, nls_cp);
+ }
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* account for null termination */
+
+ unicode_domain_string(&bcc_ptr, ses, nls_cp);
+ unicode_oslm_strings(&bcc_ptr, nls_cp);
+
+ *pbcc_area = bcc_ptr;
+}
+
+static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+
+ /* copy user */
+ /* BB what about null user mounts - check that we do this BB */
+ /* copy user */
+ if (ses->user_name != NULL) {
+ strncpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN);
+ bcc_ptr += strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
+ }
+ /* else null user mount */
+ *bcc_ptr = 0;
+ bcc_ptr++; /* account for null termination */
+
+ /* copy domain */
+ if (ses->domainName != NULL) {
+ strncpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+ bcc_ptr += strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+ } /* else we will send a null domain name
+ so the server will default to its own domain */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+
+ /* BB check for overflow here */
+
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, init_utsname()->release);
+ bcc_ptr += strlen(init_utsname()->release) + 1;
+
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+
+ *pbcc_area = bcc_ptr;
+}
+
+static void
+decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ int len;
+ char *data = *pbcc_area;
+
+ cifs_dbg(FYI, "bleft %d\n", bleft);
+
+ kfree(ses->serverOS);
+ ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
+ cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS);
+ len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
+ data += len;
+ bleft -= len;
+ if (bleft <= 0)
+ return;
+
+ kfree(ses->serverNOS);
+ ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
+ cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS);
+ len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;
+ data += len;
+ bleft -= len;
+ if (bleft <= 0)
+ return;
+
+ kfree(ses->serverDomain);
+ ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp);
+ cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain);
+
+ return;
+}
+
+static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
+ struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ int len;
+ char *bcc_ptr = *pbcc_area;
+
+ cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft);
+
+ len = strnlen(bcc_ptr, bleft);
+ if (len >= bleft)
+ return;
+
+ kfree(ses->serverOS);
+
+ ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
+ if (ses->serverOS) {
+ strncpy(ses->serverOS, bcc_ptr, len);
+ if (strncmp(ses->serverOS, "OS/2", 4) == 0)
+ cifs_dbg(FYI, "OS/2 server\n");
+ }
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ len = strnlen(bcc_ptr, bleft);
+ if (len >= bleft)
+ return;
+
+ kfree(ses->serverNOS);
+
+ ses->serverNOS = kzalloc(len + 1, GFP_KERNEL);
+ if (ses->serverNOS)
+ strncpy(ses->serverNOS, bcc_ptr, len);
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ len = strnlen(bcc_ptr, bleft);
+ if (len > bleft)
+ return;
+
+ /* No domain field in LANMAN case. Domain is
+ returned by old servers in the SMB negprot response */
+ /* BB For newer servers which do not support Unicode,
+ but thus do return domain here we could add parsing
+ for it later, but it is not very important */
+ cifs_dbg(FYI, "ascii: bytes left %d\n", bleft);
+}
+
+int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
+ struct cifs_ses *ses)
+{
+ unsigned int tioffset; /* challenge message target info area */
+ unsigned int tilen; /* challenge message target info area length */
+
+ CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr;
+
+ if (blob_len < sizeof(CHALLENGE_MESSAGE)) {
+ cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len);
+ return -EINVAL;
+ }
+
+ if (memcmp(pblob->Signature, "NTLMSSP", 8)) {
+ cifs_dbg(VFS, "blob signature incorrect %s\n",
+ pblob->Signature);
+ return -EINVAL;
+ }
+ if (pblob->MessageType != NtLmChallenge) {
+ cifs_dbg(VFS, "Incorrect message type %d\n",
+ pblob->MessageType);
+ return -EINVAL;
+ }
+
+ memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
+ /* BB we could decode pblob->NegotiateFlags; some may be useful */
+ /* In particular we can examine sign flags */
+ /* BB spec says that if AvId field of MsvAvTimestamp is populated then
+ we must set the MIC field of the AUTHENTICATE_MESSAGE */
+ ses->ntlmssp->server_flags = le32_to_cpu(pblob->NegotiateFlags);
+ tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset);
+ tilen = le16_to_cpu(pblob->TargetInfoArray.Length);
+ if (tioffset > blob_len || tioffset + tilen > blob_len) {
+ cifs_dbg(VFS, "tioffset + tilen too high %u + %u",
+ tioffset, tilen);
+ return -EINVAL;
+ }
+ if (tilen) {
+ ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen,
+ GFP_KERNEL);
+ if (!ses->auth_key.response) {
+ cifs_dbg(VFS, "Challenge target info alloc failure");
+ return -ENOMEM;
+ }
+ ses->auth_key.len = tilen;
+ }
+
+ return 0;
+}
+
+/* BB Move to ntlmssp.c eventually */
+
+/* We do not malloc the blob, it is passed in pbuffer, because
+ it is fixed size, and small, making this approach cleaner */
+void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
+ struct cifs_ses *ses)
+{
+ NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
+ __u32 flags;
+
+ memset(pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));
+ memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
+ sec_blob->MessageType = NtLmNegotiate;
+
+ /* BB is NTLMV2 session security format easier to use here? */
+ flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
+ NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
+ NTLMSSP_NEGOTIATE_SEAL;
+ if (ses->server->sign)
+ flags |= NTLMSSP_NEGOTIATE_SIGN;
+ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+
+ sec_blob->NegotiateFlags = cpu_to_le32(flags);
+
+ sec_blob->WorkstationName.BufferOffset = 0;
+ sec_blob->WorkstationName.Length = 0;
+ sec_blob->WorkstationName.MaximumLength = 0;
+
+ /* Domain name is sent on the Challenge not Negotiate NTLMSSP request */
+ sec_blob->DomainName.BufferOffset = 0;
+ sec_blob->DomainName.Length = 0;
+ sec_blob->DomainName.MaximumLength = 0;
+}
+
+static int size_of_ntlmssp_blob(struct cifs_ses *ses)
+{
+ int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
+ - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
+
+ if (ses->domainName)
+ sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+ else
+ sz += 2;
+
+ if (ses->user_name)
+ sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
+ else
+ sz += 2;
+
+ return sz;
+}
+
+int build_ntlmssp_auth_blob(unsigned char **pbuffer,
+ u16 *buflen,
+ struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ int rc;
+ AUTHENTICATE_MESSAGE *sec_blob;
+ __u32 flags;
+ unsigned char *tmp;
+
+ rc = setup_ntlmv2_rsp(ses, nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
+ *buflen = 0;
+ goto setup_ntlmv2_ret;
+ }
+ *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL);
+ if (!*pbuffer) {
+ rc = -ENOMEM;
+ cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc);
+ *buflen = 0;
+ goto setup_ntlmv2_ret;
+ }
+ sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer;
+
+ memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
+ sec_blob->MessageType = NtLmAuthenticate;
+
+ flags = NTLMSSP_NEGOTIATE_56 |
+ NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
+ NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
+ NTLMSSP_NEGOTIATE_SEAL;
+ if (ses->server->sign)
+ flags |= NTLMSSP_NEGOTIATE_SIGN;
+ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+
+ tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
+ sec_blob->NegotiateFlags = cpu_to_le32(flags);
+
+ sec_blob->LmChallengeResponse.BufferOffset =
+ cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE));
+ sec_blob->LmChallengeResponse.Length = 0;
+ sec_blob->LmChallengeResponse.MaximumLength = 0;
+
+ sec_blob->NtChallengeResponse.BufferOffset =
+ cpu_to_le32(tmp - *pbuffer);
+ if (ses->user_name != NULL) {
+ memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
+
+ sec_blob->NtChallengeResponse.Length =
+ cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ sec_blob->NtChallengeResponse.MaximumLength =
+ cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ } else {
+ /*
+ * don't send an NT Response for anonymous access
+ */
+ sec_blob->NtChallengeResponse.Length = 0;
+ sec_blob->NtChallengeResponse.MaximumLength = 0;
+ }
+
+ if (ses->domainName == NULL) {
+ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->DomainName.Length = 0;
+ sec_blob->DomainName.MaximumLength = 0;
+ tmp += 2;
+ } else {
+ int len;
+ len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
+ CIFS_MAX_DOMAINNAME_LEN, nls_cp);
+ len *= 2; /* unicode is 2 bytes each */
+ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->DomainName.Length = cpu_to_le16(len);
+ sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
+ tmp += len;
+ }
+
+ if (ses->user_name == NULL) {
+ sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->UserName.Length = 0;
+ sec_blob->UserName.MaximumLength = 0;
+ tmp += 2;
+ } else {
+ int len;
+ len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
+ CIFS_MAX_USERNAME_LEN, nls_cp);
+ len *= 2; /* unicode is 2 bytes each */
+ sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->UserName.Length = cpu_to_le16(len);
+ sec_blob->UserName.MaximumLength = cpu_to_le16(len);
+ tmp += len;
+ }
+
+ sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->WorkstationName.Length = 0;
+ sec_blob->WorkstationName.MaximumLength = 0;
+ tmp += 2;
+
+ if (((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) ||
+ (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
+ && !calc_seckey(ses)) {
+ memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
+ sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
+ sec_blob->SessionKey.MaximumLength =
+ cpu_to_le16(CIFS_CPHTXT_SIZE);
+ tmp += CIFS_CPHTXT_SIZE;
+ } else {
+ sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
+ sec_blob->SessionKey.Length = 0;
+ sec_blob->SessionKey.MaximumLength = 0;
+ }
+
+ *buflen = tmp - *pbuffer;
+setup_ntlmv2_ret:
+ return rc;
+}
+
+enum securityEnum
+cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
+{
+ switch (server->negflavor) {
+ case CIFS_NEGFLAVOR_EXTENDED:
+ switch (requested) {
+ case Kerberos:
+ case RawNTLMSSP:
+ return requested;
+ case Unspecified:
+ if (server->sec_ntlmssp &&
+ (global_secflags & CIFSSEC_MAY_NTLMSSP))
+ return RawNTLMSSP;
+ if ((server->sec_kerberos || server->sec_mskerberos) &&
+ (global_secflags & CIFSSEC_MAY_KRB5))
+ return Kerberos;
+ /* Fallthrough */
+ default:
+ return Unspecified;
+ }
+ case CIFS_NEGFLAVOR_UNENCAP:
+ switch (requested) {
+ case NTLM:
+ case NTLMv2:
+ return requested;
+ case Unspecified:
+ if (global_secflags & CIFSSEC_MAY_NTLMV2)
+ return NTLMv2;
+ if (global_secflags & CIFSSEC_MAY_NTLM)
+ return NTLM;
+ default:
+ /* Fallthrough to attempt LANMAN authentication next */
+ break;
+ }
+ case CIFS_NEGFLAVOR_LANMAN:
+ switch (requested) {
+ case LANMAN:
+ return requested;
+ case Unspecified:
+ if (global_secflags & CIFSSEC_MAY_LANMAN)
+ return LANMAN;
+ /* Fallthrough */
+ default:
+ return Unspecified;
+ }
+ default:
+ return Unspecified;
+ }
+}
+
+struct sess_data {
+ unsigned int xid;
+ struct cifs_ses *ses;
+ struct nls_table *nls_cp;
+ void (*func)(struct sess_data *);
+ int result;
+
+ /* we will send the SMB in three pieces:
+ * a fixed length beginning part, an optional
+ * SPNEGO blob (which can be zero length), and a
+ * last part which will include the strings
+ * and rest of bcc area. This allows us to avoid
+ * a large buffer 17K allocation
+ */
+ int buf0_type;
+ struct kvec iov[3];
+};
+
+static int
+sess_alloc_buffer(struct sess_data *sess_data, int wct)
+{
+ int rc;
+ struct cifs_ses *ses = sess_data->ses;
+ struct smb_hdr *smb_buf;
+
+ rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
+ (void **)&smb_buf);
+
+ if (rc)
+ return rc;
+
+ sess_data->iov[0].iov_base = (char *)smb_buf;
+ sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
+ /*
+ * This variable will be used to clear the buffer
+ * allocated above in case of any error in the calling function.
+ */
+ sess_data->buf0_type = CIFS_SMALL_BUFFER;
+
+ /* 2000 big enough to fit max user, domain, NOS name etc. */
+ sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL);
+ if (!sess_data->iov[2].iov_base) {
+ rc = -ENOMEM;
+ goto out_free_smb_buf;
+ }
+
+ return 0;
+
+out_free_smb_buf:
+ cifs_small_buf_release(smb_buf);
+ sess_data->iov[0].iov_base = NULL;
+ sess_data->iov[0].iov_len = 0;
+ sess_data->buf0_type = CIFS_NO_BUFFER;
+ return rc;
+}
+
+static void
+sess_free_buffer(struct sess_data *sess_data)
+{
+
+ free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
+ sess_data->buf0_type = CIFS_NO_BUFFER;
+ kfree(sess_data->iov[2].iov_base);
+}
+
+static int
+sess_establish_session(struct sess_data *sess_data)
+{
+ struct cifs_ses *ses = sess_data->ses;
+
+ mutex_lock(&ses->server->srv_mutex);
+ if (!ses->server->session_estab) {
+ if (ses->server->sign) {
+ ses->server->session_key.response =
+ kmemdup(ses->auth_key.response,
+ ses->auth_key.len, GFP_KERNEL);
+ if (!ses->server->session_key.response) {
+ mutex_unlock(&ses->server->srv_mutex);
+ return -ENOMEM;
+ }
+ ses->server->session_key.len =
+ ses->auth_key.len;
+ }
+ ses->server->sequence_number = 0x2;
+ ses->server->session_estab = true;
+ }
+ mutex_unlock(&ses->server->srv_mutex);
+
+ cifs_dbg(FYI, "CIFS session established successfully\n");
+ spin_lock(&GlobalMid_Lock);
+ ses->status = CifsGood;
+ ses->need_reconnect = false;
+ spin_unlock(&GlobalMid_Lock);
+
+ return 0;
+}
+
+static int
+sess_sendreceive(struct sess_data *sess_data)
+{
+ int rc;
+ struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
+ __u16 count;
+ struct kvec rsp_iov = { NULL, 0 };
+
+ count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
+ smb_buf->smb_buf_length =
+ cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count);
+ put_bcc(count, smb_buf);
+
+ rc = SendReceive2(sess_data->xid, sess_data->ses,
+ sess_data->iov, 3 /* num_iovecs */,
+ &sess_data->buf0_type,
+ CIFS_LOG_ERROR, &rsp_iov);
+ cifs_small_buf_release(sess_data->iov[0].iov_base);
+ memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
+
+ return rc;
+}
+
+/*
+ * LANMAN and plaintext are less secure and off by default.
+ * So we make this explicitly be turned on in kconfig (in the
+ * build) and turned on at runtime (changed from the default)
+ * in proc/fs/cifs or via mount parm. Unfortunately this is
+ * needed for old Win (e.g. Win95), some obscure NAS and OS/2
+ */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+static void
+sess_auth_lanman(struct sess_data *sess_data)
+{
+ int rc = 0;
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ char *bcc_ptr;
+ struct cifs_ses *ses = sess_data->ses;
+ char lnm_session_key[CIFS_AUTH_RESP_SIZE];
+ __u32 capabilities;
+ __u16 bytes_remaining;
+
+ /* lanman 2 style sessionsetup */
+ /* wct = 10 */
+ rc = sess_alloc_buffer(sess_data, 10);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ bcc_ptr = sess_data->iov[2].iov_base;
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+ pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
+
+ if (ses->user_name != NULL) {
+ /* no capabilities flags in old lanman negotiation */
+ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+
+ /* Calculate hash with password and copy into bcc_ptr.
+ * Encryption Key (stored as in cryptkey) gets used if the
+ * security mode bit in Negottiate Protocol response states
+ * to use challenge/response method (i.e. Password bit is 1).
+ */
+ rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
+ ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
+ true : false, lnm_session_key);
+ if (rc)
+ goto out;
+
+ memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ } else {
+ pSMB->old_req.PasswordLength = 0;
+ }
+
+ /*
+ * can not sign if LANMAN negotiated so no need
+ * to calculate signing key? but what if server
+ * changed to do higher than lanman dialect and
+ * we reconnected would we ever calc signing_key?
+ */
+
+ cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n");
+ /* Unicode not allowed for LANMAN dialects */
+ ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+
+ sess_data->iov[2].iov_len = (long) bcc_ptr -
+ (long) sess_data->iov[2].iov_base;
+
+ rc = sess_sendreceive(sess_data);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+
+ /* lanman response has a word count of 3 */
+ if (smb_buf->WordCount != 3) {
+ rc = -EIO;
+ cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+ goto out;
+ }
+
+ if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
+ cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
+
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
+
+ bytes_remaining = get_bcc(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ /* BB check if Unicode and decode strings */
+ if (bytes_remaining == 0) {
+ /* no string area to decode, do nothing */
+ } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+ /* unicode string area must be word-aligned */
+ if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
+ ++bcc_ptr;
+ --bytes_remaining;
+ }
+ decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ } else {
+ decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ }
+
+ rc = sess_establish_session(sess_data);
+out:
+ sess_data->result = rc;
+ sess_data->func = NULL;
+ sess_free_buffer(sess_data);
+}
+
+#endif
+
+static void
+sess_auth_ntlm(struct sess_data *sess_data)
+{
+ int rc = 0;
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ char *bcc_ptr;
+ struct cifs_ses *ses = sess_data->ses;
+ __u32 capabilities;
+ __u16 bytes_remaining;
+
+ /* old style NTLM sessionsetup */
+ /* wct = 13 */
+ rc = sess_alloc_buffer(sess_data, 13);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ bcc_ptr = sess_data->iov[2].iov_base;
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+ if (ses->user_name != NULL) {
+ pSMB->req_no_secext.CaseInsensitivePasswordLength =
+ cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+
+ /* calculate ntlm response and session key */
+ rc = setup_ntlm_response(ses, sess_data->nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "Error %d during NTLM authentication\n",
+ rc);
+ goto out;
+ }
+
+ /* copy ntlm response */
+ memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ CIFS_AUTH_RESP_SIZE);
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ CIFS_AUTH_RESP_SIZE);
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ } else {
+ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
+ pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
+ }
+
+ if (ses->capabilities & CAP_UNICODE) {
+ /* unicode strings must be word aligned */
+ if (sess_data->iov[0].iov_len % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+ } else {
+ ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+ }
+
+
+ sess_data->iov[2].iov_len = (long) bcc_ptr -
+ (long) sess_data->iov[2].iov_base;
+
+ rc = sess_sendreceive(sess_data);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+
+ if (smb_buf->WordCount != 3) {
+ rc = -EIO;
+ cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+ goto out;
+ }
+
+ if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
+ cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
+
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
+
+ bytes_remaining = get_bcc(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ /* BB check if Unicode and decode strings */
+ if (bytes_remaining == 0) {
+ /* no string area to decode, do nothing */
+ } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+ /* unicode string area must be word-aligned */
+ if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
+ ++bcc_ptr;
+ --bytes_remaining;
+ }
+ decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ } else {
+ decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ }
+
+ rc = sess_establish_session(sess_data);
+out:
+ sess_data->result = rc;
+ sess_data->func = NULL;
+ sess_free_buffer(sess_data);
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+}
+
+static void
+sess_auth_ntlmv2(struct sess_data *sess_data)
+{
+ int rc = 0;
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ char *bcc_ptr;
+ struct cifs_ses *ses = sess_data->ses;
+ __u32 capabilities;
+ __u16 bytes_remaining;
+
+ /* old style NTLM sessionsetup */
+ /* wct = 13 */
+ rc = sess_alloc_buffer(sess_data, 13);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ bcc_ptr = sess_data->iov[2].iov_base;
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+
+ /* LM2 password would be here if we supported it */
+ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
+
+ if (ses->user_name != NULL) {
+ /* calculate nlmv2 response and session key */
+ rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
+ goto out;
+ }
+
+ memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
+
+ /* set case sensitive password length after tilen may get
+ * assigned, tilen is 0 otherwise.
+ */
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ } else {
+ pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
+ }
+
+ if (ses->capabilities & CAP_UNICODE) {
+ if (sess_data->iov[0].iov_len % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+ } else {
+ ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+ }
+
+
+ sess_data->iov[2].iov_len = (long) bcc_ptr -
+ (long) sess_data->iov[2].iov_base;
+
+ rc = sess_sendreceive(sess_data);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+
+ if (smb_buf->WordCount != 3) {
+ rc = -EIO;
+ cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+ goto out;
+ }
+
+ if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
+ cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
+
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
+
+ bytes_remaining = get_bcc(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ /* BB check if Unicode and decode strings */
+ if (bytes_remaining == 0) {
+ /* no string area to decode, do nothing */
+ } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+ /* unicode string area must be word-aligned */
+ if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
+ ++bcc_ptr;
+ --bytes_remaining;
+ }
+ decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ } else {
+ decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ }
+
+ rc = sess_establish_session(sess_data);
+out:
+ sess_data->result = rc;
+ sess_data->func = NULL;
+ sess_free_buffer(sess_data);
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+}
+
+#ifdef CONFIG_CIFS_UPCALL
+static void
+sess_auth_kerberos(struct sess_data *sess_data)
+{
+ int rc = 0;
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ char *bcc_ptr;
+ struct cifs_ses *ses = sess_data->ses;
+ __u32 capabilities;
+ __u16 bytes_remaining;
+ struct key *spnego_key = NULL;
+ struct cifs_spnego_msg *msg;
+ u16 blob_len;
+
+ /* extended security */
+ /* wct = 12 */
+ rc = sess_alloc_buffer(sess_data, 12);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ bcc_ptr = sess_data->iov[2].iov_base;
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+ spnego_key = cifs_get_spnego_key(ses);
+ if (IS_ERR(spnego_key)) {
+ rc = PTR_ERR(spnego_key);
+ spnego_key = NULL;
+ goto out;
+ }
+
+ msg = spnego_key->payload.data[0];
+ /*
+ * check version field to make sure that cifs.upcall is
+ * sending us a response in an expected form
+ */
+ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
+ cifs_dbg(VFS,
+ "incorrect version of cifs.upcall (expected %d but got %d)",
+ CIFS_SPNEGO_UPCALL_VERSION, msg->version);
+ rc = -EKEYREJECTED;
+ goto out_put_spnego_key;
+ }
+
+ ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
+ GFP_KERNEL);
+ if (!ses->auth_key.response) {
+ cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory",
+ msg->sesskey_len);
+ rc = -ENOMEM;
+ goto out_put_spnego_key;
+ }
+ ses->auth_key.len = msg->sesskey_len;
+
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+ sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
+ sess_data->iov[1].iov_len = msg->secblob_len;
+ pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len);
+
+ if (ses->capabilities & CAP_UNICODE) {
+ /* unicode strings must be word aligned */
+ if ((sess_data->iov[0].iov_len
+ + sess_data->iov[1].iov_len) % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
+ unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
+ } else {
+ /* BB: is this right? */
+ ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+ }
+
+ sess_data->iov[2].iov_len = (long) bcc_ptr -
+ (long) sess_data->iov[2].iov_base;
+
+ rc = sess_sendreceive(sess_data);
+ if (rc)
+ goto out_put_spnego_key;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+
+ if (smb_buf->WordCount != 4) {
+ rc = -EIO;
+ cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+ goto out_put_spnego_key;
+ }
+
+ if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
+ cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
+
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
+
+ bytes_remaining = get_bcc(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+ if (blob_len > bytes_remaining) {
+ cifs_dbg(VFS, "bad security blob length %d\n",
+ blob_len);
+ rc = -EINVAL;
+ goto out_put_spnego_key;
+ }
+ bcc_ptr += blob_len;
+ bytes_remaining -= blob_len;
+
+ /* BB check if Unicode and decode strings */
+ if (bytes_remaining == 0) {
+ /* no string area to decode, do nothing */
+ } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+ /* unicode string area must be word-aligned */
+ if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
+ ++bcc_ptr;
+ --bytes_remaining;
+ }
+ decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ } else {
+ decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ }
+
+ rc = sess_establish_session(sess_data);
+out_put_spnego_key:
+ key_invalidate(spnego_key);
+ key_put(spnego_key);
+out:
+ sess_data->result = rc;
+ sess_data->func = NULL;
+ sess_free_buffer(sess_data);
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+}
+
+#endif /* ! CONFIG_CIFS_UPCALL */
+
+/*
+ * The required kvec buffers have to be allocated before calling this
+ * function.
+ */
+static int
+_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
+{
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ struct cifs_ses *ses = sess_data->ses;
+ __u32 capabilities;
+ char *bcc_ptr;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)pSMB;
+
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+ if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
+ cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
+ return -ENOSYS;
+ }
+
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ pSMB->req.Capabilities |= cpu_to_le32(capabilities);
+
+ bcc_ptr = sess_data->iov[2].iov_base;
+ /* unicode strings must be word aligned */
+ if ((sess_data->iov[0].iov_len + sess_data->iov[1].iov_len) % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
+
+ sess_data->iov[2].iov_len = (long) bcc_ptr -
+ (long) sess_data->iov[2].iov_base;
+
+ return 0;
+}
+
+static void
+sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data);
+
+static void
+sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
+{
+ int rc;
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ struct cifs_ses *ses = sess_data->ses;
+ __u16 bytes_remaining;
+ char *bcc_ptr;
+ u16 blob_len;
+
+ cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n");
+
+ /*
+ * if memory allocation is successful, caller of this function
+ * frees it.
+ */
+ ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
+ if (!ses->ntlmssp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ ses->ntlmssp->sesskey_per_smbsess = false;
+
+ /* wct = 12 */
+ rc = sess_alloc_buffer(sess_data, 12);
+ if (rc)
+ goto out;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+
+ /* Build security blob before we assemble the request */
+ build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses);
+ sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
+ sess_data->iov[1].iov_base = pSMB->req.SecurityBlob;
+ pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
+
+ rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
+ if (rc)
+ goto out;
+
+ rc = sess_sendreceive(sess_data);
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+
+ /* If true, rc here is expected and not an error */
+ if (sess_data->buf0_type != CIFS_NO_BUFFER &&
+ smb_buf->Status.CifsError ==
+ cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
+ rc = 0;
+
+ if (rc)
+ goto out;
+
+ cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
+
+ if (smb_buf->WordCount != 4) {
+ rc = -EIO;
+ cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+ goto out;
+ }
+
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cifs_dbg(FYI, "UID = %llu\n", ses->Suid);
+
+ bytes_remaining = get_bcc(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+ if (blob_len > bytes_remaining) {
+ cifs_dbg(VFS, "bad security blob length %d\n",
+ blob_len);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
+out:
+ sess_free_buffer(sess_data);
+
+ if (!rc) {
+ sess_data->func = sess_auth_rawntlmssp_authenticate;
+ return;
+ }
+
+ /* Else error. Cleanup */
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+ kfree(ses->ntlmssp);
+ ses->ntlmssp = NULL;
+
+ sess_data->func = NULL;
+ sess_data->result = rc;
+}
+
+static void
+sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
+{
+ int rc;
+ struct smb_hdr *smb_buf;
+ SESSION_SETUP_ANDX *pSMB;
+ struct cifs_ses *ses = sess_data->ses;
+ __u16 bytes_remaining;
+ char *bcc_ptr;
+ unsigned char *ntlmsspblob = NULL;
+ u16 blob_len;
+
+ cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
+
+ /* wct = 12 */
+ rc = sess_alloc_buffer(sess_data, 12);
+ if (rc)
+ goto out;
+
+ /* Build security blob before we assemble the request */
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)pSMB;
+ rc = build_ntlmssp_auth_blob(&ntlmsspblob,
+ &blob_len, ses, sess_data->nls_cp);
+ if (rc)
+ goto out_free_ntlmsspblob;
+ sess_data->iov[1].iov_len = blob_len;
+ sess_data->iov[1].iov_base = ntlmsspblob;
+ pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len);
+ /*
+ * Make sure that we tell the server that we are using
+ * the uid that it just gave us back on the response
+ * (challenge)
+ */
+ smb_buf->Uid = ses->Suid;
+
+ rc = _sess_auth_rawntlmssp_assemble_req(sess_data);
+ if (rc)
+ goto out_free_ntlmsspblob;
+
+ rc = sess_sendreceive(sess_data);
+ if (rc)
+ goto out_free_ntlmsspblob;
+
+ pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
+ if (smb_buf->WordCount != 4) {
+ rc = -EIO;
+ cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
+ goto out_free_ntlmsspblob;
+ }
+
+ if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN)
+ cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */
+
+ if (ses->Suid != smb_buf->Uid) {
+ ses->Suid = smb_buf->Uid;
+ cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid);
+ }
+
+ bytes_remaining = get_bcc(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+ blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+ if (blob_len > bytes_remaining) {
+ cifs_dbg(VFS, "bad security blob length %d\n",
+ blob_len);
+ rc = -EINVAL;
+ goto out_free_ntlmsspblob;
+ }
+ bcc_ptr += blob_len;
+ bytes_remaining -= blob_len;
+
+
+ /* BB check if Unicode and decode strings */
+ if (bytes_remaining == 0) {
+ /* no string area to decode, do nothing */
+ } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+ /* unicode string area must be word-aligned */
+ if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
+ ++bcc_ptr;
+ --bytes_remaining;
+ }
+ decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ } else {
+ decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,
+ sess_data->nls_cp);
+ }
+
+out_free_ntlmsspblob:
+ kfree(ntlmsspblob);
+out:
+ sess_free_buffer(sess_data);
+
+ if (!rc)
+ rc = sess_establish_session(sess_data);
+
+ /* Cleanup */
+ kfree(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+ kfree(ses->ntlmssp);
+ ses->ntlmssp = NULL;
+
+ sess_data->func = NULL;
+ sess_data->result = rc;
+}
+
+static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
+{
+ int type;
+
+ type = cifs_select_sectype(ses->server, ses->sectype);
+ cifs_dbg(FYI, "sess setup type %d\n", type);
+ if (type == Unspecified) {
+ cifs_dbg(VFS,
+ "Unable to select appropriate authentication method!");
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case LANMAN:
+ /* LANMAN and plaintext are less secure and off by default.
+ * So we make this explicitly be turned on in kconfig (in the
+ * build) and turned on at runtime (changed from the default)
+ * in proc/fs/cifs or via mount parm. Unfortunately this is
+ * needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ sess_data->func = sess_auth_lanman;
+ break;
+#else
+ return -EOPNOTSUPP;
+#endif
+ case NTLM:
+ sess_data->func = sess_auth_ntlm;
+ break;
+ case NTLMv2:
+ sess_data->func = sess_auth_ntlmv2;
+ break;
+ case Kerberos:
+#ifdef CONFIG_CIFS_UPCALL
+ sess_data->func = sess_auth_kerberos;
+ break;
+#else
+ cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
+ return -ENOSYS;
+ break;
+#endif /* CONFIG_CIFS_UPCALL */
+ case RawNTLMSSP:
+ sess_data->func = sess_auth_rawntlmssp_negotiate;
+ break;
+ default:
+ cifs_dbg(VFS, "secType %d not supported!\n", type);
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ struct sess_data *sess_data;
+
+ if (ses == NULL) {
+ WARN(1, "%s: ses == NULL!", __func__);
+ return -EINVAL;
+ }
+
+ sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL);
+ if (!sess_data)
+ return -ENOMEM;
+
+ rc = select_sec(ses, sess_data);
+ if (rc)
+ goto out;
+
+ sess_data->xid = xid;
+ sess_data->ses = ses;
+ sess_data->buf0_type = CIFS_NO_BUFFER;
+ sess_data->nls_cp = (struct nls_table *) nls_cp;
+
+ while (sess_data->func)
+ sess_data->func(sess_data);
+
+ /* Store result before we free sess_data */
+ rc = sess_data->result;
+
+out:
+ kfree(sess_data);
+ return rc;
+}
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
new file mode 100644
index 000000000..0b7f92451
--- /dev/null
+++ b/fs/cifs/smb1ops.c
@@ -0,0 +1,1133 @@
+/*
+ * SMB1 (CIFS) version specific operations
+ *
+ * Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published
+ * by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/pagemap.h>
+#include <linux/vfs.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifspdu.h"
+#include "cifs_unicode.h"
+
+/*
+ * An NT cancel request header looks just like the original request except:
+ *
+ * The Command is SMB_COM_NT_CANCEL
+ * The WordCount is zeroed out
+ * The ByteCount is zeroed out
+ *
+ * This function mangles an existing request buffer into a
+ * SMB_COM_NT_CANCEL request and then sends it.
+ */
+static int
+send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+ struct mid_q_entry *mid)
+{
+ int rc = 0;
+ struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+
+ /* -4 for RFC1001 length and +2 for BCC field */
+ in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2);
+ in_buf->Command = SMB_COM_NT_CANCEL;
+ in_buf->WordCount = 0;
+ put_bcc(0, in_buf);
+
+ mutex_lock(&server->srv_mutex);
+ rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
+ if (rc) {
+ mutex_unlock(&server->srv_mutex);
+ return rc;
+ }
+
+ /*
+ * The response to this call was already factored into the sequence
+ * number when the call went out, so we must adjust it back downward
+ * after signing here.
+ */
+ --server->sequence_number;
+ rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
+ if (rc < 0)
+ server->sequence_number--;
+
+ mutex_unlock(&server->srv_mutex);
+
+ cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n",
+ get_mid(in_buf), rc);
+
+ return rc;
+}
+
+static bool
+cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
+{
+ return ob1->fid.netfid == ob2->fid.netfid;
+}
+
+static unsigned int
+cifs_read_data_offset(char *buf)
+{
+ READ_RSP *rsp = (READ_RSP *)buf;
+ return le16_to_cpu(rsp->DataOffset);
+}
+
+static unsigned int
+cifs_read_data_length(char *buf, bool in_remaining)
+{
+ READ_RSP *rsp = (READ_RSP *)buf;
+ /* It's a bug reading remaining data for SMB1 packets */
+ WARN_ON(in_remaining);
+ return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
+ le16_to_cpu(rsp->DataLength);
+}
+
+static struct mid_q_entry *
+cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
+{
+ struct smb_hdr *buf = (struct smb_hdr *)buffer;
+ struct mid_q_entry *mid;
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_entry(mid, &server->pending_mid_q, qhead) {
+ if (compare_mid(mid->mid, buf) &&
+ mid->mid_state == MID_REQUEST_SUBMITTED &&
+ le16_to_cpu(mid->command) == buf->Command) {
+ kref_get(&mid->refcount);
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ return NULL;
+}
+
+static void
+cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add,
+ const int optype)
+{
+ spin_lock(&server->req_lock);
+ server->credits += add;
+ server->in_flight--;
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+}
+
+static void
+cifs_set_credits(struct TCP_Server_Info *server, const int val)
+{
+ spin_lock(&server->req_lock);
+ server->credits = val;
+ server->oplocks = val > 1 ? enable_oplocks : false;
+ spin_unlock(&server->req_lock);
+}
+
+static int *
+cifs_get_credits_field(struct TCP_Server_Info *server, const int optype)
+{
+ return &server->credits;
+}
+
+static unsigned int
+cifs_get_credits(struct mid_q_entry *mid)
+{
+ return 1;
+}
+
+/*
+ * Find a free multiplex id (SMB mid). Otherwise there could be
+ * mid collisions which might cause problems, demultiplexing the
+ * wrong response to this request. Multiplex ids could collide if
+ * one of a series requests takes much longer than the others, or
+ * if a very large number of long lived requests (byte range
+ * locks or FindNotify requests) are pending. No more than
+ * 64K-1 requests can be outstanding at one time. If no
+ * mids are available, return zero. A future optimization
+ * could make the combination of mids and uid the key we use
+ * to demultiplex on (rather than mid alone).
+ * In addition to the above check, the cifs demultiplex
+ * code already used the command code as a secondary
+ * check of the frame and if signing is negotiated the
+ * response would be discarded if the mid were the same
+ * but the signature was wrong. Since the mid is not put in the
+ * pending queue until later (when it is about to be dispatched)
+ * we do have to limit the number of outstanding requests
+ * to somewhat less than 64K-1 although it is hard to imagine
+ * so many threads being in the vfs at one time.
+ */
+static __u64
+cifs_get_next_mid(struct TCP_Server_Info *server)
+{
+ __u64 mid = 0;
+ __u16 last_mid, cur_mid;
+ bool collision;
+
+ spin_lock(&GlobalMid_Lock);
+
+ /* mid is 16 bit only for CIFS/SMB */
+ cur_mid = (__u16)((server->CurrentMid) & 0xffff);
+ /* we do not want to loop forever */
+ last_mid = cur_mid;
+ cur_mid++;
+ /* avoid 0xFFFF MID */
+ if (cur_mid == 0xffff)
+ cur_mid++;
+
+ /*
+ * This nested loop looks more expensive than it is.
+ * In practice the list of pending requests is short,
+ * fewer than 50, and the mids are likely to be unique
+ * on the first pass through the loop unless some request
+ * takes longer than the 64 thousand requests before it
+ * (and it would also have to have been a request that
+ * did not time out).
+ */
+ while (cur_mid != last_mid) {
+ struct mid_q_entry *mid_entry;
+ unsigned int num_mids;
+
+ collision = false;
+ if (cur_mid == 0)
+ cur_mid++;
+
+ num_mids = 0;
+ list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
+ ++num_mids;
+ if (mid_entry->mid == cur_mid &&
+ mid_entry->mid_state == MID_REQUEST_SUBMITTED) {
+ /* This mid is in use, try a different one */
+ collision = true;
+ break;
+ }
+ }
+
+ /*
+ * if we have more than 32k mids in the list, then something
+ * is very wrong. Possibly a local user is trying to DoS the
+ * box by issuing long-running calls and SIGKILL'ing them. If
+ * we get to 2^16 mids then we're in big trouble as this
+ * function could loop forever.
+ *
+ * Go ahead and assign out the mid in this situation, but force
+ * an eventual reconnect to clean out the pending_mid_q.
+ */
+ if (num_mids > 32768)
+ server->tcpStatus = CifsNeedReconnect;
+
+ if (!collision) {
+ mid = (__u64)cur_mid;
+ server->CurrentMid = mid;
+ break;
+ }
+ cur_mid++;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+}
+
+/*
+ return codes:
+ 0 not a transact2, or all data present
+ >0 transact2 with that much data missing
+ -EINVAL invalid transact2
+ */
+static int
+check2ndT2(char *buf)
+{
+ struct smb_hdr *pSMB = (struct smb_hdr *)buf;
+ struct smb_t2_rsp *pSMBt;
+ int remaining;
+ __u16 total_data_size, data_in_this_rsp;
+
+ if (pSMB->Command != SMB_COM_TRANSACTION2)
+ return 0;
+
+ /* check for plausible wct, bcc and t2 data and parm sizes */
+ /* check for parm and data offset going beyond end of smb */
+ if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
+ cifs_dbg(FYI, "invalid transact2 word count\n");
+ return -EINVAL;
+ }
+
+ pSMBt = (struct smb_t2_rsp *)pSMB;
+
+ total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
+ data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
+
+ if (total_data_size == data_in_this_rsp)
+ return 0;
+ else if (total_data_size < data_in_this_rsp) {
+ cifs_dbg(FYI, "total data %d smaller than data in frame %d\n",
+ total_data_size, data_in_this_rsp);
+ return -EINVAL;
+ }
+
+ remaining = total_data_size - data_in_this_rsp;
+
+ cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n",
+ remaining);
+ if (total_data_size > CIFSMaxBufSize) {
+ cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n",
+ total_data_size, CIFSMaxBufSize);
+ return -EINVAL;
+ }
+ return remaining;
+}
+
+static int
+coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
+{
+ struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf;
+ struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr;
+ char *data_area_of_tgt;
+ char *data_area_of_src;
+ int remaining;
+ unsigned int byte_count, total_in_tgt;
+ __u16 tgt_total_cnt, src_total_cnt, total_in_src;
+
+ src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount);
+ tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
+
+ if (tgt_total_cnt != src_total_cnt)
+ cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n",
+ src_total_cnt, tgt_total_cnt);
+
+ total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
+
+ remaining = tgt_total_cnt - total_in_tgt;
+
+ if (remaining < 0) {
+ cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n",
+ tgt_total_cnt, total_in_tgt);
+ return -EPROTO;
+ }
+
+ if (remaining == 0) {
+ /* nothing to do, ignore */
+ cifs_dbg(FYI, "no more data remains\n");
+ return 0;
+ }
+
+ total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount);
+ if (remaining < total_in_src)
+ cifs_dbg(FYI, "transact2 2nd response contains too much data\n");
+
+ /* find end of first SMB data area */
+ data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
+ get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
+
+ /* validate target area */
+ data_area_of_src = (char *)&pSMBs->hdr.Protocol +
+ get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
+
+ data_area_of_tgt += total_in_tgt;
+
+ total_in_tgt += total_in_src;
+ /* is the result too big for the field? */
+ if (total_in_tgt > USHRT_MAX) {
+ cifs_dbg(FYI, "coalesced DataCount too large (%u)\n",
+ total_in_tgt);
+ return -EPROTO;
+ }
+ put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount);
+
+ /* fix up the BCC */
+ byte_count = get_bcc(target_hdr);
+ byte_count += total_in_src;
+ /* is the result too big for the field? */
+ if (byte_count > USHRT_MAX) {
+ cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count);
+ return -EPROTO;
+ }
+ put_bcc(byte_count, target_hdr);
+
+ byte_count = be32_to_cpu(target_hdr->smb_buf_length);
+ byte_count += total_in_src;
+ /* don't allow buffer to overflow */
+ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
+ byte_count);
+ return -ENOBUFS;
+ }
+ target_hdr->smb_buf_length = cpu_to_be32(byte_count);
+
+ /* copy second buffer into end of first buffer */
+ memcpy(data_area_of_tgt, data_area_of_src, total_in_src);
+
+ if (remaining != total_in_src) {
+ /* more responses to go */
+ cifs_dbg(FYI, "waiting for more secondary responses\n");
+ return 1;
+ }
+
+ /* we are done */
+ cifs_dbg(FYI, "found the last secondary response\n");
+ return 0;
+}
+
+static void
+cifs_downgrade_oplock(struct TCP_Server_Info *server,
+ struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ cifs_set_oplock_level(cinode, oplock);
+}
+
+static bool
+cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ char *buf, int malformed)
+{
+ if (malformed)
+ return false;
+ if (check2ndT2(buf) <= 0)
+ return false;
+ mid->multiRsp = true;
+ if (mid->resp_buf) {
+ /* merge response - fix up 1st*/
+ malformed = coalesce_t2(buf, mid->resp_buf);
+ if (malformed > 0)
+ return true;
+ /* All parts received or packet is malformed. */
+ mid->multiEnd = true;
+ dequeue_mid(mid, malformed);
+ return true;
+ }
+ if (!server->large_buf) {
+ /*FIXME: switch to already allocated largebuf?*/
+ cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n");
+ } else {
+ /* Have first buffer */
+ mid->resp_buf = buf;
+ mid->large_buf = true;
+ server->bigbuf = NULL;
+ }
+ return true;
+}
+
+static bool
+cifs_need_neg(struct TCP_Server_Info *server)
+{
+ return server->maxBuf == 0;
+}
+
+static int
+cifs_negotiate(const unsigned int xid, struct cifs_ses *ses)
+{
+ int rc;
+ rc = CIFSSMBNegotiate(xid, ses);
+ if (rc == -EAGAIN) {
+ /* retry only once on 1st time connection */
+ set_credits(ses->server, 1);
+ rc = CIFSSMBNegotiate(xid, ses);
+ if (rc == -EAGAIN)
+ rc = -EHOSTDOWN;
+ }
+ return rc;
+}
+
+static unsigned int
+cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int wsize;
+
+ /* start with specified wsize, or default */
+ if (volume_info->wsize)
+ wsize = volume_info->wsize;
+ else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
+ wsize = CIFS_DEFAULT_IOSIZE;
+ else
+ wsize = CIFS_DEFAULT_NON_POSIX_WSIZE;
+
+ /* can server support 24-bit write sizes? (via UNIX extensions) */
+ if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
+ wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE);
+
+ /*
+ * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set?
+ * Limit it to max buffer offered by the server, minus the size of the
+ * WRITEX header, not including the 4 byte RFC1001 length.
+ */
+ if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
+ (!(server->capabilities & CAP_UNIX) && server->sign))
+ wsize = min_t(unsigned int, wsize,
+ server->maxBuf - sizeof(WRITE_REQ) + 4);
+
+ /* hard limit of CIFS_MAX_WSIZE */
+ wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE);
+
+ return wsize;
+}
+
+static unsigned int
+cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int rsize, defsize;
+
+ /*
+ * Set default value...
+ *
+ * HACK alert! Ancient servers have very small buffers. Even though
+ * MS-CIFS indicates that servers are only limited by the client's
+ * bufsize for reads, testing against win98se shows that it throws
+ * INVALID_PARAMETER errors if you try to request too large a read.
+ * OS/2 just sends back short reads.
+ *
+ * If the server doesn't advertise CAP_LARGE_READ_X, then assume that
+ * it can't handle a read request larger than its MaxBufferSize either.
+ */
+ if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
+ defsize = CIFS_DEFAULT_IOSIZE;
+ else if (server->capabilities & CAP_LARGE_READ_X)
+ defsize = CIFS_DEFAULT_NON_POSIX_RSIZE;
+ else
+ defsize = server->maxBuf - sizeof(READ_RSP);
+
+ rsize = volume_info->rsize ? volume_info->rsize : defsize;
+
+ /*
+ * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
+ * the client's MaxBufferSize.
+ */
+ if (!(server->capabilities & CAP_LARGE_READ_X))
+ rsize = min_t(unsigned int, CIFSMaxBufSize, rsize);
+
+ /* hard limit of CIFS_MAX_RSIZE */
+ rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE);
+
+ return rsize;
+}
+
+static void
+cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ CIFSSMBQFSDeviceInfo(xid, tcon);
+ CIFSSMBQFSAttributeInfo(xid, tcon);
+}
+
+static int
+cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path)
+{
+ int rc;
+ FILE_ALL_INFO *file_info;
+
+ file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (file_info == NULL)
+ return -ENOMEM;
+
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info,
+ 0 /* not legacy */, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+
+ if (rc == -EOPNOTSUPP || rc == -EINVAL)
+ rc = SMBQueryInformation(xid, tcon, full_path, file_info,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+ kfree(file_info);
+ return rc;
+}
+
+static int
+cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+ FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink)
+{
+ int rc;
+
+ *symlink = false;
+
+ /* could do find first instead but this returns more info */
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+ /*
+ * BB optimize code so we do not make the above call when server claims
+ * no NT SMB support and the above call failed at least once - set flag
+ * in tcon or mount.
+ */
+ if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
+ rc = SMBQueryInformation(xid, tcon, full_path, data,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ *adjustTZ = true;
+ }
+
+ if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) {
+ int tmprc;
+ int oplock = 0;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.create_options = 0;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = full_path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ /* Need to check if this is a symbolic link or not */
+ tmprc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (tmprc == -EOPNOTSUPP)
+ *symlink = true;
+ else if (tmprc == 0)
+ CIFSSMBClose(xid, tcon, fid.netfid);
+ }
+
+ return rc;
+}
+
+static int
+cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+ u64 *uniqueid, FILE_ALL_INFO *data)
+{
+ /*
+ * We can not use the IndexNumber field by default from Windows or
+ * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA
+ * CIFS spec claims that this value is unique within the scope of a
+ * share, and the windows docs hint that it's actually unique
+ * per-machine.
+ *
+ * There may be higher info levels that work but are there Windows
+ * server or network appliances for which IndexNumber field is not
+ * guaranteed unique?
+ */
+ return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+}
+
+static int
+cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, FILE_ALL_INFO *data)
+{
+ return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data);
+}
+
+static void
+cifs_clear_stats(struct cifs_tcon *tcon)
+{
+ atomic_set(&tcon->stats.cifs_stats.num_writes, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_reads, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_flushes, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_oplock_brks, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_opens, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_posixopens, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_posixmkdirs, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_closes, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_deletes, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_mkdirs, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_rmdirs, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_renames, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_t2renames, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_ffirst, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_fnext, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_fclose, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_hardlinks, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_symlinks, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_locks, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0);
+ atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0);
+}
+
+static void
+cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
+{
+ seq_printf(m, " Oplocks breaks: %d",
+ atomic_read(&tcon->stats.cifs_stats.num_oplock_brks));
+ seq_printf(m, "\nReads: %d Bytes: %llu",
+ atomic_read(&tcon->stats.cifs_stats.num_reads),
+ (long long)(tcon->bytes_read));
+ seq_printf(m, "\nWrites: %d Bytes: %llu",
+ atomic_read(&tcon->stats.cifs_stats.num_writes),
+ (long long)(tcon->bytes_written));
+ seq_printf(m, "\nFlushes: %d",
+ atomic_read(&tcon->stats.cifs_stats.num_flushes));
+ seq_printf(m, "\nLocks: %d HardLinks: %d Symlinks: %d",
+ atomic_read(&tcon->stats.cifs_stats.num_locks),
+ atomic_read(&tcon->stats.cifs_stats.num_hardlinks),
+ atomic_read(&tcon->stats.cifs_stats.num_symlinks));
+ seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d",
+ atomic_read(&tcon->stats.cifs_stats.num_opens),
+ atomic_read(&tcon->stats.cifs_stats.num_closes),
+ atomic_read(&tcon->stats.cifs_stats.num_deletes));
+ seq_printf(m, "\nPosix Opens: %d Posix Mkdirs: %d",
+ atomic_read(&tcon->stats.cifs_stats.num_posixopens),
+ atomic_read(&tcon->stats.cifs_stats.num_posixmkdirs));
+ seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
+ atomic_read(&tcon->stats.cifs_stats.num_mkdirs),
+ atomic_read(&tcon->stats.cifs_stats.num_rmdirs));
+ seq_printf(m, "\nRenames: %d T2 Renames %d",
+ atomic_read(&tcon->stats.cifs_stats.num_renames),
+ atomic_read(&tcon->stats.cifs_stats.num_t2renames));
+ seq_printf(m, "\nFindFirst: %d FNext %d FClose %d",
+ atomic_read(&tcon->stats.cifs_stats.num_ffirst),
+ atomic_read(&tcon->stats.cifs_stats.num_fnext),
+ atomic_read(&tcon->stats.cifs_stats.num_fclose));
+}
+
+static void
+cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
+ struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
+ const unsigned int xid)
+{
+ FILE_BASIC_INFO info;
+ struct cifsInodeInfo *cifsInode;
+ u32 dosattrs;
+ int rc;
+
+ memset(&info, 0, sizeof(info));
+ cifsInode = CIFS_I(inode);
+ dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
+ info.Attributes = cpu_to_le32(dosattrs);
+ rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (rc == 0)
+ cifsInode->cifsAttrs = dosattrs;
+}
+
+static int
+cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
+ __u32 *oplock, FILE_ALL_INFO *buf)
+{
+ if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
+ return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
+ oparms->disposition,
+ oparms->desired_access,
+ oparms->create_options,
+ &oparms->fid->netfid, oplock, buf,
+ oparms->cifs_sb->local_nls,
+ cifs_remap(oparms->cifs_sb));
+ return CIFS_open(xid, oparms, oplock, buf);
+}
+
+static void
+cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ cfile->fid.netfid = fid->netfid;
+ cifs_set_oplock_level(cinode, oplock);
+ cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
+}
+
+static void
+cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ CIFSSMBClose(xid, tcon, fid->netfid);
+}
+
+static int
+cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return CIFSSMBFlush(xid, tcon, fid->netfid);
+}
+
+static int
+cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid,
+ struct cifs_io_parms *parms, unsigned int *bytes_read,
+ char **buf, int *buf_type)
+{
+ parms->netfid = pfid->netfid;
+ return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type);
+}
+
+static int
+cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid,
+ struct cifs_io_parms *parms, unsigned int *written,
+ struct kvec *iov, unsigned long nr_segs)
+{
+
+ parms->netfid = pfid->netfid;
+ return CIFSSMBWrite2(xid, parms, written, iov, nr_segs);
+}
+
+static int
+smb_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid)
+{
+ int oplock = 0;
+ int rc;
+ __u32 netpid;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ struct cifsFileInfo *open_file;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = NULL;
+ struct cifs_tcon *tcon;
+
+ /* if the file is already open for write, just use that fileid */
+ open_file = find_writable_file(cinode, true);
+ if (open_file) {
+ fid.netfid = open_file->fid.netfid;
+ netpid = open_file->pid;
+ tcon = tlink_tcon(open_file->tlink);
+ goto set_via_filehandle;
+ }
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ tlink = NULL;
+ goto out;
+ }
+ tcon = tlink_tcon(tlink);
+
+ rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (rc == 0) {
+ cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
+ goto out;
+ } else if (rc != -EOPNOTSUPP && rc != -EINVAL) {
+ goto out;
+ }
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES;
+ oparms.create_options = CREATE_NOT_DIR;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = full_path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n");
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc != 0) {
+ if (rc == -EIO)
+ rc = -EINVAL;
+ goto out;
+ }
+
+ netpid = current->tgid;
+
+set_via_filehandle:
+ rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid);
+ if (!rc)
+ cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
+
+ if (open_file == NULL)
+ CIFSSMBClose(xid, tcon, fid.netfid);
+ else
+ cifsFileInfo_put(open_file);
+out:
+ if (tlink != NULL)
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static int
+cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile)
+{
+ return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid);
+}
+
+static int
+cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *path, struct cifs_sb_info *cifs_sb,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ int rc;
+
+ rc = CIFSFindFirst(xid, tcon, path, cifs_sb,
+ &fid->netfid, search_flags, srch_inf, true);
+ if (rc)
+ cifs_dbg(FYI, "find first failed=%d\n", rc);
+ return rc;
+}
+
+static int
+cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf);
+}
+
+static int
+cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return CIFSFindClose(xid, tcon, fid->netfid);
+}
+
+static int
+cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
+ struct cifsInodeInfo *cinode)
+{
+ return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0,
+ LOCKING_ANDX_OPLOCK_RELEASE, false,
+ CIFS_CACHE_READ(cinode) ? 1 : 0);
+}
+
+static int
+cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *buf)
+{
+ int rc = -EOPNOTSUPP;
+
+ buf->f_type = CIFS_MAGIC_NUMBER;
+
+ /*
+ * We could add a second check for a QFS Unix capability bit
+ */
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
+ rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
+
+ /*
+ * Only need to call the old QFSInfo if failed on newer one,
+ * e.g. by OS/2.
+ **/
+ if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
+ rc = CIFSSMBQFSInfo(xid, tcon, buf);
+
+ /*
+ * Some old Windows servers also do not support level 103, retry with
+ * older level one if old server failed the previous call or we
+ * bypassed it because we detected that this was an older LANMAN sess
+ */
+ if (rc)
+ rc = SMBOldQFSInfo(xid, tcon, buf);
+ return rc;
+}
+
+static int
+cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
+ __u64 length, __u32 type, int lock, int unlock, bool wait)
+{
+ return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid,
+ current->tgid, length, offset, unlock, lock,
+ (__u8)type, wait, 0);
+}
+
+static int
+cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *searchName, char **symlinkinfo,
+ const struct nls_table *nls_codepage)
+{
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ int rc;
+ unsigned int num_referrals = 0;
+ struct dfs_info3_param *referrals = NULL;
+
+ rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage,
+ &num_referrals, &referrals, 0);
+
+ if (!rc && num_referrals > 0) {
+ *symlinkinfo = kstrndup(referrals->node_name,
+ strlen(referrals->node_name),
+ GFP_KERNEL);
+ if (!*symlinkinfo)
+ rc = -ENOMEM;
+ free_dfs_info_array(referrals, num_referrals);
+ }
+ return rc;
+#else /* No DFS support */
+ return -EREMOTE;
+#endif
+}
+
+static int
+cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, char **target_path,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc;
+ int oplock = 0;
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+
+ cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
+
+ /* Check for unix extensions */
+ if (cap_unix(tcon->ses)) {
+ rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ if (rc == -EREMOTE)
+ rc = cifs_unix_dfs_readlink(xid, tcon, full_path,
+ target_path,
+ cifs_sb->local_nls);
+
+ goto out;
+ }
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.create_options = OPEN_REPARSE_POINT;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = full_path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc)
+ goto out;
+
+ rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path,
+ cifs_sb->local_nls);
+ if (rc)
+ goto out_close;
+
+ convert_delimiter(*target_path, '/');
+out_close:
+ CIFSSMBClose(xid, tcon, fid.netfid);
+out:
+ if (!rc)
+ cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
+ return rc;
+}
+
+static bool
+cifs_is_read_op(__u32 oplock)
+{
+ return oplock == OPLOCK_READ;
+}
+
+static unsigned int
+cifs_wp_retry_size(struct inode *inode)
+{
+ return CIFS_SB(inode->i_sb)->wsize;
+}
+
+static bool
+cifs_dir_needs_close(struct cifsFileInfo *cfile)
+{
+ return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle;
+}
+
+static bool
+cifs_can_echo(struct TCP_Server_Info *server)
+{
+ if (server->tcpStatus == CifsGood)
+ return true;
+
+ return false;
+}
+
+struct smb_version_operations smb1_operations = {
+ .send_cancel = send_nt_cancel,
+ .compare_fids = cifs_compare_fids,
+ .setup_request = cifs_setup_request,
+ .setup_async_request = cifs_setup_async_request,
+ .check_receive = cifs_check_receive,
+ .add_credits = cifs_add_credits,
+ .set_credits = cifs_set_credits,
+ .get_credits_field = cifs_get_credits_field,
+ .get_credits = cifs_get_credits,
+ .wait_mtu_credits = cifs_wait_mtu_credits,
+ .get_next_mid = cifs_get_next_mid,
+ .read_data_offset = cifs_read_data_offset,
+ .read_data_length = cifs_read_data_length,
+ .map_error = map_smb_to_linux_error,
+ .find_mid = cifs_find_mid,
+ .check_message = checkSMB,
+ .dump_detail = cifs_dump_detail,
+ .clear_stats = cifs_clear_stats,
+ .print_stats = cifs_print_stats,
+ .is_oplock_break = is_valid_oplock_break,
+ .downgrade_oplock = cifs_downgrade_oplock,
+ .check_trans2 = cifs_check_trans2,
+ .need_neg = cifs_need_neg,
+ .negotiate = cifs_negotiate,
+ .negotiate_wsize = cifs_negotiate_wsize,
+ .negotiate_rsize = cifs_negotiate_rsize,
+ .sess_setup = CIFS_SessSetup,
+ .logoff = CIFSSMBLogoff,
+ .tree_connect = CIFSTCon,
+ .tree_disconnect = CIFSSMBTDis,
+ .get_dfs_refer = CIFSGetDFSRefer,
+ .qfs_tcon = cifs_qfs_tcon,
+ .is_path_accessible = cifs_is_path_accessible,
+ .can_echo = cifs_can_echo,
+ .query_path_info = cifs_query_path_info,
+ .query_file_info = cifs_query_file_info,
+ .get_srv_inum = cifs_get_srv_inum,
+ .set_path_size = CIFSSMBSetEOF,
+ .set_file_size = CIFSSMBSetFileSize,
+ .set_file_info = smb_set_file_info,
+ .set_compression = cifs_set_compression,
+ .echo = CIFSSMBEcho,
+ .mkdir = CIFSSMBMkDir,
+ .mkdir_setinfo = cifs_mkdir_setinfo,
+ .rmdir = CIFSSMBRmDir,
+ .unlink = CIFSSMBDelFile,
+ .rename_pending_delete = cifs_rename_pending_delete,
+ .rename = CIFSSMBRename,
+ .create_hardlink = CIFSCreateHardLink,
+ .query_symlink = cifs_query_symlink,
+ .open = cifs_open_file,
+ .set_fid = cifs_set_fid,
+ .close = cifs_close_file,
+ .flush = cifs_flush_file,
+ .async_readv = cifs_async_readv,
+ .async_writev = cifs_async_writev,
+ .sync_read = cifs_sync_read,
+ .sync_write = cifs_sync_write,
+ .query_dir_first = cifs_query_dir_first,
+ .query_dir_next = cifs_query_dir_next,
+ .close_dir = cifs_close_dir,
+ .calc_smb_size = smbCalcSize,
+ .oplock_response = cifs_oplock_response,
+ .queryfs = cifs_queryfs,
+ .mand_lock = cifs_mand_lock,
+ .mand_unlock_range = cifs_unlock_range,
+ .push_mand_locks = cifs_push_mandatory_locks,
+ .query_mf_symlink = cifs_query_mf_symlink,
+ .create_mf_symlink = cifs_create_mf_symlink,
+ .is_read_op = cifs_is_read_op,
+ .wp_retry_size = cifs_wp_retry_size,
+ .dir_needs_close = cifs_dir_needs_close,
+ .select_sectype = cifs_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+ .query_all_EAs = CIFSSMBQAllEAs,
+ .set_EA = CIFSSMBSetEA,
+#endif /* CIFS_XATTR */
+#ifdef CONFIG_CIFS_ACL
+ .get_acl = get_cifs_acl,
+ .get_acl_by_fid = get_cifs_acl_by_fid,
+ .set_acl = set_cifs_acl,
+#endif /* CIFS_ACL */
+};
+
+struct smb_version_values smb1_values = {
+ .version_string = SMB1_VERSION_STRING,
+ .large_lock_type = LOCKING_ANDX_LARGE_FILES,
+ .exclusive_lock_type = 0,
+ .shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
+ .unlock_lock_type = 0,
+ .header_preamble_size = 4,
+ .header_size = sizeof(struct smb_hdr),
+ .max_header_size = MAX_CIFS_HDR_SIZE,
+ .read_rsp_size = sizeof(READ_RSP),
+ .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX),
+ .cap_unix = CAP_UNIX,
+ .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
+ .cap_large_files = CAP_LARGE_FILES,
+ .signing_enabled = SECMODE_SIGN_ENABLED,
+ .signing_required = SECMODE_SIGN_REQUIRED,
+};
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
new file mode 100644
index 000000000..9e2e120e1
--- /dev/null
+++ b/fs/cifs/smb2file.c
@@ -0,0 +1,287 @@
+/*
+ * fs/cifs/smb2file.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002, 2011
+ * Author(s): Steve French (sfrench@us.ibm.com),
+ * Pavel Shilovsky ((pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "fscache.h"
+#include "smb2proto.h"
+
+int
+smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
+ __u32 *oplock, FILE_ALL_INFO *buf)
+{
+ int rc;
+ __le16 *smb2_path;
+ struct smb2_file_all_info *smb2_data = NULL;
+ __u8 smb2_oplock;
+ struct cifs_fid *fid = oparms->fid;
+ struct network_resiliency_req nr_ioctl_req;
+
+ smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
+ if (smb2_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+ GFP_KERNEL);
+ if (smb2_data == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ oparms->desired_access |= FILE_READ_ATTRIBUTES;
+ smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
+
+ rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL,
+ NULL);
+ if (rc)
+ goto out;
+
+
+ if (oparms->tcon->use_resilient) {
+ nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */
+ nr_ioctl_req.Reserved = 0;
+ rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
+ fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
+ true /* is_fsctl */,
+ (char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
+ NULL, NULL /* no return info */);
+ if (rc == -EOPNOTSUPP) {
+ cifs_dbg(VFS,
+ "resiliency not supported by server, disabling\n");
+ oparms->tcon->use_resilient = false;
+ } else if (rc)
+ cifs_dbg(FYI, "error %d setting resiliency\n", rc);
+
+ rc = 0;
+ }
+
+ if (buf) {
+ /* open response does not have IndexNumber field - get it */
+ rc = SMB2_get_srv_num(xid, oparms->tcon, fid->persistent_fid,
+ fid->volatile_fid,
+ &smb2_data->IndexNumber);
+ if (rc) {
+ /* let get_inode_info disable server inode numbers */
+ smb2_data->IndexNumber = 0;
+ rc = 0;
+ }
+ move_smb2_info_to_cifs(buf, smb2_data);
+ }
+
+ *oplock = smb2_oplock;
+out:
+ kfree(smb2_data);
+ kfree(smb2_path);
+ return rc;
+}
+
+int
+smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
+ const unsigned int xid)
+{
+ int rc = 0, stored_rc;
+ unsigned int max_num, num = 0, max_buf;
+ struct smb2_lock_element *buf, *cur;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct cifsLockInfo *li, *tmp;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct list_head tmp_llist;
+
+ INIT_LIST_HEAD(&tmp_llist);
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it before using.
+ */
+ max_buf = tcon->ses->server->maxBuf;
+ if (max_buf < sizeof(struct smb2_lock_element))
+ return -EINVAL;
+
+ BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE);
+ max_buf = min_t(unsigned int, max_buf, PAGE_SIZE);
+ max_num = max_buf / sizeof(struct smb2_lock_element);
+ buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ cur = buf;
+
+ cifs_down_write(&cinode->lock_sem);
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
+ if (flock->fl_start > li->offset ||
+ (flock->fl_start + length) <
+ (li->offset + li->length))
+ continue;
+ if (current->tgid != li->pid)
+ continue;
+ if (cinode->can_cache_brlcks) {
+ /*
+ * We can cache brlock requests - simply remove a lock
+ * from the file's list.
+ */
+ list_del(&li->llist);
+ cifs_del_lock_waiters(li);
+ kfree(li);
+ continue;
+ }
+ cur->Length = cpu_to_le64(li->length);
+ cur->Offset = cpu_to_le64(li->offset);
+ cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK);
+ /*
+ * We need to save a lock here to let us add it again to the
+ * file's list if the unlock range request fails on the server.
+ */
+ list_move(&li->llist, &tmp_llist);
+ if (++num == max_num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc) {
+ /*
+ * We failed on the unlock range request - add
+ * all locks from the tmp list to the head of
+ * the file's list.
+ */
+ cifs_move_llist(&tmp_llist,
+ &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ /*
+ * The unlock range request succeed - free the
+ * tmp list.
+ */
+ cifs_free_llist(&tmp_llist);
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, current->tgid,
+ num, buf);
+ if (stored_rc) {
+ cifs_move_llist(&tmp_llist, &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ cifs_free_llist(&tmp_llist);
+ }
+ up_write(&cinode->lock_sem);
+
+ kfree(buf);
+ return rc;
+}
+
+static int
+smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid,
+ struct smb2_lock_element *buf, unsigned int max_num)
+{
+ int rc = 0, stored_rc;
+ struct cifsFileInfo *cfile = fdlocks->cfile;
+ struct cifsLockInfo *li;
+ unsigned int num = 0;
+ struct smb2_lock_element *cur = buf;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+
+ list_for_each_entry(li, &fdlocks->locks, llist) {
+ cur->Length = cpu_to_le64(li->length);
+ cur->Offset = cpu_to_le64(li->offset);
+ cur->Flags = cpu_to_le32(li->type |
+ SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+ if (++num == max_num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+
+ return rc;
+}
+
+int
+smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
+{
+ int rc = 0, stored_rc;
+ unsigned int xid;
+ unsigned int max_num, max_buf;
+ struct smb2_lock_element *buf;
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct cifs_fid_locks *fdlocks;
+
+ xid = get_xid();
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it for zero before using.
+ */
+ max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
+ if (max_buf < sizeof(struct smb2_lock_element)) {
+ free_xid(xid);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE);
+ max_buf = min_t(unsigned int, max_buf, PAGE_SIZE);
+ max_num = max_buf / sizeof(struct smb2_lock_element);
+ buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL);
+ if (!buf) {
+ free_xid(xid);
+ return -ENOMEM;
+ }
+
+ list_for_each_entry(fdlocks, &cinode->llist, llist) {
+ stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+
+ kfree(buf);
+ free_xid(xid);
+ return rc;
+}
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
new file mode 100644
index 000000000..0ffa18094
--- /dev/null
+++ b/fs/cifs/smb2glob.h
@@ -0,0 +1,64 @@
+/*
+ * fs/cifs/smb2glob.h
+ *
+ * Definitions for various global variables and structures
+ *
+ * Copyright (C) International Business Machines Corp., 2002, 2011
+ * Etersoft, 2012
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ */
+#ifndef _SMB2_GLOB_H
+#define _SMB2_GLOB_H
+
+#define SMB2_MAGIC_NUMBER 0xFE534D42
+
+/*
+ *****************************************************************
+ * Constants go here
+ *****************************************************************
+ */
+
+/*
+ * Identifiers for functions that use the open, operation, close pattern
+ * in smb2inode.c:smb2_open_op_close()
+ */
+#define SMB2_OP_SET_DELETE 1
+#define SMB2_OP_SET_INFO 2
+#define SMB2_OP_QUERY_INFO 3
+#define SMB2_OP_QUERY_DIR 4
+#define SMB2_OP_MKDIR 5
+#define SMB2_OP_RENAME 6
+#define SMB2_OP_DELETE 7
+#define SMB2_OP_HARDLINK 8
+#define SMB2_OP_SET_EOF 9
+#define SMB2_OP_RMDIR 10
+
+/* Used when constructing chained read requests. */
+#define CHAINED_REQUEST 1
+#define START_OF_CHAIN 2
+#define END_OF_CHAIN 4
+#define RELATED_REQUEST 8
+
+#define SMB2_SIGNATURE_SIZE (16)
+#define SMB2_NTLMV2_SESSKEY_SIZE (16)
+#define SMB2_HMACSHA256_SIZE (32)
+#define SMB2_CMACAES_SIZE (16)
+#define SMB3_SIGNKEY_SIZE (16)
+
+/* Maximum buffer size value we can send with 1 credit */
+#define SMB2_MAX_BUFFER_SIZE 65536
+
+#endif /* _SMB2_GLOB_H */
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
new file mode 100644
index 000000000..1eef1791d
--- /dev/null
+++ b/fs/cifs/smb2inode.c
@@ -0,0 +1,299 @@
+/*
+ * fs/cifs/smb2inode.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002, 2011
+ * Etersoft, 2012
+ * Author(s): Pavel Shilovsky (pshilovsky@samba.org),
+ * Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "fscache.h"
+#include "smb2glob.h"
+#include "smb2pdu.h"
+#include "smb2proto.h"
+
+static int
+smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+ __u32 desired_access, __u32 create_disposition,
+ __u32 create_options, void *data, int command)
+{
+ int rc, tmprc = 0;
+ __le16 *utf16_path = NULL;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+ bool use_cached_root_handle = false;
+
+ if ((strcmp(full_path, "") == 0) && (create_options == 0) &&
+ (desired_access == FILE_READ_ATTRIBUTES) &&
+ (create_disposition == FILE_OPEN) &&
+ (tcon->nohandlecache == false)) {
+ rc = open_shroot(xid, tcon, &fid);
+ if (rc == 0)
+ use_cached_root_handle = true;
+ }
+
+ if (use_cached_root_handle == false) {
+ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = desired_access;
+ oparms.disposition = create_disposition;
+ oparms.create_options = create_options;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
+ NULL);
+ if (rc) {
+ kfree(utf16_path);
+ return rc;
+ }
+ }
+
+ switch (command) {
+ case SMB2_OP_DELETE:
+ break;
+ case SMB2_OP_QUERY_INFO:
+ tmprc = SMB2_query_info(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid,
+ (struct smb2_file_all_info *)data);
+ break;
+ case SMB2_OP_MKDIR:
+ /*
+ * Directories are created through parameters in the
+ * SMB2_open() call.
+ */
+ break;
+ case SMB2_OP_RMDIR:
+ tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid);
+ break;
+ case SMB2_OP_RENAME:
+ tmprc = SMB2_rename(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, (__le16 *)data);
+ break;
+ case SMB2_OP_HARDLINK:
+ tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, (__le16 *)data);
+ break;
+ case SMB2_OP_SET_EOF:
+ tmprc = SMB2_set_eof(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, current->tgid,
+ (__le64 *)data, false);
+ break;
+ case SMB2_OP_SET_INFO:
+ tmprc = SMB2_set_info(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid,
+ (FILE_BASIC_INFO *)data);
+ break;
+ default:
+ cifs_dbg(VFS, "Invalid command\n");
+ break;
+ }
+
+ if (use_cached_root_handle)
+ close_shroot(&tcon->crfid);
+ else
+ rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ if (tmprc)
+ rc = tmprc;
+ kfree(utf16_path);
+ return rc;
+}
+
+void
+move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
+{
+ memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
+ dst->CurrentByteOffset = src->CurrentByteOffset;
+ dst->Mode = src->Mode;
+ dst->AlignmentRequirement = src->AlignmentRequirement;
+ dst->IndexNumber1 = 0; /* we don't use it */
+}
+
+int
+smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+ FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink)
+{
+ int rc;
+ struct smb2_file_all_info *smb2_data;
+
+ *adjust_tz = false;
+ *symlink = false;
+
+ smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+ GFP_KERNEL);
+ if (smb2_data == NULL)
+ return -ENOMEM;
+
+ rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN, 0,
+ smb2_data, SMB2_OP_QUERY_INFO);
+ if (rc == -EOPNOTSUPP) {
+ *symlink = true;
+ /* Failed on a symbolic link - query a reparse point info */
+ rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
+ FILE_READ_ATTRIBUTES, FILE_OPEN,
+ OPEN_REPARSE_POINT, smb2_data,
+ SMB2_OP_QUERY_INFO);
+ }
+ if (rc)
+ goto out;
+
+ move_smb2_info_to_cifs(data, smb2_data);
+out:
+ kfree(smb2_data);
+ return rc;
+}
+
+int
+smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_open_op_close(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+ CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR);
+}
+
+void
+smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
+ const unsigned int xid)
+{
+ FILE_BASIC_INFO data;
+ struct cifsInodeInfo *cifs_i;
+ u32 dosattrs;
+ int tmprc;
+
+ memset(&data, 0, sizeof(data));
+ cifs_i = CIFS_I(inode);
+ dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
+ data.Attributes = cpu_to_le32(dosattrs);
+ tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
+ CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO);
+ if (tmprc == 0)
+ cifs_i->cifsAttrs = dosattrs;
+}
+
+int
+smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ CREATE_NOT_FILE,
+ NULL, SMB2_OP_RMDIR);
+}
+
+int
+smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
+ NULL, SMB2_OP_DELETE);
+}
+
+static int
+smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb, __u32 access, int command)
+{
+ __le16 *smb2_to_name = NULL;
+ int rc;
+
+ smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
+ if (smb2_to_name == NULL) {
+ rc = -ENOMEM;
+ goto smb2_rename_path;
+ }
+
+ rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access,
+ FILE_OPEN, 0, smb2_to_name, command);
+smb2_rename_path:
+ kfree(smb2_to_name);
+ return rc;
+}
+
+int
+smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+ DELETE, SMB2_OP_RENAME);
+}
+
+int
+smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+ FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK);
+}
+
+int
+smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_alloc)
+{
+ __le64 eof = cpu_to_le64(size);
+ return smb2_open_op_close(xid, tcon, cifs_sb, full_path,
+ FILE_WRITE_DATA, FILE_OPEN, 0, &eof,
+ SMB2_OP_SET_EOF);
+}
+
+int
+smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ int rc;
+
+ if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
+ (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
+ (buf->Attributes == 0))
+ return 0; /* would be a no op, no sense sending this */
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
+ FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
+ SMB2_OP_SET_INFO);
+ cifs_put_tlink(tlink);
+ return rc;
+}
diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c
new file mode 100644
index 000000000..3c0bad577
--- /dev/null
+++ b/fs/cifs/smb2maperror.c
@@ -0,0 +1,2490 @@
+/*
+ * fs/smb2/smb2maperror.c
+ *
+ * Functions which do error mapping of SMB2 status codes to POSIX errors
+ *
+ * Copyright (C) International Business Machines Corp., 2009
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/errno.h>
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "smb2pdu.h"
+#include "smb2proto.h"
+#include "smb2status.h"
+#include "smb2glob.h"
+#include "trace.h"
+
+struct status_to_posix_error {
+ __le32 smb2_status;
+ int posix_error;
+ char *status_string;
+};
+
+static const struct status_to_posix_error smb2_error_map_table[] = {
+ {STATUS_SUCCESS, 0, "STATUS_SUCCESS"},
+ {STATUS_WAIT_0, 0, "STATUS_WAIT_0"},
+ {STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"},
+ {STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"},
+ {STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"},
+ {STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"},
+ {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"},
+ {STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"},
+ {STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"},
+ {STATUS_USER_APC, -EIO, "STATUS_USER_APC"},
+ {STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"},
+ {STATUS_ALERTED, -EIO, "STATUS_ALERTED"},
+ {STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"},
+ {STATUS_PENDING, -EIO, "STATUS_PENDING"},
+ {STATUS_REPARSE, -EIO, "STATUS_REPARSE"},
+ {STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"},
+ {STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"},
+ {STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"},
+ {STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO,
+ "STATUS_OPLOCK_BREAK_IN_PROGRESS"},
+ {STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"},
+ {STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"},
+ {STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"},
+ {STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"},
+ {STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"},
+ {STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO,
+ "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"},
+ {STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"},
+ {STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"},
+ {STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO,
+ "STATUS_PAGE_FAULT_COPY_ON_WRITE"},
+ {STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"},
+ {STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"},
+ {STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"},
+ {STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"},
+ {STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"},
+ {STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"},
+ {STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO,
+ "STATUS_RESOURCE_REQUIREMENTS_CHANGED"},
+ {STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"},
+ {STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO,
+ "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"},
+ {STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"},
+ {STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"},
+ {STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"},
+ {STATUS_VOLSNAP_HIBERNATE_READY, -EIO,
+ "STATUS_VOLSNAP_HIBERNATE_READY"},
+ {STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO,
+ "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"},
+ {STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO,
+ "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"},
+ {STATUS_INTERRUPT_STILL_CONNECTED, -EIO,
+ "STATUS_INTERRUPT_STILL_CONNECTED"},
+ {STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"},
+ {STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO,
+ "STATUS_FILE_LOCKED_WITH_ONLY_READERS"},
+ {STATUS_FILE_LOCKED_WITH_WRITERS, -EIO,
+ "STATUS_FILE_LOCKED_WITH_WRITERS"},
+ {STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS,
+ "STATUS_RESOURCEMANAGER_READ_ONLY"},
+ {STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"},
+ {DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"},
+ {DBG_CONTINUE, -EIO, "DBG_CONTINUE"},
+ {STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"},
+ {STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"},
+ {STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"},
+ {STATUS_WORKING_SET_LIMIT_RANGE, -EIO,
+ "STATUS_WORKING_SET_LIMIT_RANGE"},
+ {STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"},
+ {STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"},
+ {STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"},
+ {STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"},
+ {STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"},
+ {STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"},
+ {STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"},
+ {STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO,
+ "STATUS_FT_READ_RECOVERY_FROM_BACKUP"},
+ {STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"},
+ {STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT,
+ "STATUS_SERIAL_COUNTER_TIMEOUT"},
+ {STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"},
+ {STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO,
+ "STATUS_IMAGE_MACHINE_TYPE_MISMATCH"},
+ {STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"},
+ {STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"},
+ {STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO,
+ "STATUS_RECEIVE_PARTIAL_EXPEDITED"},
+ {STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"},
+ {STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"},
+ {STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"},
+ {STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"},
+ {STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"},
+ {STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"},
+ {STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"},
+ {STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"},
+ {STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"},
+ {STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"},
+ {STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"},
+ {STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"},
+ {STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"},
+ {STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"},
+ {STATUS_WX86_EXCEPTION_CONTINUE, -EIO,
+ "STATUS_WX86_EXCEPTION_CONTINUE"},
+ {STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO,
+ "STATUS_WX86_EXCEPTION_LASTCHANCE"},
+ {STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"},
+ {STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO,
+ "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"},
+ {STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"},
+ {STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"},
+ {STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"},
+ {STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"},
+ {STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"},
+ {STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"},
+ {STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"},
+ {STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"},
+ {STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"},
+ {STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO,
+ "STATUS_DRIVERS_LEAKING_LOCKED_PAGES"},
+ {STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"},
+ {STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO,
+ "STATUS_SYSTEM_POWERSTATE_TRANSITION"},
+ {STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO,
+ "STATUS_ALPC_CHECK_COMPLETION_LIST"},
+ {STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO,
+ "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"},
+ {STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"},
+ {STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"},
+ {STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"},
+ {STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"},
+ {STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"},
+ {DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"},
+ {DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"},
+ {DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"},
+ {DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"},
+ {DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"},
+ {DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"},
+ {DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"},
+ {DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"},
+ {DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"},
+ {RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"},
+ {RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"},
+ {STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"},
+ {STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"},
+ {STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO,
+ "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"},
+ {STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"},
+ {STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"},
+ {STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"},
+ {STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO,
+ "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"},
+ {STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO,
+ "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"},
+ {STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO,
+ "STATUS_GRAPHICS_DRIVER_MISMATCH"},
+ {STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO,
+ "STATUS_GRAPHICS_MODE_NOT_PINNED"},
+ {STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO,
+ "STATUS_GRAPHICS_NO_PREFERRED_MODE"},
+ {STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO,
+ "STATUS_GRAPHICS_DATASET_IS_EMPTY"},
+ {STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO,
+ "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"},
+ {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO,
+ "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"},
+ {STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO,
+ "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"},
+ {STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO,
+ "STATUS_GRAPHICS_LEADLINK_START_DEFERRED"},
+ {STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO,
+ "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"},
+ {STATUS_GRAPHICS_START_DEFERRED, -EIO,
+ "STATUS_GRAPHICS_START_DEFERRED"},
+ {STATUS_NDIS_INDICATION_REQUIRED, -EIO,
+ "STATUS_NDIS_INDICATION_REQUIRED"},
+ {STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"},
+ {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"},
+ {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"},
+ {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"},
+ {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"},
+ {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"},
+ {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"},
+ {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"},
+ {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"},
+ {STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"},
+ {STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"},
+ {STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"},
+ {STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"},
+ {STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"},
+ {STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"},
+ {STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"},
+ {STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"},
+ {STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"},
+ {STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"},
+ {STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"},
+ {STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"},
+ {STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"},
+ {STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"},
+ {STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"},
+ {STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"},
+ {STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"},
+ {STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"},
+ {STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"},
+ {STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"},
+ {STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"},
+ {STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"},
+ {STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO,
+ "STATUS_REDIRECTOR_HAS_OPEN_HANDLES"},
+ {STATUS_SERVER_HAS_OPEN_HANDLES, -EIO,
+ "STATUS_SERVER_HAS_OPEN_HANDLES"},
+ {STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"},
+ {STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"},
+ {STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO,
+ "STATUS_CLEANER_CARTRIDGE_INSTALLED"},
+ {STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"},
+ {STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"},
+ {STATUS_REGISTRY_HIVE_RECOVERED, -EIO,
+ "STATUS_REGISTRY_HIVE_RECOVERED"},
+ {STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"},
+ {STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO,
+ "STATUS_DLL_MIGHT_BE_INCOMPATIBLE"},
+ {STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"},
+ {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP,
+ "STATUS_REPARSE_NOT_HANDLED"},
+ {STATUS_DEVICE_REQUIRES_CLEANING, -EIO,
+ "STATUS_DEVICE_REQUIRES_CLEANING"},
+ {STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"},
+ {STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"},
+ {DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"},
+ {STATUS_CLUSTER_NODE_ALREADY_UP, -EIO,
+ "STATUS_CLUSTER_NODE_ALREADY_UP"},
+ {STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO,
+ "STATUS_CLUSTER_NODE_ALREADY_DOWN"},
+ {STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO,
+ "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"},
+ {STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO,
+ "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"},
+ {STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO,
+ "STATUS_CLUSTER_NODE_ALREADY_MEMBER"},
+ {STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"},
+ {STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"},
+ {STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO,
+ "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"},
+ {STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO,
+ "STATUS_TXF_METADATA_ALREADY_PRESENT"},
+ {STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO,
+ "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"},
+ {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO,
+ "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"},
+ {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"},
+ {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"},
+ {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"},
+ {STATUS_NOT_IMPLEMENTED, -ENOSYS, "STATUS_NOT_IMPLEMENTED"},
+ {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"},
+ {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"},
+ {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"},
+ {STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"},
+ {STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"},
+ {STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"},
+ {STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"},
+ {STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"},
+ {STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"},
+ {STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"},
+ {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"},
+ {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"},
+ {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"},
+ {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"},
+ {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"},
+ {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"},
+ {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"},
+ {STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"},
+ {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"},
+ {STATUS_MORE_PROCESSING_REQUIRED, -EIO,
+ "STATUS_MORE_PROCESSING_REQUIRED"},
+ {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"},
+ {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE,
+ "STATUS_CONFLICTING_ADDRESSES"},
+ {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"},
+ {STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"},
+ {STATUS_UNABLE_TO_DELETE_SECTION, -EIO,
+ "STATUS_UNABLE_TO_DELETE_SECTION"},
+ {STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"},
+ {STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"},
+ {STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"},
+ {STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"},
+ {STATUS_INVALID_FILE_FOR_SECTION, -EIO,
+ "STATUS_INVALID_FILE_FOR_SECTION"},
+ {STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"},
+ {STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"},
+ {STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"},
+ {STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"},
+ {STATUS_NONCONTINUABLE_EXCEPTION, -EIO,
+ "STATUS_NONCONTINUABLE_EXCEPTION"},
+ {STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"},
+ {STATUS_UNWIND, -EIO, "STATUS_UNWIND"},
+ {STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"},
+ {STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"},
+ {STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"},
+ {STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"},
+ {STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"},
+ {STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"},
+ {STATUS_INVALID_PORT_ATTRIBUTES, -EIO,
+ "STATUS_INVALID_PORT_ATTRIBUTES"},
+ {STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"},
+ {STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"},
+ {STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"},
+ {STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"},
+ {STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"},
+ {STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"},
+ {STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"},
+ {STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"},
+ {STATUS_DEVICE_ALREADY_ATTACHED, -EIO,
+ "STATUS_DEVICE_ALREADY_ATTACHED"},
+ {STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"},
+ {STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"},
+ {STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"},
+ {STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"},
+ {STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"},
+ {STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"},
+ {STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"},
+ {STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"},
+ {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED,
+ "STATUS_PORT_CONNECTION_REFUSED"},
+ {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"},
+ {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"},
+ {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"},
+ {STATUS_INVALID_PAGE_PROTECTION, -EIO,
+ "STATUS_INVALID_PAGE_PROTECTION"},
+ {STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"},
+ {STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO,
+ "STATUS_SEMAPHORE_LIMIT_EXCEEDED"},
+ {STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"},
+ {STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"},
+ {STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"},
+ {STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"},
+ {STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"},
+ {STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"},
+ {STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"},
+ {STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"},
+ {STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"},
+ {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"},
+ {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"},
+ {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"},
+ {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"},
+ {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"},
+ {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"},
+ {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS,
+ "STATUS_CTL_FILE_NOT_SUPPORTED"},
+ {STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"},
+ {STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"},
+ {STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"},
+ {STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"},
+ {STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"},
+ {STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"},
+ {STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"},
+ {STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"},
+ {STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"},
+ {STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"},
+ {STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"},
+ {STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"},
+ {STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"},
+ {STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"},
+ {STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"},
+ {STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"},
+ {STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"},
+ {STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"},
+ {STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"},
+ {STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"},
+ {STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"},
+ {STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"},
+ {STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"},
+ {STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"},
+ {STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"},
+ {STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"},
+ {STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"},
+ {STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"},
+ {STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO,
+ "STATUS_TOO_MANY_LUIDS_REQUESTED"},
+ {STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"},
+ {STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"},
+ {STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"},
+ {STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"},
+ {STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"},
+ {STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"},
+ {STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"},
+ {STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"},
+ {STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"},
+ {STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"},
+ {STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"},
+ {STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"},
+ {STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"},
+ {STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO,
+ "STATUS_TOO_MANY_GUIDS_REQUESTED"},
+ {STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"},
+ {STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"},
+ {STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"},
+ {STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"},
+ {STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"},
+ {STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"},
+ {STATUS_RESOURCE_DATA_NOT_FOUND, -EIO,
+ "STATUS_RESOURCE_DATA_NOT_FOUND"},
+ {STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO,
+ "STATUS_RESOURCE_TYPE_NOT_FOUND"},
+ {STATUS_RESOURCE_NAME_NOT_FOUND, -EIO,
+ "STATUS_RESOURCE_NAME_NOT_FOUND"},
+ {STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"},
+ {STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"},
+ {STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"},
+ {STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"},
+ {STATUS_FLOAT_INVALID_OPERATION, -EIO,
+ "STATUS_FLOAT_INVALID_OPERATION"},
+ {STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"},
+ {STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"},
+ {STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"},
+ {STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"},
+ {STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"},
+ {STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"},
+ {STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"},
+ {STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"},
+ {STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO,
+ "STATUS_ALLOTTED_SPACE_EXCEEDED"},
+ {STATUS_INSUFFICIENT_RESOURCES, -EAGAIN,
+ "STATUS_INSUFFICIENT_RESOURCES"},
+ {STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"},
+ {STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"},
+ {STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"},
+ {STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"},
+ {STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"},
+ {STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"},
+ {STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"},
+ {STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"},
+ {STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"},
+ {STATUS_INVALID_GROUP_ATTRIBUTES, -EIO,
+ "STATUS_INVALID_GROUP_ATTRIBUTES"},
+ {STATUS_BAD_IMPERSONATION_LEVEL, -EIO,
+ "STATUS_BAD_IMPERSONATION_LEVEL"},
+ {STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"},
+ {STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"},
+ {STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"},
+ {STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"},
+ {STATUS_INSTRUCTION_MISALIGNMENT, -EIO,
+ "STATUS_INSTRUCTION_MISALIGNMENT"},
+ {STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"},
+ {STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"},
+ {STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"},
+ {STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"},
+ {STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"},
+ {STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"},
+ {STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"},
+ {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"},
+ {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"},
+ {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"},
+ {STATUS_IO_TIMEOUT, -ETIMEDOUT, "STATUS_IO_TIMEOUT"},
+ {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"},
+ {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"},
+ {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"},
+ {STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"},
+ {STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"},
+ {STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"},
+ {STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN,
+ "STATUS_REMOTE_NOT_LISTENING"},
+ {STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"},
+ {STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"},
+ {STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"},
+ {STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"},
+ {STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"},
+ {STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"},
+ {STATUS_INVALID_NETWORK_RESPONSE, -EIO,
+ "STATUS_INVALID_NETWORK_RESPONSE"},
+ {STATUS_UNEXPECTED_NETWORK_ERROR, -EIO,
+ "STATUS_UNEXPECTED_NETWORK_ERROR"},
+ {STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"},
+ {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"},
+ {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"},
+ {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"},
+ {STATUS_NETWORK_NAME_DELETED, -EIO, "STATUS_NETWORK_NAME_DELETED"},
+ {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"},
+ {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"},
+ {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"},
+ {STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"},
+ {STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"},
+ {STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"},
+ {STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"},
+ {STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"},
+ {STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"},
+ {STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"},
+ {STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"},
+ {STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"},
+ {STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"},
+ {STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"},
+ {STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"},
+ {STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"},
+ {STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO,
+ "STATUS_CANT_ACCESS_DOMAIN_INFO"},
+ {STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"},
+ {STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"},
+ {STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"},
+ {STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"},
+ {STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"},
+ {STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"},
+ {STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"},
+ {STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"},
+ {STATUS_INVALID_OPLOCK_PROTOCOL, -EIO,
+ "STATUS_INVALID_OPLOCK_PROTOCOL"},
+ {STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"},
+ {STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"},
+ {STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"},
+ {STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"},
+ {STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"},
+ {STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"},
+ {STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO,
+ "STATUS_UNEXPECTED_MM_CREATE_ERR"},
+ {STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO,
+ "STATUS_UNEXPECTED_MM_MAP_ERROR"},
+ {STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO,
+ "STATUS_UNEXPECTED_MM_EXTEND_ERR"},
+ {STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"},
+ {STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"},
+ {STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"},
+ {STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"},
+ {STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"},
+ {STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"},
+ {STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"},
+ {STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"},
+ {STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"},
+ {STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"},
+ {STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"},
+ {STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"},
+ {STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"},
+ {STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"},
+ {STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"},
+ {STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"},
+ {STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"},
+ {STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"},
+ {STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"},
+ {STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"},
+ {STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"},
+ {STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"},
+ {STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"},
+ {STATUS_BAD_LOGON_SESSION_STATE, -EIO,
+ "STATUS_BAD_LOGON_SESSION_STATE"},
+ {STATUS_LOGON_SESSION_COLLISION, -EIO,
+ "STATUS_LOGON_SESSION_COLLISION"},
+ {STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"},
+ {STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"},
+ {STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"},
+ {STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"},
+ {STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"},
+ {STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"},
+ {STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"},
+ {STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"},
+ {STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"},
+ {STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"},
+ {STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"},
+ {STATUS_ABIOS_LID_ALREADY_OWNED, -EIO,
+ "STATUS_ABIOS_LID_ALREADY_OWNED"},
+ {STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"},
+ {STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"},
+ {STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"},
+ {STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO,
+ "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"},
+ {STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"},
+ {STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"},
+ {STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"},
+ {STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"},
+ {STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"},
+ {STATUS_INVALID_IMAGE_NE_FORMAT, -EIO,
+ "STATUS_INVALID_IMAGE_NE_FORMAT"},
+ {STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"},
+ {STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"},
+ {STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"},
+ {STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"},
+ {STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"},
+ {STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"},
+ {STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"},
+ {STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"},
+ {STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"},
+ {STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"},
+ {STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"},
+ {STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"},
+ {STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"},
+ {STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"},
+ {STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"},
+ {STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"},
+ {STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT,
+ "STATUS_PAGEFILE_QUOTA_EXCEEDED"},
+ {STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"},
+ {STATUS_INVALID_IMAGE_LE_FORMAT, -EIO,
+ "STATUS_INVALID_IMAGE_LE_FORMAT"},
+ {STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"},
+ {STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"},
+ {STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"},
+ {STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"},
+ {STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"},
+ {STATUS_SYNCHRONIZATION_REQUIRED, -EIO,
+ "STATUS_SYNCHRONIZATION_REQUIRED"},
+ {STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"},
+ {STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"},
+ {STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"},
+ {STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"},
+ {STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"},
+ {STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"},
+ {STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"},
+ {STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"},
+ {STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"},
+ {STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"},
+ {STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"},
+ {STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"},
+ {STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"},
+ {STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"},
+ {STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"},
+ {STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"},
+ {STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"},
+ {STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"},
+ {STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"},
+ {STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"},
+ {STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"},
+ {STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"},
+ {STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"},
+ {STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"},
+ {STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"},
+ {STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"},
+ {STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"},
+ {STATUS_SERIAL_NO_DEVICE_INITED, -EIO,
+ "STATUS_SERIAL_NO_DEVICE_INITED"},
+ {STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"},
+ {STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"},
+ {STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"},
+ {STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"},
+ {STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"},
+ {STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"},
+ {STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"},
+ {STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"},
+ {STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"},
+ {STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"},
+ {STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"},
+ {STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"},
+ {STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO,
+ "STATUS_NT_CROSS_ENCRYPTION_REQUIRED"},
+ {STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO,
+ "STATUS_DOMAIN_CTRLR_CONFIG_ERROR"},
+ {STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"},
+ {STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO,
+ "STATUS_ILL_FORMED_SERVICE_ENTRY"},
+ {STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"},
+ {STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"},
+ {STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"},
+ {STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"},
+ {STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO,
+ "STATUS_FLOPPY_ID_MARK_NOT_FOUND"},
+ {STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"},
+ {STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"},
+ {STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"},
+ {STATUS_DISK_RECALIBRATE_FAILED, -EIO,
+ "STATUS_DISK_RECALIBRATE_FAILED"},
+ {STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"},
+ {STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"},
+ {STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"},
+ {STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"},
+ {STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO,
+ "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"},
+ {STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"},
+ {STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"},
+ {STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"},
+ {STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"},
+ {STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"},
+ {STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"},
+ {STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"},
+ {STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"},
+ {STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"},
+ {STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"},
+ {STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"},
+ {STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"},
+ {STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO,
+ "STATUS_LM_CROSS_ENCRYPTION_REQUIRED"},
+ {STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"},
+ {STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"},
+ {STATUS_DEVICE_CONFIGURATION_ERROR, -EIO,
+ "STATUS_DEVICE_CONFIGURATION_ERROR"},
+ {STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"},
+ {STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"},
+ {STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"},
+ {STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"},
+ {STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"},
+ {STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"},
+ {STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"},
+ {STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"},
+ {STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"},
+ {STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"},
+ {STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO,
+ "STATUS_TRUSTED_RELATIONSHIP_FAILURE"},
+ {STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"},
+ {STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"},
+ {STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"},
+ {STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"},
+ {STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"},
+ {STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"},
+ {STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"},
+ {STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO,
+ "STATUS_NETWORK_CREDENTIAL_CONFLICT"},
+ {STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"},
+ {STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"},
+ {STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO,
+ "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"},
+ {STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO,
+ "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"},
+ {STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO,
+ "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"},
+ {STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO,
+ "STATUS_DOMAIN_TRUST_INCONSISTENT"},
+ {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"},
+ {STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO,
+ "STATUS_IMAGE_ALREADY_LOADED_AS_DLL"},
+ {STATUS_NETWORK_OPEN_RESTRICTION, -EIO,
+ "STATUS_NETWORK_OPEN_RESTRICTION"},
+ {STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"},
+ {STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"},
+ {STATUS_RESOURCE_LANG_NOT_FOUND, -EIO,
+ "STATUS_RESOURCE_LANG_NOT_FOUND"},
+ {STATUS_INSUFF_SERVER_RESOURCES, -EIO,
+ "STATUS_INSUFF_SERVER_RESOURCES"},
+ {STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"},
+ {STATUS_INVALID_ADDRESS_COMPONENT, -EIO,
+ "STATUS_INVALID_ADDRESS_COMPONENT"},
+ {STATUS_INVALID_ADDRESS_WILDCARD, -EIO,
+ "STATUS_INVALID_ADDRESS_WILDCARD"},
+ {STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"},
+ {STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE,
+ "STATUS_ADDRESS_ALREADY_EXISTS"},
+ {STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"},
+ {STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED,
+ "STATUS_CONNECTION_DISCONNECTED"},
+ {STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"},
+ {STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"},
+ {STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"},
+ {STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"},
+ {STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"},
+ {STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"},
+ {STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"},
+ {STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"},
+ {STATUS_TRANSACTION_INVALID_TYPE, -EIO,
+ "STATUS_TRANSACTION_INVALID_TYPE"},
+ {STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"},
+ {STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"},
+ {STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO,
+ "STATUS_CANNOT_LOAD_REGISTRY_FILE"},
+ {STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"},
+ {STATUS_SYSTEM_PROCESS_TERMINATED, -EIO,
+ "STATUS_SYSTEM_PROCESS_TERMINATED"},
+ {STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"},
+ {STATUS_NO_BROWSER_SERVERS_FOUND, -EIO,
+ "STATUS_NO_BROWSER_SERVERS_FOUND"},
+ {STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"},
+ {STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"},
+ {STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"},
+ {STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"},
+ {STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO,
+ "STATUS_IMAGE_CHECKSUM_MISMATCH"},
+ {STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"},
+ {STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO,
+ "STATUS_CLIENT_SERVER_PARAMETERS_INVALID"},
+ {STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"},
+ {STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"},
+ {STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"},
+ {STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"},
+ {STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"},
+ {STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"},
+ {STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"},
+ {STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"},
+ {STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"},
+ {STATUS_RETRY, -EAGAIN, "STATUS_RETRY"},
+ {STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"},
+ {STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"},
+ {STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"},
+ {STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"},
+ {STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"},
+ {STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO,
+ "STATUS_DOMAIN_CONTROLLER_NOT_FOUND"},
+ {STATUS_ACCOUNT_LOCKED_OUT, -EIO, "STATUS_ACCOUNT_LOCKED_OUT"},
+ {STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"},
+ {STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"},
+ {STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"},
+ {STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO,
+ "STATUS_ADDRESS_ALREADY_ASSOCIATED"},
+ {STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"},
+ {STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"},
+ {STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"},
+ {STATUS_NETWORK_UNREACHABLE, -ENETUNREACH,
+ "STATUS_NETWORK_UNREACHABLE"},
+ {STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"},
+ {STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH,
+ "STATUS_PROTOCOL_UNREACHABLE"},
+ {STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"},
+ {STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"},
+ {STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"},
+ {STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"},
+ {STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"},
+ {STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"},
+ {STATUS_TIMER_RESOLUTION_NOT_SET, -EIO,
+ "STATUS_TIMER_RESOLUTION_NOT_SET"},
+ {STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"},
+ {STATUS_LOGIN_TIME_RESTRICTION, -EACCES,
+ "STATUS_LOGIN_TIME_RESTRICTION"},
+ {STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES,
+ "STATUS_LOGIN_WKSTA_RESTRICTION"},
+ {STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"},
+ {STATUS_INSUFFICIENT_LOGON_INFO, -EIO,
+ "STATUS_INSUFFICIENT_LOGON_INFO"},
+ {STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"},
+ {STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"},
+ {STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"},
+ {STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"},
+ {STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"},
+ {STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"},
+ {STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"},
+ {STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"},
+ {STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES,
+ "STATUS_LICENSE_QUOTA_EXCEEDED"},
+ {STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"},
+ {STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"},
+ {STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"},
+ {STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"},
+ {STATUS_UNSUPPORTED_COMPRESSION, -EIO,
+ "STATUS_UNSUPPORTED_COMPRESSION"},
+ {STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"},
+ {STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO,
+ "STATUS_INVALID_PLUGPLAY_DEVICE_PATH"},
+ {STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO,
+ "STATUS_DRIVER_ORDINAL_NOT_FOUND"},
+ {STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO,
+ "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"},
+ {STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"},
+ {STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"},
+ {STATUS_QUOTA_LIST_INCONSISTENT, -EIO,
+ "STATUS_QUOTA_LIST_INCONSISTENT"},
+ {STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"},
+ {STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"},
+ {STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"},
+ {STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"},
+ {STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"},
+ {STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"},
+ {STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"},
+ {STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"},
+ {STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"},
+ {STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"},
+ {STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"},
+ {STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"},
+ {STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"},
+ {STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"},
+ {STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"},
+ {STATUS_IO_REPARSE_TAG_MISMATCH, -EIO,
+ "STATUS_IO_REPARSE_TAG_MISMATCH"},
+ {STATUS_IO_REPARSE_DATA_INVALID, -EIO,
+ "STATUS_IO_REPARSE_DATA_INVALID"},
+ {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO,
+ "STATUS_IO_REPARSE_TAG_NOT_HANDLED"},
+ {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO,
+ "STATUS_REPARSE_POINT_NOT_RESOLVED"},
+ {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO,
+ "STATUS_DIRECTORY_IS_A_REPARSE_POINT"},
+ {STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"},
+ {STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"},
+ {STATUS_DESTINATION_ELEMENT_FULL, -EIO,
+ "STATUS_DESTINATION_ELEMENT_FULL"},
+ {STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO,
+ "STATUS_ILLEGAL_ELEMENT_ADDRESS"},
+ {STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"},
+ {STATUS_REINITIALIZATION_NEEDED, -EIO,
+ "STATUS_REINITIALIZATION_NEEDED"},
+ {STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"},
+ {STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"},
+ {STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"},
+ {STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"},
+ {STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"},
+ {STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"},
+ {STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"},
+ {STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"},
+ {STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"},
+ {STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"},
+ {STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"},
+ {STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"},
+ {STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"},
+ {STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"},
+ {STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"},
+ {STATUS_POLICY_OBJECT_NOT_FOUND, -EIO,
+ "STATUS_POLICY_OBJECT_NOT_FOUND"},
+ {STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"},
+ {STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"},
+ {STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO,
+ "STATUS_REMOTE_STORAGE_NOT_ACTIVE"},
+ {STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO,
+ "STATUS_REMOTE_STORAGE_MEDIA_ERROR"},
+ {STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"},
+ {STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"},
+ {STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO,
+ "STATUS_DS_NO_ATTRIBUTE_OR_VALUE"},
+ {STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO,
+ "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"},
+ {STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO,
+ "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"},
+ {STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO,
+ "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"},
+ {STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"},
+ {STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"},
+ {STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"},
+ {STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"},
+ {STATUS_DS_INCORRECT_ROLE_OWNER, -EIO,
+ "STATUS_DS_INCORRECT_ROLE_OWNER"},
+ {STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"},
+ {STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"},
+ {STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"},
+ {STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"},
+ {STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"},
+ {STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO,
+ "STATUS_DS_CROSS_DOM_MOVE_FAILED"},
+ {STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"},
+ {STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO,
+ "STATUS_DIRECTORY_SERVICE_REQUIRED"},
+ {STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO,
+ "STATUS_REPARSE_ATTRIBUTE_CONFLICT"},
+ {STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"},
+ {STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"},
+ {STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"},
+ {STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"},
+ {STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO,
+ "STATUS_JOURNAL_DELETE_IN_PROGRESS"},
+ {STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"},
+ {STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"},
+ {STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO,
+ "STATUS_DS_ADMIN_LIMIT_EXCEEDED"},
+ {STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"},
+ {STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO,
+ "STATUS_MUTUAL_AUTHENTICATION_FAILED"},
+ {STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"},
+ {STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO,
+ "STATUS_DATATYPE_MISALIGNMENT_ERROR"},
+ {STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"},
+ {STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"},
+ {STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"},
+ {STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"},
+ {STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"},
+ {STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"},
+ {STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"},
+ {STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO,
+ "STATUS_DS_SENSITIVE_GROUP_VIOLATION"},
+ {STATUS_PNP_RESTART_ENUMERATION, -EIO,
+ "STATUS_PNP_RESTART_ENUMERATION"},
+ {STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"},
+ {STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO,
+ "STATUS_DS_CANT_MOD_PRIMARYGROUPID"},
+ {STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO,
+ "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"},
+ {STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"},
+ {STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"},
+ {STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"},
+ {STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO,
+ "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"},
+ {STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO,
+ "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"},
+ {STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO,
+ "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"},
+ {STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO,
+ "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"},
+ {STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO,
+ "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"},
+ {STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO,
+ "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"},
+ {STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO,
+ "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"},
+ {STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO,
+ "STATUS_DS_HAVE_PRIMARY_MEMBERS"},
+ {STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"},
+ {STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"},
+ {STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO,
+ "STATUS_SAM_NEED_BOOTKEY_PASSWORD"},
+ {STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO,
+ "STATUS_SAM_NEED_BOOTKEY_FLOPPY"},
+ {STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"},
+ {STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"},
+ {STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"},
+ {STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"},
+ {STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO,
+ "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"},
+ {STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO,
+ "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"},
+ {STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT,
+ "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"},
+ {STATUS_MULTIPLE_FAULT_VIOLATION, -EIO,
+ "STATUS_MULTIPLE_FAULT_VIOLATION"},
+ {STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO,
+ "STATUS_CURRENT_DOMAIN_NOT_ALLOWED"},
+ {STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"},
+ {STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"},
+ {STATUS_DS_INIT_FAILURE_CONSOLE, -EIO,
+ "STATUS_DS_INIT_FAILURE_CONSOLE"},
+ {STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO,
+ "STATUS_DS_SAM_INIT_FAILURE_CONSOLE"},
+ {STATUS_UNFINISHED_CONTEXT_DELETED, -EIO,
+ "STATUS_UNFINISHED_CONTEXT_DELETED"},
+ {STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"},
+ /* Note that ENOATTTR and ENODATA are the same errno */
+ {STATUS_OBJECTID_NOT_FOUND, -ENODATA, "STATUS_OBJECTID_NOT_FOUND"},
+ {STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"},
+ {STATUS_WRONG_CREDENTIAL_HANDLE, -EIO,
+ "STATUS_WRONG_CREDENTIAL_HANDLE"},
+ {STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"},
+ {STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"},
+ {STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"},
+ {STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO,
+ "STATUS_STRONG_CRYPTO_NOT_SUPPORTED"},
+ {STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"},
+ {STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"},
+ {STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"},
+ {STATUS_SMARTCARD_LOGON_REQUIRED, -EIO,
+ "STATUS_SMARTCARD_LOGON_REQUIRED"},
+ {STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"},
+ {STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"},
+ {STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"},
+ {STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"},
+ {STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO,
+ "STATUS_SERVER_SHUTDOWN_IN_PROGRESS"},
+ {STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP,
+ "STATUS_NOT_SUPPORTED_ON_SBS"},
+ {STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"},
+ {STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"},
+ {STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"},
+ {STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"},
+ {STATUS_COPY_PROTECTION_FAILURE, -EIO,
+ "STATUS_COPY_PROTECTION_FAILURE"},
+ {STATUS_CSS_AUTHENTICATION_FAILURE, -EIO,
+ "STATUS_CSS_AUTHENTICATION_FAILURE"},
+ {STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"},
+ {STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO,
+ "STATUS_CSS_KEY_NOT_ESTABLISHED"},
+ {STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"},
+ {STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"},
+ {STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"},
+ {STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"},
+ {STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO,
+ "STATUS_SMARTCARD_SUBSYSTEM_FAILURE"},
+ {STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"},
+ {STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"},
+ {STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"},
+ {STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"},
+ {STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"},
+ {STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"},
+ {STATUS_DS_VERSION_CHECK_FAILURE, -EIO,
+ "STATUS_DS_VERSION_CHECK_FAILURE"},
+ {STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"},
+ {STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"},
+ {STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO,
+ "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"},
+ {STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"},
+ {STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"},
+ {STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"},
+ {STATUS_NETWORK_SESSION_EXPIRED, -EIO,
+ "STATUS_NETWORK_SESSION_EXPIRED"},
+ {STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"},
+ {STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"},
+ {STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"},
+ {STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES,
+ "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"},
+ {STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES,
+ "STATUS_ACCESS_DISABLED_BY_POLICY_PATH"},
+ {STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES,
+ "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"},
+ {STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES,
+ "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"},
+ {STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"},
+ {STATUS_DEVICE_ENUMERATION_ERROR, -EIO,
+ "STATUS_DEVICE_ENUMERATION_ERROR"},
+ {STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO,
+ "STATUS_MOUNT_POINT_NOT_RESOLVED"},
+ {STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO,
+ "STATUS_INVALID_DEVICE_OBJECT_PARAMETER"},
+ {STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"},
+ {STATUS_DRIVER_BLOCKED_CRITICAL, -EIO,
+ "STATUS_DRIVER_BLOCKED_CRITICAL"},
+ {STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"},
+ {STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"},
+ {STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"},
+ {STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO,
+ "STATUS_INVALID_IMPORT_OF_NON_DLL"},
+ {STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"},
+ {STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES,
+ "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"},
+ {STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"},
+ {STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"},
+ {STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"},
+ {STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"},
+ {STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO,
+ "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"},
+ {STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"},
+ {STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO,
+ "STATUS_SMARTCARD_NO_KEY_CONTAINER"},
+ {STATUS_SMARTCARD_NO_CERTIFICATE, -EIO,
+ "STATUS_SMARTCARD_NO_CERTIFICATE"},
+ {STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"},
+ {STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"},
+ {STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"},
+ {STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"},
+ {STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"},
+ {STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"},
+ {STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"},
+ {STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"},
+ {STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO,
+ "STATUS_DRIVER_FAILED_PRIOR_UNLOAD"},
+ {STATUS_SMARTCARD_SILENT_CONTEXT, -EIO,
+ "STATUS_SMARTCARD_SILENT_CONTEXT"},
+ {STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT,
+ "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"},
+ {STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT,
+ "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"},
+ {STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT,
+ "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"},
+ {STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"},
+ {STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"},
+ {STATUS_DS_GROUP_CONVERSION_ERROR, -EIO,
+ "STATUS_DS_GROUP_CONVERSION_ERROR"},
+ {STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO,
+ "STATUS_VOLSNAP_PREPARE_HIBERNATE"},
+ {STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"},
+ {STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"},
+ {STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"},
+ {STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO,
+ "STATUS_CROSSREALM_DELEGATION_FAILURE"},
+ {STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"},
+ {STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO,
+ "STATUS_ISSUING_CA_UNTRUSTED_KDC"},
+ {STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"},
+ {STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"},
+ {STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT,
+ "STATUS_PARAMETER_QUOTA_EXCEEDED"},
+ {STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"},
+ {STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"},
+ {STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO,
+ "STATUS_AUTHENTICATION_FIREWALL_FAILED"},
+ {STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"},
+ {STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO,
+ "STATUS_HUNG_DISPLAY_DRIVER_THREAD"},
+ {STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO,
+ "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"},
+ {STATUS_INVALID_CRUNTIME_PARAMETER, -EIO,
+ "STATUS_INVALID_CRUNTIME_PARAMETER"},
+ {STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"},
+ {STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"},
+ {STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"},
+ {STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"},
+ {STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO,
+ "STATUS_INCOMPATIBLE_DRIVER_BLOCKED"},
+ {STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"},
+ {STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"},
+ {STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"},
+ {STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"},
+ {STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"},
+ {STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO,
+ "STATUS_REQUEST_OUT_OF_SEQUENCE"},
+ {STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"},
+ {STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"},
+ {STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"},
+ {STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO,
+ "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"},
+ {STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"},
+ {STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"},
+ {STATUS_CRED_REQUIRES_CONFIRMATION, -EIO,
+ "STATUS_CRED_REQUIRES_CONFIRMATION"},
+ {STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO,
+ "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"},
+ {STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO,
+ "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"},
+ {STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO,
+ "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"},
+ {STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO,
+ "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"},
+ {STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO,
+ "STATUS_CS_ENCRYPTION_FILE_NOT_CSE"},
+ {STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"},
+ {STATUS_DRIVER_PROCESS_TERMINATED, -EIO,
+ "STATUS_DRIVER_PROCESS_TERMINATED"},
+ {STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO,
+ "STATUS_AMBIGUOUS_SYSTEM_DEVICE"},
+ {STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO,
+ "STATUS_SYSTEM_DEVICE_NOT_FOUND"},
+ {STATUS_RESTART_BOOT_APPLICATION, -EIO,
+ "STATUS_RESTART_BOOT_APPLICATION"},
+ {STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"},
+ {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"},
+ {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"},
+ {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"},
+ {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"},
+ {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"},
+ {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"},
+ {STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"},
+ {STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"},
+ {STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO,
+ "STATUS_LPC_RECEIVE_BUFFER_EXPECTED"},
+ {STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO,
+ "STATUS_LPC_INVALID_CONNECTION_USAGE"},
+ {STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO,
+ "STATUS_LPC_REQUESTS_NOT_ALLOWED"},
+ {STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"},
+ {STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"},
+ {STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO,
+ "STATUS_THREADPOOL_HANDLE_EXCEPTION"},
+ {STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO,
+ "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"},
+ {STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO,
+ "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"},
+ {STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO,
+ "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"},
+ {STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO,
+ "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"},
+ {STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO,
+ "STATUS_THREADPOOL_RELEASED_DURING_OPERATION"},
+ {STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO,
+ "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"},
+ {STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO,
+ "STATUS_APC_RETURNED_WHILE_IMPERSONATING"},
+ {STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"},
+ {STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"},
+ {STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO,
+ "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"},
+ {STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"},
+ {STATUS_INVALID_IDN_NORMALIZATION, -EIO,
+ "STATUS_INVALID_IDN_NORMALIZATION"},
+ {STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"},
+ {STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"},
+ {STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"},
+ {STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO,
+ "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"},
+ {STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO,
+ "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"},
+ {STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"},
+ {STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO,
+ "STATUS_CALLBACK_RETURNED_TRANSACTION"},
+ {STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO,
+ "STATUS_CALLBACK_RETURNED_LDR_LOCK"},
+ {STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"},
+ {STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO,
+ "STATUS_CALLBACK_RETURNED_PRI_BACK"},
+ {STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO,
+ "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"},
+ {STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"},
+ {STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO,
+ "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"},
+ {STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"},
+ {STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"},
+ {STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"},
+ {STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"},
+ {STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"},
+ {STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"},
+ {STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"},
+ {STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"},
+ {STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"},
+ {STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"},
+ {STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"},
+ {STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"},
+ {STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"},
+ {STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"},
+ {STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"},
+ {STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"},
+ {STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"},
+ {STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"},
+ {STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP,
+ "STATUS_PROTOCOL_NOT_SUPPORTED"},
+ {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO,
+ "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"},
+ {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO,
+ "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"},
+ {STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO,
+ "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"},
+ {STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"},
+ {STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"},
+ {STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"},
+ {STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"},
+ {DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"},
+ {DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"},
+ {RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"},
+ {RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"},
+ {RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"},
+ {RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP,
+ "RPC_NT_PROTSEQ_NOT_SUPPORTED"},
+ {RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"},
+ {RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"},
+ {RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO,
+ "RPC_NT_INVALID_ENDPOINT_FORMAT"},
+ {RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"},
+ {RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"},
+ {RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"},
+ {RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"},
+ {RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"},
+ {RPC_NT_TYPE_ALREADY_REGISTERED, -EIO,
+ "RPC_NT_TYPE_ALREADY_REGISTERED"},
+ {RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"},
+ {RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"},
+ {RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"},
+ {RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"},
+ {RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"},
+ {RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"},
+ {RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"},
+ {RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"},
+ {RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"},
+ {RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"},
+ {RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"},
+ {RPC_NT_INVALID_NETWORK_OPTIONS, -EIO,
+ "RPC_NT_INVALID_NETWORK_OPTIONS"},
+ {RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"},
+ {RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"},
+ {RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"},
+ {RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"},
+ {RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"},
+ {RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"},
+ {RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"},
+ {RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"},
+ {RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"},
+ {RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"},
+ {RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO,
+ "RPC_NT_UNSUPPORTED_NAME_SYNTAX"},
+ {RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"},
+ {RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"},
+ {RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"},
+ {RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"},
+ {RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"},
+ {RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"},
+ {RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"},
+ {RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"},
+ {RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"},
+ {RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"},
+ {RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"},
+ {RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"},
+ {EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"},
+ {EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"},
+ {EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"},
+ {RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"},
+ {RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"},
+ {RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"},
+ {RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"},
+ {RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO,
+ "RPC_NT_NOT_ALL_OBJS_UNEXPORTED"},
+ {RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"},
+ {RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"},
+ {RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"},
+ {RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO,
+ "RPC_NT_NAME_SERVICE_UNAVAILABLE"},
+ {RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"},
+ {RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"},
+ {RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"},
+ {RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"},
+ {RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"},
+ {RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"},
+ {RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"},
+ {RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"},
+ {RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"},
+ {RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"},
+ {RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"},
+ {RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"},
+ {EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"},
+ {RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"},
+ {RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"},
+ {RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"},
+ {RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"},
+ {RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"},
+ {RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO,
+ "RPC_NT_UNSUPPORTED_AUTHN_LEVEL"},
+ {RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"},
+ {RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"},
+ {RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"},
+ {RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"},
+ {RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"},
+ {RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"},
+ {RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"},
+ {RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"},
+ {RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO,
+ "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"},
+ {RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO,
+ "RPC_NT_SS_CHAR_TRANS_SHORT_FILE"},
+ {RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"},
+ {RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"},
+ {RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"},
+ {RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"},
+ {RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO,
+ "RPC_NT_SS_CANNOT_GET_CALL_HANDLE"},
+ {RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"},
+ {RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO,
+ "RPC_NT_ENUM_VALUE_OUT_OF_RANGE"},
+ {RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"},
+ {RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"},
+ {RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"},
+ {RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"},
+ {RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"},
+ {RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"},
+ {RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"},
+ {RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"},
+ {RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"},
+ {RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"},
+ {RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"},
+ {STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"},
+ {STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"},
+ {STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO,
+ "STATUS_PNP_IRQ_TRANSLATION_FAILED"},
+ {STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"},
+ {STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"},
+ {STATUS_CTX_WINSTATION_NAME_INVALID, -EIO,
+ "STATUS_CTX_WINSTATION_NAME_INVALID"},
+ {STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"},
+ {STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"},
+ {STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"},
+ {STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"},
+ {STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO,
+ "STATUS_CTX_MODEM_INF_NOT_FOUND"},
+ {STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"},
+ {STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"},
+ {STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT,
+ "STATUS_CTX_MODEM_RESPONSE_TIMEOUT"},
+ {STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO,
+ "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"},
+ {STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO,
+ "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"},
+ {STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY,
+ "STATUS_CTX_MODEM_RESPONSE_BUSY"},
+ {STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO,
+ "STATUS_CTX_MODEM_RESPONSE_VOICE"},
+ {STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"},
+ {STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO,
+ "STATUS_CTX_LICENSE_CLIENT_INVALID"},
+ {STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO,
+ "STATUS_CTX_LICENSE_NOT_AVAILABLE"},
+ {STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"},
+ {STATUS_CTX_WINSTATION_NOT_FOUND, -EIO,
+ "STATUS_CTX_WINSTATION_NOT_FOUND"},
+ {STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO,
+ "STATUS_CTX_WINSTATION_NAME_COLLISION"},
+ {STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"},
+ {STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"},
+ {STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"},
+ {STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"},
+ {STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO,
+ "STATUS_CTX_CLIENT_QUERY_TIMEOUT"},
+ {STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"},
+ {STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"},
+ {STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"},
+ {STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES,
+ "STATUS_CTX_WINSTATION_ACCESS_DENIED"},
+ {STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"},
+ {STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"},
+ {STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"},
+ {STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"},
+ {STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"},
+ {STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO,
+ "STATUS_CTX_CLIENT_LICENSE_NOT_SET"},
+ {STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO,
+ "STATUS_CTX_CLIENT_LICENSE_IN_USE"},
+ {STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO,
+ "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"},
+ {STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"},
+ {STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"},
+ {STATUS_CTX_SECURITY_LAYER_ERROR, -EIO,
+ "STATUS_CTX_SECURITY_LAYER_ERROR"},
+ {STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO,
+ "STATUS_TS_INCOMPATIBLE_SESSIONS"},
+ {STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"},
+ {STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"},
+ {STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"},
+ {STATUS_MUI_INVALID_LOCALE_NAME, -EIO,
+ "STATUS_MUI_INVALID_LOCALE_NAME"},
+ {STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO,
+ "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"},
+ {STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"},
+ {STATUS_RESOURCE_ENUM_USER_STOP, -EIO,
+ "STATUS_RESOURCE_ENUM_USER_STOP"},
+ {STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"},
+ {STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"},
+ {STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO,
+ "STATUS_CLUSTER_JOIN_IN_PROGRESS"},
+ {STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"},
+ {STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO,
+ "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"},
+ {STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"},
+ {STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO,
+ "STATUS_CLUSTER_NETWORK_NOT_FOUND"},
+ {STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO,
+ "STATUS_CLUSTER_NETINTERFACE_EXISTS"},
+ {STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO,
+ "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"},
+ {STATUS_CLUSTER_INVALID_REQUEST, -EIO,
+ "STATUS_CLUSTER_INVALID_REQUEST"},
+ {STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO,
+ "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"},
+ {STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"},
+ {STATUS_CLUSTER_NODE_UNREACHABLE, -EIO,
+ "STATUS_CLUSTER_NODE_UNREACHABLE"},
+ {STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO,
+ "STATUS_CLUSTER_NODE_NOT_MEMBER"},
+ {STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO,
+ "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"},
+ {STATUS_CLUSTER_INVALID_NETWORK, -EIO,
+ "STATUS_CLUSTER_INVALID_NETWORK"},
+ {STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO,
+ "STATUS_CLUSTER_NO_NET_ADAPTERS"},
+ {STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"},
+ {STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"},
+ {STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO,
+ "STATUS_CLUSTER_NODE_NOT_PAUSED"},
+ {STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO,
+ "STATUS_CLUSTER_NO_SECURITY_CONTEXT"},
+ {STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO,
+ "STATUS_CLUSTER_NETWORK_NOT_INTERNAL"},
+ {STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"},
+ {STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"},
+ {STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"},
+ {STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"},
+ {STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"},
+ {STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"},
+ {STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"},
+ {STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"},
+ {STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"},
+ {STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"},
+ {STATUS_ACPI_INVALID_TARGETTYPE, -EIO,
+ "STATUS_ACPI_INVALID_TARGETTYPE"},
+ {STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO,
+ "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"},
+ {STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO,
+ "STATUS_ACPI_ADDRESS_NOT_MAPPED"},
+ {STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"},
+ {STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"},
+ {STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"},
+ {STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"},
+ {STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO,
+ "STATUS_ACPI_INVALID_ACCESS_SIZE"},
+ {STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO,
+ "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"},
+ {STATUS_ACPI_ALREADY_INITIALIZED, -EIO,
+ "STATUS_ACPI_ALREADY_INITIALIZED"},
+ {STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"},
+ {STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO,
+ "STATUS_ACPI_INVALID_MUTEX_LEVEL"},
+ {STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"},
+ {STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"},
+ {STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"},
+ {STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"},
+ {STATUS_ACPI_REG_HANDLER_FAILED, -EIO,
+ "STATUS_ACPI_REG_HANDLER_FAILED"},
+ {STATUS_ACPI_POWER_REQUEST_FAILED, -EIO,
+ "STATUS_ACPI_POWER_REQUEST_FAILED"},
+ {STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"},
+ {STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"},
+ {STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO,
+ "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"},
+ {STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"},
+ {STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO,
+ "STATUS_SXS_MANIFEST_FORMAT_ERROR"},
+ {STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO,
+ "STATUS_SXS_MANIFEST_PARSE_ERROR"},
+ {STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO,
+ "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"},
+ {STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"},
+ {STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"},
+ {STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"},
+ {STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO,
+ "STATUS_SXS_THREAD_QUERIES_DISABLED"},
+ {STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"},
+ {STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO,
+ "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"},
+ {STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"},
+ {STATUS_SXS_INVALID_DEACTIVATION, -EIO,
+ "STATUS_SXS_INVALID_DEACTIVATION"},
+ {STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO,
+ "STATUS_SXS_MULTIPLE_DEACTIVATION"},
+ {STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO,
+ "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"},
+ {STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO,
+ "STATUS_SXS_PROCESS_TERMINATION_REQUESTED"},
+ {STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO,
+ "STATUS_SXS_CORRUPT_ACTIVATION_STACK"},
+ {STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"},
+ {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO,
+ "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"},
+ {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO,
+ "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"},
+ {STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO,
+ "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"},
+ {STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO,
+ "STATUS_SXS_IDENTITY_PARSE_ERROR"},
+ {STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO,
+ "STATUS_SXS_COMPONENT_STORE_CORRUPT"},
+ {STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"},
+ {STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO,
+ "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"},
+ {STATUS_SXS_IDENTITIES_DIFFERENT, -EIO,
+ "STATUS_SXS_IDENTITIES_DIFFERENT"},
+ {STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO,
+ "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"},
+ {STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO,
+ "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"},
+ {STATUS_ADVANCED_INSTALLER_FAILED, -EIO,
+ "STATUS_ADVANCED_INSTALLER_FAILED"},
+ {STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"},
+ {STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"},
+ {STATUS_SXS_SETTING_NOT_REGISTERED, -EIO,
+ "STATUS_SXS_SETTING_NOT_REGISTERED"},
+ {STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO,
+ "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"},
+ {STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO,
+ "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"},
+ {STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"},
+ {STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"},
+ {STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"},
+ {STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"},
+ {STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"},
+ {STATUS_TM_INITIALIZATION_FAILED, -EIO,
+ "STATUS_TM_INITIALIZATION_FAILED"},
+ {STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"},
+ {STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"},
+ {STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"},
+ {STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"},
+ {STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO,
+ "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"},
+ {STATUS_LOG_RESIZE_INVALID_SIZE, -EIO,
+ "STATUS_LOG_RESIZE_INVALID_SIZE"},
+ {STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO,
+ "STATUS_REMOTE_FILE_VERSION_MISMATCH"},
+ {STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO,
+ "STATUS_CRM_PROTOCOL_ALREADY_EXISTS"},
+ {STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO,
+ "STATUS_TRANSACTION_PROPAGATION_FAILED"},
+ {STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"},
+ {STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO,
+ "STATUS_TRANSACTION_SUPERIOR_EXISTS"},
+ {STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO,
+ "STATUS_TRANSACTION_REQUEST_NOT_VALID"},
+ {STATUS_TRANSACTION_NOT_REQUESTED, -EIO,
+ "STATUS_TRANSACTION_NOT_REQUESTED"},
+ {STATUS_TRANSACTION_ALREADY_ABORTED, -EIO,
+ "STATUS_TRANSACTION_ALREADY_ABORTED"},
+ {STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO,
+ "STATUS_TRANSACTION_ALREADY_COMMITTED"},
+ {STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO,
+ "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"},
+ {STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO,
+ "STATUS_CURRENT_TRANSACTION_NOT_VALID"},
+ {STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"},
+ {STATUS_OBJECT_NO_LONGER_EXISTS, -EIO,
+ "STATUS_OBJECT_NO_LONGER_EXISTS"},
+ {STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO,
+ "STATUS_STREAM_MINIVERSION_NOT_FOUND"},
+ {STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO,
+ "STATUS_STREAM_MINIVERSION_NOT_VALID"},
+ {STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO,
+ "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"},
+ {STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO,
+ "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"},
+ {STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO,
+ "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"},
+ {STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"},
+ {STATUS_LOG_CORRUPTION_DETECTED, -EIO,
+ "STATUS_LOG_CORRUPTION_DETECTED"},
+ {STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"},
+ {STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO,
+ "STATUS_ENLISTMENT_NOT_SUPERIOR"},
+ {STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO,
+ "STATUS_FILE_IDENTITY_NOT_PERSISTENT"},
+ {STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO,
+ "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"},
+ {STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"},
+ {STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"},
+ {STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO,
+ "STATUS_INDOUBT_TRANSACTIONS_EXIST"},
+ {STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"},
+ {STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"},
+ {STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"},
+ {STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO,
+ "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"},
+ {STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO,
+ "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"},
+ {STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO,
+ "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"},
+ {STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO,
+ "STATUS_TRANSACTION_REQUIRED_PROMOTION"},
+ {STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO,
+ "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"},
+ {STATUS_TRANSACTIONS_NOT_FROZEN, -EIO,
+ "STATUS_TRANSACTIONS_NOT_FROZEN"},
+ {STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO,
+ "STATUS_TRANSACTION_FREEZE_IN_PROGRESS"},
+ {STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"},
+ {STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO,
+ "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"},
+ {STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO,
+ "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"},
+ {STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"},
+ {STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"},
+ {STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO,
+ "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"},
+ {STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO,
+ "STATUS_CANNOT_ABORT_TRANSACTIONS"},
+ {STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"},
+ {STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO,
+ "STATUS_RESOURCEMANAGER_NOT_FOUND"},
+ {STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"},
+ {STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO,
+ "STATUS_TRANSACTIONMANAGER_NOT_FOUND"},
+ {STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO,
+ "STATUS_TRANSACTIONMANAGER_NOT_ONLINE"},
+ {STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO,
+ "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"},
+ {STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"},
+ {STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO,
+ "STATUS_TRANSACTION_OBJECT_EXPIRED"},
+ {STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO,
+ "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"},
+ {STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO,
+ "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"},
+ {STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO,
+ "STATUS_TRANSACTION_RECORD_TOO_LONG"},
+ {STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO,
+ "STATUS_NO_LINK_TRACKING_IN_TRANSACTION"},
+ {STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP,
+ "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"},
+ {STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO,
+ "STATUS_TRANSACTION_INTEGRITY_VIOLATED"},
+ {STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"},
+ {STATUS_LOG_SECTOR_PARITY_INVALID, -EIO,
+ "STATUS_LOG_SECTOR_PARITY_INVALID"},
+ {STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"},
+ {STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"},
+ {STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"},
+ {STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"},
+ {STATUS_LOG_READ_CONTEXT_INVALID, -EIO,
+ "STATUS_LOG_READ_CONTEXT_INVALID"},
+ {STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"},
+ {STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"},
+ {STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"},
+ {STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"},
+ {STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"},
+ {STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"},
+ {STATUS_LOG_METADATA_INCONSISTENT, -EIO,
+ "STATUS_LOG_METADATA_INCONSISTENT"},
+ {STATUS_LOG_RESERVATION_INVALID, -EIO,
+ "STATUS_LOG_RESERVATION_INVALID"},
+ {STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"},
+ {STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO,
+ "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"},
+ {STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"},
+ {STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO,
+ "STATUS_LOG_POLICY_ALREADY_INSTALLED"},
+ {STATUS_LOG_POLICY_NOT_INSTALLED, -EIO,
+ "STATUS_LOG_POLICY_NOT_INSTALLED"},
+ {STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"},
+ {STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"},
+ {STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO,
+ "STATUS_LOG_PINNED_ARCHIVE_TAIL"},
+ {STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"},
+ {STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO,
+ "STATUS_LOG_RECORDS_RESERVED_INVALID"},
+ {STATUS_LOG_SPACE_RESERVED_INVALID, -EIO,
+ "STATUS_LOG_SPACE_RESERVED_INVALID"},
+ {STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"},
+ {STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"},
+ {STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"},
+ {STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"},
+ {STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO,
+ "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"},
+ {STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO,
+ "STATUS_LOG_ARCHIVE_IN_PROGRESS"},
+ {STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"},
+ {STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO,
+ "STATUS_LOG_NOT_ENOUGH_CONTAINERS"},
+ {STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO,
+ "STATUS_LOG_CLIENT_ALREADY_REGISTERED"},
+ {STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO,
+ "STATUS_LOG_CLIENT_NOT_REGISTERED"},
+ {STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO,
+ "STATUS_LOG_FULL_HANDLER_IN_PROGRESS"},
+ {STATUS_LOG_CONTAINER_READ_FAILED, -EIO,
+ "STATUS_LOG_CONTAINER_READ_FAILED"},
+ {STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO,
+ "STATUS_LOG_CONTAINER_WRITE_FAILED"},
+ {STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO,
+ "STATUS_LOG_CONTAINER_OPEN_FAILED"},
+ {STATUS_LOG_CONTAINER_STATE_INVALID, -EIO,
+ "STATUS_LOG_CONTAINER_STATE_INVALID"},
+ {STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"},
+ {STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"},
+ {STATUS_LOG_METADATA_FLUSH_FAILED, -EIO,
+ "STATUS_LOG_METADATA_FLUSH_FAILED"},
+ {STATUS_LOG_INCONSISTENT_SECURITY, -EIO,
+ "STATUS_LOG_INCONSISTENT_SECURITY"},
+ {STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO,
+ "STATUS_LOG_APPENDED_FLUSH_FAILED"},
+ {STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"},
+ {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO,
+ "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"},
+ {STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"},
+ {STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO,
+ "STATUS_FLT_CONTEXT_ALREADY_DEFINED"},
+ {STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO,
+ "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"},
+ {STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"},
+ {STATUS_FLT_INVALID_NAME_REQUEST, -EIO,
+ "STATUS_FLT_INVALID_NAME_REQUEST"},
+ {STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO,
+ "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"},
+ {STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"},
+ {STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"},
+ {STATUS_FLT_POST_OPERATION_CLEANUP, -EIO,
+ "STATUS_FLT_POST_OPERATION_CLEANUP"},
+ {STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"},
+ {STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"},
+ {STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO,
+ "STATUS_FLT_MUST_BE_NONPAGED_POOL"},
+ {STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"},
+ {STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"},
+ {STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"},
+ {STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"},
+ {STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO,
+ "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"},
+ {STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO,
+ "STATUS_FLT_INSTANCE_NAME_COLLISION"},
+ {STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"},
+ {STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"},
+ {STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"},
+ {STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO,
+ "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"},
+ {STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO,
+ "STATUS_FLT_INVALID_CONTEXT_REGISTRATION"},
+ {STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"},
+ {STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"},
+ {STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO,
+ "STATUS_FLT_VOLUME_ALREADY_MOUNTED"},
+ {STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"},
+ {STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO,
+ "STATUS_FLT_CONTEXT_ALREADY_LINKED"},
+ {STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO,
+ "STATUS_FLT_NO_WAITER_FOR_REPLY"},
+ {STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"},
+ {STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO,
+ "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"},
+ {STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO,
+ "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"},
+ {STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO,
+ "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"},
+ {STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO,
+ "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"},
+ {STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO,
+ "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"},
+ {STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO,
+ "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"},
+ {STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO,
+ "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"},
+ {STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO,
+ "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"},
+ {STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO,
+ "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"},
+ {STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO,
+ "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"},
+ {STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO,
+ "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"},
+ {STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO,
+ "STATUS_GRAPHICS_ADAPTER_WAS_RESET"},
+ {STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO,
+ "STATUS_GRAPHICS_INVALID_DRIVER_MODEL"},
+ {STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO,
+ "STATUS_GRAPHICS_PRESENT_MODE_CHANGED"},
+ {STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO,
+ "STATUS_GRAPHICS_PRESENT_OCCLUDED"},
+ {STATUS_GRAPHICS_PRESENT_DENIED, -EIO,
+ "STATUS_GRAPHICS_PRESENT_DENIED"},
+ {STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO,
+ "STATUS_GRAPHICS_CANNOTCOLORCONVERT"},
+ {STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO,
+ "STATUS_GRAPHICS_NO_VIDEO_MEMORY"},
+ {STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO,
+ "STATUS_GRAPHICS_CANT_LOCK_MEMORY"},
+ {STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY,
+ "STATUS_GRAPHICS_ALLOCATION_BUSY"},
+ {STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO,
+ "STATUS_GRAPHICS_TOO_MANY_REFERENCES"},
+ {STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO,
+ "STATUS_GRAPHICS_TRY_AGAIN_LATER"},
+ {STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"},
+ {STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO,
+ "STATUS_GRAPHICS_ALLOCATION_INVALID"},
+ {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO,
+ "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"},
+ {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO,
+ "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"},
+ {STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO,
+ "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"},
+ {STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO,
+ "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"},
+ {STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO,
+ "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"},
+ {STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO,
+ "STATUS_GRAPHICS_ALLOCATION_CLOSED"},
+ {STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO,
+ "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"},
+ {STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO,
+ "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"},
+ {STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO,
+ "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"},
+ {STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO,
+ "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"},
+ {STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO,
+ "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"},
+ {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"},
+ {STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"},
+ {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"},
+ {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"},
+ {STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"},
+ {STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"},
+ {STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO,
+ "STATUS_GRAPHICS_INVALID_FREQUENCY"},
+ {STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO,
+ "STATUS_GRAPHICS_INVALID_ACTIVE_REGION"},
+ {STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO,
+ "STATUS_GRAPHICS_INVALID_TOTAL_REGION"},
+ {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"},
+ {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"},
+ {STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO,
+ "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"},
+ {STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"},
+ {STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO,
+ "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"},
+ {STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"},
+ {STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"},
+ {STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO,
+ "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"},
+ {STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO,
+ "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"},
+ {STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"},
+ {STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"},
+ {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"},
+ {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"},
+ {STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO,
+ "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"},
+ {STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO,
+ "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"},
+ {STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"},
+ {STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"},
+ {STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"},
+ {STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO,
+ "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"},
+ {STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO,
+ "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"},
+ {STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO,
+ "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"},
+ {STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO,
+ "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"},
+ {STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"},
+ {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO,
+ "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"},
+ {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO,
+ "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"},
+ {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"},
+ {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"},
+ {STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO,
+ "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"},
+ {STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO,
+ "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"},
+ {STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO,
+ "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"},
+ {STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"},
+ {STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO,
+ "STATUS_GRAPHICS_RESOURCES_NOT_RELATED"},
+ {STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO,
+ "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"},
+ {STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO,
+ "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"},
+ {STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO,
+ "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"},
+ {STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO,
+ "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"},
+ {STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"},
+ {STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO,
+ "STATUS_GRAPHICS_NO_ACTIVE_VIDPN"},
+ {STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"},
+ {STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO,
+ "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"},
+ {STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"},
+ {STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO,
+ "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"},
+ {STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO,
+ "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"},
+ {STATUS_GRAPHICS_INVALID_STRIDE, -EIO,
+ "STATUS_GRAPHICS_INVALID_STRIDE"},
+ {STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO,
+ "STATUS_GRAPHICS_INVALID_PIXELFORMAT"},
+ {STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO,
+ "STATUS_GRAPHICS_INVALID_COLORBASIS"},
+ {STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO,
+ "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"},
+ {STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO,
+ "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"},
+ {STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO,
+ "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"},
+ {STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO,
+ "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"},
+ {STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO,
+ "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"},
+ {STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO,
+ "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"},
+ {STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO,
+ "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"},
+ {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED,
+ -EIO,
+ "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO,
+ "STATUS_GRAPHICS_INVALID_GAMMA_RAMP"},
+ {STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO,
+ "STATUS_GRAPHICS_MODE_NOT_IN_MODESET"},
+ {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO,
+ "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"},
+ {STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO,
+ "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"},
+ {STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO,
+ "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"},
+ {STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO,
+ "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"},
+ {STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO,
+ "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"},
+ {STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO,
+ "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"},
+ {STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO,
+ "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"},
+ {STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO,
+ "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"},
+ {STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO,
+ "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"},
+ {STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"},
+ {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO,
+ "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"},
+ {STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO,
+ "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"},
+ {STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO,
+ "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"},
+ {STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO,
+ "STATUS_GRAPHICS_INVALID_CLIENT_TYPE"},
+ {STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO,
+ "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"},
+ {STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO,
+ "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"},
+ {STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO,
+ "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"},
+ {STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO,
+ "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"},
+ {STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO,
+ "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"},
+ {STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO,
+ "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"},
+ {STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO,
+ "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"},
+ {STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO,
+ "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"},
+ {STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO,
+ "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"},
+ {STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO,
+ "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"},
+ {STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO,
+ "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"},
+ {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS,
+ -EIO,
+ "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"},
+ {STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO,
+ "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"},
+ {STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO,
+ "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"},
+ {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO,
+ "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"},
+ {STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO,
+ "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"},
+ {STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_OPM_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_COPP_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_UAB_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO,
+ "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"},
+ {STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO,
+ "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"},
+ {STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO,
+ "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"},
+ {STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO,
+ "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"},
+ {STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO,
+ "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"},
+ {STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO,
+ "STATUS_GRAPHICS_OPM_INVALID_POINTER"},
+ {STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO,
+ "STATUS_GRAPHICS_OPM_INTERNAL_ERROR"},
+ {STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO,
+ "STATUS_GRAPHICS_OPM_INVALID_HANDLE"},
+ {STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO,
+ "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"},
+ {STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO,
+ "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"},
+ {STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO,
+ "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"},
+ {STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO,
+ "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"},
+ {STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO,
+ "STATUS_GRAPHICS_PVP_HFS_FAILED"},
+ {STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO,
+ "STATUS_GRAPHICS_OPM_INVALID_SRM"},
+ {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO,
+ "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"},
+ {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO,
+ "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"},
+ {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO,
+ "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"},
+ {STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO,
+ "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"},
+ {STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO,
+ "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"},
+ {STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO,
+ "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"},
+ {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO,
+ "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"},
+ {STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO,
+ "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"},
+ {STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_I2C_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO,
+ "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"},
+ {STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO,
+ "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"},
+ {STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO,
+ "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"},
+ {STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO,
+ "STATUS_GRAPHICS_DDCCI_INVALID_DATA"},
+ {STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE,
+ -EIO,
+ "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"},
+ {STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO,
+ "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"},
+ {STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO,
+ "STATUS_GRAPHICS_MCA_INTERNAL_ERROR"},
+ {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO,
+ "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"},
+ {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO,
+ "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"},
+ {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO,
+ "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"},
+ {STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO,
+ "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"},
+ {STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO,
+ "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"},
+ {STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"},
+ {STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO,
+ "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"},
+ {STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO,
+ "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"},
+ {STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO,
+ "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"},
+ {STATUS_GRAPHICS_INVALID_POINTER, -EIO,
+ "STATUS_GRAPHICS_INVALID_POINTER"},
+ {STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO,
+ "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"},
+ {STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO,
+ "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"},
+ {STATUS_GRAPHICS_INTERNAL_ERROR, -EIO,
+ "STATUS_GRAPHICS_INTERNAL_ERROR"},
+ {STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO,
+ "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"},
+ {STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"},
+ {STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"},
+ {STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"},
+ {STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"},
+ {STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"},
+ {STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"},
+ {STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"},
+ {STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"},
+ {STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"},
+ {STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"},
+ {STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"},
+ {STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"},
+ {STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"},
+ {STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"},
+ {STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"},
+ {STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"},
+ {STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"},
+ {STATUS_FVE_FAILED_AUTHENTICATION, -EIO,
+ "STATUS_FVE_FAILED_AUTHENTICATION"},
+ {STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"},
+ {STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"},
+ {STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"},
+ {STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"},
+ {STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"},
+ {STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO,
+ "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"},
+ {STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"},
+ {STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"},
+ {STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"},
+ {STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO,
+ "STATUS_FVE_AUTH_INVALID_APPLICATION"},
+ {STATUS_FVE_AUTH_INVALID_CONFIG, -EIO,
+ "STATUS_FVE_AUTH_INVALID_CONFIG"},
+ {STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"},
+ {STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"},
+ {STATUS_FVE_BAD_METADATA_POINTER, -EIO,
+ "STATUS_FVE_BAD_METADATA_POINTER"},
+ {STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"},
+ {STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"},
+ {STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"},
+ {STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"},
+ {STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"},
+ {STATUS_FWP_CONDITION_NOT_FOUND, -EIO,
+ "STATUS_FWP_CONDITION_NOT_FOUND"},
+ {STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"},
+ {STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"},
+ {STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"},
+ {STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO,
+ "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"},
+ {STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"},
+ {STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"},
+ {STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"},
+ {STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"},
+ {STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO,
+ "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"},
+ {STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"},
+ {STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"},
+ {STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"},
+ {STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"},
+ {STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"},
+ {STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"},
+ {STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"},
+ {STATUS_FWP_NET_EVENTS_DISABLED, -EIO,
+ "STATUS_FWP_NET_EVENTS_DISABLED"},
+ {STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"},
+ {STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"},
+ {STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"},
+ {STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"},
+ {STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO,
+ "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"},
+ {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"},
+ {STATUS_FWP_NOTIFICATION_DROPPED, -EIO,
+ "STATUS_FWP_NOTIFICATION_DROPPED"},
+ {STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"},
+ {STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO,
+ "STATUS_FWP_INCOMPATIBLE_SA_STATE"},
+ {STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"},
+ {STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"},
+ {STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"},
+ {STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"},
+ {STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"},
+ {STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"},
+ {STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"},
+ {STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"},
+ {STATUS_FWP_INVALID_ACTION_TYPE, -EIO,
+ "STATUS_FWP_INVALID_ACTION_TYPE"},
+ {STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"},
+ {STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO,
+ "STATUS_FWP_MATCH_TYPE_MISMATCH"},
+ {STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"},
+ {STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"},
+ {STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"},
+ {STATUS_FWP_DUPLICATE_CONDITION, -EIO,
+ "STATUS_FWP_DUPLICATE_CONDITION"},
+ {STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"},
+ {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO,
+ "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"},
+ {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO,
+ "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"},
+ {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO,
+ "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"},
+ {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO,
+ "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"},
+ {STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO,
+ "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"},
+ {STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO,
+ "STATUS_FWP_INCOMPATIBLE_DH_GROUP"},
+ {STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP,
+ "STATUS_FWP_EM_NOT_SUPPORTED"},
+ {STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"},
+ {STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO,
+ "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"},
+ {STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"},
+ {STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"},
+ {STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO,
+ "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"},
+ {STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO,
+ "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"},
+ {STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO,
+ "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"},
+ {STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"},
+ {STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO,
+ "STATUS_FWP_INJECT_HANDLE_CLOSING"},
+ {STATUS_FWP_INJECT_HANDLE_STALE, -EIO,
+ "STATUS_FWP_INJECT_HANDLE_STALE"},
+ {STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"},
+ {STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"},
+ {STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"},
+ {STATUS_NDIS_BAD_CHARACTERISTICS, -EIO,
+ "STATUS_NDIS_BAD_CHARACTERISTICS"},
+ {STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"},
+ {STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"},
+ {STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"},
+ {STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"},
+ {STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"},
+ {STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO,
+ "STATUS_NDIS_MULTICAST_NOT_FOUND"},
+ {STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"},
+ {STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"},
+ {STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"},
+ {STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO,
+ "STATUS_NDIS_INVALID_DEVICE_REQUEST"},
+ {STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"},
+ {STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"},
+ {STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"},
+ {STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS,
+ "STATUS_NDIS_BUFFER_TOO_SHORT"},
+ {STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"},
+ {STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"},
+ {STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"},
+ {STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO,
+ "STATUS_NDIS_GROUP_ADDRESS_IN_USE"},
+ {STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"},
+ {STATUS_NDIS_ERROR_READING_FILE, -EIO,
+ "STATUS_NDIS_ERROR_READING_FILE"},
+ {STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"},
+ {STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"},
+ {STATUS_NDIS_MEDIA_DISCONNECTED, -EIO,
+ "STATUS_NDIS_MEDIA_DISCONNECTED"},
+ {STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"},
+ {STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"},
+ {STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO,
+ "STATUS_NDIS_INTERFACE_NOT_FOUND"},
+ {STATUS_NDIS_UNSUPPORTED_REVISION, -EIO,
+ "STATUS_NDIS_UNSUPPORTED_REVISION"},
+ {STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"},
+ {STATUS_NDIS_INVALID_PORT_STATE, -EIO,
+ "STATUS_NDIS_INVALID_PORT_STATE"},
+ {STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"},
+ {STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"},
+ {STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO,
+ "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"},
+ {STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO,
+ "STATUS_NDIS_DOT11_MEDIA_IN_USE"},
+ {STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO,
+ "STATUS_NDIS_DOT11_POWER_STATE_INVALID"},
+ {STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"},
+ {STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO,
+ "STATUS_IPSEC_SA_LIFETIME_EXPIRED"},
+ {STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"},
+ {STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO,
+ "STATUS_IPSEC_REPLAY_CHECK_FAILED"},
+ {STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"},
+ {STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO,
+ "STATUS_IPSEC_INTEGRITY_CHECK_FAILED"},
+ {STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"},
+ {0, 0, NULL}
+};
+
+/*****************************************************************************
+ Print an error message from the status code
+ *****************************************************************************/
+static void
+smb2_print_status(__le32 status)
+{
+ int idx = 0;
+
+ while (smb2_error_map_table[idx].status_string != NULL) {
+ if ((smb2_error_map_table[idx].smb2_status) == status) {
+ pr_notice("Status code returned 0x%08x %s\n", status,
+ smb2_error_map_table[idx].status_string);
+ }
+ idx++;
+ }
+ return;
+}
+
+int
+map_smb2_to_linux_error(char *buf, bool log_err)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+ unsigned int i;
+ int rc = -EIO;
+ __le32 smb2err = shdr->Status;
+
+ if (smb2err == 0) {
+ trace_smb3_cmd_done(shdr->TreeId, shdr->SessionId,
+ le16_to_cpu(shdr->Command), le64_to_cpu(shdr->MessageId));
+ return 0;
+ }
+
+ /* mask facility */
+ if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
+ (smb2err != STATUS_END_OF_FILE))
+ smb2_print_status(smb2err);
+ else if (cifsFYI & CIFS_RC)
+ smb2_print_status(smb2err);
+
+ for (i = 0; i < sizeof(smb2_error_map_table) /
+ sizeof(struct status_to_posix_error); i++) {
+ if (smb2_error_map_table[i].smb2_status == smb2err) {
+ rc = smb2_error_map_table[i].posix_error;
+ break;
+ }
+ }
+
+ /* on error mapping not found - return EIO */
+
+ cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n",
+ __le32_to_cpu(smb2err), rc);
+
+ trace_smb3_cmd_err(shdr->TreeId, shdr->SessionId,
+ le16_to_cpu(shdr->Command),
+ le64_to_cpu(shdr->MessageId), le32_to_cpu(smb2err), rc);
+ return rc;
+}
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
new file mode 100644
index 000000000..7177720e8
--- /dev/null
+++ b/fs/cifs/smb2misc.c
@@ -0,0 +1,882 @@
+/*
+ * fs/cifs/smb2misc.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2011
+ * Etersoft, 2012
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/ctype.h>
+#include "smb2pdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "smb2proto.h"
+#include "cifs_debug.h"
+#include "cifs_unicode.h"
+#include "smb2status.h"
+#include "smb2glob.h"
+
+static int
+check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
+{
+ __u64 wire_mid = le64_to_cpu(shdr->MessageId);
+
+ /*
+ * Make sure that this really is an SMB, that it is a response,
+ * and that the message ids match.
+ */
+ if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) &&
+ (mid == wire_mid)) {
+ if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
+ return 0;
+ else {
+ /* only one valid case where server sends us request */
+ if (shdr->Command == SMB2_OPLOCK_BREAK)
+ return 0;
+ else
+ cifs_dbg(VFS, "Received Request not response\n");
+ }
+ } else { /* bad signature or mid */
+ if (shdr->ProtocolId != SMB2_PROTO_NUMBER)
+ cifs_dbg(VFS, "Bad protocol string signature header %x\n",
+ le32_to_cpu(shdr->ProtocolId));
+ if (mid != wire_mid)
+ cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
+ mid, wire_mid);
+ }
+ cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid);
+ return 1;
+}
+
+/*
+ * The following table defines the expected "StructureSize" of SMB2 responses
+ * in order by SMB2 command. This is similar to "wct" in SMB/CIFS responses.
+ *
+ * Note that commands are defined in smb2pdu.h in le16 but the array below is
+ * indexed by command in host byte order
+ */
+static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
+ /* SMB2_NEGOTIATE */ cpu_to_le16(65),
+ /* SMB2_SESSION_SETUP */ cpu_to_le16(9),
+ /* SMB2_LOGOFF */ cpu_to_le16(4),
+ /* SMB2_TREE_CONNECT */ cpu_to_le16(16),
+ /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4),
+ /* SMB2_CREATE */ cpu_to_le16(89),
+ /* SMB2_CLOSE */ cpu_to_le16(60),
+ /* SMB2_FLUSH */ cpu_to_le16(4),
+ /* SMB2_READ */ cpu_to_le16(17),
+ /* SMB2_WRITE */ cpu_to_le16(17),
+ /* SMB2_LOCK */ cpu_to_le16(4),
+ /* SMB2_IOCTL */ cpu_to_le16(49),
+ /* BB CHECK this ... not listed in documentation */
+ /* SMB2_CANCEL */ cpu_to_le16(0),
+ /* SMB2_ECHO */ cpu_to_le16(4),
+ /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(9),
+ /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(9),
+ /* SMB2_QUERY_INFO */ cpu_to_le16(9),
+ /* SMB2_SET_INFO */ cpu_to_le16(2),
+ /* BB FIXME can also be 44 for lease break */
+ /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
+};
+
+static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len,
+ __u32 non_ctxlen)
+{
+ __u16 neg_count;
+ __u32 nc_offset, size_of_pad_before_neg_ctxts;
+ struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr;
+
+ /* Negotiate contexts are only valid for latest dialect SMB3.11 */
+ neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount);
+ if ((neg_count == 0) ||
+ (pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID)))
+ return 0;
+
+ /* Make sure that negotiate contexts start after gss security blob */
+ nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset);
+ if (nc_offset < non_ctxlen) {
+ printk_once(KERN_WARNING "invalid negotiate context offset\n");
+ return 0;
+ }
+ size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen;
+
+ /* Verify that at least minimal negotiate contexts fit within frame */
+ if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) {
+ printk_once(KERN_WARNING "negotiate context goes beyond end\n");
+ return 0;
+ }
+
+ cifs_dbg(FYI, "length of negcontexts %d pad %d\n",
+ len - nc_offset, size_of_pad_before_neg_ctxts);
+
+ /* length of negcontexts including pad from end of sec blob to them */
+ return (len - nc_offset) + size_of_pad_before_neg_ctxts;
+}
+
+int
+smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+ struct smb2_sync_pdu *pdu = (struct smb2_sync_pdu *)shdr;
+ __u64 mid;
+ __u32 clc_len; /* calculated length */
+ int command;
+ int pdu_size = sizeof(struct smb2_sync_pdu);
+ int hdr_size = sizeof(struct smb2_sync_hdr);
+
+ /*
+ * Add function to do table lookup of StructureSize by command
+ * ie Validate the wct via smb2_struct_sizes table above
+ */
+ if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
+ struct smb2_transform_hdr *thdr =
+ (struct smb2_transform_hdr *)buf;
+ struct cifs_ses *ses = NULL;
+ struct list_head *tmp;
+
+ /* decrypt frame now that it is completely read in */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &srvr->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+ if (ses->Suid == thdr->SessionId)
+ break;
+
+ ses = NULL;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ if (ses == NULL) {
+ cifs_dbg(VFS, "no decryption - session id not found\n");
+ return 1;
+ }
+ }
+
+ mid = le64_to_cpu(shdr->MessageId);
+ if (len < pdu_size) {
+ if ((len >= hdr_size)
+ && (shdr->Status != 0)) {
+ pdu->StructureSize2 = 0;
+ /*
+ * As with SMB/CIFS, on some error cases servers may
+ * not return wct properly
+ */
+ return 0;
+ } else {
+ cifs_dbg(VFS, "Length less than SMB header size\n");
+ }
+ return 1;
+ }
+ if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) {
+ cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n",
+ mid);
+ return 1;
+ }
+
+ if (check_smb2_hdr(shdr, mid))
+ return 1;
+
+ if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
+ cifs_dbg(VFS, "Illegal structure size %u\n",
+ le16_to_cpu(shdr->StructureSize));
+ return 1;
+ }
+
+ command = le16_to_cpu(shdr->Command);
+ if (command >= NUMBER_OF_SMB2_COMMANDS) {
+ cifs_dbg(VFS, "Illegal SMB2 command %d\n", command);
+ return 1;
+ }
+
+ if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
+ if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 ||
+ pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) {
+ /* error packets have 9 byte structure size */
+ cifs_dbg(VFS, "Illegal response size %u for command %d\n",
+ le16_to_cpu(pdu->StructureSize2), command);
+ return 1;
+ } else if (command == SMB2_OPLOCK_BREAK_HE
+ && (shdr->Status == 0)
+ && (le16_to_cpu(pdu->StructureSize2) != 44)
+ && (le16_to_cpu(pdu->StructureSize2) != 36)) {
+ /* special case for SMB2.1 lease break message */
+ cifs_dbg(VFS, "Illegal response size %d for oplock break\n",
+ le16_to_cpu(pdu->StructureSize2));
+ return 1;
+ }
+ }
+
+ clc_len = smb2_calc_size(buf, srvr);
+
+ if (shdr->Command == SMB2_NEGOTIATE)
+ clc_len += get_neg_ctxt_len(shdr, len, clc_len);
+
+ if (len != clc_len) {
+ cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
+ clc_len, len, mid);
+ /* create failed on symlink */
+ if (command == SMB2_CREATE_HE &&
+ shdr->Status == STATUS_STOPPED_ON_SYMLINK)
+ return 0;
+ /* Windows 7 server returns 24 bytes more */
+ if (clc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
+ return 0;
+ /* server can return one byte more due to implied bcc[0] */
+ if (clc_len == len + 1)
+ return 0;
+
+ /*
+ * Some windows servers (win2016) will pad also the final
+ * PDU in a compound to 8 bytes.
+ */
+ if (((clc_len + 7) & ~7) == len)
+ return 0;
+
+ /*
+ * MacOS server pads after SMB2.1 write response with 3 bytes
+ * of junk. Other servers match RFC1001 len to actual
+ * SMB2/SMB3 frame length (header + smb2 response specific data)
+ * Some windows servers also pad up to 8 bytes when compounding.
+ * If pad is longer than eight bytes, log the server behavior
+ * (once), since may indicate a problem but allow it and continue
+ * since the frame is parseable.
+ */
+ if (clc_len < len) {
+ pr_warn_once(
+ "srv rsp padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
+ len, clc_len, command, mid);
+ return 0;
+ }
+ pr_warn_once(
+ "srv rsp too short, len %d not %d. cmd:%d mid:%llu\n",
+ len, clc_len, command, mid);
+
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * The size of the variable area depends on the offset and length fields
+ * located in different fields for various SMB2 responses. SMB2 responses
+ * with no variable length info, show an offset of zero for the offset field.
+ */
+static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = {
+ /* SMB2_NEGOTIATE */ true,
+ /* SMB2_SESSION_SETUP */ true,
+ /* SMB2_LOGOFF */ false,
+ /* SMB2_TREE_CONNECT */ false,
+ /* SMB2_TREE_DISCONNECT */ false,
+ /* SMB2_CREATE */ true,
+ /* SMB2_CLOSE */ false,
+ /* SMB2_FLUSH */ false,
+ /* SMB2_READ */ true,
+ /* SMB2_WRITE */ false,
+ /* SMB2_LOCK */ false,
+ /* SMB2_IOCTL */ true,
+ /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */
+ /* SMB2_ECHO */ false,
+ /* SMB2_QUERY_DIRECTORY */ true,
+ /* SMB2_CHANGE_NOTIFY */ true,
+ /* SMB2_QUERY_INFO */ true,
+ /* SMB2_SET_INFO */ false,
+ /* SMB2_OPLOCK_BREAK */ false
+};
+
+/*
+ * Returns the pointer to the beginning of the data area. Length of the data
+ * area and the offset to it (from the beginning of the smb are also returned.
+ */
+char *
+smb2_get_data_area_len(int *off, int *len, struct smb2_sync_hdr *shdr)
+{
+ *off = 0;
+ *len = 0;
+
+ /* error responses do not have data area */
+ if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
+ (((struct smb2_err_rsp *)shdr)->StructureSize) ==
+ SMB2_ERROR_STRUCTURE_SIZE2)
+ return NULL;
+
+ /*
+ * Following commands have data areas so we have to get the location
+ * of the data buffer offset and data buffer length for the particular
+ * command.
+ */
+ switch (shdr->Command) {
+ case SMB2_NEGOTIATE:
+ *off = le16_to_cpu(
+ ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferOffset);
+ *len = le16_to_cpu(
+ ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferLength);
+ break;
+ case SMB2_SESSION_SETUP:
+ *off = le16_to_cpu(
+ ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferOffset);
+ *len = le16_to_cpu(
+ ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferLength);
+ break;
+ case SMB2_CREATE:
+ *off = le32_to_cpu(
+ ((struct smb2_create_rsp *)shdr)->CreateContextsOffset);
+ *len = le32_to_cpu(
+ ((struct smb2_create_rsp *)shdr)->CreateContextsLength);
+ break;
+ case SMB2_QUERY_INFO:
+ *off = le16_to_cpu(
+ ((struct smb2_query_info_rsp *)shdr)->OutputBufferOffset);
+ *len = le32_to_cpu(
+ ((struct smb2_query_info_rsp *)shdr)->OutputBufferLength);
+ break;
+ case SMB2_READ:
+ /* TODO: is this a bug ? */
+ *off = ((struct smb2_read_rsp *)shdr)->DataOffset;
+ *len = le32_to_cpu(((struct smb2_read_rsp *)shdr)->DataLength);
+ break;
+ case SMB2_QUERY_DIRECTORY:
+ *off = le16_to_cpu(
+ ((struct smb2_query_directory_rsp *)shdr)->OutputBufferOffset);
+ *len = le32_to_cpu(
+ ((struct smb2_query_directory_rsp *)shdr)->OutputBufferLength);
+ break;
+ case SMB2_IOCTL:
+ *off = le32_to_cpu(
+ ((struct smb2_ioctl_rsp *)shdr)->OutputOffset);
+ *len = le32_to_cpu(
+ ((struct smb2_ioctl_rsp *)shdr)->OutputCount);
+ break;
+ case SMB2_CHANGE_NOTIFY:
+ default:
+ /* BB FIXME for unimplemented cases above */
+ cifs_dbg(VFS, "no length check for command\n");
+ break;
+ }
+
+ /*
+ * Invalid length or offset probably means data area is invalid, but
+ * we have little choice but to ignore the data area in this case.
+ */
+ if (*off > 4096) {
+ cifs_dbg(VFS, "offset %d too large, data area ignored\n", *off);
+ *len = 0;
+ *off = 0;
+ } else if (*off < 0) {
+ cifs_dbg(VFS, "negative offset %d to data invalid ignore data area\n",
+ *off);
+ *off = 0;
+ *len = 0;
+ } else if (*len < 0) {
+ cifs_dbg(VFS, "negative data length %d invalid, data area ignored\n",
+ *len);
+ *len = 0;
+ } else if (*len > 128 * 1024) {
+ cifs_dbg(VFS, "data area larger than 128K: %d\n", *len);
+ *len = 0;
+ }
+
+ /* return pointer to beginning of data area, ie offset from SMB start */
+ if ((*off != 0) && (*len != 0))
+ return (char *)shdr + *off;
+ else
+ return NULL;
+}
+
+/*
+ * Calculate the size of the SMB message based on the fixed header
+ * portion, the number of word parameters and the data portion of the message.
+ */
+unsigned int
+smb2_calc_size(void *buf, struct TCP_Server_Info *srvr)
+{
+ struct smb2_sync_pdu *pdu = (struct smb2_sync_pdu *)buf;
+ struct smb2_sync_hdr *shdr = &pdu->sync_hdr;
+ int offset; /* the offset from the beginning of SMB to data area */
+ int data_length; /* the length of the variable length data area */
+ /* Structure Size has already been checked to make sure it is 64 */
+ int len = le16_to_cpu(shdr->StructureSize);
+
+ /*
+ * StructureSize2, ie length of fixed parameter area has already
+ * been checked to make sure it is the correct length.
+ */
+ len += le16_to_cpu(pdu->StructureSize2);
+
+ if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false)
+ goto calc_size_exit;
+
+ smb2_get_data_area_len(&offset, &data_length, shdr);
+ cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset);
+
+ if (data_length > 0) {
+ /*
+ * Check to make sure that data area begins after fixed area,
+ * Note that last byte of the fixed area is part of data area
+ * for some commands, typically those with odd StructureSize,
+ * so we must add one to the calculation.
+ */
+ if (offset + 1 < len) {
+ cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n",
+ offset + 1, len);
+ data_length = 0;
+ } else {
+ len = offset + data_length;
+ }
+ }
+calc_size_exit:
+ cifs_dbg(FYI, "SMB2 len %d\n", len);
+ return len;
+}
+
+/* Note: caller must free return buffer */
+__le16 *
+cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
+{
+ int len;
+ const char *start_of_path;
+ __le16 *to;
+ int map_type;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR)
+ map_type = SFM_MAP_UNI_RSVD;
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+ map_type = SFU_MAP_UNI_RSVD;
+ else
+ map_type = NO_MAP_UNI_RSVD;
+
+ /* Windows doesn't allow paths beginning with \ */
+ if (from[0] == '\\')
+ start_of_path = from + 1;
+
+ /* SMB311 POSIX extensions paths do not include leading slash */
+ else if (cifs_sb_master_tlink(cifs_sb) &&
+ cifs_sb_master_tcon(cifs_sb)->posix_extensions &&
+ (from[0] == '/')) {
+ start_of_path = from + 1;
+ } else
+ start_of_path = from;
+
+ to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len,
+ cifs_sb->local_nls, map_type);
+ return to;
+}
+
+__le32
+smb2_get_lease_state(struct cifsInodeInfo *cinode)
+{
+ __le32 lease = 0;
+
+ if (CIFS_CACHE_WRITE(cinode))
+ lease |= SMB2_LEASE_WRITE_CACHING;
+ if (CIFS_CACHE_HANDLE(cinode))
+ lease |= SMB2_LEASE_HANDLE_CACHING;
+ if (CIFS_CACHE_READ(cinode))
+ lease |= SMB2_LEASE_READ_CACHING;
+ return lease;
+}
+
+struct smb2_lease_break_work {
+ struct work_struct lease_break;
+ struct tcon_link *tlink;
+ __u8 lease_key[16];
+ __le32 lease_state;
+};
+
+static void
+cifs_ses_oplock_break(struct work_struct *work)
+{
+ struct smb2_lease_break_work *lw = container_of(work,
+ struct smb2_lease_break_work, lease_break);
+ int rc = 0;
+
+ rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key,
+ lw->lease_state);
+
+ cifs_dbg(FYI, "Lease release rc %d\n", rc);
+ cifs_put_tlink(lw->tlink);
+ kfree(lw);
+}
+
+static void
+smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key,
+ __le32 new_lease_state)
+{
+ struct smb2_lease_break_work *lw;
+
+ lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
+ if (!lw) {
+ cifs_put_tlink(tlink);
+ return;
+ }
+
+ INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
+ lw->tlink = tlink;
+ lw->lease_state = new_lease_state;
+ memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE);
+ queue_work(cifsiod_wq, &lw->lease_break);
+}
+
+static bool
+smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp)
+{
+ __u8 lease_state;
+ struct list_head *tmp;
+ struct cifsFileInfo *cfile;
+ struct cifsInodeInfo *cinode;
+ int ack_req = le32_to_cpu(rsp->Flags &
+ SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
+
+ lease_state = le32_to_cpu(rsp->NewLeaseState);
+
+ list_for_each(tmp, &tcon->openFileList) {
+ cfile = list_entry(tmp, struct cifsFileInfo, tlist);
+ cinode = CIFS_I(d_inode(cfile->dentry));
+
+ if (memcmp(cinode->lease_key, rsp->LeaseKey,
+ SMB2_LEASE_KEY_SIZE))
+ continue;
+
+ cifs_dbg(FYI, "found in the open list\n");
+ cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
+ lease_state);
+
+ if (ack_req)
+ cfile->oplock_break_cancelled = false;
+ else
+ cfile->oplock_break_cancelled = true;
+
+ set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
+
+ cfile->oplock_epoch = le16_to_cpu(rsp->Epoch);
+ cfile->oplock_level = lease_state;
+
+ cifs_queue_oplock_break(cfile);
+ return true;
+ }
+
+ return false;
+}
+
+static struct cifs_pending_open *
+smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon,
+ struct smb2_lease_break *rsp)
+{
+ __u8 lease_state = le32_to_cpu(rsp->NewLeaseState);
+ int ack_req = le32_to_cpu(rsp->Flags &
+ SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
+ struct cifs_pending_open *open;
+ struct cifs_pending_open *found = NULL;
+
+ list_for_each_entry(open, &tcon->pending_opens, olist) {
+ if (memcmp(open->lease_key, rsp->LeaseKey,
+ SMB2_LEASE_KEY_SIZE))
+ continue;
+
+ if (!found && ack_req) {
+ found = open;
+ }
+
+ cifs_dbg(FYI, "found in the pending open list\n");
+ cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
+ lease_state);
+
+ open->oplock = lease_state;
+ }
+
+ return found;
+}
+
+static bool
+smb2_is_valid_lease_break(char *buffer)
+{
+ struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct cifs_pending_open *open;
+
+ cifs_dbg(FYI, "Checking for lease break\n");
+
+ /* look up tcon based on tid & uid */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &cifs_tcp_ses_list) {
+ server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list);
+
+ list_for_each(tmp1, &server->smb_ses_list) {
+ ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
+
+ list_for_each(tmp2, &ses->tcon_list) {
+ tcon = list_entry(tmp2, struct cifs_tcon,
+ tcon_list);
+ spin_lock(&tcon->open_file_lock);
+ cifs_stats_inc(
+ &tcon->stats.cifs_stats.num_oplock_brks);
+ if (smb2_tcon_has_lease(tcon, rsp)) {
+ spin_unlock(&tcon->open_file_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+ open = smb2_tcon_find_pending_open_lease(tcon,
+ rsp);
+ if (open) {
+ __u8 lease_key[SMB2_LEASE_KEY_SIZE];
+ struct tcon_link *tlink;
+
+ tlink = cifs_get_tlink(open->tlink);
+ memcpy(lease_key, open->lease_key,
+ SMB2_LEASE_KEY_SIZE);
+ spin_unlock(&tcon->open_file_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ smb2_queue_pending_open_break(tlink,
+ lease_key,
+ rsp->NewLeaseState);
+ return true;
+ }
+ spin_unlock(&tcon->open_file_lock);
+
+ if (tcon->crfid.is_valid &&
+ !memcmp(rsp->LeaseKey,
+ tcon->crfid.fid->lease_key,
+ SMB2_LEASE_KEY_SIZE)) {
+ INIT_WORK(&tcon->crfid.lease_break,
+ smb2_cached_lease_break);
+ queue_work(cifsiod_wq,
+ &tcon->crfid.lease_break);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ cifs_dbg(FYI, "Can not process lease break - no lease matched\n");
+ return false;
+}
+
+bool
+smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *cinode;
+ struct cifsFileInfo *cfile;
+
+ cifs_dbg(FYI, "Checking for oplock break\n");
+
+ if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK)
+ return false;
+
+ if (rsp->StructureSize !=
+ smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
+ if (le16_to_cpu(rsp->StructureSize) == 44)
+ return smb2_is_valid_lease_break(buffer);
+ else
+ return false;
+ }
+
+ cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel);
+
+ /* look up tcon based on tid & uid */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+
+ list_for_each(tmp1, &ses->tcon_list) {
+ tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+
+ spin_lock(&tcon->open_file_lock);
+ list_for_each(tmp2, &tcon->openFileList) {
+ cfile = list_entry(tmp2, struct cifsFileInfo,
+ tlist);
+ if (rsp->PersistentFid !=
+ cfile->fid.persistent_fid ||
+ rsp->VolatileFid !=
+ cfile->fid.volatile_fid)
+ continue;
+
+ cifs_dbg(FYI, "file id match, oplock break\n");
+ cifs_stats_inc(
+ &tcon->stats.cifs_stats.num_oplock_brks);
+ cinode = CIFS_I(d_inode(cfile->dentry));
+ spin_lock(&cfile->file_info_lock);
+ if (!CIFS_CACHE_WRITE(cinode) &&
+ rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
+ cfile->oplock_break_cancelled = true;
+ else
+ cfile->oplock_break_cancelled = false;
+
+ set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
+ &cinode->flags);
+
+ cfile->oplock_epoch = 0;
+ cfile->oplock_level = rsp->OplockLevel;
+
+ spin_unlock(&cfile->file_info_lock);
+
+ cifs_queue_oplock_break(cfile);
+
+ spin_unlock(&tcon->open_file_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+ spin_unlock(&tcon->open_file_lock);
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ cifs_dbg(FYI, "No file id matched, oplock break ignored\n");
+ return true;
+}
+
+void
+smb2_cancelled_close_fid(struct work_struct *work)
+{
+ struct close_cancelled_open *cancelled = container_of(work,
+ struct close_cancelled_open, work);
+
+ cifs_dbg(VFS, "Close unmatched open\n");
+
+ SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
+ cancelled->fid.volatile_fid);
+ cifs_put_tcon(cancelled->tcon);
+ kfree(cancelled);
+}
+
+/* Caller should already has an extra reference to @tcon */
+static int
+__smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+ __u64 volatile_fid)
+{
+ struct close_cancelled_open *cancelled;
+
+ cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC);
+ if (!cancelled)
+ return -ENOMEM;
+
+ cancelled->fid.persistent_fid = persistent_fid;
+ cancelled->fid.volatile_fid = volatile_fid;
+ cancelled->tcon = tcon;
+ INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
+ WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
+
+ return 0;
+}
+
+int
+smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+ __u64 volatile_fid)
+{
+ int rc;
+
+ cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
+ spin_lock(&cifs_tcp_ses_lock);
+ tcon->tc_count++;
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ rc = __smb2_handle_cancelled_close(tcon, persistent_fid, volatile_fid);
+ if (rc)
+ cifs_put_tcon(tcon);
+
+ return rc;
+}
+
+int
+smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
+ struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
+ struct cifs_tcon *tcon;
+ int rc;
+
+ if (sync_hdr->Command != SMB2_CREATE ||
+ sync_hdr->Status != STATUS_SUCCESS)
+ return 0;
+
+ tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
+ sync_hdr->TreeId);
+ if (!tcon)
+ return -ENOENT;
+
+ rc = __smb2_handle_cancelled_close(tcon, rsp->PersistentFileId,
+ rsp->VolatileFileId);
+ if (rc)
+ cifs_put_tcon(tcon);
+
+ return rc;
+}
+
+/**
+ * smb311_update_preauth_hash - update @ses hash with the packet data in @iov
+ *
+ * Assumes @iov does not contain the rfc1002 length and iov[0] has the
+ * SMB2 header.
+ */
+int
+smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
+{
+ int i, rc;
+ struct sdesc *d;
+ struct smb2_sync_hdr *hdr;
+
+ if (ses->server->tcpStatus == CifsGood) {
+ /* skip non smb311 connections */
+ if (ses->server->dialect != SMB311_PROT_ID)
+ return 0;
+
+ /* skip last sess setup response */
+ hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
+ if (hdr->Flags & SMB2_FLAGS_SIGNED)
+ return 0;
+ }
+
+ rc = smb311_crypto_shash_allocate(ses->server);
+ if (rc)
+ return rc;
+
+ d = ses->server->secmech.sdescsha512;
+ rc = crypto_shash_init(&d->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_update(&d->shash, ses->preauth_sha_hash,
+ SMB2_PREAUTH_HASH_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not update sha512 shash\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < nvec; i++) {
+ rc = crypto_shash_update(&d->shash,
+ iov[i].iov_base, iov[i].iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not update sha512 shash\n",
+ __func__);
+ return rc;
+ }
+ }
+
+ rc = crypto_shash_final(&d->shash, ses->preauth_sha_hash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not finalize sha512 shash\n",
+ __func__);
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
new file mode 100644
index 000000000..cc34a28ae
--- /dev/null
+++ b/fs/cifs/smb2ops.c
@@ -0,0 +1,3909 @@
+/*
+ * SMB2 version specific operations
+ *
+ * Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published
+ * by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/pagemap.h>
+#include <linux/vfs.h>
+#include <linux/falloc.h>
+#include <linux/scatterlist.h>
+#include <linux/uuid.h>
+#include <crypto/aead.h>
+#include "cifsglob.h"
+#include "smb2pdu.h"
+#include "smb2proto.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_unicode.h"
+#include "smb2status.h"
+#include "smb2glob.h"
+#include "cifs_ioctl.h"
+#include "smbdirect.h"
+
+/* Change credits for different ops and return the total number of credits */
+static int
+change_conf(struct TCP_Server_Info *server)
+{
+ server->credits += server->echo_credits + server->oplock_credits;
+ server->oplock_credits = server->echo_credits = 0;
+ switch (server->credits) {
+ case 0:
+ return 0;
+ case 1:
+ server->echoes = false;
+ server->oplocks = false;
+ break;
+ case 2:
+ server->echoes = true;
+ server->oplocks = false;
+ server->echo_credits = 1;
+ break;
+ default:
+ server->echoes = true;
+ if (enable_oplocks) {
+ server->oplocks = true;
+ server->oplock_credits = 1;
+ } else
+ server->oplocks = false;
+
+ server->echo_credits = 1;
+ }
+ server->credits -= server->echo_credits + server->oplock_credits;
+ return server->credits + server->echo_credits + server->oplock_credits;
+}
+
+static void
+smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
+ const int optype)
+{
+ int *val, rc = -1;
+
+ spin_lock(&server->req_lock);
+ val = server->ops->get_credits_field(server, optype);
+ *val += add;
+ if (*val > 65000) {
+ *val = 65000; /* Don't get near 64K credits, avoid srv bugs */
+ printk_once(KERN_WARNING "server overflowed SMB3 credits\n");
+ }
+ server->in_flight--;
+ if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
+ rc = change_conf(server);
+ /*
+ * Sometimes server returns 0 credits on oplock break ack - we need to
+ * rebalance credits in this case.
+ */
+ else if (server->in_flight > 0 && server->oplock_credits == 0 &&
+ server->oplocks) {
+ if (server->credits > 1) {
+ server->credits--;
+ server->oplock_credits++;
+ }
+ }
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+
+ if (server->tcpStatus == CifsNeedReconnect)
+ return;
+
+ switch (rc) {
+ case -1:
+ /* change_conf hasn't been executed */
+ break;
+ case 0:
+ cifs_dbg(VFS, "Possible client or server bug - zero credits\n");
+ break;
+ case 1:
+ cifs_dbg(VFS, "disabling echoes and oplocks\n");
+ break;
+ case 2:
+ cifs_dbg(FYI, "disabling oplocks\n");
+ break;
+ default:
+ cifs_dbg(FYI, "add %u credits total=%d\n", add, rc);
+ }
+}
+
+static void
+smb2_set_credits(struct TCP_Server_Info *server, const int val)
+{
+ spin_lock(&server->req_lock);
+ server->credits = val;
+ spin_unlock(&server->req_lock);
+}
+
+static int *
+smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
+{
+ switch (optype) {
+ case CIFS_ECHO_OP:
+ return &server->echo_credits;
+ case CIFS_OBREAK_OP:
+ return &server->oplock_credits;
+ default:
+ return &server->credits;
+ }
+}
+
+static unsigned int
+smb2_get_credits(struct mid_q_entry *mid)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)mid->resp_buf;
+
+ return le16_to_cpu(shdr->CreditRequest);
+}
+
+static int
+smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
+ unsigned int *num, unsigned int *credits)
+{
+ int rc = 0;
+ unsigned int scredits;
+
+ spin_lock(&server->req_lock);
+ while (1) {
+ if (server->credits <= 0) {
+ spin_unlock(&server->req_lock);
+ cifs_num_waiters_inc(server);
+ rc = wait_event_killable(server->request_q,
+ has_credits(server, &server->credits));
+ cifs_num_waiters_dec(server);
+ if (rc)
+ return rc;
+ spin_lock(&server->req_lock);
+ } else {
+ if (server->tcpStatus == CifsExiting) {
+ spin_unlock(&server->req_lock);
+ return -ENOENT;
+ }
+
+ scredits = server->credits;
+ /* can deadlock with reopen */
+ if (scredits <= 8) {
+ *num = SMB2_MAX_BUFFER_SIZE;
+ *credits = 0;
+ break;
+ }
+
+ /* leave some credits for reopen and other ops */
+ scredits -= 8;
+ *num = min_t(unsigned int, size,
+ scredits * SMB2_MAX_BUFFER_SIZE);
+
+ *credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
+ server->credits -= *credits;
+ server->in_flight++;
+ break;
+ }
+ }
+ spin_unlock(&server->req_lock);
+ return rc;
+}
+
+static __u64
+smb2_get_next_mid(struct TCP_Server_Info *server)
+{
+ __u64 mid;
+ /* for SMB2 we need the current value */
+ spin_lock(&GlobalMid_Lock);
+ mid = server->CurrentMid++;
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+}
+
+static void
+smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
+{
+ spin_lock(&GlobalMid_Lock);
+ if (server->CurrentMid >= val)
+ server->CurrentMid -= val;
+ spin_unlock(&GlobalMid_Lock);
+}
+
+static struct mid_q_entry *
+smb2_find_mid(struct TCP_Server_Info *server, char *buf)
+{
+ struct mid_q_entry *mid;
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+ __u64 wire_mid = le64_to_cpu(shdr->MessageId);
+
+ if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
+ cifs_dbg(VFS, "encrypted frame parsing not supported yet");
+ return NULL;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_entry(mid, &server->pending_mid_q, qhead) {
+ if ((mid->mid == wire_mid) &&
+ (mid->mid_state == MID_REQUEST_SUBMITTED) &&
+ (mid->command == shdr->Command)) {
+ kref_get(&mid->refcount);
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ return NULL;
+}
+
+static void
+smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
+{
+#ifdef CONFIG_CIFS_DEBUG2
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+
+ cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
+ shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
+ shdr->ProcessId);
+ cifs_dbg(VFS, "smb buf %p len %u\n", buf,
+ server->ops->calc_smb_size(buf, server));
+#endif
+}
+
+static bool
+smb2_need_neg(struct TCP_Server_Info *server)
+{
+ return server->max_read == 0;
+}
+
+static int
+smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
+{
+ int rc;
+ ses->server->CurrentMid = 0;
+ rc = SMB2_negotiate(xid, ses);
+ /* BB we probably don't need to retry with modern servers */
+ if (rc == -EAGAIN)
+ rc = -EHOSTDOWN;
+ return rc;
+}
+
+static unsigned int
+smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int wsize;
+
+ /* start with specified wsize, or default */
+ wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
+ wsize = min_t(unsigned int, wsize, server->max_write);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ if (server->rdma) {
+ if (server->sign)
+ wsize = min_t(unsigned int,
+ wsize, server->smbd_conn->max_fragmented_send_size);
+ else
+ wsize = min_t(unsigned int,
+ wsize, server->smbd_conn->max_readwrite_size);
+ }
+#endif
+ if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+ wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
+
+ return wsize;
+}
+
+static unsigned int
+smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int rsize;
+
+ /* start with specified rsize, or default */
+ rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
+ rsize = min_t(unsigned int, rsize, server->max_read);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ if (server->rdma) {
+ if (server->sign)
+ rsize = min_t(unsigned int,
+ rsize, server->smbd_conn->max_fragmented_recv_size);
+ else
+ rsize = min_t(unsigned int,
+ rsize, server->smbd_conn->max_readwrite_size);
+ }
+#endif
+
+ if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+ rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
+
+ return rsize;
+}
+
+
+static int
+parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
+ size_t buf_len,
+ struct cifs_server_iface **iface_list,
+ size_t *iface_count)
+{
+ struct network_interface_info_ioctl_rsp *p;
+ struct sockaddr_in *addr4;
+ struct sockaddr_in6 *addr6;
+ struct iface_info_ipv4 *p4;
+ struct iface_info_ipv6 *p6;
+ struct cifs_server_iface *info;
+ ssize_t bytes_left;
+ size_t next = 0;
+ int nb_iface = 0;
+ int rc = 0;
+
+ *iface_list = NULL;
+ *iface_count = 0;
+
+ /*
+ * Fist pass: count and sanity check
+ */
+
+ bytes_left = buf_len;
+ p = buf;
+ while (bytes_left >= sizeof(*p)) {
+ nb_iface++;
+ next = le32_to_cpu(p->Next);
+ if (!next) {
+ bytes_left -= sizeof(*p);
+ break;
+ }
+ p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
+ bytes_left -= next;
+ }
+
+ if (!nb_iface) {
+ cifs_dbg(VFS, "%s: malformed interface info\n", __func__);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Azure rounds the buffer size up 8, to a 16 byte boundary */
+ if ((bytes_left > 8) || p->Next)
+ cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
+
+
+ /*
+ * Second pass: extract info to internal structure
+ */
+
+ *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL);
+ if (!*iface_list) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ info = *iface_list;
+ bytes_left = buf_len;
+ p = buf;
+ while (bytes_left >= sizeof(*p)) {
+ info->speed = le64_to_cpu(p->LinkSpeed);
+ info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
+ info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
+
+ cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
+ cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
+ cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__,
+ le32_to_cpu(p->Capability));
+
+ switch (p->Family) {
+ /*
+ * The kernel and wire socket structures have the same
+ * layout and use network byte order but make the
+ * conversion explicit in case either one changes.
+ */
+ case INTERNETWORK:
+ addr4 = (struct sockaddr_in *)&info->sockaddr;
+ p4 = (struct iface_info_ipv4 *)p->Buffer;
+ addr4->sin_family = AF_INET;
+ memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
+
+ /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
+ addr4->sin_port = cpu_to_be16(CIFS_PORT);
+
+ cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__,
+ &addr4->sin_addr);
+ break;
+ case INTERNETWORKV6:
+ addr6 = (struct sockaddr_in6 *)&info->sockaddr;
+ p6 = (struct iface_info_ipv6 *)p->Buffer;
+ addr6->sin6_family = AF_INET6;
+ memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
+
+ /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
+ addr6->sin6_flowinfo = 0;
+ addr6->sin6_scope_id = 0;
+ addr6->sin6_port = cpu_to_be16(CIFS_PORT);
+
+ cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__,
+ &addr6->sin6_addr);
+ break;
+ default:
+ cifs_dbg(VFS,
+ "%s: skipping unsupported socket family\n",
+ __func__);
+ goto next_iface;
+ }
+
+ (*iface_count)++;
+ info++;
+next_iface:
+ next = le32_to_cpu(p->Next);
+ if (!next)
+ break;
+ p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
+ bytes_left -= next;
+ }
+
+ if (!*iface_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+out:
+ if (rc) {
+ kfree(*iface_list);
+ *iface_count = 0;
+ *iface_list = NULL;
+ }
+ return rc;
+}
+
+
+static int
+SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ int rc;
+ unsigned int ret_data_len = 0;
+ struct network_interface_info_ioctl_rsp *out_buf = NULL;
+ struct cifs_server_iface *iface_list;
+ size_t iface_count;
+ struct cifs_ses *ses = tcon->ses;
+
+ rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+ FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
+ NULL /* no data input */, 0 /* no data input */,
+ (char **)&out_buf, &ret_data_len);
+ if (rc == -EOPNOTSUPP) {
+ cifs_dbg(FYI,
+ "server does not support query network interfaces\n");
+ goto out;
+ } else if (rc != 0) {
+ cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
+ goto out;
+ }
+
+ rc = parse_server_interfaces(out_buf, ret_data_len,
+ &iface_list, &iface_count);
+ if (rc)
+ goto out;
+
+ spin_lock(&ses->iface_lock);
+ kfree(ses->iface_list);
+ ses->iface_list = iface_list;
+ ses->iface_count = iface_count;
+ ses->iface_last_update = jiffies;
+ spin_unlock(&ses->iface_lock);
+
+out:
+ kfree(out_buf);
+ return rc;
+}
+
+static void
+smb2_close_cached_fid(struct kref *ref)
+{
+ struct cached_fid *cfid = container_of(ref, struct cached_fid,
+ refcount);
+
+ if (cfid->is_valid) {
+ cifs_dbg(FYI, "clear cached root file handle\n");
+ SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
+ cfid->fid->volatile_fid);
+ cfid->is_valid = false;
+ }
+}
+
+void close_shroot(struct cached_fid *cfid)
+{
+ mutex_lock(&cfid->fid_mutex);
+ kref_put(&cfid->refcount, smb2_close_cached_fid);
+ mutex_unlock(&cfid->fid_mutex);
+}
+
+void
+smb2_cached_lease_break(struct work_struct *work)
+{
+ struct cached_fid *cfid = container_of(work,
+ struct cached_fid, lease_break);
+
+ close_shroot(cfid);
+}
+
+/*
+ * Open the directory at the root of a share
+ */
+int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
+{
+ struct cifs_open_parms oparams;
+ int rc;
+ __le16 srch_path = 0; /* Null - since an open of top of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_II;
+
+ mutex_lock(&tcon->crfid.fid_mutex);
+ if (tcon->crfid.is_valid) {
+ cifs_dbg(FYI, "found a cached root file handle\n");
+ memcpy(pfid, tcon->crfid.fid, sizeof(struct cifs_fid));
+ kref_get(&tcon->crfid.refcount);
+ mutex_unlock(&tcon->crfid.fid_mutex);
+ return 0;
+ }
+
+ oparams.tcon = tcon;
+ oparams.create_options = 0;
+ oparams.desired_access = FILE_READ_ATTRIBUTES;
+ oparams.disposition = FILE_OPEN;
+ oparams.fid = pfid;
+ oparams.reconnect = false;
+
+ /*
+ * We do not hold the lock for the open because in case
+ * SMB2_open needs to reconnect, it will end up calling
+ * cifs_mark_open_files_invalid() which takes the lock again
+ * thus causing a deadlock
+ */
+ mutex_unlock(&tcon->crfid.fid_mutex);
+ rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL);
+ mutex_lock(&tcon->crfid.fid_mutex);
+
+ /*
+ * Now we need to check again as the cached root might have
+ * been successfully re-opened from a concurrent process
+ */
+
+ if (tcon->crfid.is_valid) {
+ /* work was already done */
+
+ /* stash fids for close() later */
+ struct cifs_fid fid = {
+ .persistent_fid = pfid->persistent_fid,
+ .volatile_fid = pfid->volatile_fid,
+ };
+
+ /*
+ * Caller expects this func to set pfid to a valid
+ * cached root, so we copy the existing one and get a
+ * reference
+ */
+ memcpy(pfid, tcon->crfid.fid, sizeof(*pfid));
+ kref_get(&tcon->crfid.refcount);
+
+ mutex_unlock(&tcon->crfid.fid_mutex);
+
+ if (rc == 0) {
+ /* close extra handle outside of critical section */
+ SMB2_close(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid);
+ }
+ return 0;
+ }
+
+ /* Cached root is still invalid, continue normaly */
+
+ if (rc == 0) {
+ memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
+ tcon->crfid.tcon = tcon;
+ tcon->crfid.is_valid = true;
+ kref_init(&tcon->crfid.refcount);
+ kref_get(&tcon->crfid.refcount);
+ }
+
+ mutex_unlock(&tcon->crfid.fid_mutex);
+ return rc;
+}
+
+static void
+smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ int rc;
+ __le16 srch_path = 0; /* Null - open root of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+ bool no_cached_open = tcon->nohandlecache;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ if (no_cached_open)
+ rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
+ NULL);
+ else
+ rc = open_shroot(xid, tcon, &fid);
+
+ if (rc)
+ return;
+
+ SMB3_request_interfaces(xid, tcon);
+
+ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ FS_ATTRIBUTE_INFORMATION);
+ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ FS_DEVICE_INFORMATION);
+ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ FS_VOLUME_INFORMATION);
+ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */
+ if (no_cached_open)
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ else
+ close_shroot(&tcon->crfid);
+
+ return;
+}
+
+static void
+smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ int rc;
+ __le16 srch_path = 0; /* Null - open root of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
+ if (rc)
+ return;
+
+ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ FS_ATTRIBUTE_INFORMATION);
+ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ FS_DEVICE_INFORMATION);
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ return;
+}
+
+static int
+smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path)
+{
+ int rc;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+
+ if ((*full_path == 0) && tcon->crfid.is_valid)
+ return 0;
+
+ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+ if (rc) {
+ kfree(utf16_path);
+ return rc;
+ }
+
+ rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ kfree(utf16_path);
+ return rc;
+}
+
+static int
+smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const char *full_path,
+ u64 *uniqueid, FILE_ALL_INFO *data)
+{
+ *uniqueid = le64_to_cpu(data->IndexNumber);
+ return 0;
+}
+
+static int
+smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, FILE_ALL_INFO *data)
+{
+ int rc;
+ struct smb2_file_all_info *smb2_data;
+
+ smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+ GFP_KERNEL);
+ if (smb2_data == NULL)
+ return -ENOMEM;
+
+ rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid,
+ smb2_data);
+ if (!rc)
+ move_smb2_info_to_cifs(data, smb2_data);
+ kfree(smb2_data);
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_XATTR
+static ssize_t
+move_smb2_ea_to_cifs(char *dst, size_t dst_size,
+ struct smb2_file_full_ea_info *src, size_t src_size,
+ const unsigned char *ea_name)
+{
+ int rc = 0;
+ unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
+ char *name, *value;
+ size_t buf_size = dst_size;
+ size_t name_len, value_len, user_name_len;
+
+ while (src_size > 0) {
+ name = &src->ea_data[0];
+ name_len = (size_t)src->ea_name_length;
+ value = &src->ea_data[src->ea_name_length + 1];
+ value_len = (size_t)le16_to_cpu(src->ea_value_length);
+
+ if (name_len == 0) {
+ break;
+ }
+
+ if (src_size < 8 + name_len + 1 + value_len) {
+ cifs_dbg(FYI, "EA entry goes beyond length of list\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ if (ea_name) {
+ if (ea_name_len == name_len &&
+ memcmp(ea_name, name, name_len) == 0) {
+ rc = value_len;
+ if (dst_size == 0)
+ goto out;
+ if (dst_size < value_len) {
+ rc = -ERANGE;
+ goto out;
+ }
+ memcpy(dst, value, value_len);
+ goto out;
+ }
+ } else {
+ /* 'user.' plus a terminating null */
+ user_name_len = 5 + 1 + name_len;
+
+ if (buf_size == 0) {
+ /* skip copy - calc size only */
+ rc += user_name_len;
+ } else if (dst_size >= user_name_len) {
+ dst_size -= user_name_len;
+ memcpy(dst, "user.", 5);
+ dst += 5;
+ memcpy(dst, src->ea_data, name_len);
+ dst += name_len;
+ *dst = 0;
+ ++dst;
+ rc += user_name_len;
+ } else {
+ /* stop before overrun buffer */
+ rc = -ERANGE;
+ break;
+ }
+ }
+
+ if (!src->next_entry_offset)
+ break;
+
+ if (src_size < le32_to_cpu(src->next_entry_offset)) {
+ /* stop before overrun buffer */
+ rc = -ERANGE;
+ break;
+ }
+ src_size -= le32_to_cpu(src->next_entry_offset);
+ src = (void *)((char *)src +
+ le32_to_cpu(src->next_entry_offset));
+ }
+
+ /* didn't find the named attribute */
+ if (ea_name)
+ rc = -ENODATA;
+
+out:
+ return (ssize_t)rc;
+}
+
+static ssize_t
+smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
+ const unsigned char *path, const unsigned char *ea_name,
+ char *ea_data, size_t buf_size,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+ struct smb2_file_full_ea_info *smb2_data;
+ int ea_buf_size = SMB2_MIN_EA_BUF;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_EA;
+ oparms.disposition = FILE_OPEN;
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+ kfree(utf16_path);
+ if (rc) {
+ cifs_dbg(FYI, "open failed rc=%d\n", rc);
+ return rc;
+ }
+
+ while (1) {
+ smb2_data = kzalloc(ea_buf_size, GFP_KERNEL);
+ if (smb2_data == NULL) {
+ SMB2_close(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid);
+ return -ENOMEM;
+ }
+
+ rc = SMB2_query_eas(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid,
+ ea_buf_size, smb2_data);
+
+ if (rc != -E2BIG)
+ break;
+
+ kfree(smb2_data);
+ ea_buf_size <<= 1;
+
+ if (ea_buf_size > SMB2_MAX_EA_BUF) {
+ cifs_dbg(VFS, "EA size is too large\n");
+ SMB2_close(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid);
+ return -ENOMEM;
+ }
+ }
+
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+ /*
+ * If ea_name is NULL (listxattr) and there are no EAs, return 0 as it's
+ * not an error. Otherwise, the specified ea_name was not found.
+ */
+ if (!rc)
+ rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data,
+ SMB2_MAX_EA_BUF, ea_name);
+ else if (!ea_name && rc == -ENODATA)
+ rc = 0;
+
+ kfree(smb2_data);
+ return rc;
+}
+
+
+static int
+smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *path, const char *ea_name, const void *ea_value,
+ const __u16 ea_value_len, const struct nls_table *nls_codepage,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+ struct smb2_file_full_ea_info *ea;
+ int ea_name_len = strlen(ea_name);
+ int len;
+
+ if (ea_name_len > 255)
+ return -EINVAL;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_WRITE_EA;
+ oparms.disposition = FILE_OPEN;
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+ kfree(utf16_path);
+ if (rc) {
+ cifs_dbg(FYI, "open failed rc=%d\n", rc);
+ return rc;
+ }
+
+ len = sizeof(*ea) + ea_name_len + ea_value_len + 1;
+ ea = kzalloc(len, GFP_KERNEL);
+ if (ea == NULL) {
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ return -ENOMEM;
+ }
+
+ ea->ea_name_length = ea_name_len;
+ ea->ea_value_length = cpu_to_le16(ea_value_len);
+ memcpy(ea->ea_data, ea_name, ea_name_len + 1);
+ memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len);
+
+ rc = SMB2_set_ea(xid, tcon, fid.persistent_fid, fid.volatile_fid, ea,
+ len);
+ kfree(ea);
+
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+ return rc;
+}
+#endif
+
+static bool
+smb2_can_echo(struct TCP_Server_Info *server)
+{
+ return server->echoes;
+}
+
+static void
+smb2_clear_stats(struct cifs_tcon *tcon)
+{
+ int i;
+ for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
+ atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0);
+ atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0);
+ }
+}
+
+static void
+smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon)
+{
+ seq_puts(m, "\n\tShare Capabilities:");
+ if (tcon->capabilities & SMB2_SHARE_CAP_DFS)
+ seq_puts(m, " DFS,");
+ if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY)
+ seq_puts(m, " CONTINUOUS AVAILABILITY,");
+ if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT)
+ seq_puts(m, " SCALEOUT,");
+ if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER)
+ seq_puts(m, " CLUSTER,");
+ if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC)
+ seq_puts(m, " ASYMMETRIC,");
+ if (tcon->capabilities == 0)
+ seq_puts(m, " None");
+ if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE)
+ seq_puts(m, " Aligned,");
+ if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE)
+ seq_puts(m, " Partition Aligned,");
+ if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY)
+ seq_puts(m, " SSD,");
+ if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED)
+ seq_puts(m, " TRIM-support,");
+
+ seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags);
+ seq_printf(m, "\n\ttid: 0x%x", tcon->tid);
+ if (tcon->perf_sector_size)
+ seq_printf(m, "\tOptimal sector size: 0x%x",
+ tcon->perf_sector_size);
+ seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access);
+}
+
+static void
+smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
+{
+ atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent;
+ atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed;
+
+ /*
+ * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO
+ * totals (requests sent) since those SMBs are per-session not per tcon
+ */
+ seq_printf(m, "\nBytes read: %llu Bytes written: %llu",
+ (long long)(tcon->bytes_read),
+ (long long)(tcon->bytes_written));
+ seq_printf(m, "\nTreeConnects: %d total %d failed",
+ atomic_read(&sent[SMB2_TREE_CONNECT_HE]),
+ atomic_read(&failed[SMB2_TREE_CONNECT_HE]));
+ seq_printf(m, "\nTreeDisconnects: %d total %d failed",
+ atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]),
+ atomic_read(&failed[SMB2_TREE_DISCONNECT_HE]));
+ seq_printf(m, "\nCreates: %d total %d failed",
+ atomic_read(&sent[SMB2_CREATE_HE]),
+ atomic_read(&failed[SMB2_CREATE_HE]));
+ seq_printf(m, "\nCloses: %d total %d failed",
+ atomic_read(&sent[SMB2_CLOSE_HE]),
+ atomic_read(&failed[SMB2_CLOSE_HE]));
+ seq_printf(m, "\nFlushes: %d total %d failed",
+ atomic_read(&sent[SMB2_FLUSH_HE]),
+ atomic_read(&failed[SMB2_FLUSH_HE]));
+ seq_printf(m, "\nReads: %d total %d failed",
+ atomic_read(&sent[SMB2_READ_HE]),
+ atomic_read(&failed[SMB2_READ_HE]));
+ seq_printf(m, "\nWrites: %d total %d failed",
+ atomic_read(&sent[SMB2_WRITE_HE]),
+ atomic_read(&failed[SMB2_WRITE_HE]));
+ seq_printf(m, "\nLocks: %d total %d failed",
+ atomic_read(&sent[SMB2_LOCK_HE]),
+ atomic_read(&failed[SMB2_LOCK_HE]));
+ seq_printf(m, "\nIOCTLs: %d total %d failed",
+ atomic_read(&sent[SMB2_IOCTL_HE]),
+ atomic_read(&failed[SMB2_IOCTL_HE]));
+ seq_printf(m, "\nQueryDirectories: %d total %d failed",
+ atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]),
+ atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE]));
+ seq_printf(m, "\nChangeNotifies: %d total %d failed",
+ atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]),
+ atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE]));
+ seq_printf(m, "\nQueryInfos: %d total %d failed",
+ atomic_read(&sent[SMB2_QUERY_INFO_HE]),
+ atomic_read(&failed[SMB2_QUERY_INFO_HE]));
+ seq_printf(m, "\nSetInfos: %d total %d failed",
+ atomic_read(&sent[SMB2_SET_INFO_HE]),
+ atomic_read(&failed[SMB2_SET_INFO_HE]));
+ seq_printf(m, "\nOplockBreaks: %d sent %d failed",
+ atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]),
+ atomic_read(&failed[SMB2_OPLOCK_BREAK_HE]));
+}
+
+static void
+smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+ struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
+
+ cfile->fid.persistent_fid = fid->persistent_fid;
+ cfile->fid.volatile_fid = fid->volatile_fid;
+ server->ops->set_oplock_level(cinode, oplock, fid->epoch,
+ &fid->purge_cache);
+ cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
+ memcpy(cfile->fid.create_guid, fid->create_guid, 16);
+}
+
+static void
+smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+}
+
+static int
+SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ struct copychunk_ioctl *pcchunk)
+{
+ int rc;
+ unsigned int ret_data_len;
+ struct resume_key_req *res_key;
+
+ rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
+ FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
+ NULL, 0 /* no input */,
+ (char **)&res_key, &ret_data_len);
+
+ if (rc) {
+ cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
+ goto req_res_key_exit;
+ }
+ if (ret_data_len < sizeof(struct resume_key_req)) {
+ cifs_dbg(VFS, "Invalid refcopy resume key length\n");
+ rc = -EINVAL;
+ goto req_res_key_exit;
+ }
+ memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE);
+
+req_res_key_exit:
+ kfree(res_key);
+ return rc;
+}
+
+static ssize_t
+smb2_copychunk_range(const unsigned int xid,
+ struct cifsFileInfo *srcfile,
+ struct cifsFileInfo *trgtfile, u64 src_off,
+ u64 len, u64 dest_off)
+{
+ int rc;
+ unsigned int ret_data_len;
+ struct copychunk_ioctl *pcchunk;
+ struct copychunk_ioctl_rsp *retbuf = NULL;
+ struct cifs_tcon *tcon;
+ int chunks_copied = 0;
+ bool chunk_sizes_updated = false;
+ ssize_t bytes_written, total_bytes_written = 0;
+ struct inode *inode;
+
+ pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
+
+ /*
+ * We need to flush all unwritten data before we can send the
+ * copychunk ioctl to the server.
+ */
+ inode = d_inode(trgtfile->dentry);
+ filemap_write_and_wait(inode->i_mapping);
+
+ if (pcchunk == NULL)
+ return -ENOMEM;
+
+ cifs_dbg(FYI, "in smb2_copychunk_range - about to call request res key\n");
+ /* Request a key from the server to identify the source of the copy */
+ rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink),
+ srcfile->fid.persistent_fid,
+ srcfile->fid.volatile_fid, pcchunk);
+
+ /* Note: request_res_key sets res_key null only if rc !=0 */
+ if (rc)
+ goto cchunk_out;
+
+ /* For now array only one chunk long, will make more flexible later */
+ pcchunk->ChunkCount = cpu_to_le32(1);
+ pcchunk->Reserved = 0;
+ pcchunk->Reserved2 = 0;
+
+ tcon = tlink_tcon(trgtfile->tlink);
+
+ while (len > 0) {
+ pcchunk->SourceOffset = cpu_to_le64(src_off);
+ pcchunk->TargetOffset = cpu_to_le64(dest_off);
+ pcchunk->Length =
+ cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
+
+ /* Request server copy to target from src identified by key */
+ kfree(retbuf);
+ retbuf = NULL;
+ rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
+ trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
+ true /* is_fsctl */, (char *)pcchunk,
+ sizeof(struct copychunk_ioctl), (char **)&retbuf,
+ &ret_data_len);
+ if (rc == 0) {
+ if (ret_data_len !=
+ sizeof(struct copychunk_ioctl_rsp)) {
+ cifs_dbg(VFS, "invalid cchunk response size\n");
+ rc = -EIO;
+ goto cchunk_out;
+ }
+ if (retbuf->TotalBytesWritten == 0) {
+ cifs_dbg(FYI, "no bytes copied\n");
+ rc = -EIO;
+ goto cchunk_out;
+ }
+ /*
+ * Check if server claimed to write more than we asked
+ */
+ if (le32_to_cpu(retbuf->TotalBytesWritten) >
+ le32_to_cpu(pcchunk->Length)) {
+ cifs_dbg(VFS, "invalid copy chunk response\n");
+ rc = -EIO;
+ goto cchunk_out;
+ }
+ if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
+ cifs_dbg(VFS, "invalid num chunks written\n");
+ rc = -EIO;
+ goto cchunk_out;
+ }
+ chunks_copied++;
+
+ bytes_written = le32_to_cpu(retbuf->TotalBytesWritten);
+ src_off += bytes_written;
+ dest_off += bytes_written;
+ len -= bytes_written;
+ total_bytes_written += bytes_written;
+
+ cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n",
+ le32_to_cpu(retbuf->ChunksWritten),
+ le32_to_cpu(retbuf->ChunkBytesWritten),
+ bytes_written);
+ } else if (rc == -EINVAL) {
+ if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
+ goto cchunk_out;
+
+ cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n",
+ le32_to_cpu(retbuf->ChunksWritten),
+ le32_to_cpu(retbuf->ChunkBytesWritten),
+ le32_to_cpu(retbuf->TotalBytesWritten));
+
+ /*
+ * Check if this is the first request using these sizes,
+ * (ie check if copy succeed once with original sizes
+ * and check if the server gave us different sizes after
+ * we already updated max sizes on previous request).
+ * if not then why is the server returning an error now
+ */
+ if ((chunks_copied != 0) || chunk_sizes_updated)
+ goto cchunk_out;
+
+ /* Check that server is not asking us to grow size */
+ if (le32_to_cpu(retbuf->ChunkBytesWritten) <
+ tcon->max_bytes_chunk)
+ tcon->max_bytes_chunk =
+ le32_to_cpu(retbuf->ChunkBytesWritten);
+ else
+ goto cchunk_out; /* server gave us bogus size */
+
+ /* No need to change MaxChunks since already set to 1 */
+ chunk_sizes_updated = true;
+ } else
+ goto cchunk_out;
+ }
+
+cchunk_out:
+ kfree(pcchunk);
+ kfree(retbuf);
+ if (rc)
+ return rc;
+ else
+ return total_bytes_written;
+}
+
+static int
+smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+}
+
+static unsigned int
+smb2_read_data_offset(char *buf)
+{
+ struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
+ return rsp->DataOffset;
+}
+
+static unsigned int
+smb2_read_data_length(char *buf, bool in_remaining)
+{
+ struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
+
+ if (in_remaining)
+ return le32_to_cpu(rsp->DataRemaining);
+
+ return le32_to_cpu(rsp->DataLength);
+}
+
+
+static int
+smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid,
+ struct cifs_io_parms *parms, unsigned int *bytes_read,
+ char **buf, int *buf_type)
+{
+ parms->persistent_fid = pfid->persistent_fid;
+ parms->volatile_fid = pfid->volatile_fid;
+ return SMB2_read(xid, parms, bytes_read, buf, buf_type);
+}
+
+static int
+smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid,
+ struct cifs_io_parms *parms, unsigned int *written,
+ struct kvec *iov, unsigned long nr_segs)
+{
+
+ parms->persistent_fid = pfid->persistent_fid;
+ parms->volatile_fid = pfid->volatile_fid;
+ return SMB2_write(xid, parms, written, iov, nr_segs);
+}
+
+/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */
+static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse)
+{
+ struct cifsInodeInfo *cifsi;
+ int rc;
+
+ cifsi = CIFS_I(inode);
+
+ /* if file already sparse don't bother setting sparse again */
+ if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse)
+ return true; /* already sparse */
+
+ if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse)
+ return true; /* already not sparse */
+
+ /*
+ * Can't check for sparse support on share the usual way via the
+ * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share
+ * since Samba server doesn't set the flag on the share, yet
+ * supports the set sparse FSCTL and returns sparse correctly
+ * in the file attributes. If we fail setting sparse though we
+ * mark that server does not support sparse files for this share
+ * to avoid repeatedly sending the unsupported fsctl to server
+ * if the file is repeatedly extended.
+ */
+ if (tcon->broken_sparse_sup)
+ return false;
+
+ rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
+ true /* is_fctl */,
+ &setsparse, 1, NULL, NULL);
+ if (rc) {
+ tcon->broken_sparse_sup = true;
+ cifs_dbg(FYI, "set sparse rc = %d\n", rc);
+ return false;
+ }
+
+ if (setsparse)
+ cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE;
+ else
+ cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE);
+
+ return true;
+}
+
+static int
+smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
+{
+ __le64 eof = cpu_to_le64(size);
+ struct inode *inode;
+
+ /*
+ * If extending file more than one page make sparse. Many Linux fs
+ * make files sparse by default when extending via ftruncate
+ */
+ inode = d_inode(cfile->dentry);
+
+ if (!set_alloc && (size > inode->i_size + 8192)) {
+ __u8 set_sparse = 1;
+
+ /* whether set sparse succeeds or not, extend the file */
+ smb2_set_sparse(xid, tcon, cfile, inode, set_sparse);
+ }
+
+ return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, cfile->pid, &eof, false);
+}
+
+static int
+smb2_duplicate_extents(const unsigned int xid,
+ struct cifsFileInfo *srcfile,
+ struct cifsFileInfo *trgtfile, u64 src_off,
+ u64 len, u64 dest_off)
+{
+ int rc;
+ unsigned int ret_data_len;
+ struct duplicate_extents_to_file dup_ext_buf;
+ struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
+
+ /* server fileays advertise duplicate extent support with this flag */
+ if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
+ FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0)
+ return -EOPNOTSUPP;
+
+ dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid;
+ dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid;
+ dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off);
+ dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off);
+ dup_ext_buf.ByteCount = cpu_to_le64(len);
+ cifs_dbg(FYI, "duplicate extents: src off %lld dst off %lld len %lld",
+ src_off, dest_off, len);
+
+ rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
+ if (rc)
+ goto duplicate_extents_out;
+
+ rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
+ trgtfile->fid.volatile_fid,
+ FSCTL_DUPLICATE_EXTENTS_TO_FILE,
+ true /* is_fsctl */,
+ (char *)&dup_ext_buf,
+ sizeof(struct duplicate_extents_to_file),
+ NULL,
+ &ret_data_len);
+
+ if (ret_data_len > 0)
+ cifs_dbg(FYI, "non-zero response length in duplicate extents");
+
+duplicate_extents_out:
+ return rc;
+}
+
+static int
+smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile)
+{
+ return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid);
+}
+
+static int
+smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile)
+{
+ struct fsctl_set_integrity_information_req integr_info;
+ unsigned int ret_data_len;
+
+ integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
+ integr_info.Flags = 0;
+ integr_info.Reserved = 0;
+
+ return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ FSCTL_SET_INTEGRITY_INFORMATION,
+ true /* is_fsctl */,
+ (char *)&integr_info,
+ sizeof(struct fsctl_set_integrity_information_req),
+ NULL,
+ &ret_data_len);
+
+}
+
+/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */
+#define GMT_TOKEN_SIZE 50
+
+/*
+ * Input buffer contains (empty) struct smb_snapshot array with size filled in
+ * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2
+ */
+static int
+smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, void __user *ioc_buf)
+{
+ char *retbuf = NULL;
+ unsigned int ret_data_len = 0;
+ int rc;
+ struct smb_snapshot_array snapshot_in;
+
+ rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ FSCTL_SRV_ENUMERATE_SNAPSHOTS,
+ true /* is_fsctl */,
+ NULL, 0 /* no input data */,
+ (char **)&retbuf,
+ &ret_data_len);
+ cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
+ rc, ret_data_len);
+ if (rc)
+ return rc;
+
+ if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) {
+ /* Fixup buffer */
+ if (copy_from_user(&snapshot_in, ioc_buf,
+ sizeof(struct smb_snapshot_array))) {
+ rc = -EFAULT;
+ kfree(retbuf);
+ return rc;
+ }
+
+ /*
+ * Check for min size, ie not large enough to fit even one GMT
+ * token (snapshot). On the first ioctl some users may pass in
+ * smaller size (or zero) to simply get the size of the array
+ * so the user space caller can allocate sufficient memory
+ * and retry the ioctl again with larger array size sufficient
+ * to hold all of the snapshot GMT tokens on the second try.
+ */
+ if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE)
+ ret_data_len = sizeof(struct smb_snapshot_array);
+
+ /*
+ * We return struct SRV_SNAPSHOT_ARRAY, followed by
+ * the snapshot array (of 50 byte GMT tokens) each
+ * representing an available previous version of the data
+ */
+ if (ret_data_len > (snapshot_in.snapshot_array_size +
+ sizeof(struct smb_snapshot_array)))
+ ret_data_len = snapshot_in.snapshot_array_size +
+ sizeof(struct smb_snapshot_array);
+
+ if (copy_to_user(ioc_buf, retbuf, ret_data_len))
+ rc = -EFAULT;
+ }
+
+ kfree(retbuf);
+ return rc;
+}
+
+static int
+smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *path, struct cifs_sb_info *cifs_sb,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ __le16 *utf16_path;
+ int rc;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
+ oparms.disposition = FILE_OPEN;
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+ oparms.fid = fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+ kfree(utf16_path);
+ if (rc) {
+ cifs_dbg(FYI, "open dir failed rc=%d\n", rc);
+ return rc;
+ }
+
+ srch_inf->entries_in_buffer = 0;
+ srch_inf->index_of_last_entry = 2;
+
+ rc = SMB2_query_directory(xid, tcon, fid->persistent_fid,
+ fid->volatile_fid, 0, srch_inf);
+ if (rc) {
+ cifs_dbg(FYI, "query directory failed rc=%d\n", rc);
+ SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+ }
+ return rc;
+}
+
+static int
+smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ return SMB2_query_directory(xid, tcon, fid->persistent_fid,
+ fid->volatile_fid, 0, srch_inf);
+}
+
+static int
+smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+}
+
+/*
+* If we negotiate SMB2 protocol and get STATUS_PENDING - update
+* the number of credits and return true. Otherwise - return false.
+*/
+static bool
+smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+
+ if (shdr->Status != STATUS_PENDING)
+ return false;
+
+ if (!length) {
+ spin_lock(&server->req_lock);
+ server->credits += le16_to_cpu(shdr->CreditRequest);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ }
+
+ return true;
+}
+
+static bool
+smb2_is_session_expired(char *buf)
+{
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+
+ if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED &&
+ shdr->Status != STATUS_USER_SESSION_DELETED)
+ return false;
+
+ trace_smb3_ses_expired(shdr->TreeId, shdr->SessionId,
+ le16_to_cpu(shdr->Command),
+ le64_to_cpu(shdr->MessageId));
+ cifs_dbg(FYI, "Session expired or deleted\n");
+
+ return true;
+}
+
+static int
+smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
+ struct cifsInodeInfo *cinode)
+{
+ if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
+ return SMB2_lease_break(0, tcon, cinode->lease_key,
+ smb2_get_lease_state(cinode));
+
+ return SMB2_oplock_break(0, tcon, fid->persistent_fid,
+ fid->volatile_fid,
+ CIFS_CACHE_READ(cinode) ? 1 : 0);
+}
+
+static void
+smb2_set_related(struct smb_rqst *rqst)
+{
+ struct smb2_sync_hdr *shdr;
+
+ shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base);
+ shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
+}
+
+char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0};
+
+static void
+smb2_set_next_command(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+ struct smb2_sync_hdr *shdr;
+ unsigned long len = smb_rqst_len(server, rqst);
+
+ /* SMB headers in a compound are 8 byte aligned. */
+ if (len & 7) {
+ rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding;
+ rqst->rq_iov[rqst->rq_nvec].iov_len = 8 - (len & 7);
+ rqst->rq_nvec++;
+ len = smb_rqst_len(server, rqst);
+ }
+
+ shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base);
+ shdr->NextCommand = cpu_to_le32(len);
+}
+
+static int
+smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *buf)
+{
+ struct smb2_query_info_rsp *rsp;
+ struct smb2_fs_full_size_info *info = NULL;
+ struct smb_rqst rqst[3];
+ int resp_buftype[3];
+ struct kvec rsp_iov[3];
+ struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+ struct kvec qi_iov[1];
+ struct kvec close_iov[1];
+ struct cifs_ses *ses = tcon->ses;
+ struct TCP_Server_Info *server = ses->server;
+ __le16 srch_path = 0; /* Null - open root of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+ int flags = 0;
+ int rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(rqst, 0, sizeof(rqst));
+ memset(resp_buftype, 0, sizeof(resp_buftype));
+ memset(rsp_iov, 0, sizeof(rsp_iov));
+
+ memset(&open_iov, 0, sizeof(open_iov));
+ rqst[0].rq_iov = open_iov;
+ rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &srch_path);
+ if (rc)
+ goto qfs_exit;
+ smb2_set_next_command(server, &rqst[0]);
+
+ memset(&qi_iov, 0, sizeof(qi_iov));
+ rqst[1].rq_iov = qi_iov;
+ rqst[1].rq_nvec = 1;
+
+ rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID,
+ FS_FULL_SIZE_INFORMATION,
+ SMB2_O_INFO_FILESYSTEM, 0,
+ sizeof(struct smb2_fs_full_size_info));
+ if (rc)
+ goto qfs_exit;
+ smb2_set_next_command(server, &rqst[1]);
+ smb2_set_related(&rqst[1]);
+
+ memset(&close_iov, 0, sizeof(close_iov));
+ rqst[2].rq_iov = close_iov;
+ rqst[2].rq_nvec = 1;
+
+ rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
+ if (rc)
+ goto qfs_exit;
+ smb2_set_related(&rqst[2]);
+
+ rc = compound_send_recv(xid, ses, flags, 3, rqst,
+ resp_buftype, rsp_iov);
+ if (rc)
+ goto qfs_exit;
+
+ rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
+ buf->f_type = SMB2_MAGIC_NUMBER;
+ info = (struct smb2_fs_full_size_info *)(
+ le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
+ rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength),
+ &rsp_iov[1],
+ sizeof(struct smb2_fs_full_size_info));
+ if (!rc)
+ smb2_copy_fs_info_to_kstatfs(info, buf);
+
+qfs_exit:
+ SMB2_open_free(&rqst[0]);
+ SMB2_query_info_free(&rqst[1]);
+ SMB2_close_free(&rqst[2]);
+ free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+ free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+ free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
+ return rc;
+}
+
+static int
+smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *buf)
+{
+ int rc;
+ __le16 srch_path = 0; /* Null - open root of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+
+ if (!tcon->posix_extensions)
+ return smb2_queryfs(xid, tcon, buf);
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, buf);
+ buf->f_type = SMB2_MAGIC_NUMBER;
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ return rc;
+}
+
+static bool
+smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
+{
+ return ob1->fid.persistent_fid == ob2->fid.persistent_fid &&
+ ob1->fid.volatile_fid == ob2->fid.volatile_fid;
+}
+
+static int
+smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
+ __u64 length, __u32 type, int lock, int unlock, bool wait)
+{
+ if (unlock && !lock)
+ type = SMB2_LOCKFLAG_UNLOCK;
+ return SMB2_lock(xid, tlink_tcon(cfile->tlink),
+ cfile->fid.persistent_fid, cfile->fid.volatile_fid,
+ current->tgid, length, offset, type, wait);
+}
+
+static void
+smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid)
+{
+ memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE);
+}
+
+static void
+smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
+{
+ memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
+}
+
+static void
+smb2_new_lease_key(struct cifs_fid *fid)
+{
+ generate_random_uuid(fid->lease_key);
+}
+
+static int
+smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+ const char *search_name,
+ struct dfs_info3_param **target_nodes,
+ unsigned int *num_of_nodes,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc;
+ __le16 *utf16_path = NULL;
+ int utf16_path_len = 0;
+ struct cifs_tcon *tcon;
+ struct fsctl_get_dfs_referral_req *dfs_req = NULL;
+ struct get_dfs_referral_rsp *dfs_rsp = NULL;
+ u32 dfs_req_size = 0, dfs_rsp_size = 0;
+
+ cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name);
+
+ /*
+ * Try to use the IPC tcon, otherwise just use any
+ */
+ tcon = ses->tcon_ipc;
+ if (tcon == NULL) {
+ spin_lock(&cifs_tcp_ses_lock);
+ tcon = list_first_entry_or_null(&ses->tcon_list,
+ struct cifs_tcon,
+ tcon_list);
+ if (tcon)
+ tcon->tc_count++;
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+
+ if (tcon == NULL) {
+ cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n",
+ ses);
+ rc = -ENOTCONN;
+ goto out;
+ }
+
+ utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX,
+ &utf16_path_len,
+ nls_codepage, remap);
+ if (!utf16_path) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ dfs_req_size = sizeof(*dfs_req) + utf16_path_len;
+ dfs_req = kzalloc(dfs_req_size, GFP_KERNEL);
+ if (!dfs_req) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Highest DFS referral version understood */
+ dfs_req->MaxReferralLevel = DFS_VERSION;
+
+ /* Path to resolve in an UTF-16 null-terminated string */
+ memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
+
+ do {
+ rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+ FSCTL_DFS_GET_REFERRALS,
+ true /* is_fsctl */,
+ (char *)dfs_req, dfs_req_size,
+ (char **)&dfs_rsp, &dfs_rsp_size);
+ } while (rc == -EAGAIN);
+
+ if (rc) {
+ if ((rc != -ENOENT) && (rc != -EOPNOTSUPP))
+ cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
+ goto out;
+ }
+
+ rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size,
+ num_of_nodes, target_nodes,
+ nls_codepage, remap, search_name,
+ true /* is_unicode */);
+ if (rc) {
+ cifs_dbg(VFS, "parse error in smb2_get_dfs_refer rc=%d\n", rc);
+ goto out;
+ }
+
+ out:
+ if (tcon && !tcon->ipc) {
+ /* ipc tcons are not refcounted */
+ spin_lock(&cifs_tcp_ses_lock);
+ tcon->tc_count--;
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+ kfree(utf16_path);
+ kfree(dfs_req);
+ kfree(dfs_rsp);
+ return rc;
+}
+#define SMB2_SYMLINK_STRUCT_SIZE \
+ (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
+
+static int
+smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, char **target_path,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
+ struct kvec err_iov = {NULL, 0};
+ struct smb2_err_rsp *err_buf = NULL;
+ int resp_buftype;
+ struct smb2_symlink_err_rsp *symlink;
+ unsigned int sub_len;
+ unsigned int sub_offset;
+ unsigned int print_len;
+ unsigned int print_offset;
+
+ cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
+
+ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
+ &resp_buftype);
+ if (!rc)
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ if (!rc || !err_iov.iov_base) {
+ rc = -ENOENT;
+ goto free_path;
+ }
+
+ err_buf = err_iov.iov_base;
+ if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
+ err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) {
+ rc = -ENOENT;
+ goto querty_exit;
+ }
+
+ /* open must fail on symlink - reset rc */
+ rc = 0;
+ symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
+ sub_len = le16_to_cpu(symlink->SubstituteNameLength);
+ sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
+ print_len = le16_to_cpu(symlink->PrintNameLength);
+ print_offset = le16_to_cpu(symlink->PrintNameOffset);
+
+ if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
+ rc = -ENOENT;
+ goto querty_exit;
+ }
+
+ if (err_iov.iov_len <
+ SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
+ rc = -ENOENT;
+ goto querty_exit;
+ }
+
+ *target_path = cifs_strndup_from_utf16(
+ (char *)symlink->PathBuffer + sub_offset,
+ sub_len, true, cifs_sb->local_nls);
+ if (!(*target_path)) {
+ rc = -ENOMEM;
+ goto querty_exit;
+ }
+ convert_delimiter(*target_path, '/');
+ cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
+
+ querty_exit:
+ free_rsp_buf(resp_buftype, err_buf);
+ free_path:
+ kfree(utf16_path);
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_ACL
+static struct cifs_ntsd *
+get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
+ const struct cifs_fid *cifsfid, u32 *pacllen)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ unsigned int xid;
+ int rc = -EOPNOTSUPP;
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+
+ if (IS_ERR(tlink))
+ return ERR_CAST(tlink);
+
+ xid = get_xid();
+ cifs_dbg(FYI, "trying to get acl\n");
+
+ rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid,
+ cifsfid->volatile_fid, (void **)&pntsd, pacllen);
+ free_xid(xid);
+
+ cifs_put_tlink(tlink);
+
+ cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
+ if (rc)
+ return ERR_PTR(rc);
+ return pntsd;
+
+}
+
+static struct cifs_ntsd *
+get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
+ const char *path, u32 *pacllen)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ unsigned int xid;
+ int rc;
+ struct cifs_tcon *tcon;
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ __le16 *utf16_path;
+
+ cifs_dbg(FYI, "get smb3 acl for path %s\n", path);
+ if (IS_ERR(tlink))
+ return ERR_CAST(tlink);
+
+ tcon = tlink_tcon(tlink);
+ xid = get_xid();
+
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path) {
+ rc = -ENOMEM;
+ free_xid(xid);
+ return ERR_PTR(rc);
+ }
+
+ oparms.tcon = tcon;
+ oparms.desired_access = READ_CONTROL;
+ oparms.disposition = FILE_OPEN;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+ kfree(utf16_path);
+ if (!rc) {
+ rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
+ fid.volatile_fid, (void **)&pntsd, pacllen);
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ }
+
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+
+ cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
+ if (rc)
+ return ERR_PTR(rc);
+ return pntsd;
+}
+
+#ifdef CONFIG_CIFS_ACL
+static int
+set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
+ struct inode *inode, const char *path, int aclflag)
+{
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ unsigned int xid;
+ int rc, access_flags = 0;
+ struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
+ struct cifs_fid fid;
+ struct cifs_open_parms oparms;
+ __le16 *utf16_path;
+
+ cifs_dbg(FYI, "set smb3 acl for path %s\n", path);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+
+ tcon = tlink_tcon(tlink);
+ xid = get_xid();
+
+ if (backup_cred(cifs_sb))
+ oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
+ else
+ oparms.create_options = 0;
+
+ if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
+ access_flags = WRITE_OWNER;
+ else
+ access_flags = WRITE_DAC;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path) {
+ rc = -ENOMEM;
+ free_xid(xid);
+ return rc;
+ }
+
+ oparms.tcon = tcon;
+ oparms.desired_access = access_flags;
+ oparms.disposition = FILE_OPEN;
+ oparms.path = path;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+ kfree(utf16_path);
+ if (!rc) {
+ rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
+ fid.volatile_fid, pnntsd, acllen, aclflag);
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+ }
+
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return rc;
+}
+#endif /* CIFS_ACL */
+
+/* Retrieve an ACL from the server */
+static struct cifs_ntsd *
+get_smb2_acl(struct cifs_sb_info *cifs_sb,
+ struct inode *inode, const char *path,
+ u32 *pacllen)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ struct cifsFileInfo *open_file = NULL;
+
+ if (inode)
+ open_file = find_readable_file(CIFS_I(inode), true);
+ if (!open_file)
+ return get_smb2_acl_by_path(cifs_sb, path, pacllen);
+
+ pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen);
+ cifsFileInfo_put(open_file);
+ return pntsd;
+}
+#endif
+
+static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
+ loff_t offset, loff_t len, bool keep_size)
+{
+ struct inode *inode;
+ struct cifsInodeInfo *cifsi;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct file_zero_data_information fsctl_buf;
+ long rc;
+ unsigned int xid;
+
+ xid = get_xid();
+
+ inode = d_inode(cfile->dentry);
+ cifsi = CIFS_I(inode);
+
+ /*
+ * We zero the range through ioctl, so we need remove the page caches
+ * first, otherwise the data may be inconsistent with the server.
+ */
+ truncate_pagecache_range(inode, offset, offset + len - 1);
+
+ /* if file not oplocked can't be sure whether asking to extend size */
+ if (!CIFS_CACHE_READ(cifsi))
+ if (keep_size == false) {
+ rc = -EOPNOTSUPP;
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * Must check if file sparse since fallocate -z (zero range) assumes
+ * non-sparse allocation
+ */
+ if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) {
+ rc = -EOPNOTSUPP;
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * need to make sure we are not asked to extend the file since the SMB3
+ * fsctl does not change the file size. In the future we could change
+ * this to zero the first part of the range then set the file size
+ * which for a non sparse file would zero the newly extended range
+ */
+ if (keep_size == false)
+ if (i_size_read(inode) < offset + len) {
+ rc = -EOPNOTSUPP;
+ free_xid(xid);
+ return rc;
+ }
+
+ cifs_dbg(FYI, "offset %lld len %lld", offset, len);
+
+ fsctl_buf.FileOffset = cpu_to_le64(offset);
+ fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
+
+ rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
+ true /* is_fctl */, (char *)&fsctl_buf,
+ sizeof(struct file_zero_data_information), NULL, NULL);
+ free_xid(xid);
+ return rc;
+}
+
+static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
+ loff_t offset, loff_t len)
+{
+ struct inode *inode;
+ struct cifsInodeInfo *cifsi;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct file_zero_data_information fsctl_buf;
+ long rc;
+ unsigned int xid;
+ __u8 set_sparse = 1;
+
+ xid = get_xid();
+
+ inode = d_inode(cfile->dentry);
+ cifsi = CIFS_I(inode);
+
+ /* Need to make file sparse, if not already, before freeing range. */
+ /* Consider adding equivalent for compressed since it could also work */
+ if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
+ rc = -EOPNOTSUPP;
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * We implement the punch hole through ioctl, so we need remove the page
+ * caches first, otherwise the data may be inconsistent with the server.
+ */
+ truncate_pagecache_range(inode, offset, offset + len - 1);
+
+ cifs_dbg(FYI, "offset %lld len %lld", offset, len);
+
+ fsctl_buf.FileOffset = cpu_to_le64(offset);
+ fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
+
+ rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
+ true /* is_fctl */, (char *)&fsctl_buf,
+ sizeof(struct file_zero_data_information), NULL, NULL);
+ free_xid(xid);
+ return rc;
+}
+
+static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
+ loff_t off, loff_t len, bool keep_size)
+{
+ struct inode *inode;
+ struct cifsInodeInfo *cifsi;
+ struct cifsFileInfo *cfile = file->private_data;
+ long rc = -EOPNOTSUPP;
+ unsigned int xid;
+
+ xid = get_xid();
+
+ inode = d_inode(cfile->dentry);
+ cifsi = CIFS_I(inode);
+
+ /* if file not oplocked can't be sure whether asking to extend size */
+ if (!CIFS_CACHE_READ(cifsi))
+ if (keep_size == false) {
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * Files are non-sparse by default so falloc may be a no-op
+ * Must check if file sparse. If not sparse, and not extending
+ * then no need to do anything since file already allocated
+ */
+ if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
+ if (keep_size == true)
+ rc = 0;
+ /* check if extending file */
+ else if (i_size_read(inode) >= off + len)
+ /* not extending file and already not sparse */
+ rc = 0;
+ /* BB: in future add else clause to extend file */
+ else
+ rc = -EOPNOTSUPP;
+ free_xid(xid);
+ return rc;
+ }
+
+ if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
+ /*
+ * Check if falloc starts within first few pages of file
+ * and ends within a few pages of the end of file to
+ * ensure that most of file is being forced to be
+ * fallocated now. If so then setting whole file sparse
+ * ie potentially making a few extra pages at the beginning
+ * or end of the file non-sparse via set_sparse is harmless.
+ */
+ if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
+ rc = -EOPNOTSUPP;
+ free_xid(xid);
+ return rc;
+ }
+
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
+ }
+ /* BB: else ... in future add code to extend file and set sparse */
+
+
+ free_xid(xid);
+ return rc;
+}
+
+
+static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
+ loff_t off, loff_t len)
+{
+ /* KEEP_SIZE already checked for by do_fallocate */
+ if (mode & FALLOC_FL_PUNCH_HOLE)
+ return smb3_punch_hole(file, tcon, off, len);
+ else if (mode & FALLOC_FL_ZERO_RANGE) {
+ if (mode & FALLOC_FL_KEEP_SIZE)
+ return smb3_zero_range(file, tcon, off, len, true);
+ return smb3_zero_range(file, tcon, off, len, false);
+ } else if (mode == FALLOC_FL_KEEP_SIZE)
+ return smb3_simple_falloc(file, tcon, off, len, true);
+ else if (mode == 0)
+ return smb3_simple_falloc(file, tcon, off, len, false);
+
+ return -EOPNOTSUPP;
+}
+
+static void
+smb2_downgrade_oplock(struct TCP_Server_Info *server,
+ struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ server->ops->set_oplock_level(cinode, oplock, 0, NULL);
+}
+
+static void
+smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache);
+
+static void
+smb3_downgrade_oplock(struct TCP_Server_Info *server,
+ struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ unsigned int old_state = cinode->oplock;
+ unsigned int old_epoch = cinode->epoch;
+ unsigned int new_state;
+
+ if (epoch > old_epoch) {
+ smb21_set_oplock_level(cinode, oplock, 0, NULL);
+ cinode->epoch = epoch;
+ }
+
+ new_state = cinode->oplock;
+ *purge_cache = false;
+
+ if ((old_state & CIFS_CACHE_READ_FLG) != 0 &&
+ (new_state & CIFS_CACHE_READ_FLG) == 0)
+ *purge_cache = true;
+ else if (old_state == new_state && (epoch - old_epoch > 1))
+ *purge_cache = true;
+}
+
+static void
+smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ oplock &= 0xFF;
+ if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
+ return;
+ if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
+ cinode->oplock = CIFS_CACHE_RHW_FLG;
+ cifs_dbg(FYI, "Batch Oplock granted on inode %p\n",
+ &cinode->vfs_inode);
+ } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+ cinode->oplock = CIFS_CACHE_RW_FLG;
+ cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
+ &cinode->vfs_inode);
+ } else if (oplock == SMB2_OPLOCK_LEVEL_II) {
+ cinode->oplock = CIFS_CACHE_READ_FLG;
+ cifs_dbg(FYI, "Level II Oplock granted on inode %p\n",
+ &cinode->vfs_inode);
+ } else
+ cinode->oplock = 0;
+}
+
+static void
+smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ char message[5] = {0};
+ unsigned int new_oplock = 0;
+
+ oplock &= 0xFF;
+ if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
+ return;
+
+ /* Check if the server granted an oplock rather than a lease */
+ if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE)
+ return smb2_set_oplock_level(cinode, oplock, epoch,
+ purge_cache);
+
+ if (oplock & SMB2_LEASE_READ_CACHING_HE) {
+ new_oplock |= CIFS_CACHE_READ_FLG;
+ strcat(message, "R");
+ }
+ if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) {
+ new_oplock |= CIFS_CACHE_HANDLE_FLG;
+ strcat(message, "H");
+ }
+ if (oplock & SMB2_LEASE_WRITE_CACHING_HE) {
+ new_oplock |= CIFS_CACHE_WRITE_FLG;
+ strcat(message, "W");
+ }
+ if (!new_oplock)
+ strncpy(message, "None", sizeof(message));
+
+ cinode->oplock = new_oplock;
+ cifs_dbg(FYI, "%s Lease granted on inode %p\n", message,
+ &cinode->vfs_inode);
+}
+
+static void
+smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
+ unsigned int epoch, bool *purge_cache)
+{
+ unsigned int old_oplock = cinode->oplock;
+
+ smb21_set_oplock_level(cinode, oplock, epoch, purge_cache);
+
+ if (purge_cache) {
+ *purge_cache = false;
+ if (old_oplock == CIFS_CACHE_READ_FLG) {
+ if (cinode->oplock == CIFS_CACHE_READ_FLG &&
+ (epoch - cinode->epoch > 0))
+ *purge_cache = true;
+ else if (cinode->oplock == CIFS_CACHE_RH_FLG &&
+ (epoch - cinode->epoch > 1))
+ *purge_cache = true;
+ else if (cinode->oplock == CIFS_CACHE_RHW_FLG &&
+ (epoch - cinode->epoch > 1))
+ *purge_cache = true;
+ else if (cinode->oplock == 0 &&
+ (epoch - cinode->epoch > 0))
+ *purge_cache = true;
+ } else if (old_oplock == CIFS_CACHE_RH_FLG) {
+ if (cinode->oplock == CIFS_CACHE_RH_FLG &&
+ (epoch - cinode->epoch > 0))
+ *purge_cache = true;
+ else if (cinode->oplock == CIFS_CACHE_RHW_FLG &&
+ (epoch - cinode->epoch > 1))
+ *purge_cache = true;
+ }
+ cinode->epoch = epoch;
+ }
+}
+
+static bool
+smb2_is_read_op(__u32 oplock)
+{
+ return oplock == SMB2_OPLOCK_LEVEL_II;
+}
+
+static bool
+smb21_is_read_op(__u32 oplock)
+{
+ return (oplock & SMB2_LEASE_READ_CACHING_HE) &&
+ !(oplock & SMB2_LEASE_WRITE_CACHING_HE);
+}
+
+static __le32
+map_oplock_to_lease(u8 oplock)
+{
+ if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
+ return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING;
+ else if (oplock == SMB2_OPLOCK_LEVEL_II)
+ return SMB2_LEASE_READ_CACHING;
+ else if (oplock == SMB2_OPLOCK_LEVEL_BATCH)
+ return SMB2_LEASE_HANDLE_CACHING | SMB2_LEASE_READ_CACHING |
+ SMB2_LEASE_WRITE_CACHING;
+ return 0;
+}
+
+static char *
+smb2_create_lease_buf(u8 *lease_key, u8 oplock)
+{
+ struct create_lease *buf;
+
+ buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE);
+ buf->lcontext.LeaseState = map_oplock_to_lease(oplock);
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_lease, lcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context));
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_lease, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */
+ buf->Name[0] = 'R';
+ buf->Name[1] = 'q';
+ buf->Name[2] = 'L';
+ buf->Name[3] = 's';
+ return (char *)buf;
+}
+
+static char *
+smb3_create_lease_buf(u8 *lease_key, u8 oplock)
+{
+ struct create_lease_v2 *buf;
+
+ buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE);
+ buf->lcontext.LeaseState = map_oplock_to_lease(oplock);
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_lease_v2, lcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2));
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_lease_v2, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */
+ buf->Name[0] = 'R';
+ buf->Name[1] = 'q';
+ buf->Name[2] = 'L';
+ buf->Name[3] = 's';
+ return (char *)buf;
+}
+
+static __u8
+smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key)
+{
+ struct create_lease *lc = (struct create_lease *)buf;
+
+ *epoch = 0; /* not used */
+ if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
+ return SMB2_OPLOCK_LEVEL_NOCHANGE;
+ return le32_to_cpu(lc->lcontext.LeaseState);
+}
+
+static __u8
+smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key)
+{
+ struct create_lease_v2 *lc = (struct create_lease_v2 *)buf;
+
+ *epoch = le16_to_cpu(lc->lcontext.Epoch);
+ if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
+ return SMB2_OPLOCK_LEVEL_NOCHANGE;
+ if (lease_key)
+ memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
+ return le32_to_cpu(lc->lcontext.LeaseState);
+}
+
+static unsigned int
+smb2_wp_retry_size(struct inode *inode)
+{
+ return min_t(unsigned int, CIFS_SB(inode->i_sb)->wsize,
+ SMB2_MAX_BUFFER_SIZE);
+}
+
+static bool
+smb2_dir_needs_close(struct cifsFileInfo *cfile)
+{
+ return !cfile->invalidHandle;
+}
+
+static void
+fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
+ struct smb_rqst *old_rq)
+{
+ struct smb2_sync_hdr *shdr =
+ (struct smb2_sync_hdr *)old_rq->rq_iov[0].iov_base;
+
+ memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr));
+ tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM;
+ tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len);
+ tr_hdr->Flags = cpu_to_le16(0x01);
+ get_random_bytes(&tr_hdr->Nonce, SMB3_AES128CMM_NONCE);
+ memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8);
+}
+
+/* We can not use the normal sg_set_buf() as we will sometimes pass a
+ * stack object as buf.
+ */
+static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
+ unsigned int buflen)
+{
+ void *addr;
+ /*
+ * VMAP_STACK (at least) puts stack into the vmalloc address space
+ */
+ if (is_vmalloc_addr(buf))
+ addr = vmalloc_to_page(buf);
+ else
+ addr = virt_to_page(buf);
+ sg_set_page(sg, addr, buflen, offset_in_page(buf));
+}
+
+/* Assumes the first rqst has a transform header as the first iov.
+ * I.e.
+ * rqst[0].rq_iov[0] is transform header
+ * rqst[0].rq_iov[1+] data to be encrypted/decrypted
+ * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
+ */
+static struct scatterlist *
+init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
+{
+ unsigned int sg_len;
+ struct scatterlist *sg;
+ unsigned int i;
+ unsigned int j;
+ unsigned int idx = 0;
+ int skip;
+
+ sg_len = 1;
+ for (i = 0; i < num_rqst; i++)
+ sg_len += rqst[i].rq_nvec + rqst[i].rq_npages;
+
+ sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
+ if (!sg)
+ return NULL;
+
+ sg_init_table(sg, sg_len);
+ for (i = 0; i < num_rqst; i++) {
+ for (j = 0; j < rqst[i].rq_nvec; j++) {
+ /*
+ * The first rqst has a transform header where the
+ * first 20 bytes are not part of the encrypted blob
+ */
+ skip = (i == 0) && (j == 0) ? 20 : 0;
+ smb2_sg_set_buf(&sg[idx++],
+ rqst[i].rq_iov[j].iov_base + skip,
+ rqst[i].rq_iov[j].iov_len - skip);
+ }
+
+ for (j = 0; j < rqst[i].rq_npages; j++) {
+ unsigned int len, offset;
+
+ rqst_page_get_length(&rqst[i], j, &len, &offset);
+ sg_set_page(&sg[idx++], rqst[i].rq_pages[j], len, offset);
+ }
+ }
+ smb2_sg_set_buf(&sg[idx], sign, SMB2_SIGNATURE_SIZE);
+ return sg;
+}
+
+static int
+smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
+{
+ struct cifs_ses *ses;
+ u8 *ses_enc_key;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ if (ses->Suid != ses_id)
+ continue;
+ ses_enc_key = enc ? ses->smb3encryptionkey :
+ ses->smb3decryptionkey;
+ memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return 0;
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ return -EAGAIN;
+}
+/*
+ * Encrypt or decrypt @rqst message. @rqst[0] has the following format:
+ * iov[0] - transform header (associate data),
+ * iov[1-N] - SMB2 header and pages - data to encrypt.
+ * On success return encrypted data in iov[1-N] and pages, leave iov[0]
+ * untouched.
+ */
+static int
+crypt_message(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *rqst, int enc)
+{
+ struct smb2_transform_hdr *tr_hdr =
+ (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base;
+ unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
+ int rc = 0;
+ struct scatterlist *sg;
+ u8 sign[SMB2_SIGNATURE_SIZE] = {};
+ u8 key[SMB3_SIGN_KEY_SIZE];
+ struct aead_request *req;
+ char *iv;
+ unsigned int iv_len;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct crypto_aead *tfm;
+ unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
+
+ rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not get %scryption key\n", __func__,
+ enc ? "en" : "de");
+ return rc;
+ }
+
+ rc = smb3_crypto_aead_allocate(server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
+ return rc;
+ }
+
+ tfm = enc ? server->secmech.ccmaesencrypt :
+ server->secmech.ccmaesdecrypt;
+ rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
+ return rc;
+ }
+
+ rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc);
+ return rc;
+ }
+
+ req = aead_request_alloc(tfm, GFP_KERNEL);
+ if (!req) {
+ cifs_dbg(VFS, "%s: Failed to alloc aead request", __func__);
+ return -ENOMEM;
+ }
+
+ if (!enc) {
+ memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
+ crypt_len += SMB2_SIGNATURE_SIZE;
+ }
+
+ sg = init_sg(num_rqst, rqst, sign);
+ if (!sg) {
+ cifs_dbg(VFS, "%s: Failed to init sg", __func__);
+ rc = -ENOMEM;
+ goto free_req;
+ }
+
+ iv_len = crypto_aead_ivsize(tfm);
+ iv = kzalloc(iv_len, GFP_KERNEL);
+ if (!iv) {
+ cifs_dbg(VFS, "%s: Failed to alloc IV", __func__);
+ rc = -ENOMEM;
+ goto free_sg;
+ }
+ iv[0] = 3;
+ memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CMM_NONCE);
+
+ aead_request_set_crypt(req, sg, sg, crypt_len, iv);
+ aead_request_set_ad(req, assoc_data_len);
+
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ rc = crypto_wait_req(enc ? crypto_aead_encrypt(req)
+ : crypto_aead_decrypt(req), &wait);
+
+ if (!rc && enc)
+ memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
+
+ kfree(iv);
+free_sg:
+ kfree(sg);
+free_req:
+ kfree(req);
+ return rc;
+}
+
+void
+smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst)
+{
+ int i, j;
+
+ for (i = 0; i < num_rqst; i++) {
+ if (rqst[i].rq_pages) {
+ for (j = rqst[i].rq_npages - 1; j >= 0; j--)
+ put_page(rqst[i].rq_pages[j]);
+ kfree(rqst[i].rq_pages);
+ }
+ }
+}
+
+/*
+ * This function will initialize new_rq and encrypt the content.
+ * The first entry, new_rq[0], only contains a single iov which contains
+ * a smb2_transform_hdr and is pre-allocated by the caller.
+ * This function then populates new_rq[1+] with the content from olq_rq[0+].
+ *
+ * The end result is an array of smb_rqst structures where the first structure
+ * only contains a single iov for the transform header which we then can pass
+ * to crypt_message().
+ *
+ * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller
+ * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests
+ */
+static int
+smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *new_rq, struct smb_rqst *old_rq)
+{
+ struct page **pages;
+ struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base;
+ unsigned int npages;
+ unsigned int orig_len = 0;
+ int i, j;
+ int rc = -ENOMEM;
+
+ for (i = 1; i < num_rqst; i++) {
+ npages = old_rq[i - 1].rq_npages;
+ pages = kmalloc_array(npages, sizeof(struct page *),
+ GFP_KERNEL);
+ if (!pages)
+ goto err_free;
+
+ new_rq[i].rq_pages = pages;
+ new_rq[i].rq_npages = npages;
+ new_rq[i].rq_offset = old_rq[i - 1].rq_offset;
+ new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz;
+ new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz;
+ new_rq[i].rq_iov = old_rq[i - 1].rq_iov;
+ new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec;
+
+ orig_len += smb_rqst_len(server, &old_rq[i - 1]);
+
+ for (j = 0; j < npages; j++) {
+ pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
+ if (!pages[j])
+ goto err_free;
+ }
+
+ /* copy pages form the old */
+ for (j = 0; j < npages; j++) {
+ char *dst, *src;
+ unsigned int offset, len;
+
+ rqst_page_get_length(&new_rq[i], j, &len, &offset);
+
+ dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset;
+ src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset;
+
+ memcpy(dst, src, len);
+ kunmap(new_rq[i].rq_pages[j]);
+ kunmap(old_rq[i - 1].rq_pages[j]);
+ }
+ }
+
+ /* fill the 1st iov with a transform header */
+ fill_transform_hdr(tr_hdr, orig_len, old_rq);
+
+ rc = crypt_message(server, num_rqst, new_rq, 1);
+ cifs_dbg(FYI, "encrypt message returned %d", rc);
+ if (rc)
+ goto err_free;
+
+ return rc;
+
+err_free:
+ smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]);
+ return rc;
+}
+
+static int
+smb3_is_transform_hdr(void *buf)
+{
+ struct smb2_transform_hdr *trhdr = buf;
+
+ return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM;
+}
+
+static int
+decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
+ unsigned int buf_data_size, struct page **pages,
+ unsigned int npages, unsigned int page_data_size)
+{
+ struct kvec iov[2];
+ struct smb_rqst rqst = {NULL};
+ int rc;
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = sizeof(struct smb2_transform_hdr);
+ iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr);
+ iov[1].iov_len = buf_data_size;
+
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+ rqst.rq_pages = pages;
+ rqst.rq_npages = npages;
+ rqst.rq_pagesz = PAGE_SIZE;
+ rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE;
+
+ rc = crypt_message(server, 1, &rqst, 0);
+ cifs_dbg(FYI, "decrypt message returned %d\n", rc);
+
+ if (rc)
+ return rc;
+
+ memmove(buf, iov[1].iov_base, buf_data_size);
+
+ server->total_read = buf_data_size + page_data_size;
+
+ return rc;
+}
+
+static int
+read_data_into_pages(struct TCP_Server_Info *server, struct page **pages,
+ unsigned int npages, unsigned int len)
+{
+ int i;
+ int length;
+
+ for (i = 0; i < npages; i++) {
+ struct page *page = pages[i];
+ size_t n;
+
+ n = len;
+ if (len >= PAGE_SIZE) {
+ /* enough data to fill the page */
+ n = PAGE_SIZE;
+ len -= n;
+ } else {
+ zero_user(page, len, PAGE_SIZE - len);
+ len = 0;
+ }
+ length = cifs_read_page_from_socket(server, page, 0, n);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+ }
+
+ return 0;
+}
+
+static int
+init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size,
+ unsigned int cur_off, struct bio_vec **page_vec)
+{
+ struct bio_vec *bvec;
+ int i;
+
+ bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL);
+ if (!bvec)
+ return -ENOMEM;
+
+ for (i = 0; i < npages; i++) {
+ bvec[i].bv_page = pages[i];
+ bvec[i].bv_offset = (i == 0) ? cur_off : 0;
+ bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size);
+ data_size -= bvec[i].bv_len;
+ }
+
+ if (data_size != 0) {
+ cifs_dbg(VFS, "%s: something went wrong\n", __func__);
+ kfree(bvec);
+ return -EIO;
+ }
+
+ *page_vec = bvec;
+ return 0;
+}
+
+static int
+handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
+ char *buf, unsigned int buf_len, struct page **pages,
+ unsigned int npages, unsigned int page_data_size)
+{
+ unsigned int data_offset;
+ unsigned int data_len;
+ unsigned int cur_off;
+ unsigned int cur_page_idx;
+ unsigned int pad_len;
+ struct cifs_readdata *rdata = mid->callback_data;
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+ struct bio_vec *bvec = NULL;
+ struct iov_iter iter;
+ struct kvec iov;
+ int length;
+ bool use_rdma_mr = false;
+
+ if (shdr->Command != SMB2_READ) {
+ cifs_dbg(VFS, "only big read responses are supported\n");
+ return -ENOTSUPP;
+ }
+
+ if (server->ops->is_session_expired &&
+ server->ops->is_session_expired(buf)) {
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -1;
+ }
+
+ if (server->ops->is_status_pending &&
+ server->ops->is_status_pending(buf, server, 0))
+ return -1;
+
+ /* set up first two iov to get credits */
+ rdata->iov[0].iov_base = buf;
+ rdata->iov[0].iov_len = 0;
+ rdata->iov[1].iov_base = buf;
+ rdata->iov[1].iov_len =
+ min_t(unsigned int, buf_len, server->vals->read_rsp_size);
+ cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
+ rdata->iov[0].iov_base, rdata->iov[0].iov_len);
+ cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
+ rdata->iov[1].iov_base, rdata->iov[1].iov_len);
+
+ rdata->result = server->ops->map_error(buf, true);
+ if (rdata->result != 0) {
+ cifs_dbg(FYI, "%s: server returned error %d\n",
+ __func__, rdata->result);
+ /* normal error on read response */
+ dequeue_mid(mid, false);
+ return 0;
+ }
+
+ data_offset = server->ops->read_data_offset(buf);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ use_rdma_mr = rdata->mr;
+#endif
+ data_len = server->ops->read_data_length(buf, use_rdma_mr);
+
+ if (data_offset < server->vals->read_rsp_size) {
+ /*
+ * win2k8 sometimes sends an offset of 0 when the read
+ * is beyond the EOF. Treat it as if the data starts just after
+ * the header.
+ */
+ cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
+ __func__, data_offset);
+ data_offset = server->vals->read_rsp_size;
+ } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
+ /* data_offset is beyond the end of smallbuf */
+ cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
+ __func__, data_offset);
+ rdata->result = -EIO;
+ dequeue_mid(mid, rdata->result);
+ return 0;
+ }
+
+ pad_len = data_offset - server->vals->read_rsp_size;
+
+ if (buf_len <= data_offset) {
+ /* read response payload is in pages */
+ cur_page_idx = pad_len / PAGE_SIZE;
+ cur_off = pad_len % PAGE_SIZE;
+
+ if (cur_page_idx != 0) {
+ /* data offset is beyond the 1st page of response */
+ cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
+ __func__, data_offset);
+ rdata->result = -EIO;
+ dequeue_mid(mid, rdata->result);
+ return 0;
+ }
+
+ if (data_len > page_data_size - pad_len) {
+ /* data_len is corrupt -- discard frame */
+ rdata->result = -EIO;
+ dequeue_mid(mid, rdata->result);
+ return 0;
+ }
+
+ rdata->result = init_read_bvec(pages, npages, page_data_size,
+ cur_off, &bvec);
+ if (rdata->result != 0) {
+ dequeue_mid(mid, rdata->result);
+ return 0;
+ }
+
+ iov_iter_bvec(&iter, WRITE | ITER_BVEC, bvec, npages, data_len);
+ } else if (buf_len >= data_offset + data_len) {
+ /* read response payload is in buf */
+ WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
+ iov.iov_base = buf + data_offset;
+ iov.iov_len = data_len;
+ iov_iter_kvec(&iter, WRITE | ITER_KVEC, &iov, 1, data_len);
+ } else {
+ /* read response payload cannot be in both buf and pages */
+ WARN_ONCE(1, "buf can not contain only a part of read data");
+ rdata->result = -EIO;
+ dequeue_mid(mid, rdata->result);
+ return 0;
+ }
+
+ length = rdata->copy_into_pages(server, rdata, &iter);
+
+ kfree(bvec);
+
+ if (length < 0)
+ return length;
+
+ dequeue_mid(mid, false);
+ return length;
+}
+
+static int
+receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
+{
+ char *buf = server->smallbuf;
+ struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
+ unsigned int npages;
+ struct page **pages;
+ unsigned int len;
+ unsigned int buflen = server->pdu_size;
+ int rc;
+ int i = 0;
+
+ len = min_t(unsigned int, buflen, server->vals->read_rsp_size +
+ sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
+
+ rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
+ if (rc < 0)
+ return rc;
+ server->total_read += rc;
+
+ len = le32_to_cpu(tr_hdr->OriginalMessageSize) -
+ server->vals->read_rsp_size;
+ npages = DIV_ROUND_UP(len, PAGE_SIZE);
+
+ pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
+ if (!pages) {
+ rc = -ENOMEM;
+ goto discard_data;
+ }
+
+ for (; i < npages; i++) {
+ pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
+ if (!pages[i]) {
+ rc = -ENOMEM;
+ goto discard_data;
+ }
+ }
+
+ /* read read data into pages */
+ rc = read_data_into_pages(server, pages, npages, len);
+ if (rc)
+ goto free_pages;
+
+ rc = cifs_discard_remaining_data(server);
+ if (rc)
+ goto free_pages;
+
+ rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size,
+ pages, npages, len);
+ if (rc)
+ goto free_pages;
+
+ *mid = smb2_find_mid(server, buf);
+ if (*mid == NULL)
+ cifs_dbg(FYI, "mid not found\n");
+ else {
+ cifs_dbg(FYI, "mid found\n");
+ (*mid)->decrypted = true;
+ rc = handle_read_data(server, *mid, buf,
+ server->vals->read_rsp_size,
+ pages, npages, len);
+ }
+
+free_pages:
+ for (i = i - 1; i >= 0; i--)
+ put_page(pages[i]);
+ kfree(pages);
+ return rc;
+discard_data:
+ cifs_discard_remaining_data(server);
+ goto free_pages;
+}
+
+static int
+receive_encrypted_standard(struct TCP_Server_Info *server,
+ struct mid_q_entry **mids, char **bufs,
+ int *num_mids)
+{
+ int ret, length;
+ char *buf = server->smallbuf;
+ struct smb2_sync_hdr *shdr;
+ unsigned int pdu_length = server->pdu_size;
+ unsigned int buf_size;
+ struct mid_q_entry *mid_entry;
+ int next_is_large;
+ char *next_buffer = NULL;
+
+ *num_mids = 0;
+
+ /* switch to large buffer if too big for a small one */
+ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) {
+ server->large_buf = true;
+ memcpy(server->bigbuf, buf, server->total_read);
+ buf = server->bigbuf;
+ }
+
+ /* now read the rest */
+ length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
+ pdu_length - HEADER_SIZE(server) + 1);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+
+ buf_size = pdu_length - sizeof(struct smb2_transform_hdr);
+ length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0);
+ if (length)
+ return length;
+
+ next_is_large = server->large_buf;
+one_more:
+ shdr = (struct smb2_sync_hdr *)buf;
+ if (shdr->NextCommand) {
+ if (next_is_large)
+ next_buffer = (char *)cifs_buf_get();
+ else
+ next_buffer = (char *)cifs_small_buf_get();
+ memcpy(next_buffer,
+ buf + le32_to_cpu(shdr->NextCommand),
+ pdu_length - le32_to_cpu(shdr->NextCommand));
+ }
+
+ mid_entry = smb2_find_mid(server, buf);
+ if (mid_entry == NULL)
+ cifs_dbg(FYI, "mid not found\n");
+ else {
+ cifs_dbg(FYI, "mid found\n");
+ mid_entry->decrypted = true;
+ mid_entry->resp_buf_size = server->pdu_size;
+ }
+
+ if (*num_mids >= MAX_COMPOUND) {
+ cifs_dbg(VFS, "too many PDUs in compound\n");
+ return -1;
+ }
+ bufs[*num_mids] = buf;
+ mids[(*num_mids)++] = mid_entry;
+
+ if (mid_entry && mid_entry->handle)
+ ret = mid_entry->handle(server, mid_entry);
+ else
+ ret = cifs_handle_standard(server, mid_entry);
+
+ if (ret == 0 && shdr->NextCommand) {
+ pdu_length -= le32_to_cpu(shdr->NextCommand);
+ server->large_buf = next_is_large;
+ if (next_is_large)
+ server->bigbuf = buf = next_buffer;
+ else
+ server->smallbuf = buf = next_buffer;
+ goto one_more;
+ } else if (ret != 0) {
+ /*
+ * ret != 0 here means that we didn't get to handle_mid() thus
+ * server->smallbuf and server->bigbuf are still valid. We need
+ * to free next_buffer because it is not going to be used
+ * anywhere.
+ */
+ if (next_is_large)
+ free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer);
+ else
+ free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer);
+ }
+
+ return ret;
+}
+
+static int
+smb3_receive_transform(struct TCP_Server_Info *server,
+ struct mid_q_entry **mids, char **bufs, int *num_mids)
+{
+ char *buf = server->smallbuf;
+ unsigned int pdu_length = server->pdu_size;
+ struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
+ unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
+
+ if (pdu_length < sizeof(struct smb2_transform_hdr) +
+ sizeof(struct smb2_sync_hdr)) {
+ cifs_dbg(VFS, "Transform message is too small (%u)\n",
+ pdu_length);
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -ECONNABORTED;
+ }
+
+ if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
+ cifs_dbg(VFS, "Transform message is broken\n");
+ cifs_reconnect(server);
+ wake_up(&server->response_q);
+ return -ECONNABORTED;
+ }
+
+ /* TODO: add support for compounds containing READ. */
+ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
+ *num_mids = 1;
+ return receive_encrypted_read(server, &mids[0]);
+ }
+
+ return receive_encrypted_standard(server, mids, bufs, num_mids);
+}
+
+int
+smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
+
+ return handle_read_data(server, mid, buf, server->pdu_size,
+ NULL, 0, 0);
+}
+
+static int
+smb2_next_header(char *buf)
+{
+ struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf;
+ struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf;
+
+ if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM)
+ return sizeof(struct smb2_transform_hdr) +
+ le32_to_cpu(t_hdr->OriginalMessageSize);
+
+ return le32_to_cpu(hdr->NextCommand);
+}
+
+struct smb_version_operations smb20_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+ .setup_async_request = smb2_setup_async_request,
+ .check_receive = smb2_check_receive,
+ .add_credits = smb2_add_credits,
+ .set_credits = smb2_set_credits,
+ .get_credits_field = smb2_get_credits_field,
+ .get_credits = smb2_get_credits,
+ .wait_mtu_credits = cifs_wait_mtu_credits,
+ .get_next_mid = smb2_get_next_mid,
+ .revert_current_mid = smb2_revert_current_mid,
+ .read_data_offset = smb2_read_data_offset,
+ .read_data_length = smb2_read_data_length,
+ .map_error = map_smb2_to_linux_error,
+ .find_mid = smb2_find_mid,
+ .check_message = smb2_check_message,
+ .dump_detail = smb2_dump_detail,
+ .clear_stats = smb2_clear_stats,
+ .print_stats = smb2_print_stats,
+ .is_oplock_break = smb2_is_valid_oplock_break,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
+ .downgrade_oplock = smb2_downgrade_oplock,
+ .need_neg = smb2_need_neg,
+ .negotiate = smb2_negotiate,
+ .negotiate_wsize = smb2_negotiate_wsize,
+ .negotiate_rsize = smb2_negotiate_rsize,
+ .sess_setup = SMB2_sess_setup,
+ .logoff = SMB2_logoff,
+ .tree_connect = SMB2_tcon,
+ .tree_disconnect = SMB2_tdis,
+ .qfs_tcon = smb2_qfs_tcon,
+ .is_path_accessible = smb2_is_path_accessible,
+ .can_echo = smb2_can_echo,
+ .echo = SMB2_echo,
+ .query_path_info = smb2_query_path_info,
+ .get_srv_inum = smb2_get_srv_inum,
+ .query_file_info = smb2_query_file_info,
+ .set_path_size = smb2_set_path_size,
+ .set_file_size = smb2_set_file_size,
+ .set_file_info = smb2_set_file_info,
+ .set_compression = smb2_set_compression,
+ .mkdir = smb2_mkdir,
+ .mkdir_setinfo = smb2_mkdir_setinfo,
+ .rmdir = smb2_rmdir,
+ .unlink = smb2_unlink,
+ .rename = smb2_rename_path,
+ .create_hardlink = smb2_create_hardlink,
+ .query_symlink = smb2_query_symlink,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+ .flush = smb2_flush_file,
+ .async_readv = smb2_async_readv,
+ .async_writev = smb2_async_writev,
+ .sync_read = smb2_sync_read,
+ .sync_write = smb2_sync_write,
+ .query_dir_first = smb2_query_dir_first,
+ .query_dir_next = smb2_query_dir_next,
+ .close_dir = smb2_close_dir,
+ .calc_smb_size = smb2_calc_size,
+ .is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
+ .oplock_response = smb2_oplock_response,
+ .queryfs = smb2_queryfs,
+ .mand_lock = smb2_mand_lock,
+ .mand_unlock_range = smb2_unlock_range,
+ .push_mand_locks = smb2_push_mandatory_locks,
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+ .calc_signature = smb2_calc_signature,
+ .is_read_op = smb2_is_read_op,
+ .set_oplock_level = smb2_set_oplock_level,
+ .create_lease_buf = smb2_create_lease_buf,
+ .parse_lease_buf = smb2_parse_lease_buf,
+ .copychunk_range = smb2_copychunk_range,
+ .wp_retry_size = smb2_wp_retry_size,
+ .dir_needs_close = smb2_dir_needs_close,
+ .get_dfs_refer = smb2_get_dfs_refer,
+ .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+ .query_all_EAs = smb2_query_eas,
+ .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
+#ifdef CONFIG_CIFS_ACL
+ .get_acl = get_smb2_acl,
+ .get_acl_by_fid = get_smb2_acl_by_fid,
+ .set_acl = set_smb2_acl,
+#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
+};
+
+struct smb_version_operations smb21_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+ .setup_async_request = smb2_setup_async_request,
+ .check_receive = smb2_check_receive,
+ .add_credits = smb2_add_credits,
+ .set_credits = smb2_set_credits,
+ .get_credits_field = smb2_get_credits_field,
+ .get_credits = smb2_get_credits,
+ .wait_mtu_credits = smb2_wait_mtu_credits,
+ .get_next_mid = smb2_get_next_mid,
+ .revert_current_mid = smb2_revert_current_mid,
+ .read_data_offset = smb2_read_data_offset,
+ .read_data_length = smb2_read_data_length,
+ .map_error = map_smb2_to_linux_error,
+ .find_mid = smb2_find_mid,
+ .check_message = smb2_check_message,
+ .dump_detail = smb2_dump_detail,
+ .clear_stats = smb2_clear_stats,
+ .print_stats = smb2_print_stats,
+ .is_oplock_break = smb2_is_valid_oplock_break,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
+ .downgrade_oplock = smb2_downgrade_oplock,
+ .need_neg = smb2_need_neg,
+ .negotiate = smb2_negotiate,
+ .negotiate_wsize = smb2_negotiate_wsize,
+ .negotiate_rsize = smb2_negotiate_rsize,
+ .sess_setup = SMB2_sess_setup,
+ .logoff = SMB2_logoff,
+ .tree_connect = SMB2_tcon,
+ .tree_disconnect = SMB2_tdis,
+ .qfs_tcon = smb2_qfs_tcon,
+ .is_path_accessible = smb2_is_path_accessible,
+ .can_echo = smb2_can_echo,
+ .echo = SMB2_echo,
+ .query_path_info = smb2_query_path_info,
+ .get_srv_inum = smb2_get_srv_inum,
+ .query_file_info = smb2_query_file_info,
+ .set_path_size = smb2_set_path_size,
+ .set_file_size = smb2_set_file_size,
+ .set_file_info = smb2_set_file_info,
+ .set_compression = smb2_set_compression,
+ .mkdir = smb2_mkdir,
+ .mkdir_setinfo = smb2_mkdir_setinfo,
+ .rmdir = smb2_rmdir,
+ .unlink = smb2_unlink,
+ .rename = smb2_rename_path,
+ .create_hardlink = smb2_create_hardlink,
+ .query_symlink = smb2_query_symlink,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+ .flush = smb2_flush_file,
+ .async_readv = smb2_async_readv,
+ .async_writev = smb2_async_writev,
+ .sync_read = smb2_sync_read,
+ .sync_write = smb2_sync_write,
+ .query_dir_first = smb2_query_dir_first,
+ .query_dir_next = smb2_query_dir_next,
+ .close_dir = smb2_close_dir,
+ .calc_smb_size = smb2_calc_size,
+ .is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
+ .oplock_response = smb2_oplock_response,
+ .queryfs = smb2_queryfs,
+ .mand_lock = smb2_mand_lock,
+ .mand_unlock_range = smb2_unlock_range,
+ .push_mand_locks = smb2_push_mandatory_locks,
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+ .calc_signature = smb2_calc_signature,
+ .is_read_op = smb21_is_read_op,
+ .set_oplock_level = smb21_set_oplock_level,
+ .create_lease_buf = smb2_create_lease_buf,
+ .parse_lease_buf = smb2_parse_lease_buf,
+ .copychunk_range = smb2_copychunk_range,
+ .wp_retry_size = smb2_wp_retry_size,
+ .dir_needs_close = smb2_dir_needs_close,
+ .enum_snapshots = smb3_enum_snapshots,
+ .get_dfs_refer = smb2_get_dfs_refer,
+ .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+ .query_all_EAs = smb2_query_eas,
+ .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
+#ifdef CONFIG_CIFS_ACL
+ .get_acl = get_smb2_acl,
+ .get_acl_by_fid = get_smb2_acl_by_fid,
+ .set_acl = set_smb2_acl,
+#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
+};
+
+struct smb_version_operations smb30_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+ .setup_async_request = smb2_setup_async_request,
+ .check_receive = smb2_check_receive,
+ .add_credits = smb2_add_credits,
+ .set_credits = smb2_set_credits,
+ .get_credits_field = smb2_get_credits_field,
+ .get_credits = smb2_get_credits,
+ .wait_mtu_credits = smb2_wait_mtu_credits,
+ .get_next_mid = smb2_get_next_mid,
+ .revert_current_mid = smb2_revert_current_mid,
+ .read_data_offset = smb2_read_data_offset,
+ .read_data_length = smb2_read_data_length,
+ .map_error = map_smb2_to_linux_error,
+ .find_mid = smb2_find_mid,
+ .check_message = smb2_check_message,
+ .dump_detail = smb2_dump_detail,
+ .clear_stats = smb2_clear_stats,
+ .print_stats = smb2_print_stats,
+ .dump_share_caps = smb2_dump_share_caps,
+ .is_oplock_break = smb2_is_valid_oplock_break,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
+ .downgrade_oplock = smb3_downgrade_oplock,
+ .need_neg = smb2_need_neg,
+ .negotiate = smb2_negotiate,
+ .negotiate_wsize = smb2_negotiate_wsize,
+ .negotiate_rsize = smb2_negotiate_rsize,
+ .sess_setup = SMB2_sess_setup,
+ .logoff = SMB2_logoff,
+ .tree_connect = SMB2_tcon,
+ .tree_disconnect = SMB2_tdis,
+ .qfs_tcon = smb3_qfs_tcon,
+ .is_path_accessible = smb2_is_path_accessible,
+ .can_echo = smb2_can_echo,
+ .echo = SMB2_echo,
+ .query_path_info = smb2_query_path_info,
+ .get_srv_inum = smb2_get_srv_inum,
+ .query_file_info = smb2_query_file_info,
+ .set_path_size = smb2_set_path_size,
+ .set_file_size = smb2_set_file_size,
+ .set_file_info = smb2_set_file_info,
+ .set_compression = smb2_set_compression,
+ .mkdir = smb2_mkdir,
+ .mkdir_setinfo = smb2_mkdir_setinfo,
+ .rmdir = smb2_rmdir,
+ .unlink = smb2_unlink,
+ .rename = smb2_rename_path,
+ .create_hardlink = smb2_create_hardlink,
+ .query_symlink = smb2_query_symlink,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+ .flush = smb2_flush_file,
+ .async_readv = smb2_async_readv,
+ .async_writev = smb2_async_writev,
+ .sync_read = smb2_sync_read,
+ .sync_write = smb2_sync_write,
+ .query_dir_first = smb2_query_dir_first,
+ .query_dir_next = smb2_query_dir_next,
+ .close_dir = smb2_close_dir,
+ .calc_smb_size = smb2_calc_size,
+ .is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
+ .oplock_response = smb2_oplock_response,
+ .queryfs = smb2_queryfs,
+ .mand_lock = smb2_mand_lock,
+ .mand_unlock_range = smb2_unlock_range,
+ .push_mand_locks = smb2_push_mandatory_locks,
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+ .generate_signingkey = generate_smb30signingkey,
+ .calc_signature = smb3_calc_signature,
+ .set_integrity = smb3_set_integrity,
+ .is_read_op = smb21_is_read_op,
+ .set_oplock_level = smb3_set_oplock_level,
+ .create_lease_buf = smb3_create_lease_buf,
+ .parse_lease_buf = smb3_parse_lease_buf,
+ .copychunk_range = smb2_copychunk_range,
+ .duplicate_extents = smb2_duplicate_extents,
+ .validate_negotiate = smb3_validate_negotiate,
+ .wp_retry_size = smb2_wp_retry_size,
+ .dir_needs_close = smb2_dir_needs_close,
+ .fallocate = smb3_fallocate,
+ .enum_snapshots = smb3_enum_snapshots,
+ .init_transform_rq = smb3_init_transform_rq,
+ .is_transform_hdr = smb3_is_transform_hdr,
+ .receive_transform = smb3_receive_transform,
+ .get_dfs_refer = smb2_get_dfs_refer,
+ .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+ .query_all_EAs = smb2_query_eas,
+ .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
+#ifdef CONFIG_CIFS_ACL
+ .get_acl = get_smb2_acl,
+ .get_acl_by_fid = get_smb2_acl_by_fid,
+ .set_acl = set_smb2_acl,
+#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
+};
+
+struct smb_version_operations smb311_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+ .setup_async_request = smb2_setup_async_request,
+ .check_receive = smb2_check_receive,
+ .add_credits = smb2_add_credits,
+ .set_credits = smb2_set_credits,
+ .get_credits_field = smb2_get_credits_field,
+ .get_credits = smb2_get_credits,
+ .wait_mtu_credits = smb2_wait_mtu_credits,
+ .get_next_mid = smb2_get_next_mid,
+ .revert_current_mid = smb2_revert_current_mid,
+ .read_data_offset = smb2_read_data_offset,
+ .read_data_length = smb2_read_data_length,
+ .map_error = map_smb2_to_linux_error,
+ .find_mid = smb2_find_mid,
+ .check_message = smb2_check_message,
+ .dump_detail = smb2_dump_detail,
+ .clear_stats = smb2_clear_stats,
+ .print_stats = smb2_print_stats,
+ .dump_share_caps = smb2_dump_share_caps,
+ .is_oplock_break = smb2_is_valid_oplock_break,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
+ .downgrade_oplock = smb3_downgrade_oplock,
+ .need_neg = smb2_need_neg,
+ .negotiate = smb2_negotiate,
+ .negotiate_wsize = smb2_negotiate_wsize,
+ .negotiate_rsize = smb2_negotiate_rsize,
+ .sess_setup = SMB2_sess_setup,
+ .logoff = SMB2_logoff,
+ .tree_connect = SMB2_tcon,
+ .tree_disconnect = SMB2_tdis,
+ .qfs_tcon = smb3_qfs_tcon,
+ .is_path_accessible = smb2_is_path_accessible,
+ .can_echo = smb2_can_echo,
+ .echo = SMB2_echo,
+ .query_path_info = smb2_query_path_info,
+ .get_srv_inum = smb2_get_srv_inum,
+ .query_file_info = smb2_query_file_info,
+ .set_path_size = smb2_set_path_size,
+ .set_file_size = smb2_set_file_size,
+ .set_file_info = smb2_set_file_info,
+ .set_compression = smb2_set_compression,
+ .mkdir = smb2_mkdir,
+ .mkdir_setinfo = smb2_mkdir_setinfo,
+ .posix_mkdir = smb311_posix_mkdir,
+ .rmdir = smb2_rmdir,
+ .unlink = smb2_unlink,
+ .rename = smb2_rename_path,
+ .create_hardlink = smb2_create_hardlink,
+ .query_symlink = smb2_query_symlink,
+ .query_mf_symlink = smb3_query_mf_symlink,
+ .create_mf_symlink = smb3_create_mf_symlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+ .flush = smb2_flush_file,
+ .async_readv = smb2_async_readv,
+ .async_writev = smb2_async_writev,
+ .sync_read = smb2_sync_read,
+ .sync_write = smb2_sync_write,
+ .query_dir_first = smb2_query_dir_first,
+ .query_dir_next = smb2_query_dir_next,
+ .close_dir = smb2_close_dir,
+ .calc_smb_size = smb2_calc_size,
+ .is_status_pending = smb2_is_status_pending,
+ .is_session_expired = smb2_is_session_expired,
+ .oplock_response = smb2_oplock_response,
+ .queryfs = smb311_queryfs,
+ .mand_lock = smb2_mand_lock,
+ .mand_unlock_range = smb2_unlock_range,
+ .push_mand_locks = smb2_push_mandatory_locks,
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+ .generate_signingkey = generate_smb311signingkey,
+ .calc_signature = smb3_calc_signature,
+ .set_integrity = smb3_set_integrity,
+ .is_read_op = smb21_is_read_op,
+ .set_oplock_level = smb3_set_oplock_level,
+ .create_lease_buf = smb3_create_lease_buf,
+ .parse_lease_buf = smb3_parse_lease_buf,
+ .copychunk_range = smb2_copychunk_range,
+ .duplicate_extents = smb2_duplicate_extents,
+/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */
+ .wp_retry_size = smb2_wp_retry_size,
+ .dir_needs_close = smb2_dir_needs_close,
+ .fallocate = smb3_fallocate,
+ .enum_snapshots = smb3_enum_snapshots,
+ .init_transform_rq = smb3_init_transform_rq,
+ .is_transform_hdr = smb3_is_transform_hdr,
+ .receive_transform = smb3_receive_transform,
+ .get_dfs_refer = smb2_get_dfs_refer,
+ .select_sectype = smb2_select_sectype,
+#ifdef CONFIG_CIFS_XATTR
+ .query_all_EAs = smb2_query_eas,
+ .set_EA = smb2_set_ea,
+#endif /* CIFS_XATTR */
+#ifdef CONFIG_CIFS_ACL
+ .get_acl = get_smb2_acl,
+ .get_acl_by_fid = get_smb2_acl_by_fid,
+ .set_acl = set_smb2_acl,
+#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
+};
+
+struct smb_version_values smb20_values = {
+ .version_string = SMB20_VERSION_STRING,
+ .protocol_id = SMB20_PROT_ID,
+ .req_capabilities = 0, /* MBZ */
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease),
+};
+
+struct smb_version_values smb21_values = {
+ .version_string = SMB21_VERSION_STRING,
+ .protocol_id = SMB21_PROT_ID,
+ .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease),
+};
+
+struct smb_version_values smb3any_values = {
+ .version_string = SMB3ANY_VERSION_STRING,
+ .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */
+ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease_v2),
+};
+
+struct smb_version_values smbdefault_values = {
+ .version_string = SMBDEFAULT_VERSION_STRING,
+ .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */
+ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease_v2),
+};
+
+struct smb_version_values smb30_values = {
+ .version_string = SMB30_VERSION_STRING,
+ .protocol_id = SMB30_PROT_ID,
+ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease_v2),
+};
+
+struct smb_version_values smb302_values = {
+ .version_string = SMB302_VERSION_STRING,
+ .protocol_id = SMB302_PROT_ID,
+ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease_v2),
+};
+
+struct smb_version_values smb311_values = {
+ .version_string = SMB311_VERSION_STRING,
+ .protocol_id = SMB311_PROT_ID,
+ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_sync_hdr),
+ .header_preamble_size = 0,
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+ .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
+ .create_lease_size = sizeof(struct create_lease_v2),
+};
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
new file mode 100644
index 000000000..3485b9bf9
--- /dev/null
+++ b/fs/cifs/smb2pdu.c
@@ -0,0 +1,4493 @@
+/*
+ * fs/cifs/smb2pdu.c
+ *
+ * Copyright (C) International Business Machines Corp., 2009, 2013
+ * Etersoft, 2012
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ *
+ * Contains the routines for constructing the SMB2 PDUs themselves
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */
+ /* Note that there are handle based routines which must be */
+ /* treated slightly differently for reconnection purposes since we never */
+ /* want to reuse a stale file handle and only the caller knows the file info */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/vfs.h>
+#include <linux/task_io_accounting_ops.h>
+#include <linux/uaccess.h>
+#include <linux/uuid.h>
+#include <linux/pagemap.h>
+#include <linux/xattr.h>
+#include "smb2pdu.h"
+#include "cifsglob.h"
+#include "cifsacl.h"
+#include "cifsproto.h"
+#include "smb2proto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "ntlmssp.h"
+#include "smb2status.h"
+#include "smb2glob.h"
+#include "cifspdu.h"
+#include "cifs_spnego.h"
+#include "smbdirect.h"
+#include "trace.h"
+
+/*
+ * The following table defines the expected "StructureSize" of SMB2 requests
+ * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests.
+ *
+ * Note that commands are defined in smb2pdu.h in le16 but the array below is
+ * indexed by command in host byte order.
+ */
+static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
+ /* SMB2_NEGOTIATE */ 36,
+ /* SMB2_SESSION_SETUP */ 25,
+ /* SMB2_LOGOFF */ 4,
+ /* SMB2_TREE_CONNECT */ 9,
+ /* SMB2_TREE_DISCONNECT */ 4,
+ /* SMB2_CREATE */ 57,
+ /* SMB2_CLOSE */ 24,
+ /* SMB2_FLUSH */ 24,
+ /* SMB2_READ */ 49,
+ /* SMB2_WRITE */ 49,
+ /* SMB2_LOCK */ 48,
+ /* SMB2_IOCTL */ 57,
+ /* SMB2_CANCEL */ 4,
+ /* SMB2_ECHO */ 4,
+ /* SMB2_QUERY_DIRECTORY */ 33,
+ /* SMB2_CHANGE_NOTIFY */ 32,
+ /* SMB2_QUERY_INFO */ 41,
+ /* SMB2_SET_INFO */ 33,
+ /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */
+};
+
+int smb3_encryption_required(const struct cifs_tcon *tcon)
+{
+ if (!tcon)
+ return 0;
+ if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) ||
+ (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA))
+ return 1;
+ if (tcon->seal &&
+ (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
+ return 1;
+ return 0;
+}
+
+static void
+smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
+ const struct cifs_tcon *tcon)
+{
+ shdr->ProtocolId = SMB2_PROTO_NUMBER;
+ shdr->StructureSize = cpu_to_le16(64);
+ shdr->Command = smb2_cmd;
+ if (tcon && tcon->ses && tcon->ses->server) {
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ spin_lock(&server->req_lock);
+ /* Request up to 2 credits but don't go over the limit. */
+ if (server->credits >= server->max_credits)
+ shdr->CreditRequest = cpu_to_le16(0);
+ else
+ shdr->CreditRequest = cpu_to_le16(
+ min_t(int, server->max_credits -
+ server->credits, 2));
+ spin_unlock(&server->req_lock);
+ } else {
+ shdr->CreditRequest = cpu_to_le16(2);
+ }
+ shdr->ProcessId = cpu_to_le32((__u16)current->tgid);
+
+ if (!tcon)
+ goto out;
+
+ /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
+ /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
+ if ((tcon->ses) && (tcon->ses->server) &&
+ (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+ shdr->CreditCharge = cpu_to_le16(1);
+ /* else CreditCharge MBZ */
+
+ shdr->TreeId = tcon->tid;
+ /* Uid is not converted */
+ if (tcon->ses)
+ shdr->SessionId = tcon->ses->Suid;
+
+ /*
+ * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
+ * to pass the path on the Open SMB prefixed by \\server\share.
+ * Not sure when we would need to do the augmented path (if ever) and
+ * setting this flag breaks the SMB2 open operation since it is
+ * illegal to send an empty path name (without \\server\share prefix)
+ * when the DFS flag is set in the SMB open header. We could
+ * consider setting the flag on all operations other than open
+ * but it is safer to net set it for now.
+ */
+/* if (tcon->share_flags & SHI1005_FLAGS_DFS)
+ shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
+
+ if (tcon->ses && tcon->ses->server && tcon->ses->server->sign &&
+ !smb3_encryption_required(tcon))
+ shdr->Flags |= SMB2_FLAGS_SIGNED;
+out:
+ return;
+}
+
+static int
+smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
+{
+ int rc;
+ struct nls_table *nls_codepage;
+ struct cifs_ses *ses;
+ struct TCP_Server_Info *server;
+
+ /*
+ * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
+ * check for tcp and smb session status done differently
+ * for those three - in the calling routine.
+ */
+ if (tcon == NULL)
+ return 0;
+
+ if (smb2_command == SMB2_TREE_CONNECT)
+ return 0;
+
+ if (tcon->tidStatus == CifsExiting) {
+ /*
+ * only tree disconnect, open, and write,
+ * (and ulogoff which does not have tcon)
+ * are allowed as we start force umount.
+ */
+ if ((smb2_command != SMB2_WRITE) &&
+ (smb2_command != SMB2_CREATE) &&
+ (smb2_command != SMB2_TREE_DISCONNECT)) {
+ cifs_dbg(FYI, "can not send cmd %d while umounting\n",
+ smb2_command);
+ return -ENODEV;
+ }
+ }
+ if ((!tcon->ses) || (tcon->ses->status == CifsExiting) ||
+ (!tcon->ses->server))
+ return -EIO;
+
+ ses = tcon->ses;
+ server = ses->server;
+
+ /*
+ * Give demultiplex thread up to 10 seconds to reconnect, should be
+ * greater than cifs socket timeout which is 7 seconds
+ */
+ while (server->tcpStatus == CifsNeedReconnect) {
+ /*
+ * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
+ * here since they are implicitly done when session drops.
+ */
+ switch (smb2_command) {
+ /*
+ * BB Should we keep oplock break and add flush to exceptions?
+ */
+ case SMB2_TREE_DISCONNECT:
+ case SMB2_CANCEL:
+ case SMB2_CLOSE:
+ case SMB2_OPLOCK_BREAK:
+ return -EAGAIN;
+ }
+
+ rc = wait_event_interruptible_timeout(server->response_q,
+ (server->tcpStatus != CifsNeedReconnect),
+ 10 * HZ);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: aborting reconnect due to a received"
+ " signal by the process\n", __func__);
+ return -ERESTARTSYS;
+ }
+
+ /* are we still trying to reconnect? */
+ if (server->tcpStatus != CifsNeedReconnect)
+ break;
+
+ /*
+ * on "soft" mounts we wait once. Hard mounts keep
+ * retrying until process is killed or server comes
+ * back on-line
+ */
+ if (!tcon->retry) {
+ cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
+ return -EHOSTDOWN;
+ }
+ }
+
+ if (!tcon->ses->need_reconnect && !tcon->need_reconnect)
+ return 0;
+
+ nls_codepage = load_nls_default();
+
+ /*
+ * need to prevent multiple threads trying to simultaneously reconnect
+ * the same SMB session
+ */
+ mutex_lock(&tcon->ses->session_mutex);
+
+ /*
+ * Recheck after acquire mutex. If another thread is negotiating
+ * and the server never sends an answer the socket will be closed
+ * and tcpStatus set to reconnect.
+ */
+ if (server->tcpStatus == CifsNeedReconnect) {
+ rc = -EHOSTDOWN;
+ mutex_unlock(&tcon->ses->session_mutex);
+ goto out;
+ }
+
+ rc = cifs_negotiate_protocol(0, tcon->ses);
+ if (!rc && tcon->ses->need_reconnect) {
+ rc = cifs_setup_session(0, tcon->ses, nls_codepage);
+ if ((rc == -EACCES) && !tcon->retry) {
+ rc = -EHOSTDOWN;
+ mutex_unlock(&tcon->ses->session_mutex);
+ goto failed;
+ } else if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ goto out;
+ }
+ }
+ if (rc || !tcon->need_reconnect) {
+ mutex_unlock(&tcon->ses->session_mutex);
+ goto out;
+ }
+
+ cifs_mark_open_files_invalid(tcon);
+ if (tcon->use_persistent)
+ tcon->need_reopen_files = true;
+
+ rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
+ mutex_unlock(&tcon->ses->session_mutex);
+
+ cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
+ if (rc) {
+ /* If sess reconnected but tcon didn't, something strange ... */
+ printk_once(KERN_WARNING "reconnect tcon failed rc = %d\n", rc);
+ goto out;
+ }
+
+ if (smb2_command != SMB2_INTERNAL_CMD)
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+
+ atomic_inc(&tconInfoReconnectCount);
+out:
+ /*
+ * Check if handle based operation so we know whether we can continue
+ * or not without returning to caller to reset file handle.
+ */
+ /*
+ * BB Is flush done by server on drop of tcp session? Should we special
+ * case it and skip above?
+ */
+ switch (smb2_command) {
+ case SMB2_FLUSH:
+ case SMB2_READ:
+ case SMB2_WRITE:
+ case SMB2_LOCK:
+ case SMB2_IOCTL:
+ case SMB2_QUERY_DIRECTORY:
+ case SMB2_CHANGE_NOTIFY:
+ case SMB2_QUERY_INFO:
+ case SMB2_SET_INFO:
+ rc = -EAGAIN;
+ }
+failed:
+ unload_nls(nls_codepage);
+ return rc;
+}
+
+static void
+fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf,
+ unsigned int *total_len)
+{
+ struct smb2_sync_pdu *spdu = (struct smb2_sync_pdu *)buf;
+ /* lookup word count ie StructureSize from table */
+ __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)];
+
+ /*
+ * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
+ * largest operations (Create)
+ */
+ memset(buf, 0, 256);
+
+ smb2_hdr_assemble(&spdu->sync_hdr, smb2_command, tcon);
+ spdu->StructureSize2 = cpu_to_le16(parmsize);
+
+ *total_len = parmsize + sizeof(struct smb2_sync_hdr);
+}
+
+/*
+ * Allocate and return pointer to an SMB request hdr, and set basic
+ * SMB information in the SMB header. If the return code is zero, this
+ * function must have filled in request_buf pointer.
+ */
+static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
+ void **request_buf, unsigned int *total_len)
+{
+ /* BB eventually switch this to SMB2 specific small buf size */
+ if (smb2_command == SMB2_SET_INFO)
+ *request_buf = cifs_buf_get();
+ else
+ *request_buf = cifs_small_buf_get();
+ if (*request_buf == NULL) {
+ /* BB should we add a retry in here if not a writepage? */
+ return -ENOMEM;
+ }
+
+ fill_small_buf(smb2_command, tcon,
+ (struct smb2_sync_hdr *)(*request_buf),
+ total_len);
+
+ if (tcon != NULL) {
+ uint16_t com_code = le16_to_cpu(smb2_command);
+ cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]);
+ cifs_stats_inc(&tcon->num_smbs_sent);
+ }
+
+ return 0;
+}
+
+static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
+ void **request_buf, unsigned int *total_len)
+{
+ int rc;
+
+ rc = smb2_reconnect(smb2_command, tcon);
+ if (rc)
+ return rc;
+
+ return __smb2_plain_req_init(smb2_command, tcon, request_buf,
+ total_len);
+}
+
+static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
+ void **request_buf, unsigned int *total_len)
+{
+ /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
+ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
+ return __smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf,
+ total_len);
+ }
+ return smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, total_len);
+}
+
+
+/* offset is sizeof smb2_negotiate_req but rounded up to 8 bytes */
+#define OFFSET_OF_NEG_CONTEXT 0x68 /* sizeof(struct smb2_negotiate_req) */
+
+
+#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1)
+#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2)
+#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100)
+
+static void
+build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
+{
+ pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES;
+ pneg_ctxt->DataLength = cpu_to_le16(38);
+ pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1);
+ pneg_ctxt->SaltLength = cpu_to_le16(SMB311_LINUX_CLIENT_SALT_SIZE);
+ get_random_bytes(pneg_ctxt->Salt, SMB311_LINUX_CLIENT_SALT_SIZE);
+ pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512;
+}
+
+static void
+build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
+{
+ pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES;
+ pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + le16 cipher */
+ pneg_ctxt->CipherCount = cpu_to_le16(1);
+/* pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;*/ /* not supported yet */
+ pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_CCM;
+}
+
+static void
+build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
+{
+ pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
+ pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
+}
+
+static void
+assemble_neg_contexts(struct smb2_negotiate_req *req,
+ unsigned int *total_len)
+{
+ char *pneg_ctxt = (char *)req + OFFSET_OF_NEG_CONTEXT;
+ unsigned int ctxt_len;
+
+ *total_len += 2; /* Add 2 due to round to 8 byte boundary for 1st ctxt */
+ build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt);
+ ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8;
+ *total_len += ctxt_len;
+ pneg_ctxt += ctxt_len;
+
+ build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
+ ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_encryption_neg_context), 8) * 8;
+ *total_len += ctxt_len;
+ pneg_ctxt += ctxt_len;
+
+ build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
+ *total_len += sizeof(struct smb2_posix_neg_context);
+
+ req->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT);
+ req->NegotiateContextCount = cpu_to_le16(3);
+}
+
+static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
+{
+ unsigned int len = le16_to_cpu(ctxt->DataLength);
+
+ /* If invalid preauth context warn but use what we requested, SHA-512 */
+ if (len < MIN_PREAUTH_CTXT_DATA_LEN) {
+ printk_once(KERN_WARNING "server sent bad preauth context\n");
+ return;
+ } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) {
+ pr_warn_once("server sent invalid SaltLength\n");
+ return;
+ }
+ if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1)
+ printk_once(KERN_WARNING "illegal SMB3 hash algorithm count\n");
+ if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512)
+ printk_once(KERN_WARNING "unknown SMB3 hash algorithm\n");
+}
+
+static int decode_encrypt_ctx(struct TCP_Server_Info *server,
+ struct smb2_encryption_neg_context *ctxt)
+{
+ unsigned int len = le16_to_cpu(ctxt->DataLength);
+
+ cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len);
+ if (len < MIN_ENCRYPT_CTXT_DATA_LEN) {
+ printk_once(KERN_WARNING "server sent bad crypto ctxt len\n");
+ return -EINVAL;
+ }
+
+ if (le16_to_cpu(ctxt->CipherCount) != 1) {
+ printk_once(KERN_WARNING "illegal SMB3.11 cipher count\n");
+ return -EINVAL;
+ }
+ cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0]));
+ if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) &&
+ (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM)) {
+ printk_once(KERN_WARNING "invalid SMB3.11 cipher returned\n");
+ return -EINVAL;
+ }
+ server->cipher_type = ctxt->Ciphers[0];
+ server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ return 0;
+}
+
+static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
+ struct TCP_Server_Info *server,
+ unsigned int len_of_smb)
+{
+ struct smb2_neg_context *pctx;
+ unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset);
+ unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount);
+ unsigned int len_of_ctxts, i;
+ int rc = 0;
+
+ cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt);
+ if (len_of_smb <= offset) {
+ cifs_dbg(VFS, "Invalid response: negotiate context offset\n");
+ return -EINVAL;
+ }
+
+ len_of_ctxts = len_of_smb - offset;
+
+ for (i = 0; i < ctxt_cnt; i++) {
+ int clen;
+ /* check that offset is not beyond end of SMB */
+ if (len_of_ctxts == 0)
+ break;
+
+ if (len_of_ctxts < sizeof(struct smb2_neg_context))
+ break;
+
+ pctx = (struct smb2_neg_context *)(offset + (char *)rsp);
+ clen = le16_to_cpu(pctx->DataLength);
+ if (clen > len_of_ctxts)
+ break;
+
+ if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES)
+ decode_preauth_context(
+ (struct smb2_preauth_neg_context *)pctx);
+ else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
+ rc = decode_encrypt_ctx(server,
+ (struct smb2_encryption_neg_context *)pctx);
+ else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE)
+ server->posix_ext_supported = true;
+ else
+ cifs_dbg(VFS, "unknown negcontext of type %d ignored\n",
+ le16_to_cpu(pctx->ContextType));
+
+ if (rc)
+ break;
+ /* offsets must be 8 byte aligned */
+ clen = (clen + 7) & ~0x7;
+ offset += clen + sizeof(struct smb2_neg_context);
+ len_of_ctxts -= clen;
+ }
+ return rc;
+}
+
+static struct create_posix *
+create_posix_buf(umode_t mode)
+{
+ struct create_posix *buf;
+
+ buf = kzalloc(sizeof(struct create_posix),
+ GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset =
+ cpu_to_le16(offsetof(struct create_posix, Mode));
+ buf->ccontext.DataLength = cpu_to_le32(4);
+ buf->ccontext.NameOffset =
+ cpu_to_le16(offsetof(struct create_posix, Name));
+ buf->ccontext.NameLength = cpu_to_le16(16);
+
+ /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
+ buf->Name[0] = 0x93;
+ buf->Name[1] = 0xAD;
+ buf->Name[2] = 0x25;
+ buf->Name[3] = 0x50;
+ buf->Name[4] = 0x9C;
+ buf->Name[5] = 0xB4;
+ buf->Name[6] = 0x11;
+ buf->Name[7] = 0xE7;
+ buf->Name[8] = 0xB4;
+ buf->Name[9] = 0x23;
+ buf->Name[10] = 0x83;
+ buf->Name[11] = 0xDE;
+ buf->Name[12] = 0x96;
+ buf->Name[13] = 0x8B;
+ buf->Name[14] = 0xCD;
+ buf->Name[15] = 0x7C;
+ buf->Mode = cpu_to_le32(mode);
+ cifs_dbg(FYI, "mode on posix create 0%o", mode);
+ return buf;
+}
+
+static int
+add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ iov[num].iov_base = create_posix_buf(mode);
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct create_posix);
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset = cpu_to_le32(
+ sizeof(struct smb2_create_req) +
+ iov[num - 1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_posix));
+ *num_iovec = num + 1;
+ return 0;
+}
+
+
+/*
+ *
+ * SMB2 Worker functions follow:
+ *
+ * The general structure of the worker functions is:
+ * 1) Call smb2_init (assembles SMB2 header)
+ * 2) Initialize SMB2 command specific fields in fixed length area of SMB
+ * 3) Call smb_sendrcv2 (sends request on socket and waits for response)
+ * 4) Decode SMB2 command specific fields in the fixed length area
+ * 5) Decode variable length data area (if any for this SMB2 command type)
+ * 6) Call free smb buffer
+ * 7) return
+ *
+ */
+
+int
+SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
+{
+ struct smb_rqst rqst;
+ struct smb2_negotiate_req *req;
+ struct smb2_negotiate_rsp *rsp;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ struct TCP_Server_Info *server = ses->server;
+ int blob_offset, blob_length;
+ char *security_blob;
+ int flags = CIFS_NEG_OP;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "Negotiate protocol\n");
+
+ if (!server) {
+ WARN(1, "%s: server is NULL!\n", __func__);
+ return -EIO;
+ }
+
+ rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ req->sync_hdr.SessionId = 0;
+
+ memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
+ memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
+
+ if (strcmp(ses->server->vals->version_string,
+ SMB3ANY_VERSION_STRING) == 0) {
+ req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
+ req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
+ req->DialectCount = cpu_to_le16(2);
+ total_len += 4;
+ } else if (strcmp(ses->server->vals->version_string,
+ SMBDEFAULT_VERSION_STRING) == 0) {
+ req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
+ req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
+ req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
+ req->DialectCount = cpu_to_le16(3);
+ total_len += 6;
+ } else {
+ /* otherwise send specific dialect */
+ req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
+ req->DialectCount = cpu_to_le16(1);
+ total_len += 2;
+ }
+
+ /* only one of SMB2 signing flags may be set in SMB2 request */
+ if (ses->sign)
+ req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
+ else if (global_secflags & CIFSSEC_MAY_SIGN)
+ req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
+ else
+ req->SecurityMode = 0;
+
+ req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);
+
+ /* ClientGUID must be zero for SMB2.02 dialect */
+ if (ses->server->vals->protocol_id == SMB20_PROT_ID)
+ memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
+ else {
+ memcpy(req->ClientGUID, server->client_guid,
+ SMB2_CLIENT_GUID_SIZE);
+ if (ses->server->vals->protocol_id == SMB311_PROT_ID)
+ assemble_neg_contexts(req, &total_len);
+ }
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
+ /*
+ * No tcon so can't do
+ * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
+ */
+ if (rc == -EOPNOTSUPP) {
+ cifs_dbg(VFS, "Dialect not supported by server. Consider "
+ "specifying vers=1.0 or vers=2.0 on mount for accessing"
+ " older servers\n");
+ goto neg_exit;
+ } else if (rc != 0)
+ goto neg_exit;
+
+ if (strcmp(ses->server->vals->version_string,
+ SMB3ANY_VERSION_STRING) == 0) {
+ if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
+ cifs_dbg(VFS,
+ "SMB2 dialect returned but not requested\n");
+ return -EIO;
+ } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
+ cifs_dbg(VFS,
+ "SMB2.1 dialect returned but not requested\n");
+ return -EIO;
+ }
+ } else if (strcmp(ses->server->vals->version_string,
+ SMBDEFAULT_VERSION_STRING) == 0) {
+ if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
+ cifs_dbg(VFS,
+ "SMB2 dialect returned but not requested\n");
+ return -EIO;
+ } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
+ /* ops set to 3.0 by default for default so update */
+ ses->server->ops = &smb21_operations;
+ ses->server->vals = &smb21_values;
+ }
+ } else if (le16_to_cpu(rsp->DialectRevision) !=
+ ses->server->vals->protocol_id) {
+ /* if requested single dialect ensure returned dialect matched */
+ cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n",
+ le16_to_cpu(rsp->DialectRevision));
+ return -EIO;
+ }
+
+ cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);
+
+ if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
+ cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
+ else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
+ cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
+ else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
+ cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
+ else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
+ cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
+ else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
+ cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
+ else {
+ cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n",
+ le16_to_cpu(rsp->DialectRevision));
+ rc = -EIO;
+ goto neg_exit;
+ }
+ server->dialect = le16_to_cpu(rsp->DialectRevision);
+
+ /*
+ * Keep a copy of the hash after negprot. This hash will be
+ * the starting hash value for all sessions made from this
+ * server.
+ */
+ memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
+ SMB2_PREAUTH_HASH_SIZE);
+
+ /* SMB2 only has an extended negflavor */
+ server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
+ /* set it to the maximum buffer size value we can send with 1 credit */
+ server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize),
+ SMB2_MAX_BUFFER_SIZE);
+ server->max_read = le32_to_cpu(rsp->MaxReadSize);
+ server->max_write = le32_to_cpu(rsp->MaxWriteSize);
+ server->sec_mode = le16_to_cpu(rsp->SecurityMode);
+ if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode)
+ cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n",
+ server->sec_mode);
+ server->capabilities = le32_to_cpu(rsp->Capabilities);
+ /* Internal types */
+ server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;
+
+ /*
+ * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context
+ * Set the cipher type manually.
+ */
+ if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
+ server->cipher_type = SMB2_ENCRYPTION_AES128_CCM;
+
+ security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
+ (struct smb2_sync_hdr *)rsp);
+ /*
+ * See MS-SMB2 section 2.2.4: if no blob, client picks default which
+ * for us will be
+ * ses->sectype = RawNTLMSSP;
+ * but for time being this is our only auth choice so doesn't matter.
+ * We just found a server which sets blob length to zero expecting raw.
+ */
+ if (blob_length == 0) {
+ cifs_dbg(FYI, "missing security blob on negprot\n");
+ server->sec_ntlmssp = true;
+ }
+
+ rc = cifs_enable_signing(server, ses->sign);
+ if (rc)
+ goto neg_exit;
+ if (blob_length) {
+ rc = decode_negTokenInit(security_blob, blob_length, server);
+ if (rc == 1)
+ rc = 0;
+ else if (rc == 0)
+ rc = -EIO;
+ }
+
+ if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+ if (rsp->NegotiateContextCount)
+ rc = smb311_decode_neg_context(rsp, server,
+ rsp_iov.iov_len);
+ else
+ cifs_dbg(VFS, "Missing expected negotiate contexts\n");
+ }
+neg_exit:
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ int rc;
+ struct validate_negotiate_info_req *pneg_inbuf;
+ struct validate_negotiate_info_rsp *pneg_rsp = NULL;
+ u32 rsplen;
+ u32 inbuflen; /* max of 4 dialects */
+
+ cifs_dbg(FYI, "validate negotiate\n");
+
+ /* In SMB3.11 preauth integrity supersedes validate negotiate */
+ if (tcon->ses->server->dialect == SMB311_PROT_ID)
+ return 0;
+
+ /*
+ * validation ioctl must be signed, so no point sending this if we
+ * can not sign it (ie are not known user). Even if signing is not
+ * required (enabled but not negotiated), in those cases we selectively
+ * sign just this, the first and only signed request on a connection.
+ * Having validation of negotiate info helps reduce attack vectors.
+ */
+ if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
+ return 0; /* validation requires signing */
+
+ if (tcon->ses->user_name == NULL) {
+ cifs_dbg(FYI, "Can't validate negotiate: null user mount\n");
+ return 0; /* validation requires signing */
+ }
+
+ if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
+ cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
+
+ pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS);
+ if (!pneg_inbuf)
+ return -ENOMEM;
+
+ pneg_inbuf->Capabilities =
+ cpu_to_le32(tcon->ses->server->vals->req_capabilities);
+ memcpy(pneg_inbuf->Guid, tcon->ses->server->client_guid,
+ SMB2_CLIENT_GUID_SIZE);
+
+ if (tcon->ses->sign)
+ pneg_inbuf->SecurityMode =
+ cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
+ else if (global_secflags & CIFSSEC_MAY_SIGN)
+ pneg_inbuf->SecurityMode =
+ cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
+ else
+ pneg_inbuf->SecurityMode = 0;
+
+
+ if (strcmp(tcon->ses->server->vals->version_string,
+ SMB3ANY_VERSION_STRING) == 0) {
+ pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
+ pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
+ pneg_inbuf->DialectCount = cpu_to_le16(2);
+ /* structure is big enough for 3 dialects, sending only 2 */
+ inbuflen = sizeof(*pneg_inbuf) -
+ sizeof(pneg_inbuf->Dialects[0]);
+ } else if (strcmp(tcon->ses->server->vals->version_string,
+ SMBDEFAULT_VERSION_STRING) == 0) {
+ pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
+ pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
+ pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
+ pneg_inbuf->DialectCount = cpu_to_le16(3);
+ /* structure is big enough for 3 dialects */
+ inbuflen = sizeof(*pneg_inbuf);
+ } else {
+ /* otherwise specific dialect was requested */
+ pneg_inbuf->Dialects[0] =
+ cpu_to_le16(tcon->ses->server->vals->protocol_id);
+ pneg_inbuf->DialectCount = cpu_to_le16(1);
+ /* structure is big enough for 3 dialects, sending only 1 */
+ inbuflen = sizeof(*pneg_inbuf) -
+ sizeof(pneg_inbuf->Dialects[0]) * 2;
+ }
+
+ rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+ FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
+ (char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen);
+ if (rc == -EOPNOTSUPP) {
+ /*
+ * Old Windows versions or Netapp SMB server can return
+ * not supported error. Client should accept it.
+ */
+ cifs_dbg(VFS, "Server does not support validate negotiate\n");
+ rc = 0;
+ goto out_free_inbuf;
+ } else if (rc != 0) {
+ cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
+ rc = -EIO;
+ goto out_free_inbuf;
+ }
+
+ rc = -EIO;
+ if (rsplen != sizeof(*pneg_rsp)) {
+ cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
+ rsplen);
+
+ /* relax check since Mac returns max bufsize allowed on ioctl */
+ if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp))
+ goto out_free_rsp;
+ }
+
+ /* check validate negotiate info response matches what we got earlier */
+ if (pneg_rsp->Dialect != cpu_to_le16(tcon->ses->server->dialect))
+ goto vneg_out;
+
+ if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode))
+ goto vneg_out;
+
+ /* do not validate server guid because not saved at negprot time yet */
+
+ if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
+ SMB2_LARGE_FILES) != tcon->ses->server->capabilities)
+ goto vneg_out;
+
+ /* validate negotiate successful */
+ rc = 0;
+ cifs_dbg(FYI, "validate negotiate info successful\n");
+ goto out_free_rsp;
+
+vneg_out:
+ cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
+out_free_rsp:
+ kfree(pneg_rsp);
+out_free_inbuf:
+ kfree(pneg_inbuf);
+ return rc;
+}
+
+enum securityEnum
+smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
+{
+ switch (requested) {
+ case Kerberos:
+ case RawNTLMSSP:
+ return requested;
+ case NTLMv2:
+ return RawNTLMSSP;
+ case Unspecified:
+ if (server->sec_ntlmssp &&
+ (global_secflags & CIFSSEC_MAY_NTLMSSP))
+ return RawNTLMSSP;
+ if ((server->sec_kerberos || server->sec_mskerberos) &&
+ (global_secflags & CIFSSEC_MAY_KRB5))
+ return Kerberos;
+ /* Fallthrough */
+ default:
+ return Unspecified;
+ }
+}
+
+struct SMB2_sess_data {
+ unsigned int xid;
+ struct cifs_ses *ses;
+ struct nls_table *nls_cp;
+ void (*func)(struct SMB2_sess_data *);
+ int result;
+ u64 previous_session;
+
+ /* we will send the SMB in three pieces:
+ * a fixed length beginning part, an optional
+ * SPNEGO blob (which can be zero length), and a
+ * last part which will include the strings
+ * and rest of bcc area. This allows us to avoid
+ * a large buffer 17K allocation
+ */
+ int buf0_type;
+ struct kvec iov[2];
+};
+
+static int
+SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
+{
+ int rc;
+ struct cifs_ses *ses = sess_data->ses;
+ struct smb2_sess_setup_req *req;
+ struct TCP_Server_Info *server = ses->server;
+ unsigned int total_len;
+
+ rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ /* First session, not a reauthenticate */
+ req->sync_hdr.SessionId = 0;
+
+ /* if reconnect, we need to send previous sess id, otherwise it is 0 */
+ req->PreviousSessionId = sess_data->previous_session;
+
+ req->Flags = 0; /* MBZ */
+
+ /* enough to enable echos and oplocks and one max size write */
+ req->sync_hdr.CreditRequest = cpu_to_le16(130);
+
+ /* only one of SMB2 signing flags may be set in SMB2 request */
+ if (server->sign)
+ req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
+ req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ else
+ req->SecurityMode = 0;
+
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS);
+#else
+ req->Capabilities = 0;
+#endif /* DFS_UPCALL */
+
+ req->Channel = 0; /* MBZ */
+
+ sess_data->iov[0].iov_base = (char *)req;
+ /* 1 for pad */
+ sess_data->iov[0].iov_len = total_len - 1;
+ /*
+ * This variable will be used to clear the buffer
+ * allocated above in case of any error in the calling function.
+ */
+ sess_data->buf0_type = CIFS_SMALL_BUFFER;
+
+ return 0;
+}
+
+static void
+SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data)
+{
+ free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
+ sess_data->buf0_type = CIFS_NO_BUFFER;
+}
+
+static int
+SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
+{
+ int rc;
+ struct smb_rqst rqst;
+ struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
+ struct kvec rsp_iov = { NULL, 0 };
+
+ /* Testing shows that buffer offset must be at location of Buffer[0] */
+ req->SecurityBufferOffset =
+ cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */);
+ req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = sess_data->iov;
+ rqst.rq_nvec = 2;
+
+ /* BB add code to build os and lm fields */
+ rc = cifs_send_recv(sess_data->xid, sess_data->ses,
+ &rqst,
+ &sess_data->buf0_type,
+ CIFS_LOG_ERROR | CIFS_NEG_OP, &rsp_iov);
+ cifs_small_buf_release(sess_data->iov[0].iov_base);
+ memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
+
+ return rc;
+}
+
+static int
+SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
+{
+ int rc = 0;
+ struct cifs_ses *ses = sess_data->ses;
+
+ mutex_lock(&ses->server->srv_mutex);
+ if (ses->server->ops->generate_signingkey) {
+ rc = ses->server->ops->generate_signingkey(ses);
+ if (rc) {
+ cifs_dbg(FYI,
+ "SMB3 session key generation failed\n");
+ mutex_unlock(&ses->server->srv_mutex);
+ return rc;
+ }
+ }
+ if (!ses->server->session_estab) {
+ ses->server->sequence_number = 0x2;
+ ses->server->session_estab = true;
+ }
+ mutex_unlock(&ses->server->srv_mutex);
+
+ cifs_dbg(FYI, "SMB2/3 session established successfully\n");
+ spin_lock(&GlobalMid_Lock);
+ ses->status = CifsGood;
+ ses->need_reconnect = false;
+ spin_unlock(&GlobalMid_Lock);
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_UPCALL
+static void
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
+{
+ int rc;
+ struct cifs_ses *ses = sess_data->ses;
+ struct cifs_spnego_msg *msg;
+ struct key *spnego_key = NULL;
+ struct smb2_sess_setup_rsp *rsp = NULL;
+
+ rc = SMB2_sess_alloc_buffer(sess_data);
+ if (rc)
+ goto out;
+
+ spnego_key = cifs_get_spnego_key(ses);
+ if (IS_ERR(spnego_key)) {
+ rc = PTR_ERR(spnego_key);
+ if (rc == -ENOKEY)
+ cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n");
+ spnego_key = NULL;
+ goto out;
+ }
+
+ msg = spnego_key->payload.data[0];
+ /*
+ * check version field to make sure that cifs.upcall is
+ * sending us a response in an expected form
+ */
+ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
+ cifs_dbg(VFS,
+ "bad cifs.upcall version. Expected %d got %d",
+ CIFS_SPNEGO_UPCALL_VERSION, msg->version);
+ rc = -EKEYREJECTED;
+ goto out_put_spnego_key;
+ }
+
+ ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
+ GFP_KERNEL);
+ if (!ses->auth_key.response) {
+ cifs_dbg(VFS,
+ "Kerberos can't allocate (%u bytes) memory",
+ msg->sesskey_len);
+ rc = -ENOMEM;
+ goto out_put_spnego_key;
+ }
+ ses->auth_key.len = msg->sesskey_len;
+
+ sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
+ sess_data->iov[1].iov_len = msg->secblob_len;
+
+ rc = SMB2_sess_sendreceive(sess_data);
+ if (rc)
+ goto out_put_spnego_key;
+
+ rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+ ses->Suid = rsp->sync_hdr.SessionId;
+
+ ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+
+ rc = SMB2_sess_establish_session(sess_data);
+out_put_spnego_key:
+ key_invalidate(spnego_key);
+ key_put(spnego_key);
+out:
+ sess_data->result = rc;
+ sess_data->func = NULL;
+ SMB2_sess_free_buffer(sess_data);
+}
+#else
+static void
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
+{
+ cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
+ sess_data->result = -EOPNOTSUPP;
+ sess_data->func = NULL;
+}
+#endif
+
+static void
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
+
+static void
+SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
+{
+ int rc;
+ struct cifs_ses *ses = sess_data->ses;
+ struct smb2_sess_setup_rsp *rsp = NULL;
+ char *ntlmssp_blob = NULL;
+ bool use_spnego = false; /* else use raw ntlmssp */
+ u16 blob_length = 0;
+
+ /*
+ * If memory allocation is successful, caller of this function
+ * frees it.
+ */
+ ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
+ if (!ses->ntlmssp) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ ses->ntlmssp->sesskey_per_smbsess = true;
+
+ rc = SMB2_sess_alloc_buffer(sess_data);
+ if (rc)
+ goto out_err;
+
+ ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
+ GFP_KERNEL);
+ if (ntlmssp_blob == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
+ if (use_spnego) {
+ /* BB eventually need to add this */
+ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
+ rc = -EOPNOTSUPP;
+ goto out;
+ } else {
+ blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
+ /* with raw NTLMSSP we don't encapsulate in SPNEGO */
+ }
+ sess_data->iov[1].iov_base = ntlmssp_blob;
+ sess_data->iov[1].iov_len = blob_length;
+
+ rc = SMB2_sess_sendreceive(sess_data);
+ rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+
+ /* If true, rc here is expected and not an error */
+ if (sess_data->buf0_type != CIFS_NO_BUFFER &&
+ rsp->sync_hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
+ rc = 0;
+
+ if (rc)
+ goto out;
+
+ if (offsetof(struct smb2_sess_setup_rsp, Buffer) !=
+ le16_to_cpu(rsp->SecurityBufferOffset)) {
+ cifs_dbg(VFS, "Invalid security buffer offset %d\n",
+ le16_to_cpu(rsp->SecurityBufferOffset));
+ rc = -EIO;
+ goto out;
+ }
+ rc = decode_ntlmssp_challenge(rsp->Buffer,
+ le16_to_cpu(rsp->SecurityBufferLength), ses);
+ if (rc)
+ goto out;
+
+ cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
+
+
+ ses->Suid = rsp->sync_hdr.SessionId;
+ ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+
+out:
+ kfree(ntlmssp_blob);
+ SMB2_sess_free_buffer(sess_data);
+ if (!rc) {
+ sess_data->result = 0;
+ sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
+ return;
+ }
+out_err:
+ kfree(ses->ntlmssp);
+ ses->ntlmssp = NULL;
+ sess_data->result = rc;
+ sess_data->func = NULL;
+}
+
+static void
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
+{
+ int rc;
+ struct cifs_ses *ses = sess_data->ses;
+ struct smb2_sess_setup_req *req;
+ struct smb2_sess_setup_rsp *rsp = NULL;
+ unsigned char *ntlmssp_blob = NULL;
+ bool use_spnego = false; /* else use raw ntlmssp */
+ u16 blob_length = 0;
+
+ rc = SMB2_sess_alloc_buffer(sess_data);
+ if (rc)
+ goto out;
+
+ req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
+ req->sync_hdr.SessionId = ses->Suid;
+
+ rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
+ sess_data->nls_cp);
+ if (rc) {
+ cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
+ goto out;
+ }
+
+ if (use_spnego) {
+ /* BB eventually need to add this */
+ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ sess_data->iov[1].iov_base = ntlmssp_blob;
+ sess_data->iov[1].iov_len = blob_length;
+
+ rc = SMB2_sess_sendreceive(sess_data);
+ if (rc)
+ goto out;
+
+ rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+
+ ses->Suid = rsp->sync_hdr.SessionId;
+ ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+
+ rc = SMB2_sess_establish_session(sess_data);
+out:
+ kfree(ntlmssp_blob);
+ SMB2_sess_free_buffer(sess_data);
+ kfree(ses->ntlmssp);
+ ses->ntlmssp = NULL;
+ sess_data->result = rc;
+ sess_data->func = NULL;
+}
+
+static int
+SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
+{
+ int type;
+
+ type = smb2_select_sectype(ses->server, ses->sectype);
+ cifs_dbg(FYI, "sess setup type %d\n", type);
+ if (type == Unspecified) {
+ cifs_dbg(VFS,
+ "Unable to select appropriate authentication method!");
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case Kerberos:
+ sess_data->func = SMB2_auth_kerberos;
+ break;
+ case RawNTLMSSP:
+ sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
+ break;
+ default:
+ cifs_dbg(VFS, "secType %d not supported!\n", type);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int
+SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ struct TCP_Server_Info *server = ses->server;
+ struct SMB2_sess_data *sess_data;
+
+ cifs_dbg(FYI, "Session Setup\n");
+
+ if (!server) {
+ WARN(1, "%s: server is NULL!\n", __func__);
+ return -EIO;
+ }
+
+ sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
+ if (!sess_data)
+ return -ENOMEM;
+
+ rc = SMB2_select_sec(ses, sess_data);
+ if (rc)
+ goto out;
+ sess_data->xid = xid;
+ sess_data->ses = ses;
+ sess_data->buf0_type = CIFS_NO_BUFFER;
+ sess_data->nls_cp = (struct nls_table *) nls_cp;
+ sess_data->previous_session = ses->Suid;
+
+ /*
+ * Initialize the session hash with the server one.
+ */
+ memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash,
+ SMB2_PREAUTH_HASH_SIZE);
+
+ while (sess_data->func)
+ sess_data->func(sess_data);
+
+ if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign))
+ cifs_dbg(VFS, "signing requested but authenticated as guest\n");
+ rc = sess_data->result;
+out:
+ kfree(sess_data);
+ return rc;
+}
+
+int
+SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
+{
+ struct smb_rqst rqst;
+ struct smb2_logoff_req *req; /* response is also trivial struct */
+ int rc = 0;
+ struct TCP_Server_Info *server;
+ int flags = 0;
+ unsigned int total_len;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ cifs_dbg(FYI, "disconnect session %p\n", ses);
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ /* no need to send SMB logoff if uid already closed due to reconnect */
+ if (ses->need_reconnect)
+ goto smb2_session_already_dead;
+
+ rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ /* since no tcon, smb2_init can not do this, so do here */
+ req->sync_hdr.SessionId = ses->Suid;
+
+ if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+ flags |= CIFS_TRANSFORM_REQ;
+ else if (server->sign)
+ req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
+ flags |= CIFS_NO_RESP;
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ /*
+ * No tcon so can't do
+ * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
+ */
+
+smb2_session_already_dead:
+ return rc;
+}
+
+static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code)
+{
+ cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]);
+}
+
+#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */)
+
+/* These are similar values to what Windows uses */
+static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon)
+{
+ tcon->max_chunks = 256;
+ tcon->max_bytes_chunk = 1048576;
+ tcon->max_bytes_copy = 16777216;
+}
+
+int
+SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
+ struct cifs_tcon *tcon, const struct nls_table *cp)
+{
+ struct smb_rqst rqst;
+ struct smb2_tree_connect_req *req;
+ struct smb2_tree_connect_rsp *rsp = NULL;
+ struct kvec iov[2];
+ struct kvec rsp_iov = { NULL, 0 };
+ int rc = 0;
+ int resp_buftype;
+ int unc_path_len;
+ __le16 *unc_path = NULL;
+ int flags = 0;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "TCON\n");
+
+ if (!(ses->server) || !tree)
+ return -EIO;
+
+ unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
+ if (unc_path == NULL)
+ return -ENOMEM;
+
+ unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1;
+ unc_path_len *= 2;
+ if (unc_path_len < 2) {
+ kfree(unc_path);
+ return -EINVAL;
+ }
+
+ /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
+ tcon->tid = 0;
+
+ rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, (void **) &req,
+ &total_len);
+ if (rc) {
+ kfree(unc_path);
+ return rc;
+ }
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ iov[0].iov_base = (char *)req;
+ /* 1 for pad */
+ iov[0].iov_len = total_len - 1;
+
+ /* Testing shows that buffer offset must be at location of Buffer[0] */
+ req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req)
+ - 1 /* pad */);
+ req->PathLength = cpu_to_le16(unc_path_len - 2);
+ iov[1].iov_base = unc_path;
+ iov[1].iov_len = unc_path_len;
+
+ /*
+ * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1
+ * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1
+ * (Samba servers don't always set the flag so also check if null user)
+ */
+ if ((ses->server->dialect == SMB311_PROT_ID) &&
+ !smb3_encryption_required(tcon) &&
+ !(ses->session_flags &
+ (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) &&
+ ((ses->user_name != NULL) || (ses->sectype == Kerberos)))
+ req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ if (tcon) {
+ cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE);
+ tcon->need_reconnect = true;
+ }
+ goto tcon_error_exit;
+ }
+
+ switch (rsp->ShareType) {
+ case SMB2_SHARE_TYPE_DISK:
+ cifs_dbg(FYI, "connection to disk share\n");
+ break;
+ case SMB2_SHARE_TYPE_PIPE:
+ tcon->pipe = true;
+ cifs_dbg(FYI, "connection to pipe share\n");
+ break;
+ case SMB2_SHARE_TYPE_PRINT:
+ tcon->print = true;
+ cifs_dbg(FYI, "connection to printer\n");
+ break;
+ default:
+ cifs_dbg(VFS, "unknown share type %d\n", rsp->ShareType);
+ rc = -EOPNOTSUPP;
+ goto tcon_error_exit;
+ }
+
+ tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
+ tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
+ tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
+ tcon->tidStatus = CifsGood;
+ tcon->need_reconnect = false;
+ tcon->tid = rsp->sync_hdr.TreeId;
+ strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
+
+ if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
+ ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
+ cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
+
+ if (tcon->seal &&
+ !(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
+ cifs_dbg(VFS, "Encryption is requested but not supported\n");
+
+ init_copy_chunk_defaults(tcon);
+ if (tcon->ses->server->ops->validate_negotiate)
+ rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
+tcon_exit:
+ free_rsp_buf(resp_buftype, rsp);
+ kfree(unc_path);
+ return rc;
+
+tcon_error_exit:
+ if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) {
+ cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
+ }
+ goto tcon_exit;
+}
+
+int
+SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
+{
+ struct smb_rqst rqst;
+ struct smb2_tree_disconnect_req *req; /* response is trivial */
+ int rc = 0;
+ struct cifs_ses *ses = tcon->ses;
+ int flags = 0;
+ unsigned int total_len;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ cifs_dbg(FYI, "Tree Disconnect\n");
+
+ if (!ses || !(ses->server))
+ return -EIO;
+
+ if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
+ return 0;
+
+ rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ flags |= CIFS_NO_RESP;
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ if (rc)
+ cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE);
+
+ return rc;
+}
+
+
+static struct create_durable *
+create_durable_buf(void)
+{
+ struct create_durable *buf;
+
+ buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_durable, Data));
+ buf->ccontext.DataLength = cpu_to_le32(16);
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_durable, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */
+ buf->Name[0] = 'D';
+ buf->Name[1] = 'H';
+ buf->Name[2] = 'n';
+ buf->Name[3] = 'Q';
+ return buf;
+}
+
+static struct create_durable *
+create_reconnect_durable_buf(struct cifs_fid *fid)
+{
+ struct create_durable *buf;
+
+ buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_durable, Data));
+ buf->ccontext.DataLength = cpu_to_le32(16);
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_durable, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ buf->Data.Fid.PersistentFileId = fid->persistent_fid;
+ buf->Data.Fid.VolatileFileId = fid->volatile_fid;
+ /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */
+ buf->Name[0] = 'D';
+ buf->Name[1] = 'H';
+ buf->Name[2] = 'n';
+ buf->Name[3] = 'C';
+ return buf;
+}
+
+static __u8
+parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp,
+ unsigned int *epoch, char *lease_key)
+{
+ char *data_offset;
+ struct create_context *cc;
+ unsigned int next;
+ unsigned int remaining;
+ char *name;
+
+ data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset);
+ remaining = le32_to_cpu(rsp->CreateContextsLength);
+ cc = (struct create_context *)data_offset;
+ while (remaining >= sizeof(struct create_context)) {
+ name = le16_to_cpu(cc->NameOffset) + (char *)cc;
+ if (le16_to_cpu(cc->NameLength) == 4 &&
+ strncmp(name, "RqLs", 4) == 0)
+ return server->ops->parse_lease_buf(cc, epoch,
+ lease_key);
+
+ next = le32_to_cpu(cc->Next);
+ if (!next)
+ break;
+ remaining -= next;
+ cc = (struct create_context *)((char *)cc + next);
+ }
+
+ return 0;
+}
+
+static int
+add_lease_context(struct TCP_Server_Info *server, struct kvec *iov,
+ unsigned int *num_iovec, u8 *lease_key, __u8 *oplock)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock);
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = server->vals->create_lease_size;
+ req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset = cpu_to_le32(
+ sizeof(struct smb2_create_req) +
+ iov[num - 1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength,
+ server->vals->create_lease_size);
+ *num_iovec = num + 1;
+ return 0;
+}
+
+static struct create_durable_v2 *
+create_durable_v2_buf(struct cifs_fid *pfid)
+{
+ struct create_durable_v2 *buf;
+
+ buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_durable_v2, dcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2));
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_durable_v2, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+
+ buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
+ buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+ generate_random_uuid(buf->dcontext.CreateGuid);
+ memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
+
+ /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
+ buf->Name[0] = 'D';
+ buf->Name[1] = 'H';
+ buf->Name[2] = '2';
+ buf->Name[3] = 'Q';
+ return buf;
+}
+
+static struct create_durable_handle_reconnect_v2 *
+create_reconnect_durable_v2_buf(struct cifs_fid *fid)
+{
+ struct create_durable_handle_reconnect_v2 *buf;
+
+ buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2),
+ GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset =
+ cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2,
+ dcontext));
+ buf->ccontext.DataLength =
+ cpu_to_le32(sizeof(struct durable_reconnect_context_v2));
+ buf->ccontext.NameOffset =
+ cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2,
+ Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+
+ buf->dcontext.Fid.PersistentFileId = fid->persistent_fid;
+ buf->dcontext.Fid.VolatileFileId = fid->volatile_fid;
+ buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+ memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16);
+
+ /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */
+ buf->Name[0] = 'D';
+ buf->Name[1] = 'H';
+ buf->Name[2] = '2';
+ buf->Name[3] = 'C';
+ return buf;
+}
+
+static int
+add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
+ struct cifs_open_parms *oparms)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ iov[num].iov_base = create_durable_v2_buf(oparms->fid);
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct create_durable_v2);
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset =
+ cpu_to_le32(sizeof(struct smb2_create_req) +
+ iov[1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2));
+ *num_iovec = num + 1;
+ return 0;
+}
+
+static int
+add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec,
+ struct cifs_open_parms *oparms)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ /* indicate that we don't need to relock the file */
+ oparms->reconnect = false;
+
+ iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid);
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2);
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset =
+ cpu_to_le32(sizeof(struct smb2_create_req) +
+ iov[1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength,
+ sizeof(struct create_durable_handle_reconnect_v2));
+ *num_iovec = num + 1;
+ return 0;
+}
+
+static int
+add_durable_context(struct kvec *iov, unsigned int *num_iovec,
+ struct cifs_open_parms *oparms, bool use_persistent)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ if (use_persistent) {
+ if (oparms->reconnect)
+ return add_durable_reconnect_v2_context(iov, num_iovec,
+ oparms);
+ else
+ return add_durable_v2_context(iov, num_iovec, oparms);
+ }
+
+ if (oparms->reconnect) {
+ iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
+ /* indicate that we don't need to relock the file */
+ oparms->reconnect = false;
+ } else
+ iov[num].iov_base = create_durable_buf();
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct create_durable);
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset =
+ cpu_to_le32(sizeof(struct smb2_create_req) +
+ iov[1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable));
+ *num_iovec = num + 1;
+ return 0;
+}
+
+/* See MS-SMB2 2.2.13.2.7 */
+static struct crt_twarp_ctxt *
+create_twarp_buf(__u64 timewarp)
+{
+ struct crt_twarp_ctxt *buf;
+
+ buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct crt_twarp_ctxt, Timestamp));
+ buf->ccontext.DataLength = cpu_to_le32(8);
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct crt_twarp_ctxt, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */
+ buf->Name[0] = 'T';
+ buf->Name[1] = 'W';
+ buf->Name[2] = 'r';
+ buf->Name[3] = 'p';
+ buf->Timestamp = cpu_to_le64(timewarp);
+ return buf;
+}
+
+/* See MS-SMB2 2.2.13.2.7 */
+static int
+add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ iov[num].iov_base = create_twarp_buf(timewarp);
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct crt_twarp_ctxt);
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset = cpu_to_le32(
+ sizeof(struct smb2_create_req) +
+ iov[num - 1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt));
+ *num_iovec = num + 1;
+ return 0;
+}
+
+static int
+alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
+ const char *treename, const __le16 *path)
+{
+ int treename_len, path_len;
+ struct nls_table *cp;
+ const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)};
+
+ /*
+ * skip leading "\\"
+ */
+ treename_len = strlen(treename);
+ if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\'))
+ return -EINVAL;
+
+ treename += 2;
+ treename_len -= 2;
+
+ path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
+
+ /*
+ * make room for one path separator between the treename and
+ * path
+ */
+ *out_len = treename_len + 1 + path_len;
+
+ /*
+ * final path needs to be null-terminated UTF16 with a
+ * size aligned to 8
+ */
+
+ *out_size = roundup((*out_len+1)*2, 8);
+ *out_path = kzalloc(*out_size, GFP_KERNEL);
+ if (!*out_path)
+ return -ENOMEM;
+
+ cp = load_nls_default();
+ cifs_strtoUTF16(*out_path, treename, treename_len, cp);
+ UniStrcat(*out_path, sep);
+ UniStrcat(*out_path, path);
+ unload_nls(cp);
+
+ return 0;
+}
+
+int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ umode_t mode, struct cifs_tcon *tcon,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb)
+{
+ struct smb_rqst rqst;
+ struct smb2_create_req *req;
+ struct smb2_create_rsp *rsp = NULL;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[3]; /* make sure at least one for each open context */
+ struct kvec rsp_iov = {NULL, 0};
+ int resp_buftype;
+ int uni_path_len;
+ __le16 *copy_path = NULL;
+ int copy_size;
+ int rc = 0;
+ unsigned int n_iov = 2;
+ __u32 file_attributes = 0;
+ char *pc_buf = NULL;
+ int flags = 0;
+ unsigned int total_len;
+ __le16 *utf16_path = NULL;
+
+ cifs_dbg(FYI, "mkdir\n");
+
+ /* resource #1: path allocation */
+ utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else {
+ rc = -EIO;
+ goto err_free_path;
+ }
+
+ /* resource #2: request */
+ rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len);
+ if (rc)
+ goto err_free_path;
+
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->ImpersonationLevel = IL_IMPERSONATION;
+ req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES);
+ /* File attributes ignored on open (used in create though) */
+ req->FileAttributes = cpu_to_le32(file_attributes);
+ req->ShareAccess = FILE_SHARE_ALL_LE;
+ req->CreateDisposition = cpu_to_le32(FILE_CREATE);
+ req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE);
+
+ iov[0].iov_base = (char *)req;
+ /* -1 since last byte is buf[0] which is sent below (path) */
+ iov[0].iov_len = total_len - 1;
+
+ req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
+
+ /* [MS-SMB2] 2.2.13 NameOffset:
+ * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
+ * the SMB2 header, the file name includes a prefix that will
+ * be processed during DFS name normalization as specified in
+ * section 3.3.5.9. Otherwise, the file name is relative to
+ * the share that is identified by the TreeId in the SMB2
+ * header.
+ */
+ if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+ int name_len;
+
+ req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+ rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
+ &name_len,
+ tcon->treeName, utf16_path);
+ if (rc)
+ goto err_free_req;
+
+ req->NameLength = cpu_to_le16(name_len * 2);
+ uni_path_len = copy_size;
+ /* free before overwriting resource */
+ kfree(utf16_path);
+ utf16_path = copy_path;
+ } else {
+ uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2;
+ /* MUST set path len (NameLength) to 0 opening root of share */
+ req->NameLength = cpu_to_le16(uni_path_len - 2);
+ if (uni_path_len % 8 != 0) {
+ copy_size = roundup(uni_path_len, 8);
+ copy_path = kzalloc(copy_size, GFP_KERNEL);
+ if (!copy_path) {
+ rc = -ENOMEM;
+ goto err_free_req;
+ }
+ memcpy((char *)copy_path, (const char *)utf16_path,
+ uni_path_len);
+ uni_path_len = copy_size;
+ /* free before overwriting resource */
+ kfree(utf16_path);
+ utf16_path = copy_path;
+ }
+ }
+
+ iov[1].iov_len = uni_path_len;
+ iov[1].iov_base = utf16_path;
+ req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
+
+ if (tcon->posix_extensions) {
+ /* resource #3: posix buf */
+ rc = add_posix_context(iov, &n_iov, mode);
+ if (rc)
+ goto err_free_req;
+ pc_buf = iov[n_iov-1].iov_base;
+ }
+
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_iov;
+
+ /* resource #4: response buffer */
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
+ trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid,
+ CREATE_NOT_FILE,
+ FILE_WRITE_ATTRIBUTES, rc);
+ goto err_free_rsp_buf;
+ }
+
+ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
+ trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid,
+ ses->Suid, CREATE_NOT_FILE,
+ FILE_WRITE_ATTRIBUTES);
+
+ SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId);
+
+ /* Eventually save off posix specific response info and timestaps */
+
+err_free_rsp_buf:
+ free_rsp_buf(resp_buftype, rsp);
+ kfree(pc_buf);
+err_free_req:
+ cifs_small_buf_release(req);
+err_free_path:
+ kfree(utf16_path);
+ return rc;
+}
+
+int
+SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock,
+ struct cifs_open_parms *oparms, __le16 *path)
+{
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct smb2_create_req *req;
+ unsigned int n_iov = 2;
+ __u32 file_attributes = 0;
+ int copy_size;
+ int uni_path_len;
+ unsigned int total_len;
+ struct kvec *iov = rqst->rq_iov;
+ __le16 *copy_path;
+ int rc;
+
+ rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ iov[0].iov_base = (char *)req;
+ /* -1 since last byte is buf[0] which is sent below (path) */
+ iov[0].iov_len = total_len - 1;
+
+ if (oparms->create_options & CREATE_OPTION_READONLY)
+ file_attributes |= ATTR_READONLY;
+ if (oparms->create_options & CREATE_OPTION_SPECIAL)
+ file_attributes |= ATTR_SYSTEM;
+
+ req->ImpersonationLevel = IL_IMPERSONATION;
+ req->DesiredAccess = cpu_to_le32(oparms->desired_access);
+ /* File attributes ignored on open (used in create though) */
+ req->FileAttributes = cpu_to_le32(file_attributes);
+ req->ShareAccess = FILE_SHARE_ALL_LE;
+ req->CreateDisposition = cpu_to_le32(oparms->disposition);
+ req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
+ req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
+
+ /* [MS-SMB2] 2.2.13 NameOffset:
+ * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
+ * the SMB2 header, the file name includes a prefix that will
+ * be processed during DFS name normalization as specified in
+ * section 3.3.5.9. Otherwise, the file name is relative to
+ * the share that is identified by the TreeId in the SMB2
+ * header.
+ */
+ if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+ int name_len;
+
+ req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+ rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
+ &name_len,
+ tcon->treeName, path);
+ if (rc)
+ return rc;
+ req->NameLength = cpu_to_le16(name_len * 2);
+ uni_path_len = copy_size;
+ path = copy_path;
+ } else {
+ uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
+ /* MUST set path len (NameLength) to 0 opening root of share */
+ req->NameLength = cpu_to_le16(uni_path_len - 2);
+ copy_size = uni_path_len;
+ if (copy_size % 8 != 0)
+ copy_size = roundup(copy_size, 8);
+ copy_path = kzalloc(copy_size, GFP_KERNEL);
+ if (!copy_path)
+ return -ENOMEM;
+ memcpy((char *)copy_path, (const char *)path,
+ uni_path_len);
+ uni_path_len = copy_size;
+ path = copy_path;
+ }
+
+ iov[1].iov_len = uni_path_len;
+ iov[1].iov_base = path;
+
+ if ((!server->oplocks) || (tcon->no_lease))
+ *oplock = SMB2_OPLOCK_LEVEL_NONE;
+
+ if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
+ *oplock == SMB2_OPLOCK_LEVEL_NONE)
+ req->RequestedOplockLevel = *oplock;
+ else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) &&
+ (oparms->create_options & CREATE_NOT_FILE))
+ req->RequestedOplockLevel = *oplock; /* no srv lease support */
+ else {
+ rc = add_lease_context(server, iov, &n_iov,
+ oparms->fid->lease_key, oplock);
+ if (rc)
+ return rc;
+ }
+
+ if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
+ /* need to set Next field of lease context if we request it */
+ if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
+ struct create_context *ccontext =
+ (struct create_context *)iov[n_iov-1].iov_base;
+ ccontext->Next =
+ cpu_to_le32(server->vals->create_lease_size);
+ }
+
+ rc = add_durable_context(iov, &n_iov, oparms,
+ tcon->use_persistent);
+ if (rc)
+ return rc;
+ }
+
+ if (tcon->posix_extensions) {
+ if (n_iov > 2) {
+ struct create_context *ccontext =
+ (struct create_context *)iov[n_iov-1].iov_base;
+ ccontext->Next =
+ cpu_to_le32(iov[n_iov-1].iov_len);
+ }
+
+ rc = add_posix_context(iov, &n_iov, oparms->mode);
+ if (rc)
+ return rc;
+ }
+
+ if (tcon->snapshot_time) {
+ cifs_dbg(FYI, "adding snapshot context\n");
+ if (n_iov > 2) {
+ struct create_context *ccontext =
+ (struct create_context *)iov[n_iov-1].iov_base;
+ ccontext->Next =
+ cpu_to_le32(iov[n_iov-1].iov_len);
+ }
+
+ rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time);
+ if (rc)
+ return rc;
+ }
+
+
+ rqst->rq_nvec = n_iov;
+ return 0;
+}
+
+/* rq_iov[0] is the request and is released by cifs_small_buf_release().
+ * All other vectors are freed by kfree().
+ */
+void
+SMB2_open_free(struct smb_rqst *rqst)
+{
+ int i;
+
+ if (rqst && rqst->rq_iov) {
+ cifs_small_buf_release(rqst->rq_iov[0].iov_base);
+ for (i = 1; i < rqst->rq_nvec; i++)
+ if (rqst->rq_iov[i].iov_base != smb2_padding)
+ kfree(rqst->rq_iov[i].iov_base);
+ }
+}
+
+int
+SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+ __u8 *oplock, struct smb2_file_all_info *buf,
+ struct kvec *err_iov, int *buftype)
+{
+ struct smb_rqst rqst;
+ struct smb2_create_rsp *rsp = NULL;
+ struct TCP_Server_Info *server;
+ struct cifs_tcon *tcon = oparms->tcon;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[SMB2_CREATE_IOV_SIZE];
+ struct kvec rsp_iov = {NULL, 0};
+ int resp_buftype = CIFS_NO_BUFFER;
+ int rc = 0;
+ int flags = 0;
+
+ cifs_dbg(FYI, "create/open\n");
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ memset(&iov, 0, sizeof(iov));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = SMB2_CREATE_IOV_SIZE;
+
+ rc = SMB2_open_init(tcon, &rqst, oplock, oparms, path);
+ if (rc)
+ goto creat_exit;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
+ &rsp_iov);
+ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
+ if (err_iov && rsp) {
+ *err_iov = rsp_iov;
+ *buftype = resp_buftype;
+ resp_buftype = CIFS_NO_BUFFER;
+ rsp = NULL;
+ }
+ trace_smb3_open_err(xid, tcon->tid, ses->Suid,
+ oparms->create_options, oparms->desired_access, rc);
+ goto creat_exit;
+ } else
+ trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid,
+ ses->Suid, oparms->create_options,
+ oparms->desired_access);
+
+ oparms->fid->persistent_fid = rsp->PersistentFileId;
+ oparms->fid->volatile_fid = rsp->VolatileFileId;
+
+ if (buf) {
+ memcpy(buf, &rsp->CreationTime, 32);
+ buf->AllocationSize = rsp->AllocationSize;
+ buf->EndOfFile = rsp->EndofFile;
+ buf->Attributes = rsp->FileAttributes;
+ buf->NumberOfLinks = cpu_to_le32(1);
+ buf->DeletePending = 0;
+ }
+
+ if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
+ *oplock = parse_lease_state(server, rsp, &oparms->fid->epoch,
+ oparms->fid->lease_key);
+ else
+ *oplock = rsp->OplockLevel;
+creat_exit:
+ SMB2_open_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+/*
+ * SMB2 IOCTL is used for both IOCTLs and FSCTLs
+ */
+int
+SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ u64 volatile_fid, u32 opcode, bool is_fsctl,
+ char *in_data, u32 indatalen,
+ char **out_data, u32 *plen /* returned data len */)
+{
+ struct smb_rqst rqst;
+ struct smb2_ioctl_req *req;
+ struct smb2_ioctl_rsp *rsp;
+ struct cifs_ses *ses;
+ struct kvec iov[2];
+ struct kvec rsp_iov;
+ int resp_buftype;
+ int n_iov;
+ int rc = 0;
+ int flags = 0;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "SMB2 IOCTL\n");
+
+ if (out_data != NULL)
+ *out_data = NULL;
+
+ /* zero out returned data len, in case of error */
+ if (plen)
+ *plen = 0;
+
+ if (tcon)
+ ses = tcon->ses;
+ else
+ return -EIO;
+
+ if (!ses || !(ses->server))
+ return -EIO;
+
+ rc = smb2_ioctl_req_init(opcode, tcon, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->CtlCode = cpu_to_le32(opcode);
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+ if (indatalen) {
+ req->InputCount = cpu_to_le32(indatalen);
+ /* do not set InputOffset if no input data */
+ req->InputOffset =
+ cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer));
+ iov[1].iov_base = in_data;
+ iov[1].iov_len = indatalen;
+ n_iov = 2;
+ } else
+ n_iov = 1;
+
+ req->OutputOffset = 0;
+ req->OutputCount = 0; /* MBZ */
+
+ /*
+ * Could increase MaxOutputResponse, but that would require more
+ * than one credit. Windows typically sets this smaller, but for some
+ * ioctls it may be useful to allow server to send more. No point
+ * limiting what the server can send as long as fits in one credit
+ * Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
+ * (by default, note that it can be overridden to make max larger)
+ * in responses (except for read responses which can be bigger.
+ * We may want to bump this limit up
+ */
+ req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize);
+
+ if (is_fsctl)
+ req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
+ else
+ req->Flags = 0;
+
+ iov[0].iov_base = (char *)req;
+
+ /*
+ * If no input data, the size of ioctl struct in
+ * protocol spec still includes a 1 byte data buffer,
+ * but if input data passed to ioctl, we do not
+ * want to double count this, so we do not send
+ * the dummy one byte of data in iovec[0] if sending
+ * input data (in iovec[1]).
+ */
+
+ if (indatalen) {
+ iov[0].iov_len = total_len - 1;
+ } else
+ iov[0].iov_len = total_len;
+
+ /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
+ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
+ req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_iov;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
+ &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0)
+ trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
+ ses->Suid, 0, opcode, rc);
+
+ if ((rc != 0) && (rc != -EINVAL)) {
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+ goto ioctl_exit;
+ } else if (rc == -EINVAL) {
+ if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
+ (opcode != FSCTL_SRV_COPYCHUNK)) {
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+ goto ioctl_exit;
+ }
+ }
+
+ /* check if caller wants to look at return data or just return rc */
+ if ((plen == NULL) || (out_data == NULL))
+ goto ioctl_exit;
+
+ *plen = le32_to_cpu(rsp->OutputCount);
+
+ /* We check for obvious errors in the output buffer length and offset */
+ if (*plen == 0)
+ goto ioctl_exit; /* server returned no data */
+ else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) {
+ cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
+ *plen = 0;
+ rc = -EIO;
+ goto ioctl_exit;
+ }
+
+ if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) {
+ cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
+ le32_to_cpu(rsp->OutputOffset));
+ *plen = 0;
+ rc = -EIO;
+ goto ioctl_exit;
+ }
+
+ *out_data = kmalloc(*plen, GFP_KERNEL);
+ if (*out_data == NULL) {
+ rc = -ENOMEM;
+ goto ioctl_exit;
+ }
+
+ memcpy(*out_data, (char *)rsp + le32_to_cpu(rsp->OutputOffset), *plen);
+ioctl_exit:
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+/*
+ * Individual callers to ioctl worker function follow
+ */
+
+int
+SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid)
+{
+ int rc;
+ struct compress_ioctl fsctl_input;
+ char *ret_data = NULL;
+
+ fsctl_input.CompressionState =
+ cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
+
+ rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
+ FSCTL_SET_COMPRESSION, true /* is_fsctl */,
+ (char *)&fsctl_input /* data input */,
+ 2 /* in data len */, &ret_data /* out data */, NULL);
+
+ cifs_dbg(FYI, "set compression rc %d\n", rc);
+
+ return rc;
+}
+
+int
+SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
+ u64 persistent_fid, u64 volatile_fid)
+{
+ struct smb2_close_req *req;
+ struct kvec *iov = rqst->rq_iov;
+ unsigned int total_len;
+ int rc;
+
+ rc = smb2_plain_req_init(SMB2_CLOSE, tcon, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ return 0;
+}
+
+void
+SMB2_close_free(struct smb_rqst *rqst)
+{
+ if (rqst && rqst->rq_iov)
+ cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
+}
+
+int
+SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int flags)
+{
+ struct smb_rqst rqst;
+ struct smb2_close_rsp *rsp = NULL;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buftype = CIFS_NO_BUFFER;
+ int rc = 0;
+
+ cifs_dbg(FYI, "Close\n");
+
+ if (!ses || !(ses->server))
+ return -EIO;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ memset(&iov, 0, sizeof(iov));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid);
+ if (rc)
+ goto close_exit;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE);
+ trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid,
+ rc);
+ goto close_exit;
+ }
+
+ /* BB FIXME - decode close response, update inode for caching */
+
+close_exit:
+ SMB2_close_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+int
+SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid)
+{
+ int rc;
+ int tmp_rc;
+
+ rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
+
+ /* retry close in a worker thread if this one is interrupted */
+ if (rc == -EINTR) {
+ tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
+ volatile_fid);
+ if (tmp_rc)
+ cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
+ persistent_fid, tmp_rc);
+ }
+
+ return rc;
+}
+
+int
+smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
+ struct kvec *iov, unsigned int min_buf_size)
+{
+ unsigned int smb_len = iov->iov_len;
+ char *end_of_smb = smb_len + (char *)iov->iov_base;
+ char *begin_of_buf = offset + (char *)iov->iov_base;
+ char *end_of_buf = begin_of_buf + buffer_length;
+
+
+ if (buffer_length < min_buf_size) {
+ cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n",
+ buffer_length, min_buf_size);
+ return -EINVAL;
+ }
+
+ /* check if beyond RFC1001 maximum length */
+ if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) {
+ cifs_dbg(VFS, "buffer length %d or smb length %d too large\n",
+ buffer_length, smb_len);
+ return -EINVAL;
+ }
+
+ if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) {
+ cifs_dbg(VFS, "illegal server response, bad offset to data\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * If SMB buffer fields are valid, copy into temporary buffer to hold result.
+ * Caller must free buffer.
+ */
+static int
+validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
+ struct kvec *iov, unsigned int minbufsize,
+ char *data)
+{
+ char *begin_of_buf = offset + (char *)iov->iov_base;
+ int rc;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize);
+ if (rc)
+ return rc;
+
+ memcpy(data, begin_of_buf, buffer_length);
+
+ return 0;
+}
+
+int
+SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
+ u64 persistent_fid, u64 volatile_fid,
+ u8 info_class, u8 info_type, u32 additional_info,
+ size_t output_len)
+{
+ struct smb2_query_info_req *req;
+ struct kvec *iov = rqst->rq_iov;
+ unsigned int total_len;
+ int rc;
+
+ rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ req->InfoType = info_type;
+ req->FileInfoClass = info_class;
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+ req->AdditionalInformation = cpu_to_le32(additional_info);
+
+ /*
+ * We do not use the input buffer (do not send extra byte)
+ */
+ req->InputBufferOffset = 0;
+
+ req->OutputBufferLength = cpu_to_le32(output_len);
+
+ iov[0].iov_base = (char *)req;
+ /* 1 for Buffer */
+ iov[0].iov_len = total_len - 1;
+ return 0;
+}
+
+void
+SMB2_query_info_free(struct smb_rqst *rqst)
+{
+ if (rqst && rqst->rq_iov)
+ cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
+}
+
+static int
+query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
+ u32 additional_info, size_t output_len, size_t min_len, void **data,
+ u32 *dlen)
+{
+ struct smb_rqst rqst;
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype = CIFS_NO_BUFFER;
+ struct cifs_ses *ses = tcon->ses;
+ int flags = 0;
+
+ cifs_dbg(FYI, "Query Info\n");
+
+ if (!ses || !(ses->server))
+ return -EIO;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ memset(&iov, 0, sizeof(iov));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = SMB2_query_info_init(tcon, &rqst, persistent_fid, volatile_fid,
+ info_class, info_type, additional_info,
+ output_len);
+ if (rc)
+ goto qinf_exit;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ trace_smb3_query_info_err(xid, persistent_fid, tcon->tid,
+ ses->Suid, info_class, (__u32)info_type, rc);
+ goto qinf_exit;
+ }
+
+ if (dlen) {
+ *dlen = le32_to_cpu(rsp->OutputBufferLength);
+ if (!*data) {
+ *data = kmalloc(*dlen, GFP_KERNEL);
+ if (!*data) {
+ cifs_dbg(VFS,
+ "Error %d allocating memory for acl\n",
+ rc);
+ *dlen = 0;
+ goto qinf_exit;
+ }
+ }
+ }
+
+ rc = validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength),
+ &rsp_iov, min_len, *data);
+
+qinf_exit:
+ SMB2_query_info_free(&rqst);
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ int ea_buf_size, struct smb2_file_full_ea_info *data)
+{
+ return query_info(xid, tcon, persistent_fid, volatile_fid,
+ FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0,
+ ea_buf_size,
+ sizeof(struct smb2_file_full_ea_info),
+ (void **)&data,
+ NULL);
+}
+
+int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
+{
+ return query_info(xid, tcon, persistent_fid, volatile_fid,
+ FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
+ sizeof(struct smb2_file_all_info), (void **)&data,
+ NULL);
+}
+
+int
+SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ void **data, u32 *plen)
+{
+ __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
+ *plen = 0;
+
+ return query_info(xid, tcon, persistent_fid, volatile_fid,
+ 0, SMB2_O_INFO_SECURITY, additional_info,
+ SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen);
+}
+
+int
+SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid)
+{
+ return query_info(xid, tcon, persistent_fid, volatile_fid,
+ FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb2_file_internal_info),
+ sizeof(struct smb2_file_internal_info),
+ (void **)&uniqueid, NULL);
+}
+
+/*
+ * This is a no-op for now. We're not really interested in the reply, but
+ * rather in the fact that the server sent one and that server->lstrp
+ * gets updated.
+ *
+ * FIXME: maybe we should consider checking that the reply matches request?
+ */
+static void
+smb2_echo_callback(struct mid_q_entry *mid)
+{
+ struct TCP_Server_Info *server = mid->callback_data;
+ struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
+ unsigned int credits_received = 0;
+
+ if (mid->mid_state == MID_RESPONSE_RECEIVED
+ || mid->mid_state == MID_RESPONSE_MALFORMED)
+ credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
+
+ DeleteMidQEntry(mid);
+ add_credits(server, credits_received, CIFS_ECHO_OP);
+}
+
+void smb2_reconnect_server(struct work_struct *work)
+{
+ struct TCP_Server_Info *server = container_of(work,
+ struct TCP_Server_Info, reconnect.work);
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon, *tcon2;
+ struct list_head tmp_list;
+ int tcon_exist = false;
+ int rc;
+ int resched = false;
+
+
+ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
+ mutex_lock(&server->reconnect_mutex);
+
+ INIT_LIST_HEAD(&tmp_list);
+ cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->need_reconnect || tcon->need_reopen_files) {
+ tcon->tc_count++;
+ list_add_tail(&tcon->rlist, &tmp_list);
+ tcon_exist = true;
+ }
+ }
+ if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
+ list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
+ tcon_exist = true;
+ }
+ }
+ /*
+ * Get the reference to server struct to be sure that the last call of
+ * cifs_put_tcon() in the loop below won't release the server pointer.
+ */
+ if (tcon_exist)
+ server->srv_count++;
+
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
+ rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon);
+ if (!rc)
+ cifs_reopen_persistent_handles(tcon);
+ else
+ resched = true;
+ list_del_init(&tcon->rlist);
+ cifs_put_tcon(tcon);
+ }
+
+ cifs_dbg(FYI, "Reconnecting tcons finished\n");
+ if (resched)
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
+ mutex_unlock(&server->reconnect_mutex);
+
+ /* now we can safely release srv struct */
+ if (tcon_exist)
+ cifs_put_tcp_session(server, 1);
+}
+
+int
+SMB2_echo(struct TCP_Server_Info *server)
+{
+ struct smb2_echo_req *req;
+ int rc = 0;
+ struct kvec iov[1];
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = 1 };
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "In echo request\n");
+
+ if (server->tcpStatus == CifsNeedNegotiate) {
+ /* No need to send echo on newly established connections */
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ return rc;
+ }
+
+ rc = smb2_plain_req_init(SMB2_ECHO, NULL, (void **)&req, &total_len);
+ if (rc)
+ return rc;
+
+ req->sync_hdr.CreditRequest = cpu_to_le16(1);
+
+ iov[0].iov_len = total_len;
+ iov[0].iov_base = (char *)req;
+
+ rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
+ server, CIFS_ECHO_OP);
+ if (rc)
+ cifs_dbg(FYI, "Echo request failed: %d\n", rc);
+
+ cifs_small_buf_release(req);
+ return rc;
+}
+
+int
+SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ u64 volatile_fid)
+{
+ struct smb_rqst rqst;
+ struct smb2_flush_req *req;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buftype;
+ int rc = 0;
+ int flags = 0;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "Flush\n");
+
+ if (!ses || !(ses->server))
+ return -EIO;
+
+ rc = smb2_plain_req_init(SMB2_FLUSH, tcon, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
+ trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid,
+ rc);
+ }
+
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+}
+
+/*
+ * To form a chain of read requests, any read requests after the first should
+ * have the end_of_chain boolean set to true.
+ */
+static int
+smb2_new_read_req(void **buf, unsigned int *total_len,
+ struct cifs_io_parms *io_parms, struct cifs_readdata *rdata,
+ unsigned int remaining_bytes, int request_type)
+{
+ int rc = -EACCES;
+ struct smb2_read_plain_req *req = NULL;
+ struct smb2_sync_hdr *shdr;
+ struct TCP_Server_Info *server;
+
+ rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, (void **) &req,
+ total_len);
+ if (rc)
+ return rc;
+
+ server = io_parms->tcon->ses->server;
+ if (server == NULL)
+ return -ECONNABORTED;
+
+ shdr = &req->sync_hdr;
+ shdr->ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+ req->VolatileFileId = io_parms->volatile_fid;
+ req->ReadChannelInfoOffset = 0; /* reserved */
+ req->ReadChannelInfoLength = 0; /* reserved */
+ req->Channel = 0; /* reserved */
+ req->MinimumCount = 0;
+ req->Length = cpu_to_le32(io_parms->length);
+ req->Offset = cpu_to_le64(io_parms->offset);
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ /*
+ * If we want to do a RDMA write, fill in and append
+ * smbd_buffer_descriptor_v1 to the end of read request
+ */
+ if (server->rdma && rdata && !server->sign &&
+ rdata->bytes >= server->smbd_conn->rdma_readwrite_threshold) {
+
+ struct smbd_buffer_descriptor_v1 *v1;
+ bool need_invalidate =
+ io_parms->tcon->ses->server->dialect == SMB30_PROT_ID;
+
+ rdata->mr = smbd_register_mr(
+ server->smbd_conn, rdata->pages,
+ rdata->nr_pages, rdata->page_offset,
+ rdata->tailsz, true, need_invalidate);
+ if (!rdata->mr)
+ return -ENOBUFS;
+
+ req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
+ if (need_invalidate)
+ req->Channel = SMB2_CHANNEL_RDMA_V1;
+ req->ReadChannelInfoOffset =
+ cpu_to_le16(offsetof(struct smb2_read_plain_req, Buffer));
+ req->ReadChannelInfoLength =
+ cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1));
+ v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0];
+ v1->offset = cpu_to_le64(rdata->mr->mr->iova);
+ v1->token = cpu_to_le32(rdata->mr->mr->rkey);
+ v1->length = cpu_to_le32(rdata->mr->mr->length);
+
+ *total_len += sizeof(*v1) - 1;
+ }
+#endif
+ if (request_type & CHAINED_REQUEST) {
+ if (!(request_type & END_OF_CHAIN)) {
+ /* next 8-byte aligned request */
+ *total_len = DIV_ROUND_UP(*total_len, 8) * 8;
+ shdr->NextCommand = cpu_to_le32(*total_len);
+ } else /* END_OF_CHAIN */
+ shdr->NextCommand = 0;
+ if (request_type & RELATED_REQUEST) {
+ shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
+ /*
+ * Related requests use info from previous read request
+ * in chain.
+ */
+ shdr->SessionId = 0xFFFFFFFFFFFFFFFF;
+ shdr->TreeId = 0xFFFFFFFF;
+ req->PersistentFileId = 0xFFFFFFFFFFFFFFFF;
+ req->VolatileFileId = 0xFFFFFFFFFFFFFFFF;
+ }
+ }
+ if (remaining_bytes > io_parms->length)
+ req->RemainingBytes = cpu_to_le32(remaining_bytes);
+ else
+ req->RemainingBytes = 0;
+
+ *buf = req;
+ return rc;
+}
+
+static void
+smb2_readv_callback(struct mid_q_entry *mid)
+{
+ struct cifs_readdata *rdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct smb2_sync_hdr *shdr =
+ (struct smb2_sync_hdr *)rdata->iov[0].iov_base;
+ unsigned int credits_received = 0;
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov[1],
+ .rq_nvec = 1,
+ .rq_pages = rdata->pages,
+ .rq_offset = rdata->page_offset,
+ .rq_npages = rdata->nr_pages,
+ .rq_pagesz = rdata->pagesz,
+ .rq_tailsz = rdata->tailsz };
+
+ cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
+ __func__, mid->mid, mid->mid_state, rdata->result,
+ rdata->bytes);
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ credits_received = le16_to_cpu(shdr->CreditRequest);
+ /* result already set, check signature */
+ if (server->sign && !mid->decrypted) {
+ int rc;
+
+ rc = smb2_verify_signature(&rqst, server);
+ if (rc)
+ cifs_dbg(VFS, "SMB signature verification returned error = %d\n",
+ rc);
+ }
+ /* FIXME: should this be counted toward the initiating task? */
+ task_io_account_read(rdata->got_bytes);
+ cifs_stats_bytes_read(tcon, rdata->got_bytes);
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ rdata->result = -EAGAIN;
+ if (server->sign && rdata->got_bytes)
+ /* reset bytes number since we can not check a sign */
+ rdata->got_bytes = 0;
+ /* FIXME: should this be counted toward the initiating task? */
+ task_io_account_read(rdata->got_bytes);
+ cifs_stats_bytes_read(tcon, rdata->got_bytes);
+ break;
+ case MID_RESPONSE_MALFORMED:
+ credits_received = le16_to_cpu(shdr->CreditRequest);
+ /* fall through */
+ default:
+ if (rdata->result != -ENODATA)
+ rdata->result = -EIO;
+ }
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ /*
+ * If this rdata has a memmory registered, the MR can be freed
+ * MR needs to be freed as soon as I/O finishes to prevent deadlock
+ * because they have limited number and are used for future I/Os
+ */
+ if (rdata->mr) {
+ smbd_deregister_mr(rdata->mr);
+ rdata->mr = NULL;
+ }
+#endif
+ if (rdata->result && rdata->result != -ENODATA) {
+ cifs_stats_fail_inc(tcon, SMB2_READ_HE);
+ trace_smb3_read_err(0 /* xid */,
+ rdata->cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid, rdata->offset,
+ rdata->bytes, rdata->result);
+ } else
+ trace_smb3_read_done(0 /* xid */,
+ rdata->cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid,
+ rdata->offset, rdata->got_bytes);
+
+ queue_work(cifsiod_wq, &rdata->work);
+ DeleteMidQEntry(mid);
+ add_credits(server, credits_received, 0);
+}
+
+/* smb2_async_readv - send an async read, and set up mid to handle result */
+int
+smb2_async_readv(struct cifs_readdata *rdata)
+{
+ int rc, flags = 0;
+ char *buf;
+ struct smb2_sync_hdr *shdr;
+ struct cifs_io_parms io_parms;
+ struct smb_rqst rqst = { .rq_iov = rdata->iov,
+ .rq_nvec = 1 };
+ struct TCP_Server_Info *server;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
+ __func__, rdata->offset, rdata->bytes);
+
+ io_parms.tcon = tlink_tcon(rdata->cfile->tlink);
+ io_parms.offset = rdata->offset;
+ io_parms.length = rdata->bytes;
+ io_parms.persistent_fid = rdata->cfile->fid.persistent_fid;
+ io_parms.volatile_fid = rdata->cfile->fid.volatile_fid;
+ io_parms.pid = rdata->pid;
+
+ server = io_parms.tcon->ses->server;
+
+ rc = smb2_new_read_req(
+ (void **) &buf, &total_len, &io_parms, rdata, 0, 0);
+ if (rc) {
+ if (rc == -EAGAIN && rdata->credits) {
+ /* credits was reset by reconnect */
+ rdata->credits = 0;
+ /* reduce in_flight value since we won't send the req */
+ spin_lock(&server->req_lock);
+ server->in_flight--;
+ spin_unlock(&server->req_lock);
+ }
+ return rc;
+ }
+
+ if (smb3_encryption_required(io_parms.tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ rdata->iov[0].iov_base = buf;
+ rdata->iov[0].iov_len = total_len;
+
+ shdr = (struct smb2_sync_hdr *)buf;
+
+ if (rdata->credits) {
+ shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
+ SMB2_MAX_BUFFER_SIZE));
+ shdr->CreditRequest =
+ cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
+ spin_lock(&server->req_lock);
+ server->credits += rdata->credits -
+ le16_to_cpu(shdr->CreditCharge);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ rdata->credits = le16_to_cpu(shdr->CreditCharge);
+ flags |= CIFS_HAS_CREDITS;
+ }
+
+ kref_get(&rdata->refcount);
+ rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
+ cifs_readv_receive, smb2_readv_callback,
+ smb3_handle_read_data, rdata, flags);
+ if (rc) {
+ kref_put(&rdata->refcount, cifs_readdata_release);
+ cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
+ trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid,
+ io_parms.tcon->tid,
+ io_parms.tcon->ses->Suid,
+ io_parms.offset, io_parms.length, rc);
+ }
+
+ cifs_small_buf_release(buf);
+ return rc;
+}
+
+int
+SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, char **buf, int *buf_type)
+{
+ struct smb_rqst rqst;
+ int resp_buftype, rc = -EACCES;
+ struct smb2_read_plain_req *req = NULL;
+ struct smb2_read_rsp *rsp = NULL;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ unsigned int total_len;
+ int flags = CIFS_LOG_ERROR;
+ struct cifs_ses *ses = io_parms->tcon->ses;
+
+ *nbytes = 0;
+ rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(io_parms->tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ if (rc != -ENODATA) {
+ cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE);
+ cifs_dbg(VFS, "Send error in read = %d\n", rc);
+ trace_smb3_read_err(xid, req->PersistentFileId,
+ io_parms->tcon->tid, ses->Suid,
+ io_parms->offset, io_parms->length,
+ rc);
+ }
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ cifs_small_buf_release(req);
+ return rc == -ENODATA ? 0 : rc;
+ } else
+ trace_smb3_read_done(xid, req->PersistentFileId,
+ io_parms->tcon->tid, ses->Suid,
+ io_parms->offset, io_parms->length);
+
+ cifs_small_buf_release(req);
+
+ *nbytes = le32_to_cpu(rsp->DataLength);
+ if ((*nbytes > CIFS_MAX_MSGSIZE) ||
+ (*nbytes > io_parms->length)) {
+ cifs_dbg(FYI, "bad length %d for count %d\n",
+ *nbytes, io_parms->length);
+ rc = -EIO;
+ *nbytes = 0;
+ }
+
+ if (*buf) {
+ memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes);
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ } else if (resp_buftype != CIFS_NO_BUFFER) {
+ *buf = rsp_iov.iov_base;
+ if (resp_buftype == CIFS_SMALL_BUFFER)
+ *buf_type = CIFS_SMALL_BUFFER;
+ else if (resp_buftype == CIFS_LARGE_BUFFER)
+ *buf_type = CIFS_LARGE_BUFFER;
+ }
+ return rc;
+}
+
+/*
+ * Check the mid_state and signature on received buffer (if any), and queue the
+ * workqueue completion task.
+ */
+static void
+smb2_writev_callback(struct mid_q_entry *mid)
+{
+ struct cifs_writedata *wdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ unsigned int written;
+ struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
+ unsigned int credits_received = 0;
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
+ wdata->result = smb2_check_receive(mid, tcon->ses->server, 0);
+ if (wdata->result != 0)
+ break;
+
+ written = le32_to_cpu(rsp->DataLength);
+ /*
+ * Mask off high 16 bits when bytes written as returned
+ * by the server is greater than bytes requested by the
+ * client. OS/2 servers are known to set incorrect
+ * CountHigh values.
+ */
+ if (written > wdata->bytes)
+ written &= 0xFFFF;
+
+ if (written < wdata->bytes)
+ wdata->result = -ENOSPC;
+ else
+ wdata->bytes = written;
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ wdata->result = -EAGAIN;
+ break;
+ case MID_RESPONSE_MALFORMED:
+ credits_received = le16_to_cpu(rsp->sync_hdr.CreditRequest);
+ /* fall through */
+ default:
+ wdata->result = -EIO;
+ break;
+ }
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ /*
+ * If this wdata has a memory registered, the MR can be freed
+ * The number of MRs available is limited, it's important to recover
+ * used MR as soon as I/O is finished. Hold MR longer in the later
+ * I/O process can possibly result in I/O deadlock due to lack of MR
+ * to send request on I/O retry
+ */
+ if (wdata->mr) {
+ smbd_deregister_mr(wdata->mr);
+ wdata->mr = NULL;
+ }
+#endif
+ if (wdata->result) {
+ cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
+ trace_smb3_write_err(0 /* no xid */,
+ wdata->cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid, wdata->offset,
+ wdata->bytes, wdata->result);
+ if (wdata->result == -ENOSPC)
+ printk_once(KERN_WARNING "Out of space writing to %s\n",
+ tcon->treeName);
+ } else
+ trace_smb3_write_done(0 /* no xid */,
+ wdata->cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid,
+ wdata->offset, wdata->bytes);
+
+ queue_work(cifsiod_wq, &wdata->work);
+ DeleteMidQEntry(mid);
+ add_credits(tcon->ses->server, credits_received, 0);
+}
+
+/* smb2_async_writev - send an async write, and set up mid to handle result */
+int
+smb2_async_writev(struct cifs_writedata *wdata,
+ void (*release)(struct kref *kref))
+{
+ int rc = -EACCES, flags = 0;
+ struct smb2_write_req *req = NULL;
+ struct smb2_sync_hdr *shdr;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct kvec iov[1];
+ struct smb_rqst rqst = { };
+ unsigned int total_len;
+
+ rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len);
+ if (rc) {
+ if (rc == -EAGAIN && wdata->credits) {
+ /* credits was reset by reconnect */
+ wdata->credits = 0;
+ /* reduce in_flight value since we won't send the req */
+ spin_lock(&server->req_lock);
+ server->in_flight--;
+ spin_unlock(&server->req_lock);
+ }
+ goto async_writev_out;
+ }
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ shdr = (struct smb2_sync_hdr *)req;
+ shdr->ProcessId = cpu_to_le32(wdata->cfile->pid);
+
+ req->PersistentFileId = wdata->cfile->fid.persistent_fid;
+ req->VolatileFileId = wdata->cfile->fid.volatile_fid;
+ req->WriteChannelInfoOffset = 0;
+ req->WriteChannelInfoLength = 0;
+ req->Channel = 0;
+ req->Offset = cpu_to_le64(wdata->offset);
+ req->DataOffset = cpu_to_le16(
+ offsetof(struct smb2_write_req, Buffer));
+ req->RemainingBytes = 0;
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ /*
+ * If we want to do a server RDMA read, fill in and append
+ * smbd_buffer_descriptor_v1 to the end of write request
+ */
+ if (server->rdma && !server->sign && wdata->bytes >=
+ server->smbd_conn->rdma_readwrite_threshold) {
+
+ struct smbd_buffer_descriptor_v1 *v1;
+ bool need_invalidate = server->dialect == SMB30_PROT_ID;
+
+ wdata->mr = smbd_register_mr(
+ server->smbd_conn, wdata->pages,
+ wdata->nr_pages, wdata->page_offset,
+ wdata->tailsz, false, need_invalidate);
+ if (!wdata->mr) {
+ rc = -ENOBUFS;
+ goto async_writev_out;
+ }
+ req->Length = 0;
+ req->DataOffset = 0;
+ if (wdata->nr_pages > 1)
+ req->RemainingBytes =
+ cpu_to_le32(
+ (wdata->nr_pages - 1) * wdata->pagesz -
+ wdata->page_offset + wdata->tailsz
+ );
+ else
+ req->RemainingBytes = cpu_to_le32(wdata->tailsz);
+ req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
+ if (need_invalidate)
+ req->Channel = SMB2_CHANNEL_RDMA_V1;
+ req->WriteChannelInfoOffset =
+ cpu_to_le16(offsetof(struct smb2_write_req, Buffer));
+ req->WriteChannelInfoLength =
+ cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1));
+ v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0];
+ v1->offset = cpu_to_le64(wdata->mr->mr->iova);
+ v1->token = cpu_to_le32(wdata->mr->mr->rkey);
+ v1->length = cpu_to_le32(wdata->mr->mr->length);
+ }
+#endif
+ iov[0].iov_len = total_len - 1;
+ iov[0].iov_base = (char *)req;
+
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+ rqst.rq_pages = wdata->pages;
+ rqst.rq_offset = wdata->page_offset;
+ rqst.rq_npages = wdata->nr_pages;
+ rqst.rq_pagesz = wdata->pagesz;
+ rqst.rq_tailsz = wdata->tailsz;
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ if (wdata->mr) {
+ iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
+ rqst.rq_npages = 0;
+ }
+#endif
+ cifs_dbg(FYI, "async write at %llu %u bytes\n",
+ wdata->offset, wdata->bytes);
+
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ /* For RDMA read, I/O size is in RemainingBytes not in Length */
+ if (!wdata->mr)
+ req->Length = cpu_to_le32(wdata->bytes);
+#else
+ req->Length = cpu_to_le32(wdata->bytes);
+#endif
+
+ if (wdata->credits) {
+ shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
+ SMB2_MAX_BUFFER_SIZE));
+ shdr->CreditRequest =
+ cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
+ spin_lock(&server->req_lock);
+ server->credits += wdata->credits -
+ le16_to_cpu(shdr->CreditCharge);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ wdata->credits = le16_to_cpu(shdr->CreditCharge);
+ flags |= CIFS_HAS_CREDITS;
+ }
+
+ kref_get(&wdata->refcount);
+ rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
+ wdata, flags);
+
+ if (rc) {
+ trace_smb3_write_err(0 /* no xid */, req->PersistentFileId,
+ tcon->tid, tcon->ses->Suid, wdata->offset,
+ wdata->bytes, rc);
+ kref_put(&wdata->refcount, release);
+ cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
+ }
+
+async_writev_out:
+ cifs_small_buf_release(req);
+ return rc;
+}
+
+/*
+ * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
+ * The length field from io_parms must be at least 1 and indicates a number of
+ * elements with data to write that begins with position 1 in iov array. All
+ * data length is specified by count.
+ */
+int
+SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, struct kvec *iov, int n_vec)
+{
+ struct smb_rqst rqst;
+ int rc = 0;
+ struct smb2_write_req *req = NULL;
+ struct smb2_write_rsp *rsp = NULL;
+ int resp_buftype;
+ struct kvec rsp_iov;
+ int flags = 0;
+ unsigned int total_len;
+
+ *nbytes = 0;
+
+ if (n_vec < 1)
+ return rc;
+
+ rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ if (smb3_encryption_required(io_parms->tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->sync_hdr.ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+ req->VolatileFileId = io_parms->volatile_fid;
+ req->WriteChannelInfoOffset = 0;
+ req->WriteChannelInfoLength = 0;
+ req->Channel = 0;
+ req->Length = cpu_to_le32(io_parms->length);
+ req->Offset = cpu_to_le64(io_parms->offset);
+ req->DataOffset = cpu_to_le16(
+ offsetof(struct smb2_write_req, Buffer));
+ req->RemainingBytes = 0;
+
+ iov[0].iov_base = (char *)req;
+ /* 1 for Buffer */
+ iov[0].iov_len = total_len - 1;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = n_vec + 1;
+
+ rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst,
+ &resp_buftype, flags, &rsp_iov);
+ rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ trace_smb3_write_err(xid, req->PersistentFileId,
+ io_parms->tcon->tid,
+ io_parms->tcon->ses->Suid,
+ io_parms->offset, io_parms->length, rc);
+ cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE);
+ cifs_dbg(VFS, "Send error in write = %d\n", rc);
+ } else {
+ *nbytes = le32_to_cpu(rsp->DataLength);
+ trace_smb3_write_done(xid, req->PersistentFileId,
+ io_parms->tcon->tid,
+ io_parms->tcon->ses->Suid,
+ io_parms->offset, *nbytes);
+ }
+
+ cifs_small_buf_release(req);
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+static unsigned int
+num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
+{
+ int len;
+ unsigned int entrycount = 0;
+ unsigned int next_offset = 0;
+ char *entryptr;
+ FILE_DIRECTORY_INFO *dir_info;
+
+ if (bufstart == NULL)
+ return 0;
+
+ entryptr = bufstart;
+
+ while (1) {
+ if (entryptr + next_offset < entryptr ||
+ entryptr + next_offset > end_of_buf ||
+ entryptr + next_offset + size > end_of_buf) {
+ cifs_dbg(VFS, "malformed search entry would overflow\n");
+ break;
+ }
+
+ entryptr = entryptr + next_offset;
+ dir_info = (FILE_DIRECTORY_INFO *)entryptr;
+
+ len = le32_to_cpu(dir_info->FileNameLength);
+ if (entryptr + len < entryptr ||
+ entryptr + len > end_of_buf ||
+ entryptr + len + size > end_of_buf) {
+ cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n",
+ end_of_buf);
+ break;
+ }
+
+ *lastentry = entryptr;
+ entrycount++;
+
+ next_offset = le32_to_cpu(dir_info->NextEntryOffset);
+ if (!next_offset)
+ break;
+ }
+
+ return entrycount;
+}
+
+/*
+ * Readdir/FindFirst
+ */
+int
+SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int index,
+ struct cifs_search_info *srch_inf)
+{
+ struct smb_rqst rqst;
+ struct smb2_query_directory_req *req;
+ struct smb2_query_directory_rsp *rsp = NULL;
+ struct kvec iov[2];
+ struct kvec rsp_iov;
+ int rc = 0;
+ int len;
+ int resp_buftype = CIFS_NO_BUFFER;
+ unsigned char *bufptr;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ __le16 asteriks = cpu_to_le16('*');
+ char *end_of_smb;
+ unsigned int output_size = CIFSMaxBufSize;
+ size_t info_buf_size;
+ int flags = 0;
+ unsigned int total_len;
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ switch (srch_inf->info_level) {
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ req->FileInformationClass = FILE_DIRECTORY_INFORMATION;
+ info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1;
+ break;
+ case SMB_FIND_FILE_ID_FULL_DIR_INFO:
+ req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
+ info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
+ break;
+ default:
+ cifs_dbg(VFS, "info level %u isn't supported\n",
+ srch_inf->info_level);
+ rc = -EINVAL;
+ goto qdir_exit;
+ }
+
+ req->FileIndex = cpu_to_le32(index);
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+ len = 0x2;
+ bufptr = req->Buffer;
+ memcpy(bufptr, &asteriks, len);
+
+ req->FileNameOffset =
+ cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1);
+ req->FileNameLength = cpu_to_le16(len);
+ /*
+ * BB could be 30 bytes or so longer if we used SMB2 specific
+ * buffer lengths, but this is safe and close enough.
+ */
+ output_size = min_t(unsigned int, output_size, server->maxBuf);
+ output_size = min_t(unsigned int, output_size, 2 << 15);
+ req->OutputBufferLength = cpu_to_le32(output_size);
+
+ iov[0].iov_base = (char *)req;
+ /* 1 for Buffer */
+ iov[0].iov_len = total_len - 1;
+
+ iov[1].iov_base = (char *)(req->Buffer);
+ iov[1].iov_len = len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ if (rc == -ENODATA &&
+ rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) {
+ srch_inf->endOfSearch = true;
+ rc = 0;
+ } else
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
+ goto qdir_exit;
+ }
+
+ rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
+ info_buf_size);
+ if (rc)
+ goto qdir_exit;
+
+ srch_inf->unicode = true;
+
+ if (srch_inf->ntwrk_buf_start) {
+ if (srch_inf->smallBuf)
+ cifs_small_buf_release(srch_inf->ntwrk_buf_start);
+ else
+ cifs_buf_release(srch_inf->ntwrk_buf_start);
+ }
+ srch_inf->ntwrk_buf_start = (char *)rsp;
+ srch_inf->srch_entries_start = srch_inf->last_entry =
+ (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
+ end_of_smb = rsp_iov.iov_len + (char *)rsp;
+ srch_inf->entries_in_buffer =
+ num_entries(srch_inf->srch_entries_start, end_of_smb,
+ &srch_inf->last_entry, info_buf_size);
+ srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
+ cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
+ srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
+ srch_inf->srch_entries_start, srch_inf->last_entry);
+ if (resp_buftype == CIFS_LARGE_BUFFER)
+ srch_inf->smallBuf = false;
+ else if (resp_buftype == CIFS_SMALL_BUFFER)
+ srch_inf->smallBuf = true;
+ else
+ cifs_dbg(VFS, "illegal search buffer type\n");
+
+ return rc;
+
+qdir_exit:
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+static int
+send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class,
+ u8 info_type, u32 additional_info, unsigned int num,
+ void **data, unsigned int *size)
+{
+ struct smb_rqst rqst;
+ struct smb2_set_info_req *req;
+ struct smb2_set_info_rsp *rsp = NULL;
+ struct kvec *iov;
+ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ unsigned int i;
+ struct cifs_ses *ses = tcon->ses;
+ int flags = 0;
+ unsigned int total_len;
+
+ if (!ses || !(ses->server))
+ return -EIO;
+
+ if (!num)
+ return -EINVAL;
+
+ iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL);
+ if (!iov)
+ return -ENOMEM;
+
+ rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, (void **) &req, &total_len);
+ if (rc) {
+ kfree(iov);
+ return rc;
+ }
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->sync_hdr.ProcessId = cpu_to_le32(pid);
+
+ req->InfoType = info_type;
+ req->FileInfoClass = info_class;
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+ req->AdditionalInformation = cpu_to_le32(additional_info);
+
+ req->BufferOffset =
+ cpu_to_le16(sizeof(struct smb2_set_info_req) - 1);
+ req->BufferLength = cpu_to_le32(*size);
+
+ memcpy(req->Buffer, *data, *size);
+ total_len += *size;
+
+ iov[0].iov_base = (char *)req;
+ /* 1 for Buffer */
+ iov[0].iov_len = total_len - 1;
+
+ for (i = 1; i < num; i++) {
+ le32_add_cpu(&req->BufferLength, size[i]);
+ iov[i].iov_base = (char *)data[i];
+ iov[i].iov_len = size[i];
+ }
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = num;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
+ &rsp_iov);
+ cifs_buf_release(req);
+ rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
+ trace_smb3_set_info_err(xid, persistent_fid, tcon->tid,
+ ses->Suid, info_class, (__u32)info_type, rc);
+ }
+
+ free_rsp_buf(resp_buftype, rsp);
+ kfree(iov);
+ return rc;
+}
+
+int
+SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+{
+ struct smb2_file_rename_info info;
+ void **data;
+ unsigned int size[2];
+ int rc;
+ int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX));
+
+ data = kmalloc_array(2, sizeof(void *), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ info.ReplaceIfExists = 1; /* 1 = replace existing target with new */
+ /* 0 = fail if target already exists */
+ info.RootDirectory = 0; /* MBZ for network ops (why does spec say?) */
+ info.FileNameLength = cpu_to_le32(len);
+
+ data[0] = &info;
+ size[0] = sizeof(struct smb2_file_rename_info);
+
+ data[1] = target_file;
+ size[1] = len + 2 /* null */;
+
+ rc = send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_RENAME_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 2, data, size);
+ kfree(data);
+ return rc;
+}
+
+int
+SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid)
+{
+ __u8 delete_pending = 1;
+ void *data;
+ unsigned int size;
+
+ data = &delete_pending;
+ size = 1; /* sizeof __u8 */
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_DISPOSITION_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, &data, &size);
+}
+
+int
+SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+{
+ struct smb2_file_link_info info;
+ void **data;
+ unsigned int size[2];
+ int rc;
+ int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX));
+
+ data = kmalloc_array(2, sizeof(void *), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ info.ReplaceIfExists = 0; /* 1 = replace existing link with new */
+ /* 0 = fail if link already exists */
+ info.RootDirectory = 0; /* MBZ for network ops (why does spec say?) */
+ info.FileNameLength = cpu_to_le32(len);
+
+ data[0] = &info;
+ size[0] = sizeof(struct smb2_file_link_info);
+
+ data[1] = target_file;
+ size[1] = len + 2 /* null */;
+
+ rc = send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_LINK_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 2, data, size);
+ kfree(data);
+ return rc;
+}
+
+int
+SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ u64 volatile_fid, u32 pid, __le64 *eof, bool is_falloc)
+{
+ struct smb2_file_eof_info info;
+ void *data;
+ unsigned int size;
+
+ info.EndOfFile = *eof;
+
+ data = &info;
+ size = sizeof(struct smb2_file_eof_info);
+
+ if (is_falloc)
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, &data, &size);
+ else
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, &data, &size);
+}
+
+int
+SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf)
+{
+ unsigned int size;
+ size = sizeof(FILE_BASIC_INFO);
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_BASIC_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, (void **)&buf, &size);
+}
+
+int
+SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ struct cifs_ntsd *pnntsd, int pacllen, int aclflag)
+{
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag,
+ 1, (void **)&pnntsd, &pacllen);
+}
+
+int
+SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ struct smb2_file_full_ea_info *buf, int len)
+{
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE,
+ 0, 1, (void **)&buf, &len);
+}
+
+int
+SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+ const u64 persistent_fid, const u64 volatile_fid,
+ __u8 oplock_level)
+{
+ struct smb_rqst rqst;
+ int rc;
+ struct smb2_oplock_break *req = NULL;
+ struct cifs_ses *ses = tcon->ses;
+ int flags = CIFS_OBREAK_OP;
+ unsigned int total_len;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ cifs_dbg(FYI, "SMB2_oplock_break\n");
+ rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->VolatileFid = volatile_fid;
+ req->PersistentFid = persistent_fid;
+ req->OplockLevel = oplock_level;
+ req->sync_hdr.CreditRequest = cpu_to_le16(1);
+
+ flags |= CIFS_NO_RESP;
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+ cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc);
+ }
+
+ return rc;
+}
+
+void
+smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
+ struct kstatfs *kst)
+{
+ kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
+ le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
+ kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
+ kst->f_bfree = kst->f_bavail =
+ le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
+ return;
+}
+
+static void
+copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data,
+ struct kstatfs *kst)
+{
+ kst->f_bsize = le32_to_cpu(response_data->BlockSize);
+ kst->f_blocks = le64_to_cpu(response_data->TotalBlocks);
+ kst->f_bfree = le64_to_cpu(response_data->BlocksAvail);
+ if (response_data->UserBlocksAvail == cpu_to_le64(-1))
+ kst->f_bavail = kst->f_bfree;
+ else
+ kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail);
+ if (response_data->TotalFileNodes != cpu_to_le64(-1))
+ kst->f_files = le64_to_cpu(response_data->TotalFileNodes);
+ if (response_data->FreeFileNodes != cpu_to_le64(-1))
+ kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes);
+
+ return;
+}
+
+static int
+build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
+ int outbuf_len, u64 persistent_fid, u64 volatile_fid)
+{
+ struct TCP_Server_Info *server;
+ int rc;
+ struct smb2_query_info_req *req;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "Query FSInfo level %d\n", level);
+
+ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+ return -EIO;
+
+ server = tcon->ses->server;
+
+ rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ req->InfoType = SMB2_O_INFO_FILESYSTEM;
+ req->FileInfoClass = level;
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+ /* 1 for pad */
+ req->InputBufferOffset =
+ cpu_to_le16(sizeof(struct smb2_query_info_req) - 1);
+ req->OutputBufferLength = cpu_to_le32(
+ outbuf_len + sizeof(struct smb2_query_info_rsp) - 1);
+
+ iov->iov_base = (char *)req;
+ iov->iov_len = total_len;
+ return 0;
+}
+
+int
+SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+ struct smb_rqst rqst;
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
+ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+ FILE_SYSTEM_POSIX_INFO *info = NULL;
+ int flags = 0;
+
+ rc = build_qfs_info_req(&iov, tcon, FS_POSIX_INFORMATION,
+ sizeof(FILE_SYSTEM_POSIX_INFO),
+ persistent_fid, volatile_fid);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto posix_qfsinf_exit;
+ }
+ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ info = (FILE_SYSTEM_POSIX_INFO *)(
+ le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
+ rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
+ sizeof(FILE_SYSTEM_POSIX_INFO));
+ if (!rc)
+ copy_posix_fs_info_to_kstatfs(info, fsdata);
+
+posix_qfsinf_exit:
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+}
+
+int
+SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+ struct smb_rqst rqst;
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
+ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+ struct smb2_fs_full_size_info *info = NULL;
+ int flags = 0;
+
+ rc = build_qfs_info_req(&iov, tcon, FS_FULL_SIZE_INFORMATION,
+ sizeof(struct smb2_fs_full_size_info),
+ persistent_fid, volatile_fid);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto qfsinf_exit;
+ }
+ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ info = (struct smb2_fs_full_size_info *)(
+ le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
+ rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
+ sizeof(struct smb2_fs_full_size_info));
+ if (!rc)
+ smb2_copy_fs_info_to_kstatfs(info, fsdata);
+
+qfsinf_exit:
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+}
+
+int
+SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int level)
+{
+ struct smb_rqst rqst;
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
+ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype, max_len, min_len;
+ struct cifs_ses *ses = tcon->ses;
+ unsigned int rsp_len, offset;
+ int flags = 0;
+
+ if (level == FS_DEVICE_INFORMATION) {
+ max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
+ min_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
+ } else if (level == FS_ATTRIBUTE_INFORMATION) {
+ max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
+ min_len = MIN_FS_ATTR_INFO_SIZE;
+ } else if (level == FS_SECTOR_SIZE_INFORMATION) {
+ max_len = sizeof(struct smb3_fs_ss_info);
+ min_len = sizeof(struct smb3_fs_ss_info);
+ } else if (level == FS_VOLUME_INFORMATION) {
+ max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN;
+ min_len = sizeof(struct smb3_fs_vol_info);
+ } else {
+ cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level);
+ return -EINVAL;
+ }
+
+ rc = build_qfs_info_req(&iov, tcon, level, max_len,
+ persistent_fid, volatile_fid);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto qfsattr_exit;
+ }
+ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ rsp_len = le32_to_cpu(rsp->OutputBufferLength);
+ offset = le16_to_cpu(rsp->OutputBufferOffset);
+ rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len);
+ if (rc)
+ goto qfsattr_exit;
+
+ if (level == FS_ATTRIBUTE_INFORMATION)
+ memcpy(&tcon->fsAttrInfo, offset
+ + (char *)rsp, min_t(unsigned int,
+ rsp_len, max_len));
+ else if (level == FS_DEVICE_INFORMATION)
+ memcpy(&tcon->fsDevInfo, offset
+ + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO));
+ else if (level == FS_SECTOR_SIZE_INFORMATION) {
+ struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *)
+ (offset + (char *)rsp);
+ tcon->ss_flags = le32_to_cpu(ss_info->Flags);
+ tcon->perf_sector_size =
+ le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf);
+ } else if (level == FS_VOLUME_INFORMATION) {
+ struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *)
+ (offset + (char *)rsp);
+ tcon->vol_serial_number = vol_info->VolumeSerialNumber;
+ tcon->vol_create_time = vol_info->VolumeCreationTime;
+ }
+
+qfsattr_exit:
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+}
+
+int
+smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+ const __u32 num_lock, struct smb2_lock_element *buf)
+{
+ struct smb_rqst rqst;
+ int rc = 0;
+ struct smb2_lock_req *req = NULL;
+ struct kvec iov[2];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+ unsigned int count;
+ int flags = CIFS_NO_RESP;
+ unsigned int total_len;
+
+ cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
+
+ rc = smb2_plain_req_init(SMB2_LOCK, tcon, (void **) &req, &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->sync_hdr.ProcessId = cpu_to_le32(pid);
+ req->LockCount = cpu_to_le16(num_lock);
+
+ req->PersistentFileId = persist_fid;
+ req->VolatileFileId = volatile_fid;
+
+ count = num_lock * sizeof(struct smb2_lock_element);
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len - sizeof(struct smb2_lock_element);
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = count;
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 2;
+
+ rc = cifs_send_recv(xid, tcon->ses, &rqst, &resp_buf_type, flags,
+ &rsp_iov);
+ cifs_small_buf_release(req);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc);
+ cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
+ trace_smb3_lock_err(xid, persist_fid, tcon->tid,
+ tcon->ses->Suid, rc);
+ }
+
+ return rc;
+}
+
+int
+SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+ const __u64 length, const __u64 offset, const __u32 lock_flags,
+ const bool wait)
+{
+ struct smb2_lock_element lock;
+
+ lock.Offset = cpu_to_le64(offset);
+ lock.Length = cpu_to_le64(length);
+ lock.Flags = cpu_to_le32(lock_flags);
+ if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK)
+ lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+
+ return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock);
+}
+
+int
+SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 *lease_key, const __le32 lease_state)
+{
+ struct smb_rqst rqst;
+ int rc;
+ struct smb2_lease_ack *req = NULL;
+ struct cifs_ses *ses = tcon->ses;
+ int flags = CIFS_OBREAK_OP;
+ unsigned int total_len;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ cifs_dbg(FYI, "SMB2_lease_break\n");
+ rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req,
+ &total_len);
+ if (rc)
+ return rc;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+
+ req->sync_hdr.CreditRequest = cpu_to_le16(1);
+ req->StructureSize = cpu_to_le16(36);
+ total_len += 12;
+
+ memcpy(req->LeaseKey, lease_key, 16);
+ req->LeaseState = lease_state;
+
+ flags |= CIFS_NO_RESP;
+
+ iov[0].iov_base = (char *)req;
+ iov[0].iov_len = total_len;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = iov;
+ rqst.rq_nvec = 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+ cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc);
+ }
+
+ return rc;
+}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
new file mode 100644
index 000000000..8a44d5994
--- /dev/null
+++ b/fs/cifs/smb2pdu.h
@@ -0,0 +1,1409 @@
+/*
+ * fs/cifs/smb2pdu.h
+ *
+ * Copyright (c) International Business Machines Corp., 2009, 2013
+ * Etersoft, 2012
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _SMB2PDU_H
+#define _SMB2PDU_H
+
+#include <net/sock.h>
+
+/*
+ * Note that, due to trying to use names similar to the protocol specifications,
+ * there are many mixed case field names in the structures below. Although
+ * this does not match typical Linux kernel style, it is necessary to be
+ * be able to match against the protocol specfication.
+ *
+ * SMB2 commands
+ * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
+ * (ie no useful data other than the SMB error code itself) and are marked such.
+ * Knowing this helps avoid response buffer allocations and copy in some cases.
+ */
+
+/* List of commands in host endian */
+#define SMB2_NEGOTIATE_HE 0x0000
+#define SMB2_SESSION_SETUP_HE 0x0001
+#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */
+#define SMB2_TREE_CONNECT_HE 0x0003
+#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */
+#define SMB2_CREATE_HE 0x0005
+#define SMB2_CLOSE_HE 0x0006
+#define SMB2_FLUSH_HE 0x0007 /* trivial resp */
+#define SMB2_READ_HE 0x0008
+#define SMB2_WRITE_HE 0x0009
+#define SMB2_LOCK_HE 0x000A
+#define SMB2_IOCTL_HE 0x000B
+#define SMB2_CANCEL_HE 0x000C
+#define SMB2_ECHO_HE 0x000D
+#define SMB2_QUERY_DIRECTORY_HE 0x000E
+#define SMB2_CHANGE_NOTIFY_HE 0x000F
+#define SMB2_QUERY_INFO_HE 0x0010
+#define SMB2_SET_INFO_HE 0x0011
+#define SMB2_OPLOCK_BREAK_HE 0x0012
+
+/* The same list in little endian */
+#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE)
+#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE)
+#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE)
+#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE)
+#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE)
+#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE)
+#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE)
+#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE)
+#define SMB2_READ cpu_to_le16(SMB2_READ_HE)
+#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE)
+#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE)
+#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE)
+#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE)
+#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE)
+#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE)
+#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE)
+#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE)
+#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE)
+#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
+
+#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF)
+
+#define NUMBER_OF_SMB2_COMMANDS 0x0013
+
+/* 52 transform hdr + 64 hdr + 88 create rsp */
+#define MAX_SMB2_HDR_SIZE 204
+
+#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
+#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
+
+/*
+ * SMB2 Header Definition
+ *
+ * "MBZ" : Must be Zero
+ * "BB" : BugBug, Something to check/review/analyze later
+ * "PDU" : "Protocol Data Unit" (ie a network "frame")
+ *
+ */
+
+#define SMB2_HEADER_STRUCTURE_SIZE cpu_to_le16(64)
+
+struct smb2_sync_hdr {
+ __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
+ __le16 StructureSize; /* 64 */
+ __le16 CreditCharge; /* MBZ */
+ __le32 Status; /* Error from server */
+ __le16 Command;
+ __le16 CreditRequest; /* CreditResponse */
+ __le32 Flags;
+ __le32 NextCommand;
+ __le64 MessageId;
+ __le32 ProcessId;
+ __u32 TreeId; /* opaque - so do not make little endian */
+ __u64 SessionId; /* opaque - so do not make little endian */
+ __u8 Signature[16];
+} __packed;
+
+struct smb2_sync_pdu {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize2; /* size of wct area (varies, request specific) */
+} __packed;
+
+#define SMB3_AES128CMM_NONCE 11
+#define SMB3_AES128GCM_NONCE 12
+
+struct smb2_transform_hdr {
+ __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */
+ __u8 Signature[16];
+ __u8 Nonce[16];
+ __le32 OriginalMessageSize;
+ __u16 Reserved1;
+ __le16 Flags; /* EncryptionAlgorithm */
+ __u64 SessionId;
+} __packed;
+
+/*
+ * SMB2 flag definitions
+ */
+#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001)
+#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002)
+#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004)
+#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008)
+#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000)
+
+/*
+ * Definitions for SMB2 Protocol Data Units (network frames)
+ *
+ * See MS-SMB2.PDF specification for protocol details.
+ * The Naming convention is the lower case version of the SMB2
+ * command code name for the struct. Note that structures must be packed.
+ *
+ */
+
+#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL
+
+#define SMB2_ERROR_STRUCTURE_SIZE2 cpu_to_le16(9)
+
+struct smb2_err_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize;
+ __le16 Reserved; /* MBZ */
+ __le32 ByteCount; /* even if zero, at least one byte follows */
+ __u8 ErrorData[1]; /* variable length */
+} __packed;
+
+struct smb2_symlink_err_rsp {
+ __le32 SymLinkLength;
+ __le32 SymLinkErrorTag;
+ __le32 ReparseTag;
+ __le16 ReparseDataLength;
+ __le16 UnparsedPathLength;
+ __le16 SubstituteNameOffset;
+ __le16 SubstituteNameLength;
+ __le16 PrintNameOffset;
+ __le16 PrintNameLength;
+ __le32 Flags;
+ __u8 PathBuffer[0];
+} __packed;
+
+/* SMB 3.1.1 and later dialects. See MS-SMB2 section 2.2.2.1 */
+struct smb2_error_context_rsp {
+ __le32 ErrorDataLength;
+ __le32 ErrorId;
+ __u8 ErrorContextData; /* ErrorDataLength long array */
+} __packed;
+
+/* Defines for Type field below (see MS-SMB2 2.2.2.2.2.1) */
+#define MOVE_DST_IPADDR_V4 cpu_to_le32(0x00000001)
+#define MOVE_DST_IPADDR_V6 cpu_to_le32(0x00000002)
+
+struct move_dst_ipaddr {
+ __le32 Type;
+ __u32 Reserved;
+ __u8 address[16]; /* IPv4 followed by 12 bytes rsvd or IPv6 address */
+} __packed;
+
+struct share_redirect_error_context_rsp {
+ __le32 StructureSize;
+ __le32 NotificationType;
+ __le32 ResourceNameOffset;
+ __le32 ResourceNameLength;
+ __le16 Flags;
+ __le16 TargetType;
+ __le32 IPAddrCount;
+ struct move_dst_ipaddr IpAddrMoveList[0];
+ /* __u8 ResourceName[] */ /* Name of share as counted Unicode string */
+} __packed;
+
+#define SMB2_CLIENT_GUID_SIZE 16
+
+struct smb2_negotiate_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 36 */
+ __le16 DialectCount;
+ __le16 SecurityMode;
+ __le16 Reserved; /* MBZ */
+ __le32 Capabilities;
+ __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE];
+ /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */
+ __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */
+ __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */
+ __le16 Reserved2;
+ __le16 Dialects[4]; /* BB expand this if autonegotiate > 4 dialects */
+} __packed;
+
+/* Dialects */
+#define SMB20_PROT_ID 0x0202
+#define SMB21_PROT_ID 0x0210
+#define SMB30_PROT_ID 0x0300
+#define SMB302_PROT_ID 0x0302
+#define SMB311_PROT_ID 0x0311
+#define BAD_PROT_ID 0xFFFF
+
+/* SecurityMode flags */
+#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001
+#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
+#define SMB2_SEC_MODE_FLAGS_ALL 0x0003
+
+/* Capabilities flags */
+#define SMB2_GLOBAL_CAP_DFS 0x00000001
+#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */
+#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */
+#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */
+#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */
+#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */
+#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */
+/* Internal types */
+#define SMB2_NT_FIND 0x00100000
+#define SMB2_LARGE_FILES 0x00200000
+
+struct smb2_neg_context {
+ __le16 ContextType;
+ __le16 DataLength;
+ __le32 Reserved;
+ /* Followed by array of data */
+} __packed;
+
+#define SMB311_LINUX_CLIENT_SALT_SIZE 32
+/* Hash Algorithm Types */
+#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001)
+#define SMB2_PREAUTH_HASH_SIZE 64
+
+/*
+ * SaltLength that the server send can be zero, so the only three required
+ * fields (all __le16) end up six bytes total, so the minimum context data len
+ * in the response is six bytes which accounts for
+ *
+ * HashAlgorithmCount, SaltLength, and 1 HashAlgorithm.
+ */
+#define MIN_PREAUTH_CTXT_DATA_LEN 6
+
+struct smb2_preauth_neg_context {
+ __le16 ContextType; /* 1 */
+ __le16 DataLength;
+ __le32 Reserved;
+ __le16 HashAlgorithmCount; /* 1 */
+ __le16 SaltLength;
+ __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */
+ __u8 Salt[SMB311_LINUX_CLIENT_SALT_SIZE];
+} __packed;
+
+/* Encryption Algorithms Ciphers */
+#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001)
+#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002)
+
+/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */
+#define MIN_ENCRYPT_CTXT_DATA_LEN 4
+struct smb2_encryption_neg_context {
+ __le16 ContextType; /* 2 */
+ __le16 DataLength;
+ __le32 Reserved;
+ __le16 CipherCount; /* AES-128-GCM and AES-128-CCM */
+ __le16 Ciphers[1]; /* Ciphers[0] since only one used now */
+} __packed;
+
+#define POSIX_CTXT_DATA_LEN 8
+struct smb2_posix_neg_context {
+ __le16 ContextType; /* 0x100 */
+ __le16 DataLength;
+ __le32 Reserved;
+ __le64 Reserved1; /* In case needed for future (eg version or caps) */
+} __packed;
+
+struct smb2_negotiate_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 65 */
+ __le16 SecurityMode;
+ __le16 DialectRevision;
+ __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */
+ __u8 ServerGUID[16];
+ __le32 Capabilities;
+ __le32 MaxTransactSize;
+ __le32 MaxReadSize;
+ __le32 MaxWriteSize;
+ __le64 SystemTime; /* MBZ */
+ __le64 ServerStartTime;
+ __le16 SecurityBufferOffset;
+ __le16 SecurityBufferLength;
+ __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */
+ __u8 Buffer[1]; /* variable length GSS security buffer */
+} __packed;
+
+/* Flags */
+#define SMB2_SESSION_REQ_FLAG_BINDING 0x01
+#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04
+
+struct smb2_sess_setup_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 25 */
+ __u8 Flags;
+ __u8 SecurityMode;
+ __le32 Capabilities;
+ __le32 Channel;
+ __le16 SecurityBufferOffset;
+ __le16 SecurityBufferLength;
+ __u64 PreviousSessionId;
+ __u8 Buffer[1]; /* variable length GSS security buffer */
+} __packed;
+
+/* Currently defined SessionFlags */
+#define SMB2_SESSION_FLAG_IS_GUEST 0x0001
+#define SMB2_SESSION_FLAG_IS_NULL 0x0002
+#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004
+struct smb2_sess_setup_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 9 */
+ __le16 SessionFlags;
+ __le16 SecurityBufferOffset;
+ __le16 SecurityBufferLength;
+ __u8 Buffer[1]; /* variable length GSS security buffer */
+} __packed;
+
+struct smb2_logoff_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __le16 Reserved;
+} __packed;
+
+struct smb2_logoff_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __le16 Reserved;
+} __packed;
+
+/* Flags/Reserved for SMB3.1.1 */
+#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001)
+#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002)
+#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004)
+
+struct smb2_tree_connect_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 9 */
+ __le16 Reserved; /* Flags in SMB3.1.1 */
+ __le16 PathOffset;
+ __le16 PathLength;
+ __u8 Buffer[1]; /* variable length */
+} __packed;
+
+/* See MS-SMB2 section 2.2.9.2 */
+/* Context Types */
+#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000
+#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001)
+
+struct tree_connect_contexts {
+ __le16 ContextType;
+ __le16 DataLength;
+ __le32 Reserved;
+ __u8 Data[0];
+} __packed;
+
+/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */
+struct smb3_blob_data {
+ __le16 BlobSize;
+ __u8 BlobData[0];
+} __packed;
+
+/* Valid values for Attr */
+#define SE_GROUP_MANDATORY 0x00000001
+#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002
+#define SE_GROUP_ENABLED 0x00000004
+#define SE_GROUP_OWNER 0x00000008
+#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010
+#define SE_GROUP_INTEGRITY 0x00000020
+#define SE_GROUP_INTEGRITY_ENABLED 0x00000040
+#define SE_GROUP_RESOURCE 0x20000000
+#define SE_GROUP_LOGON_ID 0xC0000000
+
+/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */
+
+struct sid_array_data {
+ __le16 SidAttrCount;
+ /* SidAttrList - array of sid_attr_data structs */
+} __packed;
+
+struct luid_attr_data {
+
+} __packed;
+
+/*
+ * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5
+ * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA
+ */
+
+struct privilege_array_data {
+ __le16 PrivilegeCount;
+ /* array of privilege_data structs */
+} __packed;
+
+struct remoted_identity_tcon_context {
+ __le16 TicketType; /* must be 0x0001 */
+ __le16 TicketSize; /* total size of this struct */
+ __le16 User; /* offset to SID_ATTR_DATA struct with user info */
+ __le16 UserName; /* offset to null terminated Unicode username string */
+ __le16 Domain; /* offset to null terminated Unicode domain name */
+ __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */
+ __le16 RestrictedGroups; /* similar to above */
+ __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */
+ __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */
+ __le16 Owner; /* offset to BLOB_DATA struct */
+ __le16 DefaultDacl; /* offset to BLOB_DATA struct */
+ __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */
+ __le16 UserClaims; /* offset to BLOB_DATA struct */
+ __le16 DeviceClaims; /* offset to BLOB_DATA struct */
+ __u8 TicketInfo[0]; /* variable length buf - remoted identity data */
+} __packed;
+
+struct smb2_tree_connect_req_extension {
+ __le32 TreeConnectContextOffset;
+ __le16 TreeConnectContextCount;
+ __u8 Reserved[10];
+ __u8 PathName[0]; /* variable sized array */
+ /* followed by array of TreeConnectContexts */
+} __packed;
+
+struct smb2_tree_connect_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 16 */
+ __u8 ShareType; /* see below */
+ __u8 Reserved;
+ __le32 ShareFlags; /* see below */
+ __le32 Capabilities; /* see below */
+ __le32 MaximalAccess;
+} __packed;
+
+/* Possible ShareType values */
+#define SMB2_SHARE_TYPE_DISK 0x01
+#define SMB2_SHARE_TYPE_PIPE 0x02
+#define SMB2_SHARE_TYPE_PRINT 0x03
+
+/*
+ * Possible ShareFlags - exactly one and only one of the first 4 caching flags
+ * must be set (any of the remaining, SHI1005, flags may be set individually
+ * or in combination.
+ */
+#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000
+#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010
+#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020
+#define SMB2_SHAREFLAG_NO_CACHING 0x00000030
+#define SHI1005_FLAGS_DFS 0x00000001
+#define SHI1005_FLAGS_DFS_ROOT 0x00000002
+#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100
+#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200
+#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400
+#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800
+#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000
+#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000
+#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000
+#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000
+#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */
+#define SHI1005_FLAGS_ALL 0x0004FF33
+
+/* Possible share capabilities */
+#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */
+#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */
+#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */
+#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */
+#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */
+#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */
+
+struct smb2_tree_disconnect_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __le16 Reserved;
+} __packed;
+
+struct smb2_tree_disconnect_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __le16 Reserved;
+} __packed;
+
+/* File Attrubutes */
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#define FILE_ATTRIBUTE_HIDDEN 0x00000002
+#define FILE_ATTRIBUTE_SYSTEM 0x00000004
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
+#define FILE_ATTRIBUTE_NORMAL 0x00000080
+#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
+#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200
+#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
+#define FILE_ATTRIBUTE_COMPRESSED 0x00000800
+#define FILE_ATTRIBUTE_OFFLINE 0x00001000
+#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
+#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000
+#define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000
+#define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000
+
+/* 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
+/* Non-spec internal type */
+#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99
+
+/* Desired Access Flags */
+#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
+#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002)
+#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004)
+#define FILE_READ_EA_LE cpu_to_le32(0x00000008)
+#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010)
+#define FILE_EXECUTE_LE cpu_to_le32(0x00000020)
+#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080)
+#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100)
+#define FILE_DELETE_LE cpu_to_le32(0x00010000)
+#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000)
+#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000)
+#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000)
+#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000)
+#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000)
+#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000)
+#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000)
+#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000)
+#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000)
+#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000)
+
+/* ShareAccess Flags */
+#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001)
+#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002)
+#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004)
+#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007)
+
+/* CreateDisposition Flags */
+#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000)
+#define FILE_OPEN_LE cpu_to_le32(0x00000001)
+#define FILE_CREATE_LE cpu_to_le32(0x00000002)
+#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003)
+#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004)
+#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005)
+
+/* CreateOptions Flags */
+#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001)
+/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */
+#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002)
+#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004)
+#define FILE_NO_INTERMEDIATE_BUFFERRING_LE cpu_to_le32(0x00000008)
+#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010)
+#define FILE_SYNCHRONOUS_IO_NON_ALERT_LE cpu_to_le32(0x00000020)
+#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040)
+#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100)
+#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200)
+#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800)
+#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000)
+#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000)
+#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000)
+#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000)
+#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000)
+#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000)
+#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000)
+#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000)
+
+#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \
+ | FILE_READ_ATTRIBUTES_LE)
+#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \
+ | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE)
+#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE)
+
+/* Impersonation Levels */
+#define IL_ANONYMOUS cpu_to_le32(0x00000000)
+#define IL_IDENTIFICATION cpu_to_le32(0x00000001)
+#define IL_IMPERSONATION cpu_to_le32(0x00000002)
+#define IL_DELEGATE cpu_to_le32(0x00000003)
+
+/* Create Context Values */
+#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */
+#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */
+#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ"
+#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC"
+#define SMB2_CREATE_ALLOCATION_SIZE "AISi"
+#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc"
+#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp"
+#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid"
+#define SMB2_CREATE_REQUEST_LEASE "RqLs"
+#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q"
+#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C"
+#define SMB2_CREATE_APP_INSTANCE_ID 0x45BCA66AEFA7F74A9008FA462E144D74
+#define SVHDX_OPEN_DEVICE_CONTEX 0x9CCBCF9E04C1E643980E158DA1F6EC83
+#define SMB2_CREATE_TAG_POSIX 0x93AD25509CB411E7B42383DE968BCD7C
+
+
+/*
+ * Maximum number of iovs we need for an open/create request.
+ * [0] : struct smb2_create_req
+ * [1] : path
+ * [2] : lease context
+ * [3] : durable context
+ * [4] : posix context
+ * [5] : time warp context
+ * [6] : compound padding
+ */
+#define SMB2_CREATE_IOV_SIZE 7
+
+struct smb2_create_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 57 */
+ __u8 SecurityFlags;
+ __u8 RequestedOplockLevel;
+ __le32 ImpersonationLevel;
+ __le64 SmbCreateFlags;
+ __le64 Reserved;
+ __le32 DesiredAccess;
+ __le32 FileAttributes;
+ __le32 ShareAccess;
+ __le32 CreateDisposition;
+ __le32 CreateOptions;
+ __le16 NameOffset;
+ __le16 NameLength;
+ __le32 CreateContextsOffset;
+ __le32 CreateContextsLength;
+ __u8 Buffer[0];
+} __packed;
+
+struct smb2_create_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 89 */
+ __u8 OplockLevel;
+ __u8 Reserved;
+ __le32 CreateAction;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 AllocationSize;
+ __le64 EndofFile;
+ __le32 FileAttributes;
+ __le32 Reserved2;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 CreateContextsOffset;
+ __le32 CreateContextsLength;
+ __u8 Buffer[1];
+} __packed;
+
+struct create_context {
+ __le32 Next;
+ __le16 NameOffset;
+ __le16 NameLength;
+ __le16 Reserved;
+ __le16 DataOffset;
+ __le32 DataLength;
+ __u8 Buffer[0];
+} __packed;
+
+#define SMB2_LEASE_READ_CACHING_HE 0x01
+#define SMB2_LEASE_HANDLE_CACHING_HE 0x02
+#define SMB2_LEASE_WRITE_CACHING_HE 0x04
+
+#define SMB2_LEASE_NONE cpu_to_le32(0x00)
+#define SMB2_LEASE_READ_CACHING cpu_to_le32(0x01)
+#define SMB2_LEASE_HANDLE_CACHING cpu_to_le32(0x02)
+#define SMB2_LEASE_WRITE_CACHING cpu_to_le32(0x04)
+
+#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS cpu_to_le32(0x02)
+#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET cpu_to_le32(0x00000004)
+
+#define SMB2_LEASE_KEY_SIZE 16
+
+struct lease_context {
+ u8 LeaseKey[SMB2_LEASE_KEY_SIZE];
+ __le32 LeaseState;
+ __le32 LeaseFlags;
+ __le64 LeaseDuration;
+} __packed;
+
+struct lease_context_v2 {
+ u8 LeaseKey[SMB2_LEASE_KEY_SIZE];
+ __le32 LeaseState;
+ __le32 LeaseFlags;
+ __le64 LeaseDuration;
+ __le64 ParentLeaseKeyLow;
+ __le64 ParentLeaseKeyHigh;
+ __le16 Epoch;
+ __le16 Reserved;
+} __packed;
+
+struct create_lease {
+ struct create_context ccontext;
+ __u8 Name[8];
+ struct lease_context lcontext;
+} __packed;
+
+struct create_lease_v2 {
+ struct create_context ccontext;
+ __u8 Name[8];
+ struct lease_context_v2 lcontext;
+ __u8 Pad[4];
+} __packed;
+
+struct create_durable {
+ struct create_context ccontext;
+ __u8 Name[8];
+ union {
+ __u8 Reserved[16];
+ struct {
+ __u64 PersistentFileId;
+ __u64 VolatileFileId;
+ } Fid;
+ } Data;
+} __packed;
+
+struct create_posix {
+ struct create_context ccontext;
+ __u8 Name[16];
+ __le32 Mode;
+ __u32 Reserved;
+} __packed;
+
+/* See MS-SMB2 2.2.13.2.11 */
+/* Flags */
+#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
+struct durable_context_v2 {
+ __le32 Timeout;
+ __le32 Flags;
+ __u64 Reserved;
+ __u8 CreateGuid[16];
+} __packed;
+
+struct create_durable_v2 {
+ struct create_context ccontext;
+ __u8 Name[8];
+ struct durable_context_v2 dcontext;
+} __packed;
+
+/* See MS-SMB2 2.2.13.2.12 */
+struct durable_reconnect_context_v2 {
+ struct {
+ __u64 PersistentFileId;
+ __u64 VolatileFileId;
+ } Fid;
+ __u8 CreateGuid[16];
+ __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */
+} __packed;
+
+/* See MS-SMB2 2.2.14.2.12 */
+struct durable_reconnect_context_v2_rsp {
+ __le32 Timeout;
+ __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */
+} __packed;
+
+struct create_durable_handle_reconnect_v2 {
+ struct create_context ccontext;
+ __u8 Name[8];
+ struct durable_reconnect_context_v2 dcontext;
+ __u8 Pad[4];
+} __packed;
+
+/* See MS-SMB2 2.2.13.2.5 */
+struct crt_twarp_ctxt {
+ struct create_context ccontext;
+ __u8 Name[8];
+ __le64 Timestamp;
+
+} __packed;
+
+#define COPY_CHUNK_RES_KEY_SIZE 24
+struct resume_key_req {
+ char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
+ __le32 ContextLength; /* MBZ */
+ char Context[0]; /* ignored, Windows sets to 4 bytes of zero */
+} __packed;
+
+/* this goes in the ioctl buffer when doing a copychunk request */
+struct copychunk_ioctl {
+ char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
+ __le32 ChunkCount; /* we are only sending 1 */
+ __le32 Reserved;
+ /* array will only be one chunk long for us */
+ __le64 SourceOffset;
+ __le64 TargetOffset;
+ __le32 Length; /* how many bytes to copy */
+ __u32 Reserved2;
+} __packed;
+
+/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */
+struct file_zero_data_information {
+ __le64 FileOffset;
+ __le64 BeyondFinalZero;
+} __packed;
+
+struct copychunk_ioctl_rsp {
+ __le32 ChunksWritten;
+ __le32 ChunkBytesWritten;
+ __le32 TotalBytesWritten;
+} __packed;
+
+struct fsctl_set_integrity_information_req {
+ __le16 ChecksumAlgorithm;
+ __le16 Reserved;
+ __le32 Flags;
+} __packed;
+
+struct fsctl_get_integrity_information_rsp {
+ __le16 ChecksumAlgorithm;
+ __le16 Reserved;
+ __le32 Flags;
+ __le32 ChecksumChunkSizeInBytes;
+ __le32 ClusterSizeInBytes;
+} __packed;
+
+/* Integrity ChecksumAlgorithm choices for above */
+#define CHECKSUM_TYPE_NONE 0x0000
+#define CHECKSUM_TYPE_CRC64 0x0002
+#define CHECKSUM_TYPE_UNCHANGED 0xFFFF /* set only */
+
+/* Integrity flags for above */
+#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001
+
+/* See MS-DFSC 2.2.2 */
+struct fsctl_get_dfs_referral_req {
+ __le16 MaxReferralLevel;
+ __u8 RequestFileName[];
+} __packed;
+
+/* DFS response is struct get_dfs_refer_rsp */
+
+/* See MS-SMB2 2.2.31.3 */
+struct network_resiliency_req {
+ __le32 Timeout;
+ __le32 Reserved;
+} __packed;
+/* There is no buffer for the response ie no struct network_resiliency_rsp */
+
+
+struct validate_negotiate_info_req {
+ __le32 Capabilities;
+ __u8 Guid[SMB2_CLIENT_GUID_SIZE];
+ __le16 SecurityMode;
+ __le16 DialectCount;
+ __le16 Dialects[3]; /* BB expand this if autonegotiate > 3 dialects */
+} __packed;
+
+struct validate_negotiate_info_rsp {
+ __le32 Capabilities;
+ __u8 Guid[SMB2_CLIENT_GUID_SIZE];
+ __le16 SecurityMode;
+ __le16 Dialect; /* Dialect in use for the connection */
+} __packed;
+
+#define RSS_CAPABLE cpu_to_le32(0x00000001)
+#define RDMA_CAPABLE cpu_to_le32(0x00000002)
+
+#define INTERNETWORK cpu_to_le16(0x0002)
+#define INTERNETWORKV6 cpu_to_le16(0x0017)
+
+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;
+ __le16 Family;
+ __u8 Buffer[126];
+} __packed;
+
+struct iface_info_ipv4 {
+ __be16 Port;
+ __be32 IPv4Address;
+ __be64 Reserved;
+} __packed;
+
+struct iface_info_ipv6 {
+ __be16 Port;
+ __be32 FlowInfo;
+ __u8 IPv6Address[16];
+ __be32 ScopeId;
+} __packed;
+
+#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
+
+struct compress_ioctl {
+ __le16 CompressionState; /* See cifspdu.h for possible flag values */
+} __packed;
+
+struct duplicate_extents_to_file {
+ __u64 PersistentFileHandle; /* source file handle, opaque endianness */
+ __u64 VolatileFileHandle;
+ __le64 SourceFileOffset;
+ __le64 TargetFileOffset;
+ __le64 ByteCount; /* Bytes to be copied */
+} __packed;
+
+struct smb2_ioctl_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 57 */
+ __u16 Reserved;
+ __le32 CtlCode;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 InputOffset;
+ __le32 InputCount;
+ __le32 MaxInputResponse;
+ __le32 OutputOffset;
+ __le32 OutputCount;
+ __le32 MaxOutputResponse;
+ __le32 Flags;
+ __u32 Reserved2;
+ __u8 Buffer[0];
+} __packed;
+
+struct smb2_ioctl_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 57 */
+ __u16 Reserved;
+ __le32 CtlCode;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 InputOffset;
+ __le32 InputCount;
+ __le32 OutputOffset;
+ __le32 OutputCount;
+ __le32 Flags;
+ __u32 Reserved2;
+ /* char * buffer[] */
+} __packed;
+
+/* Currently defined values for close flags */
+#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
+struct smb2_close_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 24 */
+ __le16 Flags;
+ __le32 Reserved;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+} __packed;
+
+struct smb2_close_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* 60 */
+ __le16 Flags;
+ __le32 Reserved;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */
+ __le64 EndOfFile;
+ __le32 Attributes;
+} __packed;
+
+struct smb2_flush_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 24 */
+ __le16 Reserved1;
+ __le32 Reserved2;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+} __packed;
+
+struct smb2_flush_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize;
+ __le16 Reserved;
+} __packed;
+
+/* For read request Flags field below, following flag is defined for SMB3.02 */
+#define SMB2_READFLAG_READ_UNBUFFERED 0x01
+
+/* Channel field for read and write: exactly one of following flags can be set*/
+#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000)
+#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) /* SMB3 or later */
+#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) /* >= SMB3.02 */
+
+/* SMB2 read request without RFC1001 length at the beginning */
+struct smb2_read_plain_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 49 */
+ __u8 Padding; /* offset from start of SMB2 header to place read */
+ __u8 Flags; /* MBZ unless SMB3.02 or later */
+ __le32 Length;
+ __le64 Offset;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 MinimumCount;
+ __le32 Channel; /* MBZ except for SMB3 or later */
+ __le32 RemainingBytes;
+ __le16 ReadChannelInfoOffset;
+ __le16 ReadChannelInfoLength;
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_read_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 17 */
+ __u8 DataOffset;
+ __u8 Reserved;
+ __le32 DataLength;
+ __le32 DataRemaining;
+ __u32 Reserved2;
+ __u8 Buffer[1];
+} __packed;
+
+/* For write request Flags field below the following flags are defined: */
+#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */
+#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */
+
+struct smb2_write_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 49 */
+ __le16 DataOffset; /* offset from start of SMB2 header to write data */
+ __le32 Length;
+ __le64 Offset;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 Channel; /* Reserved MBZ */
+ __le32 RemainingBytes;
+ __le16 WriteChannelInfoOffset;
+ __le16 WriteChannelInfoLength;
+ __le32 Flags;
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_write_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 17 */
+ __u8 DataOffset;
+ __u8 Reserved;
+ __le32 DataLength;
+ __le32 DataRemaining;
+ __u32 Reserved2;
+ __u8 Buffer[1];
+} __packed;
+
+#define SMB2_LOCKFLAG_SHARED_LOCK 0x0001
+#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x0002
+#define SMB2_LOCKFLAG_UNLOCK 0x0004
+#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010
+
+struct smb2_lock_element {
+ __le64 Offset;
+ __le64 Length;
+ __le32 Flags;
+ __le32 Reserved;
+} __packed;
+
+struct smb2_lock_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 48 */
+ __le16 LockCount;
+ __le32 Reserved;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ /* Followed by at least one */
+ struct smb2_lock_element locks[1];
+} __packed;
+
+struct smb2_lock_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __le16 Reserved;
+} __packed;
+
+struct smb2_echo_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __u16 Reserved;
+} __packed;
+
+struct smb2_echo_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __u16 Reserved;
+} __packed;
+
+/* search (query_directory) Flags field */
+#define SMB2_RESTART_SCANS 0x01
+#define SMB2_RETURN_SINGLE_ENTRY 0x02
+#define SMB2_INDEX_SPECIFIED 0x04
+#define SMB2_REOPEN 0x10
+
+struct smb2_query_directory_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 33 */
+ __u8 FileInformationClass;
+ __u8 Flags;
+ __le32 FileIndex;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le16 FileNameOffset;
+ __le16 FileNameLength;
+ __le32 OutputBufferLength;
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_query_directory_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 9 */
+ __le16 OutputBufferOffset;
+ __le32 OutputBufferLength;
+ __u8 Buffer[1];
+} __packed;
+
+/* Possible InfoType values */
+#define SMB2_O_INFO_FILE 0x01
+#define SMB2_O_INFO_FILESYSTEM 0x02
+#define SMB2_O_INFO_SECURITY 0x03
+#define SMB2_O_INFO_QUOTA 0x04
+
+/* Security info type additionalinfo flags. See MS-SMB2 (2.2.37) or MS-DTYP */
+#define OWNER_SECINFO 0x00000001
+#define GROUP_SECINFO 0x00000002
+#define DACL_SECINFO 0x00000004
+#define SACL_SECINFO 0x00000008
+#define LABEL_SECINFO 0x00000010
+#define ATTRIBUTE_SECINFO 0x00000020
+#define SCOPE_SECINFO 0x00000040
+#define BACKUP_SECINFO 0x00010000
+#define UNPROTECTED_SACL_SECINFO 0x10000000
+#define UNPROTECTED_DACL_SECINFO 0x20000000
+#define PROTECTED_SACL_SECINFO 0x40000000
+#define PROTECTED_DACL_SECINFO 0x80000000
+
+/* Flags used for FileFullEAinfo */
+#define SL_RESTART_SCAN 0x00000001
+#define SL_RETURN_SINGLE_ENTRY 0x00000002
+#define SL_INDEX_SPECIFIED 0x00000004
+
+struct smb2_query_info_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 41 */
+ __u8 InfoType;
+ __u8 FileInfoClass;
+ __le32 OutputBufferLength;
+ __le16 InputBufferOffset;
+ __u16 Reserved;
+ __le32 InputBufferLength;
+ __le32 AdditionalInformation;
+ __le32 Flags;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_query_info_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 9 */
+ __le16 OutputBufferOffset;
+ __le32 OutputBufferLength;
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_set_info_req {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 33 */
+ __u8 InfoType;
+ __u8 FileInfoClass;
+ __le32 BufferLength;
+ __le16 BufferOffset;
+ __u16 Reserved;
+ __le32 AdditionalInformation;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_set_info_rsp {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 2 */
+} __packed;
+
+struct smb2_oplock_break {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 24 */
+ __u8 OplockLevel;
+ __u8 Reserved;
+ __le32 Reserved2;
+ __u64 PersistentFid;
+ __u64 VolatileFid;
+} __packed;
+
+#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
+
+struct smb2_lease_break {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 44 */
+ __le16 Epoch;
+ __le32 Flags;
+ __u8 LeaseKey[16];
+ __le32 CurrentLeaseState;
+ __le32 NewLeaseState;
+ __le32 BreakReason;
+ __le32 AccessMaskHint;
+ __le32 ShareMaskHint;
+} __packed;
+
+struct smb2_lease_ack {
+ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 36 */
+ __le16 Reserved;
+ __le32 Flags;
+ __u8 LeaseKey[16];
+ __le32 LeaseState;
+ __le64 LeaseDuration;
+} __packed;
+
+/*
+ * PDU infolevel structure definitions
+ * BB consider moving to a different header
+ */
+
+/* File System Information Classes */
+#define FS_VOLUME_INFORMATION 1 /* Query */
+#define FS_LABEL_INFORMATION 2 /* Local only */
+#define FS_SIZE_INFORMATION 3 /* Query */
+#define FS_DEVICE_INFORMATION 4 /* Query */
+#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
+#define FS_CONTROL_INFORMATION 6 /* Query, Set */
+#define FS_FULL_SIZE_INFORMATION 7 /* Query */
+#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
+#define FS_DRIVER_PATH_INFORMATION 9 /* Local only */
+#define FS_VOLUME_FLAGS_INFORMATION 10 /* Local only */
+#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */
+#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */
+
+struct smb2_fs_full_size_info {
+ __le64 TotalAllocationUnits;
+ __le64 CallerAvailableAllocationUnits;
+ __le64 ActualAvailableAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __packed;
+
+#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001
+#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002
+#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004
+#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008
+
+/* sector size info struct */
+struct smb3_fs_ss_info {
+ __le32 LogicalBytesPerSector;
+ __le32 PhysicalBytesPerSectorForAtomicity;
+ __le32 PhysicalBytesPerSectorForPerf;
+ __le32 FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
+ __le32 Flags;
+ __le32 ByteOffsetForSectorAlignment;
+ __le32 ByteOffsetForPartitionAlignment;
+} __packed;
+
+/* volume info struct - see MS-FSCC 2.5.9 */
+#define MAX_VOL_LABEL_LEN 32
+struct smb3_fs_vol_info {
+ __le64 VolumeCreationTime;
+ __u32 VolumeSerialNumber;
+ __le32 VolumeLabelLength; /* includes trailing null */
+ __u8 SupportsObjects; /* True if eg like NTFS, supports objects */
+ __u8 Reserved;
+ __u8 VolumeLabel[0]; /* variable len */
+} __packed;
+
+/* partial list of QUERY INFO levels */
+#define FILE_DIRECTORY_INFORMATION 1
+#define FILE_FULL_DIRECTORY_INFORMATION 2
+#define FILE_BOTH_DIRECTORY_INFORMATION 3
+#define FILE_BASIC_INFORMATION 4
+#define FILE_STANDARD_INFORMATION 5
+#define FILE_INTERNAL_INFORMATION 6
+#define FILE_EA_INFORMATION 7
+#define FILE_ACCESS_INFORMATION 8
+#define FILE_NAME_INFORMATION 9
+#define FILE_RENAME_INFORMATION 10
+#define FILE_LINK_INFORMATION 11
+#define FILE_NAMES_INFORMATION 12
+#define FILE_DISPOSITION_INFORMATION 13
+#define FILE_POSITION_INFORMATION 14
+#define FILE_FULL_EA_INFORMATION 15
+#define FILE_MODE_INFORMATION 16
+#define FILE_ALIGNMENT_INFORMATION 17
+#define FILE_ALL_INFORMATION 18
+#define FILE_ALLOCATION_INFORMATION 19
+#define FILE_END_OF_FILE_INFORMATION 20
+#define FILE_ALTERNATE_NAME_INFORMATION 21
+#define FILE_STREAM_INFORMATION 22
+#define FILE_PIPE_INFORMATION 23
+#define FILE_PIPE_LOCAL_INFORMATION 24
+#define FILE_PIPE_REMOTE_INFORMATION 25
+#define FILE_MAILSLOT_QUERY_INFORMATION 26
+#define FILE_MAILSLOT_SET_INFORMATION 27
+#define FILE_COMPRESSION_INFORMATION 28
+#define FILE_OBJECT_ID_INFORMATION 29
+/* Number 30 not defined in documents */
+#define FILE_MOVE_CLUSTER_INFORMATION 31
+#define FILE_QUOTA_INFORMATION 32
+#define FILE_REPARSE_POINT_INFORMATION 33
+#define FILE_NETWORK_OPEN_INFORMATION 34
+#define FILE_ATTRIBUTE_TAG_INFORMATION 35
+#define FILE_TRACKING_INFORMATION 36
+#define FILEID_BOTH_DIRECTORY_INFORMATION 37
+#define FILEID_FULL_DIRECTORY_INFORMATION 38
+#define FILE_VALID_DATA_LENGTH_INFORMATION 39
+#define FILE_SHORT_NAME_INFORMATION 40
+#define FILE_SFIO_RESERVE_INFORMATION 44
+#define FILE_SFIO_VOLUME_INFORMATION 45
+#define FILE_HARD_LINK_INFORMATION 46
+#define FILE_NORMALIZED_NAME_INFORMATION 48
+#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50
+#define FILE_STANDARD_LINK_INFORMATION 54
+
+struct smb2_file_internal_info {
+ __le64 IndexNumber;
+} __packed; /* level 6 Query */
+
+struct smb2_file_rename_info { /* encoding of request for level 10 */
+ __u8 ReplaceIfExists; /* 1 = replace existing target with new */
+ /* 0 = fail if target already exists */
+ __u8 Reserved[7];
+ __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
+ __le32 FileNameLength;
+ char FileName[0]; /* New name to be assigned */
+} __packed; /* level 10 Set */
+
+struct smb2_file_link_info { /* encoding of request for level 11 */
+ __u8 ReplaceIfExists; /* 1 = replace existing link with new */
+ /* 0 = fail if link already exists */
+ __u8 Reserved[7];
+ __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
+ __le32 FileNameLength;
+ char FileName[0]; /* Name to be assigned to new link */
+} __packed; /* level 11 Set */
+
+#define SMB2_MIN_EA_BUF 2048
+#define SMB2_MAX_EA_BUF 65536
+
+struct smb2_file_full_ea_info { /* encoding of response for level 15 */
+ __le32 next_entry_offset;
+ __u8 flags;
+ __u8 ea_name_length;
+ __le16 ea_value_length;
+ char ea_data[0]; /* \0 terminated name plus value */
+} __packed; /* level 15 Set */
+
+/*
+ * This level 18, although with struct with same name is different from cifs
+ * level 0x107. Level 0x107 has an extra u64 between AccessFlags and
+ * CurrentByteOffset.
+ */
+struct smb2_file_all_info { /* data block encoding of response to level 18 */
+ __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 Attributes;
+ __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */
+ __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */
+ __le64 EndOfFile; /* size ie offset to first free byte in file */
+ __le32 NumberOfLinks; /* hard links */
+ __u8 DeletePending;
+ __u8 Directory;
+ __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */
+ __le64 IndexNumber;
+ __le32 EASize;
+ __le32 AccessFlags;
+ __le64 CurrentByteOffset;
+ __le32 Mode;
+ __le32 AlignmentRequirement;
+ __le32 FileNameLength;
+ char FileName[1];
+} __packed; /* level 18 Query */
+
+struct smb2_file_eof_info { /* encoding of request for level 10 */
+ __le64 EndOfFile; /* new end of file value */
+} __packed; /* level 20 Set */
+
+extern char smb2_padding[7];
+
+#endif /* _SMB2PDU_H */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
new file mode 100644
index 000000000..c66d7da80
--- /dev/null
+++ b/fs/cifs/smb2proto.h
@@ -0,0 +1,244 @@
+/*
+ * fs/cifs/smb2proto.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002, 2011
+ * Etersoft, 2012
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _SMB2PROTO_H
+#define _SMB2PROTO_H
+#include <linux/nls.h>
+#include <linux/key-type.h>
+
+struct statfs;
+struct smb_rqst;
+
+/*
+ *****************************************************************
+ * All Prototypes
+ *****************************************************************
+ */
+extern int map_smb2_to_linux_error(char *buf, bool log_err);
+extern int smb2_check_message(char *buf, unsigned int length,
+ struct TCP_Server_Info *server);
+extern unsigned int smb2_calc_size(void *buf, struct TCP_Server_Info *server);
+extern char *smb2_get_data_area_len(int *off, int *len,
+ struct smb2_sync_hdr *shdr);
+extern __le16 *cifs_convert_path_to_utf16(const char *from,
+ struct cifs_sb_info *cifs_sb);
+
+extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
+extern int smb2_check_receive(struct mid_q_entry *mid,
+ struct TCP_Server_Info *server, bool log_error);
+extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
+ struct smb_rqst *rqst);
+extern struct mid_q_entry *smb2_setup_async_request(
+ struct TCP_Server_Info *server, struct smb_rqst *rqst);
+extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
+ __u64 ses_id);
+extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
+ __u64 ses_id, __u32 tid);
+extern int smb2_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server);
+extern int smb3_calc_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server);
+extern void smb2_echo_request(struct work_struct *work);
+extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
+extern bool smb2_is_valid_oplock_break(char *buffer,
+ struct TCP_Server_Info *srv);
+extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
+ __u64 ses_id);
+extern int smb3_handle_read_data(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid);
+
+extern int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *pfid);
+extern void close_shroot(struct cached_fid *cfid);
+extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
+ struct smb2_file_all_info *src);
+extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const char *full_path, FILE_ALL_INFO *data,
+ bool *adjust_tz, bool *symlink);
+extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_alloc);
+extern int smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid);
+extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+ umode_t mode, struct cifs_tcon *tcon,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb);
+extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
+ struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, const unsigned int xid);
+extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
+extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
+extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb, const unsigned char *path,
+ char *pbuf, unsigned int *pbytes_written);
+extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ const unsigned char *path, char *pbuf,
+ unsigned int *pbytes_read);
+extern int smb2_open_file(const unsigned int xid,
+ struct cifs_open_parms *oparms,
+ __u32 *oplock, FILE_ALL_INFO *buf);
+extern int smb2_unlock_range(struct cifsFileInfo *cfile,
+ struct file_lock *flock, const unsigned int xid);
+extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
+extern void smb2_reconnect_server(struct work_struct *work);
+extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server);
+extern unsigned long smb_rqst_len(struct TCP_Server_Info *server,
+ struct smb_rqst *rqst);
+
+/*
+ * SMB2 Worker functions - most of protocol specific implementation details
+ * are contained within these calls.
+ */
+extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses);
+extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
+ const struct nls_table *nls_cp);
+extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses);
+extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses,
+ const char *tree, struct cifs_tcon *tcon,
+ const struct nls_table *);
+extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
+extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
+ __le16 *path, __u8 *oplock,
+ struct smb2_file_all_info *buf,
+ struct kvec *err_iov, int *resp_buftype);
+extern int SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
+ __u8 *oplock, struct cifs_open_parms *oparms,
+ __le16 *path);
+extern void SMB2_open_free(struct smb_rqst *rqst);
+extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 opcode,
+ bool is_fsctl, char *in_data, u32 indatalen,
+ char **out_data, u32 *plen /* returned data len */);
+extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id);
+extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int flags);
+extern int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
+ u64 persistent_file_id, u64 volatile_file_id);
+extern void SMB2_close_free(struct smb_rqst *rqst);
+extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id);
+extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ int ea_buf_size,
+ struct smb2_file_full_ea_info *data);
+extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ struct smb2_file_all_info *data);
+extern int SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
+ u64 persistent_fid, u64 volatile_fid,
+ u8 info_class, u8 info_type,
+ u32 additional_info, size_t output_len);
+extern void SMB2_query_info_free(struct smb_rqst *rqst);
+extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ void **data, unsigned int *plen);
+extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ __le64 *uniqueid);
+extern int smb2_async_readv(struct cifs_readdata *rdata);
+extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, char **buf, int *buf_type);
+extern int smb2_async_writev(struct cifs_writedata *wdata,
+ void (*release)(struct kref *kref));
+extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, struct kvec *iov, int n_vec);
+extern int SMB2_echo(struct TCP_Server_Info *server);
+extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int index,
+ struct cifs_search_info *srch_inf);
+extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ __le16 *target_file);
+extern int SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid);
+extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ __le16 *target_file);
+extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ __le64 *eof, bool is_fallocate);
+extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ FILE_BASIC_INFO *buf);
+extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ struct cifs_ntsd *pnntsd, int pacllen, int aclflag);
+extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ struct smb2_file_full_ea_info *buf, int len);
+extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid);
+extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+ const u64 persistent_fid, const u64 volatile_fid,
+ const __u8 oplock_level);
+extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
+ __u64 persistent_fid,
+ __u64 volatile_fid);
+extern int smb2_handle_cancelled_mid(char *buffer,
+ struct TCP_Server_Info *server);
+void smb2_cancelled_close_fid(struct work_struct *work);
+extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ struct kstatfs *FSData);
+extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ struct kstatfs *FSData);
+extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id, int lvl);
+extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid,
+ const __u32 pid, const __u64 length, const __u64 offset,
+ const __u32 lockFlags, const bool wait);
+extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid,
+ const __u32 pid, const __u32 num_lock,
+ struct smb2_lock_element *buf);
+extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 *lease_key, const __le32 lease_state);
+extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
+
+extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
+ enum securityEnum);
+extern int smb3_encryption_required(const struct cifs_tcon *tcon);
+extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
+ struct kvec *iov, unsigned int min_buf_size);
+extern void smb2_copy_fs_info_to_kstatfs(
+ struct smb2_fs_full_size_info *pfs_inf,
+ struct kstatfs *kst);
+extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
+extern int smb311_update_preauth_hash(struct cifs_ses *ses,
+ struct kvec *iov, int nvec);
+#endif /* _SMB2PROTO_H */
diff --git a/fs/cifs/smb2status.h b/fs/cifs/smb2status.h
new file mode 100644
index 000000000..3d5f62150
--- /dev/null
+++ b/fs/cifs/smb2status.h
@@ -0,0 +1,1782 @@
+/*
+ * fs/cifs/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)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * 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 __constant_cpu_to_le32(0x0000)
+#define STATUS_SEVERITY_INFORMATIONAL __constanst_cpu_to_le32(0x0001)
+#define STATUS_SEVERITY_WARNING __constanst_cpu_to_le32(0x0002)
+#define STATUS_SEVERITY_ERROR __constanst_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 __constant_cpu_to_le32(0x00000000)
+#define STATUS_WAIT_0 __constant_cpu_to_le32(0x00000000)
+#define STATUS_WAIT_1 __constant_cpu_to_le32(0x00000001)
+#define STATUS_WAIT_2 __constant_cpu_to_le32(0x00000002)
+#define STATUS_WAIT_3 __constant_cpu_to_le32(0x00000003)
+#define STATUS_WAIT_63 __constant_cpu_to_le32(0x0000003F)
+#define STATUS_ABANDONED __constant_cpu_to_le32(0x00000080)
+#define STATUS_ABANDONED_WAIT_0 __constant_cpu_to_le32(0x00000080)
+#define STATUS_ABANDONED_WAIT_63 __constant_cpu_to_le32(0x000000BF)
+#define STATUS_USER_APC __constant_cpu_to_le32(0x000000C0)
+#define STATUS_KERNEL_APC __constant_cpu_to_le32(0x00000100)
+#define STATUS_ALERTED __constant_cpu_to_le32(0x00000101)
+#define STATUS_TIMEOUT __constant_cpu_to_le32(0x00000102)
+#define STATUS_PENDING __constant_cpu_to_le32(0x00000103)
+#define STATUS_REPARSE __constant_cpu_to_le32(0x00000104)
+#define STATUS_MORE_ENTRIES __constant_cpu_to_le32(0x00000105)
+#define STATUS_NOT_ALL_ASSIGNED __constant_cpu_to_le32(0x00000106)
+#define STATUS_SOME_NOT_MAPPED __constant_cpu_to_le32(0x00000107)
+#define STATUS_OPLOCK_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x00000108)
+#define STATUS_VOLUME_MOUNTED __constant_cpu_to_le32(0x00000109)
+#define STATUS_RXACT_COMMITTED __constant_cpu_to_le32(0x0000010A)
+#define STATUS_NOTIFY_CLEANUP __constant_cpu_to_le32(0x0000010B)
+#define STATUS_NOTIFY_ENUM_DIR __constant_cpu_to_le32(0x0000010C)
+#define STATUS_NO_QUOTAS_FOR_ACCOUNT __constant_cpu_to_le32(0x0000010D)
+#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED __constant_cpu_to_le32(0x0000010E)
+#define STATUS_PAGE_FAULT_TRANSITION __constant_cpu_to_le32(0x00000110)
+#define STATUS_PAGE_FAULT_DEMAND_ZERO __constant_cpu_to_le32(0x00000111)
+#define STATUS_PAGE_FAULT_COPY_ON_WRITE __constant_cpu_to_le32(0x00000112)
+#define STATUS_PAGE_FAULT_GUARD_PAGE __constant_cpu_to_le32(0x00000113)
+#define STATUS_PAGE_FAULT_PAGING_FILE __constant_cpu_to_le32(0x00000114)
+#define STATUS_CACHE_PAGE_LOCKED __constant_cpu_to_le32(0x00000115)
+#define STATUS_CRASH_DUMP __constant_cpu_to_le32(0x00000116)
+#define STATUS_BUFFER_ALL_ZEROS __constant_cpu_to_le32(0x00000117)
+#define STATUS_REPARSE_OBJECT __constant_cpu_to_le32(0x00000118)
+#define STATUS_RESOURCE_REQUIREMENTS_CHANGED __constant_cpu_to_le32(0x00000119)
+#define STATUS_TRANSLATION_COMPLETE __constant_cpu_to_le32(0x00000120)
+#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY __constant_cpu_to_le32(0x00000121)
+#define STATUS_NOTHING_TO_TERMINATE __constant_cpu_to_le32(0x00000122)
+#define STATUS_PROCESS_NOT_IN_JOB __constant_cpu_to_le32(0x00000123)
+#define STATUS_PROCESS_IN_JOB __constant_cpu_to_le32(0x00000124)
+#define STATUS_VOLSNAP_HIBERNATE_READY __constant_cpu_to_le32(0x00000125)
+#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY __constant_cpu_to_le32(0x00000126)
+#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED __constant_cpu_to_le32(0x00000127)
+#define STATUS_INTERRUPT_STILL_CONNECTED __constant_cpu_to_le32(0x00000128)
+#define STATUS_PROCESS_CLONED __constant_cpu_to_le32(0x00000129)
+#define STATUS_FILE_LOCKED_WITH_ONLY_READERS __constant_cpu_to_le32(0x0000012A)
+#define STATUS_FILE_LOCKED_WITH_WRITERS __constant_cpu_to_le32(0x0000012B)
+#define STATUS_RESOURCEMANAGER_READ_ONLY __constant_cpu_to_le32(0x00000202)
+#define STATUS_WAIT_FOR_OPLOCK __constant_cpu_to_le32(0x00000367)
+#define DBG_EXCEPTION_HANDLED __constant_cpu_to_le32(0x00010001)
+#define DBG_CONTINUE __constant_cpu_to_le32(0x00010002)
+#define STATUS_FLT_IO_COMPLETE __constant_cpu_to_le32(0x001C0001)
+#define STATUS_OBJECT_NAME_EXISTS __constant_cpu_to_le32(0x40000000)
+#define STATUS_THREAD_WAS_SUSPENDED __constant_cpu_to_le32(0x40000001)
+#define STATUS_WORKING_SET_LIMIT_RANGE __constant_cpu_to_le32(0x40000002)
+#define STATUS_IMAGE_NOT_AT_BASE __constant_cpu_to_le32(0x40000003)
+#define STATUS_RXACT_STATE_CREATED __constant_cpu_to_le32(0x40000004)
+#define STATUS_SEGMENT_NOTIFICATION __constant_cpu_to_le32(0x40000005)
+#define STATUS_LOCAL_USER_SESSION_KEY __constant_cpu_to_le32(0x40000006)
+#define STATUS_BAD_CURRENT_DIRECTORY __constant_cpu_to_le32(0x40000007)
+#define STATUS_SERIAL_MORE_WRITES __constant_cpu_to_le32(0x40000008)
+#define STATUS_REGISTRY_RECOVERED __constant_cpu_to_le32(0x40000009)
+#define STATUS_FT_READ_RECOVERY_FROM_BACKUP __constant_cpu_to_le32(0x4000000A)
+#define STATUS_FT_WRITE_RECOVERY __constant_cpu_to_le32(0x4000000B)
+#define STATUS_SERIAL_COUNTER_TIMEOUT __constant_cpu_to_le32(0x4000000C)
+#define STATUS_NULL_LM_PASSWORD __constant_cpu_to_le32(0x4000000D)
+#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH __constant_cpu_to_le32(0x4000000E)
+#define STATUS_RECEIVE_PARTIAL __constant_cpu_to_le32(0x4000000F)
+#define STATUS_RECEIVE_EXPEDITED __constant_cpu_to_le32(0x40000010)
+#define STATUS_RECEIVE_PARTIAL_EXPEDITED __constant_cpu_to_le32(0x40000011)
+#define STATUS_EVENT_DONE __constant_cpu_to_le32(0x40000012)
+#define STATUS_EVENT_PENDING __constant_cpu_to_le32(0x40000013)
+#define STATUS_CHECKING_FILE_SYSTEM __constant_cpu_to_le32(0x40000014)
+#define STATUS_FATAL_APP_EXIT __constant_cpu_to_le32(0x40000015)
+#define STATUS_PREDEFINED_HANDLE __constant_cpu_to_le32(0x40000016)
+#define STATUS_WAS_UNLOCKED __constant_cpu_to_le32(0x40000017)
+#define STATUS_SERVICE_NOTIFICATION __constant_cpu_to_le32(0x40000018)
+#define STATUS_WAS_LOCKED __constant_cpu_to_le32(0x40000019)
+#define STATUS_LOG_HARD_ERROR __constant_cpu_to_le32(0x4000001A)
+#define STATUS_ALREADY_WIN32 __constant_cpu_to_le32(0x4000001B)
+#define STATUS_WX86_UNSIMULATE __constant_cpu_to_le32(0x4000001C)
+#define STATUS_WX86_CONTINUE __constant_cpu_to_le32(0x4000001D)
+#define STATUS_WX86_SINGLE_STEP __constant_cpu_to_le32(0x4000001E)
+#define STATUS_WX86_BREAKPOINT __constant_cpu_to_le32(0x4000001F)
+#define STATUS_WX86_EXCEPTION_CONTINUE __constant_cpu_to_le32(0x40000020)
+#define STATUS_WX86_EXCEPTION_LASTCHANCE __constant_cpu_to_le32(0x40000021)
+#define STATUS_WX86_EXCEPTION_CHAIN __constant_cpu_to_le32(0x40000022)
+#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE __constant_cpu_to_le32(0x40000023)
+#define STATUS_NO_YIELD_PERFORMED __constant_cpu_to_le32(0x40000024)
+#define STATUS_TIMER_RESUME_IGNORED __constant_cpu_to_le32(0x40000025)
+#define STATUS_ARBITRATION_UNHANDLED __constant_cpu_to_le32(0x40000026)
+#define STATUS_CARDBUS_NOT_SUPPORTED __constant_cpu_to_le32(0x40000027)
+#define STATUS_WX86_CREATEWX86TIB __constant_cpu_to_le32(0x40000028)
+#define STATUS_MP_PROCESSOR_MISMATCH __constant_cpu_to_le32(0x40000029)
+#define STATUS_HIBERNATED __constant_cpu_to_le32(0x4000002A)
+#define STATUS_RESUME_HIBERNATION __constant_cpu_to_le32(0x4000002B)
+#define STATUS_FIRMWARE_UPDATED __constant_cpu_to_le32(0x4000002C)
+#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES __constant_cpu_to_le32(0x4000002D)
+#define STATUS_MESSAGE_RETRIEVED __constant_cpu_to_le32(0x4000002E)
+#define STATUS_SYSTEM_POWERSTATE_TRANSITION __constant_cpu_to_le32(0x4000002F)
+#define STATUS_ALPC_CHECK_COMPLETION_LIST __constant_cpu_to_le32(0x40000030)
+#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION __constant_cpu_to_le32(0x40000031)
+#define STATUS_ACCESS_AUDIT_BY_POLICY __constant_cpu_to_le32(0x40000032)
+#define STATUS_ABANDON_HIBERFILE __constant_cpu_to_le32(0x40000033)
+#define STATUS_BIZRULES_NOT_ENABLED __constant_cpu_to_le32(0x40000034)
+#define STATUS_WAKE_SYSTEM __constant_cpu_to_le32(0x40000294)
+#define STATUS_DS_SHUTTING_DOWN __constant_cpu_to_le32(0x40000370)
+#define DBG_REPLY_LATER __constant_cpu_to_le32(0x40010001)
+#define DBG_UNABLE_TO_PROVIDE_HANDLE __constant_cpu_to_le32(0x40010002)
+#define DBG_TERMINATE_THREAD __constant_cpu_to_le32(0x40010003)
+#define DBG_TERMINATE_PROCESS __constant_cpu_to_le32(0x40010004)
+#define DBG_CONTROL_C __constant_cpu_to_le32(0x40010005)
+#define DBG_PRINTEXCEPTION_C __constant_cpu_to_le32(0x40010006)
+#define DBG_RIPEXCEPTION __constant_cpu_to_le32(0x40010007)
+#define DBG_CONTROL_BREAK __constant_cpu_to_le32(0x40010008)
+#define DBG_COMMAND_EXCEPTION __constant_cpu_to_le32(0x40010009)
+#define RPC_NT_UUID_LOCAL_ONLY __constant_cpu_to_le32(0x40020056)
+#define RPC_NT_SEND_INCOMPLETE __constant_cpu_to_le32(0x400200AF)
+#define STATUS_CTX_CDM_CONNECT __constant_cpu_to_le32(0x400A0004)
+#define STATUS_CTX_CDM_DISCONNECT __constant_cpu_to_le32(0x400A0005)
+#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT __constant_cpu_to_le32(0x4015000D)
+#define STATUS_RECOVERY_NOT_NEEDED __constant_cpu_to_le32(0x40190034)
+#define STATUS_RM_ALREADY_STARTED __constant_cpu_to_le32(0x40190035)
+#define STATUS_LOG_NO_RESTART __constant_cpu_to_le32(0x401A000C)
+#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST __constant_cpu_to_le32(0x401B00EC)
+#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED __constant_cpu_to_le32(0x401E000A)
+#define STATUS_GRAPHICS_DRIVER_MISMATCH __constant_cpu_to_le32(0x401E0117)
+#define STATUS_GRAPHICS_MODE_NOT_PINNED __constant_cpu_to_le32(0x401E0307)
+#define STATUS_GRAPHICS_NO_PREFERRED_MODE __constant_cpu_to_le32(0x401E031E)
+#define STATUS_GRAPHICS_DATASET_IS_EMPTY __constant_cpu_to_le32(0x401E034B)
+#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET __constant_cpu_to_le32(0x401E034C)
+#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED __constant_cpu_to_le32(0x401E0351)
+#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS __constant_cpu_to_le32(0x401E042F)
+#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED __constant_cpu_to_le32(0x401E0437)
+#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY __constant_cpu_to_le32(0x401E0439)
+#define STATUS_GRAPHICS_START_DEFERRED __constant_cpu_to_le32(0x401E043A)
+#define STATUS_NDIS_INDICATION_REQUIRED __constant_cpu_to_le32(0x40230001)
+#define STATUS_GUARD_PAGE_VIOLATION __constant_cpu_to_le32(0x80000001)
+#define STATUS_DATATYPE_MISALIGNMENT __constant_cpu_to_le32(0x80000002)
+#define STATUS_BREAKPOINT __constant_cpu_to_le32(0x80000003)
+#define STATUS_SINGLE_STEP __constant_cpu_to_le32(0x80000004)
+#define STATUS_BUFFER_OVERFLOW __constant_cpu_to_le32(0x80000005)
+#define STATUS_NO_MORE_FILES __constant_cpu_to_le32(0x80000006)
+#define STATUS_WAKE_SYSTEM_DEBUGGER __constant_cpu_to_le32(0x80000007)
+#define STATUS_HANDLES_CLOSED __constant_cpu_to_le32(0x8000000A)
+#define STATUS_NO_INHERITANCE __constant_cpu_to_le32(0x8000000B)
+#define STATUS_GUID_SUBSTITUTION_MADE __constant_cpu_to_le32(0x8000000C)
+#define STATUS_PARTIAL_COPY __constant_cpu_to_le32(0x8000000D)
+#define STATUS_DEVICE_PAPER_EMPTY __constant_cpu_to_le32(0x8000000E)
+#define STATUS_DEVICE_POWERED_OFF __constant_cpu_to_le32(0x8000000F)
+#define STATUS_DEVICE_OFF_LINE __constant_cpu_to_le32(0x80000010)
+#define STATUS_DEVICE_BUSY __constant_cpu_to_le32(0x80000011)
+#define STATUS_NO_MORE_EAS __constant_cpu_to_le32(0x80000012)
+#define STATUS_INVALID_EA_NAME __constant_cpu_to_le32(0x80000013)
+#define STATUS_EA_LIST_INCONSISTENT __constant_cpu_to_le32(0x80000014)
+#define STATUS_INVALID_EA_FLAG __constant_cpu_to_le32(0x80000015)
+#define STATUS_VERIFY_REQUIRED __constant_cpu_to_le32(0x80000016)
+#define STATUS_EXTRANEOUS_INFORMATION __constant_cpu_to_le32(0x80000017)
+#define STATUS_RXACT_COMMIT_NECESSARY __constant_cpu_to_le32(0x80000018)
+#define STATUS_NO_MORE_ENTRIES __constant_cpu_to_le32(0x8000001A)
+#define STATUS_FILEMARK_DETECTED __constant_cpu_to_le32(0x8000001B)
+#define STATUS_MEDIA_CHANGED __constant_cpu_to_le32(0x8000001C)
+#define STATUS_BUS_RESET __constant_cpu_to_le32(0x8000001D)
+#define STATUS_END_OF_MEDIA __constant_cpu_to_le32(0x8000001E)
+#define STATUS_BEGINNING_OF_MEDIA __constant_cpu_to_le32(0x8000001F)
+#define STATUS_MEDIA_CHECK __constant_cpu_to_le32(0x80000020)
+#define STATUS_SETMARK_DETECTED __constant_cpu_to_le32(0x80000021)
+#define STATUS_NO_DATA_DETECTED __constant_cpu_to_le32(0x80000022)
+#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES __constant_cpu_to_le32(0x80000023)
+#define STATUS_SERVER_HAS_OPEN_HANDLES __constant_cpu_to_le32(0x80000024)
+#define STATUS_ALREADY_DISCONNECTED __constant_cpu_to_le32(0x80000025)
+#define STATUS_LONGJUMP __constant_cpu_to_le32(0x80000026)
+#define STATUS_CLEANER_CARTRIDGE_INSTALLED __constant_cpu_to_le32(0x80000027)
+#define STATUS_PLUGPLAY_QUERY_VETOED __constant_cpu_to_le32(0x80000028)
+#define STATUS_UNWIND_CONSOLIDATE __constant_cpu_to_le32(0x80000029)
+#define STATUS_REGISTRY_HIVE_RECOVERED __constant_cpu_to_le32(0x8000002A)
+#define STATUS_DLL_MIGHT_BE_INSECURE __constant_cpu_to_le32(0x8000002B)
+#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE __constant_cpu_to_le32(0x8000002C)
+#define STATUS_STOPPED_ON_SYMLINK __constant_cpu_to_le32(0x8000002D)
+#define STATUS_DEVICE_REQUIRES_CLEANING __constant_cpu_to_le32(0x80000288)
+#define STATUS_DEVICE_DOOR_OPEN __constant_cpu_to_le32(0x80000289)
+#define STATUS_DATA_LOST_REPAIR __constant_cpu_to_le32(0x80000803)
+#define DBG_EXCEPTION_NOT_HANDLED __constant_cpu_to_le32(0x80010001)
+#define STATUS_CLUSTER_NODE_ALREADY_UP __constant_cpu_to_le32(0x80130001)
+#define STATUS_CLUSTER_NODE_ALREADY_DOWN __constant_cpu_to_le32(0x80130002)
+#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE __constant_cpu_to_le32(0x80130003)
+#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE __constant_cpu_to_le32(0x80130004)
+#define STATUS_CLUSTER_NODE_ALREADY_MEMBER __constant_cpu_to_le32(0x80130005)
+#define STATUS_COULD_NOT_RESIZE_LOG __constant_cpu_to_le32(0x80190009)
+#define STATUS_NO_TXF_METADATA __constant_cpu_to_le32(0x80190029)
+#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN __constant_cpu_to_le32(0x80190031)
+#define STATUS_TXF_METADATA_ALREADY_PRESENT __constant_cpu_to_le32(0x80190041)
+#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET __constant_cpu_to_le32(0x80190042)
+#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED __constant_cpu_to_le32(0x801B00EB)
+#define STATUS_FLT_BUFFER_TOO_SMALL __constant_cpu_to_le32(0x801C0001)
+#define STATUS_FVE_PARTIAL_METADATA __constant_cpu_to_le32(0x80210001)
+#define STATUS_UNSUCCESSFUL __constant_cpu_to_le32(0xC0000001)
+#define STATUS_NOT_IMPLEMENTED __constant_cpu_to_le32(0xC0000002)
+#define STATUS_INVALID_INFO_CLASS __constant_cpu_to_le32(0xC0000003)
+#define STATUS_INFO_LENGTH_MISMATCH __constant_cpu_to_le32(0xC0000004)
+#define STATUS_ACCESS_VIOLATION __constant_cpu_to_le32(0xC0000005)
+#define STATUS_IN_PAGE_ERROR __constant_cpu_to_le32(0xC0000006)
+#define STATUS_PAGEFILE_QUOTA __constant_cpu_to_le32(0xC0000007)
+#define STATUS_INVALID_HANDLE __constant_cpu_to_le32(0xC0000008)
+#define STATUS_BAD_INITIAL_STACK __constant_cpu_to_le32(0xC0000009)
+#define STATUS_BAD_INITIAL_PC __constant_cpu_to_le32(0xC000000A)
+#define STATUS_INVALID_CID __constant_cpu_to_le32(0xC000000B)
+#define STATUS_TIMER_NOT_CANCELED __constant_cpu_to_le32(0xC000000C)
+#define STATUS_INVALID_PARAMETER __constant_cpu_to_le32(0xC000000D)
+#define STATUS_NO_SUCH_DEVICE __constant_cpu_to_le32(0xC000000E)
+#define STATUS_NO_SUCH_FILE __constant_cpu_to_le32(0xC000000F)
+#define STATUS_INVALID_DEVICE_REQUEST __constant_cpu_to_le32(0xC0000010)
+#define STATUS_END_OF_FILE __constant_cpu_to_le32(0xC0000011)
+#define STATUS_WRONG_VOLUME __constant_cpu_to_le32(0xC0000012)
+#define STATUS_NO_MEDIA_IN_DEVICE __constant_cpu_to_le32(0xC0000013)
+#define STATUS_UNRECOGNIZED_MEDIA __constant_cpu_to_le32(0xC0000014)
+#define STATUS_NONEXISTENT_SECTOR __constant_cpu_to_le32(0xC0000015)
+#define STATUS_MORE_PROCESSING_REQUIRED __constant_cpu_to_le32(0xC0000016)
+#define STATUS_NO_MEMORY __constant_cpu_to_le32(0xC0000017)
+#define STATUS_CONFLICTING_ADDRESSES __constant_cpu_to_le32(0xC0000018)
+#define STATUS_NOT_MAPPED_VIEW __constant_cpu_to_le32(0xC0000019)
+#define STATUS_UNABLE_TO_FREE_VM __constant_cpu_to_le32(0xC000001A)
+#define STATUS_UNABLE_TO_DELETE_SECTION __constant_cpu_to_le32(0xC000001B)
+#define STATUS_INVALID_SYSTEM_SERVICE __constant_cpu_to_le32(0xC000001C)
+#define STATUS_ILLEGAL_INSTRUCTION __constant_cpu_to_le32(0xC000001D)
+#define STATUS_INVALID_LOCK_SEQUENCE __constant_cpu_to_le32(0xC000001E)
+#define STATUS_INVALID_VIEW_SIZE __constant_cpu_to_le32(0xC000001F)
+#define STATUS_INVALID_FILE_FOR_SECTION __constant_cpu_to_le32(0xC0000020)
+#define STATUS_ALREADY_COMMITTED __constant_cpu_to_le32(0xC0000021)
+#define STATUS_ACCESS_DENIED __constant_cpu_to_le32(0xC0000022)
+#define STATUS_BUFFER_TOO_SMALL __constant_cpu_to_le32(0xC0000023)
+#define STATUS_OBJECT_TYPE_MISMATCH __constant_cpu_to_le32(0xC0000024)
+#define STATUS_NONCONTINUABLE_EXCEPTION __constant_cpu_to_le32(0xC0000025)
+#define STATUS_INVALID_DISPOSITION __constant_cpu_to_le32(0xC0000026)
+#define STATUS_UNWIND __constant_cpu_to_le32(0xC0000027)
+#define STATUS_BAD_STACK __constant_cpu_to_le32(0xC0000028)
+#define STATUS_INVALID_UNWIND_TARGET __constant_cpu_to_le32(0xC0000029)
+#define STATUS_NOT_LOCKED __constant_cpu_to_le32(0xC000002A)
+#define STATUS_PARITY_ERROR __constant_cpu_to_le32(0xC000002B)
+#define STATUS_UNABLE_TO_DECOMMIT_VM __constant_cpu_to_le32(0xC000002C)
+#define STATUS_NOT_COMMITTED __constant_cpu_to_le32(0xC000002D)
+#define STATUS_INVALID_PORT_ATTRIBUTES __constant_cpu_to_le32(0xC000002E)
+#define STATUS_PORT_MESSAGE_TOO_LONG __constant_cpu_to_le32(0xC000002F)
+#define STATUS_INVALID_PARAMETER_MIX __constant_cpu_to_le32(0xC0000030)
+#define STATUS_INVALID_QUOTA_LOWER __constant_cpu_to_le32(0xC0000031)
+#define STATUS_DISK_CORRUPT_ERROR __constant_cpu_to_le32(0xC0000032)
+#define STATUS_OBJECT_NAME_INVALID __constant_cpu_to_le32(0xC0000033)
+#define STATUS_OBJECT_NAME_NOT_FOUND __constant_cpu_to_le32(0xC0000034)
+#define STATUS_OBJECT_NAME_COLLISION __constant_cpu_to_le32(0xC0000035)
+#define STATUS_PORT_DISCONNECTED __constant_cpu_to_le32(0xC0000037)
+#define STATUS_DEVICE_ALREADY_ATTACHED __constant_cpu_to_le32(0xC0000038)
+#define STATUS_OBJECT_PATH_INVALID __constant_cpu_to_le32(0xC0000039)
+#define STATUS_OBJECT_PATH_NOT_FOUND __constant_cpu_to_le32(0xC000003A)
+#define STATUS_OBJECT_PATH_SYNTAX_BAD __constant_cpu_to_le32(0xC000003B)
+#define STATUS_DATA_OVERRUN __constant_cpu_to_le32(0xC000003C)
+#define STATUS_DATA_LATE_ERROR __constant_cpu_to_le32(0xC000003D)
+#define STATUS_DATA_ERROR __constant_cpu_to_le32(0xC000003E)
+#define STATUS_CRC_ERROR __constant_cpu_to_le32(0xC000003F)
+#define STATUS_SECTION_TOO_BIG __constant_cpu_to_le32(0xC0000040)
+#define STATUS_PORT_CONNECTION_REFUSED __constant_cpu_to_le32(0xC0000041)
+#define STATUS_INVALID_PORT_HANDLE __constant_cpu_to_le32(0xC0000042)
+#define STATUS_SHARING_VIOLATION __constant_cpu_to_le32(0xC0000043)
+#define STATUS_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000044)
+#define STATUS_INVALID_PAGE_PROTECTION __constant_cpu_to_le32(0xC0000045)
+#define STATUS_MUTANT_NOT_OWNED __constant_cpu_to_le32(0xC0000046)
+#define STATUS_SEMAPHORE_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC0000047)
+#define STATUS_PORT_ALREADY_SET __constant_cpu_to_le32(0xC0000048)
+#define STATUS_SECTION_NOT_IMAGE __constant_cpu_to_le32(0xC0000049)
+#define STATUS_SUSPEND_COUNT_EXCEEDED __constant_cpu_to_le32(0xC000004A)
+#define STATUS_THREAD_IS_TERMINATING __constant_cpu_to_le32(0xC000004B)
+#define STATUS_BAD_WORKING_SET_LIMIT __constant_cpu_to_le32(0xC000004C)
+#define STATUS_INCOMPATIBLE_FILE_MAP __constant_cpu_to_le32(0xC000004D)
+#define STATUS_SECTION_PROTECTION __constant_cpu_to_le32(0xC000004E)
+#define STATUS_EAS_NOT_SUPPORTED __constant_cpu_to_le32(0xC000004F)
+#define STATUS_EA_TOO_LARGE __constant_cpu_to_le32(0xC0000050)
+#define STATUS_NONEXISTENT_EA_ENTRY __constant_cpu_to_le32(0xC0000051)
+#define STATUS_NO_EAS_ON_FILE __constant_cpu_to_le32(0xC0000052)
+#define STATUS_EA_CORRUPT_ERROR __constant_cpu_to_le32(0xC0000053)
+#define STATUS_FILE_LOCK_CONFLICT __constant_cpu_to_le32(0xC0000054)
+#define STATUS_LOCK_NOT_GRANTED __constant_cpu_to_le32(0xC0000055)
+#define STATUS_DELETE_PENDING __constant_cpu_to_le32(0xC0000056)
+#define STATUS_CTL_FILE_NOT_SUPPORTED __constant_cpu_to_le32(0xC0000057)
+#define STATUS_UNKNOWN_REVISION __constant_cpu_to_le32(0xC0000058)
+#define STATUS_REVISION_MISMATCH __constant_cpu_to_le32(0xC0000059)
+#define STATUS_INVALID_OWNER __constant_cpu_to_le32(0xC000005A)
+#define STATUS_INVALID_PRIMARY_GROUP __constant_cpu_to_le32(0xC000005B)
+#define STATUS_NO_IMPERSONATION_TOKEN __constant_cpu_to_le32(0xC000005C)
+#define STATUS_CANT_DISABLE_MANDATORY __constant_cpu_to_le32(0xC000005D)
+#define STATUS_NO_LOGON_SERVERS __constant_cpu_to_le32(0xC000005E)
+#define STATUS_NO_SUCH_LOGON_SESSION __constant_cpu_to_le32(0xC000005F)
+#define STATUS_NO_SUCH_PRIVILEGE __constant_cpu_to_le32(0xC0000060)
+#define STATUS_PRIVILEGE_NOT_HELD __constant_cpu_to_le32(0xC0000061)
+#define STATUS_INVALID_ACCOUNT_NAME __constant_cpu_to_le32(0xC0000062)
+#define STATUS_USER_EXISTS __constant_cpu_to_le32(0xC0000063)
+#define STATUS_NO_SUCH_USER __constant_cpu_to_le32(0xC0000064)
+#define STATUS_GROUP_EXISTS __constant_cpu_to_le32(0xC0000065)
+#define STATUS_NO_SUCH_GROUP __constant_cpu_to_le32(0xC0000066)
+#define STATUS_MEMBER_IN_GROUP __constant_cpu_to_le32(0xC0000067)
+#define STATUS_MEMBER_NOT_IN_GROUP __constant_cpu_to_le32(0xC0000068)
+#define STATUS_LAST_ADMIN __constant_cpu_to_le32(0xC0000069)
+#define STATUS_WRONG_PASSWORD __constant_cpu_to_le32(0xC000006A)
+#define STATUS_ILL_FORMED_PASSWORD __constant_cpu_to_le32(0xC000006B)
+#define STATUS_PASSWORD_RESTRICTION __constant_cpu_to_le32(0xC000006C)
+#define STATUS_LOGON_FAILURE __constant_cpu_to_le32(0xC000006D)
+#define STATUS_ACCOUNT_RESTRICTION __constant_cpu_to_le32(0xC000006E)
+#define STATUS_INVALID_LOGON_HOURS __constant_cpu_to_le32(0xC000006F)
+#define STATUS_INVALID_WORKSTATION __constant_cpu_to_le32(0xC0000070)
+#define STATUS_PASSWORD_EXPIRED __constant_cpu_to_le32(0xC0000071)
+#define STATUS_ACCOUNT_DISABLED __constant_cpu_to_le32(0xC0000072)
+#define STATUS_NONE_MAPPED __constant_cpu_to_le32(0xC0000073)
+#define STATUS_TOO_MANY_LUIDS_REQUESTED __constant_cpu_to_le32(0xC0000074)
+#define STATUS_LUIDS_EXHAUSTED __constant_cpu_to_le32(0xC0000075)
+#define STATUS_INVALID_SUB_AUTHORITY __constant_cpu_to_le32(0xC0000076)
+#define STATUS_INVALID_ACL __constant_cpu_to_le32(0xC0000077)
+#define STATUS_INVALID_SID __constant_cpu_to_le32(0xC0000078)
+#define STATUS_INVALID_SECURITY_DESCR __constant_cpu_to_le32(0xC0000079)
+#define STATUS_PROCEDURE_NOT_FOUND __constant_cpu_to_le32(0xC000007A)
+#define STATUS_INVALID_IMAGE_FORMAT __constant_cpu_to_le32(0xC000007B)
+#define STATUS_NO_TOKEN __constant_cpu_to_le32(0xC000007C)
+#define STATUS_BAD_INHERITANCE_ACL __constant_cpu_to_le32(0xC000007D)
+#define STATUS_RANGE_NOT_LOCKED __constant_cpu_to_le32(0xC000007E)
+#define STATUS_DISK_FULL __constant_cpu_to_le32(0xC000007F)
+#define STATUS_SERVER_DISABLED __constant_cpu_to_le32(0xC0000080)
+#define STATUS_SERVER_NOT_DISABLED __constant_cpu_to_le32(0xC0000081)
+#define STATUS_TOO_MANY_GUIDS_REQUESTED __constant_cpu_to_le32(0xC0000082)
+#define STATUS_GUIDS_EXHAUSTED __constant_cpu_to_le32(0xC0000083)
+#define STATUS_INVALID_ID_AUTHORITY __constant_cpu_to_le32(0xC0000084)
+#define STATUS_AGENTS_EXHAUSTED __constant_cpu_to_le32(0xC0000085)
+#define STATUS_INVALID_VOLUME_LABEL __constant_cpu_to_le32(0xC0000086)
+#define STATUS_SECTION_NOT_EXTENDED __constant_cpu_to_le32(0xC0000087)
+#define STATUS_NOT_MAPPED_DATA __constant_cpu_to_le32(0xC0000088)
+#define STATUS_RESOURCE_DATA_NOT_FOUND __constant_cpu_to_le32(0xC0000089)
+#define STATUS_RESOURCE_TYPE_NOT_FOUND __constant_cpu_to_le32(0xC000008A)
+#define STATUS_RESOURCE_NAME_NOT_FOUND __constant_cpu_to_le32(0xC000008B)
+#define STATUS_ARRAY_BOUNDS_EXCEEDED __constant_cpu_to_le32(0xC000008C)
+#define STATUS_FLOAT_DENORMAL_OPERAND __constant_cpu_to_le32(0xC000008D)
+#define STATUS_FLOAT_DIVIDE_BY_ZERO __constant_cpu_to_le32(0xC000008E)
+#define STATUS_FLOAT_INEXACT_RESULT __constant_cpu_to_le32(0xC000008F)
+#define STATUS_FLOAT_INVALID_OPERATION __constant_cpu_to_le32(0xC0000090)
+#define STATUS_FLOAT_OVERFLOW __constant_cpu_to_le32(0xC0000091)
+#define STATUS_FLOAT_STACK_CHECK __constant_cpu_to_le32(0xC0000092)
+#define STATUS_FLOAT_UNDERFLOW __constant_cpu_to_le32(0xC0000093)
+#define STATUS_INTEGER_DIVIDE_BY_ZERO __constant_cpu_to_le32(0xC0000094)
+#define STATUS_INTEGER_OVERFLOW __constant_cpu_to_le32(0xC0000095)
+#define STATUS_PRIVILEGED_INSTRUCTION __constant_cpu_to_le32(0xC0000096)
+#define STATUS_TOO_MANY_PAGING_FILES __constant_cpu_to_le32(0xC0000097)
+#define STATUS_FILE_INVALID __constant_cpu_to_le32(0xC0000098)
+#define STATUS_ALLOTTED_SPACE_EXCEEDED __constant_cpu_to_le32(0xC0000099)
+#define STATUS_INSUFFICIENT_RESOURCES __constant_cpu_to_le32(0xC000009A)
+#define STATUS_DFS_EXIT_PATH_FOUND __constant_cpu_to_le32(0xC000009B)
+#define STATUS_DEVICE_DATA_ERROR __constant_cpu_to_le32(0xC000009C)
+#define STATUS_DEVICE_NOT_CONNECTED __constant_cpu_to_le32(0xC000009D)
+#define STATUS_DEVICE_POWER_FAILURE __constant_cpu_to_le32(0xC000009E)
+#define STATUS_FREE_VM_NOT_AT_BASE __constant_cpu_to_le32(0xC000009F)
+#define STATUS_MEMORY_NOT_ALLOCATED __constant_cpu_to_le32(0xC00000A0)
+#define STATUS_WORKING_SET_QUOTA __constant_cpu_to_le32(0xC00000A1)
+#define STATUS_MEDIA_WRITE_PROTECTED __constant_cpu_to_le32(0xC00000A2)
+#define STATUS_DEVICE_NOT_READY __constant_cpu_to_le32(0xC00000A3)
+#define STATUS_INVALID_GROUP_ATTRIBUTES __constant_cpu_to_le32(0xC00000A4)
+#define STATUS_BAD_IMPERSONATION_LEVEL __constant_cpu_to_le32(0xC00000A5)
+#define STATUS_CANT_OPEN_ANONYMOUS __constant_cpu_to_le32(0xC00000A6)
+#define STATUS_BAD_VALIDATION_CLASS __constant_cpu_to_le32(0xC00000A7)
+#define STATUS_BAD_TOKEN_TYPE __constant_cpu_to_le32(0xC00000A8)
+#define STATUS_BAD_MASTER_BOOT_RECORD __constant_cpu_to_le32(0xC00000A9)
+#define STATUS_INSTRUCTION_MISALIGNMENT __constant_cpu_to_le32(0xC00000AA)
+#define STATUS_INSTANCE_NOT_AVAILABLE __constant_cpu_to_le32(0xC00000AB)
+#define STATUS_PIPE_NOT_AVAILABLE __constant_cpu_to_le32(0xC00000AC)
+#define STATUS_INVALID_PIPE_STATE __constant_cpu_to_le32(0xC00000AD)
+#define STATUS_PIPE_BUSY __constant_cpu_to_le32(0xC00000AE)
+#define STATUS_ILLEGAL_FUNCTION __constant_cpu_to_le32(0xC00000AF)
+#define STATUS_PIPE_DISCONNECTED __constant_cpu_to_le32(0xC00000B0)
+#define STATUS_PIPE_CLOSING __constant_cpu_to_le32(0xC00000B1)
+#define STATUS_PIPE_CONNECTED __constant_cpu_to_le32(0xC00000B2)
+#define STATUS_PIPE_LISTENING __constant_cpu_to_le32(0xC00000B3)
+#define STATUS_INVALID_READ_MODE __constant_cpu_to_le32(0xC00000B4)
+#define STATUS_IO_TIMEOUT __constant_cpu_to_le32(0xC00000B5)
+#define STATUS_FILE_FORCED_CLOSED __constant_cpu_to_le32(0xC00000B6)
+#define STATUS_PROFILING_NOT_STARTED __constant_cpu_to_le32(0xC00000B7)
+#define STATUS_PROFILING_NOT_STOPPED __constant_cpu_to_le32(0xC00000B8)
+#define STATUS_COULD_NOT_INTERPRET __constant_cpu_to_le32(0xC00000B9)
+#define STATUS_FILE_IS_A_DIRECTORY __constant_cpu_to_le32(0xC00000BA)
+#define STATUS_NOT_SUPPORTED __constant_cpu_to_le32(0xC00000BB)
+#define STATUS_REMOTE_NOT_LISTENING __constant_cpu_to_le32(0xC00000BC)
+#define STATUS_DUPLICATE_NAME __constant_cpu_to_le32(0xC00000BD)
+#define STATUS_BAD_NETWORK_PATH __constant_cpu_to_le32(0xC00000BE)
+#define STATUS_NETWORK_BUSY __constant_cpu_to_le32(0xC00000BF)
+#define STATUS_DEVICE_DOES_NOT_EXIST __constant_cpu_to_le32(0xC00000C0)
+#define STATUS_TOO_MANY_COMMANDS __constant_cpu_to_le32(0xC00000C1)
+#define STATUS_ADAPTER_HARDWARE_ERROR __constant_cpu_to_le32(0xC00000C2)
+#define STATUS_INVALID_NETWORK_RESPONSE __constant_cpu_to_le32(0xC00000C3)
+#define STATUS_UNEXPECTED_NETWORK_ERROR __constant_cpu_to_le32(0xC00000C4)
+#define STATUS_BAD_REMOTE_ADAPTER __constant_cpu_to_le32(0xC00000C5)
+#define STATUS_PRINT_QUEUE_FULL __constant_cpu_to_le32(0xC00000C6)
+#define STATUS_NO_SPOOL_SPACE __constant_cpu_to_le32(0xC00000C7)
+#define STATUS_PRINT_CANCELLED __constant_cpu_to_le32(0xC00000C8)
+#define STATUS_NETWORK_NAME_DELETED __constant_cpu_to_le32(0xC00000C9)
+#define STATUS_NETWORK_ACCESS_DENIED __constant_cpu_to_le32(0xC00000CA)
+#define STATUS_BAD_DEVICE_TYPE __constant_cpu_to_le32(0xC00000CB)
+#define STATUS_BAD_NETWORK_NAME __constant_cpu_to_le32(0xC00000CC)
+#define STATUS_TOO_MANY_NAMES __constant_cpu_to_le32(0xC00000CD)
+#define STATUS_TOO_MANY_SESSIONS __constant_cpu_to_le32(0xC00000CE)
+#define STATUS_SHARING_PAUSED __constant_cpu_to_le32(0xC00000CF)
+#define STATUS_REQUEST_NOT_ACCEPTED __constant_cpu_to_le32(0xC00000D0)
+#define STATUS_REDIRECTOR_PAUSED __constant_cpu_to_le32(0xC00000D1)
+#define STATUS_NET_WRITE_FAULT __constant_cpu_to_le32(0xC00000D2)
+#define STATUS_PROFILING_AT_LIMIT __constant_cpu_to_le32(0xC00000D3)
+#define STATUS_NOT_SAME_DEVICE __constant_cpu_to_le32(0xC00000D4)
+#define STATUS_FILE_RENAMED __constant_cpu_to_le32(0xC00000D5)
+#define STATUS_VIRTUAL_CIRCUIT_CLOSED __constant_cpu_to_le32(0xC00000D6)
+#define STATUS_NO_SECURITY_ON_OBJECT __constant_cpu_to_le32(0xC00000D7)
+#define STATUS_CANT_WAIT __constant_cpu_to_le32(0xC00000D8)
+#define STATUS_PIPE_EMPTY __constant_cpu_to_le32(0xC00000D9)
+#define STATUS_CANT_ACCESS_DOMAIN_INFO __constant_cpu_to_le32(0xC00000DA)
+#define STATUS_CANT_TERMINATE_SELF __constant_cpu_to_le32(0xC00000DB)
+#define STATUS_INVALID_SERVER_STATE __constant_cpu_to_le32(0xC00000DC)
+#define STATUS_INVALID_DOMAIN_STATE __constant_cpu_to_le32(0xC00000DD)
+#define STATUS_INVALID_DOMAIN_ROLE __constant_cpu_to_le32(0xC00000DE)
+#define STATUS_NO_SUCH_DOMAIN __constant_cpu_to_le32(0xC00000DF)
+#define STATUS_DOMAIN_EXISTS __constant_cpu_to_le32(0xC00000E0)
+#define STATUS_DOMAIN_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC00000E1)
+#define STATUS_OPLOCK_NOT_GRANTED __constant_cpu_to_le32(0xC00000E2)
+#define STATUS_INVALID_OPLOCK_PROTOCOL __constant_cpu_to_le32(0xC00000E3)
+#define STATUS_INTERNAL_DB_CORRUPTION __constant_cpu_to_le32(0xC00000E4)
+#define STATUS_INTERNAL_ERROR __constant_cpu_to_le32(0xC00000E5)
+#define STATUS_GENERIC_NOT_MAPPED __constant_cpu_to_le32(0xC00000E6)
+#define STATUS_BAD_DESCRIPTOR_FORMAT __constant_cpu_to_le32(0xC00000E7)
+#define STATUS_INVALID_USER_BUFFER __constant_cpu_to_le32(0xC00000E8)
+#define STATUS_UNEXPECTED_IO_ERROR __constant_cpu_to_le32(0xC00000E9)
+#define STATUS_UNEXPECTED_MM_CREATE_ERR __constant_cpu_to_le32(0xC00000EA)
+#define STATUS_UNEXPECTED_MM_MAP_ERROR __constant_cpu_to_le32(0xC00000EB)
+#define STATUS_UNEXPECTED_MM_EXTEND_ERR __constant_cpu_to_le32(0xC00000EC)
+#define STATUS_NOT_LOGON_PROCESS __constant_cpu_to_le32(0xC00000ED)
+#define STATUS_LOGON_SESSION_EXISTS __constant_cpu_to_le32(0xC00000EE)
+#define STATUS_INVALID_PARAMETER_1 __constant_cpu_to_le32(0xC00000EF)
+#define STATUS_INVALID_PARAMETER_2 __constant_cpu_to_le32(0xC00000F0)
+#define STATUS_INVALID_PARAMETER_3 __constant_cpu_to_le32(0xC00000F1)
+#define STATUS_INVALID_PARAMETER_4 __constant_cpu_to_le32(0xC00000F2)
+#define STATUS_INVALID_PARAMETER_5 __constant_cpu_to_le32(0xC00000F3)
+#define STATUS_INVALID_PARAMETER_6 __constant_cpu_to_le32(0xC00000F4)
+#define STATUS_INVALID_PARAMETER_7 __constant_cpu_to_le32(0xC00000F5)
+#define STATUS_INVALID_PARAMETER_8 __constant_cpu_to_le32(0xC00000F6)
+#define STATUS_INVALID_PARAMETER_9 __constant_cpu_to_le32(0xC00000F7)
+#define STATUS_INVALID_PARAMETER_10 __constant_cpu_to_le32(0xC00000F8)
+#define STATUS_INVALID_PARAMETER_11 __constant_cpu_to_le32(0xC00000F9)
+#define STATUS_INVALID_PARAMETER_12 __constant_cpu_to_le32(0xC00000FA)
+#define STATUS_REDIRECTOR_NOT_STARTED __constant_cpu_to_le32(0xC00000FB)
+#define STATUS_REDIRECTOR_STARTED __constant_cpu_to_le32(0xC00000FC)
+#define STATUS_STACK_OVERFLOW __constant_cpu_to_le32(0xC00000FD)
+#define STATUS_NO_SUCH_PACKAGE __constant_cpu_to_le32(0xC00000FE)
+#define STATUS_BAD_FUNCTION_TABLE __constant_cpu_to_le32(0xC00000FF)
+#define STATUS_VARIABLE_NOT_FOUND __constant_cpu_to_le32(0xC0000100)
+#define STATUS_DIRECTORY_NOT_EMPTY __constant_cpu_to_le32(0xC0000101)
+#define STATUS_FILE_CORRUPT_ERROR __constant_cpu_to_le32(0xC0000102)
+#define STATUS_NOT_A_DIRECTORY __constant_cpu_to_le32(0xC0000103)
+#define STATUS_BAD_LOGON_SESSION_STATE __constant_cpu_to_le32(0xC0000104)
+#define STATUS_LOGON_SESSION_COLLISION __constant_cpu_to_le32(0xC0000105)
+#define STATUS_NAME_TOO_LONG __constant_cpu_to_le32(0xC0000106)
+#define STATUS_FILES_OPEN __constant_cpu_to_le32(0xC0000107)
+#define STATUS_CONNECTION_IN_USE __constant_cpu_to_le32(0xC0000108)
+#define STATUS_MESSAGE_NOT_FOUND __constant_cpu_to_le32(0xC0000109)
+#define STATUS_PROCESS_IS_TERMINATING __constant_cpu_to_le32(0xC000010A)
+#define STATUS_INVALID_LOGON_TYPE __constant_cpu_to_le32(0xC000010B)
+#define STATUS_NO_GUID_TRANSLATION __constant_cpu_to_le32(0xC000010C)
+#define STATUS_CANNOT_IMPERSONATE __constant_cpu_to_le32(0xC000010D)
+#define STATUS_IMAGE_ALREADY_LOADED __constant_cpu_to_le32(0xC000010E)
+#define STATUS_ABIOS_NOT_PRESENT __constant_cpu_to_le32(0xC000010F)
+#define STATUS_ABIOS_LID_NOT_EXIST __constant_cpu_to_le32(0xC0000110)
+#define STATUS_ABIOS_LID_ALREADY_OWNED __constant_cpu_to_le32(0xC0000111)
+#define STATUS_ABIOS_NOT_LID_OWNER __constant_cpu_to_le32(0xC0000112)
+#define STATUS_ABIOS_INVALID_COMMAND __constant_cpu_to_le32(0xC0000113)
+#define STATUS_ABIOS_INVALID_LID __constant_cpu_to_le32(0xC0000114)
+#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE __constant_cpu_to_le32(0xC0000115)
+#define STATUS_ABIOS_INVALID_SELECTOR __constant_cpu_to_le32(0xC0000116)
+#define STATUS_NO_LDT __constant_cpu_to_le32(0xC0000117)
+#define STATUS_INVALID_LDT_SIZE __constant_cpu_to_le32(0xC0000118)
+#define STATUS_INVALID_LDT_OFFSET __constant_cpu_to_le32(0xC0000119)
+#define STATUS_INVALID_LDT_DESCRIPTOR __constant_cpu_to_le32(0xC000011A)
+#define STATUS_INVALID_IMAGE_NE_FORMAT __constant_cpu_to_le32(0xC000011B)
+#define STATUS_RXACT_INVALID_STATE __constant_cpu_to_le32(0xC000011C)
+#define STATUS_RXACT_COMMIT_FAILURE __constant_cpu_to_le32(0xC000011D)
+#define STATUS_MAPPED_FILE_SIZE_ZERO __constant_cpu_to_le32(0xC000011E)
+#define STATUS_TOO_MANY_OPENED_FILES __constant_cpu_to_le32(0xC000011F)
+#define STATUS_CANCELLED __constant_cpu_to_le32(0xC0000120)
+#define STATUS_CANNOT_DELETE __constant_cpu_to_le32(0xC0000121)
+#define STATUS_INVALID_COMPUTER_NAME __constant_cpu_to_le32(0xC0000122)
+#define STATUS_FILE_DELETED __constant_cpu_to_le32(0xC0000123)
+#define STATUS_SPECIAL_ACCOUNT __constant_cpu_to_le32(0xC0000124)
+#define STATUS_SPECIAL_GROUP __constant_cpu_to_le32(0xC0000125)
+#define STATUS_SPECIAL_USER __constant_cpu_to_le32(0xC0000126)
+#define STATUS_MEMBERS_PRIMARY_GROUP __constant_cpu_to_le32(0xC0000127)
+#define STATUS_FILE_CLOSED __constant_cpu_to_le32(0xC0000128)
+#define STATUS_TOO_MANY_THREADS __constant_cpu_to_le32(0xC0000129)
+#define STATUS_THREAD_NOT_IN_PROCESS __constant_cpu_to_le32(0xC000012A)
+#define STATUS_TOKEN_ALREADY_IN_USE __constant_cpu_to_le32(0xC000012B)
+#define STATUS_PAGEFILE_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC000012C)
+#define STATUS_COMMITMENT_LIMIT __constant_cpu_to_le32(0xC000012D)
+#define STATUS_INVALID_IMAGE_LE_FORMAT __constant_cpu_to_le32(0xC000012E)
+#define STATUS_INVALID_IMAGE_NOT_MZ __constant_cpu_to_le32(0xC000012F)
+#define STATUS_INVALID_IMAGE_PROTECT __constant_cpu_to_le32(0xC0000130)
+#define STATUS_INVALID_IMAGE_WIN_16 __constant_cpu_to_le32(0xC0000131)
+#define STATUS_LOGON_SERVER_CONFLICT __constant_cpu_to_le32(0xC0000132)
+#define STATUS_TIME_DIFFERENCE_AT_DC __constant_cpu_to_le32(0xC0000133)
+#define STATUS_SYNCHRONIZATION_REQUIRED __constant_cpu_to_le32(0xC0000134)
+#define STATUS_DLL_NOT_FOUND __constant_cpu_to_le32(0xC0000135)
+#define STATUS_OPEN_FAILED __constant_cpu_to_le32(0xC0000136)
+#define STATUS_IO_PRIVILEGE_FAILED __constant_cpu_to_le32(0xC0000137)
+#define STATUS_ORDINAL_NOT_FOUND __constant_cpu_to_le32(0xC0000138)
+#define STATUS_ENTRYPOINT_NOT_FOUND __constant_cpu_to_le32(0xC0000139)
+#define STATUS_CONTROL_C_EXIT __constant_cpu_to_le32(0xC000013A)
+#define STATUS_LOCAL_DISCONNECT __constant_cpu_to_le32(0xC000013B)
+#define STATUS_REMOTE_DISCONNECT __constant_cpu_to_le32(0xC000013C)
+#define STATUS_REMOTE_RESOURCES __constant_cpu_to_le32(0xC000013D)
+#define STATUS_LINK_FAILED __constant_cpu_to_le32(0xC000013E)
+#define STATUS_LINK_TIMEOUT __constant_cpu_to_le32(0xC000013F)
+#define STATUS_INVALID_CONNECTION __constant_cpu_to_le32(0xC0000140)
+#define STATUS_INVALID_ADDRESS __constant_cpu_to_le32(0xC0000141)
+#define STATUS_DLL_INIT_FAILED __constant_cpu_to_le32(0xC0000142)
+#define STATUS_MISSING_SYSTEMFILE __constant_cpu_to_le32(0xC0000143)
+#define STATUS_UNHANDLED_EXCEPTION __constant_cpu_to_le32(0xC0000144)
+#define STATUS_APP_INIT_FAILURE __constant_cpu_to_le32(0xC0000145)
+#define STATUS_PAGEFILE_CREATE_FAILED __constant_cpu_to_le32(0xC0000146)
+#define STATUS_NO_PAGEFILE __constant_cpu_to_le32(0xC0000147)
+#define STATUS_INVALID_LEVEL __constant_cpu_to_le32(0xC0000148)
+#define STATUS_WRONG_PASSWORD_CORE __constant_cpu_to_le32(0xC0000149)
+#define STATUS_ILLEGAL_FLOAT_CONTEXT __constant_cpu_to_le32(0xC000014A)
+#define STATUS_PIPE_BROKEN __constant_cpu_to_le32(0xC000014B)
+#define STATUS_REGISTRY_CORRUPT __constant_cpu_to_le32(0xC000014C)
+#define STATUS_REGISTRY_IO_FAILED __constant_cpu_to_le32(0xC000014D)
+#define STATUS_NO_EVENT_PAIR __constant_cpu_to_le32(0xC000014E)
+#define STATUS_UNRECOGNIZED_VOLUME __constant_cpu_to_le32(0xC000014F)
+#define STATUS_SERIAL_NO_DEVICE_INITED __constant_cpu_to_le32(0xC0000150)
+#define STATUS_NO_SUCH_ALIAS __constant_cpu_to_le32(0xC0000151)
+#define STATUS_MEMBER_NOT_IN_ALIAS __constant_cpu_to_le32(0xC0000152)
+#define STATUS_MEMBER_IN_ALIAS __constant_cpu_to_le32(0xC0000153)
+#define STATUS_ALIAS_EXISTS __constant_cpu_to_le32(0xC0000154)
+#define STATUS_LOGON_NOT_GRANTED __constant_cpu_to_le32(0xC0000155)
+#define STATUS_TOO_MANY_SECRETS __constant_cpu_to_le32(0xC0000156)
+#define STATUS_SECRET_TOO_LONG __constant_cpu_to_le32(0xC0000157)
+#define STATUS_INTERNAL_DB_ERROR __constant_cpu_to_le32(0xC0000158)
+#define STATUS_FULLSCREEN_MODE __constant_cpu_to_le32(0xC0000159)
+#define STATUS_TOO_MANY_CONTEXT_IDS __constant_cpu_to_le32(0xC000015A)
+#define STATUS_LOGON_TYPE_NOT_GRANTED __constant_cpu_to_le32(0xC000015B)
+#define STATUS_NOT_REGISTRY_FILE __constant_cpu_to_le32(0xC000015C)
+#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED __constant_cpu_to_le32(0xC000015D)
+#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR __constant_cpu_to_le32(0xC000015E)
+#define STATUS_FT_MISSING_MEMBER __constant_cpu_to_le32(0xC000015F)
+#define STATUS_ILL_FORMED_SERVICE_ENTRY __constant_cpu_to_le32(0xC0000160)
+#define STATUS_ILLEGAL_CHARACTER __constant_cpu_to_le32(0xC0000161)
+#define STATUS_UNMAPPABLE_CHARACTER __constant_cpu_to_le32(0xC0000162)
+#define STATUS_UNDEFINED_CHARACTER __constant_cpu_to_le32(0xC0000163)
+#define STATUS_FLOPPY_VOLUME __constant_cpu_to_le32(0xC0000164)
+#define STATUS_FLOPPY_ID_MARK_NOT_FOUND __constant_cpu_to_le32(0xC0000165)
+#define STATUS_FLOPPY_WRONG_CYLINDER __constant_cpu_to_le32(0xC0000166)
+#define STATUS_FLOPPY_UNKNOWN_ERROR __constant_cpu_to_le32(0xC0000167)
+#define STATUS_FLOPPY_BAD_REGISTERS __constant_cpu_to_le32(0xC0000168)
+#define STATUS_DISK_RECALIBRATE_FAILED __constant_cpu_to_le32(0xC0000169)
+#define STATUS_DISK_OPERATION_FAILED __constant_cpu_to_le32(0xC000016A)
+#define STATUS_DISK_RESET_FAILED __constant_cpu_to_le32(0xC000016B)
+#define STATUS_SHARED_IRQ_BUSY __constant_cpu_to_le32(0xC000016C)
+#define STATUS_FT_ORPHANING __constant_cpu_to_le32(0xC000016D)
+#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT __constant_cpu_to_le32(0xC000016E)
+#define STATUS_PARTITION_FAILURE __constant_cpu_to_le32(0xC0000172)
+#define STATUS_INVALID_BLOCK_LENGTH __constant_cpu_to_le32(0xC0000173)
+#define STATUS_DEVICE_NOT_PARTITIONED __constant_cpu_to_le32(0xC0000174)
+#define STATUS_UNABLE_TO_LOCK_MEDIA __constant_cpu_to_le32(0xC0000175)
+#define STATUS_UNABLE_TO_UNLOAD_MEDIA __constant_cpu_to_le32(0xC0000176)
+#define STATUS_EOM_OVERFLOW __constant_cpu_to_le32(0xC0000177)
+#define STATUS_NO_MEDIA __constant_cpu_to_le32(0xC0000178)
+#define STATUS_NO_SUCH_MEMBER __constant_cpu_to_le32(0xC000017A)
+#define STATUS_INVALID_MEMBER __constant_cpu_to_le32(0xC000017B)
+#define STATUS_KEY_DELETED __constant_cpu_to_le32(0xC000017C)
+#define STATUS_NO_LOG_SPACE __constant_cpu_to_le32(0xC000017D)
+#define STATUS_TOO_MANY_SIDS __constant_cpu_to_le32(0xC000017E)
+#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED __constant_cpu_to_le32(0xC000017F)
+#define STATUS_KEY_HAS_CHILDREN __constant_cpu_to_le32(0xC0000180)
+#define STATUS_CHILD_MUST_BE_VOLATILE __constant_cpu_to_le32(0xC0000181)
+#define STATUS_DEVICE_CONFIGURATION_ERROR __constant_cpu_to_le32(0xC0000182)
+#define STATUS_DRIVER_INTERNAL_ERROR __constant_cpu_to_le32(0xC0000183)
+#define STATUS_INVALID_DEVICE_STATE __constant_cpu_to_le32(0xC0000184)
+#define STATUS_IO_DEVICE_ERROR __constant_cpu_to_le32(0xC0000185)
+#define STATUS_DEVICE_PROTOCOL_ERROR __constant_cpu_to_le32(0xC0000186)
+#define STATUS_BACKUP_CONTROLLER __constant_cpu_to_le32(0xC0000187)
+#define STATUS_LOG_FILE_FULL __constant_cpu_to_le32(0xC0000188)
+#define STATUS_TOO_LATE __constant_cpu_to_le32(0xC0000189)
+#define STATUS_NO_TRUST_LSA_SECRET __constant_cpu_to_le32(0xC000018A)
+#define STATUS_NO_TRUST_SAM_ACCOUNT __constant_cpu_to_le32(0xC000018B)
+#define STATUS_TRUSTED_DOMAIN_FAILURE __constant_cpu_to_le32(0xC000018C)
+#define STATUS_TRUSTED_RELATIONSHIP_FAILURE __constant_cpu_to_le32(0xC000018D)
+#define STATUS_EVENTLOG_FILE_CORRUPT __constant_cpu_to_le32(0xC000018E)
+#define STATUS_EVENTLOG_CANT_START __constant_cpu_to_le32(0xC000018F)
+#define STATUS_TRUST_FAILURE __constant_cpu_to_le32(0xC0000190)
+#define STATUS_MUTANT_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC0000191)
+#define STATUS_NETLOGON_NOT_STARTED __constant_cpu_to_le32(0xC0000192)
+#define STATUS_ACCOUNT_EXPIRED __constant_cpu_to_le32(0xC0000193)
+#define STATUS_POSSIBLE_DEADLOCK __constant_cpu_to_le32(0xC0000194)
+#define STATUS_NETWORK_CREDENTIAL_CONFLICT __constant_cpu_to_le32(0xC0000195)
+#define STATUS_REMOTE_SESSION_LIMIT __constant_cpu_to_le32(0xC0000196)
+#define STATUS_EVENTLOG_FILE_CHANGED __constant_cpu_to_le32(0xC0000197)
+#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT __constant_cpu_to_le32(0xC0000198)
+#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT __constant_cpu_to_le32(0xC0000199)
+#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT __constant_cpu_to_le32(0xC000019A)
+#define STATUS_DOMAIN_TRUST_INCONSISTENT __constant_cpu_to_le32(0xC000019B)
+#define STATUS_FS_DRIVER_REQUIRED __constant_cpu_to_le32(0xC000019C)
+#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL __constant_cpu_to_le32(0xC000019D)
+#define STATUS_NETWORK_OPEN_RESTRICTION __constant_cpu_to_le32(0xC0000201)
+#define STATUS_NO_USER_SESSION_KEY __constant_cpu_to_le32(0xC0000202)
+#define STATUS_USER_SESSION_DELETED __constant_cpu_to_le32(0xC0000203)
+#define STATUS_RESOURCE_LANG_NOT_FOUND __constant_cpu_to_le32(0xC0000204)
+#define STATUS_INSUFF_SERVER_RESOURCES __constant_cpu_to_le32(0xC0000205)
+#define STATUS_INVALID_BUFFER_SIZE __constant_cpu_to_le32(0xC0000206)
+#define STATUS_INVALID_ADDRESS_COMPONENT __constant_cpu_to_le32(0xC0000207)
+#define STATUS_INVALID_ADDRESS_WILDCARD __constant_cpu_to_le32(0xC0000208)
+#define STATUS_TOO_MANY_ADDRESSES __constant_cpu_to_le32(0xC0000209)
+#define STATUS_ADDRESS_ALREADY_EXISTS __constant_cpu_to_le32(0xC000020A)
+#define STATUS_ADDRESS_CLOSED __constant_cpu_to_le32(0xC000020B)
+#define STATUS_CONNECTION_DISCONNECTED __constant_cpu_to_le32(0xC000020C)
+#define STATUS_CONNECTION_RESET __constant_cpu_to_le32(0xC000020D)
+#define STATUS_TOO_MANY_NODES __constant_cpu_to_le32(0xC000020E)
+#define STATUS_TRANSACTION_ABORTED __constant_cpu_to_le32(0xC000020F)
+#define STATUS_TRANSACTION_TIMED_OUT __constant_cpu_to_le32(0xC0000210)
+#define STATUS_TRANSACTION_NO_RELEASE __constant_cpu_to_le32(0xC0000211)
+#define STATUS_TRANSACTION_NO_MATCH __constant_cpu_to_le32(0xC0000212)
+#define STATUS_TRANSACTION_RESPONDED __constant_cpu_to_le32(0xC0000213)
+#define STATUS_TRANSACTION_INVALID_ID __constant_cpu_to_le32(0xC0000214)
+#define STATUS_TRANSACTION_INVALID_TYPE __constant_cpu_to_le32(0xC0000215)
+#define STATUS_NOT_SERVER_SESSION __constant_cpu_to_le32(0xC0000216)
+#define STATUS_NOT_CLIENT_SESSION __constant_cpu_to_le32(0xC0000217)
+#define STATUS_CANNOT_LOAD_REGISTRY_FILE __constant_cpu_to_le32(0xC0000218)
+#define STATUS_DEBUG_ATTACH_FAILED __constant_cpu_to_le32(0xC0000219)
+#define STATUS_SYSTEM_PROCESS_TERMINATED __constant_cpu_to_le32(0xC000021A)
+#define STATUS_DATA_NOT_ACCEPTED __constant_cpu_to_le32(0xC000021B)
+#define STATUS_NO_BROWSER_SERVERS_FOUND __constant_cpu_to_le32(0xC000021C)
+#define STATUS_VDM_HARD_ERROR __constant_cpu_to_le32(0xC000021D)
+#define STATUS_DRIVER_CANCEL_TIMEOUT __constant_cpu_to_le32(0xC000021E)
+#define STATUS_REPLY_MESSAGE_MISMATCH __constant_cpu_to_le32(0xC000021F)
+#define STATUS_MAPPED_ALIGNMENT __constant_cpu_to_le32(0xC0000220)
+#define STATUS_IMAGE_CHECKSUM_MISMATCH __constant_cpu_to_le32(0xC0000221)
+#define STATUS_LOST_WRITEBEHIND_DATA __constant_cpu_to_le32(0xC0000222)
+#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID __constant_cpu_to_le32(0xC0000223)
+#define STATUS_PASSWORD_MUST_CHANGE __constant_cpu_to_le32(0xC0000224)
+#define STATUS_NOT_FOUND __constant_cpu_to_le32(0xC0000225)
+#define STATUS_NOT_TINY_STREAM __constant_cpu_to_le32(0xC0000226)
+#define STATUS_RECOVERY_FAILURE __constant_cpu_to_le32(0xC0000227)
+#define STATUS_STACK_OVERFLOW_READ __constant_cpu_to_le32(0xC0000228)
+#define STATUS_FAIL_CHECK __constant_cpu_to_le32(0xC0000229)
+#define STATUS_DUPLICATE_OBJECTID __constant_cpu_to_le32(0xC000022A)
+#define STATUS_OBJECTID_EXISTS __constant_cpu_to_le32(0xC000022B)
+#define STATUS_CONVERT_TO_LARGE __constant_cpu_to_le32(0xC000022C)
+#define STATUS_RETRY __constant_cpu_to_le32(0xC000022D)
+#define STATUS_FOUND_OUT_OF_SCOPE __constant_cpu_to_le32(0xC000022E)
+#define STATUS_ALLOCATE_BUCKET __constant_cpu_to_le32(0xC000022F)
+#define STATUS_PROPSET_NOT_FOUND __constant_cpu_to_le32(0xC0000230)
+#define STATUS_MARSHALL_OVERFLOW __constant_cpu_to_le32(0xC0000231)
+#define STATUS_INVALID_VARIANT __constant_cpu_to_le32(0xC0000232)
+#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND __constant_cpu_to_le32(0xC0000233)
+#define STATUS_ACCOUNT_LOCKED_OUT __constant_cpu_to_le32(0xC0000234)
+#define STATUS_HANDLE_NOT_CLOSABLE __constant_cpu_to_le32(0xC0000235)
+#define STATUS_CONNECTION_REFUSED __constant_cpu_to_le32(0xC0000236)
+#define STATUS_GRACEFUL_DISCONNECT __constant_cpu_to_le32(0xC0000237)
+#define STATUS_ADDRESS_ALREADY_ASSOCIATED __constant_cpu_to_le32(0xC0000238)
+#define STATUS_ADDRESS_NOT_ASSOCIATED __constant_cpu_to_le32(0xC0000239)
+#define STATUS_CONNECTION_INVALID __constant_cpu_to_le32(0xC000023A)
+#define STATUS_CONNECTION_ACTIVE __constant_cpu_to_le32(0xC000023B)
+#define STATUS_NETWORK_UNREACHABLE __constant_cpu_to_le32(0xC000023C)
+#define STATUS_HOST_UNREACHABLE __constant_cpu_to_le32(0xC000023D)
+#define STATUS_PROTOCOL_UNREACHABLE __constant_cpu_to_le32(0xC000023E)
+#define STATUS_PORT_UNREACHABLE __constant_cpu_to_le32(0xC000023F)
+#define STATUS_REQUEST_ABORTED __constant_cpu_to_le32(0xC0000240)
+#define STATUS_CONNECTION_ABORTED __constant_cpu_to_le32(0xC0000241)
+#define STATUS_BAD_COMPRESSION_BUFFER __constant_cpu_to_le32(0xC0000242)
+#define STATUS_USER_MAPPED_FILE __constant_cpu_to_le32(0xC0000243)
+#define STATUS_AUDIT_FAILED __constant_cpu_to_le32(0xC0000244)
+#define STATUS_TIMER_RESOLUTION_NOT_SET __constant_cpu_to_le32(0xC0000245)
+#define STATUS_CONNECTION_COUNT_LIMIT __constant_cpu_to_le32(0xC0000246)
+#define STATUS_LOGIN_TIME_RESTRICTION __constant_cpu_to_le32(0xC0000247)
+#define STATUS_LOGIN_WKSTA_RESTRICTION __constant_cpu_to_le32(0xC0000248)
+#define STATUS_IMAGE_MP_UP_MISMATCH __constant_cpu_to_le32(0xC0000249)
+#define STATUS_INSUFFICIENT_LOGON_INFO __constant_cpu_to_le32(0xC0000250)
+#define STATUS_BAD_DLL_ENTRYPOINT __constant_cpu_to_le32(0xC0000251)
+#define STATUS_BAD_SERVICE_ENTRYPOINT __constant_cpu_to_le32(0xC0000252)
+#define STATUS_LPC_REPLY_LOST __constant_cpu_to_le32(0xC0000253)
+#define STATUS_IP_ADDRESS_CONFLICT1 __constant_cpu_to_le32(0xC0000254)
+#define STATUS_IP_ADDRESS_CONFLICT2 __constant_cpu_to_le32(0xC0000255)
+#define STATUS_REGISTRY_QUOTA_LIMIT __constant_cpu_to_le32(0xC0000256)
+#define STATUS_PATH_NOT_COVERED __constant_cpu_to_le32(0xC0000257)
+#define STATUS_NO_CALLBACK_ACTIVE __constant_cpu_to_le32(0xC0000258)
+#define STATUS_LICENSE_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000259)
+#define STATUS_PWD_TOO_SHORT __constant_cpu_to_le32(0xC000025A)
+#define STATUS_PWD_TOO_RECENT __constant_cpu_to_le32(0xC000025B)
+#define STATUS_PWD_HISTORY_CONFLICT __constant_cpu_to_le32(0xC000025C)
+#define STATUS_PLUGPLAY_NO_DEVICE __constant_cpu_to_le32(0xC000025E)
+#define STATUS_UNSUPPORTED_COMPRESSION __constant_cpu_to_le32(0xC000025F)
+#define STATUS_INVALID_HW_PROFILE __constant_cpu_to_le32(0xC0000260)
+#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH __constant_cpu_to_le32(0xC0000261)
+#define STATUS_DRIVER_ORDINAL_NOT_FOUND __constant_cpu_to_le32(0xC0000262)
+#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND __constant_cpu_to_le32(0xC0000263)
+#define STATUS_RESOURCE_NOT_OWNED __constant_cpu_to_le32(0xC0000264)
+#define STATUS_TOO_MANY_LINKS __constant_cpu_to_le32(0xC0000265)
+#define STATUS_QUOTA_LIST_INCONSISTENT __constant_cpu_to_le32(0xC0000266)
+#define STATUS_FILE_IS_OFFLINE __constant_cpu_to_le32(0xC0000267)
+#define STATUS_EVALUATION_EXPIRATION __constant_cpu_to_le32(0xC0000268)
+#define STATUS_ILLEGAL_DLL_RELOCATION __constant_cpu_to_le32(0xC0000269)
+#define STATUS_LICENSE_VIOLATION __constant_cpu_to_le32(0xC000026A)
+#define STATUS_DLL_INIT_FAILED_LOGOFF __constant_cpu_to_le32(0xC000026B)
+#define STATUS_DRIVER_UNABLE_TO_LOAD __constant_cpu_to_le32(0xC000026C)
+#define STATUS_DFS_UNAVAILABLE __constant_cpu_to_le32(0xC000026D)
+#define STATUS_VOLUME_DISMOUNTED __constant_cpu_to_le32(0xC000026E)
+#define STATUS_WX86_INTERNAL_ERROR __constant_cpu_to_le32(0xC000026F)
+#define STATUS_WX86_FLOAT_STACK_CHECK __constant_cpu_to_le32(0xC0000270)
+#define STATUS_VALIDATE_CONTINUE __constant_cpu_to_le32(0xC0000271)
+#define STATUS_NO_MATCH __constant_cpu_to_le32(0xC0000272)
+#define STATUS_NO_MORE_MATCHES __constant_cpu_to_le32(0xC0000273)
+#define STATUS_NOT_A_REPARSE_POINT __constant_cpu_to_le32(0xC0000275)
+#define STATUS_IO_REPARSE_TAG_INVALID __constant_cpu_to_le32(0xC0000276)
+#define STATUS_IO_REPARSE_TAG_MISMATCH __constant_cpu_to_le32(0xC0000277)
+#define STATUS_IO_REPARSE_DATA_INVALID __constant_cpu_to_le32(0xC0000278)
+#define STATUS_IO_REPARSE_TAG_NOT_HANDLED __constant_cpu_to_le32(0xC0000279)
+#define STATUS_REPARSE_POINT_NOT_RESOLVED __constant_cpu_to_le32(0xC0000280)
+#define STATUS_DIRECTORY_IS_A_REPARSE_POINT __constant_cpu_to_le32(0xC0000281)
+#define STATUS_RANGE_LIST_CONFLICT __constant_cpu_to_le32(0xC0000282)
+#define STATUS_SOURCE_ELEMENT_EMPTY __constant_cpu_to_le32(0xC0000283)
+#define STATUS_DESTINATION_ELEMENT_FULL __constant_cpu_to_le32(0xC0000284)
+#define STATUS_ILLEGAL_ELEMENT_ADDRESS __constant_cpu_to_le32(0xC0000285)
+#define STATUS_MAGAZINE_NOT_PRESENT __constant_cpu_to_le32(0xC0000286)
+#define STATUS_REINITIALIZATION_NEEDED __constant_cpu_to_le32(0xC0000287)
+#define STATUS_ENCRYPTION_FAILED __constant_cpu_to_le32(0xC000028A)
+#define STATUS_DECRYPTION_FAILED __constant_cpu_to_le32(0xC000028B)
+#define STATUS_RANGE_NOT_FOUND __constant_cpu_to_le32(0xC000028C)
+#define STATUS_NO_RECOVERY_POLICY __constant_cpu_to_le32(0xC000028D)
+#define STATUS_NO_EFS __constant_cpu_to_le32(0xC000028E)
+#define STATUS_WRONG_EFS __constant_cpu_to_le32(0xC000028F)
+#define STATUS_NO_USER_KEYS __constant_cpu_to_le32(0xC0000290)
+#define STATUS_FILE_NOT_ENCRYPTED __constant_cpu_to_le32(0xC0000291)
+#define STATUS_NOT_EXPORT_FORMAT __constant_cpu_to_le32(0xC0000292)
+#define STATUS_FILE_ENCRYPTED __constant_cpu_to_le32(0xC0000293)
+#define STATUS_WMI_GUID_NOT_FOUND __constant_cpu_to_le32(0xC0000295)
+#define STATUS_WMI_INSTANCE_NOT_FOUND __constant_cpu_to_le32(0xC0000296)
+#define STATUS_WMI_ITEMID_NOT_FOUND __constant_cpu_to_le32(0xC0000297)
+#define STATUS_WMI_TRY_AGAIN __constant_cpu_to_le32(0xC0000298)
+#define STATUS_SHARED_POLICY __constant_cpu_to_le32(0xC0000299)
+#define STATUS_POLICY_OBJECT_NOT_FOUND __constant_cpu_to_le32(0xC000029A)
+#define STATUS_POLICY_ONLY_IN_DS __constant_cpu_to_le32(0xC000029B)
+#define STATUS_VOLUME_NOT_UPGRADED __constant_cpu_to_le32(0xC000029C)
+#define STATUS_REMOTE_STORAGE_NOT_ACTIVE __constant_cpu_to_le32(0xC000029D)
+#define STATUS_REMOTE_STORAGE_MEDIA_ERROR __constant_cpu_to_le32(0xC000029E)
+#define STATUS_NO_TRACKING_SERVICE __constant_cpu_to_le32(0xC000029F)
+#define STATUS_SERVER_SID_MISMATCH __constant_cpu_to_le32(0xC00002A0)
+#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE __constant_cpu_to_le32(0xC00002A1)
+#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX __constant_cpu_to_le32(0xC00002A2)
+#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED __constant_cpu_to_le32(0xC00002A3)
+#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS __constant_cpu_to_le32(0xC00002A4)
+#define STATUS_DS_BUSY __constant_cpu_to_le32(0xC00002A5)
+#define STATUS_DS_UNAVAILABLE __constant_cpu_to_le32(0xC00002A6)
+#define STATUS_DS_NO_RIDS_ALLOCATED __constant_cpu_to_le32(0xC00002A7)
+#define STATUS_DS_NO_MORE_RIDS __constant_cpu_to_le32(0xC00002A8)
+#define STATUS_DS_INCORRECT_ROLE_OWNER __constant_cpu_to_le32(0xC00002A9)
+#define STATUS_DS_RIDMGR_INIT_ERROR __constant_cpu_to_le32(0xC00002AA)
+#define STATUS_DS_OBJ_CLASS_VIOLATION __constant_cpu_to_le32(0xC00002AB)
+#define STATUS_DS_CANT_ON_NON_LEAF __constant_cpu_to_le32(0xC00002AC)
+#define STATUS_DS_CANT_ON_RDN __constant_cpu_to_le32(0xC00002AD)
+#define STATUS_DS_CANT_MOD_OBJ_CLASS __constant_cpu_to_le32(0xC00002AE)
+#define STATUS_DS_CROSS_DOM_MOVE_FAILED __constant_cpu_to_le32(0xC00002AF)
+#define STATUS_DS_GC_NOT_AVAILABLE __constant_cpu_to_le32(0xC00002B0)
+#define STATUS_DIRECTORY_SERVICE_REQUIRED __constant_cpu_to_le32(0xC00002B1)
+#define STATUS_REPARSE_ATTRIBUTE_CONFLICT __constant_cpu_to_le32(0xC00002B2)
+#define STATUS_CANT_ENABLE_DENY_ONLY __constant_cpu_to_le32(0xC00002B3)
+#define STATUS_FLOAT_MULTIPLE_FAULTS __constant_cpu_to_le32(0xC00002B4)
+#define STATUS_FLOAT_MULTIPLE_TRAPS __constant_cpu_to_le32(0xC00002B5)
+#define STATUS_DEVICE_REMOVED __constant_cpu_to_le32(0xC00002B6)
+#define STATUS_JOURNAL_DELETE_IN_PROGRESS __constant_cpu_to_le32(0xC00002B7)
+#define STATUS_JOURNAL_NOT_ACTIVE __constant_cpu_to_le32(0xC00002B8)
+#define STATUS_NOINTERFACE __constant_cpu_to_le32(0xC00002B9)
+#define STATUS_DS_ADMIN_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC00002C1)
+#define STATUS_DRIVER_FAILED_SLEEP __constant_cpu_to_le32(0xC00002C2)
+#define STATUS_MUTUAL_AUTHENTICATION_FAILED __constant_cpu_to_le32(0xC00002C3)
+#define STATUS_CORRUPT_SYSTEM_FILE __constant_cpu_to_le32(0xC00002C4)
+#define STATUS_DATATYPE_MISALIGNMENT_ERROR __constant_cpu_to_le32(0xC00002C5)
+#define STATUS_WMI_READ_ONLY __constant_cpu_to_le32(0xC00002C6)
+#define STATUS_WMI_SET_FAILURE __constant_cpu_to_le32(0xC00002C7)
+#define STATUS_COMMITMENT_MINIMUM __constant_cpu_to_le32(0xC00002C8)
+#define STATUS_REG_NAT_CONSUMPTION __constant_cpu_to_le32(0xC00002C9)
+#define STATUS_TRANSPORT_FULL __constant_cpu_to_le32(0xC00002CA)
+#define STATUS_DS_SAM_INIT_FAILURE __constant_cpu_to_le32(0xC00002CB)
+#define STATUS_ONLY_IF_CONNECTED __constant_cpu_to_le32(0xC00002CC)
+#define STATUS_DS_SENSITIVE_GROUP_VIOLATION __constant_cpu_to_le32(0xC00002CD)
+#define STATUS_PNP_RESTART_ENUMERATION __constant_cpu_to_le32(0xC00002CE)
+#define STATUS_JOURNAL_ENTRY_DELETED __constant_cpu_to_le32(0xC00002CF)
+#define STATUS_DS_CANT_MOD_PRIMARYGROUPID __constant_cpu_to_le32(0xC00002D0)
+#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE __constant_cpu_to_le32(0xC00002D1)
+#define STATUS_PNP_REBOOT_REQUIRED __constant_cpu_to_le32(0xC00002D2)
+#define STATUS_POWER_STATE_INVALID __constant_cpu_to_le32(0xC00002D3)
+#define STATUS_DS_INVALID_GROUP_TYPE __constant_cpu_to_le32(0xC00002D4)
+#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN __constant_cpu_to_le32(0xC00002D5)
+#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN __constant_cpu_to_le32(0xC00002D6)
+#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER __constant_cpu_to_le32(0xC00002D7)
+#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER __constant_cpu_to_le32(0xC00002D8)
+#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER __constant_cpu_to_le32(0xC00002D9)
+#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER __constant_cpu_to_le32(0xC00002DA)
+#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER __constant_cpu_to_le32(0xC00002DB)
+#define STATUS_DS_HAVE_PRIMARY_MEMBERS __constant_cpu_to_le32(0xC00002DC)
+#define STATUS_WMI_NOT_SUPPORTED __constant_cpu_to_le32(0xC00002DD)
+#define STATUS_INSUFFICIENT_POWER __constant_cpu_to_le32(0xC00002DE)
+#define STATUS_SAM_NEED_BOOTKEY_PASSWORD __constant_cpu_to_le32(0xC00002DF)
+#define STATUS_SAM_NEED_BOOTKEY_FLOPPY __constant_cpu_to_le32(0xC00002E0)
+#define STATUS_DS_CANT_START __constant_cpu_to_le32(0xC00002E1)
+#define STATUS_DS_INIT_FAILURE __constant_cpu_to_le32(0xC00002E2)
+#define STATUS_SAM_INIT_FAILURE __constant_cpu_to_le32(0xC00002E3)
+#define STATUS_DS_GC_REQUIRED __constant_cpu_to_le32(0xC00002E4)
+#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY __constant_cpu_to_le32(0xC00002E5)
+#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS __constant_cpu_to_le32(0xC00002E6)
+#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC00002E7)
+#define STATUS_MULTIPLE_FAULT_VIOLATION __constant_cpu_to_le32(0xC00002E8)
+#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED __constant_cpu_to_le32(0xC00002E9)
+#define STATUS_CANNOT_MAKE __constant_cpu_to_le32(0xC00002EA)
+#define STATUS_SYSTEM_SHUTDOWN __constant_cpu_to_le32(0xC00002EB)
+#define STATUS_DS_INIT_FAILURE_CONSOLE __constant_cpu_to_le32(0xC00002EC)
+#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE __constant_cpu_to_le32(0xC00002ED)
+#define STATUS_UNFINISHED_CONTEXT_DELETED __constant_cpu_to_le32(0xC00002EE)
+#define STATUS_NO_TGT_REPLY __constant_cpu_to_le32(0xC00002EF)
+#define STATUS_OBJECTID_NOT_FOUND __constant_cpu_to_le32(0xC00002F0)
+#define STATUS_NO_IP_ADDRESSES __constant_cpu_to_le32(0xC00002F1)
+#define STATUS_WRONG_CREDENTIAL_HANDLE __constant_cpu_to_le32(0xC00002F2)
+#define STATUS_CRYPTO_SYSTEM_INVALID __constant_cpu_to_le32(0xC00002F3)
+#define STATUS_MAX_REFERRALS_EXCEEDED __constant_cpu_to_le32(0xC00002F4)
+#define STATUS_MUST_BE_KDC __constant_cpu_to_le32(0xC00002F5)
+#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED __constant_cpu_to_le32(0xC00002F6)
+#define STATUS_TOO_MANY_PRINCIPALS __constant_cpu_to_le32(0xC00002F7)
+#define STATUS_NO_PA_DATA __constant_cpu_to_le32(0xC00002F8)
+#define STATUS_PKINIT_NAME_MISMATCH __constant_cpu_to_le32(0xC00002F9)
+#define STATUS_SMARTCARD_LOGON_REQUIRED __constant_cpu_to_le32(0xC00002FA)
+#define STATUS_KDC_INVALID_REQUEST __constant_cpu_to_le32(0xC00002FB)
+#define STATUS_KDC_UNABLE_TO_REFER __constant_cpu_to_le32(0xC00002FC)
+#define STATUS_KDC_UNKNOWN_ETYPE __constant_cpu_to_le32(0xC00002FD)
+#define STATUS_SHUTDOWN_IN_PROGRESS __constant_cpu_to_le32(0xC00002FE)
+#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS __constant_cpu_to_le32(0xC00002FF)
+#define STATUS_NOT_SUPPORTED_ON_SBS __constant_cpu_to_le32(0xC0000300)
+#define STATUS_WMI_GUID_DISCONNECTED __constant_cpu_to_le32(0xC0000301)
+#define STATUS_WMI_ALREADY_DISABLED __constant_cpu_to_le32(0xC0000302)
+#define STATUS_WMI_ALREADY_ENABLED __constant_cpu_to_le32(0xC0000303)
+#define STATUS_MFT_TOO_FRAGMENTED __constant_cpu_to_le32(0xC0000304)
+#define STATUS_COPY_PROTECTION_FAILURE __constant_cpu_to_le32(0xC0000305)
+#define STATUS_CSS_AUTHENTICATION_FAILURE __constant_cpu_to_le32(0xC0000306)
+#define STATUS_CSS_KEY_NOT_PRESENT __constant_cpu_to_le32(0xC0000307)
+#define STATUS_CSS_KEY_NOT_ESTABLISHED __constant_cpu_to_le32(0xC0000308)
+#define STATUS_CSS_SCRAMBLED_SECTOR __constant_cpu_to_le32(0xC0000309)
+#define STATUS_CSS_REGION_MISMATCH __constant_cpu_to_le32(0xC000030A)
+#define STATUS_CSS_RESETS_EXHAUSTED __constant_cpu_to_le32(0xC000030B)
+#define STATUS_PKINIT_FAILURE __constant_cpu_to_le32(0xC0000320)
+#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE __constant_cpu_to_le32(0xC0000321)
+#define STATUS_NO_KERB_KEY __constant_cpu_to_le32(0xC0000322)
+#define STATUS_HOST_DOWN __constant_cpu_to_le32(0xC0000350)
+#define STATUS_UNSUPPORTED_PREAUTH __constant_cpu_to_le32(0xC0000351)
+#define STATUS_EFS_ALG_BLOB_TOO_BIG __constant_cpu_to_le32(0xC0000352)
+#define STATUS_PORT_NOT_SET __constant_cpu_to_le32(0xC0000353)
+#define STATUS_DEBUGGER_INACTIVE __constant_cpu_to_le32(0xC0000354)
+#define STATUS_DS_VERSION_CHECK_FAILURE __constant_cpu_to_le32(0xC0000355)
+#define STATUS_AUDITING_DISABLED __constant_cpu_to_le32(0xC0000356)
+#define STATUS_PRENT4_MACHINE_ACCOUNT __constant_cpu_to_le32(0xC0000357)
+#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER __constant_cpu_to_le32(0xC0000358)
+#define STATUS_INVALID_IMAGE_WIN_32 __constant_cpu_to_le32(0xC0000359)
+#define STATUS_INVALID_IMAGE_WIN_64 __constant_cpu_to_le32(0xC000035A)
+#define STATUS_BAD_BINDINGS __constant_cpu_to_le32(0xC000035B)
+#define STATUS_NETWORK_SESSION_EXPIRED __constant_cpu_to_le32(0xC000035C)
+#define STATUS_APPHELP_BLOCK __constant_cpu_to_le32(0xC000035D)
+#define STATUS_ALL_SIDS_FILTERED __constant_cpu_to_le32(0xC000035E)
+#define STATUS_NOT_SAFE_MODE_DRIVER __constant_cpu_to_le32(0xC000035F)
+#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT __constant_cpu_to_le32(0xC0000361)
+#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH __constant_cpu_to_le32(0xC0000362)
+#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER __constant_cpu_to_le32(0xC0000363)
+#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER __constant_cpu_to_le32(0xC0000364)
+#define STATUS_FAILED_DRIVER_ENTRY __constant_cpu_to_le32(0xC0000365)
+#define STATUS_DEVICE_ENUMERATION_ERROR __constant_cpu_to_le32(0xC0000366)
+#define STATUS_MOUNT_POINT_NOT_RESOLVED __constant_cpu_to_le32(0xC0000368)
+#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER __constant_cpu_to_le32(0xC0000369)
+#define STATUS_MCA_OCCURED __constant_cpu_to_le32(0xC000036A)
+#define STATUS_DRIVER_BLOCKED_CRITICAL __constant_cpu_to_le32(0xC000036B)
+#define STATUS_DRIVER_BLOCKED __constant_cpu_to_le32(0xC000036C)
+#define STATUS_DRIVER_DATABASE_ERROR __constant_cpu_to_le32(0xC000036D)
+#define STATUS_SYSTEM_HIVE_TOO_LARGE __constant_cpu_to_le32(0xC000036E)
+#define STATUS_INVALID_IMPORT_OF_NON_DLL __constant_cpu_to_le32(0xC000036F)
+#define STATUS_NO_SECRETS __constant_cpu_to_le32(0xC0000371)
+#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY __constant_cpu_to_le32(0xC0000372)
+#define STATUS_FAILED_STACK_SWITCH __constant_cpu_to_le32(0xC0000373)
+#define STATUS_HEAP_CORRUPTION __constant_cpu_to_le32(0xC0000374)
+#define STATUS_SMARTCARD_WRONG_PIN __constant_cpu_to_le32(0xC0000380)
+#define STATUS_SMARTCARD_CARD_BLOCKED __constant_cpu_to_le32(0xC0000381)
+#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED __constant_cpu_to_le32(0xC0000382)
+#define STATUS_SMARTCARD_NO_CARD __constant_cpu_to_le32(0xC0000383)
+#define STATUS_SMARTCARD_NO_KEY_CONTAINER __constant_cpu_to_le32(0xC0000384)
+#define STATUS_SMARTCARD_NO_CERTIFICATE __constant_cpu_to_le32(0xC0000385)
+#define STATUS_SMARTCARD_NO_KEYSET __constant_cpu_to_le32(0xC0000386)
+#define STATUS_SMARTCARD_IO_ERROR __constant_cpu_to_le32(0xC0000387)
+#define STATUS_DOWNGRADE_DETECTED __constant_cpu_to_le32(0xC0000388)
+#define STATUS_SMARTCARD_CERT_REVOKED __constant_cpu_to_le32(0xC0000389)
+#define STATUS_ISSUING_CA_UNTRUSTED __constant_cpu_to_le32(0xC000038A)
+#define STATUS_REVOCATION_OFFLINE_C __constant_cpu_to_le32(0xC000038B)
+#define STATUS_PKINIT_CLIENT_FAILURE __constant_cpu_to_le32(0xC000038C)
+#define STATUS_SMARTCARD_CERT_EXPIRED __constant_cpu_to_le32(0xC000038D)
+#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD __constant_cpu_to_le32(0xC000038E)
+#define STATUS_SMARTCARD_SILENT_CONTEXT __constant_cpu_to_le32(0xC000038F)
+#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000401)
+#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000402)
+#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000403)
+#define STATUS_DS_NAME_NOT_UNIQUE __constant_cpu_to_le32(0xC0000404)
+#define STATUS_DS_DUPLICATE_ID_FOUND __constant_cpu_to_le32(0xC0000405)
+#define STATUS_DS_GROUP_CONVERSION_ERROR __constant_cpu_to_le32(0xC0000406)
+#define STATUS_VOLSNAP_PREPARE_HIBERNATE __constant_cpu_to_le32(0xC0000407)
+#define STATUS_USER2USER_REQUIRED __constant_cpu_to_le32(0xC0000408)
+#define STATUS_STACK_BUFFER_OVERRUN __constant_cpu_to_le32(0xC0000409)
+#define STATUS_NO_S4U_PROT_SUPPORT __constant_cpu_to_le32(0xC000040A)
+#define STATUS_CROSSREALM_DELEGATION_FAILURE __constant_cpu_to_le32(0xC000040B)
+#define STATUS_REVOCATION_OFFLINE_KDC __constant_cpu_to_le32(0xC000040C)
+#define STATUS_ISSUING_CA_UNTRUSTED_KDC __constant_cpu_to_le32(0xC000040D)
+#define STATUS_KDC_CERT_EXPIRED __constant_cpu_to_le32(0xC000040E)
+#define STATUS_KDC_CERT_REVOKED __constant_cpu_to_le32(0xC000040F)
+#define STATUS_PARAMETER_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000410)
+#define STATUS_HIBERNATION_FAILURE __constant_cpu_to_le32(0xC0000411)
+#define STATUS_DELAY_LOAD_FAILED __constant_cpu_to_le32(0xC0000412)
+#define STATUS_AUTHENTICATION_FIREWALL_FAILED __constant_cpu_to_le32(0xC0000413)
+#define STATUS_VDM_DISALLOWED __constant_cpu_to_le32(0xC0000414)
+#define STATUS_HUNG_DISPLAY_DRIVER_THREAD __constant_cpu_to_le32(0xC0000415)
+#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE __constant_cpu_to_le32(0xC0000416)
+#define STATUS_INVALID_CRUNTIME_PARAMETER __constant_cpu_to_le32(0xC0000417)
+#define STATUS_NTLM_BLOCKED __constant_cpu_to_le32(0xC0000418)
+#define STATUS_ASSERTION_FAILURE __constant_cpu_to_le32(0xC0000420)
+#define STATUS_VERIFIER_STOP __constant_cpu_to_le32(0xC0000421)
+#define STATUS_CALLBACK_POP_STACK __constant_cpu_to_le32(0xC0000423)
+#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED __constant_cpu_to_le32(0xC0000424)
+#define STATUS_HIVE_UNLOADED __constant_cpu_to_le32(0xC0000425)
+#define STATUS_COMPRESSION_DISABLED __constant_cpu_to_le32(0xC0000426)
+#define STATUS_FILE_SYSTEM_LIMITATION __constant_cpu_to_le32(0xC0000427)
+#define STATUS_INVALID_IMAGE_HASH __constant_cpu_to_le32(0xC0000428)
+#define STATUS_NOT_CAPABLE __constant_cpu_to_le32(0xC0000429)
+#define STATUS_REQUEST_OUT_OF_SEQUENCE __constant_cpu_to_le32(0xC000042A)
+#define STATUS_IMPLEMENTATION_LIMIT __constant_cpu_to_le32(0xC000042B)
+#define STATUS_ELEVATION_REQUIRED __constant_cpu_to_le32(0xC000042C)
+#define STATUS_BEYOND_VDL __constant_cpu_to_le32(0xC0000432)
+#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS __constant_cpu_to_le32(0xC0000433)
+#define STATUS_PTE_CHANGED __constant_cpu_to_le32(0xC0000434)
+#define STATUS_PURGE_FAILED __constant_cpu_to_le32(0xC0000435)
+#define STATUS_CRED_REQUIRES_CONFIRMATION __constant_cpu_to_le32(0xC0000440)
+#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE __constant_cpu_to_le32(0xC0000441)
+#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER __constant_cpu_to_le32(0xC0000442)
+#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE __constant_cpu_to_le32(0xC0000443)
+#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE __constant_cpu_to_le32(0xC0000444)
+#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE __constant_cpu_to_le32(0xC0000445)
+#define STATUS_INVALID_LABEL __constant_cpu_to_le32(0xC0000446)
+#define STATUS_DRIVER_PROCESS_TERMINATED __constant_cpu_to_le32(0xC0000450)
+#define STATUS_AMBIGUOUS_SYSTEM_DEVICE __constant_cpu_to_le32(0xC0000451)
+#define STATUS_SYSTEM_DEVICE_NOT_FOUND __constant_cpu_to_le32(0xC0000452)
+#define STATUS_RESTART_BOOT_APPLICATION __constant_cpu_to_le32(0xC0000453)
+#define STATUS_INVALID_TASK_NAME __constant_cpu_to_le32(0xC0000500)
+#define STATUS_INVALID_TASK_INDEX __constant_cpu_to_le32(0xC0000501)
+#define STATUS_THREAD_ALREADY_IN_TASK __constant_cpu_to_le32(0xC0000502)
+#define STATUS_CALLBACK_BYPASS __constant_cpu_to_le32(0xC0000503)
+#define STATUS_PORT_CLOSED __constant_cpu_to_le32(0xC0000700)
+#define STATUS_MESSAGE_LOST __constant_cpu_to_le32(0xC0000701)
+#define STATUS_INVALID_MESSAGE __constant_cpu_to_le32(0xC0000702)
+#define STATUS_REQUEST_CANCELED __constant_cpu_to_le32(0xC0000703)
+#define STATUS_RECURSIVE_DISPATCH __constant_cpu_to_le32(0xC0000704)
+#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED __constant_cpu_to_le32(0xC0000705)
+#define STATUS_LPC_INVALID_CONNECTION_USAGE __constant_cpu_to_le32(0xC0000706)
+#define STATUS_LPC_REQUESTS_NOT_ALLOWED __constant_cpu_to_le32(0xC0000707)
+#define STATUS_RESOURCE_IN_USE __constant_cpu_to_le32(0xC0000708)
+#define STATUS_HARDWARE_MEMORY_ERROR __constant_cpu_to_le32(0xC0000709)
+#define STATUS_THREADPOOL_HANDLE_EXCEPTION __constant_cpu_to_le32(0xC000070A)
+#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070B)
+#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070C)
+#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070D)
+#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070E)
+#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION __constant_cpu_to_le32(0xC000070F)
+#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING __constant_cpu_to_le32(0xC0000710)
+#define STATUS_APC_RETURNED_WHILE_IMPERSONATING __constant_cpu_to_le32(0xC0000711)
+#define STATUS_PROCESS_IS_PROTECTED __constant_cpu_to_le32(0xC0000712)
+#define STATUS_MCA_EXCEPTION __constant_cpu_to_le32(0xC0000713)
+#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE __constant_cpu_to_le32(0xC0000714)
+#define STATUS_SYMLINK_CLASS_DISABLED __constant_cpu_to_le32(0xC0000715)
+#define STATUS_INVALID_IDN_NORMALIZATION __constant_cpu_to_le32(0xC0000716)
+#define STATUS_NO_UNICODE_TRANSLATION __constant_cpu_to_le32(0xC0000717)
+#define STATUS_ALREADY_REGISTERED __constant_cpu_to_le32(0xC0000718)
+#define STATUS_CONTEXT_MISMATCH __constant_cpu_to_le32(0xC0000719)
+#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST __constant_cpu_to_le32(0xC000071A)
+#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY __constant_cpu_to_le32(0xC000071B)
+#define STATUS_INVALID_THREAD __constant_cpu_to_le32(0xC000071C)
+#define STATUS_CALLBACK_RETURNED_TRANSACTION __constant_cpu_to_le32(0xC000071D)
+#define STATUS_CALLBACK_RETURNED_LDR_LOCK __constant_cpu_to_le32(0xC000071E)
+#define STATUS_CALLBACK_RETURNED_LANG __constant_cpu_to_le32(0xC000071F)
+#define STATUS_CALLBACK_RETURNED_PRI_BACK __constant_cpu_to_le32(0xC0000720)
+#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY __constant_cpu_to_le32(0xC0000721)
+#define STATUS_DISK_REPAIR_DISABLED __constant_cpu_to_le32(0xC0000800)
+#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS __constant_cpu_to_le32(0xC0000801)
+#define STATUS_DISK_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000802)
+#define STATUS_CONTENT_BLOCKED __constant_cpu_to_le32(0xC0000804)
+#define STATUS_BAD_CLUSTERS __constant_cpu_to_le32(0xC0000805)
+#define STATUS_VOLUME_DIRTY __constant_cpu_to_le32(0xC0000806)
+#define STATUS_FILE_CHECKED_OUT __constant_cpu_to_le32(0xC0000901)
+#define STATUS_CHECKOUT_REQUIRED __constant_cpu_to_le32(0xC0000902)
+#define STATUS_BAD_FILE_TYPE __constant_cpu_to_le32(0xC0000903)
+#define STATUS_FILE_TOO_LARGE __constant_cpu_to_le32(0xC0000904)
+#define STATUS_FORMS_AUTH_REQUIRED __constant_cpu_to_le32(0xC0000905)
+#define STATUS_VIRUS_INFECTED __constant_cpu_to_le32(0xC0000906)
+#define STATUS_VIRUS_DELETED __constant_cpu_to_le32(0xC0000907)
+#define STATUS_BAD_MCFG_TABLE __constant_cpu_to_le32(0xC0000908)
+#define STATUS_WOW_ASSERTION __constant_cpu_to_le32(0xC0009898)
+#define STATUS_INVALID_SIGNATURE __constant_cpu_to_le32(0xC000A000)
+#define STATUS_HMAC_NOT_SUPPORTED __constant_cpu_to_le32(0xC000A001)
+#define STATUS_IPSEC_QUEUE_OVERFLOW __constant_cpu_to_le32(0xC000A010)
+#define STATUS_ND_QUEUE_OVERFLOW __constant_cpu_to_le32(0xC000A011)
+#define STATUS_HOPLIMIT_EXCEEDED __constant_cpu_to_le32(0xC000A012)
+#define STATUS_PROTOCOL_NOT_SUPPORTED __constant_cpu_to_le32(0xC000A013)
+#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED __constant_cpu_to_le32(0xC000A080)
+#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR __constant_cpu_to_le32(0xC000A081)
+#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR __constant_cpu_to_le32(0xC000A082)
+#define STATUS_XML_PARSE_ERROR __constant_cpu_to_le32(0xC000A083)
+#define STATUS_XMLDSIG_ERROR __constant_cpu_to_le32(0xC000A084)
+#define STATUS_WRONG_COMPARTMENT __constant_cpu_to_le32(0xC000A085)
+#define STATUS_AUTHIP_FAILURE __constant_cpu_to_le32(0xC000A086)
+#define DBG_NO_STATE_CHANGE __constant_cpu_to_le32(0xC0010001)
+#define DBG_APP_NOT_IDLE __constant_cpu_to_le32(0xC0010002)
+#define RPC_NT_INVALID_STRING_BINDING __constant_cpu_to_le32(0xC0020001)
+#define RPC_NT_WRONG_KIND_OF_BINDING __constant_cpu_to_le32(0xC0020002)
+#define RPC_NT_INVALID_BINDING __constant_cpu_to_le32(0xC0020003)
+#define RPC_NT_PROTSEQ_NOT_SUPPORTED __constant_cpu_to_le32(0xC0020004)
+#define RPC_NT_INVALID_RPC_PROTSEQ __constant_cpu_to_le32(0xC0020005)
+#define RPC_NT_INVALID_STRING_UUID __constant_cpu_to_le32(0xC0020006)
+#define RPC_NT_INVALID_ENDPOINT_FORMAT __constant_cpu_to_le32(0xC0020007)
+#define RPC_NT_INVALID_NET_ADDR __constant_cpu_to_le32(0xC0020008)
+#define RPC_NT_NO_ENDPOINT_FOUND __constant_cpu_to_le32(0xC0020009)
+#define RPC_NT_INVALID_TIMEOUT __constant_cpu_to_le32(0xC002000A)
+#define RPC_NT_OBJECT_NOT_FOUND __constant_cpu_to_le32(0xC002000B)
+#define RPC_NT_ALREADY_REGISTERED __constant_cpu_to_le32(0xC002000C)
+#define RPC_NT_TYPE_ALREADY_REGISTERED __constant_cpu_to_le32(0xC002000D)
+#define RPC_NT_ALREADY_LISTENING __constant_cpu_to_le32(0xC002000E)
+#define RPC_NT_NO_PROTSEQS_REGISTERED __constant_cpu_to_le32(0xC002000F)
+#define RPC_NT_NOT_LISTENING __constant_cpu_to_le32(0xC0020010)
+#define RPC_NT_UNKNOWN_MGR_TYPE __constant_cpu_to_le32(0xC0020011)
+#define RPC_NT_UNKNOWN_IF __constant_cpu_to_le32(0xC0020012)
+#define RPC_NT_NO_BINDINGS __constant_cpu_to_le32(0xC0020013)
+#define RPC_NT_NO_PROTSEQS __constant_cpu_to_le32(0xC0020014)
+#define RPC_NT_CANT_CREATE_ENDPOINT __constant_cpu_to_le32(0xC0020015)
+#define RPC_NT_OUT_OF_RESOURCES __constant_cpu_to_le32(0xC0020016)
+#define RPC_NT_SERVER_UNAVAILABLE __constant_cpu_to_le32(0xC0020017)
+#define RPC_NT_SERVER_TOO_BUSY __constant_cpu_to_le32(0xC0020018)
+#define RPC_NT_INVALID_NETWORK_OPTIONS __constant_cpu_to_le32(0xC0020019)
+#define RPC_NT_NO_CALL_ACTIVE __constant_cpu_to_le32(0xC002001A)
+#define RPC_NT_CALL_FAILED __constant_cpu_to_le32(0xC002001B)
+#define RPC_NT_CALL_FAILED_DNE __constant_cpu_to_le32(0xC002001C)
+#define RPC_NT_PROTOCOL_ERROR __constant_cpu_to_le32(0xC002001D)
+#define RPC_NT_UNSUPPORTED_TRANS_SYN __constant_cpu_to_le32(0xC002001F)
+#define RPC_NT_UNSUPPORTED_TYPE __constant_cpu_to_le32(0xC0020021)
+#define RPC_NT_INVALID_TAG __constant_cpu_to_le32(0xC0020022)
+#define RPC_NT_INVALID_BOUND __constant_cpu_to_le32(0xC0020023)
+#define RPC_NT_NO_ENTRY_NAME __constant_cpu_to_le32(0xC0020024)
+#define RPC_NT_INVALID_NAME_SYNTAX __constant_cpu_to_le32(0xC0020025)
+#define RPC_NT_UNSUPPORTED_NAME_SYNTAX __constant_cpu_to_le32(0xC0020026)
+#define RPC_NT_UUID_NO_ADDRESS __constant_cpu_to_le32(0xC0020028)
+#define RPC_NT_DUPLICATE_ENDPOINT __constant_cpu_to_le32(0xC0020029)
+#define RPC_NT_UNKNOWN_AUTHN_TYPE __constant_cpu_to_le32(0xC002002A)
+#define RPC_NT_MAX_CALLS_TOO_SMALL __constant_cpu_to_le32(0xC002002B)
+#define RPC_NT_STRING_TOO_LONG __constant_cpu_to_le32(0xC002002C)
+#define RPC_NT_PROTSEQ_NOT_FOUND __constant_cpu_to_le32(0xC002002D)
+#define RPC_NT_PROCNUM_OUT_OF_RANGE __constant_cpu_to_le32(0xC002002E)
+#define RPC_NT_BINDING_HAS_NO_AUTH __constant_cpu_to_le32(0xC002002F)
+#define RPC_NT_UNKNOWN_AUTHN_SERVICE __constant_cpu_to_le32(0xC0020030)
+#define RPC_NT_UNKNOWN_AUTHN_LEVEL __constant_cpu_to_le32(0xC0020031)
+#define RPC_NT_INVALID_AUTH_IDENTITY __constant_cpu_to_le32(0xC0020032)
+#define RPC_NT_UNKNOWN_AUTHZ_SERVICE __constant_cpu_to_le32(0xC0020033)
+#define EPT_NT_INVALID_ENTRY __constant_cpu_to_le32(0xC0020034)
+#define EPT_NT_CANT_PERFORM_OP __constant_cpu_to_le32(0xC0020035)
+#define EPT_NT_NOT_REGISTERED __constant_cpu_to_le32(0xC0020036)
+#define RPC_NT_NOTHING_TO_EXPORT __constant_cpu_to_le32(0xC0020037)
+#define RPC_NT_INCOMPLETE_NAME __constant_cpu_to_le32(0xC0020038)
+#define RPC_NT_INVALID_VERS_OPTION __constant_cpu_to_le32(0xC0020039)
+#define RPC_NT_NO_MORE_MEMBERS __constant_cpu_to_le32(0xC002003A)
+#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED __constant_cpu_to_le32(0xC002003B)
+#define RPC_NT_INTERFACE_NOT_FOUND __constant_cpu_to_le32(0xC002003C)
+#define RPC_NT_ENTRY_ALREADY_EXISTS __constant_cpu_to_le32(0xC002003D)
+#define RPC_NT_ENTRY_NOT_FOUND __constant_cpu_to_le32(0xC002003E)
+#define RPC_NT_NAME_SERVICE_UNAVAILABLE __constant_cpu_to_le32(0xC002003F)
+#define RPC_NT_INVALID_NAF_ID __constant_cpu_to_le32(0xC0020040)
+#define RPC_NT_CANNOT_SUPPORT __constant_cpu_to_le32(0xC0020041)
+#define RPC_NT_NO_CONTEXT_AVAILABLE __constant_cpu_to_le32(0xC0020042)
+#define RPC_NT_INTERNAL_ERROR __constant_cpu_to_le32(0xC0020043)
+#define RPC_NT_ZERO_DIVIDE __constant_cpu_to_le32(0xC0020044)
+#define RPC_NT_ADDRESS_ERROR __constant_cpu_to_le32(0xC0020045)
+#define RPC_NT_FP_DIV_ZERO __constant_cpu_to_le32(0xC0020046)
+#define RPC_NT_FP_UNDERFLOW __constant_cpu_to_le32(0xC0020047)
+#define RPC_NT_FP_OVERFLOW __constant_cpu_to_le32(0xC0020048)
+#define RPC_NT_CALL_IN_PROGRESS __constant_cpu_to_le32(0xC0020049)
+#define RPC_NT_NO_MORE_BINDINGS __constant_cpu_to_le32(0xC002004A)
+#define RPC_NT_GROUP_MEMBER_NOT_FOUND __constant_cpu_to_le32(0xC002004B)
+#define EPT_NT_CANT_CREATE __constant_cpu_to_le32(0xC002004C)
+#define RPC_NT_INVALID_OBJECT __constant_cpu_to_le32(0xC002004D)
+#define RPC_NT_NO_INTERFACES __constant_cpu_to_le32(0xC002004F)
+#define RPC_NT_CALL_CANCELLED __constant_cpu_to_le32(0xC0020050)
+#define RPC_NT_BINDING_INCOMPLETE __constant_cpu_to_le32(0xC0020051)
+#define RPC_NT_COMM_FAILURE __constant_cpu_to_le32(0xC0020052)
+#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL __constant_cpu_to_le32(0xC0020053)
+#define RPC_NT_NO_PRINC_NAME __constant_cpu_to_le32(0xC0020054)
+#define RPC_NT_NOT_RPC_ERROR __constant_cpu_to_le32(0xC0020055)
+#define RPC_NT_SEC_PKG_ERROR __constant_cpu_to_le32(0xC0020057)
+#define RPC_NT_NOT_CANCELLED __constant_cpu_to_le32(0xC0020058)
+#define RPC_NT_INVALID_ASYNC_HANDLE __constant_cpu_to_le32(0xC0020062)
+#define RPC_NT_INVALID_ASYNC_CALL __constant_cpu_to_le32(0xC0020063)
+#define RPC_NT_PROXY_ACCESS_DENIED __constant_cpu_to_le32(0xC0020064)
+#define RPC_NT_NO_MORE_ENTRIES __constant_cpu_to_le32(0xC0030001)
+#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL __constant_cpu_to_le32(0xC0030002)
+#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE __constant_cpu_to_le32(0xC0030003)
+#define RPC_NT_SS_IN_NULL_CONTEXT __constant_cpu_to_le32(0xC0030004)
+#define RPC_NT_SS_CONTEXT_MISMATCH __constant_cpu_to_le32(0xC0030005)
+#define RPC_NT_SS_CONTEXT_DAMAGED __constant_cpu_to_le32(0xC0030006)
+#define RPC_NT_SS_HANDLES_MISMATCH __constant_cpu_to_le32(0xC0030007)
+#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE __constant_cpu_to_le32(0xC0030008)
+#define RPC_NT_NULL_REF_POINTER __constant_cpu_to_le32(0xC0030009)
+#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE __constant_cpu_to_le32(0xC003000A)
+#define RPC_NT_BYTE_COUNT_TOO_SMALL __constant_cpu_to_le32(0xC003000B)
+#define RPC_NT_BAD_STUB_DATA __constant_cpu_to_le32(0xC003000C)
+#define RPC_NT_INVALID_ES_ACTION __constant_cpu_to_le32(0xC0030059)
+#define RPC_NT_WRONG_ES_VERSION __constant_cpu_to_le32(0xC003005A)
+#define RPC_NT_WRONG_STUB_VERSION __constant_cpu_to_le32(0xC003005B)
+#define RPC_NT_INVALID_PIPE_OBJECT __constant_cpu_to_le32(0xC003005C)
+#define RPC_NT_INVALID_PIPE_OPERATION __constant_cpu_to_le32(0xC003005D)
+#define RPC_NT_WRONG_PIPE_VERSION __constant_cpu_to_le32(0xC003005E)
+#define RPC_NT_PIPE_CLOSED __constant_cpu_to_le32(0xC003005F)
+#define RPC_NT_PIPE_DISCIPLINE_ERROR __constant_cpu_to_le32(0xC0030060)
+#define RPC_NT_PIPE_EMPTY __constant_cpu_to_le32(0xC0030061)
+#define STATUS_PNP_BAD_MPS_TABLE __constant_cpu_to_le32(0xC0040035)
+#define STATUS_PNP_TRANSLATION_FAILED __constant_cpu_to_le32(0xC0040036)
+#define STATUS_PNP_IRQ_TRANSLATION_FAILED __constant_cpu_to_le32(0xC0040037)
+#define STATUS_PNP_INVALID_ID __constant_cpu_to_le32(0xC0040038)
+#define STATUS_IO_REISSUE_AS_CACHED __constant_cpu_to_le32(0xC0040039)
+#define STATUS_CTX_WINSTATION_NAME_INVALID __constant_cpu_to_le32(0xC00A0001)
+#define STATUS_CTX_INVALID_PD __constant_cpu_to_le32(0xC00A0002)
+#define STATUS_CTX_PD_NOT_FOUND __constant_cpu_to_le32(0xC00A0003)
+#define STATUS_CTX_CLOSE_PENDING __constant_cpu_to_le32(0xC00A0006)
+#define STATUS_CTX_NO_OUTBUF __constant_cpu_to_le32(0xC00A0007)
+#define STATUS_CTX_MODEM_INF_NOT_FOUND __constant_cpu_to_le32(0xC00A0008)
+#define STATUS_CTX_INVALID_MODEMNAME __constant_cpu_to_le32(0xC00A0009)
+#define STATUS_CTX_RESPONSE_ERROR __constant_cpu_to_le32(0xC00A000A)
+#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT __constant_cpu_to_le32(0xC00A000B)
+#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER __constant_cpu_to_le32(0xC00A000C)
+#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE __constant_cpu_to_le32(0xC00A000D)
+#define STATUS_CTX_MODEM_RESPONSE_BUSY __constant_cpu_to_le32(0xC00A000E)
+#define STATUS_CTX_MODEM_RESPONSE_VOICE __constant_cpu_to_le32(0xC00A000F)
+#define STATUS_CTX_TD_ERROR __constant_cpu_to_le32(0xC00A0010)
+#define STATUS_CTX_LICENSE_CLIENT_INVALID __constant_cpu_to_le32(0xC00A0012)
+#define STATUS_CTX_LICENSE_NOT_AVAILABLE __constant_cpu_to_le32(0xC00A0013)
+#define STATUS_CTX_LICENSE_EXPIRED __constant_cpu_to_le32(0xC00A0014)
+#define STATUS_CTX_WINSTATION_NOT_FOUND __constant_cpu_to_le32(0xC00A0015)
+#define STATUS_CTX_WINSTATION_NAME_COLLISION __constant_cpu_to_le32(0xC00A0016)
+#define STATUS_CTX_WINSTATION_BUSY __constant_cpu_to_le32(0xC00A0017)
+#define STATUS_CTX_BAD_VIDEO_MODE __constant_cpu_to_le32(0xC00A0018)
+#define STATUS_CTX_GRAPHICS_INVALID __constant_cpu_to_le32(0xC00A0022)
+#define STATUS_CTX_NOT_CONSOLE __constant_cpu_to_le32(0xC00A0024)
+#define STATUS_CTX_CLIENT_QUERY_TIMEOUT __constant_cpu_to_le32(0xC00A0026)
+#define STATUS_CTX_CONSOLE_DISCONNECT __constant_cpu_to_le32(0xC00A0027)
+#define STATUS_CTX_CONSOLE_CONNECT __constant_cpu_to_le32(0xC00A0028)
+#define STATUS_CTX_SHADOW_DENIED __constant_cpu_to_le32(0xC00A002A)
+#define STATUS_CTX_WINSTATION_ACCESS_DENIED __constant_cpu_to_le32(0xC00A002B)
+#define STATUS_CTX_INVALID_WD __constant_cpu_to_le32(0xC00A002E)
+#define STATUS_CTX_WD_NOT_FOUND __constant_cpu_to_le32(0xC00A002F)
+#define STATUS_CTX_SHADOW_INVALID __constant_cpu_to_le32(0xC00A0030)
+#define STATUS_CTX_SHADOW_DISABLED __constant_cpu_to_le32(0xC00A0031)
+#define STATUS_RDP_PROTOCOL_ERROR __constant_cpu_to_le32(0xC00A0032)
+#define STATUS_CTX_CLIENT_LICENSE_NOT_SET __constant_cpu_to_le32(0xC00A0033)
+#define STATUS_CTX_CLIENT_LICENSE_IN_USE __constant_cpu_to_le32(0xC00A0034)
+#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE __constant_cpu_to_le32(0xC00A0035)
+#define STATUS_CTX_SHADOW_NOT_RUNNING __constant_cpu_to_le32(0xC00A0036)
+#define STATUS_CTX_LOGON_DISABLED __constant_cpu_to_le32(0xC00A0037)
+#define STATUS_CTX_SECURITY_LAYER_ERROR __constant_cpu_to_le32(0xC00A0038)
+#define STATUS_TS_INCOMPATIBLE_SESSIONS __constant_cpu_to_le32(0xC00A0039)
+#define STATUS_MUI_FILE_NOT_FOUND __constant_cpu_to_le32(0xC00B0001)
+#define STATUS_MUI_INVALID_FILE __constant_cpu_to_le32(0xC00B0002)
+#define STATUS_MUI_INVALID_RC_CONFIG __constant_cpu_to_le32(0xC00B0003)
+#define STATUS_MUI_INVALID_LOCALE_NAME __constant_cpu_to_le32(0xC00B0004)
+#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME __constant_cpu_to_le32(0xC00B0005)
+#define STATUS_MUI_FILE_NOT_LOADED __constant_cpu_to_le32(0xC00B0006)
+#define STATUS_RESOURCE_ENUM_USER_STOP __constant_cpu_to_le32(0xC00B0007)
+#define STATUS_CLUSTER_INVALID_NODE __constant_cpu_to_le32(0xC0130001)
+#define STATUS_CLUSTER_NODE_EXISTS __constant_cpu_to_le32(0xC0130002)
+#define STATUS_CLUSTER_JOIN_IN_PROGRESS __constant_cpu_to_le32(0xC0130003)
+#define STATUS_CLUSTER_NODE_NOT_FOUND __constant_cpu_to_le32(0xC0130004)
+#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND __constant_cpu_to_le32(0xC0130005)
+#define STATUS_CLUSTER_NETWORK_EXISTS __constant_cpu_to_le32(0xC0130006)
+#define STATUS_CLUSTER_NETWORK_NOT_FOUND __constant_cpu_to_le32(0xC0130007)
+#define STATUS_CLUSTER_NETINTERFACE_EXISTS __constant_cpu_to_le32(0xC0130008)
+#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND __constant_cpu_to_le32(0xC0130009)
+#define STATUS_CLUSTER_INVALID_REQUEST __constant_cpu_to_le32(0xC013000A)
+#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER __constant_cpu_to_le32(0xC013000B)
+#define STATUS_CLUSTER_NODE_DOWN __constant_cpu_to_le32(0xC013000C)
+#define STATUS_CLUSTER_NODE_UNREACHABLE __constant_cpu_to_le32(0xC013000D)
+#define STATUS_CLUSTER_NODE_NOT_MEMBER __constant_cpu_to_le32(0xC013000E)
+#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS __constant_cpu_to_le32(0xC013000F)
+#define STATUS_CLUSTER_INVALID_NETWORK __constant_cpu_to_le32(0xC0130010)
+#define STATUS_CLUSTER_NO_NET_ADAPTERS __constant_cpu_to_le32(0xC0130011)
+#define STATUS_CLUSTER_NODE_UP __constant_cpu_to_le32(0xC0130012)
+#define STATUS_CLUSTER_NODE_PAUSED __constant_cpu_to_le32(0xC0130013)
+#define STATUS_CLUSTER_NODE_NOT_PAUSED __constant_cpu_to_le32(0xC0130014)
+#define STATUS_CLUSTER_NO_SECURITY_CONTEXT __constant_cpu_to_le32(0xC0130015)
+#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL __constant_cpu_to_le32(0xC0130016)
+#define STATUS_CLUSTER_POISONED __constant_cpu_to_le32(0xC0130017)
+#define STATUS_ACPI_INVALID_OPCODE __constant_cpu_to_le32(0xC0140001)
+#define STATUS_ACPI_STACK_OVERFLOW __constant_cpu_to_le32(0xC0140002)
+#define STATUS_ACPI_ASSERT_FAILED __constant_cpu_to_le32(0xC0140003)
+#define STATUS_ACPI_INVALID_INDEX __constant_cpu_to_le32(0xC0140004)
+#define STATUS_ACPI_INVALID_ARGUMENT __constant_cpu_to_le32(0xC0140005)
+#define STATUS_ACPI_FATAL __constant_cpu_to_le32(0xC0140006)
+#define STATUS_ACPI_INVALID_SUPERNAME __constant_cpu_to_le32(0xC0140007)
+#define STATUS_ACPI_INVALID_ARGTYPE __constant_cpu_to_le32(0xC0140008)
+#define STATUS_ACPI_INVALID_OBJTYPE __constant_cpu_to_le32(0xC0140009)
+#define STATUS_ACPI_INVALID_TARGETTYPE __constant_cpu_to_le32(0xC014000A)
+#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT __constant_cpu_to_le32(0xC014000B)
+#define STATUS_ACPI_ADDRESS_NOT_MAPPED __constant_cpu_to_le32(0xC014000C)
+#define STATUS_ACPI_INVALID_EVENTTYPE __constant_cpu_to_le32(0xC014000D)
+#define STATUS_ACPI_HANDLER_COLLISION __constant_cpu_to_le32(0xC014000E)
+#define STATUS_ACPI_INVALID_DATA __constant_cpu_to_le32(0xC014000F)
+#define STATUS_ACPI_INVALID_REGION __constant_cpu_to_le32(0xC0140010)
+#define STATUS_ACPI_INVALID_ACCESS_SIZE __constant_cpu_to_le32(0xC0140011)
+#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK __constant_cpu_to_le32(0xC0140012)
+#define STATUS_ACPI_ALREADY_INITIALIZED __constant_cpu_to_le32(0xC0140013)
+#define STATUS_ACPI_NOT_INITIALIZED __constant_cpu_to_le32(0xC0140014)
+#define STATUS_ACPI_INVALID_MUTEX_LEVEL __constant_cpu_to_le32(0xC0140015)
+#define STATUS_ACPI_MUTEX_NOT_OWNED __constant_cpu_to_le32(0xC0140016)
+#define STATUS_ACPI_MUTEX_NOT_OWNER __constant_cpu_to_le32(0xC0140017)
+#define STATUS_ACPI_RS_ACCESS __constant_cpu_to_le32(0xC0140018)
+#define STATUS_ACPI_INVALID_TABLE __constant_cpu_to_le32(0xC0140019)
+#define STATUS_ACPI_REG_HANDLER_FAILED __constant_cpu_to_le32(0xC0140020)
+#define STATUS_ACPI_POWER_REQUEST_FAILED __constant_cpu_to_le32(0xC0140021)
+#define STATUS_SXS_SECTION_NOT_FOUND __constant_cpu_to_le32(0xC0150001)
+#define STATUS_SXS_CANT_GEN_ACTCTX __constant_cpu_to_le32(0xC0150002)
+#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT __constant_cpu_to_le32(0xC0150003)
+#define STATUS_SXS_ASSEMBLY_NOT_FOUND __constant_cpu_to_le32(0xC0150004)
+#define STATUS_SXS_MANIFEST_FORMAT_ERROR __constant_cpu_to_le32(0xC0150005)
+#define STATUS_SXS_MANIFEST_PARSE_ERROR __constant_cpu_to_le32(0xC0150006)
+#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED __constant_cpu_to_le32(0xC0150007)
+#define STATUS_SXS_KEY_NOT_FOUND __constant_cpu_to_le32(0xC0150008)
+#define STATUS_SXS_VERSION_CONFLICT __constant_cpu_to_le32(0xC0150009)
+#define STATUS_SXS_WRONG_SECTION_TYPE __constant_cpu_to_le32(0xC015000A)
+#define STATUS_SXS_THREAD_QUERIES_DISABLED __constant_cpu_to_le32(0xC015000B)
+#define STATUS_SXS_ASSEMBLY_MISSING __constant_cpu_to_le32(0xC015000C)
+#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET __constant_cpu_to_le32(0xC015000E)
+#define STATUS_SXS_EARLY_DEACTIVATION __constant_cpu_to_le32(0xC015000F)
+#define STATUS_SXS_INVALID_DEACTIVATION __constant_cpu_to_le32(0xC0150010)
+#define STATUS_SXS_MULTIPLE_DEACTIVATION __constant_cpu_to_le32(0xC0150011)
+#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY __constant_cpu_to_le32(0xC0150012)
+#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED __constant_cpu_to_le32(0xC0150013)
+#define STATUS_SXS_CORRUPT_ACTIVATION_STACK __constant_cpu_to_le32(0xC0150014)
+#define STATUS_SXS_CORRUPTION __constant_cpu_to_le32(0xC0150015)
+#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE __constant_cpu_to_le32(0xC0150016)
+#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME __constant_cpu_to_le32(0xC0150017)
+#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE __constant_cpu_to_le32(0xC0150018)
+#define STATUS_SXS_IDENTITY_PARSE_ERROR __constant_cpu_to_le32(0xC0150019)
+#define STATUS_SXS_COMPONENT_STORE_CORRUPT __constant_cpu_to_le32(0xC015001A)
+#define STATUS_SXS_FILE_HASH_MISMATCH __constant_cpu_to_le32(0xC015001B)
+#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT __constant_cpu_to_le32(0xC015001C)
+#define STATUS_SXS_IDENTITIES_DIFFERENT __constant_cpu_to_le32(0xC015001D)
+#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT __constant_cpu_to_le32(0xC015001E)
+#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY __constant_cpu_to_le32(0xC015001F)
+#define STATUS_ADVANCED_INSTALLER_FAILED __constant_cpu_to_le32(0xC0150020)
+#define STATUS_XML_ENCODING_MISMATCH __constant_cpu_to_le32(0xC0150021)
+#define STATUS_SXS_MANIFEST_TOO_BIG __constant_cpu_to_le32(0xC0150022)
+#define STATUS_SXS_SETTING_NOT_REGISTERED __constant_cpu_to_le32(0xC0150023)
+#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE __constant_cpu_to_le32(0xC0150024)
+#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED __constant_cpu_to_le32(0xC0150025)
+#define STATUS_GENERIC_COMMAND_FAILED __constant_cpu_to_le32(0xC0150026)
+#define STATUS_SXS_FILE_HASH_MISSING __constant_cpu_to_le32(0xC0150027)
+#define STATUS_TRANSACTIONAL_CONFLICT __constant_cpu_to_le32(0xC0190001)
+#define STATUS_INVALID_TRANSACTION __constant_cpu_to_le32(0xC0190002)
+#define STATUS_TRANSACTION_NOT_ACTIVE __constant_cpu_to_le32(0xC0190003)
+#define STATUS_TM_INITIALIZATION_FAILED __constant_cpu_to_le32(0xC0190004)
+#define STATUS_RM_NOT_ACTIVE __constant_cpu_to_le32(0xC0190005)
+#define STATUS_RM_METADATA_CORRUPT __constant_cpu_to_le32(0xC0190006)
+#define STATUS_TRANSACTION_NOT_JOINED __constant_cpu_to_le32(0xC0190007)
+#define STATUS_DIRECTORY_NOT_RM __constant_cpu_to_le32(0xC0190008)
+#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE __constant_cpu_to_le32(0xC019000A)
+#define STATUS_LOG_RESIZE_INVALID_SIZE __constant_cpu_to_le32(0xC019000B)
+#define STATUS_REMOTE_FILE_VERSION_MISMATCH __constant_cpu_to_le32(0xC019000C)
+#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS __constant_cpu_to_le32(0xC019000F)
+#define STATUS_TRANSACTION_PROPAGATION_FAILED __constant_cpu_to_le32(0xC0190010)
+#define STATUS_CRM_PROTOCOL_NOT_FOUND __constant_cpu_to_le32(0xC0190011)
+#define STATUS_TRANSACTION_SUPERIOR_EXISTS __constant_cpu_to_le32(0xC0190012)
+#define STATUS_TRANSACTION_REQUEST_NOT_VALID __constant_cpu_to_le32(0xC0190013)
+#define STATUS_TRANSACTION_NOT_REQUESTED __constant_cpu_to_le32(0xC0190014)
+#define STATUS_TRANSACTION_ALREADY_ABORTED __constant_cpu_to_le32(0xC0190015)
+#define STATUS_TRANSACTION_ALREADY_COMMITTED __constant_cpu_to_le32(0xC0190016)
+#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER __constant_cpu_to_le32(0xC0190017)
+#define STATUS_CURRENT_TRANSACTION_NOT_VALID __constant_cpu_to_le32(0xC0190018)
+#define STATUS_LOG_GROWTH_FAILED __constant_cpu_to_le32(0xC0190019)
+#define STATUS_OBJECT_NO_LONGER_EXISTS __constant_cpu_to_le32(0xC0190021)
+#define STATUS_STREAM_MINIVERSION_NOT_FOUND __constant_cpu_to_le32(0xC0190022)
+#define STATUS_STREAM_MINIVERSION_NOT_VALID __constant_cpu_to_le32(0xC0190023)
+#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION __constant_cpu_to_le32(0xC0190024)
+#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT __constant_cpu_to_le32(0xC0190025)
+#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS __constant_cpu_to_le32(0xC0190026)
+#define STATUS_HANDLE_NO_LONGER_VALID __constant_cpu_to_le32(0xC0190028)
+#define STATUS_LOG_CORRUPTION_DETECTED __constant_cpu_to_le32(0xC0190030)
+#define STATUS_RM_DISCONNECTED __constant_cpu_to_le32(0xC0190032)
+#define STATUS_ENLISTMENT_NOT_SUPERIOR __constant_cpu_to_le32(0xC0190033)
+#define STATUS_FILE_IDENTITY_NOT_PERSISTENT __constant_cpu_to_le32(0xC0190036)
+#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY __constant_cpu_to_le32(0xC0190037)
+#define STATUS_CANT_CROSS_RM_BOUNDARY __constant_cpu_to_le32(0xC0190038)
+#define STATUS_TXF_DIR_NOT_EMPTY __constant_cpu_to_le32(0xC0190039)
+#define STATUS_INDOUBT_TRANSACTIONS_EXIST __constant_cpu_to_le32(0xC019003A)
+#define STATUS_TM_VOLATILE __constant_cpu_to_le32(0xC019003B)
+#define STATUS_ROLLBACK_TIMER_EXPIRED __constant_cpu_to_le32(0xC019003C)
+#define STATUS_TXF_ATTRIBUTE_CORRUPT __constant_cpu_to_le32(0xC019003D)
+#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION __constant_cpu_to_le32(0xC019003E)
+#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED __constant_cpu_to_le32(0xC019003F)
+#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE __constant_cpu_to_le32(0xC0190040)
+#define STATUS_TRANSACTION_REQUIRED_PROMOTION __constant_cpu_to_le32(0xC0190043)
+#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION __constant_cpu_to_le32(0xC0190044)
+#define STATUS_TRANSACTIONS_NOT_FROZEN __constant_cpu_to_le32(0xC0190045)
+#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS __constant_cpu_to_le32(0xC0190046)
+#define STATUS_NOT_SNAPSHOT_VOLUME __constant_cpu_to_le32(0xC0190047)
+#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES __constant_cpu_to_le32(0xC0190048)
+#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION __constant_cpu_to_le32(0xC0190049)
+#define STATUS_TM_IDENTITY_MISMATCH __constant_cpu_to_le32(0xC019004A)
+#define STATUS_FLOATED_SECTION __constant_cpu_to_le32(0xC019004B)
+#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK __constant_cpu_to_le32(0xC019004C)
+#define STATUS_CANNOT_ABORT_TRANSACTIONS __constant_cpu_to_le32(0xC019004D)
+#define STATUS_TRANSACTION_NOT_FOUND __constant_cpu_to_le32(0xC019004E)
+#define STATUS_RESOURCEMANAGER_NOT_FOUND __constant_cpu_to_le32(0xC019004F)
+#define STATUS_ENLISTMENT_NOT_FOUND __constant_cpu_to_le32(0xC0190050)
+#define STATUS_TRANSACTIONMANAGER_NOT_FOUND __constant_cpu_to_le32(0xC0190051)
+#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE __constant_cpu_to_le32(0xC0190052)
+#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION __constant_cpu_to_le32(0xC0190053)
+#define STATUS_TRANSACTION_NOT_ROOT __constant_cpu_to_le32(0xC0190054)
+#define STATUS_TRANSACTION_OBJECT_EXPIRED __constant_cpu_to_le32(0xC0190055)
+#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION __constant_cpu_to_le32(0xC0190056)
+#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED __constant_cpu_to_le32(0xC0190057)
+#define STATUS_TRANSACTION_RECORD_TOO_LONG __constant_cpu_to_le32(0xC0190058)
+#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION __constant_cpu_to_le32(0xC0190059)
+#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION __constant_cpu_to_le32(0xC019005A)
+#define STATUS_TRANSACTION_INTEGRITY_VIOLATED __constant_cpu_to_le32(0xC019005B)
+#define STATUS_LOG_SECTOR_INVALID __constant_cpu_to_le32(0xC01A0001)
+#define STATUS_LOG_SECTOR_PARITY_INVALID __constant_cpu_to_le32(0xC01A0002)
+#define STATUS_LOG_SECTOR_REMAPPED __constant_cpu_to_le32(0xC01A0003)
+#define STATUS_LOG_BLOCK_INCOMPLETE __constant_cpu_to_le32(0xC01A0004)
+#define STATUS_LOG_INVALID_RANGE __constant_cpu_to_le32(0xC01A0005)
+#define STATUS_LOG_BLOCKS_EXHAUSTED __constant_cpu_to_le32(0xC01A0006)
+#define STATUS_LOG_READ_CONTEXT_INVALID __constant_cpu_to_le32(0xC01A0007)
+#define STATUS_LOG_RESTART_INVALID __constant_cpu_to_le32(0xC01A0008)
+#define STATUS_LOG_BLOCK_VERSION __constant_cpu_to_le32(0xC01A0009)
+#define STATUS_LOG_BLOCK_INVALID __constant_cpu_to_le32(0xC01A000A)
+#define STATUS_LOG_READ_MODE_INVALID __constant_cpu_to_le32(0xC01A000B)
+#define STATUS_LOG_METADATA_CORRUPT __constant_cpu_to_le32(0xC01A000D)
+#define STATUS_LOG_METADATA_INVALID __constant_cpu_to_le32(0xC01A000E)
+#define STATUS_LOG_METADATA_INCONSISTENT __constant_cpu_to_le32(0xC01A000F)
+#define STATUS_LOG_RESERVATION_INVALID __constant_cpu_to_le32(0xC01A0010)
+#define STATUS_LOG_CANT_DELETE __constant_cpu_to_le32(0xC01A0011)
+#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC01A0012)
+#define STATUS_LOG_START_OF_LOG __constant_cpu_to_le32(0xC01A0013)
+#define STATUS_LOG_POLICY_ALREADY_INSTALLED __constant_cpu_to_le32(0xC01A0014)
+#define STATUS_LOG_POLICY_NOT_INSTALLED __constant_cpu_to_le32(0xC01A0015)
+#define STATUS_LOG_POLICY_INVALID __constant_cpu_to_le32(0xC01A0016)
+#define STATUS_LOG_POLICY_CONFLICT __constant_cpu_to_le32(0xC01A0017)
+#define STATUS_LOG_PINNED_ARCHIVE_TAIL __constant_cpu_to_le32(0xC01A0018)
+#define STATUS_LOG_RECORD_NONEXISTENT __constant_cpu_to_le32(0xC01A0019)
+#define STATUS_LOG_RECORDS_RESERVED_INVALID __constant_cpu_to_le32(0xC01A001A)
+#define STATUS_LOG_SPACE_RESERVED_INVALID __constant_cpu_to_le32(0xC01A001B)
+#define STATUS_LOG_TAIL_INVALID __constant_cpu_to_le32(0xC01A001C)
+#define STATUS_LOG_FULL __constant_cpu_to_le32(0xC01A001D)
+#define STATUS_LOG_MULTIPLEXED __constant_cpu_to_le32(0xC01A001E)
+#define STATUS_LOG_DEDICATED __constant_cpu_to_le32(0xC01A001F)
+#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS __constant_cpu_to_le32(0xC01A0020)
+#define STATUS_LOG_ARCHIVE_IN_PROGRESS __constant_cpu_to_le32(0xC01A0021)
+#define STATUS_LOG_EPHEMERAL __constant_cpu_to_le32(0xC01A0022)
+#define STATUS_LOG_NOT_ENOUGH_CONTAINERS __constant_cpu_to_le32(0xC01A0023)
+#define STATUS_LOG_CLIENT_ALREADY_REGISTERED __constant_cpu_to_le32(0xC01A0024)
+#define STATUS_LOG_CLIENT_NOT_REGISTERED __constant_cpu_to_le32(0xC01A0025)
+#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS __constant_cpu_to_le32(0xC01A0026)
+#define STATUS_LOG_CONTAINER_READ_FAILED __constant_cpu_to_le32(0xC01A0027)
+#define STATUS_LOG_CONTAINER_WRITE_FAILED __constant_cpu_to_le32(0xC01A0028)
+#define STATUS_LOG_CONTAINER_OPEN_FAILED __constant_cpu_to_le32(0xC01A0029)
+#define STATUS_LOG_CONTAINER_STATE_INVALID __constant_cpu_to_le32(0xC01A002A)
+#define STATUS_LOG_STATE_INVALID __constant_cpu_to_le32(0xC01A002B)
+#define STATUS_LOG_PINNED __constant_cpu_to_le32(0xC01A002C)
+#define STATUS_LOG_METADATA_FLUSH_FAILED __constant_cpu_to_le32(0xC01A002D)
+#define STATUS_LOG_INCONSISTENT_SECURITY __constant_cpu_to_le32(0xC01A002E)
+#define STATUS_LOG_APPENDED_FLUSH_FAILED __constant_cpu_to_le32(0xC01A002F)
+#define STATUS_LOG_PINNED_RESERVATION __constant_cpu_to_le32(0xC01A0030)
+#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD __constant_cpu_to_le32(0xC01B00EA)
+#define STATUS_FLT_NO_HANDLER_DEFINED __constant_cpu_to_le32(0xC01C0001)
+#define STATUS_FLT_CONTEXT_ALREADY_DEFINED __constant_cpu_to_le32(0xC01C0002)
+#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST __constant_cpu_to_le32(0xC01C0003)
+#define STATUS_FLT_DISALLOW_FAST_IO __constant_cpu_to_le32(0xC01C0004)
+#define STATUS_FLT_INVALID_NAME_REQUEST __constant_cpu_to_le32(0xC01C0005)
+#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION __constant_cpu_to_le32(0xC01C0006)
+#define STATUS_FLT_NOT_INITIALIZED __constant_cpu_to_le32(0xC01C0007)
+#define STATUS_FLT_FILTER_NOT_READY __constant_cpu_to_le32(0xC01C0008)
+#define STATUS_FLT_POST_OPERATION_CLEANUP __constant_cpu_to_le32(0xC01C0009)
+#define STATUS_FLT_INTERNAL_ERROR __constant_cpu_to_le32(0xC01C000A)
+#define STATUS_FLT_DELETING_OBJECT __constant_cpu_to_le32(0xC01C000B)
+#define STATUS_FLT_MUST_BE_NONPAGED_POOL __constant_cpu_to_le32(0xC01C000C)
+#define STATUS_FLT_DUPLICATE_ENTRY __constant_cpu_to_le32(0xC01C000D)
+#define STATUS_FLT_CBDQ_DISABLED __constant_cpu_to_le32(0xC01C000E)
+#define STATUS_FLT_DO_NOT_ATTACH __constant_cpu_to_le32(0xC01C000F)
+#define STATUS_FLT_DO_NOT_DETACH __constant_cpu_to_le32(0xC01C0010)
+#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION __constant_cpu_to_le32(0xC01C0011)
+#define STATUS_FLT_INSTANCE_NAME_COLLISION __constant_cpu_to_le32(0xC01C0012)
+#define STATUS_FLT_FILTER_NOT_FOUND __constant_cpu_to_le32(0xC01C0013)
+#define STATUS_FLT_VOLUME_NOT_FOUND __constant_cpu_to_le32(0xC01C0014)
+#define STATUS_FLT_INSTANCE_NOT_FOUND __constant_cpu_to_le32(0xC01C0015)
+#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND __constant_cpu_to_le32(0xC01C0016)
+#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION __constant_cpu_to_le32(0xC01C0017)
+#define STATUS_FLT_NAME_CACHE_MISS __constant_cpu_to_le32(0xC01C0018)
+#define STATUS_FLT_NO_DEVICE_OBJECT __constant_cpu_to_le32(0xC01C0019)
+#define STATUS_FLT_VOLUME_ALREADY_MOUNTED __constant_cpu_to_le32(0xC01C001A)
+#define STATUS_FLT_ALREADY_ENLISTED __constant_cpu_to_le32(0xC01C001B)
+#define STATUS_FLT_CONTEXT_ALREADY_LINKED __constant_cpu_to_le32(0xC01C001C)
+#define STATUS_FLT_NO_WAITER_FOR_REPLY __constant_cpu_to_le32(0xC01C0020)
+#define STATUS_MONITOR_NO_DESCRIPTOR __constant_cpu_to_le32(0xC01D0001)
+#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT __constant_cpu_to_le32(0xC01D0002)
+#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM __constant_cpu_to_le32(0xC01D0003)
+#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK __constant_cpu_to_le32(0xC01D0004)
+#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED __constant_cpu_to_le32(0xC01D0005)
+#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK __constant_cpu_to_le32(0xC01D0006)
+#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK __constant_cpu_to_le32(0xC01D0007)
+#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA __constant_cpu_to_le32(0xC01D0008)
+#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK __constant_cpu_to_le32(0xC01D0009)
+#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER __constant_cpu_to_le32(0xC01E0000)
+#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER __constant_cpu_to_le32(0xC01E0001)
+#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER __constant_cpu_to_le32(0xC01E0002)
+#define STATUS_GRAPHICS_ADAPTER_WAS_RESET __constant_cpu_to_le32(0xC01E0003)
+#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL __constant_cpu_to_le32(0xC01E0004)
+#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED __constant_cpu_to_le32(0xC01E0005)
+#define STATUS_GRAPHICS_PRESENT_OCCLUDED __constant_cpu_to_le32(0xC01E0006)
+#define STATUS_GRAPHICS_PRESENT_DENIED __constant_cpu_to_le32(0xC01E0007)
+#define STATUS_GRAPHICS_CANNOTCOLORCONVERT __constant_cpu_to_le32(0xC01E0008)
+#define STATUS_GRAPHICS_NO_VIDEO_MEMORY __constant_cpu_to_le32(0xC01E0100)
+#define STATUS_GRAPHICS_CANT_LOCK_MEMORY __constant_cpu_to_le32(0xC01E0101)
+#define STATUS_GRAPHICS_ALLOCATION_BUSY __constant_cpu_to_le32(0xC01E0102)
+#define STATUS_GRAPHICS_TOO_MANY_REFERENCES __constant_cpu_to_le32(0xC01E0103)
+#define STATUS_GRAPHICS_TRY_AGAIN_LATER __constant_cpu_to_le32(0xC01E0104)
+#define STATUS_GRAPHICS_TRY_AGAIN_NOW __constant_cpu_to_le32(0xC01E0105)
+#define STATUS_GRAPHICS_ALLOCATION_INVALID __constant_cpu_to_le32(0xC01E0106)
+#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE __constant_cpu_to_le32(0xC01E0107)
+#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED __constant_cpu_to_le32(0xC01E0108)
+#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION __constant_cpu_to_le32(0xC01E0109)
+#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE __constant_cpu_to_le32(0xC01E0110)
+#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION __constant_cpu_to_le32(0xC01E0111)
+#define STATUS_GRAPHICS_ALLOCATION_CLOSED __constant_cpu_to_le32(0xC01E0112)
+#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE __constant_cpu_to_le32(0xC01E0113)
+#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE __constant_cpu_to_le32(0xC01E0114)
+#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE __constant_cpu_to_le32(0xC01E0115)
+#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST __constant_cpu_to_le32(0xC01E0116)
+#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE __constant_cpu_to_le32(0xC01E0200)
+#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY __constant_cpu_to_le32(0xC01E0300)
+#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0301)
+#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0302)
+#define STATUS_GRAPHICS_INVALID_VIDPN __constant_cpu_to_le32(0xC01E0303)
+#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE __constant_cpu_to_le32(0xC01E0304)
+#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET __constant_cpu_to_le32(0xC01E0305)
+#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0306)
+#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET __constant_cpu_to_le32(0xC01E0308)
+#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET __constant_cpu_to_le32(0xC01E0309)
+#define STATUS_GRAPHICS_INVALID_FREQUENCY __constant_cpu_to_le32(0xC01E030A)
+#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION __constant_cpu_to_le32(0xC01E030B)
+#define STATUS_GRAPHICS_INVALID_TOTAL_REGION __constant_cpu_to_le32(0xC01E030C)
+#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE __constant_cpu_to_le32(0xC01E0310)
+#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE __constant_cpu_to_le32(0xC01E0311)
+#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET __constant_cpu_to_le32(0xC01E0312)
+#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0313)
+#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET __constant_cpu_to_le32(0xC01E0314)
+#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET __constant_cpu_to_le32(0xC01E0315)
+#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET __constant_cpu_to_le32(0xC01E0316)
+#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E0317)
+#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E0318)
+#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH __constant_cpu_to_le32(0xC01E0319)
+#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY __constant_cpu_to_le32(0xC01E031A)
+#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET __constant_cpu_to_le32(0xC01E031B)
+#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE __constant_cpu_to_le32(0xC01E031C)
+#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET __constant_cpu_to_le32(0xC01E031D)
+#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E031F)
+#define STATUS_GRAPHICS_STALE_MODESET __constant_cpu_to_le32(0xC01E0320)
+#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET __constant_cpu_to_le32(0xC01E0321)
+#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE __constant_cpu_to_le32(0xC01E0322)
+#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN __constant_cpu_to_le32(0xC01E0323)
+#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E0324)
+#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION __constant_cpu_to_le32(0xC01E0325)
+#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES __constant_cpu_to_le32(0xC01E0326)
+#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0327)
+#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE __constant_cpu_to_le32(0xC01E0328)
+#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET __constant_cpu_to_le32(0xC01E0329)
+#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET __constant_cpu_to_le32(0xC01E032A)
+#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR __constant_cpu_to_le32(0xC01E032B)
+#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET __constant_cpu_to_le32(0xC01E032C)
+#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E032D)
+#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E032E)
+#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE __constant_cpu_to_le32(0xC01E032F)
+#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED __constant_cpu_to_le32(0xC01E0330)
+#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E0331)
+#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E0332)
+#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET __constant_cpu_to_le32(0xC01E0333)
+#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER __constant_cpu_to_le32(0xC01E0334)
+#define STATUS_GRAPHICS_NO_VIDPNMGR __constant_cpu_to_le32(0xC01E0335)
+#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN __constant_cpu_to_le32(0xC01E0336)
+#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY __constant_cpu_to_le32(0xC01E0337)
+#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED __constant_cpu_to_le32(0xC01E0338)
+#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0339)
+#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE __constant_cpu_to_le32(0xC01E033A)
+#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE __constant_cpu_to_le32(0xC01E033B)
+#define STATUS_GRAPHICS_INVALID_STRIDE __constant_cpu_to_le32(0xC01E033C)
+#define STATUS_GRAPHICS_INVALID_PIXELFORMAT __constant_cpu_to_le32(0xC01E033D)
+#define STATUS_GRAPHICS_INVALID_COLORBASIS __constant_cpu_to_le32(0xC01E033E)
+#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE __constant_cpu_to_le32(0xC01E033F)
+#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0340)
+#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT __constant_cpu_to_le32(0xC01E0341)
+#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE __constant_cpu_to_le32(0xC01E0342)
+#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN __constant_cpu_to_le32(0xC01E0343)
+#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL __constant_cpu_to_le32(0xC01E0344)
+#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION __constant_cpu_to_le32(0xC01E0345)
+#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0346)
+#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP __constant_cpu_to_le32(0xC01E0347)
+#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0348)
+#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0349)
+#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET __constant_cpu_to_le32(0xC01E034A)
+#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON __constant_cpu_to_le32(0xC01E034D)
+#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE __constant_cpu_to_le32(0xC01E034E)
+#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE __constant_cpu_to_le32(0xC01E034F)
+#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS __constant_cpu_to_le32(0xC01E0350)
+#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING __constant_cpu_to_le32(0xC01E0352)
+#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED __constant_cpu_to_le32(0xC01E0353)
+#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS __constant_cpu_to_le32(0xC01E0354)
+#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT __constant_cpu_to_le32(0xC01E0355)
+#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM __constant_cpu_to_le32(0xC01E0356)
+#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN __constant_cpu_to_le32(0xC01E0357)
+#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT __constant_cpu_to_le32(0xC01E0358)
+#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED __constant_cpu_to_le32(0xC01E0359)
+#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION __constant_cpu_to_le32(0xC01E035A)
+#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE __constant_cpu_to_le32(0xC01E035B)
+#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET __constant_cpu_to_le32(0xC01E035C)
+#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED __constant_cpu_to_le32(0xC01E0400)
+#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0401)
+#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER __constant_cpu_to_le32(0xC01E0430)
+#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED __constant_cpu_to_le32(0xC01E0431)
+#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED __constant_cpu_to_le32(0xC01E0432)
+#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY __constant_cpu_to_le32(0xC01E0433)
+#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED __constant_cpu_to_le32(0xC01E0434)
+#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON __constant_cpu_to_le32(0xC01E0435)
+#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE __constant_cpu_to_le32(0xC01E0436)
+#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER __constant_cpu_to_le32(0xC01E0438)
+#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED __constant_cpu_to_le32(0xC01E043B)
+#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS __constant_cpu_to_le32(0xC01E051C)
+#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST __constant_cpu_to_le32(0xC01E051D)
+#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E051E)
+#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS __constant_cpu_to_le32(0xC01E051F)
+#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0520)
+#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST __constant_cpu_to_le32(0xC01E0521)
+#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0500)
+#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0501)
+#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0502)
+#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS __constant_cpu_to_le32(0xC01E0503)
+#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL __constant_cpu_to_le32(0xC01E0504)
+#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST __constant_cpu_to_le32(0xC01E0505)
+#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME __constant_cpu_to_le32(0xC01E0506)
+#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP __constant_cpu_to_le32(0xC01E0507)
+#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0508)
+#define STATUS_GRAPHICS_OPM_INVALID_POINTER __constant_cpu_to_le32(0xC01E050A)
+#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E050B)
+#define STATUS_GRAPHICS_OPM_INVALID_HANDLE __constant_cpu_to_le32(0xC01E050C)
+#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE __constant_cpu_to_le32(0xC01E050D)
+#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH __constant_cpu_to_le32(0xC01E050E)
+#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED __constant_cpu_to_le32(0xC01E050F)
+#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED __constant_cpu_to_le32(0xC01E0510)
+#define STATUS_GRAPHICS_PVP_HFS_FAILED __constant_cpu_to_le32(0xC01E0511)
+#define STATUS_GRAPHICS_OPM_INVALID_SRM __constant_cpu_to_le32(0xC01E0512)
+#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP __constant_cpu_to_le32(0xC01E0513)
+#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP __constant_cpu_to_le32(0xC01E0514)
+#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA __constant_cpu_to_le32(0xC01E0515)
+#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET __constant_cpu_to_le32(0xC01E0516)
+#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH __constant_cpu_to_le32(0xC01E0517)
+#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE __constant_cpu_to_le32(0xC01E0518)
+#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS __constant_cpu_to_le32(0xC01E051A)
+#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS __constant_cpu_to_le32(0xC01E051B)
+#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0580)
+#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST __constant_cpu_to_le32(0xC01E0581)
+#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA __constant_cpu_to_le32(0xC01E0582)
+#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA __constant_cpu_to_le32(0xC01E0583)
+#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0584)
+#define STATUS_GRAPHICS_DDCCI_INVALID_DATA __constant_cpu_to_le32(0xC01E0585)
+#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE __constant_cpu_to_le32(0xC01E0586)
+#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING __constant_cpu_to_le32(0xC01E0587)
+#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E0588)
+#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND __constant_cpu_to_le32(0xC01E0589)
+#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH __constant_cpu_to_le32(0xC01E058A)
+#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM __constant_cpu_to_le32(0xC01E058B)
+#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE __constant_cpu_to_le32(0xC01E058C)
+#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS __constant_cpu_to_le32(0xC01E058D)
+#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED __constant_cpu_to_le32(0xC01E05E0)
+#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME __constant_cpu_to_le32(0xC01E05E1)
+#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP __constant_cpu_to_le32(0xC01E05E2)
+#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E05E3)
+#define STATUS_GRAPHICS_INVALID_POINTER __constant_cpu_to_le32(0xC01E05E4)
+#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE __constant_cpu_to_le32(0xC01E05E5)
+#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL __constant_cpu_to_le32(0xC01E05E6)
+#define STATUS_GRAPHICS_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E05E7)
+#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS __constant_cpu_to_le32(0xC01E05E8)
+#define STATUS_FVE_LOCKED_VOLUME __constant_cpu_to_le32(0xC0210000)
+#define STATUS_FVE_NOT_ENCRYPTED __constant_cpu_to_le32(0xC0210001)
+#define STATUS_FVE_BAD_INFORMATION __constant_cpu_to_le32(0xC0210002)
+#define STATUS_FVE_TOO_SMALL __constant_cpu_to_le32(0xC0210003)
+#define STATUS_FVE_FAILED_WRONG_FS __constant_cpu_to_le32(0xC0210004)
+#define STATUS_FVE_FAILED_BAD_FS __constant_cpu_to_le32(0xC0210005)
+#define STATUS_FVE_FS_NOT_EXTENDED __constant_cpu_to_le32(0xC0210006)
+#define STATUS_FVE_FS_MOUNTED __constant_cpu_to_le32(0xC0210007)
+#define STATUS_FVE_NO_LICENSE __constant_cpu_to_le32(0xC0210008)
+#define STATUS_FVE_ACTION_NOT_ALLOWED __constant_cpu_to_le32(0xC0210009)
+#define STATUS_FVE_BAD_DATA __constant_cpu_to_le32(0xC021000A)
+#define STATUS_FVE_VOLUME_NOT_BOUND __constant_cpu_to_le32(0xC021000B)
+#define STATUS_FVE_NOT_DATA_VOLUME __constant_cpu_to_le32(0xC021000C)
+#define STATUS_FVE_CONV_READ_ERROR __constant_cpu_to_le32(0xC021000D)
+#define STATUS_FVE_CONV_WRITE_ERROR __constant_cpu_to_le32(0xC021000E)
+#define STATUS_FVE_OVERLAPPED_UPDATE __constant_cpu_to_le32(0xC021000F)
+#define STATUS_FVE_FAILED_SECTOR_SIZE __constant_cpu_to_le32(0xC0210010)
+#define STATUS_FVE_FAILED_AUTHENTICATION __constant_cpu_to_le32(0xC0210011)
+#define STATUS_FVE_NOT_OS_VOLUME __constant_cpu_to_le32(0xC0210012)
+#define STATUS_FVE_KEYFILE_NOT_FOUND __constant_cpu_to_le32(0xC0210013)
+#define STATUS_FVE_KEYFILE_INVALID __constant_cpu_to_le32(0xC0210014)
+#define STATUS_FVE_KEYFILE_NO_VMK __constant_cpu_to_le32(0xC0210015)
+#define STATUS_FVE_TPM_DISABLED __constant_cpu_to_le32(0xC0210016)
+#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO __constant_cpu_to_le32(0xC0210017)
+#define STATUS_FVE_TPM_INVALID_PCR __constant_cpu_to_le32(0xC0210018)
+#define STATUS_FVE_TPM_NO_VMK __constant_cpu_to_le32(0xC0210019)
+#define STATUS_FVE_PIN_INVALID __constant_cpu_to_le32(0xC021001A)
+#define STATUS_FVE_AUTH_INVALID_APPLICATION __constant_cpu_to_le32(0xC021001B)
+#define STATUS_FVE_AUTH_INVALID_CONFIG __constant_cpu_to_le32(0xC021001C)
+#define STATUS_FVE_DEBUGGER_ENABLED __constant_cpu_to_le32(0xC021001D)
+#define STATUS_FVE_DRY_RUN_FAILED __constant_cpu_to_le32(0xC021001E)
+#define STATUS_FVE_BAD_METADATA_POINTER __constant_cpu_to_le32(0xC021001F)
+#define STATUS_FVE_OLD_METADATA_COPY __constant_cpu_to_le32(0xC0210020)
+#define STATUS_FVE_REBOOT_REQUIRED __constant_cpu_to_le32(0xC0210021)
+#define STATUS_FVE_RAW_ACCESS __constant_cpu_to_le32(0xC0210022)
+#define STATUS_FVE_RAW_BLOCKED __constant_cpu_to_le32(0xC0210023)
+#define STATUS_FWP_CALLOUT_NOT_FOUND __constant_cpu_to_le32(0xC0220001)
+#define STATUS_FWP_CONDITION_NOT_FOUND __constant_cpu_to_le32(0xC0220002)
+#define STATUS_FWP_FILTER_NOT_FOUND __constant_cpu_to_le32(0xC0220003)
+#define STATUS_FWP_LAYER_NOT_FOUND __constant_cpu_to_le32(0xC0220004)
+#define STATUS_FWP_PROVIDER_NOT_FOUND __constant_cpu_to_le32(0xC0220005)
+#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND __constant_cpu_to_le32(0xC0220006)
+#define STATUS_FWP_SUBLAYER_NOT_FOUND __constant_cpu_to_le32(0xC0220007)
+#define STATUS_FWP_NOT_FOUND __constant_cpu_to_le32(0xC0220008)
+#define STATUS_FWP_ALREADY_EXISTS __constant_cpu_to_le32(0xC0220009)
+#define STATUS_FWP_IN_USE __constant_cpu_to_le32(0xC022000A)
+#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS __constant_cpu_to_le32(0xC022000B)
+#define STATUS_FWP_WRONG_SESSION __constant_cpu_to_le32(0xC022000C)
+#define STATUS_FWP_NO_TXN_IN_PROGRESS __constant_cpu_to_le32(0xC022000D)
+#define STATUS_FWP_TXN_IN_PROGRESS __constant_cpu_to_le32(0xC022000E)
+#define STATUS_FWP_TXN_ABORTED __constant_cpu_to_le32(0xC022000F)
+#define STATUS_FWP_SESSION_ABORTED __constant_cpu_to_le32(0xC0220010)
+#define STATUS_FWP_INCOMPATIBLE_TXN __constant_cpu_to_le32(0xC0220011)
+#define STATUS_FWP_TIMEOUT __constant_cpu_to_le32(0xC0220012)
+#define STATUS_FWP_NET_EVENTS_DISABLED __constant_cpu_to_le32(0xC0220013)
+#define STATUS_FWP_INCOMPATIBLE_LAYER __constant_cpu_to_le32(0xC0220014)
+#define STATUS_FWP_KM_CLIENTS_ONLY __constant_cpu_to_le32(0xC0220015)
+#define STATUS_FWP_LIFETIME_MISMATCH __constant_cpu_to_le32(0xC0220016)
+#define STATUS_FWP_BUILTIN_OBJECT __constant_cpu_to_le32(0xC0220017)
+#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS __constant_cpu_to_le32(0xC0220018)
+#define STATUS_FWP_TOO_MANY_CALLOUTS __constant_cpu_to_le32(0xC0220018)
+#define STATUS_FWP_NOTIFICATION_DROPPED __constant_cpu_to_le32(0xC0220019)
+#define STATUS_FWP_TRAFFIC_MISMATCH __constant_cpu_to_le32(0xC022001A)
+#define STATUS_FWP_INCOMPATIBLE_SA_STATE __constant_cpu_to_le32(0xC022001B)
+#define STATUS_FWP_NULL_POINTER __constant_cpu_to_le32(0xC022001C)
+#define STATUS_FWP_INVALID_ENUMERATOR __constant_cpu_to_le32(0xC022001D)
+#define STATUS_FWP_INVALID_FLAGS __constant_cpu_to_le32(0xC022001E)
+#define STATUS_FWP_INVALID_NET_MASK __constant_cpu_to_le32(0xC022001F)
+#define STATUS_FWP_INVALID_RANGE __constant_cpu_to_le32(0xC0220020)
+#define STATUS_FWP_INVALID_INTERVAL __constant_cpu_to_le32(0xC0220021)
+#define STATUS_FWP_ZERO_LENGTH_ARRAY __constant_cpu_to_le32(0xC0220022)
+#define STATUS_FWP_NULL_DISPLAY_NAME __constant_cpu_to_le32(0xC0220023)
+#define STATUS_FWP_INVALID_ACTION_TYPE __constant_cpu_to_le32(0xC0220024)
+#define STATUS_FWP_INVALID_WEIGHT __constant_cpu_to_le32(0xC0220025)
+#define STATUS_FWP_MATCH_TYPE_MISMATCH __constant_cpu_to_le32(0xC0220026)
+#define STATUS_FWP_TYPE_MISMATCH __constant_cpu_to_le32(0xC0220027)
+#define STATUS_FWP_OUT_OF_BOUNDS __constant_cpu_to_le32(0xC0220028)
+#define STATUS_FWP_RESERVED __constant_cpu_to_le32(0xC0220029)
+#define STATUS_FWP_DUPLICATE_CONDITION __constant_cpu_to_le32(0xC022002A)
+#define STATUS_FWP_DUPLICATE_KEYMOD __constant_cpu_to_le32(0xC022002B)
+#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER __constant_cpu_to_le32(0xC022002C)
+#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER __constant_cpu_to_le32(0xC022002D)
+#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER __constant_cpu_to_le32(0xC022002E)
+#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT __constant_cpu_to_le32(0xC022002F)
+#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD __constant_cpu_to_le32(0xC0220030)
+#define STATUS_FWP_INCOMPATIBLE_DH_GROUP __constant_cpu_to_le32(0xC0220031)
+#define STATUS_FWP_EM_NOT_SUPPORTED __constant_cpu_to_le32(0xC0220032)
+#define STATUS_FWP_NEVER_MATCH __constant_cpu_to_le32(0xC0220033)
+#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH __constant_cpu_to_le32(0xC0220034)
+#define STATUS_FWP_INVALID_PARAMETER __constant_cpu_to_le32(0xC0220035)
+#define STATUS_FWP_TOO_MANY_SUBLAYERS __constant_cpu_to_le32(0xC0220036)
+#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED __constant_cpu_to_le32(0xC0220037)
+#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG __constant_cpu_to_le32(0xC0220038)
+#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG __constant_cpu_to_le32(0xC0220039)
+#define STATUS_FWP_TCPIP_NOT_READY __constant_cpu_to_le32(0xC0220100)
+#define STATUS_FWP_INJECT_HANDLE_CLOSING __constant_cpu_to_le32(0xC0220101)
+#define STATUS_FWP_INJECT_HANDLE_STALE __constant_cpu_to_le32(0xC0220102)
+#define STATUS_FWP_CANNOT_PEND __constant_cpu_to_le32(0xC0220103)
+#define STATUS_NDIS_CLOSING __constant_cpu_to_le32(0xC0230002)
+#define STATUS_NDIS_BAD_VERSION __constant_cpu_to_le32(0xC0230004)
+#define STATUS_NDIS_BAD_CHARACTERISTICS __constant_cpu_to_le32(0xC0230005)
+#define STATUS_NDIS_ADAPTER_NOT_FOUND __constant_cpu_to_le32(0xC0230006)
+#define STATUS_NDIS_OPEN_FAILED __constant_cpu_to_le32(0xC0230007)
+#define STATUS_NDIS_DEVICE_FAILED __constant_cpu_to_le32(0xC0230008)
+#define STATUS_NDIS_MULTICAST_FULL __constant_cpu_to_le32(0xC0230009)
+#define STATUS_NDIS_MULTICAST_EXISTS __constant_cpu_to_le32(0xC023000A)
+#define STATUS_NDIS_MULTICAST_NOT_FOUND __constant_cpu_to_le32(0xC023000B)
+#define STATUS_NDIS_REQUEST_ABORTED __constant_cpu_to_le32(0xC023000C)
+#define STATUS_NDIS_RESET_IN_PROGRESS __constant_cpu_to_le32(0xC023000D)
+#define STATUS_NDIS_INVALID_PACKET __constant_cpu_to_le32(0xC023000F)
+#define STATUS_NDIS_INVALID_DEVICE_REQUEST __constant_cpu_to_le32(0xC0230010)
+#define STATUS_NDIS_ADAPTER_NOT_READY __constant_cpu_to_le32(0xC0230011)
+#define STATUS_NDIS_INVALID_LENGTH __constant_cpu_to_le32(0xC0230014)
+#define STATUS_NDIS_INVALID_DATA __constant_cpu_to_le32(0xC0230015)
+#define STATUS_NDIS_BUFFER_TOO_SHORT __constant_cpu_to_le32(0xC0230016)
+#define STATUS_NDIS_INVALID_OID __constant_cpu_to_le32(0xC0230017)
+#define STATUS_NDIS_ADAPTER_REMOVED __constant_cpu_to_le32(0xC0230018)
+#define STATUS_NDIS_UNSUPPORTED_MEDIA __constant_cpu_to_le32(0xC0230019)
+#define STATUS_NDIS_GROUP_ADDRESS_IN_USE __constant_cpu_to_le32(0xC023001A)
+#define STATUS_NDIS_FILE_NOT_FOUND __constant_cpu_to_le32(0xC023001B)
+#define STATUS_NDIS_ERROR_READING_FILE __constant_cpu_to_le32(0xC023001C)
+#define STATUS_NDIS_ALREADY_MAPPED __constant_cpu_to_le32(0xC023001D)
+#define STATUS_NDIS_RESOURCE_CONFLICT __constant_cpu_to_le32(0xC023001E)
+#define STATUS_NDIS_MEDIA_DISCONNECTED __constant_cpu_to_le32(0xC023001F)
+#define STATUS_NDIS_INVALID_ADDRESS __constant_cpu_to_le32(0xC0230022)
+#define STATUS_NDIS_PAUSED __constant_cpu_to_le32(0xC023002A)
+#define STATUS_NDIS_INTERFACE_NOT_FOUND __constant_cpu_to_le32(0xC023002B)
+#define STATUS_NDIS_UNSUPPORTED_REVISION __constant_cpu_to_le32(0xC023002C)
+#define STATUS_NDIS_INVALID_PORT __constant_cpu_to_le32(0xC023002D)
+#define STATUS_NDIS_INVALID_PORT_STATE __constant_cpu_to_le32(0xC023002E)
+#define STATUS_NDIS_LOW_POWER_STATE __constant_cpu_to_le32(0xC023002F)
+#define STATUS_NDIS_NOT_SUPPORTED __constant_cpu_to_le32(0xC02300BB)
+#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED __constant_cpu_to_le32(0xC0232000)
+#define STATUS_NDIS_DOT11_MEDIA_IN_USE __constant_cpu_to_le32(0xC0232001)
+#define STATUS_NDIS_DOT11_POWER_STATE_INVALID __constant_cpu_to_le32(0xC0232002)
+#define STATUS_IPSEC_BAD_SPI __constant_cpu_to_le32(0xC0360001)
+#define STATUS_IPSEC_SA_LIFETIME_EXPIRED __constant_cpu_to_le32(0xC0360002)
+#define STATUS_IPSEC_WRONG_SA __constant_cpu_to_le32(0xC0360003)
+#define STATUS_IPSEC_REPLAY_CHECK_FAILED __constant_cpu_to_le32(0xC0360004)
+#define STATUS_IPSEC_INVALID_PACKET __constant_cpu_to_le32(0xC0360005)
+#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED __constant_cpu_to_le32(0xC0360006)
+#define STATUS_IPSEC_CLEAR_TEXT_DROP __constant_cpu_to_le32(0xC0360007)
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
new file mode 100644
index 000000000..63264db78
--- /dev/null
+++ b/fs/cifs/smb2transport.c
@@ -0,0 +1,748 @@
+/*
+ * fs/cifs/smb2transport.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002, 2011
+ * Etersoft, 2012
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org) 2006
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/net.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <asm/processor.h>
+#include <linux/mempool.h>
+#include <linux/highmem.h>
+#include <crypto/aead.h>
+#include "smb2pdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "smb2proto.h"
+#include "cifs_debug.h"
+#include "smb2status.h"
+#include "smb2glob.h"
+
+static int
+smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
+{
+ return cifs_alloc_hash("hmac(sha256)",
+ &server->secmech.hmacsha256,
+ &server->secmech.sdeschmacsha256);
+}
+
+static int
+smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
+{
+ struct cifs_secmech *p = &server->secmech;
+ int rc;
+
+ rc = cifs_alloc_hash("hmac(sha256)",
+ &p->hmacsha256,
+ &p->sdeschmacsha256);
+ if (rc)
+ goto err;
+
+ rc = cifs_alloc_hash("cmac(aes)", &p->cmacaes, &p->sdesccmacaes);
+ if (rc)
+ goto err;
+
+ return 0;
+err:
+ cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
+ return rc;
+}
+
+int
+smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
+{
+ struct cifs_secmech *p = &server->secmech;
+ int rc = 0;
+
+ rc = cifs_alloc_hash("hmac(sha256)",
+ &p->hmacsha256,
+ &p->sdeschmacsha256);
+ if (rc)
+ return rc;
+
+ rc = cifs_alloc_hash("cmac(aes)", &p->cmacaes, &p->sdesccmacaes);
+ if (rc)
+ goto err;
+
+ rc = cifs_alloc_hash("sha512", &p->sha512, &p->sdescsha512);
+ if (rc)
+ goto err;
+
+ return 0;
+
+err:
+ cifs_free_hash(&p->cmacaes, &p->sdesccmacaes);
+ cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
+ return rc;
+}
+
+static struct cifs_ses *
+smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
+{
+ struct cifs_ses *ses;
+
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ if (ses->Suid != ses_id)
+ continue;
+ return ses;
+ }
+
+ return NULL;
+}
+
+struct cifs_ses *
+smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
+{
+ struct cifs_ses *ses;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ ses = smb2_find_smb_ses_unlocked(server, ses_id);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ return ses;
+}
+
+static struct cifs_tcon *
+smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
+{
+ struct cifs_tcon *tcon;
+
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->tid != tid)
+ continue;
+ ++tcon->tc_count;
+ return tcon;
+ }
+
+ return NULL;
+}
+
+/*
+ * Obtain tcon corresponding to the tid in the given
+ * cifs_ses
+ */
+
+struct cifs_tcon *
+smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
+{
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ ses = smb2_find_smb_ses_unlocked(server, ses_id);
+ if (!ses) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+ }
+ tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ return tcon;
+}
+
+int
+smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ int rc;
+ unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
+ unsigned char *sigptr = smb2_signature;
+ struct kvec *iov = rqst->rq_iov;
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
+ struct cifs_ses *ses;
+ struct shash_desc *shash;
+ struct smb_rqst drqst;
+
+ ses = smb2_find_smb_ses(server, shdr->SessionId);
+ if (!ses) {
+ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+ return 0;
+ }
+
+ memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
+ memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+ rc = smb2_crypto_shash_allocate(server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: sha256 alloc failed\n", __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_setkey(server->secmech.hmacsha256,
+ ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
+ return rc;
+ }
+
+ shash = &server->secmech.sdeschmacsha256->shash;
+ rc = crypto_shash_init(shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init sha256", __func__);
+ return rc;
+ }
+
+ /*
+ * For SMB2+, __cifs_calc_signature() expects to sign only the actual
+ * data, that is, iov[0] should not contain a rfc1002 length.
+ *
+ * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to
+ * __cifs_calc_signature().
+ */
+ drqst = *rqst;
+ if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
+ rc = crypto_shash_update(shash, iov[0].iov_base,
+ iov[0].iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with payload\n",
+ __func__);
+ return rc;
+ }
+ drqst.rq_iov++;
+ drqst.rq_nvec--;
+ }
+
+ rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
+ if (!rc)
+ memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+
+ return rc;
+}
+
+static int generate_key(struct cifs_ses *ses, struct kvec label,
+ struct kvec context, __u8 *key, unsigned int key_size)
+{
+ unsigned char zero = 0x0;
+ __u8 i[4] = {0, 0, 0, 1};
+ __u8 L[4] = {0, 0, 0, 128};
+ int rc = 0;
+ unsigned char prfhash[SMB2_HMACSHA256_SIZE];
+ unsigned char *hashptr = prfhash;
+
+ memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
+ memset(key, 0x0, key_size);
+
+ rc = smb3_crypto_shash_allocate(ses->server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_setkey(ses->server->secmech.hmacsha256,
+ ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not set with session key\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_init(&ses->server->secmech.sdeschmacsha256->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+ i, 4);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with n\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+ label.iov_base, label.iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with label\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+ &zero, 1);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with zero\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+ context.iov_base, context.iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with context\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+ L, 4);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with L\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ rc = crypto_shash_final(&ses->server->secmech.sdeschmacsha256->shash,
+ hashptr);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
+ goto smb3signkey_ret;
+ }
+
+ memcpy(key, hashptr, key_size);
+
+smb3signkey_ret:
+ return rc;
+}
+
+struct derivation {
+ struct kvec label;
+ struct kvec context;
+};
+
+struct derivation_triplet {
+ struct derivation signing;
+ struct derivation encryption;
+ struct derivation decryption;
+};
+
+static int
+generate_smb3signingkey(struct cifs_ses *ses,
+ const struct derivation_triplet *ptriplet)
+{
+ int rc;
+
+ rc = generate_key(ses, ptriplet->signing.label,
+ ptriplet->signing.context, ses->smb3signingkey,
+ SMB3_SIGN_KEY_SIZE);
+ if (rc)
+ return rc;
+
+ rc = generate_key(ses, ptriplet->encryption.label,
+ ptriplet->encryption.context, ses->smb3encryptionkey,
+ SMB3_SIGN_KEY_SIZE);
+ if (rc)
+ return rc;
+
+ rc = generate_key(ses, ptriplet->decryption.label,
+ ptriplet->decryption.context,
+ ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
+
+ if (rc)
+ return rc;
+
+#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
+ cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__);
+ /*
+ * The session id is opaque in terms of endianness, so we can't
+ * print it as a long long. we dump it as we got it on the wire
+ */
+ cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid),
+ &ses->Suid);
+ cifs_dbg(VFS, "Session Key %*ph\n",
+ SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
+ cifs_dbg(VFS, "Signing Key %*ph\n",
+ SMB3_SIGN_KEY_SIZE, ses->smb3signingkey);
+ cifs_dbg(VFS, "ServerIn Key %*ph\n",
+ SMB3_SIGN_KEY_SIZE, ses->smb3encryptionkey);
+ cifs_dbg(VFS, "ServerOut Key %*ph\n",
+ SMB3_SIGN_KEY_SIZE, ses->smb3decryptionkey);
+#endif
+ return rc;
+}
+
+int
+generate_smb30signingkey(struct cifs_ses *ses)
+
+{
+ struct derivation_triplet triplet;
+ struct derivation *d;
+
+ d = &triplet.signing;
+ d->label.iov_base = "SMB2AESCMAC";
+ d->label.iov_len = 12;
+ d->context.iov_base = "SmbSign";
+ d->context.iov_len = 8;
+
+ d = &triplet.encryption;
+ d->label.iov_base = "SMB2AESCCM";
+ d->label.iov_len = 11;
+ d->context.iov_base = "ServerIn ";
+ d->context.iov_len = 10;
+
+ d = &triplet.decryption;
+ d->label.iov_base = "SMB2AESCCM";
+ d->label.iov_len = 11;
+ d->context.iov_base = "ServerOut";
+ d->context.iov_len = 10;
+
+ return generate_smb3signingkey(ses, &triplet);
+}
+
+int
+generate_smb311signingkey(struct cifs_ses *ses)
+
+{
+ struct derivation_triplet triplet;
+ struct derivation *d;
+
+ d = &triplet.signing;
+ d->label.iov_base = "SMBSigningKey";
+ d->label.iov_len = 14;
+ d->context.iov_base = ses->preauth_sha_hash;
+ d->context.iov_len = 64;
+
+ d = &triplet.encryption;
+ d->label.iov_base = "SMBC2SCipherKey";
+ d->label.iov_len = 16;
+ d->context.iov_base = ses->preauth_sha_hash;
+ d->context.iov_len = 64;
+
+ d = &triplet.decryption;
+ d->label.iov_base = "SMBS2CCipherKey";
+ d->label.iov_len = 16;
+ d->context.iov_base = ses->preauth_sha_hash;
+ d->context.iov_len = 64;
+
+ return generate_smb3signingkey(ses, &triplet);
+}
+
+int
+smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ int rc;
+ unsigned char smb3_signature[SMB2_CMACAES_SIZE];
+ unsigned char *sigptr = smb3_signature;
+ struct kvec *iov = rqst->rq_iov;
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
+ struct cifs_ses *ses;
+ struct shash_desc *shash = &server->secmech.sdesccmacaes->shash;
+ struct smb_rqst drqst;
+
+ ses = smb2_find_smb_ses(server, shdr->SessionId);
+ if (!ses) {
+ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+ return 0;
+ }
+
+ memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
+ memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+ rc = crypto_shash_setkey(server->secmech.cmacaes,
+ ses->smb3signingkey, SMB2_CMACAES_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
+ return rc;
+ }
+
+ /*
+ * we already allocate sdesccmacaes when we init smb3 signing key,
+ * so unlike smb2 case we do not have to check here if secmech are
+ * initialized
+ */
+ rc = crypto_shash_init(shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
+ return rc;
+ }
+
+ /*
+ * For SMB2+, __cifs_calc_signature() expects to sign only the actual
+ * data, that is, iov[0] should not contain a rfc1002 length.
+ *
+ * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to
+ * __cifs_calc_signature().
+ */
+ drqst = *rqst;
+ if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
+ rc = crypto_shash_update(shash, iov[0].iov_base,
+ iov[0].iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with payload\n",
+ __func__);
+ return rc;
+ }
+ drqst.rq_iov++;
+ drqst.rq_nvec--;
+ }
+
+ rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
+ if (!rc)
+ memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+
+ return rc;
+}
+
+/* must be called with server->srv_mutex held */
+static int
+smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ struct smb2_sync_hdr *shdr =
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
+
+ if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
+ server->tcpStatus == CifsNeedNegotiate)
+ return rc;
+
+ if (!server->session_estab) {
+ strncpy(shdr->Signature, "BSRSPYL", 8);
+ return rc;
+ }
+
+ rc = server->ops->calc_signature(rqst, server);
+
+ return rc;
+}
+
+int
+smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ unsigned int rc;
+ char server_response_sig[16];
+ struct smb2_sync_hdr *shdr =
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
+
+ if ((shdr->Command == SMB2_NEGOTIATE) ||
+ (shdr->Command == SMB2_SESSION_SETUP) ||
+ (shdr->Command == SMB2_OPLOCK_BREAK) ||
+ (!server->session_estab))
+ return 0;
+
+ /*
+ * BB what if signatures are supposed to be on for session but
+ * server does not send one? BB
+ */
+
+ /* Do not need to verify session setups with signature "BSRSPYL " */
+ if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0)
+ cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n",
+ shdr->Command);
+
+ /*
+ * Save off the origiginal signature so we can modify the smb and check
+ * our calculated signature against what the server sent.
+ */
+ memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE);
+
+ memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
+
+ mutex_lock(&server->srv_mutex);
+ rc = server->ops->calc_signature(rqst, server);
+ mutex_unlock(&server->srv_mutex);
+
+ if (rc)
+ return rc;
+
+ if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE))
+ return -EACCES;
+ else
+ return 0;
+}
+
+/*
+ * Set message id for the request. Should be called after wait_for_free_request
+ * and when srv_mutex is held.
+ */
+static inline void
+smb2_seq_num_into_buf(struct TCP_Server_Info *server,
+ struct smb2_sync_hdr *shdr)
+{
+ unsigned int i, num = le16_to_cpu(shdr->CreditCharge);
+
+ shdr->MessageId = get_next_mid64(server);
+ /* skip message numbers according to CreditCharge field */
+ for (i = 1; i < num; i++)
+ get_next_mid(server);
+}
+
+static struct mid_q_entry *
+smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
+ struct TCP_Server_Info *server)
+{
+ struct mid_q_entry *temp;
+ unsigned int credits = le16_to_cpu(shdr->CreditCharge);
+
+ if (server == NULL) {
+ cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n");
+ return NULL;
+ }
+
+ temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
+ memset(temp, 0, sizeof(struct mid_q_entry));
+ kref_init(&temp->refcount);
+ temp->mid = le64_to_cpu(shdr->MessageId);
+ temp->credits = credits > 0 ? credits : 1;
+ temp->pid = current->pid;
+ temp->command = shdr->Command; /* Always LE */
+ temp->when_alloc = jiffies;
+ temp->server = server;
+
+ /*
+ * The default is for the mid to be synchronous, so the
+ * default callback just wakes up the current task.
+ */
+ temp->callback = cifs_wake_up_task;
+ temp->callback_data = current;
+
+ atomic_inc(&midCount);
+ temp->mid_state = MID_REQUEST_ALLOCATED;
+ return temp;
+}
+
+static int
+smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
+ struct mid_q_entry **mid)
+{
+ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ if (ses->server->tcpStatus == CifsNeedReconnect) {
+ cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
+ return -EAGAIN;
+ }
+
+ if (ses->status == CifsNew) {
+ if ((shdr->Command != SMB2_SESSION_SETUP) &&
+ (shdr->Command != SMB2_NEGOTIATE))
+ return -EAGAIN;
+ /* else ok - we are setting up session */
+ }
+
+ if (ses->status == CifsExiting) {
+ if (shdr->Command != SMB2_LOGOFF)
+ return -EAGAIN;
+ /* else ok - we are shutting down the session */
+ }
+
+ *mid = smb2_mid_entry_alloc(shdr, ses->server);
+ if (*mid == NULL)
+ return -ENOMEM;
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
+ spin_unlock(&GlobalMid_Lock);
+ return 0;
+}
+
+int
+smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ bool log_error)
+{
+ unsigned int len = mid->resp_buf_size;
+ struct kvec iov[1];
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = 1 };
+
+ iov[0].iov_base = (char *)mid->resp_buf;
+ iov[0].iov_len = len;
+
+ dump_smb(mid->resp_buf, min_t(u32, 80, len));
+ /* convert the length into a more usable form */
+ if (len > 24 && server->sign && !mid->decrypted) {
+ int rc;
+
+ rc = smb2_verify_signature(&rqst, server);
+ if (rc)
+ cifs_dbg(VFS, "SMB signature verification returned error = %d\n",
+ rc);
+ }
+
+ return map_smb2_to_linux_error(mid->resp_buf, log_error);
+}
+
+struct mid_q_entry *
+smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
+{
+ int rc;
+ struct smb2_sync_hdr *shdr =
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
+ smb2_seq_num_into_buf(ses->server, shdr);
+
+ rc = smb2_get_mid_entry(ses, shdr, &mid);
+ if (rc) {
+ revert_current_mid_from_hdr(ses->server, shdr);
+ return ERR_PTR(rc);
+ }
+
+ rc = smb2_sign_rqst(rqst, ses->server);
+ if (rc) {
+ revert_current_mid_from_hdr(ses->server, shdr);
+ cifs_delete_mid(mid);
+ return ERR_PTR(rc);
+ }
+
+ return mid;
+}
+
+struct mid_q_entry *
+smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+ int rc;
+ struct smb2_sync_hdr *shdr =
+ (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
+ smb2_seq_num_into_buf(server, shdr);
+
+ mid = smb2_mid_entry_alloc(shdr, server);
+ if (mid == NULL) {
+ revert_current_mid_from_hdr(server, shdr);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = smb2_sign_rqst(rqst, server);
+ if (rc) {
+ revert_current_mid_from_hdr(server, shdr);
+ DeleteMidQEntry(mid);
+ return ERR_PTR(rc);
+ }
+
+ return mid;
+}
+
+int
+smb3_crypto_aead_allocate(struct TCP_Server_Info *server)
+{
+ struct crypto_aead *tfm;
+
+ if (!server->secmech.ccmaesencrypt) {
+ tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
+ if (IS_ERR(tfm)) {
+ cifs_dbg(VFS, "%s: Failed to alloc encrypt aead\n",
+ __func__);
+ return PTR_ERR(tfm);
+ }
+ server->secmech.ccmaesencrypt = tfm;
+ }
+
+ if (!server->secmech.ccmaesdecrypt) {
+ tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
+ if (IS_ERR(tfm)) {
+ crypto_free_aead(server->secmech.ccmaesencrypt);
+ server->secmech.ccmaesencrypt = NULL;
+ cifs_dbg(VFS, "%s: Failed to alloc decrypt aead\n",
+ __func__);
+ return PTR_ERR(tfm);
+ }
+ server->secmech.ccmaesdecrypt = tfm;
+ }
+
+ return 0;
+}
diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c
new file mode 100644
index 000000000..784628ec4
--- /dev/null
+++ b/fs/cifs/smbdirect.c
@@ -0,0 +1,2645 @@
+/*
+ * Copyright (C) 2017, Microsoft Corporation.
+ *
+ * Author(s): Long Li <longli@microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include "smbdirect.h"
+#include "cifs_debug.h"
+#include "cifsproto.h"
+#include "smb2proto.h"
+
+static struct smbd_response *get_empty_queue_buffer(
+ struct smbd_connection *info);
+static struct smbd_response *get_receive_buffer(
+ struct smbd_connection *info);
+static void put_receive_buffer(
+ struct smbd_connection *info,
+ struct smbd_response *response);
+static int allocate_receive_buffers(struct smbd_connection *info, int num_buf);
+static void destroy_receive_buffers(struct smbd_connection *info);
+
+static void put_empty_packet(
+ struct smbd_connection *info, struct smbd_response *response);
+static void enqueue_reassembly(
+ struct smbd_connection *info,
+ struct smbd_response *response, int data_length);
+static struct smbd_response *_get_first_reassembly(
+ struct smbd_connection *info);
+
+static int smbd_post_recv(
+ struct smbd_connection *info,
+ struct smbd_response *response);
+
+static int smbd_post_send_empty(struct smbd_connection *info);
+static int smbd_post_send_data(
+ struct smbd_connection *info,
+ struct kvec *iov, int n_vec, int remaining_data_length);
+static int smbd_post_send_page(struct smbd_connection *info,
+ struct page *page, unsigned long offset,
+ size_t size, int remaining_data_length);
+
+static void destroy_mr_list(struct smbd_connection *info);
+static int allocate_mr_list(struct smbd_connection *info);
+
+/* SMBD version number */
+#define SMBD_V1 0x0100
+
+/* Port numbers for SMBD transport */
+#define SMB_PORT 445
+#define SMBD_PORT 5445
+
+/* Address lookup and resolve timeout in ms */
+#define RDMA_RESOLVE_TIMEOUT 5000
+
+/* SMBD negotiation timeout in seconds */
+#define SMBD_NEGOTIATE_TIMEOUT 120
+
+/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */
+#define SMBD_MIN_RECEIVE_SIZE 128
+#define SMBD_MIN_FRAGMENTED_SIZE 131072
+
+/*
+ * Default maximum number of RDMA read/write outstanding on this connection
+ * This value is possibly decreased during QP creation on hardware limit
+ */
+#define SMBD_CM_RESPONDER_RESOURCES 32
+
+/* Maximum number of retries on data transfer operations */
+#define SMBD_CM_RETRY 6
+/* No need to retry on Receiver Not Ready since SMBD manages credits */
+#define SMBD_CM_RNR_RETRY 0
+
+/*
+ * User configurable initial values per SMBD transport connection
+ * as defined in [MS-SMBD] 3.1.1.1
+ * Those may change after a SMBD negotiation
+ */
+/* The local peer's maximum number of credits to grant to the peer */
+int smbd_receive_credit_max = 255;
+
+/* The remote peer's credit request of local peer */
+int smbd_send_credit_target = 255;
+
+/* The maximum single message size can be sent to remote peer */
+int smbd_max_send_size = 1364;
+
+/* The maximum fragmented upper-layer payload receive size supported */
+int smbd_max_fragmented_recv_size = 1024 * 1024;
+
+/* The maximum single-message size which can be received */
+int smbd_max_receive_size = 8192;
+
+/* The timeout to initiate send of a keepalive message on idle */
+int smbd_keep_alive_interval = 120;
+
+/*
+ * User configurable initial values for RDMA transport
+ * The actual values used may be lower and are limited to hardware capabilities
+ */
+/* Default maximum number of SGEs in a RDMA write/read */
+int smbd_max_frmr_depth = 2048;
+
+/* If payload is less than this byte, use RDMA send/recv not read/write */
+int rdma_readwrite_threshold = 4096;
+
+/* Transport logging functions
+ * Logging are defined as classes. They can be OR'ed to define the actual
+ * logging level via module parameter smbd_logging_class
+ * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and
+ * log_rdma_event()
+ */
+#define LOG_OUTGOING 0x1
+#define LOG_INCOMING 0x2
+#define LOG_READ 0x4
+#define LOG_WRITE 0x8
+#define LOG_RDMA_SEND 0x10
+#define LOG_RDMA_RECV 0x20
+#define LOG_KEEP_ALIVE 0x40
+#define LOG_RDMA_EVENT 0x80
+#define LOG_RDMA_MR 0x100
+static unsigned int smbd_logging_class;
+module_param(smbd_logging_class, uint, 0644);
+MODULE_PARM_DESC(smbd_logging_class,
+ "Logging class for SMBD transport 0x0 to 0x100");
+
+#define ERR 0x0
+#define INFO 0x1
+static unsigned int smbd_logging_level = ERR;
+module_param(smbd_logging_level, uint, 0644);
+MODULE_PARM_DESC(smbd_logging_level,
+ "Logging level for SMBD transport, 0 (default): error, 1: info");
+
+#define log_rdma(level, class, fmt, args...) \
+do { \
+ if (level <= smbd_logging_level || class & smbd_logging_class) \
+ cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\
+} while (0)
+
+#define log_outgoing(level, fmt, args...) \
+ log_rdma(level, LOG_OUTGOING, fmt, ##args)
+#define log_incoming(level, fmt, args...) \
+ log_rdma(level, LOG_INCOMING, fmt, ##args)
+#define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args)
+#define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args)
+#define log_rdma_send(level, fmt, args...) \
+ log_rdma(level, LOG_RDMA_SEND, fmt, ##args)
+#define log_rdma_recv(level, fmt, args...) \
+ log_rdma(level, LOG_RDMA_RECV, fmt, ##args)
+#define log_keep_alive(level, fmt, args...) \
+ log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args)
+#define log_rdma_event(level, fmt, args...) \
+ log_rdma(level, LOG_RDMA_EVENT, fmt, ##args)
+#define log_rdma_mr(level, fmt, args...) \
+ log_rdma(level, LOG_RDMA_MR, fmt, ##args)
+
+/*
+ * Destroy the transport and related RDMA and memory resources
+ * Need to go through all the pending counters and make sure on one is using
+ * the transport while it is destroyed
+ */
+static void smbd_destroy_rdma_work(struct work_struct *work)
+{
+ struct smbd_response *response;
+ struct smbd_connection *info =
+ container_of(work, struct smbd_connection, destroy_work);
+ unsigned long flags;
+
+ log_rdma_event(INFO, "destroying qp\n");
+ ib_drain_qp(info->id->qp);
+ rdma_destroy_qp(info->id);
+
+ /* Unblock all I/O waiting on the send queue */
+ wake_up_interruptible_all(&info->wait_send_queue);
+
+ log_rdma_event(INFO, "cancelling idle timer\n");
+ cancel_delayed_work_sync(&info->idle_timer_work);
+ log_rdma_event(INFO, "cancelling send immediate work\n");
+ cancel_delayed_work_sync(&info->send_immediate_work);
+
+ log_rdma_event(INFO, "wait for all send to finish\n");
+ wait_event(info->wait_smbd_send_pending,
+ info->smbd_send_pending == 0);
+
+ log_rdma_event(INFO, "wait for all recv to finish\n");
+ wake_up_interruptible(&info->wait_reassembly_queue);
+ wait_event(info->wait_smbd_recv_pending,
+ info->smbd_recv_pending == 0);
+
+ log_rdma_event(INFO, "wait for all send posted to IB to finish\n");
+ wait_event(info->wait_send_pending,
+ atomic_read(&info->send_pending) == 0);
+ wait_event(info->wait_send_payload_pending,
+ atomic_read(&info->send_payload_pending) == 0);
+
+ log_rdma_event(INFO, "freeing mr list\n");
+ wake_up_interruptible_all(&info->wait_mr);
+ wait_event(info->wait_for_mr_cleanup,
+ atomic_read(&info->mr_used_count) == 0);
+ destroy_mr_list(info);
+
+ /* It's not posssible for upper layer to get to reassembly */
+ log_rdma_event(INFO, "drain the reassembly queue\n");
+ do {
+ spin_lock_irqsave(&info->reassembly_queue_lock, flags);
+ response = _get_first_reassembly(info);
+ if (response) {
+ list_del(&response->list);
+ spin_unlock_irqrestore(
+ &info->reassembly_queue_lock, flags);
+ put_receive_buffer(info, response);
+ } else
+ spin_unlock_irqrestore(&info->reassembly_queue_lock, flags);
+ } while (response);
+
+ info->reassembly_data_length = 0;
+
+ log_rdma_event(INFO, "free receive buffers\n");
+ wait_event(info->wait_receive_queues,
+ info->count_receive_queue + info->count_empty_packet_queue
+ == info->receive_credit_max);
+ destroy_receive_buffers(info);
+
+ ib_free_cq(info->send_cq);
+ ib_free_cq(info->recv_cq);
+ ib_dealloc_pd(info->pd);
+ rdma_destroy_id(info->id);
+
+ /* free mempools */
+ mempool_destroy(info->request_mempool);
+ kmem_cache_destroy(info->request_cache);
+
+ mempool_destroy(info->response_mempool);
+ kmem_cache_destroy(info->response_cache);
+
+ info->transport_status = SMBD_DESTROYED;
+ wake_up_all(&info->wait_destroy);
+}
+
+static int smbd_process_disconnected(struct smbd_connection *info)
+{
+ schedule_work(&info->destroy_work);
+ return 0;
+}
+
+static void smbd_disconnect_rdma_work(struct work_struct *work)
+{
+ struct smbd_connection *info =
+ container_of(work, struct smbd_connection, disconnect_work);
+
+ if (info->transport_status == SMBD_CONNECTED) {
+ info->transport_status = SMBD_DISCONNECTING;
+ rdma_disconnect(info->id);
+ }
+}
+
+static void smbd_disconnect_rdma_connection(struct smbd_connection *info)
+{
+ queue_work(info->workqueue, &info->disconnect_work);
+}
+
+/* Upcall from RDMA CM */
+static int smbd_conn_upcall(
+ struct rdma_cm_id *id, struct rdma_cm_event *event)
+{
+ struct smbd_connection *info = id->context;
+
+ log_rdma_event(INFO, "event=%d status=%d\n",
+ event->event, event->status);
+
+ switch (event->event) {
+ case RDMA_CM_EVENT_ADDR_RESOLVED:
+ case RDMA_CM_EVENT_ROUTE_RESOLVED:
+ info->ri_rc = 0;
+ complete(&info->ri_done);
+ break;
+
+ case RDMA_CM_EVENT_ADDR_ERROR:
+ info->ri_rc = -EHOSTUNREACH;
+ complete(&info->ri_done);
+ break;
+
+ case RDMA_CM_EVENT_ROUTE_ERROR:
+ info->ri_rc = -ENETUNREACH;
+ complete(&info->ri_done);
+ break;
+
+ case RDMA_CM_EVENT_ESTABLISHED:
+ log_rdma_event(INFO, "connected event=%d\n", event->event);
+ info->transport_status = SMBD_CONNECTED;
+ wake_up_interruptible(&info->conn_wait);
+ break;
+
+ case RDMA_CM_EVENT_CONNECT_ERROR:
+ case RDMA_CM_EVENT_UNREACHABLE:
+ case RDMA_CM_EVENT_REJECTED:
+ log_rdma_event(INFO, "connecting failed event=%d\n", event->event);
+ info->transport_status = SMBD_DISCONNECTED;
+ wake_up_interruptible(&info->conn_wait);
+ break;
+
+ case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ case RDMA_CM_EVENT_DISCONNECTED:
+ /* This happenes when we fail the negotiation */
+ if (info->transport_status == SMBD_NEGOTIATE_FAILED) {
+ info->transport_status = SMBD_DISCONNECTED;
+ wake_up(&info->conn_wait);
+ break;
+ }
+
+ info->transport_status = SMBD_DISCONNECTED;
+ smbd_process_disconnected(info);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Upcall from RDMA QP */
+static void
+smbd_qp_async_error_upcall(struct ib_event *event, void *context)
+{
+ struct smbd_connection *info = context;
+
+ log_rdma_event(ERR, "%s on device %s info %p\n",
+ ib_event_msg(event->event), event->device->name, info);
+
+ switch (event->event) {
+ case IB_EVENT_CQ_ERR:
+ case IB_EVENT_QP_FATAL:
+ smbd_disconnect_rdma_connection(info);
+
+ default:
+ break;
+ }
+}
+
+static inline void *smbd_request_payload(struct smbd_request *request)
+{
+ return (void *)request->packet;
+}
+
+static inline void *smbd_response_payload(struct smbd_response *response)
+{
+ return (void *)response->packet;
+}
+
+/* Called when a RDMA send is done */
+static void send_done(struct ib_cq *cq, struct ib_wc *wc)
+{
+ int i;
+ struct smbd_request *request =
+ container_of(wc->wr_cqe, struct smbd_request, cqe);
+
+ log_rdma_send(INFO, "smbd_request %p completed wc->status=%d\n",
+ request, wc->status);
+
+ if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
+ log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n",
+ wc->status, wc->opcode);
+ smbd_disconnect_rdma_connection(request->info);
+ }
+
+ for (i = 0; i < request->num_sge; i++)
+ ib_dma_unmap_single(request->info->id->device,
+ request->sge[i].addr,
+ request->sge[i].length,
+ DMA_TO_DEVICE);
+
+ if (request->has_payload) {
+ if (atomic_dec_and_test(&request->info->send_payload_pending))
+ wake_up(&request->info->wait_send_payload_pending);
+ } else {
+ if (atomic_dec_and_test(&request->info->send_pending))
+ wake_up(&request->info->wait_send_pending);
+ }
+
+ mempool_free(request, request->info->request_mempool);
+}
+
+static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp)
+{
+ log_rdma_event(INFO, "resp message min_version %u max_version %u "
+ "negotiated_version %u credits_requested %u "
+ "credits_granted %u status %u max_readwrite_size %u "
+ "preferred_send_size %u max_receive_size %u "
+ "max_fragmented_size %u\n",
+ resp->min_version, resp->max_version, resp->negotiated_version,
+ resp->credits_requested, resp->credits_granted, resp->status,
+ resp->max_readwrite_size, resp->preferred_send_size,
+ resp->max_receive_size, resp->max_fragmented_size);
+}
+
+/*
+ * Process a negotiation response message, according to [MS-SMBD]3.1.5.7
+ * response, packet_length: the negotiation response message
+ * return value: true if negotiation is a success, false if failed
+ */
+static bool process_negotiation_response(
+ struct smbd_response *response, int packet_length)
+{
+ struct smbd_connection *info = response->info;
+ struct smbd_negotiate_resp *packet = smbd_response_payload(response);
+
+ if (packet_length < sizeof(struct smbd_negotiate_resp)) {
+ log_rdma_event(ERR,
+ "error: packet_length=%d\n", packet_length);
+ return false;
+ }
+
+ if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) {
+ log_rdma_event(ERR, "error: negotiated_version=%x\n",
+ le16_to_cpu(packet->negotiated_version));
+ return false;
+ }
+ info->protocol = le16_to_cpu(packet->negotiated_version);
+
+ if (packet->credits_requested == 0) {
+ log_rdma_event(ERR, "error: credits_requested==0\n");
+ return false;
+ }
+ info->receive_credit_target = le16_to_cpu(packet->credits_requested);
+
+ if (packet->credits_granted == 0) {
+ log_rdma_event(ERR, "error: credits_granted==0\n");
+ return false;
+ }
+ atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted));
+
+ atomic_set(&info->receive_credits, 0);
+
+ if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) {
+ log_rdma_event(ERR, "error: preferred_send_size=%d\n",
+ le32_to_cpu(packet->preferred_send_size));
+ return false;
+ }
+ info->max_receive_size = le32_to_cpu(packet->preferred_send_size);
+
+ if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) {
+ log_rdma_event(ERR, "error: max_receive_size=%d\n",
+ le32_to_cpu(packet->max_receive_size));
+ return false;
+ }
+ info->max_send_size = min_t(int, info->max_send_size,
+ le32_to_cpu(packet->max_receive_size));
+
+ if (le32_to_cpu(packet->max_fragmented_size) <
+ SMBD_MIN_FRAGMENTED_SIZE) {
+ log_rdma_event(ERR, "error: max_fragmented_size=%d\n",
+ le32_to_cpu(packet->max_fragmented_size));
+ return false;
+ }
+ info->max_fragmented_send_size =
+ le32_to_cpu(packet->max_fragmented_size);
+ info->rdma_readwrite_threshold =
+ rdma_readwrite_threshold > info->max_fragmented_send_size ?
+ info->max_fragmented_send_size :
+ rdma_readwrite_threshold;
+
+
+ info->max_readwrite_size = min_t(u32,
+ le32_to_cpu(packet->max_readwrite_size),
+ info->max_frmr_depth * PAGE_SIZE);
+ info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE;
+
+ return true;
+}
+
+/*
+ * Check and schedule to send an immediate packet
+ * This is used to extend credtis to remote peer to keep the transport busy
+ */
+static void check_and_send_immediate(struct smbd_connection *info)
+{
+ if (info->transport_status != SMBD_CONNECTED)
+ return;
+
+ info->send_immediate = true;
+
+ /*
+ * Promptly send a packet if our peer is running low on receive
+ * credits
+ */
+ if (atomic_read(&info->receive_credits) <
+ info->receive_credit_target - 1)
+ queue_delayed_work(
+ info->workqueue, &info->send_immediate_work, 0);
+}
+
+static void smbd_post_send_credits(struct work_struct *work)
+{
+ int ret = 0;
+ int use_receive_queue = 1;
+ int rc;
+ struct smbd_response *response;
+ struct smbd_connection *info =
+ container_of(work, struct smbd_connection,
+ post_send_credits_work);
+
+ if (info->transport_status != SMBD_CONNECTED) {
+ wake_up(&info->wait_receive_queues);
+ return;
+ }
+
+ if (info->receive_credit_target >
+ atomic_read(&info->receive_credits)) {
+ while (true) {
+ if (use_receive_queue)
+ response = get_receive_buffer(info);
+ else
+ response = get_empty_queue_buffer(info);
+ if (!response) {
+ /* now switch to emtpy packet queue */
+ if (use_receive_queue) {
+ use_receive_queue = 0;
+ continue;
+ } else
+ break;
+ }
+
+ response->type = SMBD_TRANSFER_DATA;
+ response->first_segment = false;
+ rc = smbd_post_recv(info, response);
+ if (rc) {
+ log_rdma_recv(ERR,
+ "post_recv failed rc=%d\n", rc);
+ put_receive_buffer(info, response);
+ break;
+ }
+
+ ret++;
+ }
+ }
+
+ spin_lock(&info->lock_new_credits_offered);
+ info->new_credits_offered += ret;
+ spin_unlock(&info->lock_new_credits_offered);
+
+ atomic_add(ret, &info->receive_credits);
+
+ /* Check if we can post new receive and grant credits to peer */
+ check_and_send_immediate(info);
+}
+
+static void smbd_recv_done_work(struct work_struct *work)
+{
+ struct smbd_connection *info =
+ container_of(work, struct smbd_connection, recv_done_work);
+
+ /*
+ * We may have new send credits granted from remote peer
+ * If any sender is blcoked on lack of credets, unblock it
+ */
+ if (atomic_read(&info->send_credits))
+ wake_up_interruptible(&info->wait_send_queue);
+
+ /*
+ * Check if we need to send something to remote peer to
+ * grant more credits or respond to KEEP_ALIVE packet
+ */
+ check_and_send_immediate(info);
+}
+
+/* Called from softirq, when recv is done */
+static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
+{
+ struct smbd_data_transfer *data_transfer;
+ struct smbd_response *response =
+ container_of(wc->wr_cqe, struct smbd_response, cqe);
+ struct smbd_connection *info = response->info;
+ int data_length = 0;
+
+ log_rdma_recv(INFO, "response=%p type=%d wc status=%d wc opcode %d "
+ "byte_len=%d pkey_index=%x\n",
+ response, response->type, wc->status, wc->opcode,
+ wc->byte_len, wc->pkey_index);
+
+ if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) {
+ log_rdma_recv(INFO, "wc->status=%d opcode=%d\n",
+ wc->status, wc->opcode);
+ smbd_disconnect_rdma_connection(info);
+ goto error;
+ }
+
+ ib_dma_sync_single_for_cpu(
+ wc->qp->device,
+ response->sge.addr,
+ response->sge.length,
+ DMA_FROM_DEVICE);
+
+ switch (response->type) {
+ /* SMBD negotiation response */
+ case SMBD_NEGOTIATE_RESP:
+ dump_smbd_negotiate_resp(smbd_response_payload(response));
+ info->full_packet_received = true;
+ info->negotiate_done =
+ process_negotiation_response(response, wc->byte_len);
+ complete(&info->negotiate_completion);
+ break;
+
+ /* SMBD data transfer packet */
+ case SMBD_TRANSFER_DATA:
+ data_transfer = smbd_response_payload(response);
+ data_length = le32_to_cpu(data_transfer->data_length);
+
+ /*
+ * If this is a packet with data playload place the data in
+ * reassembly queue and wake up the reading thread
+ */
+ if (data_length) {
+ if (info->full_packet_received)
+ response->first_segment = true;
+
+ if (le32_to_cpu(data_transfer->remaining_data_length))
+ info->full_packet_received = false;
+ else
+ info->full_packet_received = true;
+
+ enqueue_reassembly(
+ info,
+ response,
+ data_length);
+ } else
+ put_empty_packet(info, response);
+
+ if (data_length)
+ wake_up_interruptible(&info->wait_reassembly_queue);
+
+ atomic_dec(&info->receive_credits);
+ info->receive_credit_target =
+ le16_to_cpu(data_transfer->credits_requested);
+ atomic_add(le16_to_cpu(data_transfer->credits_granted),
+ &info->send_credits);
+
+ log_incoming(INFO, "data flags %d data_offset %d "
+ "data_length %d remaining_data_length %d\n",
+ le16_to_cpu(data_transfer->flags),
+ le32_to_cpu(data_transfer->data_offset),
+ le32_to_cpu(data_transfer->data_length),
+ le32_to_cpu(data_transfer->remaining_data_length));
+
+ /* Send a KEEP_ALIVE response right away if requested */
+ info->keep_alive_requested = KEEP_ALIVE_NONE;
+ if (le16_to_cpu(data_transfer->flags) &
+ SMB_DIRECT_RESPONSE_REQUESTED) {
+ info->keep_alive_requested = KEEP_ALIVE_PENDING;
+ }
+
+ queue_work(info->workqueue, &info->recv_done_work);
+ return;
+
+ default:
+ log_rdma_recv(ERR,
+ "unexpected response type=%d\n", response->type);
+ }
+
+error:
+ put_receive_buffer(info, response);
+}
+
+static struct rdma_cm_id *smbd_create_id(
+ struct smbd_connection *info,
+ struct sockaddr *dstaddr, int port)
+{
+ struct rdma_cm_id *id;
+ int rc;
+ __be16 *sport;
+
+ id = rdma_create_id(&init_net, smbd_conn_upcall, info,
+ RDMA_PS_TCP, IB_QPT_RC);
+ if (IS_ERR(id)) {
+ rc = PTR_ERR(id);
+ log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc);
+ return id;
+ }
+
+ if (dstaddr->sa_family == AF_INET6)
+ sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port;
+ else
+ sport = &((struct sockaddr_in *)dstaddr)->sin_port;
+
+ *sport = htons(port);
+
+ init_completion(&info->ri_done);
+ info->ri_rc = -ETIMEDOUT;
+
+ rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr,
+ RDMA_RESOLVE_TIMEOUT);
+ if (rc) {
+ log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc);
+ goto out;
+ }
+ wait_for_completion_interruptible_timeout(
+ &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT));
+ rc = info->ri_rc;
+ if (rc) {
+ log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc);
+ goto out;
+ }
+
+ info->ri_rc = -ETIMEDOUT;
+ rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT);
+ if (rc) {
+ log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc);
+ goto out;
+ }
+ wait_for_completion_interruptible_timeout(
+ &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT));
+ rc = info->ri_rc;
+ if (rc) {
+ log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc);
+ goto out;
+ }
+
+ return id;
+
+out:
+ rdma_destroy_id(id);
+ return ERR_PTR(rc);
+}
+
+/*
+ * Test if FRWR (Fast Registration Work Requests) is supported on the device
+ * This implementation requries FRWR on RDMA read/write
+ * return value: true if it is supported
+ */
+static bool frwr_is_supported(struct ib_device_attr *attrs)
+{
+ if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS))
+ return false;
+ if (attrs->max_fast_reg_page_list_len == 0)
+ return false;
+ return true;
+}
+
+static int smbd_ia_open(
+ struct smbd_connection *info,
+ struct sockaddr *dstaddr, int port)
+{
+ int rc;
+
+ info->id = smbd_create_id(info, dstaddr, port);
+ if (IS_ERR(info->id)) {
+ rc = PTR_ERR(info->id);
+ goto out1;
+ }
+
+ if (!frwr_is_supported(&info->id->device->attrs)) {
+ log_rdma_event(ERR,
+ "Fast Registration Work Requests "
+ "(FRWR) is not supported\n");
+ log_rdma_event(ERR,
+ "Device capability flags = %llx "
+ "max_fast_reg_page_list_len = %u\n",
+ info->id->device->attrs.device_cap_flags,
+ info->id->device->attrs.max_fast_reg_page_list_len);
+ rc = -EPROTONOSUPPORT;
+ goto out2;
+ }
+ info->max_frmr_depth = min_t(int,
+ smbd_max_frmr_depth,
+ info->id->device->attrs.max_fast_reg_page_list_len);
+ info->mr_type = IB_MR_TYPE_MEM_REG;
+ if (info->id->device->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG)
+ info->mr_type = IB_MR_TYPE_SG_GAPS;
+
+ info->pd = ib_alloc_pd(info->id->device, 0);
+ if (IS_ERR(info->pd)) {
+ rc = PTR_ERR(info->pd);
+ log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc);
+ goto out2;
+ }
+
+ return 0;
+
+out2:
+ rdma_destroy_id(info->id);
+ info->id = NULL;
+
+out1:
+ return rc;
+}
+
+/*
+ * Send a negotiation request message to the peer
+ * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3
+ * After negotiation, the transport is connected and ready for
+ * carrying upper layer SMB payload
+ */
+static int smbd_post_send_negotiate_req(struct smbd_connection *info)
+{
+ struct ib_send_wr send_wr;
+ int rc = -ENOMEM;
+ struct smbd_request *request;
+ struct smbd_negotiate_req *packet;
+
+ request = mempool_alloc(info->request_mempool, GFP_KERNEL);
+ if (!request)
+ return rc;
+
+ request->info = info;
+
+ packet = smbd_request_payload(request);
+ packet->min_version = cpu_to_le16(SMBD_V1);
+ packet->max_version = cpu_to_le16(SMBD_V1);
+ packet->reserved = 0;
+ packet->credits_requested = cpu_to_le16(info->send_credit_target);
+ packet->preferred_send_size = cpu_to_le32(info->max_send_size);
+ packet->max_receive_size = cpu_to_le32(info->max_receive_size);
+ packet->max_fragmented_size =
+ cpu_to_le32(info->max_fragmented_recv_size);
+
+ request->num_sge = 1;
+ request->sge[0].addr = ib_dma_map_single(
+ info->id->device, (void *)packet,
+ sizeof(*packet), DMA_TO_DEVICE);
+ if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
+ rc = -EIO;
+ goto dma_mapping_failed;
+ }
+
+ request->sge[0].length = sizeof(*packet);
+ request->sge[0].lkey = info->pd->local_dma_lkey;
+
+ ib_dma_sync_single_for_device(
+ info->id->device, request->sge[0].addr,
+ request->sge[0].length, DMA_TO_DEVICE);
+
+ request->cqe.done = send_done;
+
+ send_wr.next = NULL;
+ send_wr.wr_cqe = &request->cqe;
+ send_wr.sg_list = request->sge;
+ send_wr.num_sge = request->num_sge;
+ send_wr.opcode = IB_WR_SEND;
+ send_wr.send_flags = IB_SEND_SIGNALED;
+
+ log_rdma_send(INFO, "sge addr=%llx length=%x lkey=%x\n",
+ request->sge[0].addr,
+ request->sge[0].length, request->sge[0].lkey);
+
+ request->has_payload = false;
+ atomic_inc(&info->send_pending);
+ rc = ib_post_send(info->id->qp, &send_wr, NULL);
+ if (!rc)
+ return 0;
+
+ /* if we reach here, post send failed */
+ log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+ atomic_dec(&info->send_pending);
+ ib_dma_unmap_single(info->id->device, request->sge[0].addr,
+ request->sge[0].length, DMA_TO_DEVICE);
+
+ smbd_disconnect_rdma_connection(info);
+
+dma_mapping_failed:
+ mempool_free(request, info->request_mempool);
+ return rc;
+}
+
+/*
+ * Extend the credits to remote peer
+ * This implements [MS-SMBD] 3.1.5.9
+ * The idea is that we should extend credits to remote peer as quickly as
+ * it's allowed, to maintain data flow. We allocate as much receive
+ * buffer as possible, and extend the receive credits to remote peer
+ * return value: the new credtis being granted.
+ */
+static int manage_credits_prior_sending(struct smbd_connection *info)
+{
+ int new_credits;
+
+ spin_lock(&info->lock_new_credits_offered);
+ new_credits = info->new_credits_offered;
+ info->new_credits_offered = 0;
+ spin_unlock(&info->lock_new_credits_offered);
+
+ return new_credits;
+}
+
+/*
+ * Check if we need to send a KEEP_ALIVE message
+ * The idle connection timer triggers a KEEP_ALIVE message when expires
+ * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send
+ * back a response.
+ * return value:
+ * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set
+ * 0: otherwise
+ */
+static int manage_keep_alive_before_sending(struct smbd_connection *info)
+{
+ if (info->keep_alive_requested == KEEP_ALIVE_PENDING) {
+ info->keep_alive_requested = KEEP_ALIVE_SENT;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Build and prepare the SMBD packet header
+ * This function waits for avaialbe send credits and build a SMBD packet
+ * header. The caller then optional append payload to the packet after
+ * the header
+ * intput values
+ * size: the size of the payload
+ * remaining_data_length: remaining data to send if this is part of a
+ * fragmented packet
+ * output values
+ * request_out: the request allocated from this function
+ * return values: 0 on success, otherwise actual error code returned
+ */
+static int smbd_create_header(struct smbd_connection *info,
+ int size, int remaining_data_length,
+ struct smbd_request **request_out)
+{
+ struct smbd_request *request;
+ struct smbd_data_transfer *packet;
+ int header_length;
+ int rc;
+
+ /* Wait for send credits. A SMBD packet needs one credit */
+ rc = wait_event_interruptible(info->wait_send_queue,
+ atomic_read(&info->send_credits) > 0 ||
+ info->transport_status != SMBD_CONNECTED);
+ if (rc)
+ return rc;
+
+ if (info->transport_status != SMBD_CONNECTED) {
+ log_outgoing(ERR, "disconnected not sending\n");
+ return -ENOENT;
+ }
+ atomic_dec(&info->send_credits);
+
+ request = mempool_alloc(info->request_mempool, GFP_KERNEL);
+ if (!request) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ request->info = info;
+
+ /* Fill in the packet header */
+ packet = smbd_request_payload(request);
+ packet->credits_requested = cpu_to_le16(info->send_credit_target);
+ packet->credits_granted =
+ cpu_to_le16(manage_credits_prior_sending(info));
+ info->send_immediate = false;
+
+ packet->flags = 0;
+ if (manage_keep_alive_before_sending(info))
+ packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED);
+
+ packet->reserved = 0;
+ if (!size)
+ packet->data_offset = 0;
+ else
+ packet->data_offset = cpu_to_le32(24);
+ packet->data_length = cpu_to_le32(size);
+ packet->remaining_data_length = cpu_to_le32(remaining_data_length);
+ packet->padding = 0;
+
+ log_outgoing(INFO, "credits_requested=%d credits_granted=%d "
+ "data_offset=%d data_length=%d remaining_data_length=%d\n",
+ le16_to_cpu(packet->credits_requested),
+ le16_to_cpu(packet->credits_granted),
+ le32_to_cpu(packet->data_offset),
+ le32_to_cpu(packet->data_length),
+ le32_to_cpu(packet->remaining_data_length));
+
+ /* Map the packet to DMA */
+ header_length = sizeof(struct smbd_data_transfer);
+ /* If this is a packet without payload, don't send padding */
+ if (!size)
+ header_length = offsetof(struct smbd_data_transfer, padding);
+
+ request->num_sge = 1;
+ request->sge[0].addr = ib_dma_map_single(info->id->device,
+ (void *)packet,
+ header_length,
+ DMA_BIDIRECTIONAL);
+ if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
+ mempool_free(request, info->request_mempool);
+ rc = -EIO;
+ goto err;
+ }
+
+ request->sge[0].length = header_length;
+ request->sge[0].lkey = info->pd->local_dma_lkey;
+
+ *request_out = request;
+ return 0;
+
+err:
+ atomic_inc(&info->send_credits);
+ return rc;
+}
+
+static void smbd_destroy_header(struct smbd_connection *info,
+ struct smbd_request *request)
+{
+
+ ib_dma_unmap_single(info->id->device,
+ request->sge[0].addr,
+ request->sge[0].length,
+ DMA_TO_DEVICE);
+ mempool_free(request, info->request_mempool);
+ atomic_inc(&info->send_credits);
+}
+
+/* Post the send request */
+static int smbd_post_send(struct smbd_connection *info,
+ struct smbd_request *request, bool has_payload)
+{
+ struct ib_send_wr send_wr;
+ int rc, i;
+
+ for (i = 0; i < request->num_sge; i++) {
+ log_rdma_send(INFO,
+ "rdma_request sge[%d] addr=%llu length=%u\n",
+ i, request->sge[i].addr, request->sge[i].length);
+ ib_dma_sync_single_for_device(
+ info->id->device,
+ request->sge[i].addr,
+ request->sge[i].length,
+ DMA_TO_DEVICE);
+ }
+
+ request->cqe.done = send_done;
+
+ send_wr.next = NULL;
+ send_wr.wr_cqe = &request->cqe;
+ send_wr.sg_list = request->sge;
+ send_wr.num_sge = request->num_sge;
+ send_wr.opcode = IB_WR_SEND;
+ send_wr.send_flags = IB_SEND_SIGNALED;
+
+ if (has_payload) {
+ request->has_payload = true;
+ atomic_inc(&info->send_payload_pending);
+ } else {
+ request->has_payload = false;
+ atomic_inc(&info->send_pending);
+ }
+
+ rc = ib_post_send(info->id->qp, &send_wr, NULL);
+ if (rc) {
+ log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+ if (has_payload) {
+ if (atomic_dec_and_test(&info->send_payload_pending))
+ wake_up(&info->wait_send_payload_pending);
+ } else {
+ if (atomic_dec_and_test(&info->send_pending))
+ wake_up(&info->wait_send_pending);
+ }
+ smbd_disconnect_rdma_connection(info);
+ } else
+ /* Reset timer for idle connection after packet is sent */
+ mod_delayed_work(info->workqueue, &info->idle_timer_work,
+ info->keep_alive_interval*HZ);
+
+ return rc;
+}
+
+static int smbd_post_send_sgl(struct smbd_connection *info,
+ struct scatterlist *sgl, int data_length, int remaining_data_length)
+{
+ int num_sgs;
+ int i, rc;
+ struct smbd_request *request;
+ struct scatterlist *sg;
+
+ rc = smbd_create_header(
+ info, data_length, remaining_data_length, &request);
+ if (rc)
+ return rc;
+
+ num_sgs = sgl ? sg_nents(sgl) : 0;
+ for_each_sg(sgl, sg, num_sgs, i) {
+ request->sge[i+1].addr =
+ ib_dma_map_page(info->id->device, sg_page(sg),
+ sg->offset, sg->length, DMA_BIDIRECTIONAL);
+ if (ib_dma_mapping_error(
+ info->id->device, request->sge[i+1].addr)) {
+ rc = -EIO;
+ request->sge[i+1].addr = 0;
+ goto dma_mapping_failure;
+ }
+ request->sge[i+1].length = sg->length;
+ request->sge[i+1].lkey = info->pd->local_dma_lkey;
+ request->num_sge++;
+ }
+
+ rc = smbd_post_send(info, request, data_length);
+ if (!rc)
+ return 0;
+
+dma_mapping_failure:
+ for (i = 1; i < request->num_sge; i++)
+ if (request->sge[i].addr)
+ ib_dma_unmap_single(info->id->device,
+ request->sge[i].addr,
+ request->sge[i].length,
+ DMA_TO_DEVICE);
+ smbd_destroy_header(info, request);
+ return rc;
+}
+
+/*
+ * Send a page
+ * page: the page to send
+ * offset: offset in the page to send
+ * size: length in the page to send
+ * remaining_data_length: remaining data to send in this payload
+ */
+static int smbd_post_send_page(struct smbd_connection *info, struct page *page,
+ unsigned long offset, size_t size, int remaining_data_length)
+{
+ struct scatterlist sgl;
+
+ sg_init_table(&sgl, 1);
+ sg_set_page(&sgl, page, size, offset);
+
+ return smbd_post_send_sgl(info, &sgl, size, remaining_data_length);
+}
+
+/*
+ * Send an empty message
+ * Empty message is used to extend credits to peer to for keep live
+ * while there is no upper layer payload to send at the time
+ */
+static int smbd_post_send_empty(struct smbd_connection *info)
+{
+ info->count_send_empty++;
+ return smbd_post_send_sgl(info, NULL, 0, 0);
+}
+
+/*
+ * Send a data buffer
+ * iov: the iov array describing the data buffers
+ * n_vec: number of iov array
+ * remaining_data_length: remaining data to send following this packet
+ * in segmented SMBD packet
+ */
+static int smbd_post_send_data(
+ struct smbd_connection *info, struct kvec *iov, int n_vec,
+ int remaining_data_length)
+{
+ int i;
+ u32 data_length = 0;
+ struct scatterlist sgl[SMBDIRECT_MAX_SGE];
+
+ if (n_vec > SMBDIRECT_MAX_SGE) {
+ cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec);
+ return -EINVAL;
+ }
+
+ sg_init_table(sgl, n_vec);
+ for (i = 0; i < n_vec; i++) {
+ data_length += iov[i].iov_len;
+ sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len);
+ }
+
+ return smbd_post_send_sgl(info, sgl, data_length, remaining_data_length);
+}
+
+/*
+ * Post a receive request to the transport
+ * The remote peer can only send data when a receive request is posted
+ * The interaction is controlled by send/receive credit system
+ */
+static int smbd_post_recv(
+ struct smbd_connection *info, struct smbd_response *response)
+{
+ struct ib_recv_wr recv_wr;
+ int rc = -EIO;
+
+ response->sge.addr = ib_dma_map_single(
+ info->id->device, response->packet,
+ info->max_receive_size, DMA_FROM_DEVICE);
+ if (ib_dma_mapping_error(info->id->device, response->sge.addr))
+ return rc;
+
+ response->sge.length = info->max_receive_size;
+ response->sge.lkey = info->pd->local_dma_lkey;
+
+ response->cqe.done = recv_done;
+
+ recv_wr.wr_cqe = &response->cqe;
+ recv_wr.next = NULL;
+ recv_wr.sg_list = &response->sge;
+ recv_wr.num_sge = 1;
+
+ rc = ib_post_recv(info->id->qp, &recv_wr, NULL);
+ if (rc) {
+ ib_dma_unmap_single(info->id->device, response->sge.addr,
+ response->sge.length, DMA_FROM_DEVICE);
+ smbd_disconnect_rdma_connection(info);
+ log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc);
+ }
+
+ return rc;
+}
+
+/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */
+static int smbd_negotiate(struct smbd_connection *info)
+{
+ int rc;
+ struct smbd_response *response = get_receive_buffer(info);
+
+ response->type = SMBD_NEGOTIATE_RESP;
+ rc = smbd_post_recv(info, response);
+ log_rdma_event(INFO,
+ "smbd_post_recv rc=%d iov.addr=%llx iov.length=%x "
+ "iov.lkey=%x\n",
+ rc, response->sge.addr,
+ response->sge.length, response->sge.lkey);
+ if (rc)
+ return rc;
+
+ init_completion(&info->negotiate_completion);
+ info->negotiate_done = false;
+ rc = smbd_post_send_negotiate_req(info);
+ if (rc)
+ return rc;
+
+ rc = wait_for_completion_interruptible_timeout(
+ &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ);
+ log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc);
+
+ if (info->negotiate_done)
+ return 0;
+
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ else if (rc == -ERESTARTSYS)
+ rc = -EINTR;
+ else
+ rc = -ENOTCONN;
+
+ return rc;
+}
+
+static void put_empty_packet(
+ struct smbd_connection *info, struct smbd_response *response)
+{
+ spin_lock(&info->empty_packet_queue_lock);
+ list_add_tail(&response->list, &info->empty_packet_queue);
+ info->count_empty_packet_queue++;
+ spin_unlock(&info->empty_packet_queue_lock);
+
+ queue_work(info->workqueue, &info->post_send_credits_work);
+}
+
+/*
+ * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1
+ * This is a queue for reassembling upper layer payload and present to upper
+ * layer. All the inncoming payload go to the reassembly queue, regardless of
+ * if reassembly is required. The uuper layer code reads from the queue for all
+ * incoming payloads.
+ * Put a received packet to the reassembly queue
+ * response: the packet received
+ * data_length: the size of payload in this packet
+ */
+static void enqueue_reassembly(
+ struct smbd_connection *info,
+ struct smbd_response *response,
+ int data_length)
+{
+ spin_lock(&info->reassembly_queue_lock);
+ list_add_tail(&response->list, &info->reassembly_queue);
+ info->reassembly_queue_length++;
+ /*
+ * Make sure reassembly_data_length is updated after list and
+ * reassembly_queue_length are updated. On the dequeue side
+ * reassembly_data_length is checked without a lock to determine
+ * if reassembly_queue_length and list is up to date
+ */
+ virt_wmb();
+ info->reassembly_data_length += data_length;
+ spin_unlock(&info->reassembly_queue_lock);
+ info->count_reassembly_queue++;
+ info->count_enqueue_reassembly_queue++;
+}
+
+/*
+ * Get the first entry at the front of reassembly queue
+ * Caller is responsible for locking
+ * return value: the first entry if any, NULL if queue is empty
+ */
+static struct smbd_response *_get_first_reassembly(struct smbd_connection *info)
+{
+ struct smbd_response *ret = NULL;
+
+ if (!list_empty(&info->reassembly_queue)) {
+ ret = list_first_entry(
+ &info->reassembly_queue,
+ struct smbd_response, list);
+ }
+ return ret;
+}
+
+static struct smbd_response *get_empty_queue_buffer(
+ struct smbd_connection *info)
+{
+ struct smbd_response *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->empty_packet_queue_lock, flags);
+ if (!list_empty(&info->empty_packet_queue)) {
+ ret = list_first_entry(
+ &info->empty_packet_queue,
+ struct smbd_response, list);
+ list_del(&ret->list);
+ info->count_empty_packet_queue--;
+ }
+ spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags);
+
+ return ret;
+}
+
+/*
+ * Get a receive buffer
+ * For each remote send, we need to post a receive. The receive buffers are
+ * pre-allocated in advance.
+ * return value: the receive buffer, NULL if none is available
+ */
+static struct smbd_response *get_receive_buffer(struct smbd_connection *info)
+{
+ struct smbd_response *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->receive_queue_lock, flags);
+ if (!list_empty(&info->receive_queue)) {
+ ret = list_first_entry(
+ &info->receive_queue,
+ struct smbd_response, list);
+ list_del(&ret->list);
+ info->count_receive_queue--;
+ info->count_get_receive_buffer++;
+ }
+ spin_unlock_irqrestore(&info->receive_queue_lock, flags);
+
+ return ret;
+}
+
+/*
+ * Return a receive buffer
+ * Upon returning of a receive buffer, we can post new receive and extend
+ * more receive credits to remote peer. This is done immediately after a
+ * receive buffer is returned.
+ */
+static void put_receive_buffer(
+ struct smbd_connection *info, struct smbd_response *response)
+{
+ unsigned long flags;
+
+ ib_dma_unmap_single(info->id->device, response->sge.addr,
+ response->sge.length, DMA_FROM_DEVICE);
+
+ spin_lock_irqsave(&info->receive_queue_lock, flags);
+ list_add_tail(&response->list, &info->receive_queue);
+ info->count_receive_queue++;
+ info->count_put_receive_buffer++;
+ spin_unlock_irqrestore(&info->receive_queue_lock, flags);
+
+ queue_work(info->workqueue, &info->post_send_credits_work);
+}
+
+/* Preallocate all receive buffer on transport establishment */
+static int allocate_receive_buffers(struct smbd_connection *info, int num_buf)
+{
+ int i;
+ struct smbd_response *response;
+
+ INIT_LIST_HEAD(&info->reassembly_queue);
+ spin_lock_init(&info->reassembly_queue_lock);
+ info->reassembly_data_length = 0;
+ info->reassembly_queue_length = 0;
+
+ INIT_LIST_HEAD(&info->receive_queue);
+ spin_lock_init(&info->receive_queue_lock);
+ info->count_receive_queue = 0;
+
+ INIT_LIST_HEAD(&info->empty_packet_queue);
+ spin_lock_init(&info->empty_packet_queue_lock);
+ info->count_empty_packet_queue = 0;
+
+ init_waitqueue_head(&info->wait_receive_queues);
+
+ for (i = 0; i < num_buf; i++) {
+ response = mempool_alloc(info->response_mempool, GFP_KERNEL);
+ if (!response)
+ goto allocate_failed;
+
+ response->info = info;
+ list_add_tail(&response->list, &info->receive_queue);
+ info->count_receive_queue++;
+ }
+
+ return 0;
+
+allocate_failed:
+ while (!list_empty(&info->receive_queue)) {
+ response = list_first_entry(
+ &info->receive_queue,
+ struct smbd_response, list);
+ list_del(&response->list);
+ info->count_receive_queue--;
+
+ mempool_free(response, info->response_mempool);
+ }
+ return -ENOMEM;
+}
+
+static void destroy_receive_buffers(struct smbd_connection *info)
+{
+ struct smbd_response *response;
+
+ while ((response = get_receive_buffer(info)))
+ mempool_free(response, info->response_mempool);
+
+ while ((response = get_empty_queue_buffer(info)))
+ mempool_free(response, info->response_mempool);
+}
+
+/*
+ * Check and send an immediate or keep alive packet
+ * The condition to send those packets are defined in [MS-SMBD] 3.1.1.1
+ * Connection.KeepaliveRequested and Connection.SendImmediate
+ * The idea is to extend credits to server as soon as it becomes available
+ */
+static void send_immediate_work(struct work_struct *work)
+{
+ struct smbd_connection *info = container_of(
+ work, struct smbd_connection,
+ send_immediate_work.work);
+
+ if (info->keep_alive_requested == KEEP_ALIVE_PENDING ||
+ info->send_immediate) {
+ log_keep_alive(INFO, "send an empty message\n");
+ smbd_post_send_empty(info);
+ }
+}
+
+/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */
+static void idle_connection_timer(struct work_struct *work)
+{
+ struct smbd_connection *info = container_of(
+ work, struct smbd_connection,
+ idle_timer_work.work);
+
+ if (info->keep_alive_requested != KEEP_ALIVE_NONE) {
+ log_keep_alive(ERR,
+ "error status info->keep_alive_requested=%d\n",
+ info->keep_alive_requested);
+ smbd_disconnect_rdma_connection(info);
+ return;
+ }
+
+ log_keep_alive(INFO, "about to send an empty idle message\n");
+ smbd_post_send_empty(info);
+
+ /* Setup the next idle timeout work */
+ queue_delayed_work(info->workqueue, &info->idle_timer_work,
+ info->keep_alive_interval*HZ);
+}
+
+/* Destroy this SMBD connection, called from upper layer */
+void smbd_destroy(struct smbd_connection *info)
+{
+ log_rdma_event(INFO, "destroying rdma session\n");
+
+ /* Kick off the disconnection process */
+ smbd_disconnect_rdma_connection(info);
+
+ log_rdma_event(INFO, "wait for transport being destroyed\n");
+ wait_event(info->wait_destroy,
+ info->transport_status == SMBD_DESTROYED);
+
+ destroy_workqueue(info->workqueue);
+ log_rdma_event(INFO, "rdma session destroyed\n");
+ kfree(info);
+}
+
+/*
+ * Reconnect this SMBD connection, called from upper layer
+ * return value: 0 on success, or actual error code
+ */
+int smbd_reconnect(struct TCP_Server_Info *server)
+{
+ log_rdma_event(INFO, "reconnecting rdma session\n");
+
+ if (!server->smbd_conn) {
+ log_rdma_event(INFO, "rdma session already destroyed\n");
+ goto create_conn;
+ }
+
+ /*
+ * This is possible if transport is disconnected and we haven't received
+ * notification from RDMA, but upper layer has detected timeout
+ */
+ if (server->smbd_conn->transport_status == SMBD_CONNECTED) {
+ log_rdma_event(INFO, "disconnecting transport\n");
+ smbd_disconnect_rdma_connection(server->smbd_conn);
+ }
+
+ /* wait until the transport is destroyed */
+ if (!wait_event_timeout(server->smbd_conn->wait_destroy,
+ server->smbd_conn->transport_status == SMBD_DESTROYED, 5*HZ))
+ return -EAGAIN;
+
+ destroy_workqueue(server->smbd_conn->workqueue);
+ kfree(server->smbd_conn);
+
+create_conn:
+ log_rdma_event(INFO, "creating rdma session\n");
+ server->smbd_conn = smbd_get_connection(
+ server, (struct sockaddr *) &server->dstaddr);
+
+ if (server->smbd_conn)
+ cifs_dbg(VFS, "RDMA transport re-established\n");
+
+ return server->smbd_conn ? 0 : -ENOENT;
+}
+
+static void destroy_caches_and_workqueue(struct smbd_connection *info)
+{
+ destroy_receive_buffers(info);
+ destroy_workqueue(info->workqueue);
+ mempool_destroy(info->response_mempool);
+ kmem_cache_destroy(info->response_cache);
+ mempool_destroy(info->request_mempool);
+ kmem_cache_destroy(info->request_cache);
+}
+
+#define MAX_NAME_LEN 80
+static int allocate_caches_and_workqueue(struct smbd_connection *info)
+{
+ char name[MAX_NAME_LEN];
+ int rc;
+
+ snprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
+ info->request_cache =
+ kmem_cache_create(
+ name,
+ sizeof(struct smbd_request) +
+ sizeof(struct smbd_data_transfer),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!info->request_cache)
+ return -ENOMEM;
+
+ info->request_mempool =
+ mempool_create(info->send_credit_target, mempool_alloc_slab,
+ mempool_free_slab, info->request_cache);
+ if (!info->request_mempool)
+ goto out1;
+
+ snprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
+ info->response_cache =
+ kmem_cache_create(
+ name,
+ sizeof(struct smbd_response) +
+ info->max_receive_size,
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!info->response_cache)
+ goto out2;
+
+ info->response_mempool =
+ mempool_create(info->receive_credit_max, mempool_alloc_slab,
+ mempool_free_slab, info->response_cache);
+ if (!info->response_mempool)
+ goto out3;
+
+ snprintf(name, MAX_NAME_LEN, "smbd_%p", info);
+ info->workqueue = create_workqueue(name);
+ if (!info->workqueue)
+ goto out4;
+
+ rc = allocate_receive_buffers(info, info->receive_credit_max);
+ if (rc) {
+ log_rdma_event(ERR, "failed to allocate receive buffers\n");
+ goto out5;
+ }
+
+ return 0;
+
+out5:
+ destroy_workqueue(info->workqueue);
+out4:
+ mempool_destroy(info->response_mempool);
+out3:
+ kmem_cache_destroy(info->response_cache);
+out2:
+ mempool_destroy(info->request_mempool);
+out1:
+ kmem_cache_destroy(info->request_cache);
+ return -ENOMEM;
+}
+
+/* Create a SMBD connection, called by upper layer */
+static struct smbd_connection *_smbd_get_connection(
+ struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port)
+{
+ int rc;
+ struct smbd_connection *info;
+ struct rdma_conn_param conn_param;
+ struct ib_qp_init_attr qp_attr;
+ struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr;
+ struct ib_port_immutable port_immutable;
+ u32 ird_ord_hdr[2];
+
+ info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL);
+ if (!info)
+ return NULL;
+
+ info->transport_status = SMBD_CONNECTING;
+ rc = smbd_ia_open(info, dstaddr, port);
+ if (rc) {
+ log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc);
+ goto create_id_failed;
+ }
+
+ if (smbd_send_credit_target > info->id->device->attrs.max_cqe ||
+ smbd_send_credit_target > info->id->device->attrs.max_qp_wr) {
+ log_rdma_event(ERR,
+ "consider lowering send_credit_target = %d. "
+ "Possible CQE overrun, device "
+ "reporting max_cpe %d max_qp_wr %d\n",
+ smbd_send_credit_target,
+ info->id->device->attrs.max_cqe,
+ info->id->device->attrs.max_qp_wr);
+ goto config_failed;
+ }
+
+ if (smbd_receive_credit_max > info->id->device->attrs.max_cqe ||
+ smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) {
+ log_rdma_event(ERR,
+ "consider lowering receive_credit_max = %d. "
+ "Possible CQE overrun, device "
+ "reporting max_cpe %d max_qp_wr %d\n",
+ smbd_receive_credit_max,
+ info->id->device->attrs.max_cqe,
+ info->id->device->attrs.max_qp_wr);
+ goto config_failed;
+ }
+
+ info->receive_credit_max = smbd_receive_credit_max;
+ info->send_credit_target = smbd_send_credit_target;
+ info->max_send_size = smbd_max_send_size;
+ info->max_fragmented_recv_size = smbd_max_fragmented_recv_size;
+ info->max_receive_size = smbd_max_receive_size;
+ info->keep_alive_interval = smbd_keep_alive_interval;
+
+ if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SGE) {
+ log_rdma_event(ERR,
+ "warning: device max_send_sge = %d too small\n",
+ info->id->device->attrs.max_send_sge);
+ log_rdma_event(ERR, "Queue Pair creation may fail\n");
+ }
+ if (info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_SGE) {
+ log_rdma_event(ERR,
+ "warning: device max_recv_sge = %d too small\n",
+ info->id->device->attrs.max_recv_sge);
+ log_rdma_event(ERR, "Queue Pair creation may fail\n");
+ }
+
+ info->send_cq = NULL;
+ info->recv_cq = NULL;
+ info->send_cq = ib_alloc_cq(info->id->device, info,
+ info->send_credit_target, 0, IB_POLL_SOFTIRQ);
+ if (IS_ERR(info->send_cq)) {
+ info->send_cq = NULL;
+ goto alloc_cq_failed;
+ }
+
+ info->recv_cq = ib_alloc_cq(info->id->device, info,
+ info->receive_credit_max, 0, IB_POLL_SOFTIRQ);
+ if (IS_ERR(info->recv_cq)) {
+ info->recv_cq = NULL;
+ goto alloc_cq_failed;
+ }
+
+ memset(&qp_attr, 0, sizeof(qp_attr));
+ qp_attr.event_handler = smbd_qp_async_error_upcall;
+ qp_attr.qp_context = info;
+ qp_attr.cap.max_send_wr = info->send_credit_target;
+ qp_attr.cap.max_recv_wr = info->receive_credit_max;
+ qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SGE;
+ qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_SGE;
+ qp_attr.cap.max_inline_data = 0;
+ qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
+ qp_attr.qp_type = IB_QPT_RC;
+ qp_attr.send_cq = info->send_cq;
+ qp_attr.recv_cq = info->recv_cq;
+ qp_attr.port_num = ~0;
+
+ rc = rdma_create_qp(info->id, info->pd, &qp_attr);
+ if (rc) {
+ log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc);
+ goto create_qp_failed;
+ }
+
+ memset(&conn_param, 0, sizeof(conn_param));
+ conn_param.initiator_depth = 0;
+
+ conn_param.responder_resources =
+ info->id->device->attrs.max_qp_rd_atom
+ < SMBD_CM_RESPONDER_RESOURCES ?
+ info->id->device->attrs.max_qp_rd_atom :
+ SMBD_CM_RESPONDER_RESOURCES;
+ info->responder_resources = conn_param.responder_resources;
+ log_rdma_mr(INFO, "responder_resources=%d\n",
+ info->responder_resources);
+
+ /* Need to send IRD/ORD in private data for iWARP */
+ info->id->device->get_port_immutable(
+ info->id->device, info->id->port_num, &port_immutable);
+ if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) {
+ ird_ord_hdr[0] = info->responder_resources;
+ ird_ord_hdr[1] = 1;
+ conn_param.private_data = ird_ord_hdr;
+ conn_param.private_data_len = sizeof(ird_ord_hdr);
+ } else {
+ conn_param.private_data = NULL;
+ conn_param.private_data_len = 0;
+ }
+
+ conn_param.retry_count = SMBD_CM_RETRY;
+ conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY;
+ conn_param.flow_control = 0;
+ init_waitqueue_head(&info->wait_destroy);
+
+ log_rdma_event(INFO, "connecting to IP %pI4 port %d\n",
+ &addr_in->sin_addr, port);
+
+ init_waitqueue_head(&info->conn_wait);
+ rc = rdma_connect(info->id, &conn_param);
+ if (rc) {
+ log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc);
+ goto rdma_connect_failed;
+ }
+
+ wait_event_interruptible(
+ info->conn_wait, info->transport_status != SMBD_CONNECTING);
+
+ if (info->transport_status != SMBD_CONNECTED) {
+ log_rdma_event(ERR, "rdma_connect failed port=%d\n", port);
+ goto rdma_connect_failed;
+ }
+
+ log_rdma_event(INFO, "rdma_connect connected\n");
+
+ rc = allocate_caches_and_workqueue(info);
+ if (rc) {
+ log_rdma_event(ERR, "cache allocation failed\n");
+ goto allocate_cache_failed;
+ }
+
+ init_waitqueue_head(&info->wait_send_queue);
+ init_waitqueue_head(&info->wait_reassembly_queue);
+
+ INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer);
+ INIT_DELAYED_WORK(&info->send_immediate_work, send_immediate_work);
+ queue_delayed_work(info->workqueue, &info->idle_timer_work,
+ info->keep_alive_interval*HZ);
+
+ init_waitqueue_head(&info->wait_smbd_send_pending);
+ info->smbd_send_pending = 0;
+
+ init_waitqueue_head(&info->wait_smbd_recv_pending);
+ info->smbd_recv_pending = 0;
+
+ init_waitqueue_head(&info->wait_send_pending);
+ atomic_set(&info->send_pending, 0);
+
+ init_waitqueue_head(&info->wait_send_payload_pending);
+ atomic_set(&info->send_payload_pending, 0);
+
+ INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work);
+ INIT_WORK(&info->destroy_work, smbd_destroy_rdma_work);
+ INIT_WORK(&info->recv_done_work, smbd_recv_done_work);
+ INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits);
+ info->new_credits_offered = 0;
+ spin_lock_init(&info->lock_new_credits_offered);
+
+ rc = smbd_negotiate(info);
+ if (rc) {
+ log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc);
+ goto negotiation_failed;
+ }
+
+ rc = allocate_mr_list(info);
+ if (rc) {
+ log_rdma_mr(ERR, "memory registration allocation failed\n");
+ goto allocate_mr_failed;
+ }
+
+ return info;
+
+allocate_mr_failed:
+ /* At this point, need to a full transport shutdown */
+ smbd_destroy(info);
+ return NULL;
+
+negotiation_failed:
+ cancel_delayed_work_sync(&info->idle_timer_work);
+ destroy_caches_and_workqueue(info);
+ info->transport_status = SMBD_NEGOTIATE_FAILED;
+ init_waitqueue_head(&info->conn_wait);
+ rdma_disconnect(info->id);
+ wait_event(info->conn_wait,
+ info->transport_status == SMBD_DISCONNECTED);
+
+allocate_cache_failed:
+rdma_connect_failed:
+ rdma_destroy_qp(info->id);
+
+create_qp_failed:
+alloc_cq_failed:
+ if (info->send_cq)
+ ib_free_cq(info->send_cq);
+ if (info->recv_cq)
+ ib_free_cq(info->recv_cq);
+
+config_failed:
+ ib_dealloc_pd(info->pd);
+ rdma_destroy_id(info->id);
+
+create_id_failed:
+ kfree(info);
+ return NULL;
+}
+
+struct smbd_connection *smbd_get_connection(
+ struct TCP_Server_Info *server, struct sockaddr *dstaddr)
+{
+ struct smbd_connection *ret;
+ int port = SMBD_PORT;
+
+try_again:
+ ret = _smbd_get_connection(server, dstaddr, port);
+
+ /* Try SMB_PORT if SMBD_PORT doesn't work */
+ if (!ret && port == SMBD_PORT) {
+ port = SMB_PORT;
+ goto try_again;
+ }
+ return ret;
+}
+
+/*
+ * Receive data from receive reassembly queue
+ * All the incoming data packets are placed in reassembly queue
+ * buf: the buffer to read data into
+ * size: the length of data to read
+ * return value: actual data read
+ * Note: this implementation copies the data from reassebmly queue to receive
+ * buffers used by upper layer. This is not the optimal code path. A better way
+ * to do it is to not have upper layer allocate its receive buffers but rather
+ * borrow the buffer from reassembly queue, and return it after data is
+ * consumed. But this will require more changes to upper layer code, and also
+ * need to consider packet boundaries while they still being reassembled.
+ */
+static int smbd_recv_buf(struct smbd_connection *info, char *buf,
+ unsigned int size)
+{
+ struct smbd_response *response;
+ struct smbd_data_transfer *data_transfer;
+ int to_copy, to_read, data_read, offset;
+ u32 data_length, remaining_data_length, data_offset;
+ int rc;
+
+again:
+ if (info->transport_status != SMBD_CONNECTED) {
+ log_read(ERR, "disconnected\n");
+ return -ENODEV;
+ }
+
+ /*
+ * No need to hold the reassembly queue lock all the time as we are
+ * 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
+ */
+ log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size,
+ info->reassembly_data_length);
+ if (info->reassembly_data_length >= size) {
+ int queue_length;
+ int queue_removed = 0;
+
+ /*
+ * Need to make sure reassembly_data_length is read before
+ * reading reassembly_queue_length and calling
+ * _get_first_reassembly. This call is lock free
+ * as we never read at the end of the queue which are being
+ * updated in SOFTIRQ as more data is received
+ */
+ virt_rmb();
+ queue_length = info->reassembly_queue_length;
+ data_read = 0;
+ to_read = size;
+ offset = info->first_entry_offset;
+ while (data_read < size) {
+ response = _get_first_reassembly(info);
+ data_transfer = smbd_response_payload(response);
+ data_length = le32_to_cpu(data_transfer->data_length);
+ remaining_data_length =
+ le32_to_cpu(
+ data_transfer->remaining_data_length);
+ data_offset = le32_to_cpu(data_transfer->data_offset);
+
+ /*
+ * The upper layer expects RFC1002 length at the
+ * beginning of the payload. Return it to indicate
+ * the total length of the packet. This minimize the
+ * change to upper layer packet processing logic. This
+ * will be eventually remove when an intermediate
+ * transport layer is added
+ */
+ if (response->first_segment && size == 4) {
+ unsigned int rfc1002_len =
+ data_length + remaining_data_length;
+ *((__be32 *)buf) = cpu_to_be32(rfc1002_len);
+ data_read = 4;
+ response->first_segment = false;
+ log_read(INFO, "returning rfc1002 length %d\n",
+ rfc1002_len);
+ goto read_rfc1002_done;
+ }
+
+ to_copy = min_t(int, data_length - offset, to_read);
+ memcpy(
+ buf + data_read,
+ (char *)data_transfer + data_offset + offset,
+ to_copy);
+
+ /* move on to the next buffer? */
+ if (to_copy == data_length - offset) {
+ queue_length--;
+ /*
+ * No need to lock if we are not at the
+ * end of the queue
+ */
+ if (queue_length)
+ list_del(&response->list);
+ else {
+ spin_lock_irq(
+ &info->reassembly_queue_lock);
+ list_del(&response->list);
+ spin_unlock_irq(
+ &info->reassembly_queue_lock);
+ }
+ queue_removed++;
+ info->count_reassembly_queue--;
+ info->count_dequeue_reassembly_queue++;
+ put_receive_buffer(info, response);
+ offset = 0;
+ log_read(INFO, "put_receive_buffer offset=0\n");
+ } else
+ offset += to_copy;
+
+ to_read -= to_copy;
+ data_read += to_copy;
+
+ log_read(INFO, "_get_first_reassembly memcpy %d bytes "
+ "data_transfer_length-offset=%d after that "
+ "to_read=%d data_read=%d offset=%d\n",
+ to_copy, data_length - offset,
+ to_read, data_read, offset);
+ }
+
+ spin_lock_irq(&info->reassembly_queue_lock);
+ info->reassembly_data_length -= data_read;
+ info->reassembly_queue_length -= queue_removed;
+ spin_unlock_irq(&info->reassembly_queue_lock);
+
+ info->first_entry_offset = offset;
+ log_read(INFO, "returning to thread data_read=%d "
+ "reassembly_data_length=%d first_entry_offset=%d\n",
+ data_read, info->reassembly_data_length,
+ info->first_entry_offset);
+read_rfc1002_done:
+ return data_read;
+ }
+
+ log_read(INFO, "wait_event on more data\n");
+ rc = wait_event_interruptible(
+ info->wait_reassembly_queue,
+ info->reassembly_data_length >= size ||
+ info->transport_status != SMBD_CONNECTED);
+ /* Don't return any data if interrupted */
+ if (rc)
+ return -ENODEV;
+
+ goto again;
+}
+
+/*
+ * Receive a page from receive reassembly queue
+ * page: the page to read data into
+ * to_read: the length of data to read
+ * return value: actual data read
+ */
+static int smbd_recv_page(struct smbd_connection *info,
+ struct page *page, unsigned int page_offset,
+ unsigned int to_read)
+{
+ int ret;
+ char *to_address;
+ void *page_address;
+
+ /* make sure we have the page ready for read */
+ ret = wait_event_interruptible(
+ info->wait_reassembly_queue,
+ info->reassembly_data_length >= to_read ||
+ info->transport_status != SMBD_CONNECTED);
+ if (ret)
+ return ret;
+
+ /* now we can read from reassembly queue and not sleep */
+ page_address = kmap_atomic(page);
+ to_address = (char *) page_address + page_offset;
+
+ log_read(INFO, "reading from page=%p address=%p to_read=%d\n",
+ page, to_address, to_read);
+
+ ret = smbd_recv_buf(info, to_address, to_read);
+ kunmap_atomic(page_address);
+
+ return ret;
+}
+
+/*
+ * Receive data from transport
+ * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC
+ * return: total bytes read, or 0. SMB Direct will not do partial read.
+ */
+int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
+{
+ char *buf;
+ struct page *page;
+ unsigned int to_read, page_offset;
+ int rc;
+
+ info->smbd_recv_pending++;
+
+ switch (msg->msg_iter.type) {
+ case READ | ITER_KVEC:
+ buf = msg->msg_iter.kvec->iov_base;
+ to_read = msg->msg_iter.kvec->iov_len;
+ rc = smbd_recv_buf(info, buf, to_read);
+ break;
+
+ case READ | ITER_BVEC:
+ page = msg->msg_iter.bvec->bv_page;
+ page_offset = msg->msg_iter.bvec->bv_offset;
+ to_read = msg->msg_iter.bvec->bv_len;
+ rc = smbd_recv_page(info, page, page_offset, to_read);
+ break;
+
+ default:
+ /* It's a bug in upper layer to get there */
+ cifs_dbg(VFS, "CIFS: invalid msg type %d\n",
+ msg->msg_iter.type);
+ rc = -EINVAL;
+ }
+
+ info->smbd_recv_pending--;
+ wake_up(&info->wait_smbd_recv_pending);
+
+ /* SMBDirect will read it all or nothing */
+ if (rc > 0)
+ msg->msg_iter.count = 0;
+ return rc;
+}
+
+/*
+ * Send data to transport
+ * Each rqst is transported as a SMBDirect payload
+ * rqst: the data to write
+ * return value: 0 if successfully write, otherwise error code
+ */
+int smbd_send(struct TCP_Server_Info *server,
+ int num_rqst, struct smb_rqst *rqst_array)
+{
+ struct smbd_connection *info = server->smbd_conn;
+ struct kvec vec;
+ int nvecs;
+ int size;
+ unsigned int buflen, remaining_data_length;
+ int start, i, j;
+ int max_iov_size =
+ info->max_send_size - sizeof(struct smbd_data_transfer);
+ struct kvec *iov;
+ int rc;
+ struct smb_rqst *rqst;
+ int rqst_idx;
+
+ info->smbd_send_pending++;
+ if (info->transport_status != SMBD_CONNECTED) {
+ rc = -ENODEV;
+ goto done;
+ }
+
+ /*
+ * Add in the page array if there is one. The caller needs to set
+ * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and
+ * ends at page boundary
+ */
+ remaining_data_length = 0;
+ for (i = 0; i < num_rqst; i++)
+ remaining_data_length += smb_rqst_len(server, &rqst_array[i]);
+
+ if (remaining_data_length + sizeof(struct smbd_data_transfer) >
+ info->max_fragmented_send_size) {
+ log_write(ERR, "payload size %d > max size %d\n",
+ remaining_data_length, info->max_fragmented_send_size);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ rqst_idx = 0;
+
+next_rqst:
+ rqst = &rqst_array[rqst_idx];
+ iov = rqst->rq_iov;
+
+ cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n",
+ rqst_idx, smb_rqst_len(server, rqst));
+ for (i = 0; i < rqst->rq_nvec; i++)
+ dump_smb(iov[i].iov_base, iov[i].iov_len);
+
+
+ log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d "
+ "rq_tailsz=%d buflen=%lu\n",
+ rqst_idx, rqst->rq_nvec, rqst->rq_npages, rqst->rq_pagesz,
+ rqst->rq_tailsz, smb_rqst_len(server, rqst));
+
+ 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);
+ log_write(INFO, "sending iov[] from start=%d "
+ "i=%d nvecs=%d "
+ "remaining_data_length=%d\n",
+ start, i, i-start,
+ remaining_data_length);
+ rc = smbd_post_send_data(
+ info, &iov[start], i-start,
+ remaining_data_length);
+ if (rc)
+ goto done;
+ } else {
+ /* iov[start] is too big, break it */
+ nvecs = (buflen+max_iov_size-1)/max_iov_size;
+ log_write(INFO, "iov[%d] iov_base=%p buflen=%d"
+ " break to %d vectors\n",
+ start, iov[start].iov_base,
+ buflen, nvecs);
+ for (j = 0; j < nvecs; j++) {
+ vec.iov_base =
+ (char *)iov[start].iov_base +
+ j*max_iov_size;
+ vec.iov_len = max_iov_size;
+ if (j == nvecs-1)
+ vec.iov_len =
+ buflen -
+ max_iov_size*(nvecs-1);
+ remaining_data_length -= vec.iov_len;
+ log_write(INFO,
+ "sending vec j=%d iov_base=%p"
+ " iov_len=%zu "
+ "remaining_data_length=%d\n",
+ j, vec.iov_base, vec.iov_len,
+ remaining_data_length);
+ rc = smbd_post_send_data(
+ info, &vec, 1,
+ remaining_data_length);
+ if (rc)
+ goto done;
+ }
+ i++;
+ if (i == rqst->rq_nvec)
+ break;
+ }
+ start = i;
+ buflen = 0;
+ } else {
+ i++;
+ if (i == rqst->rq_nvec) {
+ /* send out all remaining vecs */
+ remaining_data_length -= buflen;
+ log_write(INFO,
+ "sending iov[] from start=%d i=%d "
+ "nvecs=%d remaining_data_length=%d\n",
+ start, i, i-start,
+ remaining_data_length);
+ rc = smbd_post_send_data(info, &iov[start],
+ i-start, remaining_data_length);
+ if (rc)
+ goto done;
+ break;
+ }
+ }
+ log_write(INFO, "looping i=%d buflen=%d\n", i, buflen);
+ }
+
+ /* now sending pages if there are any */
+ for (i = 0; i < rqst->rq_npages; i++) {
+ unsigned int offset;
+
+ rqst_page_get_length(rqst, i, &buflen, &offset);
+ nvecs = (buflen + max_iov_size - 1) / max_iov_size;
+ log_write(INFO, "sending pages buflen=%d nvecs=%d\n",
+ buflen, nvecs);
+ for (j = 0; j < nvecs; j++) {
+ size = max_iov_size;
+ if (j == nvecs-1)
+ size = buflen - j*max_iov_size;
+ remaining_data_length -= size;
+ log_write(INFO, "sending pages i=%d offset=%d size=%d"
+ " remaining_data_length=%d\n",
+ i, j*max_iov_size+offset, size,
+ remaining_data_length);
+ rc = smbd_post_send_page(
+ info, rqst->rq_pages[i],
+ j*max_iov_size + offset,
+ size, remaining_data_length);
+ if (rc)
+ goto done;
+ }
+ }
+
+ rqst_idx++;
+ if (rqst_idx < num_rqst)
+ goto next_rqst;
+
+done:
+ /*
+ * As an optimization, we don't wait for individual I/O to finish
+ * before sending the next one.
+ * Send them all and wait for pending send count to get to 0
+ * that means all the I/Os have been out and we are good to return
+ */
+
+ wait_event(info->wait_send_payload_pending,
+ atomic_read(&info->send_payload_pending) == 0);
+
+ info->smbd_send_pending--;
+ wake_up(&info->wait_smbd_send_pending);
+
+ return rc;
+}
+
+static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc)
+{
+ struct smbd_mr *mr;
+ struct ib_cqe *cqe;
+
+ if (wc->status) {
+ log_rdma_mr(ERR, "status=%d\n", wc->status);
+ cqe = wc->wr_cqe;
+ mr = container_of(cqe, struct smbd_mr, cqe);
+ smbd_disconnect_rdma_connection(mr->conn);
+ }
+}
+
+/*
+ * The work queue function that recovers MRs
+ * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used
+ * again. Both calls are slow, so finish them in a workqueue. This will not
+ * block I/O path.
+ * There is one workqueue that recovers MRs, there is no need to lock as the
+ * I/O requests calling smbd_register_mr will never update the links in the
+ * mr_list.
+ */
+static void smbd_mr_recovery_work(struct work_struct *work)
+{
+ struct smbd_connection *info =
+ container_of(work, struct smbd_connection, mr_recovery_work);
+ struct smbd_mr *smbdirect_mr;
+ int rc;
+
+ list_for_each_entry(smbdirect_mr, &info->mr_list, list) {
+ if (smbdirect_mr->state == MR_INVALIDATED ||
+ smbdirect_mr->state == MR_ERROR) {
+
+ /* recover this MR entry */
+ rc = ib_dereg_mr(smbdirect_mr->mr);
+ if (rc) {
+ log_rdma_mr(ERR,
+ "ib_dereg_mr failed rc=%x\n",
+ rc);
+ smbd_disconnect_rdma_connection(info);
+ continue;
+ }
+
+ smbdirect_mr->mr = ib_alloc_mr(
+ info->pd, info->mr_type,
+ info->max_frmr_depth);
+ if (IS_ERR(smbdirect_mr->mr)) {
+ log_rdma_mr(ERR,
+ "ib_alloc_mr failed mr_type=%x "
+ "max_frmr_depth=%x\n",
+ info->mr_type,
+ info->max_frmr_depth);
+ smbd_disconnect_rdma_connection(info);
+ continue;
+ }
+
+ if (smbdirect_mr->state == MR_INVALIDATED)
+ ib_dma_unmap_sg(
+ info->id->device, smbdirect_mr->sgl,
+ smbdirect_mr->sgl_count,
+ smbdirect_mr->dir);
+
+ smbdirect_mr->state = MR_READY;
+
+ /* smbdirect_mr->state is updated by this function
+ * and is read and updated by I/O issuing CPUs trying
+ * to get a MR, the call to atomic_inc_return
+ * implicates a memory barrier and guarantees this
+ * value is updated before waking up any calls to
+ * get_mr() from the I/O issuing CPUs
+ */
+ if (atomic_inc_return(&info->mr_ready_count) == 1)
+ wake_up_interruptible(&info->wait_mr);
+ }
+ }
+}
+
+static void destroy_mr_list(struct smbd_connection *info)
+{
+ struct smbd_mr *mr, *tmp;
+
+ cancel_work_sync(&info->mr_recovery_work);
+ list_for_each_entry_safe(mr, tmp, &info->mr_list, list) {
+ if (mr->state == MR_INVALIDATED)
+ ib_dma_unmap_sg(info->id->device, mr->sgl,
+ mr->sgl_count, mr->dir);
+ ib_dereg_mr(mr->mr);
+ kfree(mr->sgl);
+ kfree(mr);
+ }
+}
+
+/*
+ * Allocate MRs used for RDMA read/write
+ * The number of MRs will not exceed hardware capability in responder_resources
+ * All MRs are kept in mr_list. The MR can be recovered after it's used
+ * Recovery is done in smbd_mr_recovery_work. The content of list entry changes
+ * as MRs are used and recovered for I/O, but the list links will not change
+ */
+static int allocate_mr_list(struct smbd_connection *info)
+{
+ int i;
+ struct smbd_mr *smbdirect_mr, *tmp;
+
+ INIT_LIST_HEAD(&info->mr_list);
+ init_waitqueue_head(&info->wait_mr);
+ spin_lock_init(&info->mr_list_lock);
+ atomic_set(&info->mr_ready_count, 0);
+ atomic_set(&info->mr_used_count, 0);
+ init_waitqueue_head(&info->wait_for_mr_cleanup);
+ /* Allocate more MRs (2x) than hardware responder_resources */
+ for (i = 0; i < info->responder_resources * 2; i++) {
+ smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL);
+ if (!smbdirect_mr)
+ goto out;
+ smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type,
+ info->max_frmr_depth);
+ if (IS_ERR(smbdirect_mr->mr)) {
+ log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x "
+ "max_frmr_depth=%x\n",
+ info->mr_type, info->max_frmr_depth);
+ goto out;
+ }
+ smbdirect_mr->sgl = kcalloc(
+ info->max_frmr_depth,
+ sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if (!smbdirect_mr->sgl) {
+ log_rdma_mr(ERR, "failed to allocate sgl\n");
+ ib_dereg_mr(smbdirect_mr->mr);
+ goto out;
+ }
+ smbdirect_mr->state = MR_READY;
+ smbdirect_mr->conn = info;
+
+ list_add_tail(&smbdirect_mr->list, &info->mr_list);
+ atomic_inc(&info->mr_ready_count);
+ }
+ INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work);
+ return 0;
+
+out:
+ kfree(smbdirect_mr);
+
+ list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) {
+ ib_dereg_mr(smbdirect_mr->mr);
+ kfree(smbdirect_mr->sgl);
+ kfree(smbdirect_mr);
+ }
+ return -ENOMEM;
+}
+
+/*
+ * Get a MR from mr_list. This function waits until there is at least one
+ * MR available in the list. It may access the list while the
+ * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock
+ * as they never modify the same places. However, there may be several CPUs
+ * issueing I/O trying to get MR at the same time, mr_list_lock is used to
+ * protect this situation.
+ */
+static struct smbd_mr *get_mr(struct smbd_connection *info)
+{
+ struct smbd_mr *ret;
+ int rc;
+again:
+ rc = wait_event_interruptible(info->wait_mr,
+ atomic_read(&info->mr_ready_count) ||
+ info->transport_status != SMBD_CONNECTED);
+ if (rc) {
+ log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc);
+ return NULL;
+ }
+
+ if (info->transport_status != SMBD_CONNECTED) {
+ log_rdma_mr(ERR, "info->transport_status=%x\n",
+ info->transport_status);
+ return NULL;
+ }
+
+ spin_lock(&info->mr_list_lock);
+ list_for_each_entry(ret, &info->mr_list, list) {
+ if (ret->state == MR_READY) {
+ ret->state = MR_REGISTERED;
+ spin_unlock(&info->mr_list_lock);
+ atomic_dec(&info->mr_ready_count);
+ atomic_inc(&info->mr_used_count);
+ return ret;
+ }
+ }
+
+ spin_unlock(&info->mr_list_lock);
+ /*
+ * It is possible that we could fail to get MR because other processes may
+ * try to acquire a MR at the same time. If this is the case, retry it.
+ */
+ goto again;
+}
+
+/*
+ * Register memory for RDMA read/write
+ * pages[]: the list of pages to register memory with
+ * num_pages: the number of pages to register
+ * tailsz: if non-zero, the bytes to register in the last page
+ * writing: true if this is a RDMA write (SMB read), false for RDMA read
+ * need_invalidate: true if this MR needs to be locally invalidated after I/O
+ * return value: the MR registered, NULL if failed.
+ */
+struct smbd_mr *smbd_register_mr(
+ struct smbd_connection *info, struct page *pages[], int num_pages,
+ int offset, int tailsz, bool writing, bool need_invalidate)
+{
+ struct smbd_mr *smbdirect_mr;
+ int rc, i;
+ enum dma_data_direction dir;
+ struct ib_reg_wr *reg_wr;
+
+ if (num_pages > info->max_frmr_depth) {
+ log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n",
+ num_pages, info->max_frmr_depth);
+ return NULL;
+ }
+
+ smbdirect_mr = get_mr(info);
+ if (!smbdirect_mr) {
+ log_rdma_mr(ERR, "get_mr returning NULL\n");
+ return NULL;
+ }
+ smbdirect_mr->need_invalidate = need_invalidate;
+ smbdirect_mr->sgl_count = num_pages;
+ sg_init_table(smbdirect_mr->sgl, num_pages);
+
+ log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n",
+ num_pages, offset, tailsz);
+
+ if (num_pages == 1) {
+ sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset);
+ goto skip_multiple_pages;
+ }
+
+ /* We have at least two pages to register */
+ sg_set_page(
+ &smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset);
+ i = 1;
+ while (i < num_pages - 1) {
+ sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0);
+ i++;
+ }
+ sg_set_page(&smbdirect_mr->sgl[i], pages[i],
+ tailsz ? tailsz : PAGE_SIZE, 0);
+
+skip_multiple_pages:
+ dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ smbdirect_mr->dir = dir;
+ rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir);
+ if (!rc) {
+ log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n",
+ num_pages, dir, rc);
+ goto dma_map_error;
+ }
+
+ rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages,
+ NULL, PAGE_SIZE);
+ if (rc != num_pages) {
+ log_rdma_mr(ERR,
+ "ib_map_mr_sg failed rc = %d num_pages = %x\n",
+ rc, num_pages);
+ goto map_mr_error;
+ }
+
+ ib_update_fast_reg_key(smbdirect_mr->mr,
+ ib_inc_rkey(smbdirect_mr->mr->rkey));
+ reg_wr = &smbdirect_mr->wr;
+ reg_wr->wr.opcode = IB_WR_REG_MR;
+ smbdirect_mr->cqe.done = register_mr_done;
+ reg_wr->wr.wr_cqe = &smbdirect_mr->cqe;
+ reg_wr->wr.num_sge = 0;
+ reg_wr->wr.send_flags = IB_SEND_SIGNALED;
+ reg_wr->mr = smbdirect_mr->mr;
+ reg_wr->key = smbdirect_mr->mr->rkey;
+ reg_wr->access = writing ?
+ IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
+ IB_ACCESS_REMOTE_READ;
+
+ /*
+ * There is no need for waiting for complemtion on ib_post_send
+ * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution
+ * on the next ib_post_send when we actaully send I/O to remote peer
+ */
+ rc = ib_post_send(info->id->qp, &reg_wr->wr, NULL);
+ if (!rc)
+ return smbdirect_mr;
+
+ log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n",
+ rc, reg_wr->key);
+
+ /* If all failed, attempt to recover this MR by setting it MR_ERROR*/
+map_mr_error:
+ ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgl,
+ smbdirect_mr->sgl_count, smbdirect_mr->dir);
+
+dma_map_error:
+ smbdirect_mr->state = MR_ERROR;
+ if (atomic_dec_and_test(&info->mr_used_count))
+ wake_up(&info->wait_for_mr_cleanup);
+
+ smbd_disconnect_rdma_connection(info);
+
+ return NULL;
+}
+
+static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc)
+{
+ struct smbd_mr *smbdirect_mr;
+ struct ib_cqe *cqe;
+
+ cqe = wc->wr_cqe;
+ smbdirect_mr = container_of(cqe, struct smbd_mr, cqe);
+ smbdirect_mr->state = MR_INVALIDATED;
+ if (wc->status != IB_WC_SUCCESS) {
+ log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status);
+ smbdirect_mr->state = MR_ERROR;
+ }
+ complete(&smbdirect_mr->invalidate_done);
+}
+
+/*
+ * Deregister a MR after I/O is done
+ * This function may wait if remote invalidation is not used
+ * and we have to locally invalidate the buffer to prevent data is being
+ * modified by remote peer after upper layer consumes it
+ */
+int smbd_deregister_mr(struct smbd_mr *smbdirect_mr)
+{
+ struct ib_send_wr *wr;
+ struct smbd_connection *info = smbdirect_mr->conn;
+ int rc = 0;
+
+ if (smbdirect_mr->need_invalidate) {
+ /* Need to finish local invalidation before returning */
+ wr = &smbdirect_mr->inv_wr;
+ wr->opcode = IB_WR_LOCAL_INV;
+ smbdirect_mr->cqe.done = local_inv_done;
+ wr->wr_cqe = &smbdirect_mr->cqe;
+ wr->num_sge = 0;
+ wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey;
+ wr->send_flags = IB_SEND_SIGNALED;
+
+ init_completion(&smbdirect_mr->invalidate_done);
+ rc = ib_post_send(info->id->qp, wr, NULL);
+ if (rc) {
+ log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc);
+ smbd_disconnect_rdma_connection(info);
+ goto done;
+ }
+ wait_for_completion(&smbdirect_mr->invalidate_done);
+ smbdirect_mr->need_invalidate = false;
+ } else
+ /*
+ * For remote invalidation, just set it to MR_INVALIDATED
+ * and defer to mr_recovery_work to recover the MR for next use
+ */
+ smbdirect_mr->state = MR_INVALIDATED;
+
+ /*
+ * Schedule the work to do MR recovery for future I/Os
+ * MR recovery is slow and we don't want it to block the current I/O
+ */
+ queue_work(info->workqueue, &info->mr_recovery_work);
+
+done:
+ if (atomic_dec_and_test(&info->mr_used_count))
+ wake_up(&info->wait_for_mr_cleanup);
+
+ return rc;
+}
diff --git a/fs/cifs/smbdirect.h b/fs/cifs/smbdirect.h
new file mode 100644
index 000000000..b5c240ff2
--- /dev/null
+++ b/fs/cifs/smbdirect.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2017, Microsoft Corporation.
+ *
+ * Author(s): Long Li <longli@microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ */
+#ifndef _SMBDIRECT_H
+#define _SMBDIRECT_H
+
+#ifdef CONFIG_CIFS_SMB_DIRECT
+#define cifs_rdma_enabled(server) ((server)->rdma)
+
+#include "cifsglob.h"
+#include <rdma/ib_verbs.h>
+#include <rdma/rdma_cm.h>
+#include <linux/mempool.h>
+
+extern int rdma_readwrite_threshold;
+extern int smbd_max_frmr_depth;
+extern int smbd_keep_alive_interval;
+extern int smbd_max_receive_size;
+extern int smbd_max_fragmented_recv_size;
+extern int smbd_max_send_size;
+extern int smbd_send_credit_target;
+extern int smbd_receive_credit_max;
+
+enum keep_alive_status {
+ KEEP_ALIVE_NONE,
+ KEEP_ALIVE_PENDING,
+ KEEP_ALIVE_SENT,
+};
+
+enum smbd_connection_status {
+ SMBD_CREATED,
+ SMBD_CONNECTING,
+ SMBD_CONNECTED,
+ SMBD_NEGOTIATE_FAILED,
+ SMBD_DISCONNECTING,
+ SMBD_DISCONNECTED,
+ SMBD_DESTROYED
+};
+
+/*
+ * The context for the SMBDirect transport
+ * Everything related to the transport is here. It has several logical parts
+ * 1. RDMA related structures
+ * 2. SMBDirect connection parameters
+ * 3. Memory registrations
+ * 4. Receive and reassembly queues for data receive path
+ * 5. mempools for allocating packets
+ */
+struct smbd_connection {
+ enum smbd_connection_status transport_status;
+
+ /* RDMA related */
+ struct rdma_cm_id *id;
+ struct ib_qp_init_attr qp_attr;
+ struct ib_pd *pd;
+ struct ib_cq *send_cq, *recv_cq;
+ struct ib_device_attr dev_attr;
+ int ri_rc;
+ struct completion ri_done;
+ wait_queue_head_t conn_wait;
+ wait_queue_head_t wait_destroy;
+
+ struct completion negotiate_completion;
+ bool negotiate_done;
+
+ struct work_struct destroy_work;
+ struct work_struct disconnect_work;
+ struct work_struct recv_done_work;
+ struct work_struct post_send_credits_work;
+
+ spinlock_t lock_new_credits_offered;
+ int new_credits_offered;
+
+ /* Connection parameters defined in [MS-SMBD] 3.1.1.1 */
+ int receive_credit_max;
+ int send_credit_target;
+ int max_send_size;
+ int max_fragmented_recv_size;
+ int max_fragmented_send_size;
+ int max_receive_size;
+ int keep_alive_interval;
+ int max_readwrite_size;
+ enum keep_alive_status keep_alive_requested;
+ int protocol;
+ atomic_t send_credits;
+ atomic_t receive_credits;
+ int receive_credit_target;
+ int fragment_reassembly_remaining;
+
+ /* Memory registrations */
+ /* Maximum number of RDMA read/write outstanding on this connection */
+ int responder_resources;
+ /* Maximum number of SGEs in a RDMA write/read */
+ int max_frmr_depth;
+ /*
+ * If payload is less than or equal to the threshold,
+ * use RDMA send/recv to send upper layer I/O.
+ * If payload is more than the threshold,
+ * use RDMA read/write through memory registration for I/O.
+ */
+ int rdma_readwrite_threshold;
+ enum ib_mr_type mr_type;
+ struct list_head mr_list;
+ spinlock_t mr_list_lock;
+ /* The number of available MRs ready for memory registration */
+ atomic_t mr_ready_count;
+ atomic_t mr_used_count;
+ wait_queue_head_t wait_mr;
+ struct work_struct mr_recovery_work;
+ /* Used by transport to wait until all MRs are returned */
+ wait_queue_head_t wait_for_mr_cleanup;
+
+ /* Activity accoutning */
+ /* Pending reqeusts issued from upper layer */
+ int smbd_send_pending;
+ wait_queue_head_t wait_smbd_send_pending;
+
+ int smbd_recv_pending;
+ wait_queue_head_t wait_smbd_recv_pending;
+
+ atomic_t send_pending;
+ wait_queue_head_t wait_send_pending;
+ atomic_t send_payload_pending;
+ wait_queue_head_t wait_send_payload_pending;
+
+ /* Receive queue */
+ struct list_head receive_queue;
+ int count_receive_queue;
+ spinlock_t receive_queue_lock;
+
+ struct list_head empty_packet_queue;
+ int count_empty_packet_queue;
+ spinlock_t empty_packet_queue_lock;
+
+ wait_queue_head_t wait_receive_queues;
+
+ /* Reassembly queue */
+ struct list_head reassembly_queue;
+ spinlock_t reassembly_queue_lock;
+ wait_queue_head_t wait_reassembly_queue;
+
+ /* total data length of reassembly queue */
+ int reassembly_data_length;
+ int reassembly_queue_length;
+ /* the offset to first buffer in reassembly queue */
+ int first_entry_offset;
+
+ bool send_immediate;
+
+ wait_queue_head_t wait_send_queue;
+
+ /*
+ * Indicate if we have received a full packet on the connection
+ * This is used to identify the first SMBD packet of a assembled
+ * payload (SMB packet) in reassembly queue so we can return a
+ * RFC1002 length to upper layer to indicate the length of the SMB
+ * packet received
+ */
+ bool full_packet_received;
+
+ struct workqueue_struct *workqueue;
+ struct delayed_work idle_timer_work;
+ struct delayed_work send_immediate_work;
+
+ /* Memory pool for preallocating buffers */
+ /* request pool for RDMA send */
+ struct kmem_cache *request_cache;
+ mempool_t *request_mempool;
+
+ /* response pool for RDMA receive */
+ struct kmem_cache *response_cache;
+ mempool_t *response_mempool;
+
+ /* for debug purposes */
+ unsigned int count_get_receive_buffer;
+ unsigned int count_put_receive_buffer;
+ unsigned int count_reassembly_queue;
+ unsigned int count_enqueue_reassembly_queue;
+ unsigned int count_dequeue_reassembly_queue;
+ unsigned int count_send_empty;
+};
+
+enum smbd_message_type {
+ SMBD_NEGOTIATE_RESP,
+ SMBD_TRANSFER_DATA,
+};
+
+#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001
+
+/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */
+struct smbd_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;
+
+/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */
+struct smbd_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;
+
+/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */
+struct smbd_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;
+
+/* The packet fields for a registered RDMA buffer */
+struct smbd_buffer_descriptor_v1 {
+ __le64 offset;
+ __le32 token;
+ __le32 length;
+} __packed;
+
+/* Default maximum number of SGEs in a RDMA send/recv */
+#define SMBDIRECT_MAX_SGE 16
+/* The context for a SMBD request */
+struct smbd_request {
+ struct smbd_connection *info;
+ struct ib_cqe cqe;
+
+ /* true if this request carries upper layer payload */
+ bool has_payload;
+
+ /* the SGE entries for this packet */
+ struct ib_sge sge[SMBDIRECT_MAX_SGE];
+ int num_sge;
+
+ /* SMBD packet header follows this structure */
+ u8 packet[];
+};
+
+/* The context for a SMBD response */
+struct smbd_response {
+ struct smbd_connection *info;
+ struct ib_cqe cqe;
+ struct ib_sge sge;
+
+ enum smbd_message_type type;
+
+ /* Link to receive queue or reassembly queue */
+ struct list_head list;
+
+ /* Indicate if this is the 1st packet of a payload */
+ bool first_segment;
+
+ /* SMBD packet header and payload follows this structure */
+ u8 packet[];
+};
+
+/* Create a SMBDirect session */
+struct smbd_connection *smbd_get_connection(
+ struct TCP_Server_Info *server, struct sockaddr *dstaddr);
+
+/* Reconnect SMBDirect session */
+int smbd_reconnect(struct TCP_Server_Info *server);
+/* Destroy SMBDirect session */
+void smbd_destroy(struct smbd_connection *info);
+
+/* Interface for carrying upper layer I/O through send/recv */
+int smbd_recv(struct smbd_connection *info, struct msghdr *msg);
+int smbd_send(struct TCP_Server_Info *server,
+ int num_rqst, struct smb_rqst *rqst);
+
+enum mr_state {
+ MR_READY,
+ MR_REGISTERED,
+ MR_INVALIDATED,
+ MR_ERROR
+};
+
+struct smbd_mr {
+ struct smbd_connection *conn;
+ struct list_head list;
+ enum mr_state state;
+ struct ib_mr *mr;
+ struct scatterlist *sgl;
+ int sgl_count;
+ enum dma_data_direction dir;
+ union {
+ struct ib_reg_wr wr;
+ struct ib_send_wr inv_wr;
+ };
+ struct ib_cqe cqe;
+ bool need_invalidate;
+ struct completion invalidate_done;
+};
+
+/* Interfaces to register and deregister MR for RDMA read/write */
+struct smbd_mr *smbd_register_mr(
+ struct smbd_connection *info, struct page *pages[], int num_pages,
+ int offset, int tailsz, bool writing, bool need_invalidate);
+int smbd_deregister_mr(struct smbd_mr *mr);
+
+#else
+#define cifs_rdma_enabled(server) 0
+struct smbd_connection {};
+static inline void *smbd_get_connection(
+ struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;}
+static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; }
+static inline void smbd_destroy(struct smbd_connection *info) {}
+static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; }
+static inline int smbd_send(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst) {return -1; }
+#endif
+
+#endif
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
new file mode 100644
index 000000000..a0b80ac65
--- /dev/null
+++ b/fs/cifs/smbencrypt.c
@@ -0,0 +1,222 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Modified by Jeremy Allison 1995.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+ Modified by Steve French (sfrench@us.ibm.com) 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/crypto.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifsproto.h"
+
+#ifndef false
+#define false 0
+#endif
+#ifndef true
+#define true 1
+#endif
+
+/* following came from the other byteorder.h to avoid include conflicts */
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val)))
+
+static void
+str_to_key(unsigned char *str, unsigned char *key)
+{
+ int i;
+
+ key[0] = str[0] >> 1;
+ key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2);
+ key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3);
+ key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4);
+ key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5);
+ key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6);
+ key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7);
+ key[7] = str[6] & 0x7F;
+ for (i = 0; i < 8; i++)
+ key[i] = (key[i] << 1);
+}
+
+static int
+smbhash(unsigned char *out, const unsigned char *in, unsigned char *key)
+{
+ unsigned char key2[8];
+ struct crypto_cipher *tfm_des;
+
+ str_to_key(key, key2);
+
+ tfm_des = crypto_alloc_cipher("des", 0, 0);
+ if (IS_ERR(tfm_des)) {
+ cifs_dbg(VFS, "could not allocate des crypto API\n");
+ return PTR_ERR(tfm_des);
+ }
+
+ crypto_cipher_setkey(tfm_des, key2, 8);
+ crypto_cipher_encrypt_one(tfm_des, out, in);
+ crypto_free_cipher(tfm_des);
+
+ return 0;
+}
+
+static int
+E_P16(unsigned char *p14, unsigned char *p16)
+{
+ int rc;
+ unsigned char sp8[8] =
+ { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
+
+ rc = smbhash(p16, sp8, p14);
+ if (rc)
+ return rc;
+ rc = smbhash(p16 + 8, sp8, p14 + 7);
+ return rc;
+}
+
+static int
+E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24)
+{
+ int rc;
+
+ rc = smbhash(p24, c8, p21);
+ if (rc)
+ return rc;
+ rc = smbhash(p24 + 8, c8, p21 + 7);
+ if (rc)
+ return rc;
+ rc = smbhash(p24 + 16, c8, p21 + 14);
+ return rc;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+int
+mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len)
+{
+ int rc;
+ struct crypto_shash *md4 = NULL;
+ struct sdesc *sdescmd4 = NULL;
+
+ rc = cifs_alloc_hash("md4", &md4, &sdescmd4);
+ if (rc)
+ goto mdfour_err;
+
+ rc = crypto_shash_init(&sdescmd4->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not init md4 shash\n", __func__);
+ goto mdfour_err;
+ }
+ rc = crypto_shash_update(&sdescmd4->shash, link_str, link_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
+ goto mdfour_err;
+ }
+ rc = crypto_shash_final(&sdescmd4->shash, md4_hash);
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate md4 hash\n", __func__);
+
+mdfour_err:
+ cifs_free_hash(&md4, &sdescmd4);
+ return rc;
+}
+
+/*
+ This implements the X/Open SMB password encryption
+ It takes a password, a 8 byte "crypt key" and puts 24 bytes of
+ encrypted password into p24 */
+/* Note that password must be uppercased and null terminated */
+int
+SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24)
+{
+ int rc;
+ unsigned char p14[14], p16[16], p21[21];
+
+ memset(p14, '\0', 14);
+ memset(p16, '\0', 16);
+ memset(p21, '\0', 21);
+
+ memcpy(p14, passwd, 14);
+ rc = E_P16(p14, p16);
+ if (rc)
+ return rc;
+
+ memcpy(p21, p16, 16);
+ rc = E_P24(p21, c8, p24);
+
+ return rc;
+}
+
+/*
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ */
+
+int
+E_md4hash(const unsigned char *passwd, unsigned char *p16,
+ const struct nls_table *codepage)
+{
+ int rc;
+ int len;
+ __le16 wpwd[129];
+
+ /* Password cannot be longer than 128 characters */
+ if (passwd) /* Password must be converted to NT unicode */
+ len = cifs_strtoUTF16(wpwd, passwd, 128, codepage);
+ else {
+ len = 0;
+ *wpwd = 0; /* Ensure string is null terminated */
+ }
+
+ rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__le16));
+ memzero_explicit(wpwd, sizeof(wpwd));
+
+ return rc;
+}
+
+/* Does the NT MD4 hash then des encryption. */
+int
+SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24,
+ const struct nls_table *codepage)
+{
+ int rc;
+ unsigned char p16[16], p21[21];
+
+ memset(p16, '\0', 16);
+ memset(p21, '\0', 21);
+
+ rc = E_md4hash(passwd, p16, codepage);
+ if (rc) {
+ cifs_dbg(FYI, "%s Can't generate NT hash, error: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ memcpy(p21, p16, 16);
+ rc = E_P24(p21, c8, p24);
+ return rc;
+}
diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h
new file mode 100644
index 000000000..7f16cb825
--- /dev/null
+++ b/fs/cifs/smberr.h
@@ -0,0 +1,184 @@
+/*
+ * fs/cifs/smberr.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * See Error Codes section of the SNIA CIFS Specification
+ * for more information
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define SUCCESS 0x00 /* The request was successful. */
+#define ERRDOS 0x01 /* Error is from the core DOS operating system set */
+#define ERRSRV 0x02 /* Error is generated by the file server daemon */
+#define ERRHRD 0x03 /* Error is a hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/* The following error codes may be generated with the SUCCESS error class.*/
+
+/*#define SUCCESS 0 The request was successful. */
+
+/* The following error codes may be generated with the ERRDOS error class.*/
+
+#define ERRbadfunc 1 /* Invalid function. The server did not
+ recognize or could not perform a
+ system call generated by the server,
+ e.g., set the DIRECTORY attribute on
+ a data file, invalid seek mode. */
+#define ERRbadfile 2 /* File not found. The last component
+ of a file's pathname could not be
+ found. */
+#define ERRbadpath 3 /* Directory invalid. A directory
+ component in a pathname could not be
+ found. */
+#define ERRnofids 4 /* Too many open files. The server has
+ no file handles available. */
+#define ERRnoaccess 5 /* Access denied, the client's context
+ does not permit the requested
+ function. This includes the
+ following conditions: invalid rename
+ command, write to Fid open for read
+ only, read on Fid open for write
+ only, attempt to delete a non-empty
+ directory */
+#define ERRbadfid 6 /* Invalid file handle. The file handle
+ specified was not recognized by the
+ server. */
+#define ERRbadmcb 7 /* Memory control blocks destroyed. */
+#define ERRnomem 8 /* Insufficient server memory to
+ perform the requested function. */
+#define ERRbadmem 9 /* Invalid memory block address. */
+#define ERRbadenv 10 /* Invalid environment. */
+#define ERRbadformat 11 /* Invalid format. */
+#define ERRbadaccess 12 /* Invalid open mode. */
+#define ERRbaddata 13 /* Invalid data (generated only by
+ IOCTL calls within the server). */
+#define ERRbaddrive 15 /* Invalid drive specified. */
+#define ERRremcd 16 /* A Delete Directory request attempted
+ to remove the server's current
+ directory. */
+#define ERRdiffdevice 17 /* Not same device (e.g., a cross
+ volume rename was attempted */
+#define ERRnofiles 18 /* A File Search command can find no
+ more files matching the specified
+ criteria. */
+#define ERRwriteprot 19 /* media is write protected */
+#define ERRgeneral 31
+#define ERRbadshare 32 /* The sharing mode specified for an
+ Open conflicts with existing FIDs on
+ the file. */
+#define ERRlock 33 /* A Lock request conflicted with an
+ existing lock or specified an
+ invalid mode, or an Unlock requested
+ attempted to remove a lock held by
+ another process. */
+#define ERRunsup 50
+#define ERRnosuchshare 67
+#define ERRfilexists 80 /* The file named in the request
+ already exists. */
+#define ERRinvparm 87
+#define ERRdiskfull 112
+#define ERRinvname 123
+#define ERRinvlevel 124
+#define ERRdirnotempty 145
+#define ERRnotlocked 158
+#define ERRcancelviolation 173
+#define ERRalreadyexists 183
+#define ERRbadpipe 230
+#define ERRpipebusy 231
+#define ERRpipeclosing 232
+#define ERRnotconnected 233
+#define ERRmoredata 234
+#define ERReasnotsupported 282
+#define ErrQuota 0x200 /* The operation would cause a quota
+ limit to be exceeded. */
+#define ErrNotALink 0x201 /* A link operation was performed on a
+ pathname that was not a link. */
+
+/* Below errors are used internally (do not come over the wire) for passthrough
+ from STATUS codes to POSIX only */
+#define ERRsymlink 0xFFFD
+#define ErrTooManyLinks 0xFFFE
+
+/* Following error codes may be generated with the ERRSRV error class.*/
+
+#define ERRerror 1 /* Non-specific error code. It is
+ returned under the following
+ conditions: resource other than disk
+ space exhausted (e.g. TIDs), first
+ SMB command was not negotiate,
+ multiple negotiates attempted, and
+ internal server error. */
+#define ERRbadpw 2 /* Bad password - name/password pair in
+ a TreeConnect or Session Setup are
+ invalid. */
+#define ERRbadtype 3 /* used for indicating DFS referral
+ needed */
+#define ERRaccess 4 /* The client does not have the
+ necessary access rights within the
+ specified context for requested
+ function. */
+#define ERRinvtid 5 /* The Tid specified in a command was
+ invalid. */
+#define ERRinvnetname 6 /* Invalid network name in tree
+ connect. */
+#define ERRinvdevice 7 /* Invalid device - printer request
+ made to non-printer connection or
+ non-printer request made to printer
+ connection. */
+#define ERRqfull 49 /* Print queue full (files) -- returned
+ by open print file. */
+#define ERRqtoobig 50 /* Print queue full -- no space. */
+#define ERRqeof 51 /* EOF on print queue dump */
+#define ERRinvpfid 52 /* Invalid print file FID. */
+#define ERRsmbcmd 64 /* The server did not recognize the
+ command received. */
+#define ERRsrverror 65 /* The server encountered an internal
+ error, e.g., system file
+ unavailable. */
+#define ERRbadBID 66 /* (obsolete) */
+#define ERRfilespecs 67 /* The Fid and pathname parameters
+ contained an invalid combination of
+ values. */
+#define ERRbadLink 68 /* (obsolete) */
+#define ERRbadpermits 69 /* The access permissions specified for
+ a file or directory are not a valid
+ combination. */
+#define ERRbadPID 70
+#define ERRsetattrmode 71 /* attribute (mode) is invalid */
+#define ERRpaused 81 /* Server is paused */
+#define ERRmsgoff 82 /* reserved - messaging off */
+#define ERRnoroom 83 /* reserved - no room for message */
+#define ERRrmuns 87 /* reserved - too many remote names */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource 89 /* No resources available for request
+ */
+#define ERRtoomanyuids 90 /* Too many UIDs active on this session
+ */
+#define ERRbaduid 91 /* The UID is not known as a valid user
+ */
+#define ERRusempx 250 /* temporarily unable to use raw */
+#define ERRusestd 251 /* temporarily unable to use either raw
+ or mpx */
+#define ERR_NOTIFY_ENUM_DIR 1024
+#define ERRnoSuchUser 2238 /* user account does not exist */
+#define ERRaccountexpired 2239
+#define ERRbadclient 2240 /* can not logon from this client */
+#define ERRbadLogonTime 2241 /* logon hours do not allow this */
+#define ERRpasswordExpired 2242
+#define ERRnetlogonNotStarted 2455
+#define ERRnosupport 0xFFFF
diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h
new file mode 100644
index 000000000..f996daeea
--- /dev/null
+++ b/fs/cifs/smbfsctl.h
@@ -0,0 +1,124 @@
+/*
+ * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2013
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* IOCTL information */
+/*
+ * List of ioctl/fsctl function codes that are or could be useful in the
+ * future to remote clients like cifs or SMB2/SMB3 client. This is probably
+ * a slightly larger set of fsctls that NTFS local filesystem could handle,
+ * including the seven below that we do not have struct definitions for.
+ * Even with protocol definitions for most of these now available, we still
+ * need to do some experimentation to identify which are practical to do
+ * remotely. Some of the following, such as the encryption/compression ones
+ * could be invoked from tools via a specialized hook into the VFS rather
+ * than via the standard vfs entry points
+ *
+ * See MS-SMB2 Section 2.2.31 (last checked June 2013, all of that list are
+ * below). Additional detail on less common ones can be found in MS-FSCC
+ * section 2.3.
+ */
+#define FSCTL_DFS_GET_REFERRALS 0x00060194
+#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0
+#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
+#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004
+#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008
+#define FSCTL_LOCK_VOLUME 0x00090018
+#define FSCTL_UNLOCK_VOLUME 0x0009001C
+#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */
+#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */
+#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */
+#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */
+/* Verify the next FSCTL number, we had it as 0x00090090 before */
+#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */
+#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */
+#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */
+#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */
+#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */
+#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C
+#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */
+#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */
+#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */
+#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */
+#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */
+#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */
+#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */
+#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */
+#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */
+#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */
+#define FSCTL_SET_ZERO_DATA 0x000980C8
+#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */
+#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */
+#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */
+#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */
+#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */
+#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */
+#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */
+#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */
+#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */
+#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
+#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
+#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
+#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */
+#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
+#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
+#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
+#define FSCTL_SIS_LINK_FILES 0x0009C104
+#define FSCTL_SET_INTEGRITY_INFORMATION 0x0009C280
+#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */
+#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */
+/* strange that the number for this op is not sequential with previous op */
+#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */
+/* Enumerate previous versions of a file */
+#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064
+/* Retrieve an opaque file reference for server-side data movement ie copy */
+#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078
+#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4
+#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
+#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
+#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
+/* Perform server-side data movement */
+#define FSCTL_SRV_COPYCHUNK 0x001440F2
+#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2
+#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */
+#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */
+
+/* See FSCC 2.1.2.5 */
+#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
+#define IO_REPARSE_TAG_HSM 0xC0000004
+#define IO_REPARSE_TAG_SIS 0x80000007
+#define IO_REPARSE_TAG_HSM2 0x80000006
+#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005
+/* Used by the DFS filter. See MS-DFSC */
+#define IO_REPARSE_TAG_DFS 0x8000000A
+/* Used by the DFS filter See MS-DFSC */
+#define IO_REPARSE_TAG_DFSR 0x80000012
+#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B
+/* See section MS-FSCC 2.1.2.4 */
+#define IO_REPARSE_TAG_SYMLINK 0xA000000C
+#define IO_REPARSE_TAG_DEDUP 0x80000013
+#define IO_REPARSE_APPXSTREAM 0xC0000014
+/* NFS symlinks, Win 8/SMB3 and later */
+#define IO_REPARSE_TAG_NFS 0x80000014
+
+/* fsctl flags */
+/* If Flags is set to this value, the request is an FSCTL not ioctl request */
+#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
+
diff --git a/fs/cifs/trace.c b/fs/cifs/trace.c
new file mode 100644
index 000000000..bd4a546fe
--- /dev/null
+++ b/fs/cifs/trace.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018, Microsoft Corporation.
+ *
+ * Author(s): Steve French <stfrench@microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ */
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h
new file mode 100644
index 000000000..d4aed5217
--- /dev/null
+++ b/fs/cifs/trace.h
@@ -0,0 +1,494 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018, Microsoft Corporation.
+ *
+ * Author(s): Steve French <stfrench@microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cifs
+
+#if !defined(_CIFS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _CIFS_TRACE_H
+
+#include <linux/tracepoint.h>
+
+/* For logging errors in read or write */
+DECLARE_EVENT_CLASS(smb3_rw_err_class,
+ TP_PROTO(unsigned int xid,
+ __u64 fid,
+ __u32 tid,
+ __u64 sesid,
+ __u64 offset,
+ __u32 len,
+ int rc),
+ TP_ARGS(xid, fid, tid, sesid, offset, len, rc),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u64, fid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u64, offset)
+ __field(__u32, len)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->fid = fid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->offset = offset;
+ __entry->len = len;
+ __entry->rc = rc;
+ ),
+ TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->offset, __entry->len, __entry->rc)
+)
+
+#define DEFINE_SMB3_RW_ERR_EVENT(name) \
+DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u64 fid, \
+ __u32 tid, \
+ __u64 sesid, \
+ __u64 offset, \
+ __u32 len, \
+ int rc), \
+ TP_ARGS(xid, fid, tid, sesid, offset, len, rc))
+
+DEFINE_SMB3_RW_ERR_EVENT(write_err);
+DEFINE_SMB3_RW_ERR_EVENT(read_err);
+
+
+/* For logging successful read or write */
+DECLARE_EVENT_CLASS(smb3_rw_done_class,
+ TP_PROTO(unsigned int xid,
+ __u64 fid,
+ __u32 tid,
+ __u64 sesid,
+ __u64 offset,
+ __u32 len),
+ TP_ARGS(xid, fid, tid, sesid, offset, len),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u64, fid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u64, offset)
+ __field(__u32, len)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->fid = fid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->offset = offset;
+ __entry->len = len;
+ ),
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->offset, __entry->len)
+)
+
+#define DEFINE_SMB3_RW_DONE_EVENT(name) \
+DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u64 fid, \
+ __u32 tid, \
+ __u64 sesid, \
+ __u64 offset, \
+ __u32 len), \
+ TP_ARGS(xid, fid, tid, sesid, offset, len))
+
+DEFINE_SMB3_RW_DONE_EVENT(write_done);
+DEFINE_SMB3_RW_DONE_EVENT(read_done);
+
+/*
+ * For handle based calls other than read and write, and get/set info
+ */
+DECLARE_EVENT_CLASS(smb3_fd_err_class,
+ TP_PROTO(unsigned int xid,
+ __u64 fid,
+ __u32 tid,
+ __u64 sesid,
+ int rc),
+ TP_ARGS(xid, fid, tid, sesid, rc),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u64, fid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->fid = fid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->rc = rc;
+ ),
+ TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->rc)
+)
+
+#define DEFINE_SMB3_FD_ERR_EVENT(name) \
+DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u64 fid, \
+ __u32 tid, \
+ __u64 sesid, \
+ int rc), \
+ TP_ARGS(xid, fid, tid, sesid, rc))
+
+DEFINE_SMB3_FD_ERR_EVENT(flush_err);
+DEFINE_SMB3_FD_ERR_EVENT(lock_err);
+DEFINE_SMB3_FD_ERR_EVENT(close_err);
+
+/*
+ * For handle based query/set info calls
+ */
+DECLARE_EVENT_CLASS(smb3_inf_err_class,
+ TP_PROTO(unsigned int xid,
+ __u64 fid,
+ __u32 tid,
+ __u64 sesid,
+ __u8 infclass,
+ __u32 type,
+ int rc),
+ TP_ARGS(xid, fid, tid, sesid, infclass, type, rc),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u64, fid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u8, infclass)
+ __field(__u32, type)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->fid = fid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->infclass = infclass;
+ __entry->type = type;
+ __entry->rc = rc;
+ ),
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x rc=%d",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->infclass, __entry->type, __entry->rc)
+)
+
+#define DEFINE_SMB3_INF_ERR_EVENT(name) \
+DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u64 fid, \
+ __u32 tid, \
+ __u64 sesid, \
+ __u8 infclass, \
+ __u32 type, \
+ int rc), \
+ TP_ARGS(xid, fid, tid, sesid, infclass, type, rc))
+
+DEFINE_SMB3_INF_ERR_EVENT(query_info_err);
+DEFINE_SMB3_INF_ERR_EVENT(set_info_err);
+DEFINE_SMB3_INF_ERR_EVENT(fsctl_err);
+
+/*
+ * For logging SMB3 Status code and Command for responses which return errors
+ */
+DECLARE_EVENT_CLASS(smb3_cmd_err_class,
+ TP_PROTO(__u32 tid,
+ __u64 sesid,
+ __u16 cmd,
+ __u64 mid,
+ __u32 status,
+ int rc),
+ TP_ARGS(tid, sesid, cmd, mid, status, rc),
+ TP_STRUCT__entry(
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u16, cmd)
+ __field(__u64, mid)
+ __field(__u32, status)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->cmd = cmd;
+ __entry->mid = mid;
+ __entry->status = status;
+ __entry->rc = rc;
+ ),
+ TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d",
+ __entry->sesid, __entry->tid, __entry->cmd, __entry->mid,
+ __entry->status, __entry->rc)
+)
+
+#define DEFINE_SMB3_CMD_ERR_EVENT(name) \
+DEFINE_EVENT(smb3_cmd_err_class, smb3_##name, \
+ TP_PROTO(__u32 tid, \
+ __u64 sesid, \
+ __u16 cmd, \
+ __u64 mid, \
+ __u32 status, \
+ int rc), \
+ TP_ARGS(tid, sesid, cmd, mid, status, rc))
+
+DEFINE_SMB3_CMD_ERR_EVENT(cmd_err);
+
+DECLARE_EVENT_CLASS(smb3_cmd_done_class,
+ TP_PROTO(__u32 tid,
+ __u64 sesid,
+ __u16 cmd,
+ __u64 mid),
+ TP_ARGS(tid, sesid, cmd, mid),
+ TP_STRUCT__entry(
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u16, cmd)
+ __field(__u64, mid)
+ ),
+ TP_fast_assign(
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->cmd = cmd;
+ __entry->mid = mid;
+ ),
+ TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu",
+ __entry->sesid, __entry->tid,
+ __entry->cmd, __entry->mid)
+)
+
+#define DEFINE_SMB3_CMD_DONE_EVENT(name) \
+DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \
+ TP_PROTO(__u32 tid, \
+ __u64 sesid, \
+ __u16 cmd, \
+ __u64 mid), \
+ TP_ARGS(tid, sesid, cmd, mid))
+
+DEFINE_SMB3_CMD_DONE_EVENT(cmd_done);
+DEFINE_SMB3_CMD_DONE_EVENT(ses_expired);
+
+DECLARE_EVENT_CLASS(smb3_mid_class,
+ TP_PROTO(__u16 cmd,
+ __u64 mid,
+ __u32 pid,
+ unsigned long when_sent,
+ unsigned long when_received),
+ TP_ARGS(cmd, mid, pid, when_sent, when_received),
+ TP_STRUCT__entry(
+ __field(__u16, cmd)
+ __field(__u64, mid)
+ __field(__u32, pid)
+ __field(unsigned long, when_sent)
+ __field(unsigned long, when_received)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->mid = mid;
+ __entry->pid = pid;
+ __entry->when_sent = when_sent;
+ __entry->when_received = when_received;
+ ),
+ TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu",
+ __entry->cmd, __entry->mid, __entry->pid, __entry->when_sent,
+ __entry->when_received)
+)
+
+#define DEFINE_SMB3_MID_EVENT(name) \
+DEFINE_EVENT(smb3_mid_class, smb3_##name, \
+ TP_PROTO(__u16 cmd, \
+ __u64 mid, \
+ __u32 pid, \
+ unsigned long when_sent, \
+ unsigned long when_received), \
+ TP_ARGS(cmd, mid, pid, when_sent, when_received))
+
+DEFINE_SMB3_MID_EVENT(slow_rsp);
+
+DECLARE_EVENT_CLASS(smb3_exit_err_class,
+ TP_PROTO(unsigned int xid,
+ const char *func_name,
+ int rc),
+ TP_ARGS(xid, func_name, rc),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(const char *, func_name)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->func_name = func_name;
+ __entry->rc = rc;
+ ),
+ TP_printk("\t%s: xid=%u rc=%d",
+ __entry->func_name, __entry->xid, __entry->rc)
+)
+
+#define DEFINE_SMB3_EXIT_ERR_EVENT(name) \
+DEFINE_EVENT(smb3_exit_err_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ const char *func_name, \
+ int rc), \
+ TP_ARGS(xid, func_name, rc))
+
+DEFINE_SMB3_EXIT_ERR_EVENT(exit_err);
+
+DECLARE_EVENT_CLASS(smb3_enter_exit_class,
+ TP_PROTO(unsigned int xid,
+ const char *func_name),
+ TP_ARGS(xid, func_name),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(const char *, func_name)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->func_name = func_name;
+ ),
+ TP_printk("\t%s: xid=%u",
+ __entry->func_name, __entry->xid)
+)
+
+#define DEFINE_SMB3_ENTER_EXIT_EVENT(name) \
+DEFINE_EVENT(smb3_enter_exit_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ const char *func_name), \
+ TP_ARGS(xid, func_name))
+
+DEFINE_SMB3_ENTER_EXIT_EVENT(enter);
+DEFINE_SMB3_ENTER_EXIT_EVENT(exit_done);
+
+/*
+ * For smb2/smb3 open call
+ */
+DECLARE_EVENT_CLASS(smb3_open_err_class,
+ TP_PROTO(unsigned int xid,
+ __u32 tid,
+ __u64 sesid,
+ int create_options,
+ int desired_access,
+ int rc),
+ TP_ARGS(xid, tid, sesid, create_options, desired_access, rc),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(int, create_options)
+ __field(int, desired_access)
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->create_options = create_options;
+ __entry->desired_access = desired_access;
+ __entry->rc = rc;
+ ),
+ TP_printk("xid=%u sid=0x%llx tid=0x%x cr_opts=0x%x des_access=0x%x rc=%d",
+ __entry->xid, __entry->sesid, __entry->tid,
+ __entry->create_options, __entry->desired_access, __entry->rc)
+)
+
+#define DEFINE_SMB3_OPEN_ERR_EVENT(name) \
+DEFINE_EVENT(smb3_open_err_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u32 tid, \
+ __u64 sesid, \
+ int create_options, \
+ int desired_access, \
+ int rc), \
+ TP_ARGS(xid, tid, sesid, create_options, desired_access, rc))
+
+DEFINE_SMB3_OPEN_ERR_EVENT(open_err);
+DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err);
+
+DECLARE_EVENT_CLASS(smb3_open_done_class,
+ TP_PROTO(unsigned int xid,
+ __u64 fid,
+ __u32 tid,
+ __u64 sesid,
+ int create_options,
+ int desired_access),
+ TP_ARGS(xid, fid, tid, sesid, create_options, desired_access),
+ TP_STRUCT__entry(
+ __field(unsigned int, xid)
+ __field(__u64, fid)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(int, create_options)
+ __field(int, desired_access)
+ ),
+ TP_fast_assign(
+ __entry->xid = xid;
+ __entry->fid = fid;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->create_options = create_options;
+ __entry->desired_access = desired_access;
+ ),
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x",
+ __entry->xid, __entry->sesid, __entry->tid, __entry->fid,
+ __entry->create_options, __entry->desired_access)
+)
+
+#define DEFINE_SMB3_OPEN_DONE_EVENT(name) \
+DEFINE_EVENT(smb3_open_done_class, smb3_##name, \
+ TP_PROTO(unsigned int xid, \
+ __u64 fid, \
+ __u32 tid, \
+ __u64 sesid, \
+ int create_options, \
+ int desired_access), \
+ TP_ARGS(xid, fid, tid, sesid, create_options, desired_access))
+
+DEFINE_SMB3_OPEN_DONE_EVENT(open_done);
+DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done);
+
+DECLARE_EVENT_CLASS(smb3_reconnect_class,
+ TP_PROTO(__u64 currmid,
+ char *hostname),
+ TP_ARGS(currmid, hostname),
+ TP_STRUCT__entry(
+ __field(__u64, currmid)
+ __field(char *, hostname)
+ ),
+ TP_fast_assign(
+ __entry->currmid = currmid;
+ __entry->hostname = hostname;
+ ),
+ TP_printk("server=%s current_mid=0x%llx",
+ __entry->hostname,
+ __entry->currmid)
+)
+
+#define DEFINE_SMB3_RECONNECT_EVENT(name) \
+DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \
+ TP_PROTO(__u64 currmid, \
+ char *hostname), \
+ TP_ARGS(currmid, hostname))
+
+DEFINE_SMB3_RECONNECT_EVENT(reconnect);
+DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect);
+
+#endif /* _CIFS_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
new file mode 100644
index 000000000..59643acb6
--- /dev/null
+++ b/fs/cifs/transport.c
@@ -0,0 +1,1321 @@
+/*
+ * fs/cifs/transport.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org) 2006.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/gfp.h>
+#include <linux/wait.h>
+#include <linux/net.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/tcp.h>
+#include <linux/bvec.h>
+#include <linux/highmem.h>
+#include <linux/uaccess.h>
+#include <asm/processor.h>
+#include <linux/mempool.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "smb2proto.h"
+#include "smbdirect.h"
+
+/* Max number of iovectors we can use off the stack when sending requests. */
+#define CIFS_MAX_IOV_SIZE 8
+
+void
+cifs_wake_up_task(struct mid_q_entry *mid)
+{
+ wake_up_process(mid->callback_data);
+}
+
+struct mid_q_entry *
+AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
+{
+ struct mid_q_entry *temp;
+
+ if (server == NULL) {
+ cifs_dbg(VFS, "Null TCP session in AllocMidQEntry\n");
+ return NULL;
+ }
+
+ temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
+ memset(temp, 0, sizeof(struct mid_q_entry));
+ kref_init(&temp->refcount);
+ temp->mid = get_mid(smb_buffer);
+ temp->pid = current->pid;
+ temp->command = cpu_to_le16(smb_buffer->Command);
+ cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
+ /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
+ /* when mid allocated can be before when sent */
+ temp->when_alloc = jiffies;
+ temp->server = server;
+
+ /*
+ * The default is for the mid to be synchronous, so the
+ * default callback just wakes up the current task.
+ */
+ temp->callback = cifs_wake_up_task;
+ temp->callback_data = current;
+
+ atomic_inc(&midCount);
+ temp->mid_state = MID_REQUEST_ALLOCATED;
+ return temp;
+}
+
+static void _cifs_mid_q_entry_release(struct kref *refcount)
+{
+ struct mid_q_entry *mid = container_of(refcount, struct mid_q_entry,
+ refcount);
+
+ mempool_free(mid, cifs_mid_poolp);
+}
+
+void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
+{
+ spin_lock(&GlobalMid_Lock);
+ kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
+ spin_unlock(&GlobalMid_Lock);
+}
+
+void
+DeleteMidQEntry(struct mid_q_entry *midEntry)
+{
+#ifdef CONFIG_CIFS_STATS2
+ __le16 command = midEntry->server->vals->lock_cmd;
+ unsigned long now;
+#endif
+ midEntry->mid_state = MID_FREE;
+ atomic_dec(&midCount);
+ if (midEntry->large_buf)
+ cifs_buf_release(midEntry->resp_buf);
+ else
+ cifs_small_buf_release(midEntry->resp_buf);
+#ifdef CONFIG_CIFS_STATS2
+ now = jiffies;
+ /* commands taking longer than one second are indications that
+ something is wrong, unless it is quite a slow link or server */
+ if (time_after(now, midEntry->when_alloc + HZ) &&
+ (midEntry->command != command)) {
+ /* smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command */
+ if ((le16_to_cpu(midEntry->command) < NUMBER_OF_SMB2_COMMANDS) &&
+ (le16_to_cpu(midEntry->command) >= 0))
+ cifs_stats_inc(&midEntry->server->smb2slowcmd[le16_to_cpu(midEntry->command)]);
+
+ trace_smb3_slow_rsp(le16_to_cpu(midEntry->command),
+ midEntry->mid, midEntry->pid,
+ midEntry->when_sent, midEntry->when_received);
+ if (cifsFYI & CIFS_TIMER) {
+ pr_debug(" CIFS slow rsp: cmd %d mid %llu",
+ midEntry->command, midEntry->mid);
+ pr_info(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
+ now - midEntry->when_alloc,
+ now - midEntry->when_sent,
+ now - midEntry->when_received);
+ }
+ }
+#endif
+ cifs_mid_q_entry_release(midEntry);
+}
+
+void
+cifs_delete_mid(struct mid_q_entry *mid)
+{
+ spin_lock(&GlobalMid_Lock);
+ list_del_init(&mid->qhead);
+ mid->mid_flags |= MID_DELETED;
+ spin_unlock(&GlobalMid_Lock);
+
+ DeleteMidQEntry(mid);
+}
+
+/*
+ * smb_send_kvec - send an array of kvecs to the server
+ * @server: Server to send the data to
+ * @smb_msg: Message to send
+ * @sent: amount of data sent on socket is stored here
+ *
+ * Our basic "send data to server" function. Should be called with srv_mutex
+ * held. The caller is responsible for handling the results.
+ */
+static int
+smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
+ size_t *sent)
+{
+ int rc = 0;
+ int retries = 0;
+ struct socket *ssocket = server->ssocket;
+
+ *sent = 0;
+
+ smb_msg->msg_name = (struct sockaddr *) &server->dstaddr;
+ smb_msg->msg_namelen = sizeof(struct sockaddr);
+ smb_msg->msg_control = NULL;
+ smb_msg->msg_controllen = 0;
+ if (server->noblocksnd)
+ smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
+ else
+ smb_msg->msg_flags = MSG_NOSIGNAL;
+
+ while (msg_data_left(smb_msg)) {
+ /*
+ * If blocking send, we try 3 times, since each can block
+ * for 5 seconds. For nonblocking we have to try more
+ * but wait increasing amounts of time allowing time for
+ * socket to clear. The overall time we wait in either
+ * case to send on the socket is about 15 seconds.
+ * Similarly we wait for 15 seconds for a response from
+ * the server in SendReceive[2] for the server to send
+ * a response back for most types of requests (except
+ * SMB Write past end of file which can be slow, and
+ * blocking lock operations). NFS waits slightly longer
+ * than CIFS, but this can make it take longer for
+ * nonresponsive servers to be detected and 15 seconds
+ * is more than enough time for modern networks to
+ * send a packet. In most cases if we fail to send
+ * after the retries we will kill the socket and
+ * reconnect which may clear the network problem.
+ */
+ rc = sock_sendmsg(ssocket, smb_msg);
+ if (rc == -EAGAIN) {
+ retries++;
+ if (retries >= 14 ||
+ (!server->noblocksnd && (retries > 2))) {
+ cifs_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
+ ssocket);
+ return -EAGAIN;
+ }
+ msleep(1 << retries);
+ continue;
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (rc == 0) {
+ /* should never happen, letting socket clear before
+ retrying is our only obvious option here */
+ cifs_dbg(VFS, "tcp sent no data\n");
+ msleep(500);
+ continue;
+ }
+
+ /* send was at least partially successful */
+ *sent += rc;
+ retries = 0; /* in case we get ENOSPC on the next send */
+ }
+ return 0;
+}
+
+unsigned long
+smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+ unsigned int i;
+ struct kvec *iov;
+ int nvec;
+ unsigned long buflen = 0;
+
+ if (server->vals->header_preamble_size == 0 &&
+ rqst->rq_nvec >= 2 && rqst->rq_iov[0].iov_len == 4) {
+ iov = &rqst->rq_iov[1];
+ nvec = rqst->rq_nvec - 1;
+ } else {
+ iov = rqst->rq_iov;
+ nvec = rqst->rq_nvec;
+ }
+
+ /* total up iov array first */
+ for (i = 0; i < nvec; i++)
+ buflen += iov[i].iov_len;
+
+ /*
+ * Add in the page array if there is one. The caller needs to make
+ * sure rq_offset and rq_tailsz are set correctly. If a buffer of
+ * multiple pages ends at page boundary, rq_tailsz needs to be set to
+ * PAGE_SIZE.
+ */
+ if (rqst->rq_npages) {
+ if (rqst->rq_npages == 1)
+ buflen += rqst->rq_tailsz;
+ else {
+ /*
+ * If there is more than one page, calculate the
+ * buffer length based on rq_offset and rq_tailsz
+ */
+ buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
+ rqst->rq_offset;
+ buflen += rqst->rq_tailsz;
+ }
+ }
+
+ return buflen;
+}
+
+static int
+__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *rqst)
+{
+ int rc = 0;
+ struct kvec *iov;
+ int n_vec;
+ unsigned int send_length = 0;
+ unsigned int i, j;
+ size_t total_len = 0, sent, size;
+ struct socket *ssocket = server->ssocket;
+ struct msghdr smb_msg;
+ int val = 1;
+ __be32 rfc1002_marker;
+
+ if (cifs_rdma_enabled(server)) {
+ /* return -EAGAIN when connecting or reconnecting */
+ rc = -EAGAIN;
+ if (server->smbd_conn)
+ rc = smbd_send(server, num_rqst, rqst);
+ goto smbd_done;
+ }
+ if (ssocket == NULL)
+ return -ENOTSOCK;
+
+ /* cork the socket */
+ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
+ (char *)&val, sizeof(val));
+
+ for (j = 0; j < num_rqst; j++)
+ send_length += smb_rqst_len(server, &rqst[j]);
+ rfc1002_marker = cpu_to_be32(send_length);
+
+ /* Generate a rfc1002 marker for SMB2+ */
+ if (server->vals->header_preamble_size == 0) {
+ struct kvec hiov = {
+ .iov_base = &rfc1002_marker,
+ .iov_len = 4
+ };
+ iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, &hiov,
+ 1, 4);
+ rc = smb_send_kvec(server, &smb_msg, &sent);
+ if (rc < 0)
+ goto uncork;
+
+ total_len += sent;
+ send_length += 4;
+ }
+
+ cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length);
+
+ for (j = 0; j < num_rqst; j++) {
+ iov = rqst[j].rq_iov;
+ n_vec = rqst[j].rq_nvec;
+
+ size = 0;
+ for (i = 0; i < n_vec; i++) {
+ dump_smb(iov[i].iov_base, iov[i].iov_len);
+ size += iov[i].iov_len;
+ }
+
+ iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC,
+ iov, n_vec, size);
+
+ rc = smb_send_kvec(server, &smb_msg, &sent);
+ if (rc < 0)
+ goto uncork;
+
+ total_len += sent;
+
+ /* now walk the page array and send each page in it */
+ for (i = 0; i < rqst[j].rq_npages; i++) {
+ struct bio_vec bvec;
+
+ bvec.bv_page = rqst[j].rq_pages[i];
+ rqst_page_get_length(&rqst[j], i, &bvec.bv_len,
+ &bvec.bv_offset);
+
+ iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC,
+ &bvec, 1, bvec.bv_len);
+ rc = smb_send_kvec(server, &smb_msg, &sent);
+ if (rc < 0)
+ break;
+
+ total_len += sent;
+ }
+ }
+
+uncork:
+ /* uncork it */
+ val = 0;
+ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
+ (char *)&val, sizeof(val));
+
+ if ((total_len > 0) && (total_len != send_length)) {
+ cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n",
+ send_length, total_len);
+ /*
+ * If we have only sent part of an SMB then the next SMB could
+ * be taken as the remainder of this one. We need to kill the
+ * socket so the server throws away the partial SMB
+ */
+ server->tcpStatus = CifsNeedReconnect;
+ trace_smb3_partial_send_reconnect(server->CurrentMid,
+ server->hostname);
+ }
+smbd_done:
+ if (rc < 0 && rc != -EINTR)
+ cifs_dbg(VFS, "Error %d sending data on socket to server\n",
+ rc);
+ else if (rc > 0)
+ rc = 0;
+
+ return rc;
+}
+
+static int
+smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *rqst, int flags)
+{
+ struct kvec iov;
+ struct smb2_transform_hdr *tr_hdr;
+ struct smb_rqst cur_rqst[MAX_COMPOUND];
+ int rc;
+
+ if (!(flags & CIFS_TRANSFORM_REQ))
+ return __smb_send_rqst(server, num_rqst, rqst);
+
+ if (num_rqst > MAX_COMPOUND - 1)
+ return -ENOMEM;
+
+ if (!server->ops->init_transform_rq) {
+ cifs_dbg(VFS, "Encryption requested but transform callback "
+ "is missing\n");
+ return -EIO;
+ }
+
+ tr_hdr = kmalloc(sizeof(*tr_hdr), GFP_NOFS);
+ if (!tr_hdr)
+ return -ENOMEM;
+
+ memset(&cur_rqst[0], 0, sizeof(cur_rqst));
+ memset(&iov, 0, sizeof(iov));
+ memset(tr_hdr, 0, sizeof(*tr_hdr));
+
+ iov.iov_base = tr_hdr;
+ iov.iov_len = sizeof(*tr_hdr);
+ cur_rqst[0].rq_iov = &iov;
+ cur_rqst[0].rq_nvec = 1;
+
+ rc = server->ops->init_transform_rq(server, num_rqst + 1,
+ &cur_rqst[0], rqst);
+ if (rc)
+ goto out;
+
+ rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]);
+ smb3_free_compound_rqst(num_rqst, &cur_rqst[1]);
+out:
+ kfree(tr_hdr);
+ return rc;
+}
+
+int
+smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
+ unsigned int smb_buf_length)
+{
+ struct kvec iov[2];
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = 2 };
+
+ iov[0].iov_base = smb_buffer;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = (char *)smb_buffer + 4;
+ iov[1].iov_len = smb_buf_length;
+
+ return __smb_send_rqst(server, 1, &rqst);
+}
+
+static int
+wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
+ int *credits)
+{
+ int rc;
+
+ spin_lock(&server->req_lock);
+ if (timeout == CIFS_ASYNC_OP) {
+ /* oplock breaks must not be held up */
+ server->in_flight++;
+ *credits -= 1;
+ spin_unlock(&server->req_lock);
+ return 0;
+ }
+
+ while (1) {
+ if (*credits <= 0) {
+ spin_unlock(&server->req_lock);
+ cifs_num_waiters_inc(server);
+ rc = wait_event_killable(server->request_q,
+ has_credits(server, credits));
+ cifs_num_waiters_dec(server);
+ if (rc)
+ return rc;
+ spin_lock(&server->req_lock);
+ } else {
+ if (server->tcpStatus == CifsExiting) {
+ spin_unlock(&server->req_lock);
+ return -ENOENT;
+ }
+
+ /*
+ * Can not count locking commands against total
+ * as they are allowed to block on server.
+ */
+
+ /* update # of requests on the wire to server */
+ if (timeout != CIFS_BLOCKING_OP) {
+ *credits -= 1;
+ server->in_flight++;
+ }
+ spin_unlock(&server->req_lock);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int
+wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
+ const int optype)
+{
+ int *val;
+
+ val = server->ops->get_credits_field(server, optype);
+ /* Since an echo is already inflight, no need to wait to send another */
+ if (*val <= 0 && optype == CIFS_ECHO_OP)
+ return -EAGAIN;
+ return wait_for_free_credits(server, timeout, val);
+}
+
+int
+cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
+ unsigned int *num, unsigned int *credits)
+{
+ *num = size;
+ *credits = 0;
+ return 0;
+}
+
+static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
+ struct mid_q_entry **ppmidQ)
+{
+ if (ses->server->tcpStatus == CifsExiting) {
+ return -ENOENT;
+ }
+
+ if (ses->server->tcpStatus == CifsNeedReconnect) {
+ cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
+ return -EAGAIN;
+ }
+
+ if (ses->status == CifsNew) {
+ if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
+ (in_buf->Command != SMB_COM_NEGOTIATE))
+ return -EAGAIN;
+ /* else ok - we are setting up session */
+ }
+
+ if (ses->status == CifsExiting) {
+ /* check if SMB session is bad because we are setting it up */
+ if (in_buf->Command != SMB_COM_LOGOFF_ANDX)
+ return -EAGAIN;
+ /* else ok - we are shutting down session */
+ }
+
+ *ppmidQ = AllocMidQEntry(in_buf, ses->server);
+ if (*ppmidQ == NULL)
+ return -ENOMEM;
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
+ spin_unlock(&GlobalMid_Lock);
+ return 0;
+}
+
+static int
+wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
+{
+ int error;
+
+ error = wait_event_freezekillable_unsafe(server->response_q,
+ midQ->mid_state != MID_REQUEST_SUBMITTED);
+ if (error < 0)
+ return -ERESTARTSYS;
+
+ return 0;
+}
+
+struct mid_q_entry *
+cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+ int rc;
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
+ if (rqst->rq_iov[0].iov_len != 4 ||
+ rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+ return ERR_PTR(-EIO);
+
+ /* enable signing if server requires it */
+ if (server->sign)
+ hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ mid = AllocMidQEntry(hdr, server);
+ if (mid == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
+ if (rc) {
+ DeleteMidQEntry(mid);
+ return ERR_PTR(rc);
+ }
+
+ return mid;
+}
+
+/*
+ * Send a SMB request and set the callback function in the mid to handle
+ * the result. Caller is responsible for dealing with timeouts.
+ */
+int
+cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+ mid_receive_t *receive, mid_callback_t *callback,
+ mid_handle_t *handle, void *cbdata, const int flags)
+{
+ int rc, timeout, optype;
+ struct mid_q_entry *mid;
+ unsigned int credits = 0;
+
+ timeout = flags & CIFS_TIMEOUT_MASK;
+ optype = flags & CIFS_OP_MASK;
+
+ if ((flags & CIFS_HAS_CREDITS) == 0) {
+ rc = wait_for_free_request(server, timeout, optype);
+ if (rc)
+ return rc;
+ credits = 1;
+ }
+
+ mutex_lock(&server->srv_mutex);
+ mid = server->ops->setup_async_request(server, rqst);
+ if (IS_ERR(mid)) {
+ mutex_unlock(&server->srv_mutex);
+ add_credits_and_wake_if(server, credits, optype);
+ return PTR_ERR(mid);
+ }
+
+ mid->receive = receive;
+ mid->callback = callback;
+ mid->callback_data = cbdata;
+ mid->handle = handle;
+ mid->mid_state = MID_REQUEST_SUBMITTED;
+
+ /* put it on the pending_mid_q */
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&mid->qhead, &server->pending_mid_q);
+ spin_unlock(&GlobalMid_Lock);
+
+ /*
+ * Need to store the time in mid before calling I/O. For call_async,
+ * I/O response may come back and free the mid entry on another thread.
+ */
+ cifs_save_when_sent(mid);
+ cifs_in_send_inc(server);
+ rc = smb_send_rqst(server, 1, rqst, flags);
+ cifs_in_send_dec(server);
+
+ if (rc < 0) {
+ revert_current_mid(server, mid->credits);
+ server->sequence_number -= 2;
+ cifs_delete_mid(mid);
+ }
+
+ mutex_unlock(&server->srv_mutex);
+
+ if (rc == 0)
+ return 0;
+
+ add_credits_and_wake_if(server, credits, optype);
+ return rc;
+}
+
+/*
+ *
+ * Send an SMB Request. No response info (other than return code)
+ * needs to be parsed.
+ *
+ * flags indicate the type of request buffer and how long to wait
+ * and whether to log NT STATUS code (error) before mapping it to POSIX error
+ *
+ */
+int
+SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
+ char *in_buf, int flags)
+{
+ int rc;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ iov[0].iov_base = in_buf;
+ iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
+ flags |= CIFS_NO_RESP;
+ rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
+
+ return rc;
+}
+
+static int
+cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
+{
+ int rc = 0;
+
+ cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n",
+ __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state);
+
+ spin_lock(&GlobalMid_Lock);
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ spin_unlock(&GlobalMid_Lock);
+ return rc;
+ case MID_RETRY_NEEDED:
+ rc = -EAGAIN;
+ break;
+ case MID_RESPONSE_MALFORMED:
+ rc = -EIO;
+ break;
+ case MID_SHUTDOWN:
+ rc = -EHOSTDOWN;
+ break;
+ default:
+ list_del_init(&mid->qhead);
+ cifs_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
+ __func__, mid->mid, mid->mid_state);
+ rc = -EIO;
+ }
+ spin_unlock(&GlobalMid_Lock);
+
+ DeleteMidQEntry(mid);
+ return rc;
+}
+
+static inline int
+send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+ struct mid_q_entry *mid)
+{
+ return server->ops->send_cancel ?
+ server->ops->send_cancel(server, rqst, mid) : 0;
+}
+
+int
+cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ bool log_error)
+{
+ unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
+
+ dump_smb(mid->resp_buf, min_t(u32, 92, len));
+
+ /* convert the length into a more usable form */
+ if (server->sign) {
+ struct kvec iov[2];
+ int rc = 0;
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = 2 };
+
+ iov[0].iov_base = mid->resp_buf;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = (char *)mid->resp_buf + 4;
+ iov[1].iov_len = len - 4;
+ /* FIXME: add code to kill session */
+ rc = cifs_verify_signature(&rqst, server,
+ mid->sequence_number);
+ if (rc)
+ cifs_dbg(VFS, "SMB signature verification returned error = %d\n",
+ rc);
+ }
+
+ /* BB special case reconnect tid and uid here? */
+ return map_smb_to_linux_error(mid->resp_buf, log_error);
+}
+
+struct mid_q_entry *
+cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
+{
+ int rc;
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
+ if (rqst->rq_iov[0].iov_len != 4 ||
+ rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+ return ERR_PTR(-EIO);
+
+ rc = allocate_mid(ses, hdr, &mid);
+ if (rc)
+ return ERR_PTR(rc);
+ rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
+ if (rc) {
+ cifs_delete_mid(mid);
+ return ERR_PTR(rc);
+ }
+ return mid;
+}
+
+static void
+cifs_noop_callback(struct mid_q_entry *mid)
+{
+}
+
+int
+compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
+ const int flags, const int num_rqst, struct smb_rqst *rqst,
+ int *resp_buf_type, struct kvec *resp_iov)
+{
+ int i, j, rc = 0;
+ int timeout, optype;
+ struct mid_q_entry *midQ[MAX_COMPOUND];
+ bool cancelled_mid[MAX_COMPOUND] = {false};
+ unsigned int credits[MAX_COMPOUND] = {0};
+ char *buf;
+
+ timeout = flags & CIFS_TIMEOUT_MASK;
+ optype = flags & CIFS_OP_MASK;
+
+ for (i = 0; i < num_rqst; i++)
+ resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */
+
+ if ((ses == NULL) || (ses->server == NULL)) {
+ cifs_dbg(VFS, "Null session\n");
+ return -EIO;
+ }
+
+ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /*
+ * Ensure we obtain 1 credit per request in the compound chain.
+ * It can be optimized further by waiting for all the credits
+ * at once but this can wait long enough if we don't have enough
+ * credits due to some heavy operations in progress or the server
+ * not granting us much, so a fallback to the current approach is
+ * needed anyway.
+ */
+ for (i = 0; i < num_rqst; i++) {
+ rc = wait_for_free_request(ses->server, timeout, optype);
+ if (rc) {
+ /*
+ * We haven't sent an SMB packet to the server yet but
+ * we already obtained credits for i requests in the
+ * compound chain - need to return those credits back
+ * for future use. Note that we need to call add_credits
+ * multiple times to match the way we obtained credits
+ * in the first place and to account for in flight
+ * requests correctly.
+ */
+ for (j = 0; j < i; j++)
+ add_credits(ses->server, 1, optype);
+ return rc;
+ }
+ credits[i] = 1;
+ }
+
+ /*
+ * Make sure that we sign in the same order that we send on this socket
+ * and avoid races inside tcp sendmsg code that could cause corruption
+ * of smb data.
+ */
+
+ mutex_lock(&ses->server->srv_mutex);
+
+ for (i = 0; i < num_rqst; i++) {
+ midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]);
+ if (IS_ERR(midQ[i])) {
+ revert_current_mid(ses->server, i);
+ for (j = 0; j < i; j++)
+ cifs_delete_mid(midQ[j]);
+ mutex_unlock(&ses->server->srv_mutex);
+
+ /* Update # of requests on wire to server */
+ for (j = 0; j < num_rqst; j++)
+ add_credits(ses->server, credits[j], optype);
+ return PTR_ERR(midQ[i]);
+ }
+
+ midQ[i]->mid_state = MID_REQUEST_SUBMITTED;
+ /*
+ * We don't invoke the callback compounds unless it is the last
+ * request.
+ */
+ if (i < num_rqst - 1)
+ midQ[i]->callback = cifs_noop_callback;
+ }
+ cifs_in_send_inc(ses->server);
+ rc = smb_send_rqst(ses->server, num_rqst, rqst, flags);
+ cifs_in_send_dec(ses->server);
+
+ for (i = 0; i < num_rqst; i++)
+ cifs_save_when_sent(midQ[i]);
+
+ if (rc < 0) {
+ revert_current_mid(ses->server, num_rqst);
+ ses->server->sequence_number -= 2;
+ }
+
+ mutex_unlock(&ses->server->srv_mutex);
+
+ if (rc < 0)
+ goto out;
+
+ /*
+ * Compounding is never used during session establish.
+ */
+ if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
+ mutex_lock(&ses->server->srv_mutex);
+ smb311_update_preauth_hash(ses, rqst[0].rq_iov,
+ rqst[0].rq_nvec);
+ mutex_unlock(&ses->server->srv_mutex);
+ }
+
+ if (timeout == CIFS_ASYNC_OP)
+ goto out;
+
+ for (i = 0; i < num_rqst; i++) {
+ rc = wait_for_response(ses->server, midQ[i]);
+ if (rc != 0) {
+ cifs_dbg(FYI, "Cancelling wait for mid %llu\n",
+ midQ[i]->mid);
+ send_cancel(ses->server, &rqst[i], midQ[i]);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
+ midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
+ midQ[i]->callback = DeleteMidQEntry;
+ cancelled_mid[i] = true;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ }
+ }
+
+ for (i = 0; i < num_rqst; i++)
+ if (!cancelled_mid[i] && midQ[i]->resp_buf
+ && (midQ[i]->mid_state == MID_RESPONSE_RECEIVED))
+ credits[i] = ses->server->ops->get_credits(midQ[i]);
+
+ for (i = 0; i < num_rqst; i++) {
+ if (rc < 0)
+ goto out;
+
+ rc = cifs_sync_mid_result(midQ[i], ses->server);
+ if (rc != 0) {
+ /* mark this mid as cancelled to not free it below */
+ cancelled_mid[i] = true;
+ goto out;
+ }
+
+ if (!midQ[i]->resp_buf ||
+ midQ[i]->mid_state != MID_RESPONSE_RECEIVED) {
+ rc = -EIO;
+ cifs_dbg(FYI, "Bad MID state?\n");
+ goto out;
+ }
+
+ buf = (char *)midQ[i]->resp_buf;
+ resp_iov[i].iov_base = buf;
+ resp_iov[i].iov_len = midQ[i]->resp_buf_size +
+ ses->server->vals->header_preamble_size;
+
+ if (midQ[i]->large_buf)
+ resp_buf_type[i] = CIFS_LARGE_BUFFER;
+ else
+ resp_buf_type[i] = CIFS_SMALL_BUFFER;
+
+ rc = ses->server->ops->check_receive(midQ[i], ses->server,
+ flags & CIFS_LOG_ERROR);
+
+ /* mark it so buf will not be freed by cifs_delete_mid */
+ if ((flags & CIFS_NO_RESP) == 0)
+ midQ[i]->resp_buf = NULL;
+
+ }
+
+ /*
+ * Compounding is never used during session establish.
+ */
+ if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
+ struct kvec iov = {
+ .iov_base = resp_iov[0].iov_base,
+ .iov_len = resp_iov[0].iov_len
+ };
+ mutex_lock(&ses->server->srv_mutex);
+ smb311_update_preauth_hash(ses, &iov, 1);
+ mutex_unlock(&ses->server->srv_mutex);
+ }
+
+out:
+ /*
+ * This will dequeue all mids. After this it is important that the
+ * demultiplex_thread will not process any of these mids any futher.
+ * This is prevented above by using a noop callback that will not
+ * wake this thread except for the very last PDU.
+ */
+ for (i = 0; i < num_rqst; i++) {
+ if (!cancelled_mid[i])
+ cifs_delete_mid(midQ[i]);
+ add_credits(ses->server, credits[i], optype);
+ }
+
+ return rc;
+}
+
+int
+cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_rqst *rqst, int *resp_buf_type, const int flags,
+ struct kvec *resp_iov)
+{
+ return compound_send_recv(xid, ses, flags, 1, rqst, resp_buf_type,
+ resp_iov);
+}
+
+int
+SendReceive2(const unsigned int xid, struct cifs_ses *ses,
+ struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
+ const int flags, struct kvec *resp_iov)
+{
+ struct smb_rqst rqst;
+ struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
+ int rc;
+
+ if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
+ new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
+ GFP_KERNEL);
+ if (!new_iov) {
+ /* otherwise cifs_send_recv below sets resp_buf_type */
+ *resp_buf_type = CIFS_NO_BUFFER;
+ return -ENOMEM;
+ }
+ } else
+ new_iov = s_iov;
+
+ /* 1st iov is a RFC1001 length followed by the rest of the packet */
+ memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
+
+ new_iov[0].iov_base = new_iov[1].iov_base;
+ new_iov[0].iov_len = 4;
+ new_iov[1].iov_base += 4;
+ new_iov[1].iov_len -= 4;
+
+ memset(&rqst, 0, sizeof(struct smb_rqst));
+ rqst.rq_iov = new_iov;
+ rqst.rq_nvec = n_vec + 1;
+
+ rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov);
+ if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
+ kfree(new_iov);
+ return rc;
+}
+
+int
+SendReceive(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+ int *pbytes_returned, const int timeout)
+{
+ int rc = 0;
+ struct mid_q_entry *midQ;
+ unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
+ struct kvec iov = { .iov_base = in_buf, .iov_len = len };
+ struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+
+ if (ses == NULL) {
+ cifs_dbg(VFS, "Null smb session\n");
+ return -EIO;
+ }
+ if (ses->server == NULL) {
+ cifs_dbg(VFS, "Null tcp session\n");
+ return -EIO;
+ }
+
+ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
+ len);
+ return -EIO;
+ }
+
+ rc = wait_for_free_request(ses->server, timeout, 0);
+ if (rc)
+ return rc;
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ mutex_lock(&ses->server->srv_mutex);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ /* Update # of requests on wire to server */
+ add_credits(ses->server, 1, 0);
+ return rc;
+ }
+
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ goto out;
+ }
+
+ midQ->mid_state = MID_REQUEST_SUBMITTED;
+
+ cifs_in_send_inc(ses->server);
+ rc = smb_send(ses->server, in_buf, len);
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
+
+ if (rc < 0)
+ ses->server->sequence_number -= 2;
+
+ mutex_unlock(&ses->server->srv_mutex);
+
+ if (rc < 0)
+ goto out;
+
+ if (timeout == CIFS_ASYNC_OP)
+ goto out;
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc != 0) {
+ send_cancel(ses->server, &rqst, midQ);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ /* no longer considered to be "in-flight" */
+ midQ->callback = DeleteMidQEntry;
+ spin_unlock(&GlobalMid_Lock);
+ add_credits(ses->server, 1, 0);
+ return rc;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ }
+
+ rc = cifs_sync_mid_result(midQ, ses->server);
+ if (rc != 0) {
+ add_credits(ses->server, 1, 0);
+ return rc;
+ }
+
+ if (!midQ->resp_buf || !out_buf ||
+ midQ->mid_state != MID_RESPONSE_RECEIVED) {
+ rc = -EIO;
+ cifs_dbg(VFS, "Bad MID state?\n");
+ goto out;
+ }
+
+ *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+ memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
+ rc = cifs_check_receive(midQ, ses->server, 0);
+out:
+ cifs_delete_mid(midQ);
+ add_credits(ses->server, 1, 0);
+
+ return rc;
+}
+
+/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
+ blocking lock to return. */
+
+static int
+send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
+ struct smb_hdr *in_buf,
+ struct smb_hdr *out_buf)
+{
+ int bytes_returned;
+ struct cifs_ses *ses = tcon->ses;
+ LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+
+ /* We just modify the current in_buf to change
+ the type of lock from LOCKING_ANDX_SHARED_LOCK
+ or LOCKING_ANDX_EXCLUSIVE_LOCK to
+ LOCKING_ANDX_CANCEL_LOCK. */
+
+ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+ pSMB->Timeout = 0;
+ pSMB->hdr.Mid = get_next_mid(ses->server);
+
+ return SendReceive(xid, ses, in_buf, out_buf,
+ &bytes_returned, 0);
+}
+
+int
+SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+ int *pbytes_returned)
+{
+ int rc = 0;
+ int rstart = 0;
+ struct mid_q_entry *midQ;
+ struct cifs_ses *ses;
+ unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
+ struct kvec iov = { .iov_base = in_buf, .iov_len = len };
+ struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+
+ if (tcon == NULL || tcon->ses == NULL) {
+ cifs_dbg(VFS, "Null smb session\n");
+ return -EIO;
+ }
+ ses = tcon->ses;
+
+ if (ses->server == NULL) {
+ cifs_dbg(VFS, "Null tcp session\n");
+ return -EIO;
+ }
+
+ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
+ len);
+ return -EIO;
+ }
+
+ rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0);
+ if (rc)
+ return rc;
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ mutex_lock(&ses->server->srv_mutex);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ return rc;
+ }
+
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ cifs_delete_mid(midQ);
+ mutex_unlock(&ses->server->srv_mutex);
+ return rc;
+ }
+
+ midQ->mid_state = MID_REQUEST_SUBMITTED;
+ cifs_in_send_inc(ses->server);
+ rc = smb_send(ses->server, in_buf, len);
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
+
+ if (rc < 0)
+ ses->server->sequence_number -= 2;
+
+ mutex_unlock(&ses->server->srv_mutex);
+
+ if (rc < 0) {
+ cifs_delete_mid(midQ);
+ return rc;
+ }
+
+ /* Wait for a reply - allow signals to interrupt. */
+ rc = wait_event_interruptible(ses->server->response_q,
+ (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ /* Were we interrupted by a signal ? */
+ if ((rc == -ERESTARTSYS) &&
+ (midQ->mid_state == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ if (in_buf->Command == SMB_COM_TRANSACTION2) {
+ /* POSIX lock. We send a NT_CANCEL SMB to cause the
+ blocking lock to return. */
+ rc = send_cancel(ses->server, &rqst, midQ);
+ if (rc) {
+ cifs_delete_mid(midQ);
+ return rc;
+ }
+ } else {
+ /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
+ to cause the blocking lock to return. */
+
+ rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
+
+ /* If we get -ENOLCK back the lock may have
+ already been removed. Don't exit in this case. */
+ if (rc && rc != -ENOLCK) {
+ cifs_delete_mid(midQ);
+ return rc;
+ }
+ }
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc) {
+ send_cancel(ses->server, &rqst, midQ);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ /* no longer considered to be "in-flight" */
+ midQ->callback = DeleteMidQEntry;
+ spin_unlock(&GlobalMid_Lock);
+ return rc;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ }
+
+ /* We got the response - restart system call. */
+ rstart = 1;
+ }
+
+ rc = cifs_sync_mid_result(midQ, ses->server);
+ if (rc != 0)
+ return rc;
+
+ /* rcvd frame is ok */
+ if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {
+ rc = -EIO;
+ cifs_dbg(VFS, "Bad MID state?\n");
+ goto out;
+ }
+
+ *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+ memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
+ rc = cifs_check_receive(midQ, ses->server, 0);
+out:
+ cifs_delete_mid(midQ);
+ if (rstart && rc == -EACCES)
+ return -ERESTARTSYS;
+ return rc;
+}
diff --git a/fs/cifs/winucase.c b/fs/cifs/winucase.c
new file mode 100644
index 000000000..1506d4fdd
--- /dev/null
+++ b/fs/cifs/winucase.c
@@ -0,0 +1,663 @@
+/*
+ * fs/cifs/winucase.c
+ *
+ * Copyright (c) Jeffrey Layton <jlayton@redhat.com>, 2013
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The const tables in this file were converted from the following info
+ * provided by Microsoft:
+ *
+ * 3.1.5.3 Mapping UTF-16 Strings to Upper Case:
+ *
+ * http://msdn.microsoft.com/en-us/library/hh877830.aspx
+ * http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=10921
+ *
+ * In particular, the table in "Windows 8 Upper Case Mapping Table.txt" was
+ * post-processed using the winucase_convert.pl script.
+ */
+
+#include <linux/nls.h>
+
+wchar_t cifs_toupper(wchar_t in); /* quiet sparse */
+
+static const wchar_t t2_00[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0000,
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178,
+};
+
+static const wchar_t t2_01[256] = {
+ 0x0000, 0x0100, 0x0000, 0x0102, 0x0000, 0x0104, 0x0000, 0x0106,
+ 0x0000, 0x0108, 0x0000, 0x010a, 0x0000, 0x010c, 0x0000, 0x010e,
+ 0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0000, 0x0116,
+ 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x011c, 0x0000, 0x011e,
+ 0x0000, 0x0120, 0x0000, 0x0122, 0x0000, 0x0124, 0x0000, 0x0126,
+ 0x0000, 0x0128, 0x0000, 0x012a, 0x0000, 0x012c, 0x0000, 0x012e,
+ 0x0000, 0x0000, 0x0000, 0x0132, 0x0000, 0x0134, 0x0000, 0x0136,
+ 0x0000, 0x0000, 0x0139, 0x0000, 0x013b, 0x0000, 0x013d, 0x0000,
+ 0x013f, 0x0000, 0x0141, 0x0000, 0x0143, 0x0000, 0x0145, 0x0000,
+ 0x0147, 0x0000, 0x0000, 0x014a, 0x0000, 0x014c, 0x0000, 0x014e,
+ 0x0000, 0x0150, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156,
+ 0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x0000, 0x015e,
+ 0x0000, 0x0160, 0x0000, 0x0162, 0x0000, 0x0164, 0x0000, 0x0166,
+ 0x0000, 0x0168, 0x0000, 0x016a, 0x0000, 0x016c, 0x0000, 0x016e,
+ 0x0000, 0x0170, 0x0000, 0x0172, 0x0000, 0x0174, 0x0000, 0x0176,
+ 0x0000, 0x0000, 0x0179, 0x0000, 0x017b, 0x0000, 0x017d, 0x0000,
+ 0x0243, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0000, 0x0000,
+ 0x0187, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x01f6, 0x0000, 0x0000,
+ 0x0000, 0x0198, 0x023d, 0x0000, 0x0000, 0x0000, 0x0220, 0x0000,
+ 0x0000, 0x01a0, 0x0000, 0x01a2, 0x0000, 0x01a4, 0x0000, 0x0000,
+ 0x01a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ac, 0x0000, 0x0000,
+ 0x01af, 0x0000, 0x0000, 0x0000, 0x01b3, 0x0000, 0x01b5, 0x0000,
+ 0x0000, 0x01b8, 0x0000, 0x0000, 0x0000, 0x01bc, 0x0000, 0x01f7,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01c4, 0x0000,
+ 0x0000, 0x01c7, 0x0000, 0x0000, 0x01ca, 0x0000, 0x01cd, 0x0000,
+ 0x01cf, 0x0000, 0x01d1, 0x0000, 0x01d3, 0x0000, 0x01d5, 0x0000,
+ 0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x018e, 0x0000, 0x01de,
+ 0x0000, 0x01e0, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6,
+ 0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee,
+ 0x0000, 0x0000, 0x0000, 0x01f1, 0x0000, 0x01f4, 0x0000, 0x0000,
+ 0x0000, 0x01f8, 0x0000, 0x01fa, 0x0000, 0x01fc, 0x0000, 0x01fe,
+};
+
+static const wchar_t t2_02[256] = {
+ 0x0000, 0x0200, 0x0000, 0x0202, 0x0000, 0x0204, 0x0000, 0x0206,
+ 0x0000, 0x0208, 0x0000, 0x020a, 0x0000, 0x020c, 0x0000, 0x020e,
+ 0x0000, 0x0210, 0x0000, 0x0212, 0x0000, 0x0214, 0x0000, 0x0216,
+ 0x0000, 0x0218, 0x0000, 0x021a, 0x0000, 0x021c, 0x0000, 0x021e,
+ 0x0000, 0x0000, 0x0000, 0x0222, 0x0000, 0x0224, 0x0000, 0x0226,
+ 0x0000, 0x0228, 0x0000, 0x022a, 0x0000, 0x022c, 0x0000, 0x022e,
+ 0x0000, 0x0230, 0x0000, 0x0232, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x023b, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0241, 0x0000, 0x0000, 0x0000, 0x0000, 0x0246,
+ 0x0000, 0x0248, 0x0000, 0x024a, 0x0000, 0x024c, 0x0000, 0x024e,
+ 0x2c6f, 0x2c6d, 0x0000, 0x0181, 0x0186, 0x0000, 0x0189, 0x018a,
+ 0x0000, 0x018f, 0x0000, 0x0190, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0193, 0x0000, 0x0000, 0x0194, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0197, 0x0196, 0x0000, 0x2c62, 0x0000, 0x0000, 0x0000, 0x019c,
+ 0x0000, 0x2c6e, 0x019d, 0x0000, 0x0000, 0x019f, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2c64, 0x0000, 0x0000,
+ 0x01a6, 0x0000, 0x0000, 0x01a9, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x01b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_03[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0370, 0x0000, 0x0372, 0x0000, 0x0000, 0x0000, 0x0376,
+ 0x0000, 0x0000, 0x0000, 0x03fd, 0x03fe, 0x03ff, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0386, 0x0388, 0x0389, 0x038a,
+ 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+ 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03cf,
+ 0x0000, 0x03d8, 0x0000, 0x03da, 0x0000, 0x03dc, 0x0000, 0x03de,
+ 0x0000, 0x03e0, 0x0000, 0x03e2, 0x0000, 0x03e4, 0x0000, 0x03e6,
+ 0x0000, 0x03e8, 0x0000, 0x03ea, 0x0000, 0x03ec, 0x0000, 0x03ee,
+ 0x0000, 0x0000, 0x03f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x03f7, 0x0000, 0x0000, 0x03fa, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_04[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
+ 0x0000, 0x0460, 0x0000, 0x0462, 0x0000, 0x0464, 0x0000, 0x0466,
+ 0x0000, 0x0468, 0x0000, 0x046a, 0x0000, 0x046c, 0x0000, 0x046e,
+ 0x0000, 0x0470, 0x0000, 0x0472, 0x0000, 0x0474, 0x0000, 0x0476,
+ 0x0000, 0x0478, 0x0000, 0x047a, 0x0000, 0x047c, 0x0000, 0x047e,
+ 0x0000, 0x0480, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x048a, 0x0000, 0x048c, 0x0000, 0x048e,
+ 0x0000, 0x0490, 0x0000, 0x0492, 0x0000, 0x0494, 0x0000, 0x0496,
+ 0x0000, 0x0498, 0x0000, 0x049a, 0x0000, 0x049c, 0x0000, 0x049e,
+ 0x0000, 0x04a0, 0x0000, 0x04a2, 0x0000, 0x04a4, 0x0000, 0x04a6,
+ 0x0000, 0x04a8, 0x0000, 0x04aa, 0x0000, 0x04ac, 0x0000, 0x04ae,
+ 0x0000, 0x04b0, 0x0000, 0x04b2, 0x0000, 0x04b4, 0x0000, 0x04b6,
+ 0x0000, 0x04b8, 0x0000, 0x04ba, 0x0000, 0x04bc, 0x0000, 0x04be,
+ 0x0000, 0x0000, 0x04c1, 0x0000, 0x04c3, 0x0000, 0x04c5, 0x0000,
+ 0x04c7, 0x0000, 0x04c9, 0x0000, 0x04cb, 0x0000, 0x04cd, 0x04c0,
+ 0x0000, 0x04d0, 0x0000, 0x04d2, 0x0000, 0x04d4, 0x0000, 0x04d6,
+ 0x0000, 0x04d8, 0x0000, 0x04da, 0x0000, 0x04dc, 0x0000, 0x04de,
+ 0x0000, 0x04e0, 0x0000, 0x04e2, 0x0000, 0x04e4, 0x0000, 0x04e6,
+ 0x0000, 0x04e8, 0x0000, 0x04ea, 0x0000, 0x04ec, 0x0000, 0x04ee,
+ 0x0000, 0x04f0, 0x0000, 0x04f2, 0x0000, 0x04f4, 0x0000, 0x04f6,
+ 0x0000, 0x04f8, 0x0000, 0x04fa, 0x0000, 0x04fc, 0x0000, 0x04fe,
+};
+
+static const wchar_t t2_05[256] = {
+ 0x0000, 0x0500, 0x0000, 0x0502, 0x0000, 0x0504, 0x0000, 0x0506,
+ 0x0000, 0x0508, 0x0000, 0x050a, 0x0000, 0x050c, 0x0000, 0x050e,
+ 0x0000, 0x0510, 0x0000, 0x0512, 0x0000, 0x0514, 0x0000, 0x0516,
+ 0x0000, 0x0518, 0x0000, 0x051a, 0x0000, 0x051c, 0x0000, 0x051e,
+ 0x0000, 0x0520, 0x0000, 0x0522, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,
+ 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f,
+ 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,
+ 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f,
+ 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_1d[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xa77d, 0x0000, 0x0000, 0x0000, 0x2c63, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_1e[256] = {
+ 0x0000, 0x1e00, 0x0000, 0x1e02, 0x0000, 0x1e04, 0x0000, 0x1e06,
+ 0x0000, 0x1e08, 0x0000, 0x1e0a, 0x0000, 0x1e0c, 0x0000, 0x1e0e,
+ 0x0000, 0x1e10, 0x0000, 0x1e12, 0x0000, 0x1e14, 0x0000, 0x1e16,
+ 0x0000, 0x1e18, 0x0000, 0x1e1a, 0x0000, 0x1e1c, 0x0000, 0x1e1e,
+ 0x0000, 0x1e20, 0x0000, 0x1e22, 0x0000, 0x1e24, 0x0000, 0x1e26,
+ 0x0000, 0x1e28, 0x0000, 0x1e2a, 0x0000, 0x1e2c, 0x0000, 0x1e2e,
+ 0x0000, 0x1e30, 0x0000, 0x1e32, 0x0000, 0x1e34, 0x0000, 0x1e36,
+ 0x0000, 0x1e38, 0x0000, 0x1e3a, 0x0000, 0x1e3c, 0x0000, 0x1e3e,
+ 0x0000, 0x1e40, 0x0000, 0x1e42, 0x0000, 0x1e44, 0x0000, 0x1e46,
+ 0x0000, 0x1e48, 0x0000, 0x1e4a, 0x0000, 0x1e4c, 0x0000, 0x1e4e,
+ 0x0000, 0x1e50, 0x0000, 0x1e52, 0x0000, 0x1e54, 0x0000, 0x1e56,
+ 0x0000, 0x1e58, 0x0000, 0x1e5a, 0x0000, 0x1e5c, 0x0000, 0x1e5e,
+ 0x0000, 0x1e60, 0x0000, 0x1e62, 0x0000, 0x1e64, 0x0000, 0x1e66,
+ 0x0000, 0x1e68, 0x0000, 0x1e6a, 0x0000, 0x1e6c, 0x0000, 0x1e6e,
+ 0x0000, 0x1e70, 0x0000, 0x1e72, 0x0000, 0x1e74, 0x0000, 0x1e76,
+ 0x0000, 0x1e78, 0x0000, 0x1e7a, 0x0000, 0x1e7c, 0x0000, 0x1e7e,
+ 0x0000, 0x1e80, 0x0000, 0x1e82, 0x0000, 0x1e84, 0x0000, 0x1e86,
+ 0x0000, 0x1e88, 0x0000, 0x1e8a, 0x0000, 0x1e8c, 0x0000, 0x1e8e,
+ 0x0000, 0x1e90, 0x0000, 0x1e92, 0x0000, 0x1e94, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x1ea0, 0x0000, 0x1ea2, 0x0000, 0x1ea4, 0x0000, 0x1ea6,
+ 0x0000, 0x1ea8, 0x0000, 0x1eaa, 0x0000, 0x1eac, 0x0000, 0x1eae,
+ 0x0000, 0x1eb0, 0x0000, 0x1eb2, 0x0000, 0x1eb4, 0x0000, 0x1eb6,
+ 0x0000, 0x1eb8, 0x0000, 0x1eba, 0x0000, 0x1ebc, 0x0000, 0x1ebe,
+ 0x0000, 0x1ec0, 0x0000, 0x1ec2, 0x0000, 0x1ec4, 0x0000, 0x1ec6,
+ 0x0000, 0x1ec8, 0x0000, 0x1eca, 0x0000, 0x1ecc, 0x0000, 0x1ece,
+ 0x0000, 0x1ed0, 0x0000, 0x1ed2, 0x0000, 0x1ed4, 0x0000, 0x1ed6,
+ 0x0000, 0x1ed8, 0x0000, 0x1eda, 0x0000, 0x1edc, 0x0000, 0x1ede,
+ 0x0000, 0x1ee0, 0x0000, 0x1ee2, 0x0000, 0x1ee4, 0x0000, 0x1ee6,
+ 0x0000, 0x1ee8, 0x0000, 0x1eea, 0x0000, 0x1eec, 0x0000, 0x1eee,
+ 0x0000, 0x1ef0, 0x0000, 0x1ef2, 0x0000, 0x1ef4, 0x0000, 0x1ef6,
+ 0x0000, 0x1ef8, 0x0000, 0x1efa, 0x0000, 0x1efc, 0x0000, 0x1efe,
+};
+
+static const wchar_t t2_1f[256] = {
+ 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x1f59, 0x0000, 0x1f5b, 0x0000, 0x1f5d, 0x0000, 0x1f5f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb,
+ 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x0000, 0x0000,
+ 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1fb8, 0x1fb9, 0x0000, 0x1fbc, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x1fcc, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1fd8, 0x1fd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1fe8, 0x1fe9, 0x0000, 0x0000, 0x0000, 0x1fec, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x1ffc, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_21[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2132, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167,
+ 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x2183, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_24[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd,
+ 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5,
+ 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd,
+ 0x24ce, 0x24cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_2c[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07,
+ 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f,
+ 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17,
+ 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f,
+ 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27,
+ 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x0000,
+ 0x0000, 0x2c60, 0x0000, 0x0000, 0x0000, 0x023a, 0x023e, 0x0000,
+ 0x2c67, 0x0000, 0x2c69, 0x0000, 0x2c6b, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x2c72, 0x0000, 0x0000, 0x2c75, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2c80, 0x0000, 0x2c82, 0x0000, 0x2c84, 0x0000, 0x2c86,
+ 0x0000, 0x2c88, 0x0000, 0x2c8a, 0x0000, 0x2c8c, 0x0000, 0x2c8e,
+ 0x0000, 0x2c90, 0x0000, 0x2c92, 0x0000, 0x2c94, 0x0000, 0x2c96,
+ 0x0000, 0x2c98, 0x0000, 0x2c9a, 0x0000, 0x2c9c, 0x0000, 0x2c9e,
+ 0x0000, 0x2ca0, 0x0000, 0x2ca2, 0x0000, 0x2ca4, 0x0000, 0x2ca6,
+ 0x0000, 0x2ca8, 0x0000, 0x2caa, 0x0000, 0x2cac, 0x0000, 0x2cae,
+ 0x0000, 0x2cb0, 0x0000, 0x2cb2, 0x0000, 0x2cb4, 0x0000, 0x2cb6,
+ 0x0000, 0x2cb8, 0x0000, 0x2cba, 0x0000, 0x2cbc, 0x0000, 0x2cbe,
+ 0x0000, 0x2cc0, 0x0000, 0x2cc2, 0x0000, 0x2cc4, 0x0000, 0x2cc6,
+ 0x0000, 0x2cc8, 0x0000, 0x2cca, 0x0000, 0x2ccc, 0x0000, 0x2cce,
+ 0x0000, 0x2cd0, 0x0000, 0x2cd2, 0x0000, 0x2cd4, 0x0000, 0x2cd6,
+ 0x0000, 0x2cd8, 0x0000, 0x2cda, 0x0000, 0x2cdc, 0x0000, 0x2cde,
+ 0x0000, 0x2ce0, 0x0000, 0x2ce2, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_2d[256] = {
+ 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7,
+ 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af,
+ 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7,
+ 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf,
+ 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_a6[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xa640, 0x0000, 0xa642, 0x0000, 0xa644, 0x0000, 0xa646,
+ 0x0000, 0xa648, 0x0000, 0xa64a, 0x0000, 0xa64c, 0x0000, 0xa64e,
+ 0x0000, 0xa650, 0x0000, 0xa652, 0x0000, 0xa654, 0x0000, 0xa656,
+ 0x0000, 0xa658, 0x0000, 0xa65a, 0x0000, 0xa65c, 0x0000, 0xa65e,
+ 0x0000, 0x0000, 0x0000, 0xa662, 0x0000, 0xa664, 0x0000, 0xa666,
+ 0x0000, 0xa668, 0x0000, 0xa66a, 0x0000, 0xa66c, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xa680, 0x0000, 0xa682, 0x0000, 0xa684, 0x0000, 0xa686,
+ 0x0000, 0xa688, 0x0000, 0xa68a, 0x0000, 0xa68c, 0x0000, 0xa68e,
+ 0x0000, 0xa690, 0x0000, 0xa692, 0x0000, 0xa694, 0x0000, 0xa696,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_a7[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0xa722, 0x0000, 0xa724, 0x0000, 0xa726,
+ 0x0000, 0xa728, 0x0000, 0xa72a, 0x0000, 0xa72c, 0x0000, 0xa72e,
+ 0x0000, 0x0000, 0x0000, 0xa732, 0x0000, 0xa734, 0x0000, 0xa736,
+ 0x0000, 0xa738, 0x0000, 0xa73a, 0x0000, 0xa73c, 0x0000, 0xa73e,
+ 0x0000, 0xa740, 0x0000, 0xa742, 0x0000, 0xa744, 0x0000, 0xa746,
+ 0x0000, 0xa748, 0x0000, 0xa74a, 0x0000, 0xa74c, 0x0000, 0xa74e,
+ 0x0000, 0xa750, 0x0000, 0xa752, 0x0000, 0xa754, 0x0000, 0xa756,
+ 0x0000, 0xa758, 0x0000, 0xa75a, 0x0000, 0xa75c, 0x0000, 0xa75e,
+ 0x0000, 0xa760, 0x0000, 0xa762, 0x0000, 0xa764, 0x0000, 0xa766,
+ 0x0000, 0xa768, 0x0000, 0xa76a, 0x0000, 0xa76c, 0x0000, 0xa76e,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0xa779, 0x0000, 0xa77b, 0x0000, 0x0000, 0xa77e,
+ 0x0000, 0xa780, 0x0000, 0xa782, 0x0000, 0xa784, 0x0000, 0xa786,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xa78b, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t t2_ff[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27,
+ 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f,
+ 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37,
+ 0xff38, 0xff39, 0xff3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const wchar_t *const toplevel[256] = {
+ t2_00, t2_01, t2_02, t2_03, t2_04, t2_05, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, t2_1d, t2_1e, t2_1f,
+ NULL, t2_21, NULL, NULL, t2_24, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, t2_2c, t2_2d, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, t2_a6, t2_a7,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, t2_ff,
+};
+
+/**
+ * cifs_toupper - convert a wchar_t from lower to uppercase
+ * @in: character to convert from lower to uppercase
+ *
+ * This function consults the static tables above to convert a wchar_t from
+ * lower to uppercase. In the event that there is no mapping, the original
+ * "in" character is returned.
+ */
+wchar_t
+cifs_toupper(wchar_t in)
+{
+ unsigned char idx;
+ const wchar_t *tbl;
+ wchar_t out;
+
+ /* grab upper byte */
+ idx = (in & 0xff00) >> 8;
+
+ /* find pointer to 2nd layer table */
+ tbl = toplevel[idx];
+ if (!tbl)
+ return in;
+
+ /* grab lower byte */
+ idx = in & 0xff;
+
+ /* look up character in table */
+ out = tbl[idx];
+ if (out)
+ return out;
+
+ return in;
+}
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
new file mode 100644
index 000000000..a2db401a5
--- /dev/null
+++ b/fs/cifs/xattr.c
@@ -0,0 +1,411 @@
+/*
+ * fs/cifs/xattr.c
+ *
+ * Copyright (c) International Business Machines Corp., 2003, 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/slab.h>
+#include <linux/xattr.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+
+#define MAX_EA_VALUE_SIZE CIFSMaxBufSize
+#define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
+#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */
+#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */
+/*
+ * Although these three are just aliases for the above, need to move away from
+ * confusing users and using the 20+ year old term 'cifs' when it is no longer
+ * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago
+ */
+#define SMB3_XATTR_CIFS_ACL "system.smb3_acl"
+#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */
+#define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */
+/* BB need to add server (Samba e.g) support for security and trusted prefix */
+
+enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT };
+
+static int cifs_xattr_set(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *name, const void *value,
+ size_t size, int flags)
+{
+ int rc = -EOPNOTSUPP;
+ unsigned int xid;
+ struct super_block *sb = dentry->d_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ char *full_path;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ pTcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ full_path = build_path_from_dentry(dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+
+ /* if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+ if (size > MAX_EA_VALUE_SIZE) {
+ cifs_dbg(FYI, "size of EA value too large\n");
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ switch (handler->flags) {
+ case XATTR_USER:
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto out;
+
+ if (pTcon->ses->server->ops->set_EA)
+ rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
+ full_path, name, value, (__u16)size,
+ cifs_sb->local_nls, cifs_sb);
+ break;
+
+ case XATTR_CIFS_ACL: {
+#ifdef CONFIG_CIFS_ACL
+ struct cifs_ntsd *pacl;
+
+ if (!value)
+ goto out;
+ pacl = kmalloc(size, GFP_KERNEL);
+ if (!pacl) {
+ rc = -ENOMEM;
+ } else {
+ memcpy(pacl, value, size);
+ if (value &&
+ pTcon->ses->server->ops->set_acl)
+ rc = pTcon->ses->server->ops->set_acl(pacl,
+ size, inode,
+ full_path, CIFS_ACL_DACL);
+ else
+ rc = -EOPNOTSUPP;
+ if (rc == 0) /* force revalidate of the inode */
+ CIFS_I(inode)->time = 0;
+ kfree(pacl);
+ }
+#endif /* CONFIG_CIFS_ACL */
+ break;
+ }
+
+ case XATTR_ACL_ACCESS:
+#ifdef CONFIG_CIFS_POSIX
+ if (!value)
+ goto out;
+ if (sb->s_flags & SB_POSIXACL)
+ rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
+ value, (const int)size,
+ ACL_TYPE_ACCESS, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+#endif /* CONFIG_CIFS_POSIX */
+ break;
+
+ case XATTR_ACL_DEFAULT:
+#ifdef CONFIG_CIFS_POSIX
+ if (!value)
+ goto out;
+ if (sb->s_flags & SB_POSIXACL)
+ rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
+ value, (const int)size,
+ ACL_TYPE_DEFAULT, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+#endif /* CONFIG_CIFS_POSIX */
+ break;
+ }
+
+out:
+ kfree(full_path);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static int cifs_attrib_get(struct dentry *dentry,
+ struct inode *inode, void *value,
+ size_t size)
+{
+ ssize_t rc;
+ __u32 *pattribute;
+
+ rc = cifs_revalidate_dentry_attr(dentry);
+
+ if (rc)
+ return rc;
+
+ if ((value == NULL) || (size == 0))
+ return sizeof(__u32);
+ else if (size < sizeof(__u32))
+ return -ERANGE;
+
+ /* return dos attributes as pseudo xattr */
+ pattribute = (__u32 *)value;
+ *pattribute = CIFS_I(inode)->cifsAttrs;
+
+ return sizeof(__u32);
+}
+
+static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode,
+ void *value, size_t size)
+{
+ ssize_t rc;
+ __u64 * pcreatetime;
+
+ rc = cifs_revalidate_dentry_attr(dentry);
+ if (rc)
+ return rc;
+
+ if ((value == NULL) || (size == 0))
+ return sizeof(__u64);
+ else if (size < sizeof(__u64))
+ return -ERANGE;
+
+ /* return dos attributes as pseudo xattr */
+ pcreatetime = (__u64 *)value;
+ *pcreatetime = CIFS_I(inode)->createtime;
+ return sizeof(__u64);
+}
+
+
+static int cifs_xattr_get(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *name, void *value, size_t size)
+{
+ ssize_t rc = -EOPNOTSUPP;
+ unsigned int xid;
+ struct super_block *sb = dentry->d_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ char *full_path;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ pTcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ full_path = build_path_from_dentry(dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* return alt name if available as pseudo attr */
+ switch (handler->flags) {
+ case XATTR_USER:
+ cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
+ if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) ||
+ (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) {
+ rc = cifs_attrib_get(dentry, inode, value, size);
+ break;
+ } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) ||
+ (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) {
+ rc = cifs_creation_time_get(dentry, inode, value, size);
+ break;
+ }
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto out;
+
+ if (pTcon->ses->server->ops->query_all_EAs)
+ rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
+ full_path, name, value, size, cifs_sb);
+ break;
+
+ case XATTR_CIFS_ACL: {
+#ifdef CONFIG_CIFS_ACL
+ u32 acllen;
+ struct cifs_ntsd *pacl;
+
+ if (pTcon->ses->server->ops->get_acl == NULL)
+ goto out; /* rc already EOPNOTSUPP */
+
+ pacl = pTcon->ses->server->ops->get_acl(cifs_sb,
+ inode, full_path, &acllen);
+ if (IS_ERR(pacl)) {
+ rc = PTR_ERR(pacl);
+ cifs_dbg(VFS, "%s: error %zd getting sec desc\n",
+ __func__, rc);
+ } else {
+ if (value) {
+ if (acllen > size)
+ acllen = -ERANGE;
+ else
+ memcpy(value, pacl, acllen);
+ }
+ rc = acllen;
+ kfree(pacl);
+ }
+#endif /* CONFIG_CIFS_ACL */
+ break;
+ }
+
+ case XATTR_ACL_ACCESS:
+#ifdef CONFIG_CIFS_POSIX
+ if (sb->s_flags & SB_POSIXACL)
+ rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
+ value, size, ACL_TYPE_ACCESS,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+#endif /* CONFIG_CIFS_POSIX */
+ break;
+
+ case XATTR_ACL_DEFAULT:
+#ifdef CONFIG_CIFS_POSIX
+ if (sb->s_flags & SB_POSIXACL)
+ rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
+ value, size, ACL_TYPE_DEFAULT,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+#endif /* CONFIG_CIFS_POSIX */
+ break;
+ }
+
+ /* We could add an additional check for streams ie
+ if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+
+ if (rc == -EINVAL)
+ rc = -EOPNOTSUPP;
+
+out:
+ kfree(full_path);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
+{
+ ssize_t rc = -EOPNOTSUPP;
+ unsigned int xid;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *pTcon;
+ char *full_path;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ return -EOPNOTSUPP;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ pTcon = tlink_tcon(tlink);
+
+ xid = get_xid();
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto list_ea_exit;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+
+ /* if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+
+ if (pTcon->ses->server->ops->query_all_EAs)
+ rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
+ full_path, NULL, data, buf_size, cifs_sb);
+list_ea_exit:
+ kfree(full_path);
+ free_xid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static const struct xattr_handler cifs_user_xattr_handler = {
+ .prefix = XATTR_USER_PREFIX,
+ .flags = XATTR_USER,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+/* os2.* attributes are treated like user.* attributes */
+static const struct xattr_handler cifs_os2_xattr_handler = {
+ .prefix = XATTR_OS2_PREFIX,
+ .flags = XATTR_USER,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+static const struct xattr_handler cifs_cifs_acl_xattr_handler = {
+ .name = CIFS_XATTR_CIFS_ACL,
+ .flags = XATTR_CIFS_ACL,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+/*
+ * Although this is just an alias for the above, need to move away from
+ * confusing users and using the 20 year old term 'cifs' when it is no
+ * longer secure and was replaced by SMB2/SMB3 a long time ago, and
+ * SMB3 and later are highly secure.
+ */
+static const struct xattr_handler smb3_acl_xattr_handler = {
+ .name = SMB3_XATTR_CIFS_ACL,
+ .flags = XATTR_CIFS_ACL,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+static const struct xattr_handler cifs_posix_acl_access_xattr_handler = {
+ .name = XATTR_NAME_POSIX_ACL_ACCESS,
+ .flags = XATTR_ACL_ACCESS,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+static const struct xattr_handler cifs_posix_acl_default_xattr_handler = {
+ .name = XATTR_NAME_POSIX_ACL_DEFAULT,
+ .flags = XATTR_ACL_DEFAULT,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+const struct xattr_handler *cifs_xattr_handlers[] = {
+ &cifs_user_xattr_handler,
+ &cifs_os2_xattr_handler,
+ &cifs_cifs_acl_xattr_handler,
+ &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
+ &cifs_posix_acl_access_xattr_handler,
+ &cifs_posix_acl_default_xattr_handler,
+ NULL
+};