Commit 49583f79 authored by Pavel Shilovsky's avatar Pavel Shilovsky

Add sources for CentOS 7.0

parent a9210afb
config CIFS
tristate "CIFS support (advanced network filesystem, SMBFS successor)"
depends on INET
select NLS
select CRYPTO
select CRYPTO_MD4
select CRYPTO_MD5
select CRYPTO_HMAC
select CRYPTO_ARC4
select CRYPTO_ECB
select CRYPTO_DES
select CRYPTO_SHA256
select CRYPTO_CMAC
help
This is the client VFS module for the Common Internet File System
(CIFS) protocol which is the successor to the Server Message Block
(SMB) protocol, the native file sharing mechanism for most early
PC operating systems. The CIFS protocol is fully supported by
file servers such as Windows 2000 (including Windows 2003, Windows 2008,
NT 4 and Windows XP) as well by Samba (which provides excellent CIFS
server support for Linux and many other operating systems). Limited
support for OS/2 and Windows ME and similar servers is provided as
well.
The cifs module provides an advanced network file system
client for mounting to CIFS compliant servers. It includes
support for DFS (hierarchical name space), secure per-user
session establishment via Kerberos or NTLM or NTLMv2,
safe distributed caching (oplock), optional packet
signing, Unicode and other internationalization improvements.
If you need to mount to Samba or Windows from this machine, say Y.
config CIFS_STATS
bool "CIFS statistics"
depends on CIFS
help
Enabling this option will cause statistics for each server share
mounted by the cifs client to be displayed in /proc/fs/cifs/Stats
config CIFS_STATS2
bool "Extended statistics"
depends on CIFS_STATS
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_WEAK_PW_HASH
bool "Support legacy servers which use weaker LANMAN security"
depends on CIFS
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 N.
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, or visit
<http://acl.bestbits.at/> 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 N.
config CIFS_POSIX
bool "CIFS POSIX Extensions"
depends on 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.
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_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 N.
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_SMB2
bool "SMB2 network file system support"
depends on CIFS && INET
select NLS
select KEYS
select FSCACHE
select DNS_RESOLVER
help
This enables experimental support for the SMB2 (Server Message Block
version 2) protocol. The SMB2 protocol is the successor to the
popular CIFS and SMB network file sharing protocols. SMB2 is the
native file sharing mechanism for recent versions of Windows
operating systems (since Vista). SMB2 enablement will eventually
allow users better performance, security and features, than would be
possible with cifs. Note that smb2 mount options also are simpler
(compared to cifs) due to protocol improvements.
Unless you are a developer or tester, 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.
#
# Makefile for Linux CIFS VFS client
#
obj-$(CONFIG_CIFS) += etercifs.o
etercifs-y := 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 xattr.o cifsencrypt.o \
readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o
etercifs-$(CONFIG_CIFS_ACL) += cifsacl.o
etercifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
etercifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
etercifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
etercifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o \
smb2misc.o smb2pdu.o smb2inode.o smb2file.o
/*
* 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(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 (%d) exit 0\n",
cls, con, tag, end, *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 (%d) exit 1\n",
cls, con, tag, end, *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 (%d) exit 0\n",
cls, con, tag, end, *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 end = %p (%d) exit 1\n",
cls, con, tag, end, *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;
}
/*
* 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);
}
/*
* Key layout of CIFS server cache index object
*/
struct cifs_server_key {
uint16_t family; /* address family */
__be16 port; /* IP port */
union {
struct in_addr ipv4_addr;
struct in6_addr ipv6_addr;
} addr[0];
};
/*
* Server object keyed by {IPaddress,port,family} tuple
*/
static uint16_t cifs_server_get_key(const void *cookie_netfs_data,
void *buffer, uint16_t maxbuf)
{
const struct TCP_Server_Info *server = cookie_netfs_data;
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 = buffer;
uint16_t key_len = sizeof(struct cifs_server_key);
memset(key, 0, key_len);
/*
* Should not be a problem as sin_family/sin6_family overlays
* sa_family field
*/
switch (sa->sa_family) {
case AF_INET:
key->family = sa->sa_family;
key->port = addr->sin_port;
key->addr[0].ipv4_addr = addr->sin_addr;
key_len += sizeof(key->addr[0].ipv4_addr);
break;
case AF_INET6:
key->family = sa->sa_family;
key->port = addr6->sin6_port;
key->addr[0].ipv6_addr = addr6->sin6_addr;
key_len += sizeof(key->addr[0].ipv6_addr);
break;
default:
cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);
key_len = 0;
break;
}
return key_len;
}
/*
* Server object for FS-Cache
*/
const struct fscache_cookie_def cifs_fscache_server_index_def = {
.name = "CIFS.server",
.type = FSCACHE_COOKIE_TYPE_INDEX,
.get_key = cifs_server_get_key,
};
/*
* Auxiliary data attached to CIFS superblock within the cache
*/
struct cifs_fscache_super_auxdata {
u64 resource_id; /* unique server resource id */
};
static 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;
}
/*
* Superblock object currently keyed by share name
*/
static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer,
uint16_t maxbuf)
{
const struct cifs_tcon *tcon = cookie_netfs_data;
char *sharename;
uint16_t len;
sharename = extract_sharename(tcon->treeName);
if (IS_ERR(sharename)) {
cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
sharename = NULL;
return 0;
}
len = strlen(sharename);
if (len > maxbuf)
return 0;
memcpy(buffer, sharename, len);
kfree(sharename);
return len;
}
static uint16_t
cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer,
uint16_t maxbuf)
{
struct cifs_fscache_super_auxdata auxdata;
const struct cifs_tcon *tcon = cookie_netfs_data;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.resource_id = tcon->resource_id;
if (maxbuf > sizeof(auxdata))
maxbuf = sizeof(auxdata);
memcpy(buffer, &auxdata, maxbuf);
return maxbuf;
}
static enum
fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
const void *data,
uint16_t datalen)
{
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,
.get_key = cifs_super_get_key,
.get_aux = cifs_fscache_super_get_aux,
.check_aux = cifs_fscache_super_check_aux,
};
/*
* Auxiliary data attached to CIFS inode within the cache
*/
struct cifs_fscache_inode_auxdata {
struct timespec last_write_time;
struct timespec last_change_time;
u64 eof;
};
static uint16_t cifs_fscache_inode_get_key(const void *cookie_netfs_data,
void *buffer, uint16_t maxbuf)
{
const struct cifsInodeInfo *cifsi = cookie_netfs_data;
uint16_t keylen;
/* use the UniqueId as the key */
keylen = sizeof(cifsi->uniqueid);
if (keylen > maxbuf)
keylen = 0;
else
memcpy(buffer, &cifsi->uniqueid, keylen);
return keylen;
}
static void
cifs_fscache_inode_get_attr(const void *cookie_netfs_data, uint64_t *size)
{
const struct cifsInodeInfo *cifsi = cookie_netfs_data;
*size = cifsi->vfs_inode.i_size;
}
static uint16_t
cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer,
uint16_t maxbuf)
{
struct cifs_fscache_inode_auxdata auxdata;
const struct cifsInodeInfo *cifsi = cookie_netfs_data;
memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
if (maxbuf > sizeof(auxdata))
maxbuf = sizeof(auxdata);
memcpy(buffer, &auxdata, maxbuf);
return maxbuf;
}
static enum
fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
const void *data,
uint16_t datalen)
{
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 = cifsi->vfs_inode.i_mtime;
auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
if (memcmp(data, &auxdata, datalen) != 0)
return FSCACHE_CHECKAUX_OBSOLETE;
return FSCACHE_CHECKAUX_OKAY;
}
static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data)
{
struct cifsInodeInfo *cifsi = cookie_netfs_data;
struct pagevec pvec;
pgoff_t first;
int loop, nr_pages;
pagevec_init(&pvec, 0);
first = 0;
cifs_dbg(FYI, "%s: cifs inode 0x%p now uncached\n", __func__, cifsi);
for (;;) {
nr_pages = pagevec_lookup(&pvec,
cifsi->vfs_inode.i_mapping, first,
PAGEVEC_SIZE - pagevec_count(&pvec));
if (!nr_pages)
break;
for (loop = 0; loop < nr_pages; loop++)
ClearPageFsCache(pvec.pages[loop]);
first = pvec.pages[nr_pages - 1]->index + 1;
pvec.nr = nr_pages;
pagevec_release(&pvec);
cond_resched();
}
}
const struct fscache_cookie_def cifs_fscache_inode_object_def = {
.name = "CIFS.uniqueid",
.type = FSCACHE_COOKIE_TYPE_DATAFILE,
.get_key = cifs_fscache_inode_get_key,
.get_attr = cifs_fscache_inode_get_attr,
.get_aux = cifs_fscache_inode_get_aux,
.check_aux = cifs_fscache_inode_check_aux,
.now_uncached = cifs_fscache_inode_now_uncached,
};
/*
* 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 <asm/uaccess.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "cifsfs.h"
void
cifs_dump_mem(char *label, void *data, int length)
{
int i, j;
int *intptr = data;
char *charptr = data;
char buf[10], line[80];
printk(KERN_DEBUG "%s: dump of %d bytes of data at 0x%p\n",
label, length, data);
for (i = 0; i < length; i += 16) {
line[0] = 0;
for (j = 0; (j < 4) && (i + j * 4 < length); j++) {
sprintf(buf, " %08x", intptr[i / 4 + j]);
strcat(line, buf);
}
buf[0] = ' ';
buf[2] = 0;
for (j = 0; (j < 16) && (i + j < length); j++) {
buf[1] = isprint(charptr[i + j]) ? charptr[i + j] : '.';
strcat(line, buf);
}
printk(KERN_DEBUG "%s\n", line);
}
}
#ifdef CONFIG_CIFS_DEBUG
void cifs_vfs_err(const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
printk(KERN_ERR "CIFS VFS: %pV", &vaf);
va_end(args);
}
#endif
void cifs_dump_detail(void *buf)
{
#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, smbCalcSize(smb));
#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);
cifs_dump_mem("existing buf: ",
mid_entry->resp_buf, 62);
}
}
spin_unlock(&GlobalMid_Lock);
#endif /* CONFIG_CIFS_DEBUG2 */
}
#ifdef CONFIG_PROC_FS
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;
__u32 dev_type;
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_WEAK_PW_HASH
seq_printf(m, " lanman");
#endif
#ifdef CONFIG_CIFS_POSIX
seq_printf(m, " posix");
#endif
#ifdef CONFIG_CIFS_UPCALL
seq_printf(m, " 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);
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) entry for %s not fully "
"displayed\n\t", i, ses->serverName);
} 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);
}
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;
list_for_each(tmp3, &ses->tcon_list) {
tcon = list_entry(tmp3, struct cifs_tcon,
tcon_list);
++j;
dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
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: 0x%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 (server->ops->dump_share_caps)
server->ops->dump_share_caps(m, tcon);
if (tcon->need_reconnect)
seq_puts(m, "\tDISCONNECTED ");
seq_putc(m, '\n');
}
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_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 int cifs_debug_data_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, cifs_debug_data_proc_show, NULL);
}
static const struct file_operations cifs_debug_data_proc_fops = {
.owner = THIS_MODULE,
.open = cifs_debug_data_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#ifdef CONFIG_CIFS_STATS
static ssize_t cifs_stats_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos)
{
char c;
int rc;
struct list_head *tmp1, *tmp2, *tmp3;
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
#ifdef CONFIG_CIFS_STATS2
atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0);
#endif /* CONFIG_CIFS_STATS2 */
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);
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);
if (server->ops->clear_stats)
server->ops->clear_stats(tcon);
}
}
}
spin_unlock(&cifs_tcp_ses_lock);
}
return count;
}
static int cifs_stats_proc_show(struct seq_file *m, void *v)
{
int i;
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);
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 = {
.owner = THIS_MODULE,
.open = cifs_stats_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = cifs_stats_proc_write,
};
#endif /* STATS */
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("DebugData", 0, proc_fs_cifs, &cifs_debug_data_proc_fops);
#ifdef CONFIG_CIFS_STATS
proc_create("Stats", 0, proc_fs_cifs, &cifs_stats_proc_fops);
#endif /* STATS */
proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops);
proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
&cifs_linux_ext_proc_fops);
proc_create("SecurityFlags", 0, proc_fs_cifs,
&cifs_security_flags_proc_fops);
proc_create("LookupCacheEnabled", 0, proc_fs_cifs,
&cifs_lookup_cache_proc_fops);
}
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);
#ifdef CONFIG_CIFS_STATS
remove_proc_entry("Stats", proc_fs_cifs);
#endif
remove_proc_entry("SecurityFlags", proc_fs_cifs);
remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
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;
int rc;
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
cifsFYI = 0;
else if (c == '1' || c == 'y' || c == 'Y')
cifsFYI = 1;
else if ((c > '1') && (c <= '9'))
cifsFYI = (int) (c - '0'); /* see cifs_debug.h for meanings */
return count;
}
static const struct file_operations cifsFYI_proc_fops = {
.owner = THIS_MODULE,
.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)
{
char c;
int rc;
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
linuxExtEnabled = 0;
else if (c == '1' || c == 'y' || c == 'Y')
linuxExtEnabled = 1;
return count;
}
static const struct file_operations cifs_linux_ext_proc_fops = {
.owner = THIS_MODULE,
.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)
{
char c;
int rc;
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
lookupCacheEnabled = 0;
else if (c == '1' || c == 'y' || c == 'Y')
lookupCacheEnabled = 1;
return count;
}
static const struct file_operations cifs_lookup_cache_proc_fops = {
.owner = THIS_MODULE,
.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)
{
char c;
int rc;
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
traceSMB = 0;
else if (c == '1' || c == 'y' || c == 'Y')
traceSMB = 1;
return count;
}
static const struct file_operations traceSMB_proc_fops = {
.owner = THIS_MODULE,
.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 ((*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN)
*flags = CIFSSEC_MUST_LANMAN;
else if ((*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];
char c;
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 */
c = flags_string[0];
if (c == '0' || c == 'n' || c == 'N') {
global_secflags = CIFSSEC_DEF; /* default */
return count;
} else if (c == '1' || c == 'y' || c == 'Y') {
global_secflags = CIFSSEC_MAX;
return count;
} else if (!isdigit(c)) {
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 = {
.owner = THIS_MODULE,
.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 */
/*
*
* 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 *);
void cifs_dump_mids(struct TCP_Server_Info *);
extern int 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
/*
* debug ON
* --------
*/
#ifdef CONFIG_CIFS_DEBUG
__printf(1, 2) void cifs_vfs_err(const char *fmt, ...);
/* information message: e.g., configuration, major event */
#define cifs_dbg(type, fmt, ...) \
do { \
if (type == FYI) { \
if (cifsFYI & CIFS_INFO) { \
printk(KERN_DEBUG "%s: " fmt, \
__FILE__, ##__VA_ARGS__); \
} \
} else if (type == VFS) { \
cifs_vfs_err(fmt, ##__VA_ARGS__); \
} else if (type == NOISY && type != 0) { \
printk(KERN_DEBUG fmt, ##__VA_ARGS__); \
} \
} while (0)
/*
* debug OFF
* ---------
*/
#else /* _CIFS_DEBUG */
#define cifs_dbg(type, fmt, ...) \
do { \
if (0) \
printk(KERN_DEBUG fmt, ##__VA_ARGS__); \
} while (0)
#endif
#endif /* _H_CIFS_DEBUG */
/*
* 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"
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;
*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 + 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 (strnicmp(sb_mountdata + off, "unc=", 4) == 0) {
off += noff;
continue;
}
if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) {
off += noff;
continue;
}
if (strnicmp(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 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_kern_mount(&cifs_fs_type, 0, 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: %hd, srv_type: %hd\n",
ref->flags, ref->server_type);
cifs_dbg(FYI, "DFS: ref_flags: %hd, path_consumed: %hd\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);
full_path = build_path_from_dentry(mntpt);
if (full_path == NULL)
goto cdda_exit;
cifs_sb = CIFS_SB(mntpt->d_inode->i_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_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
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(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 = {
};
/*
* 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 MS_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 */
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 backing_dev_info bdi;
struct delayed_work prune_tlinks;
};
#endif /* _CIFS_FS_SB_H */
/*
* 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/inet.h>
#include "cifsglob.h"
#include "cifs_spnego.h"
#include "cifs_debug.h"
/* 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 = payload;
ret = 0;
error:
return ret;
}
static void
cifs_spnego_key_destroy(struct key *key)
{
kfree(key->payload.data);
}
/*
* keytype for CIFS spnego keys
*/
struct key_type cifs_spnego_key_type = {
.name = "cifs.spnego",
.instantiate = cifs_spnego_key_instantiate,
.match = user_match,
.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;
/* 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
goto out;
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);
spnego_key = request_key(&cifs_spnego_key_type, description, "");
#ifdef CONFIG_CIFS_DEBUG2
if (cifsFYI && !IS_ERR(spnego_key)) {
struct cifs_spnego_msg *msg = spnego_key->payload.data;
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;
}
/*
* 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 */
/*
* 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_unicode.h"
#include "cifs_uniupr.h"
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifs_debug.h"
/*
* 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;
for (i = 0; i < maxwords; i++) {
ftmp = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
if (charlen > 0)
outlen += charlen;
else
outlen++;
}
return outlen;
}
/*
* cifs_mapchar - convert a host-endian char to proper char in codepage
* @target - where converted character should be copied
* @src_char - 2 byte host-endian source character
* @cp - codepage to which character should be converted
* @mapchar - should character be mapped according to mapchars mount option?
*
* 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 src_char, const struct nls_table *cp,
bool mapchar)
{
int len = 1;
if (!mapchar)
goto cp_convert;
/*
* 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:
goto cp_convert;
}
out:
return len;
cp_convert:
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
if (len <= 0) {
*target = '?';
len = 1;
}
goto out;
}
/*
* 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, bool mapchar)
{
int i, charlen, safelen;
int outlen = 0;
int nullsize = nls_nullsize(codepage);
int fromwords = fromlen / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp;
/*
* 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 = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
/*
* 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, mapchar);
if ((outlen + charlen) > (tolen - nullsize))
break;
}
/* put converted char into 'to' buffer */
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
outlen += charlen;
}
/* 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_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,
false);
} else {
len = strnlen(src, maxlen);
len++;
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return NULL;
strlcpy(dst, src, len);
}
return dst;
}
/*
* 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 mapChars)
{
int i, j, charlen;
char src_char;
__le16 dst_char;
wchar_t tmp;
if (!mapChars)
return cifs_strtoUTF16(target, source, PATH_MAX, cp);
for (i = 0, j = 0; i < srclen; j++) {
src_char = source[i];
charlen = 1;
switch (src_char) {
case 0:
put_unaligned(0, &target[j]);
goto ctoUTF16_out;
case ':':
dst_char = cpu_to_le16(UNI_COLON);
break;
case '*':
dst_char = cpu_to_le16(UNI_ASTERISK);
break;
case '?':
dst_char = cpu_to_le16(UNI_QUESTION);
break;
case '<':
dst_char = cpu_to_le16(UNI_LESSTHAN);
break;
case '>':
dst_char = cpu_to_le16(UNI_GRTRTHAN);
break;
case '|':
dst_char = cpu_to_le16(UNI_PIPE);
break;
/*
* 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.
*/
default:
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 < 1) {
dst_char = cpu_to_le16(0x003f);
charlen = 1;
}
}
/*
* 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:
return j;
}
#ifdef CONFIG_CIFS_SMB2
/*
* 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;
}
#endif /* CONFIG_CIFS_SMB2 */
/*
* 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)
/* 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 *codepage, bool mapchar);
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);
#ifdef CONFIG_CIFS_SMB2
extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
int *utf16_len, const struct nls_table *cp,
int remap);
#endif /* CONFIG_CIFS_SMB2 */
#endif
wchar_t cifs_toupper(wchar_t in);
/*
* UniStrcat: Concatenate the second string to the first
*
* Returns:
* Address of the first string
*/
static inline wchar_t *
UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
{
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
while (*ucs1++) ; /* 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 */
/*
* 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
/*
* 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}, {__constant_cpu_to_le32(11)} };
/* group users */
static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
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.value = 0;
memcpy(&key->payload.value, prep->data, prep->datalen);
key->datalen = prep->datalen;
return 0;
}
payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
if (!payload)
return -ENOMEM;
key->payload.data = 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);
}
static struct key_type cifs_idmap_key_type = {
.name = "cifs.idmap",
.instantiate = cifs_idmap_key_instantiate,
.destroy = cifs_idmap_key_destroy,
.describe = user_describe,
.match = user_match,
};
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 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.value :
(struct cifs_sid *)sidkey->payload.data;
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;
}
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.value, 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.value, 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.
*/
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);
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 0x%x\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: 0x%x, 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(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;
}
static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
__u16 fid, 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), fid, &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;
__u16 fid;
struct cifs_tcon *tcon;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
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;
rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL,
FILE_SHARE_ALL, create_options, &fid, &oplock,
NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (!rc) {
rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
CIFSSMBClose(xid, tcon, 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;
}
/* 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.netfid, 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;
__u16 fid;
struct cifs_tcon *tcon;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
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;
rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, access_flags,
FILE_SHARE_ALL, create_options, &fid, &oplock,
NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) {
cifs_dbg(VFS, "Unable to open file to set ACL\n");
goto out;
}
rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen, aclflag);
cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc);
CIFSSMBClose(xid, tcon, fid);
out:
free_xid(xid);
cifs_put_tlink(tlink);
return rc;
}
/* Translate the CIFS ACL (simlar 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 __u16 *pfid)
{
struct cifs_ntsd *pntsd = NULL;
u32 acllen = 0;
int rc = 0;
cifs_dbg(NOISY, "converting ACL to mode for %s\n", path);
if (pfid)
pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen);
else
pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen);
/* 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);
}
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 */
cifs_dbg(NOISY, "set ACL from mode for %s\n", path);
/* Get the security descriptor */
pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
if (IS_ERR(pntsd)) {
rc = PTR_ERR(pntsd);
cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
goto out;
}
/*
* 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);
return -ENOMEM;
}
rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
&aclflag);
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
if (!rc) {
/* Set the security descriptor */
rc = set_cifs_acl(pnntsd, secdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
}
kfree(pnntsd);
kfree(pntsd);
out:
return rc;
}
/*
* 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));
#endif /* _CIFSACL_H */
/*
* fs/cifs/cifsencrypt.c
*
* 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>
static int
cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server)
{
int rc;
unsigned int size;
if (server->secmech.sdescmd5 != NULL)
return 0; /* already allocated */
server->secmech.md5 = crypto_alloc_shash("md5", 0, 0);
if (IS_ERR(server->secmech.md5)) {
cifs_dbg(VFS, "could not allocate crypto md5\n");
rc = PTR_ERR(server->secmech.md5);
server->secmech.md5 = NULL;
return rc;
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.md5);
server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdescmd5) {
crypto_free_shash(server->secmech.md5);
server->secmech.md5 = NULL;
return -ENOMEM;
}
server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
server->secmech.sdescmd5->shash.flags = 0x0;
return 0;
}
/*
* 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 i;
int rc;
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
if (iov == NULL || signature == NULL || server == NULL)
return -EINVAL;
if (!server->secmech.sdescmd5) {
rc = cifs_crypto_shash_md5_allocate(server);
if (rc) {
cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__);
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;
}
for (i = 0; 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;
}
/* The first entry includes a length field (which does not get
signed that occupies the first 4 bytes before the header */
if (i == 0) {
if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
break; /* nothing to sign or corrupt header */
rc =
crypto_shash_update(&server->secmech.sdescmd5->shash,
iov[i].iov_base + 4, iov[i].iov_len - 4);
} else {
rc =
crypto_shash_update(&server->secmech.sdescmd5->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++) {
struct kvec p_iov;
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
crypto_shash_update(&server->secmech.sdescmd5->shash,
p_iov.iov_base, p_iov.iov_len);
kunmap(rqst->rq_pages[i]);
}
rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
return rc;
}
/* 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 ((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;
iov.iov_base = cifs_pdu;
iov.iov_len = be32_to_cpu(cifs_pdu->smb_buf_length) + 4;
return cifs_sign_smbv(&iov, 1, 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 (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];
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
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, false);
break;
}
}
blobptr += attrsize; /* advance attr value */
}
return 0;
}
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 if (ses->serverName) {
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;
unsigned int offset = CIFS_SESS_KEY_SIZE + 8;
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(ses->auth_key.response + offset,
ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
else
memcpy(ses->auth_key.response + offset,
ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + offset, ses->auth_key.len - offset);
if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
return rc;
}
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + CIFS_SESS_KEY_SIZE);
if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
return rc;
}
static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server)
{
int rc;
unsigned int size;
/* check if already allocated */
if (server->secmech.sdeschmacmd5)
return 0;
server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0);
if (IS_ERR(server->secmech.hmacmd5)) {
cifs_dbg(VFS, "could not allocate crypto hmacmd5\n");
rc = PTR_ERR(server->secmech.hmacmd5);
server->secmech.hmacmd5 = NULL;
return rc;
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacmd5);
server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdeschmacmd5) {
crypto_free_shash(server->secmech.hmacmd5);
server->secmech.hmacmd5 = NULL;
return -ENOMEM;
}
server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
server->secmech.sdeschmacmd5->shash.flags = 0x0;
return 0;
}
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
int rc;
int baselen;
unsigned int tilen;
struct ntlmv2_resp *buf;
char ntlmv2_hash[16];
unsigned char *tiblob = NULL; /* target info blob */
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
if (!ses->domainName) {
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 {
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;
}
}
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;
buf = (struct ntlmv2_resp *)
(ses->auth_key.response + CIFS_SESS_KEY_SIZE);
buf->blob_signature = cpu_to_le32(0x00000101);
buf->reserved = 0;
buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
buf->reserved2 = 0;
memcpy(ses->auth_key.response + baselen, tiblob, tilen);
rc = crypto_hmacmd5_alloc(ses->server);
if (rc) {
cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto setup_ntlmv2_rsp_ret;
}
/* 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 setup_ntlmv2_rsp_ret;
}
/* 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 setup_ntlmv2_rsp_ret;
}
/* 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 setup_ntlmv2_rsp_ret;
}
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) {
cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
goto setup_ntlmv2_rsp_ret;
}
rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + CIFS_SESS_KEY_SIZE,
CIFS_HMAC_MD5_HASH_SIZE);
if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
goto setup_ntlmv2_rsp_ret;
}
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__);
setup_ntlmv2_rsp_ret:
kfree(tiblob);
return rc;
}
int
calc_seckey(struct cifs_ses *ses)
{
int rc;
struct crypto_blkcipher *tfm_arc4;
struct scatterlist sgin, sgout;
struct blkcipher_desc desc;
unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */
get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE);
tfm_arc4 = crypto_alloc_blkcipher("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");
return rc;
}
desc.tfm = tfm_arc4;
rc = crypto_blkcipher_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__);
return rc;
}
sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE);
sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, CIFS_CPHTXT_SIZE);
if (rc) {
cifs_dbg(VFS, "could not encrypt session key rc: %d\n", rc);
crypto_free_blkcipher(tfm_arc4);
return rc;
}
/* 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;
crypto_free_blkcipher(tfm_arc4);
return rc;
}
void
cifs_crypto_shash_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.hmacmd5) {
crypto_free_shash(server->secmech.hmacmd5);
server->secmech.hmacmd5 = 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;
}
/*
* 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 <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"
#ifdef CONFIG_CIFS_SMB2
#include "smb2pdu.h"
#endif
int cifsFYI = 0;
int traceSMB = 0;
bool enable_oplocks = true;
unsigned int linuxExtEnabled = 1;
unsigned int lookupCacheEnabled = 1;
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, 0);
MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "
"Default: 16384 Range: 8192 to 130048");
unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
module_param(cifs_min_rcv, uint, 0);
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, 0);
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. "
"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");
extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq;
#ifdef CONFIG_CIFS_SMB2
__u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
#endif
/*
* 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;
int rc = 0;
cifs_sb = CIFS_SB(sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
sb->s_flags |= MS_POSIXACL;
if (cifs_sb_master_tcon(cifs_sb)->ses->capabilities & 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_bdi = &cifs_sb->bdi;
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 (cifs_sb_master_tcon(cifs_sb)->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();
/*
* PATH_MAX may be too long - it would presumably be total path,
* but note that some servers (includinng Samba 3) have a shorter
* maximum path.
*
* Instead could get the real value via SMB_QUERY_FS_ATTRIBUTE_INFO.
*/
buf->f_namelen = PATH_MAX;
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 0;
}
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->delete_pending = false;
cifs_inode->invalid_mapping = false;
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;
#ifdef CONFIG_CIFS_SMB2
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
#endif
/*
* 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_printf(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_printf(s, "(unknown)");
}
}
static void
cifs_show_security(struct seq_file *s, struct cifs_ses *ses)
{
if (ses->sectype == Unspecified)
return;
seq_printf(s, ",sec=");
switch (ses->sectype) {
case LANMAN:
seq_printf(s, "lanman");
break;
case NTLMv2:
seq_printf(s, "ntlmv2");
break;
case NTLM:
seq_printf(s, "ntlm");
break;
case Kerberos:
seq_printf(s, "krb5");
break;
case RawNTLMSSP:
seq_printf(s, "ntlmssp");
break;
default:
/* shouldn't ever happen */
seq_printf(s, "unknown");
break;
}
if (ses->sign)
seq_printf(s, "i");
}
static void
cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
{
seq_printf(s, ",cache=");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
seq_printf(s, "strict");
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
seq_printf(s, "none");
else
seq_printf(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_printf(s, ",vers=%s", tcon->ses->server->vals->version_string);
cifs_show_security(s, tcon->ses);
cifs_show_cache_flavor(s, cifs_sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
seq_printf(s, ",multiuser");
else if (tcon->ses->user_name)
seq_printf(s, ",username=%s", tcon->ses->user_name);
if (tcon->ses->domainName)
seq_printf(s, ",domain=%s", 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_printf(s, ",forceuid");
else
seq_printf(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_printf(s, ",forcegid");
else
seq_printf(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_printf(s, ",seal");
if (tcon->nocase)
seq_printf(s, ",nocase");
if (tcon->retry)
seq_printf(s, ",hard");
if (tcon->unix_ext)
seq_printf(s, ",unix");
else
seq_printf(s, ",nounix");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
seq_printf(s, ",posixpaths");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
seq_printf(s, ",setuids");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
seq_printf(s, ",serverino");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
seq_printf(s, ",rwpidforward");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL)
seq_printf(s, ",forcemand");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
seq_printf(s, ",nouser_xattr");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
seq_printf(s, ",mapchars");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
seq_printf(s, ",sfu");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
seq_printf(s, ",nobrl");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
seq_printf(s, ",cifsacl");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
seq_printf(s, ",dynperm");
if (root->d_sb->s_flags & MS_POSIXACL)
seq_printf(s, ",acl");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
seq_printf(s, ",mfsymlinks");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE)
seq_printf(s, ",fsc");
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL))
seq_printf(s, ",wine");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)
seq_printf(s, ",nostrictsync");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
seq_printf(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);
/* 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)
{
*flags |= MS_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;
full_path = cifs_build_path_to_root(vol, cifs_sb,
cifs_sb_master_tcon(cifs_sb));
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 = dentry->d_inode;
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++;
mutex_lock(&dir->i_mutex);
child = lookup_one_len(p, dentry, s - p);
mutex_unlock(&dir->i_mutex);
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_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
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);
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_cifs_sb;
}
cifs_setup_cifs_sb(volume_info, cifs_sb);
rc = cifs_mount(cifs_sb, volume_info);
if (rc) {
if (!(flags & MS_SILENT))
cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n",
rc);
root = ERR_PTR(rc);
goto out_mountdata;
}
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 |= MS_NODIRATIME | MS_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 |= MS_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);
out:
cifs_cleanup_volume_info(volume_info);
return root;
out_mountdata:
kfree(cifs_sb->mountdata);
out_cifs_sb:
kfree(cifs_sb);
out_nls:
unload_nls(volume_info->local_nls);
goto out;
}
static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode = file_inode(iocb->ki_filp);
ssize_t written;
int rc;
written = generic_file_aio_write(iocb, iov, nr_segs, pos);
if (CIFS_CACHE_WRITE(CIFS_I(inode)))
return written;
rc = filemap_fdatawrite(inode->i_mapping);
if (rc)
cifs_dbg(FYI, "cifs_file_aio_write: %d rc on %p inode\n",
rc, inode);
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)
{
/*
* 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 (((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode))))
return generic_setlease(file, arg, lease);
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);
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");
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_rename,
.permission = cifs_permission,
/* revalidate:cifs_revalidate, */
.setattr = cifs_setattr,
.symlink = cifs_symlink,
.mknod = cifs_mknod,
#ifdef CONFIG_CIFS_XATTR
.setxattr = cifs_setxattr,
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
#endif
};
const struct inode_operations cifs_file_inode_ops = {
/* revalidate:cifs_revalidate, */
.setattr = cifs_setattr,
.getattr = cifs_getattr, /* do we need this anymore? */
.rename = cifs_rename,
.permission = cifs_permission,
#ifdef CONFIG_CIFS_XATTR
.setxattr = cifs_setxattr,
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
#endif
};
const struct inode_operations cifs_symlink_inode_ops = {
.readlink = generic_readlink,
.follow_link = cifs_follow_link,
.put_link = cifs_put_link,
.permission = cifs_permission,
/* BB add the following two eventually */
/* revalidate: cifs_revalidate,
setattr: cifs_notify_change, *//* BB do we need notify change */
#ifdef CONFIG_CIFS_XATTR
.setxattr = cifs_setxattr,
.getxattr = cifs_getxattr,
.listxattr = cifs_listxattr,
.removexattr = cifs_removexattr,
#endif
};
const struct file_operations cifs_file_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = cifs_file_aio_write,
.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,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_strict_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = 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,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_ops = {
/* BB reevaluate whether they can be done with directio, no cache */
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_user_readv,
.aio_write = 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,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.llseek = cifs_llseek,
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = cifs_file_aio_write,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_fsync,
.flush = cifs_flush,
.mmap = cifs_file_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_strict_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = 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,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_nobrl_ops = {
/* BB reevaluate whether they can be done with directio, no cache */
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_user_readv,
.aio_write = 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,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.llseek = cifs_llseek,
.setlease = cifs_setlease,
};
const struct file_operations cifs_dir_ops = {
.readdir = cifs_readdir,
.release = cifs_closedir,
.read = generic_read_dir,
.unlocked_ioctl = cifs_ioctl,
.llseek = generic_file_llseek,
};
static void
cifs_init_once(void *inode)
{
struct cifsInodeInfo *cifsi = inode;
inode_init_once(&cifsi->vfs_inode);
init_rwsem(&cifsi->lock_sem);
}
static int
cifs_init_inodecache(void)
{
cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
sizeof(struct cifsInodeInfo),
0, (SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD),
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)
{
size_t max_hdr_size = MAX_CIFS_HDR_SIZE;
#ifdef CONFIG_CIFS_SMB2
/*
* SMB2 maximum header size is bigger than CIFS one - no problems to
* allocate some more bytes for CIFS.
*/
max_hdr_size = MAX_SMB2_HDR_SIZE;
#endif
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("cifs_request",
CIFSMaxBufSize + max_hdr_size, 0,
SLAB_HWCACHE_ALIGN, 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("cifs_small_rq",
MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN,
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(&cifs_file_list_lock);
spin_lock_init(&GlobalMid_Lock);
#ifdef CONFIG_CIFS_SMB2
get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
#endif
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;
}
rc = cifs_fscache_register();
if (rc)
goto out_destroy_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 = register_key_type(&cifs_spnego_key_type);
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;
return 0;
out_init_cifs_idmap:
#ifdef CONFIG_CIFS_ACL
exit_cifs_idmap();
out_register_key_type:
#endif
#ifdef CONFIG_CIFS_UPCALL
unregister_key_type(&cifs_spnego_key_type);
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_wq:
destroy_workqueue(cifsiod_wq);
out_clean_proc:
cifs_proc_clean();
return rc;
}
static void __exit
exit_cifs(void)
{
cifs_dbg(NOISY, "exit_cifs\n");
unregister_filesystem(&cifs_fs_type);
cifs_dfs_release_automount_timer();
#ifdef CONFIG_CIFS_ACL
exit_cifs_idmap();
#endif
#ifdef CONFIG_CIFS_UPCALL
unregister_key_type(&cifs_spnego_key_type);
#endif
cifs_destroy_request_bufs();
cifs_destroy_mids();
cifs_destroy_inodecache();
cifs_fscache_unregister();
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_init(init_cifs)
module_exit(exit_cifs)
/*
* 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
#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.
*/
static inline ino_t
cifs_uniqueid_to_ino_t(u64 fileid)
{
ino_t ino = (ino_t) fileid;
if (sizeof(ino_t) < sizeof(u64))
ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8;
return ino;
}
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,
int *);
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_rename(struct inode *, struct dentry *, struct inode *,
struct dentry *);
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_getattr(struct vfsmount *, struct dentry *, struct kstat *);
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, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
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, void *direntry, filldir_t filldir);
/* 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 void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
extern void cifs_put_link(struct dentry *direntry,
struct nameidata *nd, void *);
extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
int buflen);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
extern int cifs_removexattr(struct dentry *, const char *);
extern int cifs_setxattr(struct dentry *, const char *, const void *,
size_t, int);
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
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.01"
#endif /* _CIFSFS_H */
/*
* 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>
#ifdef CONFIG_CIFS_SMB2
#include "smb2pdu.h"
#endif
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
/*
* 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)
/* currently length of NIP6_FMT */
#define SERVER_NAME_LENGTH 40
#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1)
/* used to define string lengths for reversing unicode strings */
/* (256+1)*2 = 514 */
/* (max path length + 1 for null) * 2 for unicode */
#define MAX_NAME 514
/* SMB echo "timeout" -- FIXME: tunable? */
#define SMB_ECHO_INTERVAL (60 * HZ)
#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 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 */
};
/* 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_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,
};
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 *, void *,
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 *);
/* data offset from read response message */
unsigned int (*read_data_offset)(char *);
/* data length from read response message */
unsigned int (*read_data_length)(char *);
/* 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 *);
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);
bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
/* 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 *);
/* 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);
/* 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 (*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 cifsFileInfo *,
struct cifs_io_parms *, unsigned int *, char **,
int *);
/* sync write to the server */
int (*sync_write)(const unsigned int, struct cifsFileInfo *,
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 *);
/* check for STATUS_PENDING and process it in a positive case */
bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
/* 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 (*query_mf_symlink)(const unsigned char *, char *, unsigned int *,
struct cifs_sb_info *, 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 *, u8);
/* parse lease context buffer and return oplock/epoch info */
__u8 (*parse_lease_buf)(void *, unsigned int *);
};
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_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 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 posix_paths:1; /* unset to not ask for posix pathnames. */
bool no_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 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 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;
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;
};
#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_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 (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \
MS_NODEV | MS_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;
int credits; /* send no more requests at once */
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 */
#ifdef CONFIG_CIFS_SMB2
int echo_credits; /* echo reserved slots */
int oplock_credits; /* oplock break reserved slots */
bool echoes:1; /* enable echoes */
#endif
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 */
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? */
struct delayed_work echo; /* echo ping workqueue job */
struct kvec *iov; /* reusable kvec array for receives */
unsigned int nr_iov; /* number of kvecs in array */
char *smallbuf; /* pointer to current "small" buffer */
char *bigbuf; /* pointer to current "big" buffer */
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 */
#endif
#ifdef CONFIG_CIFS_SMB2
unsigned int max_read;
unsigned int max_write;
#endif /* CONFIG_CIFS_SMB2 */
};
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
set_credits(struct TCP_Server_Info *server, const int val)
{
server->ops->set_credits(server, val);
}
static inline __u64
get_next_mid(struct TCP_Server_Info *server)
{
return server->ops->get_next_mid(server);
}
/*
* 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_CACHE_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_CACHE_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
/*
* 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 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 */
__u16 ipc_tid; /* special tid for connection to IPC share */
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 * 2]; /* BB make bigger for
TCP names - will ipv6 and sctp addresses fit? */
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 */
#ifdef CONFIG_CIFS_SMB2
__u16 session_flags;
char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
#endif /* CONFIG_CIFS_SMB2 */
};
static inline bool
cap_unix(struct cifs_ses *ses)
{
return ses->server->vals->cap_unix & ses->capabilities;
}
/*
* 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 openFileList;
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;
#ifdef CONFIG_CIFS_STATS
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;
#ifdef CONFIG_CIFS_SMB2
struct {
atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS];
atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS];
} smb2_stats;
#endif /* CONFIG_CIFS_SMB2 */
} stats;
#ifdef CONFIG_CIFS_STATS2
unsigned long long time_writes;
unsigned long long time_reads;
unsigned long long time_opens;
unsigned long long time_deletes;
unsigned long long time_closes;
unsigned long long time_mkdirs;
unsigned long long time_rmdirs;
unsigned long long time_renames;
unsigned long long time_t2renames;
unsigned long long time_ffirst;
unsigned long long time_fnext;
unsigned long long time_fclose;
#endif /* CONFIG_CIFS_STATS2 */
__u64 bytes_read;
__u64 bytes_written;
spinlock_t stat_lock;
#endif /* CONFIG_CIFS_STATS */
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$ eg for RPC/PIPES */
bool retry:1;
bool nocase:1;
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 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 need_reconnect:1; /* connection reset, tid now invalid */
#ifdef CONFIG_CIFS_SMB2
bool print:1; /* set if connection to printer share */
bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */
__le32 capabilities;
__u32 share_flags;
__u32 maximal_access;
__u32 vol_serial_number;
__le64 vol_create_time;
#endif /* CONFIG_CIFS_SMB2 */
#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 */
/* 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);
static inline struct cifs_tcon *
tlink_tcon(struct tcon_link *tlink)
{
return tlink->tl_tcon;
}
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 share_access;
int create_options;
const char *path;
struct cifs_fid *fid;
bool reconnect:1;
};
struct cifs_fid {
__u16 netfid;
#ifdef CONFIG_CIFS_SMB2
__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 */
#endif
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 {
struct list_head tlist; /* pointer to next fid owned by tcon */
struct list_head flist; /* next fid (file instance) for this inode */
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 */
/* BB add lock scope info here if needed */ ;
/* lock scope id (0 if none) */
struct dentry *dentry;
unsigned int f_flags;
struct tcon_link *tlink;
bool invalidHandle:1; /* file closed via session abend */
bool oplock_break_cancelled:1;
int count; /* refcount protected by cifs_file_list_lock */
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 */
};
#define SHARE_FLAGS_SHIFT 28
static inline int cifs_get_share_flags(unsigned int flags)
{
return (~(flags >> SHARE_FLAGS_SHIFT)) & 7;
}
struct cifs_io_parms {
__u16 netfid;
#ifdef CONFIG_CIFS_SMB2
__u64 persistent_fid; /* persist file id for smb2 */
__u64 volatile_fid; /* volatile file id for smb2 */
#endif
__u32 pid;
__u64 offset;
unsigned int length;
struct cifs_tcon *tcon;
};
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;
__u64 offset;
unsigned int 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);
struct kvec iov;
unsigned int pagesz;
unsigned int tailsz;
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;
__u64 offset;
pid_t pid;
unsigned int bytes;
int result;
unsigned int pagesz;
unsigned int tailsz;
unsigned int nr_pages;
struct page *pages[];
};
/*
* Take a reference on the file private data. Must be called with
* cifs_file_list_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);
#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 */
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;
__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 */
bool delete_pending; /* DELETE_ON_CLOSE is set */
bool invalid_mapping; /* pagecache is invalid */
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 */
#ifdef CONFIG_CIFS_SMB2
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
#endif
#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 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;
}
#ifdef CONFIG_CIFS_STATS
#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);
}
#else
#define cifs_stats_inc(field) do {} while (0)
#define cifs_stats_bytes_written(tcon, bytes) do {} while (0)
#define cifs_stats_bytes_read(tcon, bytes) do {} while (0)
#endif
/*
* 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);
/* 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 TCP_Server_Info *server; /* server corresponding to this mid */
__u64 mid; /* multiplex id */
__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 */
void *callback_data; /* general purpose pointer for callback */
void *resp_buf; /* pointer to received SMB header */
int mid_state; /* wish this were enum but can not pass to wait_event */
__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 */
};
/* 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
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 timespec cf_atime;
struct timespec cf_mtime;
struct timespec 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);
}
#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
/* 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 */
/* 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
* cifs_file_list_lock protects:
* list operations on tcp and SMB session lists and tCon lists
* f_owner.lock protects certain per file struct operations
* mapping->page_lock protects certain per page operations
*
* 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.
*/
GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
/*
* This lock protects the cifs_file->llist and cifs_file->flist
* list operations, and updates to some flags (cifs_file->invalidHandle)
* It will be moved to either use the tcon->stat_lock or equivalent later.
* If cifs_tcp_ses_lock and the lock below are both needed to be held, then
* the cifs_tcp_ses_lock must be grabbed first and released last.
*/
GLOBAL_EXTERN spinlock_t cifs_file_list_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 */
GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */
GLOBAL_EXTERN unsigned int lookupCacheEnabled;
GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */
GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */
GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */
GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
#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);
extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq;
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 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;
#endif /* _CIFS_GLOB_H */
/*
* 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
#ifndef CONFIG_CIFS_XATTR
#define CONFIG_CIFS_XATTR
#endif
#ifndef CONFIG_CIFS_POSIX
#define CONFIG_CIFS_POSIX
#endif
#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;
__u16 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 {
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
__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_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));
struct reparse_data {
__u32 ReparseTag;
__u16 ReparseDataLength;
__u16 Reserved;
__u16 SubstituteNameOffset;
__u16 SubstituteNameLength;
__u16 PrintNameOffset;
__u16 PrintNameLength;
__u32 Flags;
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;
typedef struct smb_com_transaction_get_dfs_refer_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
__u8 Pad;
__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 */
} __attribute__((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
typedef struct {
__le32 DeviceType;
__le32 DeviceCharacteristics;
} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
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 */
/* 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 chattr command */
#define EXT_SECURE_DELETE 0x00000001 /* EXT3_SECRM_FL */
#define EXT_ENABLE_UNDELETE 0x00000002 /* EXT3_UNRM_FL */
/* Reserved for compress file 0x4 */
#define EXT_SYNCHRONOUS 0x00000008 /* EXT3_SYNC_FL */
#define EXT_IMMUTABLE_FL 0x00000010 /* EXT3_IMMUTABLE_FL */
#define EXT_OPEN_APPEND_ONLY 0x00000020 /* EXT3_APPEND_FL */
#define EXT_DO_NOT_BACKUP 0x00000040 /* EXT3_NODUMP_FL */
#define EXT_NO_UPDATE_ATIME 0x00000080 /* EXT3_NOATIME_FL */
/* 0x100 through 0x800 reserved for compression flags and are GET-ONLY */
#define EXT_HASH_TREE_INDEXED_DIR 0x00001000 /* GET-ONLY EXT3_INDEX_FL */
/* 0x2000 reserved for IMAGIC_FL */
#define EXT_JOURNAL_THIS_FILE 0x00004000 /* GET-ONLY EXT3_JOURNAL_DATA_FL */
/* 0x8000 reserved for EXT3_NOTAIL_FL */
#define EXT_SYNCHRONOUS_DIR 0x00010000 /* EXT3_DIRSYNC_FL */
#define EXT_TOPDIR 0x00020000 /* EXT3_TOPDIR_FL */
#define EXT_SET_MASK 0x000300FF
#define EXT_GET_MASK 0x0003DFFF
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 */
/*
* 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>
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 cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
struct kvec *iov);
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())); \
__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); \
} while (0)
extern int init_cifs_idmap(void);
extern void exit_cifs_idmap(void);
extern char *build_path_from_dentry(struct dentry *);
extern char *cifs_build_path_to_root(struct smb_vol *vol,
struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon);
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_wake_up_task(struct mid_q_entry *mid);
extern int cifs_call_async(struct TCP_Server_Info *server,
struct smb_rqst *rqst,
mid_receive_t *receive, mid_callback_t *callback,
void *cbdata, const int flags);
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 SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */ , const int flags);
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 length);
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);
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 timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
extern u64 cifs_UnixTimeToNT(struct timespec);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
int offset);
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
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 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 __u16 *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 __u16 *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 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_readv_from_socket(struct TCP_Server_Info *server,
struct kvec *iov_orig, unsigned int nr_segs,
unsigned int to_read);
extern void 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);
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 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);
#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 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);
extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
struct cifs_tcon *tcon,
const unsigned char *searchName, char **syminfo,
const struct nls_table *nls_codepage);
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
__u16 fid, char **symlinkinfo,
const struct nls_table *nls_codepage);
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const int disposition,
const int access_flags, const int share_flags, const int omode,
__u16 *netfid, int *pOplock, FILE_ALL_INFO *,
const struct nls_table *nls_codepage, int remap);
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,
const char __user *ubuf, const int long_op);
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_shash_release(struct TCP_Server_Info *);
extern int calc_seckey(struct cifs_ses *);
extern int generate_smb3signingkey(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, const struct nls_table *nls_codepage,
int remap_special_chars);
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, int remap_special_chars);
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 CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr);
extern int CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, unsigned int xid);
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);
void cifs_writedata_release(struct kref *refcount);
int open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
unsigned int xid);
#endif /* _CIFSPROTO_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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 {
direntry->d_time = 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 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 (tcon->Flags & SMB_SHARE_IS_IN_DFS)
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 dentry *temp;
int namelen;
int dfsplen;
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 (tcon->Flags & SMB_SHARE_IS_IN_DFS)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else
dfsplen = 0;
cifs_bp_rename_retry:
namelen = dfsplen;
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 || 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 (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 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_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
int i;
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;
int share_access;
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_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
(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_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");
share_access = cifs_get_share_flags(oflags);
/*
* 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.share_access = share_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) {
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->netfid);
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);
if (server->ops->close)
server->ops->close(xid, tcon, fid);
goto out;
}
d_drop(direntry);
d_add(direntry, newinode);
out:
kfree(buf);
kfree(full_path);
return rc;
}
int
cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct file *file, unsigned oflags, umode_t mode,
int *opened)
{
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_unhashed(direntry))
return -ENOENT;
res = cifs_lookup(inode, direntry, 0);
if (IS_ERR(res))
return PTR_ERR(res);
return finish_no_open(file, res);
}
rc = check_name(direntry);
if (rc)
return rc;
xid = get_xid();
cifs_dbg(FYI, "parent inode = 0x%p name is: %s and dentry = 0x%p\n",
inode, direntry->d_name.name, 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);
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))
*opened |= FILE_CREATED;
rc = finish_open(file, direntry, generic_file_open, opened);
if (rc) {
if (server->ops->close)
server->ops->close(xid, tcon, &fid);
cifs_del_pending_open(&open);
goto out;
}
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);
fput(file);
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: %s and dentry = 0x%p\n",
inode, direntry->d_name.name, 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 *pTcon;
struct cifs_io_parms io_parms;
char *full_path = NULL;
struct inode *newinode = NULL;
int oplock = 0;
u16 fileHandle;
FILE_ALL_INFO *buf = NULL;
unsigned int bytes_written;
struct win_dev *pdev;
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);
pTcon = tlink_tcon(tlink);
xid = get_xid();
full_path = build_path_from_dentry(direntry);
if (full_path == NULL) {
rc = -ENOMEM;
goto mknod_out;
}
if (pTcon->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, pTcon, full_path, &args,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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 (!(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) {
kfree(full_path);
rc = -ENOMEM;
free_xid(xid);
return rc;
}
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, GENERIC_WRITE,
FILE_SHARE_ALL, create_options, &fileHandle, &oplock,
buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
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.netfid = fileHandle;
io_parms.pid = current->tgid;
io_parms.tcon = pTcon;
io_parms.offset = 0;
io_parms.length = 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 = CIFSSMBWrite(xid, &io_parms,
&bytes_written, (char *)pdev,
NULL, 0);
} 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 = CIFSSMBWrite(xid, &io_parms,
&bytes_written, (char *)pdev,
NULL, 0);
} /* else if (S_ISFIFO) */
CIFSSMBClose(xid, pTcon, fileHandle);
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: %s and dentry = 0x%p\n",
parent_dir_inode, direntry->d_name.name, 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 (struct dentry *)tlink;
}
pTcon = tlink_tcon(tlink);
rc = check_name(direntry);
if (rc)
goto lookup_out;
/* 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) {
rc = -ENOMEM;
goto lookup_out;
}
if (direntry->d_inode != NULL) {
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, direntry->d_inode);
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) && (newInode != NULL)) {
d_add(direntry, newInode);
/* 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) {
rc = 0;
direntry->d_time = jiffies;
d_add(direntry, NULL);
/* if it was once a directory (but how can we tell?) we could do
shrink_dcache_parent(direntry); */
} else if (rc != -EACCES) {
cifs_dbg(VFS, "Unexpected lookup error %d\n", rc);
/* We special case check for Access Denied - since that
is a common return code */
}
lookup_out:
kfree(full_path);
cifs_put_tlink(tlink);
free_xid(xid);
return ERR_PTR(rc);
}
static int
cifs_d_revalidate(struct dentry *direntry, unsigned int flags)
{
if (flags & LOOKUP_RCU)
return -ECHILD;
if (direntry->d_inode) {
if (cifs_revalidate_dentry(direntry))
return 0;
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(direntry->d_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, direntry->d_time + 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 = %s\n", direntry->d_name.name);
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();
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 *parent, const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
struct nls_table *codepage = CIFS_SB(parent->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,
};
/*
* 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;
}
/*
* 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 */
/*
* 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 */
/*
* 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"
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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_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 share_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);
share_access = cifs_get_share_flags(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;
oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb;
oparms.desired_access = desired_access;
oparms.share_access = share_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->netfid);
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;
}
struct cifsFileInfo *
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct tcon_link *tlink, __u32 oplock)
{
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
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;
down_write(&cinode->lock_sem);
list_add(&fdlocks->llist, &cinode->llist);
up_write(&cinode->lock_sem);
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);
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;
}
spin_lock(&cifs_file_list_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*/
if (file->f_mode & FMODE_READ)
list_add(&cfile->flist, &cinode->openFileList);
else
list_add_tail(&cfile->flist, &cinode->openFileList);
spin_unlock(&cifs_file_list_lock);
if (fid->purge_cache)
cifs_invalidate_mapping(inode);
file->private_data = cfile;
return cfile;
}
struct cifsFileInfo *
cifsFileInfo_get(struct cifsFileInfo *cifs_file)
{
spin_lock(&cifs_file_list_lock);
cifsFileInfo_get_locked(cifs_file);
spin_unlock(&cifs_file_list_lock);
return cifs_file;
}
/*
* Release a reference on the file private data. This may involve closing
* the filehandle out on the server. Must be called without holding
* cifs_file_list_lock.
*/
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
struct inode *inode = cifs_file->dentry->d_inode;
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;
spin_lock(&cifs_file_list_lock);
if (--cifs_file->count > 0) {
spin_unlock(&cifs_file_list_lock);
return;
}
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",
cifs_file->dentry->d_inode);
/*
* 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)
CIFS_I(inode)->invalid_mapping = true;
cifs_set_oplock_level(cifsi, 0);
}
spin_unlock(&cifs_file_list_lock);
cancel_work_sync(&cifs_file->oplock_break);
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);
}
cifs_del_pending_open(&open);
/*
* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
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->f_path.dentry);
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 (server->oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
if (!tcon->broken_posix_open && tcon->unix_ext && cap_unix(tcon->ses)
&& ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
(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(cfile->dentry->d_inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
down_read(&cinode->lock_sem);
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 share_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 = cfile->dentry->d_inode;
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_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
(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);
share_access = cifs_get_share_flags(cfile->f_flags);
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
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.share_access = share_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
* CIFSSMBOpen 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);
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.
*/
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;
}
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(&cifs_file_list_lock);
if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) {
cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_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(&cifs_file_list_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(cfile->dentry->d_inode);
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(cfile->dentry->d_inode);
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(cfile->dentry->d_inode);
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(cfile->dentry->d_inode);
bool exist;
int rc = 0;
try_again:
exist = false;
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;
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:
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;
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 for zero before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf) {
free_xid(xid);
return -EINVAL;
}
max_num = (max_buf - sizeof(struct smb_hdr)) /
sizeof(LOCKING_ANDX_RANGE);
buf = kzalloc(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;
}
/* copied from fs/locks.c with a name change */
#define cifs_for_each_lock(inode, lockp) \
for (lockp = &inode->i_flock; *lockp != NULL; \
lockp = &(*lockp)->fl_next)
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 = cfile->dentry->d_inode;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct file_lock *flock, **before;
unsigned int count = 0, i = 0;
int rc = 0, xid, type;
struct list_head locks_to_send, *el;
struct lock_to_push *lck, *tmp;
__u64 length;
xid = get_xid();
spin_lock(&inode->i_lock);
cifs_for_each_lock(inode, before) {
if ((*before)->fl_flags & FL_POSIX)
count++;
}
spin_unlock(&inode->i_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 < 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(&inode->i_lock);
cifs_for_each_lock(inode, before) {
flock = *before;
if ((flock->fl_flags & FL_POSIX) == 0)
continue;
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 = flock->fl_pid;
lck->netfid = cfile->fid.netfid;
lck->length = length;
lck->type = type;
lck->offset = flock->fl_start;
el = el->next;
}
spin_unlock(&inode->i_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(cfile->dentry->d_inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
/* we are going to update can_cache_brlcks here - need a write access */
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, current->tgid,
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;
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(cfile->dentry->d_inode);
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 for zero before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf)
return -EINVAL;
max_num = (max_buf - sizeof(struct smb_hdr)) /
sizeof(LOCKING_ANDX_RANGE);
buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
if (!buf)
return -ENOMEM;
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 = cfile->dentry->d_inode;
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,
current->tgid, 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_invalidate_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)
posix_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_SB(file->f_path.dentry->d_sb);
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(dentry->d_inode);
struct cifs_io_parms io_parms;
cifs_sb = CIFS_SB(dentry->d_sb);
cifs_dbg(FYI, "write %zd bytes to offset %lld of %s\n",
write_size, *offset, dentry->d_name.name);
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((size_t)cifs_sb->wsize,
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, &io_parms,
&bytes_written, iov, 1);
}
if (rc || (bytes_written == 0)) {
if (total_written)
break;
else {
free_xid(xid);
return rc;
}
} else {
spin_lock(&dentry->d_inode->i_lock);
cifs_update_eof(cifsi, *offset, bytes_written);
spin_unlock(&dentry->d_inode->i_lock);
*offset += bytes_written;
}
}
cifs_stats_bytes_written(tcon, total_written);
if (total_written > 0) {
spin_lock(&dentry->d_inode->i_lock);
if (*offset > dentry->d_inode->i_size)
i_size_write(dentry->d_inode, *offset);
spin_unlock(&dentry->d_inode->i_lock);
}
mark_inode_dirty_sync(dentry->d_inode);
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_file_list_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_locked(open_file);
spin_unlock(&cifs_file_list_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_file_list_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_file_list_lock);
refind_writable:
if (refind > MAX_REOPEN_ATT) {
spin_unlock(&cifs_file_list_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_locked(open_file);
spin_unlock(&cifs_file_list_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_locked(inv_file);
}
spin_unlock(&cifs_file_list_lock);
if (inv_file) {
rc = cifs_reopen_file(inv_file, false);
if (!rc)
return inv_file;
else {
spin_lock(&cifs_file_list_lock);
list_move_tail(&inv_file->flist,
&cifs_inode->openFileList);
spin_unlock(&cifs_file_list_lock);
cifsFileInfo_put(inv_file);
spin_lock(&cifs_file_list_lock);
++refind;
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_CACHE_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_CACHE_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_fs_time(inode->i_sb);
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 int cifs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb);
bool done = false, scanned = false, range_whole = false;
pgoff_t end, index;
struct cifs_writedata *wdata;
struct TCP_Server_Info *server;
struct page *page;
int 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_CACHE_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_CACHE_SHIFT;
end = wbc->range_end >> PAGE_CACHE_SHIFT;
if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
range_whole = true;
scanned = true;
}
retry:
while (!done && index <= end) {
unsigned int i, nr_pages, found_pages;
pgoff_t next = 0, tofind;
struct page **pages;
tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1,
end - index) + 1;
wdata = cifs_writedata_alloc((unsigned int)tofind,
cifs_writev_complete);
if (!wdata) {
rc = -ENOMEM;
break;
}
/*
* find_get_pages_tag seems to return a max of 256 on each
* iteration, so we must call it several times in order to
* fill the array or the wsize is effectively limited to
* 256 * PAGE_CACHE_SIZE.
*/
found_pages = 0;
pages = wdata->pages;
do {
nr_pages = find_get_pages_tag(mapping, &index,
PAGECACHE_TAG_DIRTY,
tofind, pages);
found_pages += nr_pages;
tofind -= nr_pages;
pages += nr_pages;
} while (nr_pages && tofind && index <= end);
if (found_pages == 0) {
kref_put(&wdata->refcount, cifs_writedata_release);
break;
}
nr_pages = 0;
for (i = 0; i < found_pages; i++) {
page = wdata->pages[i];
/*
* At this point we hold neither mapping->tree_lock nor
* lock on the page itself: 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++) {
page_cache_release(wdata->pages[i]);
wdata->pages[i] = NULL;
}
/* nothing to write? */
if (nr_pages == 0) {
kref_put(&wdata->refcount, cifs_writedata_release);
continue;
}
wdata->sync_mode = wbc->sync_mode;
wdata->nr_pages = nr_pages;
wdata->offset = page_offset(wdata->pages[0]);
wdata->pagesz = PAGE_CACHE_SIZE;
wdata->tailsz =
min(i_size_read(mapping->host) -
page_offset(wdata->pages[nr_pages - 1]),
(loff_t)PAGE_CACHE_SIZE);
wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) +
wdata->tailsz;
do {
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;
break;
}
wdata->pid = wdata->cfile->pid;
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata,
cifs_writedata_release);
} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
for (i = 0; i < nr_pages; ++i)
unlock_page(wdata->pages[i]);
/* send failure -- clean up the mess */
if (rc != 0) {
for (i = 0; i < nr_pages; ++i) {
if (rc == -EAGAIN)
redirty_page_for_writepage(wbc,
wdata->pages[i]);
else
SetPageError(wdata->pages[i]);
end_page_writeback(wdata->pages[i]);
page_cache_release(wdata->pages[i]);
}
if (rc != -EAGAIN)
mapping_set_error(mapping, rc);
}
kref_put(&wdata->refcount, cifs_writedata_release);
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 (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 */
page_cache_get(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_CACHE_SIZE);
if (rc == -EAGAIN && wbc->sync_mode == WB_SYNC_ALL)
goto retry_write;
else if (rc == -EAGAIN)
redirty_page_for_writepage(wbc, page);
else if (rc != 0)
SetPageError(page);
else
SetPageUptodate(page);
end_page_writeback(page);
page_cache_release(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_CACHE_SIZE)
SetPageUptodate(page);
if (!PageUptodate(page)) {
char *page_data;
unsigned offset = pos & (PAGE_CACHE_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);
page_cache_release(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 = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (rc)
return rc;
mutex_lock(&inode->i_mutex);
xid = get_xid();
cifs_dbg(FYI, "Sync file - name: %s datasync: 0x%x\n",
file->f_path.dentry->d_name.name, datasync);
if (!CIFS_CACHE_READ(CIFS_I(inode))) {
rc = cifs_invalidate_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);
mutex_unlock(&inode->i_mutex);
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_SB(file->f_path.dentry->d_sb);
struct inode *inode = file->f_mapping->host;
rc = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (rc)
return rc;
mutex_lock(&inode->i_mutex);
xid = get_xid();
cifs_dbg(FYI, "Sync file - name: %s datasync: 0x%x\n",
file->f_path.dentry->d_name.name, 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);
mutex_unlock(&inode->i_mutex);
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);
for (i = 0; i < wdata->nr_pages; i++)
put_page(wdata->pages[i]);
cifs_writedata_release(refcount);
}
static void
cifs_uncached_writev_complete(struct work_struct *work)
{
struct cifs_writedata *wdata = container_of(work,
struct cifs_writedata, work);
struct inode *inode = wdata->cfile->dentry->d_inode;
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);
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
}
/* attempt to send write to server, retry on any -EAGAIN errors */
static int
cifs_uncached_retry_writev(struct cifs_writedata *wdata)
{
int rc;
struct TCP_Server_Info *server;
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
do {
if (wdata->cfile->invalidHandle) {
rc = cifs_reopen_file(wdata->cfile, false);
if (rc != 0)
continue;
}
rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release);
} while (rc == -EAGAIN);
return rc;
}
static ssize_t
cifs_iovec_write(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
unsigned long nr_pages, i;
size_t bytes, copied, len, cur_len;
ssize_t total_written = 0;
loff_t offset;
struct iov_iter it;
struct cifsFileInfo *open_file;
struct cifs_tcon *tcon;
struct cifs_sb_info *cifs_sb;
struct cifs_writedata *wdata, *tmp;
struct list_head wdata_list;
int rc;
pid_t pid;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
rc = generic_write_checks(file, poffset, &len, 0);
if (rc)
return rc;
INIT_LIST_HEAD(&wdata_list);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
open_file = file->private_data;
tcon = tlink_tcon(open_file->tlink);
if (!tcon->ses->server->ops->async_writev)
return -ENOSYS;
offset = *poffset;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid;
else
pid = current->tgid;
iov_iter_init(&it, iov, nr_segs, len, 0);
do {
size_t save_len;
nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
wdata = cifs_writedata_alloc(nr_pages,
cifs_uncached_writev_complete);
if (!wdata) {
rc = -ENOMEM;
break;
}
rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
if (rc) {
kfree(wdata);
break;
}
save_len = cur_len;
for (i = 0; i < nr_pages; i++) {
bytes = min_t(const size_t, cur_len, PAGE_SIZE);
copied = iov_iter_copy_from_user(wdata->pages[i], &it,
0, bytes);
cur_len -= copied;
iov_iter_advance(&it, 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;
/*
* 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. Set the rc to -EFAULT,
* free anything we allocated and bail out.
*/
if (!cur_len) {
for (i = 0; i < nr_pages; i++)
put_page(wdata->pages[i]);
kfree(wdata);
rc = -EFAULT;
break;
}
/*
* i + 1 now represents the number of pages we actually used in
* the copy phase above. Bring nr_pages down to that, and free
* any pages that we didn't use.
*/
for ( ; nr_pages > i + 1; 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);
rc = cifs_uncached_retry_writev(wdata);
if (rc) {
kref_put(&wdata->refcount,
cifs_uncached_writedata_release);
break;
}
list_add_tail(&wdata->list, &wdata_list);
offset += cur_len;
len -= cur_len;
} while (len > 0);
/*
* 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(&wdata_list))
rc = 0;
/*
* Wait for and collect replies for any successful sends in order of
* increasing offset. Once an error is hit or we get a fatal signal
* while waiting, then return without waiting for any more replies.
*/
restart_loop:
list_for_each_entry_safe(wdata, tmp, &wdata_list, list) {
if (!rc) {
/* FIXME: freezable too? */
rc = wait_for_completion_killable(&wdata->done);
if (rc)
rc = -EINTR;
else if (wdata->result)
rc = wdata->result;
else
total_written += wdata->bytes;
/* resend call if it's a retryable error */
if (rc == -EAGAIN) {
rc = cifs_uncached_retry_writev(wdata);
goto restart_loop;
}
}
list_del_init(&wdata->list);
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
}
if (total_written > 0)
*poffset += total_written;
cifs_stats_bytes_written(tcon, total_written);
return total_written ? total_written : (ssize_t)rc;
}
ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t written;
struct inode *inode;
inode = file_inode(iocb->ki_filp);
/*
* 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.
*/
written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
if (written > 0) {
CIFS_I(inode)->invalid_mapping = true;
iocb->ki_pos = pos;
}
return written;
}
static ssize_t
cifs_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
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 = -EACCES;
BUG_ON(iocb->ki_pos != pos);
/*
* We need to hold the sem to be sure nobody modifies lock list
* with a brlock that prevents writing.
*/
down_read(&cinode->lock_sem);
if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
server->vals->exclusive_lock_type, NULL,
CIFS_WRITE_OP)) {
mutex_lock(&inode->i_mutex);
rc = __generic_file_aio_write(iocb, iov, nr_segs,
&iocb->ki_pos);
mutex_unlock(&inode->i_mutex);
}
if (rc > 0 || rc == -EIOCBQUEUED) {
ssize_t err;
err = generic_write_sync(file, pos, rc);
if (err < 0 && rc > 0)
rc = err;
}
up_read(&cinode->lock_sem);
return rc;
}
ssize_t
cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
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;
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))
return generic_file_aio_write(iocb, iov, nr_segs, pos);
return cifs_writev(iocb, iov, nr_segs, pos);
}
/*
* 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, iov, nr_segs, pos);
if (written > 0 && CIFS_CACHE_READ(cinode)) {
/*
* Windows 7 server can delay breaking level2 oplock if a write
* request comes - break it on the client to prevent reading
* an old data.
*/
cifs_invalidate_mapping(inode);
cifs_dbg(FYI, "Set no oplock for inode=%p after a write operation\n",
inode);
cinode->oplock = 0;
}
return written;
}
static struct cifs_readdata *
cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete)
{
struct cifs_readdata *rdata;
rdata = kzalloc(sizeof(*rdata) + (sizeof(struct page *) * nr_pages),
GFP_KERNEL);
if (rdata != NULL) {
kref_init(&rdata->refcount);
INIT_LIST_HEAD(&rdata->list);
init_completion(&rdata->done);
INIT_WORK(&rdata->work, complete);
}
return rdata;
}
void
cifs_readdata_release(struct kref *refcount)
{
struct cifs_readdata *rdata = container_of(refcount,
struct cifs_readdata, refcount);
if (rdata->cfile)
cifsFileInfo_put(rdata->cfile);
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) {
for (i = 0; i < nr_pages; 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;
for (i = 0; i < rdata->nr_pages; i++) {
put_page(rdata->pages[i]);
rdata->pages[i] = NULL;
}
cifs_readdata_release(refcount);
}
static int
cifs_retry_async_readv(struct cifs_readdata *rdata)
{
int rc;
struct TCP_Server_Info *server;
server = tlink_tcon(rdata->cfile->tlink)->ses->server;
do {
if (rdata->cfile->invalidHandle) {
rc = cifs_reopen_file(rdata->cfile, true);
if (rc != 0)
continue;
}
rc = server->ops->async_readv(rdata);
} while (rc == -EAGAIN);
return rc;
}
/**
* cifs_readdata_to_iov - copy data from pages in response to an iovec
* @rdata: the readdata response with list of pages holding data
* @iov: vector in which we should copy the data
* @nr_segs: number of segments in vector
* @offset: offset into file of the first iovec
* @copied: used to return the amount of data copied to the iov
*
* 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 ssize_t
cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov,
unsigned long nr_segs, loff_t offset, ssize_t *copied)
{
int rc = 0;
struct iov_iter ii;
size_t pos = rdata->offset - offset;
ssize_t remaining = rdata->bytes;
unsigned char *pdata;
unsigned int i;
/* set up iov_iter and advance to the correct offset */
iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0);
iov_iter_advance(&ii, pos);
*copied = 0;
for (i = 0; i < rdata->nr_pages; i++) {
ssize_t copy;
struct page *page = rdata->pages[i];
/* copy a whole page or whatever's left */
copy = min_t(ssize_t, remaining, PAGE_SIZE);
/* ...but limit it to whatever space is left in the iov */
copy = min_t(ssize_t, copy, iov_iter_count(&ii));
/* go while there's data to be copied and no errors */
if (copy && !rc) {
pdata = kmap(page);
rc = memcpy_toiovecend(ii.iov, pdata, ii.iov_offset,
(int)copy);
kunmap(page);
if (!rc) {
*copied += copy;
remaining -= copy;
iov_iter_advance(&ii, copy);
}
}
}
return rc;
}
static void
cifs_uncached_readv_complete(struct work_struct *work)
{
struct cifs_readdata *rdata = container_of(work,
struct cifs_readdata, work);
complete(&rdata->done);
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
}
static int
cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
struct cifs_readdata *rdata, unsigned int len)
{
int total_read = 0, result = 0;
unsigned int i;
unsigned int nr_pages = rdata->nr_pages;
struct kvec iov;
rdata->tailsz = PAGE_SIZE;
for (i = 0; i < nr_pages; i++) {
struct page *page = rdata->pages[i];
if (len >= PAGE_SIZE) {
/* enough data to fill the page */
iov.iov_base = kmap(page);
iov.iov_len = PAGE_SIZE;
cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n",
i, iov.iov_base, iov.iov_len);
len -= PAGE_SIZE;
} else if (len > 0) {
/* enough for partial page, fill and zero the rest */
iov.iov_base = kmap(page);
iov.iov_len = len;
cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n",
i, iov.iov_base, iov.iov_len);
memset(iov.iov_base + len, '\0', PAGE_SIZE - len);
rdata->tailsz = len;
len = 0;
} else {
/* no need to hold page hostage */
rdata->pages[i] = NULL;
rdata->nr_pages--;
put_page(page);
continue;
}
result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len);
kunmap(page);
if (result < 0)
break;
total_read += result;
}
return total_read > 0 ? total_read : result;
}
static ssize_t
cifs_iovec_read(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
ssize_t rc;
size_t len, cur_len;
ssize_t total_read = 0;
loff_t offset = *poffset;
unsigned int npages;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
struct cifsFileInfo *open_file;
struct cifs_readdata *rdata, *tmp;
struct list_head rdata_list;
pid_t pid;
if (!nr_segs)
return 0;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
INIT_LIST_HEAD(&rdata_list);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
open_file = file->private_data;
tcon = tlink_tcon(open_file->tlink);
if (!tcon->ses->server->ops->async_readv)
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");
do {
cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
npages = DIV_ROUND_UP(cur_len, PAGE_SIZE);
/* allocate a readdata struct */
rdata = cifs_readdata_alloc(npages,
cifs_uncached_readv_complete);
if (!rdata) {
rc = -ENOMEM;
goto error;
}
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->read_into_pages = cifs_uncached_read_into_pages;
rc = cifs_retry_async_readv(rdata);
error:
if (rc) {
kref_put(&rdata->refcount,
cifs_uncached_readdata_release);
break;
}
list_add_tail(&rdata->list, &rdata_list);
offset += cur_len;
len -= cur_len;
} while (len > 0);
/* if at least one read request send succeeded, then reset rc */
if (!list_empty(&rdata_list))
rc = 0;
/* the loop below should proceed in the order of increasing offsets */
restart_loop:
list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
if (!rc) {
ssize_t copied;
/* FIXME: freezable sleep too? */
rc = wait_for_completion_killable(&rdata->done);
if (rc)
rc = -EINTR;
else if (rdata->result)
rc = rdata->result;
else {
rc = cifs_readdata_to_iov(rdata, iov,
nr_segs, *poffset,
&copied);
total_read += copied;
}
/* resend call if it's a retryable error */
if (rc == -EAGAIN) {
rc = cifs_retry_async_readv(rdata);
goto restart_loop;
}
}
list_del_init(&rdata->list);
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
}
cifs_stats_bytes_read(tcon, total_read);
*poffset += total_read;
/* mask nodata case */
if (rc == -ENODATA)
rc = 0;
return total_read ? total_read : rc;
}
ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t read;
read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos);
if (read > 0)
iocb->ki_pos = pos;
return read;
}
ssize_t
cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
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, iov, nr_segs, pos);
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_aio_read(iocb, iov, nr_segs, pos);
/*
* 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, pos, iov_length(iov, nr_segs),
tcon->ses->server->vals->shared_lock_type,
NULL, CIFS_READ_OP))
rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
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_SB(file->f_path.dentry->d_sb);
/* 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) {
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) && !(tcon->ses->capabilities &
tcon->ses->server->vals->cap_large_files)) {
current_read_size = min_t(uint, current_read_size,
CIFSMaxBufSize);
}
rc = -EAGAIN;
while (rc == -EAGAIN) {
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, &io_parms,
&bytes_read, &cur_offset,
&buf_type);
}
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 int
cifs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct page *page = vmf->page;
lock_page(page);
return VM_FAULT_LOCKED;
}
static struct vm_operations_struct cifs_file_vm_ops = {
.fault = filemap_fault,
.page_mkwrite = cifs_page_mkwrite,
.remap_pages = generic_file_remap_pages,
};
int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
{
int rc, xid;
struct inode *inode = file_inode(file);
xid = get_xid();
if (!CIFS_CACHE_READ(CIFS_I(inode))) {
rc = cifs_invalidate_mapping(inode);
if (rc)
return rc;
}
rc = generic_file_mmap(file, vma);
if (rc == 0)
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);
free_xid(xid);
return rc;
}
rc = generic_file_mmap(file, vma);
if (rc == 0)
vma->vm_ops = &cifs_file_vm_ops;
free_xid(xid);
return rc;
}
static void
cifs_readv_complete(struct work_struct *work)
{
unsigned int i;
struct cifs_readdata *rdata = container_of(work,
struct cifs_readdata, work);
for (i = 0; i < rdata->nr_pages; i++) {
struct page *page = rdata->pages[i];
lru_cache_add_file(page);
if (rdata->result == 0) {
flush_dcache_page(page);
SetPageUptodate(page);
}
unlock_page(page);
if (rdata->result == 0)
cifs_readpage_to_fscache(rdata->mapping->host, page);
page_cache_release(page);
rdata->pages[i] = NULL;
}
kref_put(&rdata->refcount, cifs_readdata_release);
}
static int
cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
struct cifs_readdata *rdata, unsigned int len)
{
int total_read = 0, result = 0;
unsigned int i;
u64 eof;
pgoff_t eof_index;
unsigned int nr_pages = rdata->nr_pages;
struct kvec iov;
/* determine the eof that the server (probably) has */
eof = CIFS_I(rdata->mapping->host)->server_eof;
eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index);
rdata->tailsz = PAGE_CACHE_SIZE;
for (i = 0; i < nr_pages; i++) {
struct page *page = rdata->pages[i];
if (len >= PAGE_CACHE_SIZE) {
/* enough data to fill the page */
iov.iov_base = kmap(page);
iov.iov_len = PAGE_CACHE_SIZE;
cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n",
i, page->index, iov.iov_base, iov.iov_len);
len -= PAGE_CACHE_SIZE;
} else if (len > 0) {
/* enough for partial page, fill and zero the rest */
iov.iov_base = kmap(page);
iov.iov_len = len;
cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n",
i, page->index, iov.iov_base, iov.iov_len);
memset(iov.iov_base + len,
'\0', PAGE_CACHE_SIZE - len);
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_CACHE_SIZE);
lru_cache_add_file(page);
flush_dcache_page(page);
SetPageUptodate(page);
unlock_page(page);
page_cache_release(page);
rdata->pages[i] = NULL;
rdata->nr_pages--;
continue;
} else {
/* no need to hold page hostage */
lru_cache_add_file(page);
unlock_page(page);
page_cache_release(page);
rdata->pages[i] = NULL;
rdata->nr_pages--;
continue;
}
result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len);
kunmap(page);
if (result < 0)
break;
total_read += result;
}
return total_read > 0 ? total_read : result;
}
static int cifs_readpages(struct file *file, struct address_space *mapping,
struct list_head *page_list, unsigned num_pages)
{
int rc;
struct list_head tmplist;
struct cifsFileInfo *open_file = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
unsigned int rsize = cifs_sb->rsize;
pid_t pid;
/*
* 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_CACHE_SIZE))
return 0;
/*
* Reads as many pages as possible from fscache. Returns -ENOBUFS
* immediately if the cookie is negative
*/
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;
INIT_LIST_HEAD(&tmplist);
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)) {
unsigned int i;
unsigned int bytes = PAGE_CACHE_SIZE;
unsigned int expected_index;
unsigned int nr_pages = 1;
loff_t offset;
struct page *page, *tpage;
struct cifs_readdata *rdata;
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.
*/
__set_page_locked(page);
rc = add_to_page_cache_locked(page, mapping,
page->index, GFP_KERNEL);
/* give up if we can't stick it in the cache */
if (rc) {
__clear_page_locked(page);
break;
}
/* move first page to the tmplist */
offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
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_CACHE_SIZE > rsize)
break;
__set_page_locked(page);
if (add_to_page_cache_locked(page, mapping,
page->index, GFP_KERNEL)) {
__clear_page_locked(page);
break;
}
list_move_tail(&page->lru, &tmplist);
bytes += PAGE_CACHE_SIZE;
expected_index++;
nr_pages++;
}
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);
page_cache_release(page);
}
rc = -ENOMEM;
break;
}
rdata->cfile = cifsFileInfo_get(open_file);
rdata->mapping = mapping;
rdata->offset = offset;
rdata->bytes = bytes;
rdata->pid = pid;
rdata->pagesz = PAGE_CACHE_SIZE;
rdata->read_into_pages = cifs_readpages_read_into_pages;
list_for_each_entry_safe(page, tpage, &tmplist, lru) {
list_del(&page->lru);
rdata->pages[rdata->nr_pages++] = page;
}
rc = cifs_retry_async_readv(rdata);
if (rc != 0) {
for (i = 0; i < rdata->nr_pages; i++) {
page = rdata->pages[i];
lru_cache_add_file(page);
unlock_page(page);
page_cache_release(page);
}
kref_put(&rdata->refcount, cifs_readdata_release);
break;
}
kref_put(&rdata->refcount, cifs_readdata_release);
}
return rc;
}
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;
page_cache_get(page);
read_data = kmap(page);
/* for reads over a certain size could initiate async read ahead */
rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset);
if (rc < 0)
goto io_error;
else
cifs_dbg(FYI, "Bytes read %d\n", rc);
file_inode(file)->i_atime =
current_fs_time(file_inode(file)->i_sb);
if (PAGE_CACHE_SIZE > rc)
memset(read_data + rc, 0, PAGE_CACHE_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);
page_cache_release(page);
read_complete:
return rc;
}
static int cifs_readpage(struct file *file, struct page *page)
{
loff_t offset = (loff_t)page->index << PAGE_CACHE_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);
unlock_page(page);
free_xid(xid);
return rc;
}
static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
{
struct cifsFileInfo *open_file;
spin_lock(&cifs_file_list_lock);
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
spin_unlock(&cifs_file_list_lock);
return 1;
}
}
spin_unlock(&cifs_file_list_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)
{
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
loff_t offset = pos & (PAGE_CACHE_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);
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_CACHE_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_CACHE_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) {
/*
* 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);
} 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 long offset)
{
struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host);
if (offset == 0)
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_CACHE_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 = cfile->dentry->d_inode;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
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)) {
rc = filemap_fdatawait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
cifs_invalidate_mapping(inode);
}
cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);
}
rc = cifs_push_locks(cfile);
if (rc)
cifs_dbg(VFS, "Push locks rc = %d\n", rc);
/*
* 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);
}
}
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,
.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,
};
/*
* 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"
void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server)
{
server->fscache =
fscache_acquire_cookie(cifs_fscache_netfs.primary_index,
&cifs_fscache_server_index_def, server);
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, 0);
server->fscache = NULL;
}
void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
{
struct TCP_Server_Info *server = tcon->ses->server;
tcon->fscache =
fscache_acquire_cookie(server->fscache,
&cifs_fscache_super_index_def, tcon);
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, 0);
tcon->fscache = NULL;
}
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) {
cifsi->fscache = fscache_acquire_cookie(tcon->fscache,
&cifs_fscache_inode_object_def, cifsi);
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 cifsInodeInfo *cifsi = CIFS_I(inode);
if (cifsi->fscache) {
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
fscache_relinquish_cookie(cifsi->fscache, 0);
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, 1);
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 fscache_cookie *old = cifsi->fscache;
if (cifsi->fscache) {
/* retire the current fscache cache and get a new one */
fscache_relinquish_cookie(cifsi->fscache, 1);
cifsi->fscache = fscache_acquire_cookie(
cifs_sb_master_tcon(cifs_sb)->fscache,
&cifs_fscache_inode_object_def,
cifsi);
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)
{
int ret;
cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n",
__func__, CIFS_I(inode)->fscache, page, inode);
ret = fscache_write_page(CIFS_I(inode)->fscache, page, GFP_KERNEL);
if (ret != 0)
fscache_uncache_page(CIFS_I(inode)->fscache, page);
}
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);
}
/*
* 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
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);
/*
* 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_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);
}
#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) {}
#endif /* CONFIG_CIFS_FSCACHE */
#endif /* _CIFS_FSCACHE_H */
/*
* 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 <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 "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_CACHE_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 (timespec_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);
cifs_i->invalid_mapping = true;
}
/* 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;
set_nlink(inode, fattr->cf_nlink);
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;
cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING;
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);
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;
fattr->cf_atime = CURRENT_TIME;
fattr->cf_ctime = CURRENT_TIME;
fattr->cf_mtime = CURRENT_TIME;
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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 = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid);
if (tmprc)
cifs_dbg(FYI, "CIFSCheckMFSymlink: %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 */
cifs_fattr_to_inode(*pinode, &fattr);
}
return rc;
}
static int
cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
struct cifs_sb_info *cifs_sb, unsigned int xid)
{
int rc;
int oplock = 0;
__u16 netfid;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
struct cifs_io_parms io_parms;
char buf[24];
unsigned int bytes_read;
char *pbuf;
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);
rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ,
FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock,
NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
int buf_type = CIFS_NO_BUFFER;
/* Read header */
io_parms.netfid = netfid;
io_parms.pid = current->tgid;
io_parms.tcon = tcon;
io_parms.offset = 0;
io_parms.length = 24;
rc = CIFSSMBRead(xid, &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 */
}
CIFSSMBClose(xid, tcon, netfid);
}
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);
rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS",
ea_value, 4 /* size of buf */, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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 cifs_sb_info *cifs_sb, bool adjust_tz)
{
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
fattr->cf_atime = CURRENT_TIME;
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);
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.
*/
fattr->cf_nlink = 2;
} else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK;
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
} 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);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
if (fattr->cf_nlink < 1) {
cifs_dbg(1, "replacing bogus file nlink value %u\n",
fattr->cf_nlink);
fattr->cf_nlink = 1;
}
}
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 cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
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, cifs_sb, 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;
}
int
cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb, int xid,
const __u16 *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;
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_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);
}
if (!rc) {
cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *)data, cifs_sb,
adjust_tz);
} else if (rc == -EREMOTE) {
cifs_create_dfs_fattr(&fattr, sb);
rc = 0;
} else if (rc == -EACCES && backup_cred(cifs_sb)) {
srchinf = kzalloc(sizeof(struct cifs_search_info),
GFP_KERNEL);
if (srchinf == NULL) {
rc = -ENOMEM;
goto cgii_exit;
}
srchinf->endOfSearch = false;
srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_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);
} 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
fattr.cf_uniqueid = iunique(sb, ROOT_I);
} 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 = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid);
if (tmprc)
cifs_dbg(FYI, "CIFSCheckMFSymlink: %d\n", tmprc);
}
if (!*inode) {
*inode = cifs_iget(sb, &fattr);
if (!*inode)
rc = -ENOMEM;
} else {
cifs_fattr_to_inode(*inode, &fattr);
}
cgii_exit:
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_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 & MS_NOATIME)
inode->i_flags |= S_NOATIME | S_NOCMTIME;
if (inode->i_state & I_NEW) {
inode->i_ino = hash;
if (S_ISREG(inode->i_mode))
inode->i_data.backing_dev_info = sb->s_bdi;
#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);
xid = get_xid();
if (tcon->unix_ext)
rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
else
rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
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->ipc) {
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:
/* can not call macro free_xid here since in a void func
* TODO: This is no longer true
*/
_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;
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;
__u16 netfid;
struct inode *inode = dentry->d_inode;
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;
}
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
DELETE|FILE_WRITE_ATTRIBUTES, FILE_SHARE_ALL, CREATE_NOT_DIR,
&netfid, &oplock, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
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, 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, netfid, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != 0) {
rc = -ETXTBSY;
goto undo_setattr;
}
/* try to set DELETE_ON_CLOSE */
if (!cifsInode->delete_pending) {
rc = CIFSSMBSetFileDisposition(xid, tcon, true, 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 = -ETXTBSY;
goto undo_rename;
}
cifsInode->delete_pending = true;
}
out_close:
CIFSSMBClose(xid, tcon, 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, netfid, dentry->d_name.name,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
undo_setattr:
if (dosattr != origattr) {
info_buf->Attributes = cpu_to_le32(origattr);
if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, 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 dentry->d_inode 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 = dentry->d_inode;
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_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
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 == -ETXTBSY) {
if (server->ops->rename_pending_delete) {
rc = server->ops->rename_pending_delete(full_path,
dentry, xid);
if (rc == 0)
cifs_drop_nlink(inode);
}
if (rc == -ETXTBSY)
rc = -EBUSY;
} 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_fs_time(sb);
}
dir->i_ctime = dir->i_mtime = current_fs_time(sb);
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
} 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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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 %s to inode %p\n",
dentry, dentry->d_name.name, 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 = 0x%hx 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;
}
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;
}
server = tcon->ses->server;
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(&direntry->d_inode->i_lock);
i_size_write(direntry->d_inode, 0);
clear_nlink(direntry->d_inode);
spin_unlock(&direntry->d_inode->i_lock);
}
cifsInode = CIFS_I(direntry->d_inode);
/* 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;
direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
current_fs_time(inode->i_sb);
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;
__u16 srcfid;
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 != -ETXTBSY)
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;
/* open the file to be renamed -- we need DELETE perms */
rc = CIFSSMBOpen(xid, tcon, from_path, FILE_OPEN, DELETE, FILE_SHARE_ALL,
CREATE_NOT_DIR, &srcfid, &oplock, NULL,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
rc = CIFSSMBRenameOpenFile(xid, tcon, srcfid,
(const char *) to_dentry->d_name.name,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
CIFSSMBClose(xid, tcon, srcfid);
}
do_rename_exit:
cifs_put_tlink(tlink);
return rc;
}
int
cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
struct inode *target_dir, struct dentry *target_dentry)
{
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;
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);
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(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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (tmprc != 0)
goto unlink_target;
tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name,
info_buf_target,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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 (target_dentry->d_inode && (rc == -EACCES || rc == -EEXIST)) {
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);
}
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_CACHE_READ(cifs_i))
return false;
if (!lookupCacheEnabled)
return true;
if (cifs_i->time == 0)
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;
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
cifs_i->invalid_mapping = false;
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_i->invalid_mapping = true;
}
}
cifs_fscache_reset_inode_cookie(inode);
return rc;
}
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 = dentry->d_inode;
struct super_block *sb = dentry->d_sb;
char *full_path = NULL;
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, dentry->d_time, jiffies);
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);
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;
if (CIFS_I(inode)->invalid_mapping)
rc = cifs_invalidate_mapping(inode);
return rc;
}
/* revalidate a dentry's inode attributes */
int cifs_revalidate_dentry(struct dentry *dentry)
{
int rc;
struct inode *inode = dentry->d_inode;
rc = cifs_revalidate_dentry_attr(dentry);
if (rc)
return rc;
if (CIFS_I(inode)->invalid_mapping)
rc = cifs_invalidate_mapping(inode);
return rc;
}
int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct inode *inode = dentry->d_inode;
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;
/*
* 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_CACHE_SHIFT;
unsigned offset = from & (PAGE_CACHE_SIZE - 1);
struct page *page;
int rc = 0;
page = grab_cache_page(mapping, index);
if (!page)
return -ENOMEM;
zero_user_segment(page, offset, PAGE_CACHE_SIZE);
unlock_page(page);
page_cache_release(page);
return rc;
}
static void cifs_setsize(struct inode *inode, loff_t offset)
{
loff_t oldsize;
spin_lock(&inode->i_lock);
oldsize = inode->i_size;
i_size_write(inode, offset);
spin_unlock(&inode->i_lock);
truncate_pagecache(inode, oldsize, 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;
struct cifs_io_parms io_parms;
/*
* 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);
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
unsigned int bytes_written;
io_parms.netfid = open_file->fid.netfid;
io_parms.pid = open_file->pid;
io_parms.tcon = tcon;
io_parms.offset = 0;
io_parms.length = attrs->ia_size;
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written,
NULL, NULL, 1);
cifs_dbg(FYI, "Wrt seteof 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 ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
__u16 netfid;
int oplock = 0;
rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN,
GENERIC_WRITE, CREATE_NOT_DIR, &netfid,
&oplock, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
unsigned int bytes_written;
io_parms.netfid = netfid;
io_parms.pid = current->tgid;
io_parms.tcon = tcon;
io_parms.offset = 0;
io_parms.length = attrs->ia_size;
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, NULL,
NULL, 1);
cifs_dbg(FYI, "wrt seteof rc %d\n", rc);
CIFSSMBClose(xid, tcon, netfid);
}
}
if (tlink)
cifs_put_tlink(tlink);
set_size_out:
if (rc == 0) {
cifsInode->server_eof = attrs->ia_size;
cifs_setsize(inode, attrs->ia_size);
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 = direntry->d_inode;
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 %s attrs->ia_valid=0x%x\n",
direntry->d_name.name, attrs->ia_valid);
xid = get_xid();
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
attrs->ia_valid |= ATTR_FORCE;
rc = inode_change_ok(inode, 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);
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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 = direntry->d_inode;
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 %s attrs->iavalid 0x%x\n",
direntry->d_name.name, attrs->ia_valid);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
attrs->ia_valid |= ATTR_FORCE;
rc = inode_change_ok(inode, 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);
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 inode *inode = direntry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);
if (pTcon->unix_ext)
return cifs_setattr_unix(direntry, attrs);
return cifs_setattr_nounix(direntry, attrs);
/* BB: add cifs_setattr_legacy for really old servers */
}
#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
/*
* fs/cifs/ioctl.c
*
* vfs operations that deal with io control
*
* Copyright (C) International Business Machines Corp., 2005,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 "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "cifsfs.h"
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;
#ifdef CONFIG_CIFS_POSIX
struct cifsFileInfo *pSMBFile = filep->private_data;
struct cifs_tcon *tcon;
__u64 ExtAttrBits = 0;
__u64 ExtAttrMask = 0;
__u64 caps;
#endif /* CONFIG_CIFS_POSIX */
xid = get_xid();
cifs_dbg(FYI, "ioctl file %p cmd %u arg %lu\n", filep, command, arg);
cifs_sb = CIFS_SB(inode->i_sb);
switch (command) {
#ifdef CONFIG_CIFS_POSIX
case FS_IOC_GETFLAGS:
if (pSMBFile == NULL)
break;
tcon = tlink_tcon(pSMBFile->tlink);
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
if (CIFS_UNIX_EXTATTR_CAP & caps) {
rc = CIFSGetExtAttr(xid, tcon,
pSMBFile->fid.netfid,
&ExtAttrBits, &ExtAttrMask);
if (rc == 0)
rc = put_user(ExtAttrBits &
FS_FL_USER_VISIBLE,
(int __user *)arg);
}
break;
case FS_IOC_SETFLAGS:
if (pSMBFile == NULL)
break;
tcon = tlink_tcon(pSMBFile->tlink);
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
if (CIFS_UNIX_EXTATTR_CAP & caps) {
if (get_user(ExtAttrBits, (int __user *)arg)) {
rc = -EFAULT;
break;
}
/*
* rc = CIFSGetExtAttr(xid, tcon,
* pSMBFile->fid.netfid,
* extAttrBits,
* &ExtAttrMask);
*/
}
cifs_dbg(FYI, "set flags not implemented yet\n");
break;
#endif /* CONFIG_CIFS_POSIX */
default:
cifs_dbg(FYI, "unsupported ioctl\n");
break;
}
free_xid(xid);
return rc;
}
/*
* 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"
#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 \
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n"
#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \
md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \
md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \
md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\
md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15]
static int
symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
{
int rc;
unsigned int size;
struct crypto_shash *md5;
struct sdesc *sdescmd5;
md5 = crypto_alloc_shash("md5", 0, 0);
if (IS_ERR(md5)) {
rc = PTR_ERR(md5);
cifs_dbg(VFS, "%s: Crypto md5 allocation error %d\n",
__func__, rc);
return rc;
}
size = sizeof(struct shash_desc) + crypto_shash_descsize(md5);
sdescmd5 = kmalloc(size, GFP_KERNEL);
if (!sdescmd5) {
rc = -ENOMEM;
goto symlink_hash_err;
}
sdescmd5->shash.tfm = md5;
sdescmd5->shash.flags = 0x0;
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:
crypto_free_shash(md5);
kfree(sdescmd5);
return rc;
}
static int
CIFSParseMFSymlink(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;
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
CIFSFormatMFSymlink(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;
}
static int
CIFSCreateMFSymLink(const unsigned int xid, struct cifs_tcon *tcon,
const char *fromName, const char *toName,
struct cifs_sb_info *cifs_sb)
{
int rc;
int oplock = 0;
int remap;
int create_options = CREATE_NOT_DIR;
__u16 netfid = 0;
u8 *buf;
unsigned int bytes_written = 0;
struct cifs_io_parms io_parms;
struct nls_table *nls_codepage;
nls_codepage = cifs_sb->local_nls;
remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
if (rc != 0) {
kfree(buf);
return rc;
}
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
FILE_SHARE_ALL, create_options, &netfid, &oplock, NULL,
nls_codepage, remap);
if (rc != 0) {
kfree(buf);
return rc;
}
io_parms.netfid = 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, &bytes_written, buf, NULL, 0);
CIFSSMBClose(xid, tcon, netfid);
kfree(buf);
if (rc != 0)
return rc;
if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
return -EIO;
return 0;
}
static int
CIFSQueryMFSymLink(const unsigned int xid, struct cifs_tcon *tcon,
const unsigned char *searchName, char **symlinkinfo,
const struct nls_table *nls_codepage, int remap)
{
int rc;
int oplock = 0;
__u16 netfid = 0;
u8 *buf;
char *pbuf;
unsigned int bytes_read = 0;
int buf_type = CIFS_NO_BUFFER;
unsigned int link_len = 0;
struct cifs_io_parms io_parms;
FILE_ALL_INFO file_info;
rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ,
FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock,
&file_info, nls_codepage, remap);
if (rc != 0)
return rc;
if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
CIFSSMBClose(xid, tcon, netfid);
/* it's not a symlink */
return -EINVAL;
}
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
pbuf = buf;
io_parms.netfid = 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, &bytes_read, &pbuf, &buf_type);
CIFSSMBClose(xid, tcon, netfid);
if (rc != 0) {
kfree(buf);
return rc;
}
rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo);
kfree(buf);
if (rc != 0)
return rc;
return 0;
}
bool
CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
{
if (!(fattr->cf_mode & S_IFREG))
/* 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;
}
int
open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
unsigned int xid)
{
int rc;
int oplock = 0;
__u16 netfid = 0;
struct tcon_link *tlink;
struct cifs_tcon *ptcon;
struct cifs_io_parms io_parms;
int buf_type = CIFS_NO_BUFFER;
FILE_ALL_INFO file_info;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
ptcon = tlink_tcon(tlink);
rc = CIFSSMBOpen(xid, ptcon, path, FILE_OPEN, GENERIC_READ,
FILE_SHARE_ALL, CREATE_NOT_DIR, &netfid, &oplock,
&file_info, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != 0) {
cifs_put_tlink(tlink);
return rc;
}
if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
CIFSSMBClose(xid, ptcon, netfid);
cifs_put_tlink(tlink);
/* it's not a symlink */
return rc;
}
io_parms.netfid = netfid;
io_parms.pid = current->tgid;
io_parms.tcon = ptcon;
io_parms.offset = 0;
io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
CIFSSMBClose(xid, ptcon, netfid);
cifs_put_tlink(tlink);
return rc;
}
int
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, unsigned int xid)
{
int rc = 0;
u8 *buf = NULL;
unsigned int link_len = 0;
unsigned int bytes_read = 0;
struct cifs_tcon *ptcon;
if (!CIFSCouldBeMFSymlink(fattr))
/* it's not a symlink */
return 0;
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}
ptcon = tlink_tcon(cifs_sb_tlink(cifs_sb));
if ((ptcon->ses) && (ptcon->ses->server->ops->query_mf_symlink))
rc = ptcon->ses->server->ops->query_mf_symlink(path, buf,
&bytes_read, cifs_sb, xid);
else
goto out;
if (rc != 0)
goto out;
if (bytes_read == 0) /* not a symlink */
goto out;
rc = CIFSParseMFSymlink(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;
}
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
else {
server = tcon->ses->server;
if (!server->ops->create_hardlink)
return -ENOSYS;
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 (old_file->d_inode) {
cifsInode = CIFS_I(old_file->d_inode);
if (rc == 0) {
spin_lock(&old_file->d_inode->i_lock);
inc_nlink(old_file->d_inode);
spin_unlock(&old_file->d_inode->i_lock);
/*
* BB should we make this contingent on superblock flag
* NOATIME?
*/
/* old_file->d_inode->i_ctime = CURRENT_TIME; */
/*
* 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
*/
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;
}
void *
cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
{
struct inode *inode = direntry->d_inode;
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;
xid = get_xid();
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
tlink = NULL;
goto out;
}
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
full_path = build_path_from_dentry(direntry);
if (!full_path)
goto out;
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 = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != 0 && server->ops->query_symlink)
rc = server->ops->query_symlink(xid, tcon, full_path,
&target_path, cifs_sb);
kfree(full_path);
out:
if (rc != 0) {
kfree(target_path);
target_path = ERR_PTR(rc);
}
free_xid(xid);
if (tlink)
cifs_put_tlink(tlink);
nd_set_link(nd, target_path);
return NULL;
}
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 = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
cifs_sb);
else if (pTcon->unix_ext)
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
cifs_sb->local_nls);
/* 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;
}
void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
{
char *p = nd_get_link(nd);
if (!IS_ERR(p))
kfree(p);
}
/*
* 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 "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smberr.h"
#include "nterr.h"
#include "cifs_unicode.h"
#ifdef CONFIG_CIFS_SMB2
#include "smb2pdu.h"
#endif
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);
}
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);
if (buf_to_free->password) {
memset(buf_to_free->password, 0, strlen(buf_to_free->password));
kfree(buf_to_free->password);
}
kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
kfree(buf_to_free->auth_key.response);
kfree(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);
#ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock);
#endif
}
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);
if (buf_to_free->password) {
memset(buf_to_free->password, 0, strlen(buf_to_free->password));
kfree(buf_to_free->password);
}
kfree(buf_to_free);
}
struct smb_hdr *
cifs_buf_get(void)
{
struct smb_hdr *ret_buf = NULL;
size_t buf_size = sizeof(struct smb_hdr);
#ifdef CONFIG_CIFS_SMB2
/*
* SMB2 header is bigger than CIFS one - no problems to clean some
* more bytes for CIFS.
*/
buf_size = sizeof(struct smb2_hdr);
#endif
/*
* 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 */
if (ret_buf) {
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);
if (ret_buf) {
/* 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;
}
/* 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, __u16 mid)
{
/* 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;
}
/* Make sure that message ids match */
if (mid != smb->Mid) {
cifs_dbg(VFS, "Mids do not match. received=%u expected=%u\n",
smb->Mid, mid);
return 1;
}
/* if it's a response then accept */
if (smb->Flags & SMBFLG_RESPONSE)
return 0;
/* 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", smb->Mid);
return 1;
}
int
checkSMB(char *buf, unsigned int total_read)
{
struct smb_hdr *smb = (struct smb_hdr *)buf;
__u16 mid = smb->Mid;
__u32 rfclen = be32_to_cpu(smb->smb_buf_length);
__u32 clc_len; /* calculated length */
cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
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, mid))
return -EIO;
clc_len = smbCalcSize(smb);
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) {
/* 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, smb->Mid);
if (4 + rfclen < clc_len) {
cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
rfclen, smb->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, smb->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;
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);
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%d\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%d level 0x%d\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(&cifs_file_list_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(netfile->dentry->d_inode);
cifs_set_oplock_level(pCifsInode,
pSMB->OplockLevel ? OPLOCK_READ : 0);
queue_work(cifsiod_wq,
&netfile->oplock_break);
netfile->oplock_break_cancelled = false;
spin_unlock(&cifs_file_list_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
spin_unlock(&cifs_file_list_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)
{
int i, j;
char debug_line[17];
unsigned char *buffer = buf;
if (traceSMB == 0)
return;
for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
if (i % 8 == 0) {
/* have reached the beginning of line */
printk(KERN_DEBUG "| ");
j = 0;
}
printk("%0#4x ", buffer[i]);
debug_line[2 * j] = ' ';
if (isprint(buffer[i]))
debug_line[1 + (2 * j)] = buffer[i];
else
debug_line[1 + (2 * j)] = '_';
if (i % 8 == 7) {
/* reached end of line, time to print ascii */
debug_line[16] = 0;
printk(" | %s\n", debug_line);
}
}
for (; j < 8; j++) {
printk(" ");
debug_line[2 * j] = ' ';
debug_line[1 + (2 * j)] = ' ';
}
printk(" | %s\n", debug_line);
return;
}
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_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;
}
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(&cifs_file_list_lock);
list_del(&open->olist);
spin_unlock(&cifs_file_list_lock);
}
void
cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
struct cifs_pending_open *open)
{
#ifdef CONFIG_CIFS_SMB2
memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
#endif
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(&cifs_file_list_lock);
cifs_add_pending_open_locked(fid, tlink, open);
spin_unlock(&cifs_file_list_lock);
}
/*
* 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, -ENOMEM},
{ERRbadmem, -EFAULT},
{ERRbadenv, -EFAULT},
{ERRbadformat, -EINVAL},
{ERRbadaccess, -EACCES},
{ERRbaddata, -EIO},
{ERRbaddrive, -ENXIO},
{ERRremcd, -EACCES},
{ERRdiffdevice, -EXDEV},
{ERRnofiles, -ENOENT},
{ERRwriteprot, -EROFS},
{ERRbadshare, -ETXTBSY},
{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}
};
static const struct smb_to_posix_error mapping_table_ERRHRD[] = {
{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}, };
/*****************************************************************************
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)) {
printk(KERN_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 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 timespec
cifs_NTtimeToUnix(__le64 ntutc)
{
struct timespec ts;
/* BB what about the timezone? BB */
/* Subtract the NTFS time offset, then convert to 1s intervals. */
u64 t;
t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
ts.tv_nsec = do_div(t, 10000000) * 100;
ts.tv_sec = t;
return ts;
}
/* Convert the Unix UTC into NT UTC. */
u64
cifs_UnixTimeToNT(struct timespec 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 int total_days_of_prev_months[] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
{
struct timespec ts;
int sec, 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 %d\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 > 31) || (month > 12)) {
cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
if (month > 12)
month = 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;
}
/*
* 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}
};
/*
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 */
/*
* 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);
/*
* 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 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_inode->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
dentry = d_hash_and_lookup(parent, name);
if (unlikely(IS_ERR(dentry)))
return;
if (dentry) {
int err;
inode = dentry->d_inode;
if (inode) {
/*
* 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 i_ino didn't change */
if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
cifs_fattr_to_inode(inode, fattr);
goto out;
}
}
err = d_invalidate(dentry);
dput(dentry);
if (err)
return;
}
/*
* 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;
dentry = d_alloc(parent, name);
if (!dentry)
return;
inode = cifs_iget(sb, fattr);
if (!inode)
goto out;
alias = d_materialise_unique(dentry, inode);
if (alias && !IS_ERR(alias))
dput(alias);
out:
dput(dentry);
}
/*
* Is it possible that this directory might turn out to be a DFS referral
* once we go to try and use it?
*/
static bool
cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb)
{
#ifdef CONFIG_CIFS_DFS_UPCALL
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
return true;
#endif
return false;
}
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;
/*
* Windows CIFS servers generally make DFS referrals look
* like directories in FIND_* responses with the reparse
* attribute flag also set (since DFS junctions are
* reparse points). We must revalidate at least these
* directory inodes before trying to use them (if
* they are DFS we will get PATH_NOT_COVERED back
* when queried directly and can then try to connect
* to the DFS target)
*/
if (cifs_dfs_is_possible(cifs_sb) &&
(fattr->cf_cifsattrs & ATTR_REPARSE))
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
} else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK;
} else {
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
fattr->cf_dtype = DT_REG;
}
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,
FILE_SHARE_ALL, OPEN_REPARSE_POINT, &fid, &oplock, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
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_SB(file->f_path.dentry->d_sb);
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;
}
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->f_path.dentry);
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
new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
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,
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 = file->f_pos;
struct cifsFileInfo *cfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
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(&cifs_file_list_lock);
if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) {
cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_lock);
if (server->ops->close)
server->ops->close(xid, tcon, &cfile->fid);
} else
spin_unlock(&cifs_file_list_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 = cfile->srch_inf.ntwrk_buf_start +
server->ops->calc_smb_size(
cfile->srch_inf.ntwrk_buf_start);
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, filldir_t filldir,
void *dirent, char *scratch_buf, unsigned int max_len)
{
struct cifsFileInfo *file_info = file->private_data;
struct super_block *sb = file->f_path.dentry->d_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;
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,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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) &&
CIFSCouldBeMFSymlink(&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->f_dentry, &name, &fattr);
ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
rc = filldir(dirent, name.name, name.len, file->f_pos, ino,
fattr.cf_dtype);
return rc;
}
int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
{
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;
}
switch ((int) file->f_pos) {
case 0:
if (filldir(direntry, ".", 1, file->f_pos,
file_inode(file)->i_ino, DT_DIR) < 0) {
cifs_dbg(VFS, "Filldir for current dir failed\n");
rc = -ENOMEM;
break;
}
file->f_pos++;
case 1:
if (filldir(direntry, "..", 2, file->f_pos,
parent_ino(file->f_path.dentry), DT_DIR) < 0) {
cifs_dbg(VFS, "Filldir for parent dir failed\n");
rc = -ENOMEM;
break;
}
file->f_pos++;
default:
/* 1) If search is active,
is in current search buffer?
if it before then restart search
if after then keep searching till find it */
if (file->private_data == NULL) {
rc = -EINVAL;
free_xid(xid);
return rc;
}
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;
break;
}
} /* else {
cifsFile->invalidHandle = true;
tcon->ses->server->close(xid, tcon, &cifsFile->fid);
} */
tcon = tlink_tcon(cifsFile->tlink);
rc = find_cifs_entry(xid, tcon, 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", file->f_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);
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;
break;
}
for (i = 0; (i < num_to_fill) && (rc == 0); 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?
*/
rc = cifs_filldir(current_entry, file, filldir,
direntry, tmp_buf, max_len);
if (rc == -EOVERFLOW) {
rc = 0;
break;
}
file->f_pos++;
if (file->f_pos ==
cifsFile->srch_inf.index_of_last_entry) {
cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
file->f_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);
break;
} /* end switch */
rddir2_exit:
free_xid(xid);
return rc;
}
/*
* 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 "
/*
* 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 = __constant_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;
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;
}
/* We do not malloc the blob, it is passed in pbuffer, because its
maximum possible size is fixed and small, making this approach cleaner.
This function returns the length of the data in the blob */
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 = (AUTHENTICATE_MESSAGE *)pbuffer;
__u32 flags;
unsigned char *tmp;
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;
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);
rc = setup_ntlmv2_rsp(ses, nls_cp);
if (rc) {
cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
goto setup_ntlmv2_ret;
}
memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
ses->auth_key.len - CIFS_SESS_KEY_SIZE);
tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
sec_blob->NtChallengeResponse.Length =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
sec_blob->NtChallengeResponse.MaximumLength =
cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
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_USERNAME_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;
}
setup_ntlmv2_ret:
*buflen = tmp - pbuffer;
return rc;
}
enum securityEnum
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;
/* Fallthrough */
default:
return Unspecified;
}
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;
}
}
int
CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
int rc = 0;
int wct;
struct smb_hdr *smb_buf;
char *bcc_ptr;
char *str_area;
SESSION_SETUP_ANDX *pSMB;
__u32 capabilities;
__u16 count;
int resp_buf_type;
struct kvec iov[3];
enum securityEnum type;
__u16 action, bytes_remaining;
struct key *spnego_key = NULL;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
u16 blob_len;
char *ntlmsspblob = NULL;
if (ses == NULL) {
WARN(1, "%s: ses == NULL!", __func__);
return -EINVAL;
}
type = 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;
}
if (type == RawNTLMSSP) {
/* if memory allocation is successful, caller of this function
* frees it.
*/
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
if (!ses->ntlmssp)
return -ENOMEM;
ses->ntlmssp->sesskey_per_smbsess = false;
}
ssetup_ntlmssp_authenticate:
if (phase == NtLmChallenge)
phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
if (type == LANMAN) {
#ifndef CONFIG_CIFS_WEAK_PW_HASH
/* 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 */
return -EOPNOTSUPP;
#endif
wct = 10; /* lanman 2 style sessionsetup */
} else if ((type == NTLM) || (type == NTLMv2)) {
/* For NTLMv2 failures eventually may need to retry NTLM */
wct = 13; /* old style NTLM sessionsetup */
} else /* same size: negotiate or auth, NTLMSSP or extended security */
wct = 12;
rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
(void **)&smb_buf);
if (rc)
return rc;
pSMB = (SESSION_SETUP_ANDX *)smb_buf;
capabilities = cifs_ssetup_hdr(ses, pSMB);
/* 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 */
iov[0].iov_base = (char *)pSMB;
iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
/* setting this here allows the code at the end of the function
to free the request buffer if there's an error */
resp_buf_type = CIFS_SMALL_BUFFER;
/* 2000 big enough to fit max user, domain, NOS name etc. */
str_area = kmalloc(2000, GFP_KERNEL);
if (str_area == NULL) {
rc = -ENOMEM;
goto ssetup_exit;
}
bcc_ptr = str_area;
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
if (type == LANMAN) {
#ifdef CONFIG_CIFS_WEAK_PW_HASH
char lnm_session_key[CIFS_AUTH_RESP_SIZE];
pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
/* 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);
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
bcc_ptr += CIFS_AUTH_RESP_SIZE;
/* 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, nls_cp);
#endif
} else if (type == NTLM) {
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
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, nls_cp);
if (rc) {
cifs_dbg(VFS, "Error %d during NTLM authentication\n",
rc);
goto ssetup_exit;
}
/* 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;
if (ses->capabilities & CAP_UNICODE) {
/* unicode strings must be word aligned */
if (iov[0].iov_len % 2) {
*bcc_ptr = 0;
bcc_ptr++;
}
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
} else
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
} else if (type == NTLMv2) {
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
/* LM2 password would be here if we supported it */
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
/* calculate nlmv2 response and session key */
rc = setup_ntlmv2_rsp(ses, nls_cp);
if (rc) {
cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n",
rc);
goto ssetup_exit;
}
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);
if (ses->capabilities & CAP_UNICODE) {
if (iov[0].iov_len % 2) {
*bcc_ptr = 0;
bcc_ptr++;
}
unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
} else
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
} else if (type == Kerberos) {
#ifdef CONFIG_CIFS_UPCALL
struct cifs_spnego_msg *msg;
spnego_key = cifs_get_spnego_key(ses);
if (IS_ERR(spnego_key)) {
rc = PTR_ERR(spnego_key);
spnego_key = NULL;
goto ssetup_exit;
}
msg = spnego_key->payload.data;
/* 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 ssetup_exit;
}
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 ssetup_exit;
}
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);
iov[1].iov_base = msg->data + msg->sesskey_len;
iov[1].iov_len = msg->secblob_len;
pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len);
if (ses->capabilities & CAP_UNICODE) {
/* unicode strings must be word aligned */
if ((iov[0].iov_len + iov[1].iov_len) % 2) {
*bcc_ptr = 0;
bcc_ptr++;
}
unicode_oslm_strings(&bcc_ptr, nls_cp);
unicode_domain_string(&bcc_ptr, ses, nls_cp);
} else
/* BB: is this right? */
ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
#else /* ! CONFIG_CIFS_UPCALL */
cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
rc = -ENOSYS;
goto ssetup_exit;
#endif /* CONFIG_CIFS_UPCALL */
} else if (type == RawNTLMSSP) {
if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
rc = -ENOSYS;
goto ssetup_exit;
}
cifs_dbg(FYI, "ntlmssp session setup phase %d\n", phase);
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
capabilities |= CAP_EXTENDED_SECURITY;
pSMB->req.Capabilities |= cpu_to_le32(capabilities);
switch(phase) {
case NtLmNegotiate:
build_ntlmssp_negotiate_blob(
pSMB->req.SecurityBlob, ses);
iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
iov[1].iov_base = pSMB->req.SecurityBlob;
pSMB->req.SecurityBlobLength =
cpu_to_le16(sizeof(NEGOTIATE_MESSAGE));
break;
case NtLmAuthenticate:
/*
* 5 is an empirical value, large enough to hold
* authenticate message plus max 10 of av paris,
* domain, user, workstation names, flags, etc.
*/
ntlmsspblob = kzalloc(
5*sizeof(struct _AUTHENTICATE_MESSAGE),
GFP_KERNEL);
if (!ntlmsspblob) {
rc = -ENOMEM;
goto ssetup_exit;
}
rc = build_ntlmssp_auth_blob(ntlmsspblob,
&blob_len, ses, nls_cp);
if (rc)
goto ssetup_exit;
iov[1].iov_len = blob_len;
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;
break;
default:
cifs_dbg(VFS, "invalid phase %d\n", phase);
rc = -ENOSYS;
goto ssetup_exit;
}
/* unicode strings must be word aligned */
if ((iov[0].iov_len + iov[1].iov_len) % 2) {
*bcc_ptr = 0;
bcc_ptr++;
}
unicode_oslm_strings(&bcc_ptr, nls_cp);
} else {
cifs_dbg(VFS, "secType %d not supported!\n", type);
rc = -ENOSYS;
goto ssetup_exit;
}
iov[2].iov_base = str_area;
iov[2].iov_len = (long) bcc_ptr - (long) str_area;
count = iov[1].iov_len + 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(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
CIFS_LOG_ERROR);
/* SMB request buf freed in SendReceive2 */
pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
smb_buf = (struct smb_hdr *)iov[0].iov_base;
if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) &&
(smb_buf->Status.CifsError ==
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {
if (phase != NtLmNegotiate) {
cifs_dbg(VFS, "Unexpected more processing error\n");
goto ssetup_exit;
}
/* NTLMSSP Negotiate sent now processing challenge (response) */
phase = NtLmChallenge; /* process ntlmssp challenge */
rc = 0; /* MORE_PROC rc is not an error here, but expected */
}
if (rc)
goto ssetup_exit;
if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {
rc = -EIO;
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto ssetup_exit;
}
action = le16_to_cpu(pSMB->resp.Action);
if (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);
/* response can have either 3 or 4 word count - Samba sends 3 */
/* and lanman response is 3 */
bytes_remaining = get_bcc(smb_buf);
bcc_ptr = pByteArea(smb_buf);
if (smb_buf->WordCount == 4) {
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 ssetup_exit;
}
if (phase == NtLmChallenge) {
rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses);
/* now goto beginning for ntlmssp authenticate phase */
if (rc)
goto ssetup_exit;
}
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, nls_cp);
} else {
decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);
}
ssetup_exit:
if (spnego_key) {
key_invalidate(spnego_key);
key_put(spnego_key);
}
kfree(str_area);
kfree(ntlmsspblob);
ntlmsspblob = NULL;
if (resp_buf_type == CIFS_SMALL_BUFFER) {
cifs_dbg(FYI, "ssetup freeing small buf %p\n", iov[0].iov_base);
cifs_small_buf_release(iov[0].iov_base);
} else if (resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
if ((phase == NtLmChallenge) && (rc == 0))
goto ssetup_ntlmssp_authenticate;
if (!rc) {
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) {
rc = -ENOMEM;
mutex_unlock(&ses->server->srv_mutex);
goto keycp_exit;
}
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);
}
keycp_exit:
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
kfree(ses->ntlmssp);
return rc;
}
/*
* 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"
/*
* 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, void *buf,
struct mid_q_entry *mid)
{
int rc = 0;
struct smb_hdr *in_buf = (struct smb_hdr *)buf;
/* -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",
in_buf->Mid, 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)
{
READ_RSP *rsp = (READ_RSP *)buf;
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 (mid->mid == buf->Mid &&
mid->mid_state == MID_REQUEST_SUBMITTED &&
le16_to_cpu(mid->command) == buf->Command) {
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++;
/*
* 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=%hu\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 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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == -EOPNOTSUPP || rc == -EINVAL)
rc = SMBQueryInformation(xid, tcon, full_path, file_info,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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)
{
int rc;
/* 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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
/*
* 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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
*adjustTZ = true;
}
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
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)
{
#ifdef CONFIG_CIFS_STATS
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);
#endif
}
static void
cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
{
#ifdef CONFIG_CIFS_STATS
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));
#endif
}
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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,
oparms->cifs_sb->mnt_cifs_flags
& CIFS_MOUNT_MAP_SPECIAL_CHR);
return CIFSSMBOpen(xid, oparms->tcon, oparms->path,
oparms->disposition, oparms->desired_access,
oparms->share_access, oparms->create_options,
&oparms->fid->netfid, oplock, buf,
oparms->cifs_sb->local_nls,
oparms->cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
static void
cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
{
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
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 cifsFileInfo *cfile,
struct cifs_io_parms *parms, unsigned int *bytes_read,
char **buf, int *buf_type)
{
parms->netfid = cfile->fid.netfid;
return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type);
}
static int
cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
struct cifs_io_parms *parms, unsigned int *written,
struct kvec *iov, unsigned long nr_segs)
{
parms->netfid = cfile->fid.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;
__u16 netfid;
__u32 netpid;
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) {
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_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc == 0) {
cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
goto out;
} else if (rc != -EOPNOTSUPP && rc != -EINVAL) {
goto out;
}
cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n");
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, FILE_SHARE_ALL,
CREATE_NOT_DIR, &netfid, &oplock, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != 0) {
if (rc == -EIO)
rc = -EINVAL;
goto out;
}
netpid = current->tgid;
set_via_filehandle:
rc = CIFSSMBSetFileInfo(xid, tcon, buf, netfid, netpid);
if (!rc)
cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
if (open_file == NULL)
CIFSSMBClose(xid, tcon, netfid);
else
cifsFileInfo_put(open_file);
out:
if (tlink != NULL)
cifs_put_tlink(tlink);
return rc;
}
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)
{
return CIFSFindFirst(xid, tcon, path, cifs_sb,
&fid->netfid, search_flags, srch_inf, true);
}
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;
__u16 netfid;
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);
if (rc == -EREMOTE)
rc = cifs_unix_dfs_readlink(xid, tcon, full_path,
target_path,
cifs_sb->local_nls);
goto out;
}
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
FILE_READ_ATTRIBUTES, FILE_SHARE_ALL,
OPEN_REPARSE_POINT, &netfid, &oplock, NULL,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc)
goto out;
rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path,
cifs_sb->local_nls);
if (rc)
goto out_close;
convert_delimiter(*target_path, '/');
out_close:
CIFSSMBClose(xid, tcon, 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;
}
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,
.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,
.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,
.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,
.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 = open_query_close_cifs_symlink,
.is_read_op = cifs_is_read_op,
};
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_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,
};
/*
* 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[17];
struct cifs_fid *fid = oparms->fid;
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) + MAX_NAME * 2,
GFP_KERNEL);
if (smb2_data == NULL) {
rc = -ENOMEM;
goto out;
}
oparms->desired_access |= FILE_READ_ATTRIBUTES;
*smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL);
if (rc)
goto out;
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(cfile->dentry->d_inode);
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 for zero before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf)
return -EINVAL;
max_num = max_buf / sizeof(struct smb2_lock_element);
buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
if (!buf)
return -ENOMEM;
cur = buf;
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(cfile->dentry->d_inode);
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) {
free_xid(xid);
return -EINVAL;
}
max_num = max_buf / sizeof(struct smb2_lock_element);
buf = kzalloc(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;
}
/*
* 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
/* 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)
#endif /* _SMB2_GLOB_H */
/*
* 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;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_open_parms oparms;
struct cifs_fid fid;
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.share_access = FILE_SHARE_ALL;
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);
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_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);
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;
}
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)
{
int rc;
struct smb2_file_all_info *smb2_data;
*adjust_tz = false;
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 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,
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 | CREATE_DELETE_ON_CLOSE,
NULL, SMB2_OP_DELETE);
}
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;
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;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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"
static int
check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
{
/*
* Make sure that this really is an SMB, that it is a response,
* and that the message ids match.
*/
if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
(mid == hdr->MessageId)) {
if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
return 0;
else {
/* only one valid case where server sends us request */
if (hdr->Command == SMB2_OPLOCK_BREAK)
return 0;
else
cifs_dbg(VFS, "Received Request not response\n");
}
} else { /* bad signature or mid */
if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER)
cifs_dbg(VFS, "Bad protocol string signature header %x\n",
*(unsigned int *) hdr->ProtocolId);
if (mid != hdr->MessageId)
cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
mid, hdr->MessageId);
}
cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", hdr->MessageId);
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 */ __constant_cpu_to_le16(65),
/* SMB2_SESSION_SETUP */ __constant_cpu_to_le16(9),
/* SMB2_LOGOFF */ __constant_cpu_to_le16(4),
/* SMB2_TREE_CONNECT */ __constant_cpu_to_le16(16),
/* SMB2_TREE_DISCONNECT */ __constant_cpu_to_le16(4),
/* SMB2_CREATE */ __constant_cpu_to_le16(89),
/* SMB2_CLOSE */ __constant_cpu_to_le16(60),
/* SMB2_FLUSH */ __constant_cpu_to_le16(4),
/* SMB2_READ */ __constant_cpu_to_le16(17),
/* SMB2_WRITE */ __constant_cpu_to_le16(17),
/* SMB2_LOCK */ __constant_cpu_to_le16(4),
/* SMB2_IOCTL */ __constant_cpu_to_le16(49),
/* BB CHECK this ... not listed in documentation */
/* SMB2_CANCEL */ __constant_cpu_to_le16(0),
/* SMB2_ECHO */ __constant_cpu_to_le16(4),
/* SMB2_QUERY_DIRECTORY */ __constant_cpu_to_le16(9),
/* SMB2_CHANGE_NOTIFY */ __constant_cpu_to_le16(9),
/* SMB2_QUERY_INFO */ __constant_cpu_to_le16(9),
/* SMB2_SET_INFO */ __constant_cpu_to_le16(2),
/* BB FIXME can also be 44 for lease break */
/* SMB2_OPLOCK_BREAK */ __constant_cpu_to_le16(24)
};
int
smb2_check_message(char *buf, unsigned int length)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
__u64 mid = hdr->MessageId;
__u32 len = get_rfc1002_length(buf);
__u32 clc_len; /* calculated length */
int command;
/* BB disable following printk later */
cifs_dbg(FYI, "%s length: 0x%x, smb_buf_length: 0x%x\n",
__func__, length, len);
/*
* Add function to do table lookup of StructureSize by command
* ie Validate the wct via smb2_struct_sizes table above
*/
if (length < sizeof(struct smb2_pdu)) {
if ((length >= sizeof(struct smb2_hdr)) && (hdr->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 - 4) {
cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n",
mid);
return 1;
}
if (check_smb2_hdr(hdr, mid))
return 1;
if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
cifs_dbg(VFS, "Illegal structure size %u\n",
le16_to_cpu(hdr->StructureSize));
return 1;
}
command = le16_to_cpu(hdr->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 && (hdr->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 && (hdr->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;
}
}
if (4 + len != length) {
cifs_dbg(VFS, "Total length %u RFC1002 length %u mismatch mid %llu\n",
length, 4 + len, mid);
return 1;
}
clc_len = smb2_calc_size(hdr);
if (4 + len != clc_len) {
cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
clc_len, 4 + len, mid);
/* create failed on symlink */
if (command == SMB2_CREATE_HE &&
hdr->Status == STATUS_STOPPED_ON_SYMLINK)
return 0;
/* Windows 7 server returns 24 bytes more */
if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
return 0;
/* server can return one byte more */
if (clc_len == 4 + len + 1)
return 0;
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_hdr *hdr)
{
*off = 0;
*len = 0;
/* error responses do not have data area */
if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
(((struct smb2_err_rsp *)hdr)->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 (hdr->Command) {
case SMB2_NEGOTIATE:
*off = le16_to_cpu(
((struct smb2_negotiate_rsp *)hdr)->SecurityBufferOffset);
*len = le16_to_cpu(
((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength);
break;
case SMB2_SESSION_SETUP:
*off = le16_to_cpu(
((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferOffset);
*len = le16_to_cpu(
((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength);
break;
case SMB2_CREATE:
*off = le32_to_cpu(
((struct smb2_create_rsp *)hdr)->CreateContextsOffset);
*len = le32_to_cpu(
((struct smb2_create_rsp *)hdr)->CreateContextsLength);
break;
case SMB2_QUERY_INFO:
*off = le16_to_cpu(
((struct smb2_query_info_rsp *)hdr)->OutputBufferOffset);
*len = le32_to_cpu(
((struct smb2_query_info_rsp *)hdr)->OutputBufferLength);
break;
case SMB2_READ:
*off = ((struct smb2_read_rsp *)hdr)->DataOffset;
*len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength);
break;
case SMB2_QUERY_DIRECTORY:
*off = le16_to_cpu(
((struct smb2_query_directory_rsp *)hdr)->OutputBufferOffset);
*len = le32_to_cpu(
((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength);
break;
case SMB2_IOCTL:
*off = le32_to_cpu(
((struct smb2_ioctl_rsp *)hdr)->OutputOffset);
*len = le32_to_cpu(((struct smb2_ioctl_rsp *)hdr)->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 hdr->ProtocolId + *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 smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)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 = 4 + le16_to_cpu(pdu->hdr.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(hdr->Command)] == false)
goto calc_size_exit;
smb2_get_data_area_len(&offset, &data_length, hdr);
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 (and 4 to account for
* the size of the RFC1001 hdr.
*/
if (offset + 4 + 1 < len) {
cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n",
offset + 4 + 1, len);
data_length = 0;
} else {
len = 4 + 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;
/* Windows doesn't allow paths beginning with \ */
if (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,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
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;
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 bool
smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
struct smb2_lease_break_work *lw)
{
bool found;
__u8 lease_state;
struct list_head *tmp;
struct cifsFileInfo *cfile;
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_pending_open *open;
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(cfile->dentry->d_inode);
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%d\n",
le32_to_cpu(rsp->NewLeaseState));
server->ops->set_oplock_level(cinode, lease_state, 0, NULL);
if (ack_req)
cfile->oplock_break_cancelled = false;
else
cfile->oplock_break_cancelled = true;
queue_work(cifsiod_wq, &cfile->oplock_break);
kfree(lw);
return true;
}
found = false;
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 = true;
memcpy(lw->lease_key, open->lease_key,
SMB2_LEASE_KEY_SIZE);
lw->tlink = cifs_get_tlink(open->tlink);
queue_work(cifsiod_wq, &lw->lease_break);
}
cifs_dbg(FYI, "found in the pending open list\n");
cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
le32_to_cpu(rsp->NewLeaseState));
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 smb2_lease_break_work *lw;
lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
if (!lw)
return false;
INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
lw->lease_state = rsp->NewLeaseState;
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);
spin_lock(&cifs_file_list_lock);
list_for_each(tmp2, &ses->tcon_list) {
tcon = list_entry(tmp2, struct cifs_tcon,
tcon_list);
cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks);
if (smb2_tcon_has_lease(tcon, rsp, lw)) {
spin_unlock(&cifs_file_list_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
}
spin_unlock(&cifs_file_list_lock);
}
}
spin_unlock(&cifs_tcp_ses_lock);
kfree(lw);
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->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%d\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);
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
spin_lock(&cifs_file_list_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");
cinode = CIFS_I(cfile->dentry->d_inode);
if (!CIFS_CACHE_WRITE(cinode) &&
rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
cfile->oplock_break_cancelled = true;
else
cfile->oplock_break_cancelled = false;
server->ops->set_oplock_level(cinode,
rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0,
0, NULL);
queue_work(cifsiod_wq, &cfile->oplock_break);
spin_unlock(&cifs_file_list_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
spin_unlock(&cifs_file_list_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 false;
}
/*
* 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 "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"
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 -1;
case 1:
server->echoes = false;
server->oplocks = false;
cifs_dbg(VFS, "disabling echoes and oplocks\n");
break;
case 2:
server->echoes = true;
server->oplocks = false;
server->echo_credits = 1;
cifs_dbg(FYI, "disabling oplocks\n");
break;
default:
server->echoes = true;
server->oplocks = true;
server->echo_credits = 1;
server->oplock_credits = 1;
}
server->credits -= server->echo_credits + server->oplock_credits;
return 0;
}
static void
smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
const int optype)
{
int *val, rc = 0;
spin_lock(&server->req_lock);
val = server->ops->get_credits_field(server, optype);
*val += add;
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 (rc)
cifs_reconnect(server);
}
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)
{
return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
}
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 struct mid_q_entry *
smb2_find_mid(struct TCP_Server_Info *server, char *buf)
{
struct mid_q_entry *mid;
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
spin_lock(&GlobalMid_Lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if ((mid->mid == hdr->MessageId) &&
(mid->mid_state == MID_REQUEST_SUBMITTED) &&
(mid->command == hdr->Command)) {
spin_unlock(&GlobalMid_Lock);
return mid;
}
}
spin_unlock(&GlobalMid_Lock);
return NULL;
}
static void
smb2_dump_detail(void *buf)
{
#ifdef CONFIG_CIFS_DEBUG2
struct smb2_hdr *smb = (struct smb2_hdr *)buf;
cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
smb->Command, smb->Status, smb->Flags, smb->MessageId,
smb->ProcessId);
cifs_dbg(VFS, "smb buf %p len %u\n", smb, smb2_calc_size(smb));
#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);
/*
* limit write size to 2 ** 16, because we don't support multicredit
* requests now.
*/
wsize = min_t(unsigned int, wsize, 2 << 15);
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);
/*
* limit write size to 2 ** 16, because we don't support multicredit
* requests now.
*/
rsize = min_t(unsigned int, rsize, 2 << 15);
return rsize;
}
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;
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.share_access = FILE_SHARE_ALL;
oparms.disposition = FILE_OPEN;
oparms.create_options = 0;
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, 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) + MAX_NAME * 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;
}
static bool
smb2_can_echo(struct TCP_Server_Info *server)
{
return server->echoes;
}
static void
smb2_clear_stats(struct cifs_tcon *tcon)
{
#ifdef CONFIG_CIFS_STATS
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);
}
#endif
}
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");
seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags);
}
static void
smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
{
#ifdef CONFIG_CIFS_STATS
atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent;
atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed;
seq_printf(m, "\nNegotiates: %d sent %d failed",
atomic_read(&sent[SMB2_NEGOTIATE_HE]),
atomic_read(&failed[SMB2_NEGOTIATE_HE]));
seq_printf(m, "\nSessionSetups: %d sent %d failed",
atomic_read(&sent[SMB2_SESSION_SETUP_HE]),
atomic_read(&failed[SMB2_SESSION_SETUP_HE]));
seq_printf(m, "\nLogoffs: %d sent %d failed",
atomic_read(&sent[SMB2_LOGOFF_HE]),
atomic_read(&failed[SMB2_LOGOFF_HE]));
seq_printf(m, "\nTreeConnects: %d sent %d failed",
atomic_read(&sent[SMB2_TREE_CONNECT_HE]),
atomic_read(&failed[SMB2_TREE_CONNECT_HE]));
seq_printf(m, "\nTreeDisconnects: %d sent %d failed",
atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]),
atomic_read(&failed[SMB2_TREE_DISCONNECT_HE]));
seq_printf(m, "\nCreates: %d sent %d failed",
atomic_read(&sent[SMB2_CREATE_HE]),
atomic_read(&failed[SMB2_CREATE_HE]));
seq_printf(m, "\nCloses: %d sent %d failed",
atomic_read(&sent[SMB2_CLOSE_HE]),
atomic_read(&failed[SMB2_CLOSE_HE]));
seq_printf(m, "\nFlushes: %d sent %d failed",
atomic_read(&sent[SMB2_FLUSH_HE]),
atomic_read(&failed[SMB2_FLUSH_HE]));
seq_printf(m, "\nReads: %d sent %d failed",
atomic_read(&sent[SMB2_READ_HE]),
atomic_read(&failed[SMB2_READ_HE]));
seq_printf(m, "\nWrites: %d sent %d failed",
atomic_read(&sent[SMB2_WRITE_HE]),
atomic_read(&failed[SMB2_WRITE_HE]));
seq_printf(m, "\nLocks: %d sent %d failed",
atomic_read(&sent[SMB2_LOCK_HE]),
atomic_read(&failed[SMB2_LOCK_HE]));
seq_printf(m, "\nIOCTLs: %d sent %d failed",
atomic_read(&sent[SMB2_IOCTL_HE]),
atomic_read(&failed[SMB2_IOCTL_HE]));
seq_printf(m, "\nCancels: %d sent %d failed",
atomic_read(&sent[SMB2_CANCEL_HE]),
atomic_read(&failed[SMB2_CANCEL_HE]));
seq_printf(m, "\nEchos: %d sent %d failed",
atomic_read(&sent[SMB2_ECHO_HE]),
atomic_read(&failed[SMB2_ECHO_HE]));
seq_printf(m, "\nQueryDirectories: %d sent %d failed",
atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]),
atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE]));
seq_printf(m, "\nChangeNotifies: %d sent %d failed",
atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]),
atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE]));
seq_printf(m, "\nQueryInfos: %d sent %d failed",
atomic_read(&sent[SMB2_QUERY_INFO_HE]),
atomic_read(&failed[SMB2_QUERY_INFO_HE]));
seq_printf(m, "\nSetInfos: %d sent %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]));
#endif
}
static void
smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
{
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
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);
}
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_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)
{
struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
return le32_to_cpu(rsp->DataLength);
}
static int
smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
struct cifs_io_parms *parms, unsigned int *bytes_read,
char **buf, int *buf_type)
{
parms->persistent_fid = cfile->fid.persistent_fid;
parms->volatile_fid = cfile->fid.volatile_fid;
return SMB2_read(xid, parms, bytes_read, buf, buf_type);
}
static int
smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
struct cifs_io_parms *parms, unsigned int *written,
struct kvec *iov, unsigned long nr_segs)
{
parms->persistent_fid = cfile->fid.persistent_fid;
parms->volatile_fid = cfile->fid.volatile_fid;
return SMB2_write(xid, parms, written, iov, nr_segs);
}
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);
return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, &eof);
}
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.share_access = FILE_SHARE_ALL;
oparms.disposition = FILE_OPEN;
oparms.create_options = 0;
oparms.fid = fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
kfree(utf16_path);
if (rc) {
cifs_dbg(VFS, "open dir failed\n");
return rc;
}
srch_inf->entries_in_buffer = 0;
srch_inf->index_of_last_entry = 0;
rc = SMB2_query_directory(xid, tcon, fid->persistent_fid,
fid->volatile_fid, 0, srch_inf);
if (rc) {
cifs_dbg(VFS, "query directory failed\n");
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_hdr *hdr = (struct smb2_hdr *)buf;
if (hdr->Status != STATUS_PENDING)
return false;
if (!length) {
spin_lock(&server->req_lock);
server->credits += le16_to_cpu(hdr->CreditRequest);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
}
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 int
smb2_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;
oparms.tcon = tcon;
oparms.desired_access = FILE_READ_ATTRIBUTES;
oparms.share_access = FILE_SHARE_ALL;
oparms.disposition = FILE_OPEN;
oparms.create_options = 0;
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
if (rc)
return rc;
buf->f_type = SMB2_MAGIC_NUMBER;
rc = SMB2_QFS_info(xid, tcon, fid.persistent_fid, fid.volatile_fid,
buf);
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)
{
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
}
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 smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink;
unsigned int sub_len, sub_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.share_access = FILE_SHARE_ALL;
oparms.disposition = FILE_OPEN;
oparms.create_options = 0;
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_buf);
if (!rc || !err_buf) {
kfree(utf16_path);
return -ENOENT;
}
/* 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);
*target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls);
if (!(*target_path)) {
kfree(utf16_path);
return -ENOMEM;
}
convert_delimiter(*target_path, '/');
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
kfree(utf16_path);
return rc;
}
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};
oplock &= 0xFF;
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
return;
cinode->oplock = 0;
if (oplock & SMB2_LEASE_READ_CACHING_HE) {
cinode->oplock |= CIFS_CACHE_READ_FLG;
strcat(message, "R");
}
if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) {
cinode->oplock |= CIFS_CACHE_HANDLE_FLG;
strcat(message, "H");
}
if (oplock & SMB2_LEASE_WRITE_CACHING_HE) {
cinode->oplock |= CIFS_CACHE_WRITE_FLG;
strcat(message, "W");
}
if (!cinode->oplock)
strcat(message, "None");
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;
buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
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);
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;
buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
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);
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)
{
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)
{
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;
return le32_to_cpu(lc->lcontext.LeaseState);
}
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,
.get_next_mid = smb2_get_next_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,
.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,
.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,
.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,
.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,
.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,
};
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,
.get_next_mid = smb2_get_next_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,
.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,
.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,
.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,
.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,
.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,
};
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,
.get_next_mid = smb2_get_next_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,
.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,
.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,
.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,
.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,
.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_smb3signingkey,
.calc_signature = smb3_calc_signature,
.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,
};
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_hdr),
.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_hdr),
.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 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,
.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_hdr),
.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,
.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_hdr),
.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),
};
/*
* 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/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"
/*
* 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 */
};
static void
smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
const struct cifs_tcon *tcon)
{
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
char *temp = (char *)hdr;
/* lookup word count ie StructureSize from table */
__u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_cmd)];
/*
* smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
* largest operations (Create)
*/
memset(temp, 0, 256);
/* Note this is only network field converted to big endian */
hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr)
- 4 /* RFC 1001 length field itself not counted */);
hdr->ProtocolId[0] = 0xFE;
hdr->ProtocolId[1] = 'S';
hdr->ProtocolId[2] = 'M';
hdr->ProtocolId[3] = 'B';
hdr->StructureSize = cpu_to_le16(64);
hdr->Command = smb2_cmd;
hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
if (!tcon)
goto out;
/* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */
/* 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->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
hdr->CreditCharge = cpu_to_le16(1);
/* else CreditCharge MBZ */
hdr->TreeId = tcon->tid;
/* Uid is not converted */
if (tcon->ses)
hdr->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)
hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
if (tcon->ses && tcon->ses->server && tcon->ses->server->sign)
hdr->Flags |= SMB2_FLAGS_SIGNED;
out:
pdu->StructureSize2 = cpu_to_le16(parmsize);
return;
}
static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
{
int rc = 0;
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 rc;
if (smb2_command == SMB2_TREE_CONNECT)
return rc;
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;
}
wait_event_interruptible_timeout(server->response_q,
(server->tcpStatus != CifsNeedReconnect), 10 * HZ);
/* 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 rc;
nls_codepage = load_nls_default();
/*
* need to prevent multiple threads trying to simultaneously reconnect
* the same SMB session
*/
mutex_lock(&tcon->ses->session_mutex);
rc = cifs_negotiate_protocol(0, tcon->ses);
if (!rc && tcon->ses->need_reconnect)
rc = cifs_setup_session(0, tcon->ses, nls_codepage);
if (rc || !tcon->need_reconnect) {
mutex_unlock(&tcon->ses->session_mutex);
goto out;
}
cifs_mark_open_files_invalid(tcon);
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)
goto out;
atomic_inc(&tconInfoReconnectCount);
/*
* BB FIXME add code to check if wsize needs update due to negotiated
* smb buffer size shrinking.
*/
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:
return -EAGAIN;
}
unload_nls(nls_codepage);
return rc;
}
/*
* 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
small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon,
void **request_buf)
{
int rc = 0;
rc = smb2_reconnect(smb2_command, tcon);
if (rc)
return rc;
/* BB eventually switch this to SMB2 specific small buf size */
*request_buf = cifs_small_buf_get();
if (*request_buf == NULL) {
/* BB should we add a retry in here if not a writepage? */
return -ENOMEM;
}
smb2_hdr_assemble((struct smb2_hdr *) *request_buf, smb2_command, tcon);
if (tcon != NULL) {
#ifdef CONFIG_CIFS_STATS2
uint16_t com_code = le16_to_cpu(smb2_command);
cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]);
#endif
cifs_stats_inc(&tcon->num_smbs_sent);
}
return rc;
}
static 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);
}
/*
*
* 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 smb2_negotiate_req *req;
struct smb2_negotiate_rsp *rsp;
struct kvec iov[1];
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;
cifs_dbg(FYI, "Negotiate protocol\n");
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
return -EIO;
}
rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req);
if (rc)
return rc;
req->hdr.SessionId = 0;
req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
req->DialectCount = cpu_to_le16(1); /* One vers= at a time for now */
inc_rfc1001_len(req, 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);
memcpy(req->ClientGUID, cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags);
rsp = (struct smb2_negotiate_rsp *)iov[0].iov_base;
/*
* No tcon so can't do
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
*/
if (rc != 0)
goto neg_exit;
cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);
/* BB we may eventually want to match the negotiated vs. requested
dialect, even though we are only requesting one at a time */
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 {
cifs_dbg(VFS, "Illegal dialect returned by server %d\n",
le16_to_cpu(rsp->DialectRevision));
rc = -EIO;
goto neg_exit;
}
server->dialect = le16_to_cpu(rsp->DialectRevision);
/* SMB2 only has an extended negflavor */
server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
server->maxBuf = le32_to_cpu(rsp->MaxTransactSize);
server->max_read = le32_to_cpu(rsp->MaxReadSize);
server->max_write = le32_to_cpu(rsp->MaxWriteSize);
/* BB Do we need to validate the SecurityMode? */
server->sec_mode = le16_to_cpu(rsp->SecurityMode);
server->capabilities = le32_to_cpu(rsp->Capabilities);
/* Internal types */
server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;
security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
&rsp->hdr);
/*
* 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");
rc = cifs_enable_signing(server, ses->sign);
#ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */
if (rc)
goto neg_exit;
if (blob_length)
rc = decode_neg_token_init(security_blob, blob_length,
&server->sec_type);
if (rc == 1)
rc = 0;
else if (rc == 0) {
rc = -EIO;
goto neg_exit;
}
#endif
neg_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
}
int
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
struct smb2_sess_setup_req *req;
struct smb2_sess_setup_rsp *rsp = NULL;
struct kvec iov[2];
int rc = 0;
int resp_buftype;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
struct TCP_Server_Info *server = ses->server;
u16 blob_length = 0;
char *security_blob;
char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */
cifs_dbg(FYI, "Session Setup\n");
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
return -EIO;
}
/*
* If we are here due to reconnect, free per-smb session key
* in case signing was required.
*/
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
/*
* If memory allocation is successful, caller of this function
* frees it.
*/
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
if (!ses->ntlmssp)
return -ENOMEM;
ses->ntlmssp->sesskey_per_smbsess = true;
/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
ses->sectype = RawNTLMSSP;
ssetup_ntlmssp_authenticate:
if (phase == NtLmChallenge)
phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
if (rc)
return rc;
req->hdr.SessionId = 0; /* First session, not a reauthenticate */
req->VcNumber = 0; /* MBZ */
/* to enable echos and oplocks */
req->hdr.CreditRequest = cpu_to_le16(3);
/* 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;
req->Capabilities = 0;
req->Channel = 0; /* MBZ */
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field and 1 for pad */
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
if (phase == NtLmNegotiate) {
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
GFP_KERNEL);
if (ntlmssp_blob == NULL) {
rc = -ENOMEM;
goto ssetup_exit;
}
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
if (use_spnego) {
/* blob_length = build_spnego_ntlmssp_blob(
&security_blob,
sizeof(struct _NEGOTIATE_MESSAGE),
ntlmssp_blob); */
/* BB eventually need to add this */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP;
kfree(ntlmssp_blob);
goto ssetup_exit;
} else {
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
security_blob = ntlmssp_blob;
}
} else if (phase == NtLmAuthenticate) {
req->hdr.SessionId = ses->Suid;
ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500,
GFP_KERNEL);
if (ntlmssp_blob == NULL) {
rc = -ENOMEM;
goto ssetup_exit;
}
rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses,
nls_cp);
if (rc) {
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
rc);
goto ssetup_exit; /* BB double check error handling */
}
if (use_spnego) {
/* blob_length = build_spnego_ntlmssp_blob(
&security_blob,
blob_length,
ntlmssp_blob); */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP;
kfree(ntlmssp_blob);
goto ssetup_exit;
} else {
security_blob = ntlmssp_blob;
}
} else {
cifs_dbg(VFS, "illegal ntlmssp phase\n");
rc = -EIO;
goto ssetup_exit;
}
/* 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 */ - 4 /* rfc1001 len */);
req->SecurityBufferLength = cpu_to_le16(blob_length);
iov[1].iov_base = security_blob;
iov[1].iov_len = blob_length;
inc_rfc1001_len(req, blob_length - 1 /* pad */);
/* BB add code to build os and lm fields */
rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
CIFS_LOG_ERROR | CIFS_NEG_OP);
kfree(security_blob);
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
if (resp_buftype != CIFS_NO_BUFFER &&
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
if (phase != NtLmNegotiate) {
cifs_dbg(VFS, "Unexpected more processing error\n");
goto ssetup_exit;
}
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
le16_to_cpu(rsp->SecurityBufferOffset)) {
cifs_dbg(VFS, "Invalid security buffer offset %d\n",
le16_to_cpu(rsp->SecurityBufferOffset));
rc = -EIO;
goto ssetup_exit;
}
/* NTLMSSP Negotiate sent now processing challenge (response) */
phase = NtLmChallenge; /* process ntlmssp challenge */
rc = 0; /* MORE_PROCESSING is not an error here but expected */
ses->Suid = rsp->hdr.SessionId;
rc = decode_ntlmssp_challenge(rsp->Buffer,
le16_to_cpu(rsp->SecurityBufferLength), ses);
}
/*
* BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
* but at least the raw NTLMSSP case works.
*/
/*
* No tcon so can't do
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
*/
if (rc != 0)
goto ssetup_exit;
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
ssetup_exit:
free_rsp_buf(resp_buftype, rsp);
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
if ((phase == NtLmChallenge) && (rc == 0))
goto ssetup_ntlmssp_authenticate;
if (!rc) {
mutex_lock(&server->srv_mutex);
if (server->sign && server->ops->generate_signingkey) {
rc = server->ops->generate_signingkey(ses);
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
if (rc) {
cifs_dbg(FYI,
"SMB3 session key generation failed\n");
mutex_unlock(&server->srv_mutex);
goto keygen_exit;
}
}
if (!server->session_estab) {
server->sequence_number = 0x2;
server->session_estab = true;
}
mutex_unlock(&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);
}
keygen_exit:
if (!server->sign) {
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
}
kfree(ses->ntlmssp);
return rc;
}
int
SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
{
struct smb2_logoff_req *req; /* response is also trivial struct */
int rc = 0;
struct TCP_Server_Info *server;
cifs_dbg(FYI, "disconnect session %p\n", ses);
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req);
if (rc)
return rc;
/* since no tcon, smb2_init can not do this, so do here */
req->hdr.SessionId = ses->Suid;
if (server->sign)
req->hdr.Flags |= SMB2_FLAGS_SIGNED;
rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
/*
* No tcon so can't do
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
*/
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 */)
int
SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
struct cifs_tcon *tcon, const struct nls_table *cp)
{
struct smb2_tree_connect_req *req;
struct smb2_tree_connect_rsp *rsp = NULL;
struct kvec iov[2];
int rc = 0;
int resp_buftype;
int unc_path_len;
struct TCP_Server_Info *server;
__le16 *unc_path = NULL;
cifs_dbg(FYI, "TCON\n");
if ((ses->server) && tree)
server = ses->server;
else
return -EIO;
if (tcon && tcon->bad_network_name)
return -ENOENT;
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;
}
rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req);
if (rc) {
kfree(unc_path);
return rc;
}
if (tcon == NULL) {
/* since no tcon, smb2_init can not do this, so do here */
req->hdr.SessionId = ses->Suid;
/* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
req->hdr.Flags |= SMB2_FLAGS_SIGNED; */
}
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field and 1 for pad */
iov[0].iov_len = get_rfc1002_length(req) + 4 - 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 */ - 4 /* do not count rfc1001 len field */);
req->PathLength = cpu_to_le16(unc_path_len - 2);
iov[1].iov_base = unc_path;
iov[1].iov_len = unc_path_len;
inc_rfc1001_len(req, unc_path_len - 1 /* pad */);
rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0);
rsp = (struct smb2_tree_connect_rsp *)iov[0].iov_base;
if (rc != 0) {
if (tcon) {
cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE);
tcon->need_reconnect = true;
}
goto tcon_error_exit;
}
if (tcon == NULL) {
ses->ipc_tid = rsp->hdr.TreeId;
goto tcon_exit;
}
if (rsp->ShareType & SMB2_SHARE_TYPE_DISK)
cifs_dbg(FYI, "connection to disk share\n");
else if (rsp->ShareType & SMB2_SHARE_TYPE_PIPE) {
tcon->ipc = true;
cifs_dbg(FYI, "connection to pipe share\n");
} else if (rsp->ShareType & SMB2_SHARE_TYPE_PRINT) {
tcon->print = true;
cifs_dbg(FYI, "connection to printer\n");
} else {
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->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");
tcon_exit:
free_rsp_buf(resp_buftype, rsp);
kfree(unc_path);
return rc;
tcon_error_exit:
if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) {
cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
tcon->bad_network_name = true;
}
goto tcon_exit;
}
int
SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
{
struct smb2_tree_disconnect_req *req; /* response is trivial */
int rc = 0;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
cifs_dbg(FYI, "Tree Disconnect\n");
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
return 0;
rc = small_smb2_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req);
if (rc)
return rc;
rc = SendReceiveNoRsp(xid, ses, (char *)&req->hdr, 0);
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);
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;
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 *data_offset;
struct create_context *cc;
unsigned int next = 0;
char *name;
data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
cc = (struct create_context *)data_offset;
do {
cc = (struct create_context *)((char *)cc + next);
name = le16_to_cpu(cc->NameOffset) + (char *)cc;
if (le16_to_cpu(cc->NameLength) != 4 ||
strncmp(name, "RqLs", 4)) {
next = le32_to_cpu(cc->Next);
continue;
}
return server->ops->parse_lease_buf(cc, epoch);
} while (next != 0);
return 0;
}
static int
add_lease_context(struct TCP_Server_Info *server, struct kvec *iov,
unsigned int *num_iovec, __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(oplock+1, *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) - 4 +
iov[num - 1].iov_len);
le32_add_cpu(&req->CreateContextsLength,
server->vals->create_lease_size);
inc_rfc1001_len(&req->hdr, server->vals->create_lease_size);
*num_iovec = num + 1;
return 0;
}
static int
add_durable_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;
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) - 4 +
iov[1].iov_len);
le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable));
inc_rfc1001_len(&req->hdr, sizeof(struct create_durable));
*num_iovec = num + 1;
return 0;
}
int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf,
struct smb2_err_rsp **err_buf)
{
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_tcon *tcon = oparms->tcon;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[4];
int resp_buftype;
int uni_path_len;
__le16 *copy_path = NULL;
int copy_size;
int rc = 0;
unsigned int num_iovecs = 2;
__u32 file_attributes = 0;
cifs_dbg(FYI, "create/open\n");
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_CREATE, tcon, (void **) &req);
if (rc)
return rc;
if (oparms->create_options & CREATE_OPTION_READONLY)
file_attributes |= ATTR_READONLY;
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 = cpu_to_le32(oparms->share_access);
req->CreateDisposition = cpu_to_le32(oparms->disposition);
req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
/* do not count rfc1001 len field */
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
/* MUST set path len (NameLength) to 0 opening root of share */
req->NameLength = cpu_to_le16(uni_path_len - 2);
/* -1 since last byte is buf[0] which is sent below (path) */
iov[0].iov_len--;
if (uni_path_len % 8 != 0) {
copy_size = uni_path_len / 8 * 8;
if (copy_size < uni_path_len)
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;
/* -1 since last byte is buf[0] which was counted in smb2_buf_len */
inc_rfc1001_len(req, uni_path_len - 1);
if (!server->oplocks)
*oplock = SMB2_OPLOCK_LEVEL_NONE;
if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
*oplock == SMB2_OPLOCK_LEVEL_NONE)
req->RequestedOplockLevel = *oplock;
else {
rc = add_lease_context(server, iov, &num_iovecs, oplock);
if (rc) {
cifs_small_buf_release(req);
kfree(copy_path);
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[num_iovecs-1].iov_base;
ccontext->Next =
cpu_to_le32(server->vals->create_lease_size);
}
rc = add_durable_context(iov, &num_iovecs, oparms);
if (rc) {
cifs_small_buf_release(req);
kfree(copy_path);
kfree(iov[num_iovecs-1].iov_base);
return rc;
}
}
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
rsp = (struct smb2_create_rsp *)iov[0].iov_base;
if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
if (err_buf)
*err_buf = kmemdup(rsp, get_rfc1002_length(rsp) + 4,
GFP_KERNEL);
goto creat_exit;
}
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);
else
*oplock = rsp->OplockLevel;
creat_exit:
kfree(copy_path);
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 smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[2];
int resp_buftype;
int num_iovecs;
int rc = 0;
cifs_dbg(FYI, "SMB2 IOCTL\n");
/* zero out returned data len, in case of error */
if (plen)
*plen = 0;
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req);
if (rc)
return rc;
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) - 4);
iov[1].iov_base = in_data;
iov[1].iov_len = indatalen;
num_iovecs = 2;
} else
num_iovecs = 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
*/
req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
if (is_fsctl)
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
else
req->Flags = 0;
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
if (indatalen)
inc_rfc1001_len(req, indatalen);
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
if (rc != 0) {
if (tcon)
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 > 0xFF00) {
cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
*plen = 0;
rc = -EIO;
goto ioctl_exit;
}
if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) {
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, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
*plen);
ioctl_exit:
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)
{
struct smb2_close_req *req;
struct smb2_close_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[1];
int resp_buftype;
int rc = 0;
cifs_dbg(FYI, "Close\n");
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_CLOSE, tcon, (void **) &req);
if (rc)
return rc;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
rsp = (struct smb2_close_rsp *)iov[0].iov_base;
if (rc != 0) {
if (tcon)
cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE);
goto close_exit;
}
/* BB FIXME - decode close response, update inode for caching */
close_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
}
static int
validate_buf(unsigned int offset, unsigned int buffer_length,
struct smb2_hdr *hdr, unsigned int min_buf_size)
{
unsigned int smb_len = be32_to_cpu(hdr->smb2_buf_length);
char *end_of_smb = smb_len + 4 /* RFC1001 length field */ + (char *)hdr;
char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr;
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_buf(unsigned int offset, unsigned int buffer_length,
struct smb2_hdr *hdr, unsigned int minbufsize,
char *data)
{
char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr;
int rc;
if (!data)
return -EINVAL;
rc = validate_buf(offset, buffer_length, hdr, minbufsize);
if (rc)
return rc;
memcpy(data, begin_of_buf, buffer_length);
return 0;
}
static int
query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u8 info_class,
size_t output_len, size_t min_len, void *data)
{
struct smb2_query_info_req *req;
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov[2];
int rc = 0;
int resp_buftype;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
cifs_dbg(FYI, "Query Info\n");
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req);
if (rc)
return rc;
req->InfoType = SMB2_O_INFO_FILE;
req->FileInfoClass = info_class;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
/* 4 for rfc1002 length field and 1 for Buffer */
req->InputBufferOffset =
cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
req->OutputBufferLength = cpu_to_le32(output_len);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
rsp = (struct smb2_query_info_rsp *)iov[0].iov_base;
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
goto qinf_exit;
}
rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength),
&rsp->hdr, min_len, data);
qinf_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
}
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,
sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
sizeof(struct smb2_file_all_info), data);
}
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,
sizeof(struct smb2_file_internal_info),
sizeof(struct smb2_file_internal_info), uniqueid);
}
/*
* 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 *smb2 = (struct smb2_echo_rsp *)mid->resp_buf;
unsigned int credits_received = 1;
if (mid->mid_state == MID_RESPONSE_RECEIVED)
credits_received = le16_to_cpu(smb2->hdr.CreditRequest);
DeleteMidQEntry(mid);
add_credits(server, credits_received, CIFS_ECHO_OP);
}
int
SMB2_echo(struct TCP_Server_Info *server)
{
struct smb2_echo_req *req;
int rc = 0;
struct kvec iov;
struct smb_rqst rqst = { .rq_iov = &iov,
.rq_nvec = 1 };
cifs_dbg(FYI, "In echo request\n");
rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req);
if (rc)
return rc;
req->hdr.CreditRequest = cpu_to_le16(1);
iov.iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov.iov_len = get_rfc1002_length(req) + 4;
rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, 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 smb2_flush_req *req;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[1];
int resp_buftype;
int rc = 0;
cifs_dbg(FYI, "Flush\n");
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_FLUSH, tcon, (void **) &req);
if (rc)
return rc;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
if ((rc != 0) && tcon)
cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
free_rsp_buf(resp_buftype, iov[0].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(struct kvec *iov, struct cifs_io_parms *io_parms,
unsigned int remaining_bytes, int request_type)
{
int rc = -EACCES;
struct smb2_read_req *req = NULL;
rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req);
if (rc)
return rc;
if (io_parms->tcon->ses->server == NULL)
return -ECONNABORTED;
req->hdr.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);
if (request_type & CHAINED_REQUEST) {
if (!(request_type & END_OF_CHAIN)) {
/* 4 for rfc1002 length field */
req->hdr.NextCommand =
cpu_to_le32(get_rfc1002_length(req) + 4);
} else /* END_OF_CHAIN */
req->hdr.NextCommand = 0;
if (request_type & RELATED_REQUEST) {
req->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
/*
* Related requests use info from previous read request
* in chain.
*/
req->hdr.SessionId = 0xFFFFFFFF;
req->hdr.TreeId = 0xFFFFFFFF;
req->PersistentFileId = 0xFFFFFFFF;
req->VolatileFileId = 0xFFFFFFFF;
}
}
if (remaining_bytes > io_parms->length)
req->RemainingBytes = cpu_to_le32(remaining_bytes);
else
req->RemainingBytes = 0;
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
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_hdr *buf = (struct smb2_hdr *)rdata->iov.iov_base;
unsigned int credits_received = 1;
struct smb_rqst rqst = { .rq_iov = &rdata->iov,
.rq_nvec = 1,
.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:
credits_received = le16_to_cpu(buf->CreditRequest);
/* result already set, check signature */
if (server->sign) {
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->bytes);
cifs_stats_bytes_read(tcon, rdata->bytes);
break;
case MID_REQUEST_SUBMITTED:
case MID_RETRY_NEEDED:
rdata->result = -EAGAIN;
break;
default:
if (rdata->result != -ENODATA)
rdata->result = -EIO;
}
if (rdata->result)
cifs_stats_fail_inc(tcon, SMB2_READ_HE);
queue_work(cifsiod_wq, &rdata->work);
DeleteMidQEntry(mid);
add_credits(server, credits_received, 0);
}
/* smb2_async_readv - send an async write, and set up mid to handle result */
int
smb2_async_readv(struct cifs_readdata *rdata)
{
int rc;
struct smb2_hdr *buf;
struct cifs_io_parms io_parms;
struct smb_rqst rqst = { .rq_iov = &rdata->iov,
.rq_nvec = 1 };
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;
rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0);
if (rc)
return rc;
buf = (struct smb2_hdr *)rdata->iov.iov_base;
/* 4 for rfc1002 length field */
rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4;
kref_get(&rdata->refcount);
rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
cifs_readv_receive, smb2_readv_callback,
rdata, 0);
if (rc) {
kref_put(&rdata->refcount, cifs_readdata_release);
cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
}
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)
{
int resp_buftype, rc = -EACCES;
struct smb2_read_rsp *rsp = NULL;
struct kvec iov[1];
*nbytes = 0;
rc = smb2_new_read_req(iov, io_parms, 0, 0);
if (rc)
return rc;
rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1,
&resp_buftype, CIFS_LOG_ERROR);
rsp = (struct smb2_read_rsp *)iov[0].iov_base;
if (rsp->hdr.Status == STATUS_END_OF_FILE) {
free_rsp_buf(resp_buftype, iov[0].iov_base);
return 0;
}
if (rc) {
cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE);
cifs_dbg(VFS, "Send error in read = %d\n", rc);
} else {
*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->hdr.ProtocolId + rsp->DataOffset,
*nbytes);
free_rsp_buf(resp_buftype, iov[0].iov_base);
} else if (resp_buftype != CIFS_NO_BUFFER) {
*buf = iov[0].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 = 1;
switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED:
credits_received = le16_to_cpu(rsp->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;
default:
wdata->result = -EIO;
break;
}
if (wdata->result)
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
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;
struct smb2_write_req *req = NULL;
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
struct kvec iov;
struct smb_rqst rqst;
rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req);
if (rc)
goto async_writev_out;
req->hdr.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);
/* 4 for rfc1002 length field */
req->DataOffset = cpu_to_le16(
offsetof(struct smb2_write_req, Buffer) - 4);
req->RemainingBytes = 0;
/* 4 for rfc1002 length field and 1 for Buffer */
iov.iov_len = get_rfc1002_length(req) + 4 - 1;
iov.iov_base = req;
rqst.rq_iov = &iov;
rqst.rq_nvec = 1;
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);
req->Length = cpu_to_le32(wdata->bytes);
inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */);
kref_get(&wdata->refcount);
rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
smb2_writev_callback, wdata, 0);
if (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)
{
int rc = 0;
struct smb2_write_req *req = NULL;
struct smb2_write_rsp *rsp = NULL;
int resp_buftype;
*nbytes = 0;
if (n_vec < 1)
return rc;
rc = small_smb2_init(SMB2_WRITE, io_parms->tcon, (void **) &req);
if (rc)
return rc;
if (io_parms->tcon->ses->server == NULL)
return -ECONNABORTED;
req->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);
/* 4 for rfc1002 length field */
req->DataOffset = cpu_to_le16(
offsetof(struct smb2_write_req, Buffer) - 4);
req->RemainingBytes = 0;
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field and 1 for Buffer */
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
/* length of entire message including data to be written */
inc_rfc1001_len(req, io_parms->length - 1 /* Buffer */);
rc = SendReceive2(xid, io_parms->tcon->ses, iov, n_vec + 1,
&resp_buftype, 0);
rsp = (struct smb2_write_rsp *)iov[0].iov_base;
if (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);
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;
FILE_DIRECTORY_INFO *entryptr;
if (bufstart == NULL)
return 0;
entryptr = (FILE_DIRECTORY_INFO *)bufstart;
while (1) {
entryptr = (FILE_DIRECTORY_INFO *)
((char *)entryptr + next_offset);
if ((char *)entryptr + size > end_of_buf) {
cifs_dbg(VFS, "malformed search entry would overflow\n");
break;
}
len = le32_to_cpu(entryptr->FileNameLength);
if ((char *)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 = (char *)entryptr;
entrycount++;
next_offset = le32_to_cpu(entryptr->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 smb2_query_directory_req *req;
struct smb2_query_directory_rsp *rsp = NULL;
struct kvec iov[2];
int rc = 0;
int len;
int resp_buftype;
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;
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = small_smb2_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req);
if (rc)
return rc;
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 - 4);
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;
/* 4 for RFC1001 length and 1 for Buffer */
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
iov[1].iov_base = (char *)(req->Buffer);
iov[1].iov_len = len;
inc_rfc1001_len(req, len - 1 /* Buffer */);
rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0);
rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base;
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
goto qdir_exit;
}
rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
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 = 4 /* rfclen */ +
(char *)&rsp->hdr + le16_to_cpu(rsp->OutputBufferOffset);
/* 4 for rfc1002 length field */
end_of_smb = get_rfc1002_length(rsp) + 4 + (char *)&rsp->hdr;
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");
if (rsp->hdr.Status == STATUS_NO_MORE_FILES)
srch_inf->endOfSearch = 1;
else
srch_inf->endOfSearch = 0;
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, int info_class,
unsigned int num, void **data, unsigned int *size)
{
struct smb2_set_info_req *req;
struct smb2_set_info_rsp *rsp = NULL;
struct kvec *iov;
int rc = 0;
int resp_buftype;
unsigned int i;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
if (!num)
return -EINVAL;
iov = kmalloc(sizeof(struct kvec) * num, GFP_KERNEL);
if (!iov)
return -ENOMEM;
rc = small_smb2_init(SMB2_SET_INFO, tcon, (void **) &req);
if (rc) {
kfree(iov);
return rc;
}
req->hdr.ProcessId = cpu_to_le32(pid);
req->InfoType = SMB2_O_INFO_FILE;
req->FileInfoClass = info_class;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
/* 4 for RFC1001 length and 1 for Buffer */
req->BufferOffset =
cpu_to_le16(sizeof(struct smb2_set_info_req) - 1 - 4);
req->BufferLength = cpu_to_le32(*size);
inc_rfc1001_len(req, *size - 1 /* Buffer */);
memcpy(req->Buffer, *data, *size);
iov[0].iov_base = (char *)req;
/* 4 for RFC1001 length */
iov[0].iov_len = get_rfc1002_length(req) + 4;
for (i = 1; i < num; i++) {
inc_rfc1001_len(req, size[i]);
le32_add_cpu(&req->BufferLength, size[i]);
iov[i].iov_base = (char *)data[i];
iov[i].iov_len = size[i];
}
rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
goto out;
}
out:
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(sizeof(void *) * 2, 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, 2, data,
size);
kfree(data);
return rc;
}
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(sizeof(void *) * 2, 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, 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)
{
struct smb2_file_eof_info info;
void *data;
unsigned int size;
info.EndOfFile = *eof;
data = &info;
size = sizeof(struct smb2_file_eof_info);
return send_set_info(xid, tcon, persistent_fid, volatile_fid, pid,
FILE_END_OF_FILE_INFORMATION, 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, 1,
(void **)&buf, &size);
}
int
SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
__u8 oplock_level)
{
int rc;
struct smb2_oplock_break *req = NULL;
cifs_dbg(FYI, "SMB2_oplock_break\n");
rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
if (rc)
return rc;
req->VolatileFid = volatile_fid;
req->PersistentFid = persistent_fid;
req->OplockLevel = oplock_level;
req->hdr.CreditRequest = cpu_to_le16(1);
rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
/* SMB2 buffer freed by function above */
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc);
}
return rc;
}
static void
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 = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits);
kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
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)
{
int rc;
struct smb2_query_info_req *req;
cifs_dbg(FYI, "Query FSInfo level %d\n", level);
if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
return -EIO;
rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req);
if (rc)
return rc;
req->InfoType = SMB2_O_INFO_FILESYSTEM;
req->FileInfoClass = level;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
/* 4 for rfc1002 length field and 1 for pad */
req->InputBufferOffset =
cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
req->OutputBufferLength = cpu_to_le32(
outbuf_len + sizeof(struct smb2_query_info_rsp) - 1 - 4);
iov->iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov->iov_len = get_rfc1002_length(req) + 4;
return 0;
}
int
SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
{
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov;
int rc = 0;
int resp_buftype;
struct cifs_ses *ses = tcon->ses;
struct smb2_fs_full_size_info *info = NULL;
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;
rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
goto qinf_exit;
}
rsp = (struct smb2_query_info_rsp *)iov.iov_base;
info = (struct smb2_fs_full_size_info *)(4 /* RFC1001 len */ +
le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr);
rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
sizeof(struct smb2_fs_full_size_info));
if (!rc)
copy_fs_info_to_kstatfs(info, fsdata);
qinf_exit:
free_rsp_buf(resp_buftype, 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)
{
int rc = 0;
struct smb2_lock_req *req = NULL;
struct kvec iov[2];
int resp_buf_type;
unsigned int count;
cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
rc = small_smb2_init(SMB2_LOCK, tcon, (void **) &req);
if (rc)
return rc;
req->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);
inc_rfc1001_len(req, count - sizeof(struct smb2_lock_element));
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field and count for all locks */
iov[0].iov_len = get_rfc1002_length(req) + 4 - count;
iov[1].iov_base = (char *)buf;
iov[1].iov_len = count;
cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP);
if (rc) {
cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc);
cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
}
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)
{
int rc;
struct smb2_lease_ack *req = NULL;
cifs_dbg(FYI, "SMB2_lease_break\n");
rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
if (rc)
return rc;
req->hdr.CreditRequest = cpu_to_le16(1);
req->StructureSize = cpu_to_le16(36);
inc_rfc1001_len(req, 12);
memcpy(req->LeaseKey, lease_key, 16);
req->LeaseState = lease_state;
rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
/* SMB2 buffer freed by function above */
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc);
}
return rc;
}
/*
* 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 NUMBER_OF_SMB2_COMMANDS 0x0013
/* BB FIXME - analyze following length BB */
#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
#define SMB2_PROTO_NUMBER __constant_cpu_to_le32(0x424d53fe)
/*
* 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 __constant_cpu_to_le16(64)
struct smb2_hdr {
__be32 smb2_buf_length; /* big endian on wire */
/* length is only two or three bytes - with
one or two byte type preceding it that MBZ */
__u8 ProtocolId[4]; /* 0xFE 'S' 'M' 'B' */
__le16 StructureSize; /* 64 */
__le16 CreditCharge; /* MBZ */
__le32 Status; /* Error from server */
__le16 Command;
__le16 CreditRequest; /* CreditResponse */
__le32 Flags;
__le32 NextCommand;
__u64 MessageId; /* opaque - so can stay little endian */
__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_pdu {
struct smb2_hdr hdr;
__le16 StructureSize2; /* size of wct area (varies, request specific) */
} __packed;
/*
* SMB2 flag definitions
*/
#define SMB2_FLAGS_SERVER_TO_REDIR __constant_cpu_to_le32(0x00000001)
#define SMB2_FLAGS_ASYNC_COMMAND __constant_cpu_to_le32(0x00000002)
#define SMB2_FLAGS_RELATED_OPERATIONS __constant_cpu_to_le32(0x00000004)
#define SMB2_FLAGS_SIGNED __constant_cpu_to_le32(0x00000008)
#define SMB2_FLAGS_DFS_OPERATIONS __constant_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 SMB2_ERROR_STRUCTURE_SIZE2 __constant_cpu_to_le16(9)
struct smb2_err_rsp {
struct smb2_hdr 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;
#define SMB2_CLIENT_GUID_SIZE 16
extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
struct smb2_negotiate_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 36 */
__le16 DialectCount;
__le16 SecurityMode;
__le16 Reserved; /* MBZ */
__le32 Capabilities;
__u8 ClientGUID[SMB2_CLIENT_GUID_SIZE];
__le64 ClientStartTime; /* MBZ */
__le16 Dialects[1]; /* One dialect (vers=) at a time for now */
} __packed;
/* Dialects */
#define SMB20_PROT_ID 0x0202
#define SMB21_PROT_ID 0x0210
#define SMB30_PROT_ID 0x0300
#define SMB302_PROT_ID 0x0302
#define BAD_PROT_ID 0xFFFF
/* SecurityMode flags */
#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001
#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
/* 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_negotiate_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 65 */
__le16 SecurityMode;
__le16 DialectRevision;
__le16 Reserved; /* MBZ */
__u8 ServerGUID[16];
__le32 Capabilities;
__le32 MaxTransactSize;
__le32 MaxReadSize;
__le32 MaxWriteSize;
__le64 SystemTime; /* MBZ */
__le64 ServerStartTime;
__le16 SecurityBufferOffset;
__le16 SecurityBufferLength;
__le32 Reserved2; /* may be any value, ignore */
__u8 Buffer[1]; /* variable length GSS security buffer */
} __packed;
struct smb2_sess_setup_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 25 */
__u8 VcNumber;
__u8 SecurityMode;
__le32 Capabilities;
__le32 Channel;
__le16 SecurityBufferOffset;
__le16 SecurityBufferLength;
__le64 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
struct smb2_sess_setup_rsp {
struct smb2_hdr 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_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_logoff_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_tree_connect_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 Reserved;
__le16 PathOffset;
__le16 PathLength;
__u8 Buffer[1]; /* variable length */
} __packed;
struct smb2_tree_connect_rsp {
struct smb2_hdr 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 SHI1005_FLAGS_ALL 0x0000FF33
/* 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 */
struct smb2_tree_disconnect_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_tree_disconnect_rsp {
struct smb2_hdr 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
/* 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 "AlSi"
#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"
struct smb2_create_req {
struct smb2_hdr 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_hdr 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 __constant_cpu_to_le32(0x00)
#define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01)
#define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02)
#define SMB2_LEASE_WRITE_CACHING __constant_cpu_to_le32(0x04)
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x02)
#define SMB2_LEASE_KEY_SIZE 16
struct lease_context {
__le64 LeaseKeyLow;
__le64 LeaseKeyHigh;
__le32 LeaseState;
__le32 LeaseFlags;
__le64 LeaseDuration;
} __packed;
struct lease_context_v2 {
__le64 LeaseKeyLow;
__le64 LeaseKeyHigh;
__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;
/* this goes in the ioctl buffer when doing a copychunk request */
struct copychunk_ioctl {
char SourceKey[24];
__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;
/* Response and Request are the same format */
struct validate_negotiate_info {
__le32 Capabilities;
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
__le16 SecurityMode;
__le16 DialectCount;
__le16 Dialect[1];
} __packed;
#define RSS_CAPABLE 0x00000001
#define RDMA_CAPABLE 0x00000002
struct network_interface_info_ioctl_rsp {
__le32 Next; /* next interface. zero if this is last one */
__le32 IfIndex;
__le32 Capability; /* RSS or RDMA Capable */
__le32 Reserved;
__le64 LinkSpeed;
char SockAddr_Storage[128];
} __packed;
#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
struct smb2_ioctl_req {
struct smb2_hdr 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;
char Buffer[0];
} __packed;
struct smb2_ioctl_rsp {
struct smb2_hdr 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_hdr 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_hdr 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_hdr 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_hdr 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 0x00000000
#define SMB2_CHANNEL_RDMA_V1 0x00000001 /* SMB3 or later */
#define SMB2_CHANNEL_RDMA_V1_INVALIDATE 0x00000001 /* SMB3.02 or later */
struct smb2_read_req {
struct smb2_hdr 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; /* Reserved MBZ */
__le16 ReadChannelInfoLength; /* Reserved MBZ */
__u8 Buffer[1];
} __packed;
struct smb2_read_rsp {
struct smb2_hdr 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_hdr 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; /* Reserved MBZ */
__le16 WriteChannelInfoLength; /* Reserved MBZ */
__le32 Flags;
__u8 Buffer[1];
} __packed;
struct smb2_write_rsp {
struct smb2_hdr 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_hdr 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_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__le16 Reserved;
} __packed;
struct smb2_echo_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
__u16 Reserved;
} __packed;
struct smb2_echo_rsp {
struct smb2_hdr 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_hdr 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_hdr 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
struct smb2_query_info_req {
struct smb2_hdr 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_hdr hdr;
__le16 StructureSize; /* Must be 9 */
__le16 OutputBufferOffset;
__le32 OutputBufferLength;
__u8 Buffer[1];
} __packed;
struct smb2_set_info_req {
struct smb2_hdr 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_hdr hdr;
__le16 StructureSize; /* Must be 2 */
} __packed;
struct smb2_oplock_break {
struct smb2_hdr 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_hdr hdr;
__le16 StructureSize; /* Must be 44 */
__le16 Reserved;
__le32 Flags;
__u8 LeaseKey[16];
__le32 CurrentLeaseState;
__le32 NewLeaseState;
__le32 BreakReason;
__le32 AccessMaskHint;
__le32 ShareMaskHint;
} __packed;
struct smb2_lease_ack {
struct smb2_hdr 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 /* Set */
#define FS_SIZE_INFORMATION 3 /* Query */
#define FS_DEVICE_INFORMATION 4 /* Query */
#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
#define FS_CONTROL_INFORMATION 6 /* Query, Set */
#define FS_FULL_SIZE_INFORMATION 7 /* Query */
#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
#define FS_DRIVER_PATH_INFORMATION 9 /* Query */
struct smb2_fs_full_size_info {
__le64 TotalAllocationUnits;
__le64 CallerAvailableAllocationUnits;
__le64 ActualAvailableAllocationUnits;
__le32 SectorsPerAllocationUnit;
__le32 BytesPerSector;
} __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 */
/*
* 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 */
#endif /* _SMB2PDU_H */
/*
* 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);
extern unsigned int smb2_calc_size(void *buf);
extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
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 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 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);
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 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 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);
/*
* 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 smb2_err_rsp **err_buf);
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_flush(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id);
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_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_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);
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_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level);
extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id,
struct kstatfs *FSData);
extern int SMB2_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);
#endif /* _SMB2PROTO_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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 "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)
{
int rc;
unsigned int size;
if (server->secmech.sdeschmacsha256 != NULL)
return 0; /* already allocated */
server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
if (IS_ERR(server->secmech.hmacsha256)) {
cifs_dbg(VFS, "could not allocate crypto hmacsha256\n");
rc = PTR_ERR(server->secmech.hmacsha256);
server->secmech.hmacsha256 = NULL;
return rc;
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacsha256);
server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdeschmacsha256) {
crypto_free_shash(server->secmech.hmacsha256);
server->secmech.hmacsha256 = NULL;
return -ENOMEM;
}
server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
server->secmech.sdeschmacsha256->shash.flags = 0x0;
return 0;
}
static int
smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
{
unsigned int size;
int rc;
if (server->secmech.sdesccmacaes != NULL)
return 0; /* already allocated */
rc = smb2_crypto_shash_allocate(server);
if (rc)
return rc;
server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
if (IS_ERR(server->secmech.cmacaes)) {
cifs_dbg(VFS, "could not allocate crypto cmac-aes");
kfree(server->secmech.sdeschmacsha256);
server->secmech.sdeschmacsha256 = NULL;
crypto_free_shash(server->secmech.hmacsha256);
server->secmech.hmacsha256 = NULL;
rc = PTR_ERR(server->secmech.cmacaes);
server->secmech.cmacaes = NULL;
return rc;
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.cmacaes);
server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdesccmacaes) {
cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
kfree(server->secmech.sdeschmacsha256);
server->secmech.sdeschmacsha256 = NULL;
crypto_free_shash(server->secmech.hmacsha256);
crypto_free_shash(server->secmech.cmacaes);
server->secmech.hmacsha256 = NULL;
server->secmech.cmacaes = NULL;
return -ENOMEM;
}
server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
server->secmech.sdesccmacaes->shash.flags = 0x0;
return 0;
}
static struct cifs_ses *
smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server)
{
struct cifs_ses *ses;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
if (ses->Suid != smb2hdr->SessionId)
continue;
spin_unlock(&cifs_tcp_ses_lock);
return ses;
}
spin_unlock(&cifs_tcp_ses_lock);
return NULL;
}
int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int i, rc;
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
unsigned char *sigptr = smb2_signature;
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(smb2_pdu, server);
if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0;
}
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = smb2_crypto_shash_allocate(server);
if (rc) {
cifs_dbg(VFS, "%s: shah256 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;
}
rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
if (rc) {
cifs_dbg(VFS, "%s: Could not init sha256", __func__);
return rc;
}
for (i = 0; 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;
}
/*
* The first entry includes a length field (which does not get
* signed that occupies the first 4 bytes before the header).
*/
if (i == 0) {
if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
break; /* nothing to sign or corrupt header */
rc =
crypto_shash_update(
&server->secmech.sdeschmacsha256->shash,
iov[i].iov_base + 4, iov[i].iov_len - 4);
} else {
rc =
crypto_shash_update(
&server->secmech.sdeschmacsha256->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++) {
struct kvec p_iov;
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
p_iov.iov_base, p_iov.iov_len);
kunmap(rqst->rq_pages[i]);
}
rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
sigptr);
if (rc)
cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
return rc;
}
int
generate_smb3signingkey(struct cifs_ses *ses)
{
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(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_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,
"SMB2AESCMAC", 12);
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,
"SmbSign", 8);
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(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE);
smb3signkey_ret:
return rc;
}
int
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int i;
int rc = 0;
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(smb2_pdu, server);
if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0;
}
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
memset(smb2_pdu->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(&server->secmech.sdesccmacaes->shash);
if (rc) {
cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
return rc;
}
for (i = 0; i < n_vec; i++) {
if (iov[i].iov_len == 0)
continue;
if (iov[i].iov_base == NULL) {
cifs_dbg(VFS, "null iovec entry");
return -EIO;
}
/*
* The first entry includes a length field (which does not get
* signed that occupies the first 4 bytes before the header).
*/
if (i == 0) {
if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
break; /* nothing to sign or corrupt header */
rc =
crypto_shash_update(
&server->secmech.sdesccmacaes->shash,
iov[i].iov_base + 4, iov[i].iov_len - 4);
} else {
rc =
crypto_shash_update(
&server->secmech.sdesccmacaes->shash,
iov[i].iov_base, iov[i].iov_len);
}
if (rc) {
cifs_dbg(VFS, "%s: Couldn't update cmac aes with payload\n",
__func__);
return rc;
}
}
/* now hash over the rq_pages array */
for (i = 0; i < rqst->rq_npages; i++) {
struct kvec p_iov;
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
crypto_shash_update(&server->secmech.sdesccmacaes->shash,
p_iov.iov_base, p_iov.iov_len);
kunmap(rqst->rq_pages[i]);
}
rc = crypto_shash_final(&server->secmech.sdesccmacaes->shash,
sigptr);
if (rc)
cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__);
memcpy(smb2_pdu->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_hdr *smb2_pdu = rqst->rq_iov[0].iov_base;
if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) ||
server->tcpStatus == CifsNeedNegotiate)
return rc;
if (!server->session_estab) {
strncpy(smb2_pdu->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_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
(smb2_pdu->Command == SMB2_SESSION_SETUP) ||
(smb2_pdu->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(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n",
smb2_pdu->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, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE);
memset(smb2_pdu->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, smb2_pdu->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_hdr *hdr)
{
hdr->MessageId = get_next_mid(server);
}
static struct mid_q_entry *
smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer,
struct TCP_Server_Info *server)
{
struct mid_q_entry *temp;
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);
if (temp == NULL)
return temp;
else {
memset(temp, 0, sizeof(struct mid_q_entry));
temp->mid = smb_buffer->MessageId; /* always LE */
temp->pid = current->pid;
temp->command = smb_buffer->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_hdr *buf,
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 != CifsGood) {
/* check if SMB2 session is bad because we are setting it up */
if ((buf->Command != SMB2_SESSION_SETUP) &&
(buf->Command != SMB2_NEGOTIATE))
return -EAGAIN;
/* else ok - we are setting up session */
}
*mid = smb2_mid_entry_alloc(buf, 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 = get_rfc1002_length(mid->resp_buf);
struct kvec iov;
struct smb_rqst rqst = { .rq_iov = &iov,
.rq_nvec = 1 };
iov.iov_base = (char *)mid->resp_buf;
iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4;
dump_smb(mid->resp_buf, min_t(u32, 80, len));
/* convert the length into a more usable form */
if (len > 24 && server->sign) {
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_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
smb2_seq_num_into_buf(ses->server, hdr);
rc = smb2_get_mid_entry(ses, hdr, &mid);
if (rc)
return ERR_PTR(rc);
rc = smb2_sign_rqst(rqst, ses->server);
if (rc) {
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_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
smb2_seq_num_into_buf(server, hdr);
mid = smb2_mid_entry_alloc(hdr, server);
if (mid == NULL)
return ERR_PTR(-ENOMEM);
rc = smb2_sign_rqst(rqst, server);
if (rc) {
DeleteMidQEntry(mid);
return ERR_PTR(rc);
}
return mid;
}
/*
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/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/random.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)
{
int rc;
unsigned char key2[8];
struct crypto_blkcipher *tfm_des;
struct scatterlist sgin, sgout;
struct blkcipher_desc desc;
str_to_key(key, key2);
tfm_des = crypto_alloc_blkcipher("ecb(des)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_des)) {
rc = PTR_ERR(tfm_des);
cifs_dbg(VFS, "could not allocate des crypto API\n");
goto smbhash_err;
}
desc.tfm = tfm_des;
crypto_blkcipher_setkey(tfm_des, key2, 8);
sg_init_one(&sgin, in, 8);
sg_init_one(&sgout, out, 8);
rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, 8);
if (rc)
cifs_dbg(VFS, "could not encrypt crypt key rc: %d\n", rc);
crypto_free_blkcipher(tfm_des);
smbhash_err:
return rc;
}
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;
unsigned int size;
struct crypto_shash *md4;
struct sdesc *sdescmd4;
md4 = crypto_alloc_shash("md4", 0, 0);
if (IS_ERR(md4)) {
rc = PTR_ERR(md4);
cifs_dbg(VFS, "%s: Crypto md4 allocation error %d\n",
__func__, rc);
return rc;
}
size = sizeof(struct shash_desc) + crypto_shash_descsize(md4);
sdescmd4 = kmalloc(size, GFP_KERNEL);
if (!sdescmd4) {
rc = -ENOMEM;
goto mdfour_err;
}
sdescmd4->shash.tfm = md4;
sdescmd4->shash.flags = 0x0;
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:
crypto_free_shash(md4);
kfree(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));
memset(wpwd, 0, 129 * sizeof(__le16));
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;
}
/*
* 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
/*
* 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 0x000900C8 /* BB add struct */
#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_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_SIS_LINK_FILES 0x0009C104
#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 /* BB add struct */
#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 /* BB add struct */
/* 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 */
#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
#define IO_REPARSE_TAG_HSM 0xC0000004
#define IO_REPARSE_TAG_SIS 0x80000007
/* fsctl flags */
/* If Flags is set to this value, the request is an FSCTL not ioctl request */
#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
/*
* 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/highmem.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <linux/mempool.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
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);
if (temp == NULL)
return temp;
else {
memset(temp, 0, sizeof(struct mid_q_entry));
temp->mid = smb_buffer->Mid; /* always LE */
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;
}
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 ((now - midEntry->when_alloc) > HZ) {
if ((cifsFYI & CIFS_TIMER) && (midEntry->command != command)) {
printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu",
midEntry->command, midEntry->mid);
printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
now - midEntry->when_alloc,
now - midEntry->when_sent,
now - midEntry->when_received);
}
}
#endif
mempool_free(midEntry, cifs_mid_poolp);
}
void
cifs_delete_mid(struct mid_q_entry *mid)
{
spin_lock(&GlobalMid_Lock);
list_del(&mid->qhead);
spin_unlock(&GlobalMid_Lock);
DeleteMidQEntry(mid);
}
/*
* smb_send_kvec - send an array of kvecs to the server
* @server: Server to send the data to
* @iov: Pointer to array of kvecs
* @n_vec: length of kvec array
* @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 kvec *iov, size_t n_vec,
size_t *sent)
{
int rc = 0;
int i = 0;
struct msghdr smb_msg;
unsigned int remaining;
size_t first_vec = 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;
remaining = 0;
for (i = 0; i < n_vec; i++)
remaining += iov[i].iov_len;
i = 0;
while (remaining) {
/*
* 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 = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
n_vec - first_vec, remaining);
if (rc == -EAGAIN) {
i++;
if (i >= 14 || (!server->noblocksnd && (i > 2))) {
cifs_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
ssocket);
rc = -EAGAIN;
break;
}
msleep(1 << i);
continue;
}
if (rc < 0)
break;
/* send was at least partially successful */
*sent += rc;
if (rc == remaining) {
remaining = 0;
break;
}
if (rc > remaining) {
cifs_dbg(VFS, "sent %d requested %d\n", rc, remaining);
break;
}
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;
}
remaining -= rc;
/* the line below resets i */
for (i = first_vec; i < n_vec; i++) {
if (iov[i].iov_len) {
if (rc > iov[i].iov_len) {
rc -= iov[i].iov_len;
iov[i].iov_len = 0;
} else {
iov[i].iov_base += rc;
iov[i].iov_len -= rc;
first_vec = i;
break;
}
}
}
i = 0; /* in case we get ENOSPC on the next send */
rc = 0;
}
return rc;
}
/**
* rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec
* @rqst: pointer to smb_rqst
* @idx: index into the array of the page
* @iov: pointer to struct kvec that will hold the result
*
* Helper function to convert a slot in the rqst->rq_pages array into a kvec.
* The page will be kmapped and the address placed into iov_base. The length
* will then be adjusted according to the ptailoff.
*/
void
cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
struct kvec *iov)
{
/*
* FIXME: We could avoid this kmap altogether if we used
* kernel_sendpage instead of kernel_sendmsg. That will only
* work if signing is disabled though as sendpage inlines the
* page directly into the fraglist. If userspace modifies the
* page after we calculate the signature, then the server will
* reject it and may break the connection. kernel_sendmsg does
* an extra copy of the data and avoids that issue.
*/
iov->iov_base = kmap(rqst->rq_pages[idx]);
/* if last page, don't send beyond this offset into page */
if (idx == (rqst->rq_npages - 1))
iov->iov_len = rqst->rq_tailsz;
else
iov->iov_len = rqst->rq_pagesz;
}
static unsigned long
rqst_len(struct smb_rqst *rqst)
{
unsigned int i;
struct kvec *iov = rqst->rq_iov;
unsigned long buflen = 0;
/* total up iov array first */
for (i = 0; i < rqst->rq_nvec; i++)
buflen += iov[i].iov_len;
/* add in the page array if there is one */
if (rqst->rq_npages) {
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1);
buflen += rqst->rq_tailsz;
}
return buflen;
}
static int
smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
int rc;
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
unsigned long send_length;
unsigned int i;
size_t total_len = 0, sent;
struct socket *ssocket = server->ssocket;
int val = 1;
if (ssocket == NULL)
return -ENOTSOCK;
/* sanity check send length */
send_length = rqst_len(rqst);
if (send_length != smb_buf_length + 4) {
WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n",
send_length, smb_buf_length);
return -EIO;
}
cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length);
dump_smb(iov[0].iov_base, iov[0].iov_len);
/* cork the socket */
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
(char *)&val, sizeof(val));
rc = smb_send_kvec(server, iov, n_vec, &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->rq_npages; i++) {
struct kvec p_iov;
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
rc = smb_send_kvec(server, &p_iov, 1, &sent);
kunmap(rqst->rq_pages[i]);
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 != smb_buf_length + 4)) {
cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n",
smb_buf_length + 4, 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;
}
if (rc < 0 && rc != -EINTR)
cifs_dbg(VFS, "Error %d sending data on socket to server\n",
rc);
else
rc = 0;
return rc;
}
static int
smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
{
struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = n_vec };
return smb_send_rqst(server, &rqst);
}
int
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
unsigned int smb_buf_length)
{
struct kvec iov;
iov.iov_base = smb_buffer;
iov.iov_len = smb_buf_length + 4;
return smb_sendv(server, &iov, 1);
}
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)
{
return wait_for_free_credits(server, timeout,
server->ops->get_credits_field(server, optype));
}
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 != CifsGood) {
/* check if SMB session is bad because we are setting it up */
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE))
return -EAGAIN;
/* else ok - we are setting up 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;
/* 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,
void *cbdata, const int flags)
{
int rc, timeout, optype;
struct mid_q_entry *mid;
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK;
rc = wait_for_free_request(server, timeout, optype);
if (rc)
return rc;
mutex_lock(&server->srv_mutex);
mid = server->ops->setup_async_request(server, rqst);
if (IS_ERR(mid)) {
mutex_unlock(&server->srv_mutex);
add_credits(server, 1, optype);
wake_up(&server->request_q);
return PTR_ERR(mid);
}
mid->receive = receive;
mid->callback = callback;
mid->callback_data = cbdata;
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);
cifs_in_send_inc(server);
rc = smb_send_rqst(server, rqst);
cifs_in_send_dec(server);
cifs_save_when_sent(mid);
if (rc < 0)
server->sequence_number -= 2;
mutex_unlock(&server->srv_mutex);
if (rc == 0)
return 0;
cifs_delete_mid(mid);
add_credits(server, 1, optype);
wake_up(&server->request_q);
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];
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);
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, void *buf, struct mid_q_entry *mid)
{
return server->ops->send_cancel ?
server->ops->send_cancel(server, buf, 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;
int rc = 0;
struct smb_rqst rqst = { .rq_iov = &iov,
.rq_nvec = 1 };
iov.iov_base = mid->resp_buf;
iov.iov_len = len;
/* 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;
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;
}
int
SendReceive2(const unsigned int xid, struct cifs_ses *ses,
struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
const int flags)
{
int rc = 0;
int timeout, optype;
struct mid_q_entry *midQ;
char *buf = iov[0].iov_base;
unsigned int credits = 1;
struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = n_vec };
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK;
*resp_buf_type = CIFS_NO_BUFFER; /* no response buf yet */
if ((ses == NULL) || (ses->server == NULL)) {
cifs_small_buf_release(buf);
cifs_dbg(VFS, "Null session\n");
return -EIO;
}
if (ses->server->tcpStatus == CifsExiting) {
cifs_small_buf_release(buf);
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.
*/
rc = wait_for_free_request(ses->server, timeout, optype);
if (rc) {
cifs_small_buf_release(buf);
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);
midQ = ses->server->ops->setup_request(ses, &rqst);
if (IS_ERR(midQ)) {
mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(buf);
/* Update # of requests on wire to server */
add_credits(ses->server, 1, optype);
return PTR_ERR(midQ);
}
midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server);
rc = smb_sendv(ses->server, iov, n_vec);
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_small_buf_release(buf);
goto out;
}
if (timeout == CIFS_ASYNC_OP) {
cifs_small_buf_release(buf);
goto out;
}
rc = wait_for_response(ses->server, midQ);
if (rc != 0) {
send_cancel(ses->server, buf, midQ);
spin_lock(&GlobalMid_Lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock);
cifs_small_buf_release(buf);
add_credits(ses->server, 1, optype);
return rc;
}
spin_unlock(&GlobalMid_Lock);
}
cifs_small_buf_release(buf);
rc = cifs_sync_mid_result(midQ, ses->server);
if (rc != 0) {
add_credits(ses->server, 1, optype);
return rc;
}
if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO;
cifs_dbg(FYI, "Bad MID state?\n");
goto out;
}
buf = (char *)midQ->resp_buf;
iov[0].iov_base = buf;
iov[0].iov_len = get_rfc1002_length(buf) + 4;
if (midQ->large_buf)
*resp_buf_type = CIFS_LARGE_BUFFER;
else
*resp_buf_type = CIFS_SMALL_BUFFER;
credits = ses->server->ops->get_credits(midQ);
rc = ses->server->ops->check_receive(midQ, 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->resp_buf = NULL;
out:
cifs_delete_mid(midQ);
add_credits(ses->server, credits, optype);
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;
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 (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize +
MAX_CIFS_HDR_SIZE - 4) {
cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
be32_to_cpu(in_buf->smb_buf_length));
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, be32_to_cpu(in_buf->smb_buf_length));
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, in_buf, 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;
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 (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize +
MAX_CIFS_HDR_SIZE - 4) {
cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
be32_to_cpu(in_buf->smb_buf_length));
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, be32_to_cpu(in_buf->smb_buf_length));
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, in_buf, 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, in_buf, 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;
}
/*
* 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;
}
/*
* 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"
#define MAX_EA_VALUE_SIZE 65535
#define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
#define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
/* BB need to add server (Samba e.g) support for security and trusted prefix */
int cifs_removexattr(struct dentry *direntry, const char *ea_name)
{
int rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
unsigned int xid;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
struct super_block *sb;
char *full_path = NULL;
if (direntry == NULL)
return -EIO;
if (direntry->d_inode == NULL)
return -EIO;
sb = direntry->d_inode->i_sb;
if (sb == NULL)
return -EIO;
cifs_sb = CIFS_SB(sb);
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 remove_ea_exit;
}
if (ea_name == NULL) {
cifs_dbg(FYI, "Null xattr names not supported\n");
} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
&& (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) {
cifs_dbg(FYI,
"illegal xattr request %s (only user namespace supported)\n",
ea_name);
/* BB what if no namespace prefix? */
/* Should we just pass them to server, except for
system and perhaps security prefixes? */
} else {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto remove_ea_exit;
ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
(__u16)0, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
}
remove_ea_exit:
kfree(full_path);
free_xid(xid);
cifs_put_tlink(tlink);
#endif
return rc;
}
int cifs_setxattr(struct dentry *direntry, const char *ea_name,
const void *ea_value, size_t value_size, int flags)
{
int rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
unsigned int xid;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
struct super_block *sb;
char *full_path;
if (direntry == NULL)
return -EIO;
if (direntry->d_inode == NULL)
return -EIO;
sb = direntry->d_inode->i_sb;
if (sb == NULL)
return -EIO;
cifs_sb = CIFS_SB(sb);
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 set_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 (value_size > MAX_EA_VALUE_SIZE) {
cifs_dbg(FYI, "size of EA value too large\n");
rc = -EOPNOTSUPP;
goto set_ea_exit;
}
if (ea_name == NULL) {
cifs_dbg(FYI, "Null xattr names not supported\n");
} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
== 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto set_ea_exit;
if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
cifs_dbg(FYI, "attempt to set cifs inode metadata\n");
ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
(__u16)value_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)
== 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto set_ea_exit;
ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
(__u16)value_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL,
strlen(CIFS_XATTR_CIFS_ACL)) == 0) {
#ifdef CONFIG_CIFS_ACL
struct cifs_ntsd *pacl;
pacl = kmalloc(value_size, GFP_KERNEL);
if (!pacl) {
rc = -ENOMEM;
} else {
memcpy(pacl, ea_value, value_size);
rc = set_cifs_acl(pacl, value_size,
direntry->d_inode, full_path, CIFS_ACL_DACL);
if (rc == 0) /* force revalidate of the inode */
CIFS_I(direntry->d_inode)->time = 0;
kfree(pacl);
}
#else
cifs_dbg(FYI, "Set CIFS ACL not supported yet\n");
#endif /* CONFIG_CIFS_ACL */
} else {
int temp;
temp = strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
strlen(POSIX_ACL_XATTR_ACCESS));
if (temp == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
ea_value, (const int)value_size,
ACL_TYPE_ACCESS, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_dbg(FYI, "set POSIX ACL rc %d\n", rc);
#else
cifs_dbg(FYI, "set POSIX ACL not supported\n");
#endif
} else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
ea_value, (const int)value_size,
ACL_TYPE_DEFAULT, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_dbg(FYI, "set POSIX default ACL rc %d\n", rc);
#else
cifs_dbg(FYI, "set default POSIX ACL not supported\n");
#endif
} else {
cifs_dbg(FYI, "illegal xattr request %s (only user namespace supported)\n",
ea_name);
/* BB what if no namespace prefix? */
/* Should we just pass them to server, except for
system and perhaps security prefixes? */
}
}
set_ea_exit:
kfree(full_path);
free_xid(xid);
cifs_put_tlink(tlink);
#endif
return rc;
}
ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
void *ea_value, size_t buf_size)
{
ssize_t rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
unsigned int xid;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
struct super_block *sb;
char *full_path;
if (direntry == NULL)
return -EIO;
if (direntry->d_inode == NULL)
return -EIO;
sb = direntry->d_inode->i_sb;
if (sb == NULL)
return -EIO;
cifs_sb = CIFS_SB(sb);
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 get_ea_exit;
}
/* return dos attributes as pseudo xattr */
/* return alt name if available as pseudo attr */
if (ea_name == NULL) {
cifs_dbg(FYI, "Null xattr names not supported\n");
} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
== 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto get_ea_exit;
if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) {
cifs_dbg(FYI, "attempt to query cifs inode metadata\n");
/* revalidate/getattr then populate from inode */
} /* BB add else when above is implemented */
ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
buf_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
goto get_ea_exit;
ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
buf_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
} else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
ea_value, buf_size, ACL_TYPE_ACCESS,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
#else
cifs_dbg(FYI, "Query POSIX ACL not supported yet\n");
#endif /* CONFIG_CIFS_POSIX */
} else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
ea_value, buf_size, ACL_TYPE_DEFAULT,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
#else
cifs_dbg(FYI, "Query POSIX default ACL not supported yet\n");
#endif /* CONFIG_CIFS_POSIX */
} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL,
strlen(CIFS_XATTR_CIFS_ACL)) == 0) {
#ifdef CONFIG_CIFS_ACL
u32 acllen;
struct cifs_ntsd *pacl;
pacl = get_cifs_acl(cifs_sb, direntry->d_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 (ea_value) {
if (acllen > buf_size)
acllen = -ERANGE;
else
memcpy(ea_value, pacl, acllen);
}
rc = acllen;
kfree(pacl);
}
#else
cifs_dbg(FYI, "Query CIFS ACL not supported yet\n");
#endif /* CONFIG_CIFS_ACL */
} else if (strncmp(ea_name,
XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
cifs_dbg(FYI, "Trusted xattr namespace not supported yet\n");
} else if (strncmp(ea_name,
XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
cifs_dbg(FYI, "Security xattr namespace not supported yet\n");
} else
cifs_dbg(FYI,
"illegal xattr request %s (only user namespace supported)\n",
ea_name);
/* 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;
get_ea_exit:
kfree(full_path);
free_xid(xid);
cifs_put_tlink(tlink);
#endif
return rc;
}
ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
{
ssize_t rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
unsigned int xid;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
struct super_block *sb;
char *full_path;
if (direntry == NULL)
return -EIO;
if (direntry->d_inode == NULL)
return -EIO;
sb = direntry->d_inode->i_sb;
if (sb == NULL)
return -EIO;
cifs_sb = CIFS_SB(sb);
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 */
rc = CIFSSMBQAllEAs(xid, pTcon, full_path, NULL, data,
buf_size, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
list_ea_exit:
kfree(full_path);
free_xid(xid);
cifs_put_tlink(tlink);
#endif
return rc;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment