cifs-test-base/asn1.c0000644000175000017500000003437211117756171014267 0ustar stevefstevef/* * 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 #include #include #include #include #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_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 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 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; } 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, enum securityEnum *secType) { struct asn1_ctx ctx; unsigned char *end; unsigned char *sequence_end; unsigned long *oid = NULL; unsigned int cls, con, tag, oidlen, rc; bool use_ntlmssp = false; bool use_kerberos = false; bool use_mskerberos = false; *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default*/ /* 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) { cFYI(1, ("Error decoding negTokenInit header")); return 0; } else if ((cls != ASN1_APL) || (con != ASN1_CON) || (tag != ASN1_EOC)) { cFYI(1, ("cls = %d con = %d tag = %d", 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) { cFYI(1, ("Error decoding negTokenInit header")); return 0; } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding negTokenInit")); return 0; } else if ((cls != ASN1_CTX) || (con != ASN1_CON) || (tag != ASN1_EOC)) { cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d) exit 0", cls, con, tag, end, *end)); return 0; } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding negTokenInit")); return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_CON) || (tag != ASN1_SEQ)) { cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d) exit 1", cls, con, tag, end, *end)); return 0; } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding 2nd part of negTokenInit")); return 0; } else if ((cls != ASN1_CTX) || (con != ASN1_CON) || (tag != ASN1_EOC)) { cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d) exit 0", cls, con, tag, end, *end)); return 0; } if (asn1_header_decode (&ctx, &sequence_end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding 2nd part of negTokenInit")); return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_CON) || (tag != ASN1_SEQ)) { cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d) exit 1", cls, con, tag, end, *end)); return 0; } while (!asn1_eoc_decode(&ctx, sequence_end)) { rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag); if (!rc) { cFYI(1, ("Error decoding negTokenInit hdr exit2")); return 0; } if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) { cFYI(1, ("OID len = %d oid = 0x%lx 0x%lx " "0x%lx 0x%lx", oidlen, *oid, *(oid + 1), *(oid + 2), *(oid + 3))); if (compare_oid(oid, oidlen, MSKRB5_OID, MSKRB5_OID_LEN) && !use_kerberos) use_mskerberos = true; else if (compare_oid(oid, oidlen, KRB5_OID, KRB5_OID_LEN) && !use_mskerberos) use_kerberos = true; else if (compare_oid(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN)) use_ntlmssp = true; kfree(oid); } } else { cFYI(1, ("Should be an oid what is going on?")); } } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding last part negTokenInit exit3")); return 0; } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) { /* tag = 3 indicating mechListMIC */ cFYI(1, ("Exit 4 cls = %d con = %d tag = %d end = %p (%d)", cls, con, tag, end, *end)); return 0; } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding last part negTokenInit exit5")); return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_CON) || (tag != ASN1_SEQ)) { cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d)", cls, con, tag, end, *end)); } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding last part negTokenInit exit 7")); return 0; } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) { cFYI(1, ("Exit 8 cls = %d con = %d tag = %d end = %p (%d)", cls, con, tag, end, *end)); return 0; } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, ("Error decoding last part negTokenInit exit9")); return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_PRI) || (tag != ASN1_GENSTR)) { cFYI(1, ("Exit10 cls = %d con = %d tag = %d end = %p (%d)", cls, con, tag, end, *end)); return 0; } cFYI(1, ("Need to call asn1_octets_decode() function for %s", ctx.pointer)); /* is this UTF-8 or ASCII? */ if (use_kerberos) *secType = Kerberos; else if (use_mskerberos) *secType = MSKerberos; else if (use_ntlmssp) *secType = NTLMSSP; return 1; } cifs-test-base/AUTHORS0000644000175000017500000000510611117756171014322 0ustar stevefstevefOriginal Author =============== Steve French (sfrench@samba.org) The author wishes to express his appreciation and thanks to: Andrew Tridgell (Samba team) for his early suggestions about smb/cifs VFS improvements. Thanks to IBM for allowing me time and test resources to pursue this project, to Jim McDonough from IBM (and the Samba Team) for his help, to the IBM Linux JFS team for explaining many esoteric Linux filesystem features. Jeremy Allison of the Samba team has done invaluable work in adding the server side of the original CIFS Unix extensions and reviewing and implementing portions of the newer CIFS POSIX extensions into the Samba 3 file server. Thank Dave Boutcher of IBM Rochester (author of the OS/400 smb/cifs filesystem client) for proving years ago that very good smb/cifs clients could be done on Unix-like operating systems. Volker Lendecke, Andrew Tridgell, Urban Widmark, John Newbigin and others for their work on the Linux smbfs module. Thanks to the other members of the Storage Network Industry Association CIFS Technical Workgroup for their work specifying this highly complex protocol and finally thanks to the Samba team for their technical advice and encouragement. Patch Contributors ------------------ Zwane Mwaikambo Andi Kleen Amrut Joshi Shobhit Dayal Sergey Vlasov Richard Hughes Yury Umanets Mark Hamzy (for some of the early cifs IPv6 work) Domen Puncer Jesper Juhl (in particular for lots of whitespace/formatting cleanup) Vince Negri and Dave Stahl (for finding an important caching bug) Adrian Bunk (kcalloc cleanups) Miklos Szeredi Kazeon team for various fixes especially for 2.4 version. Asser Ferno (Change Notify support) Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup Gunter Kukkukk (testing and suggestions for support of old servers) Igor Mammedov (DFS support) Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code) Test case and Bug Report contributors ------------------------------------- Thanks to those in the community who have submitted detailed bug reports and debug of problems they have found: Jochen Dolze, David Blaine, Rene Scharfe, Martin Josefsson, Alexander Wild, Anthony Liguori, Lars Muller, Urban Widmark, Massimiliano Ferrero, Howard Owen, Olaf Kirch, Kieron Briggs, Nick Millington and others. Also special mention to the Stanford Checker (SWAT) which pointed out many minor bugs in error paths. Valuable suggestions also have come from Al Viro and Dave Miller. And thanks to the IBM LTC and Power test teams and SuSE testers for finding multiple bugs during excellent stress test runs. cifs-test-base/CHANGES0000644000175000017500000012372511117756171014255 0ustar stevefstevefVersion 1.56 ------------ Add "forcemandatorylock" mount option to allow user to use mandatory rather than posix (advisory) byte range locks, even though server would support posix byte range locks. Fix query of root inode when prefixpath specified and user does not have access to query information about the top of the share. Version 1.55 ------------ Various fixes to make delete of open files behavior more predictable (when delete of an open file fails we mark the file as "delete-on-close" in a way that more servers accept, but only if we can first rename the file to a temporary name). Add experimental support for more safely handling fcntl(F_SETLEASE). Convert cifs to using blocking tcp sends, and also let tcp autotune the socket send and receive buffers. This reduces the number of EAGAIN errors returned by TCP/IP in high stress workloads (and the number of retries on socket writes when sending large SMBWriteX requests). Fix case in which a portion of data can in some cases not get written to the file on the server before the file is closed. Fix DFS parsing to properly handle path consumed field, and to handle certain codepage conversions better. Fix mount and umount race that can cause oops in mount or umount or reconnect. Version 1.54 ------------ Fix premature write failure on congested networks (we would give up on EAGAIN from the socket too quickly on large writes). Cifs_mkdir and cifs_create now respect the setgid bit on parent dir. Fix endian problems in acl (mode from/to cifs acl) on bigendian architectures. Fix problems with preserving timestamps on copying open files (e.g. "cp -a") to Windows servers. For mkdir and create honor setgid bit on parent directory when server supports Unix Extensions but not POSIX create. Update cifs.upcall version to handle new Kerberos sec flags (this requires update of cifs.upcall program from Samba). Fix memory leak on dns_upcall (resolving DFS referralls). Fix plain text password authentication (requires setting SecurityFlags to 0x30030 to enable lanman and plain text though). Fix writes to be at correct offset when file is open with O_APPEND and file is on a directio (forcediretio) mount. Fix bug in rewinding readdir directory searches. Add nodfs mount option. Version 1.53 ------------ DFS support added (Microsoft Distributed File System client support needed for referrals which enable a hierarchical name space among servers). Disable temporary caching of mode bits to servers which do not support storing of mode (e.g. Windows servers, when client mounts without cifsacl mount option) and add new "dynperm" mount option to enable temporary caching of mode (enable old behavior). Fix hang on mount caused when server crashes tcp session during negotiate protocol. Version 1.52 ------------ Fix oops on second mount to server when null auth is used. Enable experimental Kerberos support. Return writebehind errors on flush and sync so that events like out of disk space get reported properly on cached files. Fix setxattr failure to certain Samba versions. Fix mount of second share to disconnected server session (autoreconnect on this). Add ability to modify cifs acls for handling chmod (when mounted with cifsacl flag). Fix prefixpath path separator so we can handle mounts with prefixpaths longer than one directory (one path component) when mounted to Windows servers. Fix slow file open when cifsacl enabled. Fix memory leak in FindNext when the SMB call returns -EBADF. Version 1.51 ------------ Fix memory leak in statfs when mounted to very old servers (e.g. Windows 9x). Add new feature "POSIX open" which allows servers which support the current POSIX Extensions to provide better semantics (e.g. delete for open files opened with posix open). Take into account umask on posix mkdir not just older style mkdir. Add ability to mount to IPC$ share (which allows CIFS named pipes to be opened, read and written as if they were files). When 1st tree connect fails (e.g. due to signing negotiation failure) fix leak that causes cifsd not to stop and rmmod to fail to cleanup cifs_request_buffers pool. Fix problem with POSIX Open/Mkdir on bigendian architectures. Fix possible memory corruption when EAGAIN returned on kern_recvmsg. Return better error if server requires packet signing but client has disabled it. When mounted with cifsacl mount option - mode bits are approximated based on the contents of the ACL of the file or directory. When cifs mount helper is missing convert make sure that UNC name has backslash (not forward slash) between ip address of server and the share name. Version 1.50 ------------ Fix NTLMv2 signing. NFS server mounted over cifs works (if cifs mount is done with "serverino" mount option). Add support for POSIX Unlink (helps with certain sharing violation cases when server such as Samba supports newer POSIX CIFS Protocol Extensions). Add "nounix" mount option to allow disabling the CIFS Unix Extensions for just that mount. Fix hang on spinlock in find_writable_file (race when reopening file after session crash). Byte range unlock request to windows server could unlock more bytes (on server copy of file) than intended if start of unlock request is well before start of a previous byte range lock that we issued. Version 1.49 ------------ IPv6 support. Enable ipv6 addresses to be passed on mount (put the ipv6 address after the "ip=" mount option, at least until mount.cifs is fixed to handle DNS host to ipv6 name translation). Accept override of uid or gid on mount even when Unix Extensions are negotiated (it used to be ignored when Unix Extensions were ignored). This allows users to override the default uid and gid for files when they are certain that the uids or gids on the server do not match those of the client. Make "sec=none" mount override username (so that null user connection is attempted) to match what documentation said. Support for very large reads, over 127K, available to some newer servers (such as Samba 3.0.26 and later but note that it also requires setting CIFSMaxBufSize at module install time to a larger value which may hurt performance in some cases). Make sign option force signing (or fail if server does not support it). Version 1.48 ------------ Fix mtime bouncing around from local idea of last write times to remote time. Fix hang (in i_size_read) when simultaneous size update of same remote file on smp system corrupts sequence number. Do not reread unnecessarily partial page (which we are about to overwrite anyway) when writing out file opened rw. When DOS attribute of file on non-Unix server's file changes on the server side from read-only back to read-write, reflect this change in default file mode (we had been leaving a file's mode read-only until the inode were reloaded). Allow setting of attribute back to ATTR_NORMAL (removing readonly dos attribute when archive dos attribute not set and we are changing mode back to writeable on server which does not support the Unix Extensions). Remove read only dos attribute on chmod when adding any write permission (ie on any of user/group/other (not all of user/group/other ie 0222) when mounted to windows. Add support for POSIX MkDir (slight performance enhancement and eliminates the network race between the mkdir and set path info of the mode). Version 1.47 ------------ Fix oops in list_del during mount caused by unaligned string. Fix file corruption which could occur on some large file copies caused by writepages page i/o completion bug. Seek to SEEK_END forces check for update of file size for non-cached files. Allow file size to be updated on remote extend of locally open, non-cached file. Fix reconnect to newer Samba servers (or other servers which support the CIFS Unix/POSIX extensions) so that we again tell the server the Unix/POSIX cifs capabilities which we support (SetFSInfo). Add experimental support for new POSIX Open/Mkdir (which returns stat information on the open, and allows setting the mode). Version 1.46 ------------ Support deep tree mounts. Better support OS/2, Win9x (DOS) time stamps. Allow null user to be specified on mount ("username="). Do not return EINVAL on readdir when filldir fails due to overwritten blocksize (fixes FC problem). Return error in rename 2nd attempt retry (ie report if rename by handle also fails, after rename by path fails, we were not reporting whether the retry worked or not). Fix NTLMv2 to work to Windows servers (mount with option "sec=ntlmv2"). Version 1.45 ------------ Do not time out lockw calls when using posix extensions. Do not time out requests if server still responding reasonably fast on requests on other threads. Improve POSIX locking emulation, (lock cancel now works, and unlock of merged range works even to Windows servers now). Fix oops on mount to lanman servers (win9x, os/2 etc.) when null password. Do not send listxattr (SMB to query all EAs) if nouser_xattr specified. Fix SE Linux problem (instantiate inodes/dentries in right order for readdir). Version 1.44 ------------ Rewritten sessionsetup support, including support for legacy SMB session setup needed for OS/2 and older servers such as Windows 95 and 98. Fix oops on ls to OS/2 servers. Add support for level 1 FindFirst so we can do search (ls etc.) to OS/2. Do not send NTCreateX or recent levels of FindFirst unless server says it supports NT SMBs (instead use legacy equivalents from LANMAN dialect). Fix to allow NTLMv2 authentication support (now can use stronger password hashing on mount if corresponding /proc/fs/cifs/SecurityFlags is set (0x4004). Allow override of global cifs security flags on mount via "sec=" option(s). Version 1.43 ------------ POSIX locking to servers which support CIFS POSIX Extensions (disabled by default controlled by proc/fs/cifs/Experimental). Handle conversion of long share names (especially Asian languages) to Unicode during mount. Fix memory leak in sess struct on reconnect. Fix rare oops after acpi suspend. Fix O_TRUNC opens to overwrite on cifs open which helps rare case when setpathinfo fails or server does not support it. Version 1.42 ------------ Fix slow oplock break when mounted to different servers at the same time and the tids match and we try to find matching fid on wrong server. Fix read looping when signing required by server (2.6.16 kernel only). Fix readdir vs. rename race which could cause each to hang. Return . and .. even if server does not. Allow searches to skip first three entries and begin at any location. Fix oops in find_writeable_file. Version 1.41 ------------ Fix NTLMv2 security (can be enabled in /proc/fs/cifs) so customers can configure stronger authentication. Fix sfu symlinks so they can be followed (not just recognized). Fix wraparound of bcc on read responses when buffer size over 64K and also fix wrap of max smb buffer size when CIFSMaxBufSize over 64K. Fix oops in cifs_user_read and cifs_readpages (when EAGAIN on send of smb on socket is returned over and over). Add POSIX (advisory) byte range locking support (requires server with newest CIFS UNIX Extensions to the protocol implemented). Slow down negprot slightly in port 139 RFC1001 case to give session_init time on buggy servers. Version 1.40 ------------ Use fsuid (fsgid) more consistently instead of uid (gid). Improve performance of readpages by eliminating one extra memcpy. Allow update of file size from remote server even if file is open for write as long as mount is directio. Recognize share mode security and send NTLM encrypted password on tree connect if share mode negotiated. Version 1.39 ------------ Defer close of a file handle slightly if pending writes depend on that handle (this reduces the EBADF bad file handle errors that can be logged under heavy stress on writes). Modify cifs Kconfig options to expose CONFIG_CIFS_STATS2 Fix SFU style symlinks and mknod needed for servers which do not support the CIFS Unix Extensions. Fix setfacl/getfacl on bigendian. Timeout negative dentries so files that the client sees as deleted but that later get created on the server will be recognized. Add client side permission check on setattr. Timeout stuck requests better (where server has never responded or sent corrupt responses) Version 1.38 ------------ Fix tcp socket retransmission timeouts (e.g. on ENOSPACE from the socket) to be smaller at first (but increasing) so large write performance performance over GigE is better. Do not hang thread on illegal byte range lock response from Windows (Windows can send an RFC1001 size which does not match smb size) by allowing an SMBs TCP length to be up to a few bytes longer than it should be. wsize and rsize can now be larger than negotiated buffer size if server supports large readx/writex, even when directio mount flag not specified. Write size will in many cases now be 16K instead of 4K which greatly helps file copy performance on lightly loaded networks. Fix oops in dnotify when experimental config flag enabled. Make cifsFYI more granular. Version 1.37 ------------ Fix readdir caching when unlink removes file in current search buffer, and this is followed by a rewind search to just before the deleted entry. Do not attempt to set ctime unless atime and/or mtime change requested (most servers throw it away anyway). Fix length check of received smbs to be more accurate. Fix big endian problem with mapchars mount option, and with a field returned by statfs. Version 1.36 ------------ Add support for mounting to older pre-CIFS servers such as Windows9x and ME. For these older servers, add option for passing netbios name of server in on mount (servernetbiosname). Add suspend support for power management, to avoid cifsd thread preventing software suspend from working. Add mount option for disabling the default behavior of sending byte range lock requests to the server (necessary for certain applications which break with mandatory lock behavior such as Evolution), and also mount option for requesting case insensitive matching for path based requests (requesting case sensitive is the default). Version 1.35 ------------ Add writepage performance improvements. Fix path name conversions for long filenames on mounts which were done with "mapchars" mount option specified. Ensure multiplex ids do not collide. Fix case in which rmmod can oops if done soon after last unmount. Fix truncated search (readdir) output when resume filename was a long filename. Fix filename conversion when mapchars mount option was specified and filename was a long filename. Version 1.34 ------------ Fix error mapping of the TOO_MANY_LINKS (hardlinks) case. Do not oops if root user kills cifs oplock kernel thread or kills the cifsd thread (NB: killing the cifs kernel threads is not recommended, unmount and rmmod cifs will kill them when they are no longer needed). Fix readdir to ASCII servers (ie older servers which do not support Unicode) and also require asterisk. Fix out of memory case in which data could be written one page off in the page cache. Version 1.33 ------------ Fix caching problem, in which readdir of directory containing a file which was cached could cause the file's time stamp to be updated without invalidating the readahead data (so we could get stale file data on the client for that file even as the server copy changed). Cleanup response processing so cifsd can not loop when abnormally terminated. Version 1.32 ------------ Fix oops in ls when Transact2 FindFirst (or FindNext) returns more than one transact response for an SMB request and search entry split across two frames. Add support for lsattr (getting ext2/ext3/reiserfs attr flags from the server) as new protocol extensions. Do not send Get/Set calls for POSIX ACLs unless server explicitly claims to support them in CIFS Unix extensions POSIX ACL capability bit. Fix packet signing when multiuser mounting with different users from the same client to the same server. Fix oops in cifs_close. Add mount option for remapping reserved characters in filenames (also allow recognizing files with created by SFU which have any of these seven reserved characters, except backslash, to be recognized). Fix invalid transact2 message (we were sometimes trying to interpret oplock breaks as SMB responses). Add ioctl for checking that the current uid matches the uid of the mounter (needed by umount.cifs). Reduce the number of large buffer allocations in cifs response processing (significantly reduces memory pressure under heavy stress with multiple processes accessing the same server at the same time). Version 1.31 ------------ Fix updates of DOS attributes and time fields so that files on NT4 servers do not get marked delete on close. Display sizes of cifs buffer pools in cifs stats. Fix oops in unmount when cifsd thread being killed by shutdown. Add generic readv/writev and aio support. Report inode numbers consistently in readdir and lookup (when serverino mount option is specified use the inode number that the server reports - for both lookup and readdir, otherwise by default the locally generated inode number is used for inodes created in either path since servers are not always able to provide unique inode numbers when exporting multiple volumes from under one sharename). Version 1.30 ------------ Allow new nouser_xattr mount parm to disable xattr support for user namespace. Do not flag user_xattr mount parm in dmesg. Retry failures setting file time (mostly affects NT4 servers) by retry with handle based network operation. Add new POSIX Query FS Info for returning statfs info more accurately. Handle passwords with multiple commas in them. Version 1.29 ------------ Fix default mode in sysfs of cifs module parms. Remove old readdir routine. Fix capabilities flags for large readx so as to allow reads larger than 64K. Version 1.28 ------------ Add module init parm for large SMB buffer size (to allow it to be changed from its default of 16K) which is especially useful for large file copy when mounting with the directio mount option. Fix oops after returning from mount when experimental ExtendedSecurity enabled and SpnegoNegotiated returning invalid error. Fix case to retry better when peek returns from 1 to 3 bytes on socket which should have more data. Fixed path based calls (such as cifs lookup) to handle path names longer than 530 (now can handle PATH_MAX). Fix pass through authentication from Samba server to DC (Samba required dummy LM password). Version 1.27 ------------ Turn off DNOTIFY (directory change notification support) by default (unless built with the experimental flag) to fix hang with KDE file browser. Fix DNOTIFY flag mappings. Fix hang (in wait_event waiting on an SMB response) in SendReceive when session dies but reconnects quickly from another task. Add module init parms for minimum number of large and small network buffers in the buffer pools, and for the maximum number of simultaneous requests. Version 1.26 ------------ Add setfacl support to allow setting of ACLs remotely to Samba 3.10 and later and other POSIX CIFS compliant servers. Fix error mapping for getfacl to EOPNOTSUPP when server does not support posix acls on the wire. Fix improperly zeroed buffer in CIFS Unix extensions set times call. Version 1.25 ------------ Fix internationalization problem in cifs readdir with filenames that map to longer UTF-8 strings than the string on the wire was in Unicode. Add workaround for readdir to netapp servers. Fix search rewind (seek into readdir to return non-consecutive entries). Do not do readdir when server negotiates buffer size to small to fit filename. Add support for reading POSIX ACLs from the server (add also acl and noacl mount options). Version 1.24 ------------ Optionally allow using server side inode numbers, rather than client generated ones by specifying mount option "serverino" - this is required for some apps to work which double check hardlinked files and have persistent inode numbers. Version 1.23 ------------ Multiple bigendian fixes. On little endian systems (for reconnect after network failure) fix tcp session reconnect code so we do not try first to reconnect on reverse of port 445. Treat reparse points (NTFS junctions) as directories rather than symlinks because we can do follow link on them. Version 1.22 ------------ Add config option to enable XATTR (extended attribute) support, mapping xattr names in the "user." namespace space to SMB/CIFS EAs. Lots of minor fixes pointed out by the Stanford SWAT checker (mostly missing or out of order NULL pointer checks in little used error paths). Version 1.21 ------------ Add new mount parm to control whether mode check (generic_permission) is done on the client. If Unix extensions are enabled and the uids on the client and server do not match, client permission checks are meaningless on server uids that do not exist on the client (this does not affect the normal ACL check which occurs on the server). Fix default uid on mknod to match create and mkdir. Add optional mount parm to allow override of the default uid behavior (in which the server sets the uid and gid of newly created files). Normally for network filesystem mounts user want the server to set the uid/gid on newly created files (rather than using uid of the client processes you would in a local filesystem). Version 1.20 ------------ Make transaction counts more consistent. Merge /proc/fs/cifs/SimultaneousOps info into /proc/fs/cifs/DebugData. Fix oops in rare oops in readdir (in build_wildcard_path_from_dentry). Fix mknod to pass type field (block/char/fifo) properly. Remove spurious mount warning log entry when credentials passed as mount argument. Set major/minor device number in inode for block and char devices when unix extensions enabled. Version 1.19 ------------ Fix /proc/fs/cifs/Stats and DebugData display to handle larger amounts of return data. Properly limit requests to MAX_REQ (50 is the usual maximum active multiplex SMB/CIFS requests per server). Do not kill cifsd (and thus hurt the other SMB session) when more than one session to the same server (but with different userids) exists and one of the two user's smb sessions is being removed while leaving the other. Do not loop reconnecting in cifsd demultiplex thread when admin kills the thread without going through unmount. Version 1.18 ------------ Do not rename hardlinked files (since that should be a noop). Flush cached write behind data when reopening a file after session abend, except when already in write. Grab per socket sem during reconnect to avoid oops in sendmsg if overlapping with reconnect. Do not reset cached inode file size on readdir for files open for write on client. Version 1.17 ------------ Update number of blocks in file so du command is happier (in Linux a fake blocksize of 512 is required for calculating number of blocks in inode). Fix prepare write of partial pages to read in data from server if possible. Fix race on tcpStatus field between unmount and reconnection code, causing cifsd process sometimes to hang around forever. Improve out of memory checks in cifs_filldir Version 1.16 ------------ Fix incorrect file size in file handle based setattr on big endian hardware. Fix oops in build_path_from_dentry when out of memory. Add checks for invalid and closing file structs in writepage/partialpagewrite. Add statistics for each mounted share (new menuconfig option). Fix endianness problem in volume information displayed in /proc/fs/cifs/DebugData (only affects affects big endian architectures). Prevent renames while constructing path names for open, mkdir and rmdir. Version 1.15 ------------ Change to mempools for alloc smb request buffers and multiplex structs to better handle low memory problems (and potential deadlocks). Version 1.14 ------------ Fix incomplete listings of large directories on Samba servers when Unix extensions enabled. Fix oops when smb_buffer can not be allocated. Fix rename deadlock when writing out dirty pages at same time. Version 1.13 ------------ Fix open of files in which O_CREATE can cause the mode to change in some cases. Fix case in which retry of write overlaps file close. Fix PPC64 build error. Reduce excessive stack usage in smb password hashing. Fix overwrite of Linux user's view of file mode to Windows servers. Version 1.12 ------------ Fixes for large file copy, signal handling, socket retry, buffer allocation and low memory situations. Version 1.11 ------------ Better port 139 support to Windows servers (RFC1001/RFC1002 Session_Initialize) also now allowing support for specifying client netbiosname. NT4 support added. Version 1.10 ------------ Fix reconnection (and certain failed mounts) to properly wake up the blocked users thread so it does not seem hung (in some cases was blocked until the cifs receive timeout expired). Fix spurious error logging to kernel log when application with open network files killed. Version 1.09 ------------ Fix /proc/fs module unload warning message (that could be logged to the kernel log). Fix intermittent failure in connectathon test7 (hardlink count not immediately refreshed in case in which inode metadata can be incorrectly kept cached when time near zero) Version 1.08 ------------ Allow file_mode and dir_mode (specified at mount time) to be enforced locally (the server already enforced its own ACLs too) for servers that do not report the correct mode (do not support the CIFS Unix Extensions). Version 1.07 ------------ Fix some small memory leaks in some unmount error paths. Fix major leak of cache pages in readpages causing multiple read oriented stress testcases (including fsx, and even large file copy) to fail over time. Version 1.06 ------------ Send NTCreateX with ATTR_POSIX if Linux/Unix extensions negotiated with server. This allows files that differ only in case and improves performance of file creation and file open to such servers. Fix semaphore conflict which causes slow delete of open file to Samba (which unfortunately can cause an oplock break to self while vfs_unlink held i_sem) which can hang for 20 seconds. Version 1.05 ------------ fixes to cifs_readpages for fsx test case Version 1.04 ------------ Fix caching data integrity bug when extending file size especially when no oplock on file. Fix spurious logging of valid already parsed mount options that are parsed outside of the cifs vfs such as nosuid. Version 1.03 ------------ Connect to server when port number override not specified, and tcp port unitialized. Reset search to restart at correct file when kernel routine filldir returns error during large directory searches (readdir). Version 1.02 ------------ Fix caching problem when files opened by multiple clients in which page cache could contain stale data, and write through did not occur often enough while file was still open when read ahead (read oplock) not allowed. Treat "sep=" when first mount option as an override of comma as the default separator between mount options. Version 1.01 ------------ Allow passwords longer than 16 bytes. Allow null password string. Version 1.00 ------------ Gracefully clean up failed mounts when attempting to mount to servers such as Windows 98 that terminate tcp sessions during protocol negotiation. Handle embedded commas in mount parsing of passwords. Version 0.99 ------------ Invalidate local inode cached pages on oplock break and when last file instance is closed so that the client does not continue using stale local copy rather than later modified server copy of file. Do not reconnect when server drops the tcp session prematurely before negotiate protocol response. Fix oops in reopen_file when dentry freed. Allow the support for CIFS Unix Extensions to be disabled via proc interface. Version 0.98 ------------ Fix hang in commit_write during reconnection of open files under heavy load. Fix unload_nls oops in a mount failure path. Serialize writes to same socket which also fixes any possible races when cifs signatures are enabled in SMBs being sent out of signature sequence number order. Version 0.97 ------------ Fix byte range locking bug (endian problem) causing bad offset and length. Version 0.96 ------------ Fix oops (in send_sig) caused by CIFS unmount code trying to wake up the demultiplex thread after it had exited. Do not log error on harmless oplock release of closed handle. Version 0.95 ------------ Fix unsafe global variable usage and password hash failure on gcc 3.3.1 Fix problem reconnecting secondary mounts to same server after session failure. Fix invalid dentry - race in mkdir when directory gets created by another client between the lookup and mkdir. Version 0.94 ------------ Fix to list processing in reopen_files. Fix reconnection when server hung but tcpip session still alive. Set proper timeout on socket read. Version 0.93 ------------ Add missing mount options including iocharset. SMP fixes in write and open. Fix errors in reconnecting after TCP session failure. Fix module unloading of default nls codepage Version 0.92 ------------ Active smb transactions should never go negative (fix double FreeXid). Fix list processing in file routines. Check return code on kmalloc in open. Fix spinlock usage for SMP. Version 0.91 ------------ Fix oops in reopen_files when invalid dentry. drop dentry on server rename and on revalidate errors. Fix cases where pid is now tgid. Fix return code on create hard link when server does not support them. Version 0.90 ------------ Fix scheduling while atomic error in getting inode info on newly created file. Fix truncate of existing files opened with O_CREAT but not O_TRUNC set. Version 0.89 ------------ Fix oops on write to dead tcp session. Remove error log write for case when file open O_CREAT but not O_EXCL Version 0.88 ------------ Fix non-POSIX behavior on rename of open file and delete of open file by taking advantage of trans2 SetFileInfo rename facility if available on target server. Retry on ENOSPC and EAGAIN socket errors. Version 0.87 ------------ Fix oops on big endian readdir. Set blksize to be even power of two (2**blkbits) to fix allocation size miscalculation. After oplock token lost do not read through cache. Version 0.86 ------------ Fix oops on empty file readahead. Fix for file size handling for locally cached files. Version 0.85 ------------ Fix oops in mkdir when server fails to return inode info. Fix oops in reopen_files during auto reconnection to server after server recovered from failure. Version 0.84 ------------ Finish support for Linux 2.5 open/create changes, which removes the redundant NTCreate/QPathInfo/close that was sent during file create. Enable oplock by default. Enable packet signing by default (needed to access many recent Windows servers) Version 0.83 ------------ Fix oops when mounting to long server names caused by inverted parms to kmalloc. Fix MultiuserMount (/proc/fs/cifs configuration setting) so that when enabled we will choose a cifs user session (smb uid) that better matches the local uid if a) the mount uid does not match the current uid and b) we have another session to the same server (ip address) for a different mount which matches the current local uid. Version 0.82 ------------ Add support for mknod of block or character devices. Fix oplock code (distributed caching) to properly send response to oplock break from server. Version 0.81 ------------ Finish up CIFS packet digital signing for the default NTLM security case. This should help Windows 2003 network interoperability since it is common for packet signing to be required now. Fix statfs (stat -f) which recently started returning errors due to invalid value (-1 instead of 0) being set in the struct kstatfs f_ffiles field. Version 0.80 ----------- Fix oops on stopping oplock thread when removing cifs when built as module. Version 0.79 ------------ Fix mount options for ro (readonly), uid, gid and file and directory mode. Version 0.78 ------------ Fix errors displayed on failed mounts to be more understandable. Fixed various incorrect or misleading smb to posix error code mappings. Version 0.77 ------------ Fix display of NTFS DFS junctions to display as symlinks. They are the network equivalent. Fix oops in cifs_partialpagewrite caused by missing spinlock protection of openfile linked list. Allow writebehind caching errors to be returned to the application at file close. Version 0.76 ------------ Clean up options displayed in /proc/mounts by show_options to be more consistent with other filesystems. Version 0.75 ------------ Fix delete of readonly file to Windows servers. Reflect presence or absence of read only dos attribute in mode bits for servers that do not support CIFS Unix extensions. Fix shortened results on readdir of large directories to servers supporting CIFS Unix extensions (caused by incorrect resume key). Version 0.74 ------------ Fix truncate bug (set file size) that could cause hangs e.g. running fsx Version 0.73 ------------ unload nls if mount fails. Version 0.72 ------------ Add resume key support to search (readdir) code to workaround Windows bug. Add /proc/fs/cifs/LookupCacheEnable which allows disabling caching of attribute information for lookups. Version 0.71 ------------ Add more oplock handling (distributed caching code). Remove dead code. Remove excessive stack space utilization from symlink routines. Version 0.70 ------------ Fix oops in get dfs referral (triggered when null path sent in to mount). Add support for overriding rsize at mount time. Version 0.69 ------------ Fix buffer overrun in readdir which caused intermittent kernel oopses. Fix writepage code to release kmap on write data. Allow "-ip=" new mount option to be passed in on parameter distinct from the first part (server name portion of) the UNC name. Allow override of the tcp port of the target server via new mount option "-port=" Version 0.68 ------------ Fix search handle leak on rewind. Fix setuid and gid so that they are reflected in the local inode immediately. Cleanup of whitespace to make 2.4 and 2.5 versions more consistent. Version 0.67 ------------ Fix signal sending so that captive thread (cifsd) exits on umount (which was causing the warning in kmem_cache_free of the request buffers at rmmod time). This had broken as a sideeffect of the recent global kernel change to daemonize. Fix memory leak in readdir code which showed up in "ls -R" (and applications that did search rewinding). Version 0.66 ------------ Reconnect tids and fids after session reconnection (still do not reconnect byte range locks though). Fix problem caching lookup information for directory inodes, improving performance, especially in deep directory trees. Fix various build warnings. Version 0.65 ------------ Finish fixes to commit write for caching/readahead consistency. fsx now works to Samba servers. Fix oops caused when readahead was interrupted by a signal. Version 0.64 ------------ Fix data corruption (in partial page after truncate) that caused fsx to fail to Windows servers. Cleaned up some extraneous error logging in common error paths. Add generic sendfile support. Version 0.63 ------------ Fix memory leak in AllocMidQEntry. Finish reconnection logic, so connection with server can be dropped (or server rebooted) and the cifs client will reconnect. Version 0.62 ------------ Fix temporary socket leak when bad userid or password specified (or other SMBSessSetup failure). Increase maximum buffer size to slightly over 16K to allow negotiation of up to Samba and Windows server default read sizes. Add support for readpages Version 0.61 ------------ Fix oops when username not passed in on mount. Extensive fixes and improvements to error logging (strip redundant newlines, change debug macros to ensure newline passed in and to be more consistent). Fix writepage wrong file handle problem, a readonly file handle could be incorrectly used to attempt to write out file updates through the page cache to multiply open files. This could cause the iozone benchmark to fail on the fwrite test. Fix bug mounting two different shares to the same Windows server when using different usernames (doing this to Samba servers worked but Windows was rejecting it) - now it is possible to use different userids when connecting to the same server from a Linux client. Fix oops when treeDisconnect called during unmount on previously freed socket. Version 0.60 ------------ Fix oops in readpages caused by not setting address space operations in inode in rare code path. Version 0.59 ------------ Includes support for deleting of open files and renaming over existing files (per POSIX requirement). Add readlink support for Windows junction points (directory symlinks). Version 0.58 ------------ Changed read and write to go through pagecache. Added additional address space operations. Memory mapped operations now working. Version 0.57 ------------ Added writepage code for additional memory mapping support. Fixed leak in xids causing the simultaneous operations counter (/proc/fs/cifs/SimultaneousOps) to increase on every stat call. Additional formatting cleanup. Version 0.56 ------------ Fix bigendian bug in order of time conversion. Merge 2.5 to 2.4 version. Formatting cleanup. Version 0.55 ------------ Fixes from Zwane Mwaikambo for adding missing return code checking in a few places. Also included a modified version of his fix to protect global list manipulation of the smb session and tree connection and mid related global variables. Version 0.54 ------------ Fix problem with captive thread hanging around at unmount time. Adjust to 2.5.42-pre changes to superblock layout. Remove wasteful allocation of smb buffers (now the send buffer is reused for responses). Add more oplock handling. Additional minor cleanup. Version 0.53 ------------ More stylistic updates to better match kernel style. Add additional statistics for filesystem which can be viewed via /proc/fs/cifs. Add more pieces of NTLMv2 and CIFS Packet Signing enablement. Version 0.52 ------------ Replace call to sleep_on with safer wait_on_event. Make stylistic changes to better match kernel style recommendations. Remove most typedef usage (except for the PDUs themselves). Version 0.51 ------------ Update mount so the -unc mount option is no longer required (the ip address can be specified in a UNC style device name. Implementation of readpage/writepage started. Version 0.50 ------------ Fix intermittent problem with incorrect smb header checking on badly fragmented tcp responses Version 0.49 ------------ Fixes to setting of allocation size and file size. Version 0.48 ------------ Various 2.5.38 fixes. Now works on 2.5.38 Version 0.47 ------------ Prepare for 2.5 kernel merge. Remove ifdefs. Version 0.46 ------------ Socket buffer management fixes. Fix dual free. Version 0.45 ------------ Various big endian fixes for hardlinks and symlinks and also for dfs. Version 0.44 ------------ Various big endian fixes for servers with Unix extensions such as Samba Version 0.43 ------------ Various FindNext fixes for incorrect filenames on large directory searches on big endian clients. basic posix file i/o tests now work on big endian machines, not just le Version 0.42 ------------ SessionSetup and NegotiateProtocol now work from Big Endian machines. Various Big Endian fixes found during testing on the Linux on 390. Various fixes for compatibility with older versions of 2.4 kernel (now builds and works again on kernels at least as early as 2.4.7). Version 0.41 ------------ Various minor fixes for Connectathon Posix "basic" file i/o test suite. Directory caching fixed so hardlinked files now return the correct number of links on fstat as they are repeatedly linked and unlinked. Version 0.40 ------------ Implemented "Raw" (i.e. not encapsulated in SPNEGO) NTLMSSP (i.e. the Security Provider Interface used to negotiate session advanced session authentication). Raw NTLMSSP is preferred by Windows 2000 Professional and Windows XP. Began implementing support for SPNEGO encapsulation of NTLMSSP based session authentication blobs (which is the mechanism preferred by Windows 2000 server in the absence of Kerberos). Version 0.38 ------------ Introduced optional mount helper utility mount.cifs and made coreq changes to cifs vfs to enable it. Fixed a few bugs in the DFS code (e.g. bcc two bytes too short and incorrect uid in PDU). Version 0.37 ------------ Rewrote much of connection and mount/unmount logic to handle bugs with multiple uses to same share, multiple users to same server etc. Version 0.36 ------------ Fixed major problem with dentry corruption (missing call to dput) Version 0.35 ------------ Rewrite of readdir code to fix bug. Various fixes for bigendian machines. Begin adding oplock support. Multiusermount and oplockEnabled flags added to /proc/fs/cifs although corresponding function not fully implemented in the vfs yet Version 0.34 ------------ Fixed dentry caching bug, misc. cleanup Version 0.33 ------------ Fixed 2.5 support to handle build and configure changes as well as misc. 2.5 changes. Now can build on current 2.5 beta version (2.5.24) of the Linux kernel as well as on 2.4 Linux kernels. Support for STATUS codes (newer 32 bit NT error codes) added. DFS support begun to be added. Version 0.32 ------------ Unix extensions (symlink, readlink, hardlink, chmod and some chgrp and chown) implemented and tested against Samba 2.2.5 Version 0.31 ------------ 1) Fixed lockrange to be correct (it was one byte too short) 2) Fixed GETLK (i.e. the fcntl call to test a range of bytes in a file to see if locked) to correctly show range as locked when there is a conflict with an existing lock. 3) default file perms are now 2767 (indicating support for mandatory locks) instead of 777 for directories in most cases. Eventually will offer optional ability to query server for the correct perms. 3) Fixed eventual trap when mounting twice to different shares on the same server when the first succeeded but the second one was invalid and failed (the second one was incorrectly disconnecting the tcp and smb session) 4) Fixed error logging of valid mount options 5) Removed logging of password field. 6) Moved negotiate, treeDisconnect and uloggoffX (only tConx and SessSetup remain in connect.c) to cifssmb.c and cleaned them up and made them more consistent with other cifs functions. 7) Server support for Unix extensions is now fully detected and FindFirst is implemented both ways (with or without Unix extensions) but FindNext and QueryPathInfo with the Unix extensions are not completed, nor is the symlink support using the Unix extensions 8) Started adding the readlink and follow_link code Version 0.3 ----------- Initial drop cifs-test-base/cifsacl.c0000644000175000017500000005072011117756171015024 0ustar stevefstevef/* * 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 #include "cifspdu.h" #include "cifsglob.h" #include "cifsacl.h" #include "cifsproto.h" #include "cifs_debug.h" #ifdef CONFIG_CIFS_EXPERIMENTAL static struct cifs_wksid wksidarr[NUM_WK_SIDS] = { {{1, 0, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0} }, "null user"}, {{1, 1, {0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0} }, "nobody"}, {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(11), 0, 0, 0, 0} }, "net-users"}, {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(18), 0, 0, 0, 0} }, "sys"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(544), 0, 0, 0} }, "root"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(545), 0, 0, 0} }, "users"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(546), 0, 0, 0} }, "guest"} } ; /* security id for everyone */ static const struct cifs_sid sid_everyone = { 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; /* group users */ static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; int match_sid(struct cifs_sid *ctsid) { int i, j; int num_subauth, num_sat, num_saw; struct cifs_sid *cwsid; if (!ctsid) return -1; for (i = 0; i < NUM_WK_SIDS; ++i) { cwsid = &(wksidarr[i].cifssid); /* compare the revision */ if (ctsid->revision != cwsid->revision) continue; /* compare all of the six auth values */ for (j = 0; j < 6; ++j) { if (ctsid->authority[j] != cwsid->authority[j]) break; } if (j < 6) continue; /* all of the auth values did not match */ /* 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 (j = 0; j < num_subauth; ++j) { if (ctsid->sub_auth[j] != cwsid->sub_auth[j]) break; } if (j < num_subauth) continue; /* all sub_auth values do not match */ } cFYI(1, ("matching sid: %s\n", wksidarr[i].sidname)); return 0; /* sids compare/match */ } cFYI(1, ("No matching sid")); return -1; } /* if the two SIDs (roughly equivalent to a UUID for a user or group) are the same returns 1, if they do not match returns 0 */ 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 0; /* compare the revision */ if (ctsid->revision != cwsid->revision) return 0; /* compare all of the six auth values */ for (i = 0; i < 6; ++i) { if (ctsid->authority[i] != cwsid->authority[i]) return 0; } /* 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]) return 0; } } return 1; /* sids compare/match */ } /* 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) { int i; 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); nowner_sid_ptr->revision = owner_sid_ptr->revision; nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth; for (i = 0; i < 6; i++) nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i]; for (i = 0; i < 5; i++) nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i]; /* 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)); ngroup_sid_ptr->revision = group_sid_ptr->revision; ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth; for (i = 0; i < 6; i++) ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i]; for (i = 0; i < 5; i++) ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i]; 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) { cERROR(1, ("unknown access control type %d", type)); return; } /* else ACCESS_ALLOWED type */ if (flags & GENERIC_ALL) { *pmode |= (S_IRWXUGO & (*pbits_to_set)); cFYI(DBG2, ("all perms")); 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)); cFYI(DBG2, ("access flags 0x%x mode now 0x%x", 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; cFYI(DBG2, ("mode: 0x%x, access flags now 0x%x", 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 < 6; 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) { cERROR(1, ("ACE too small, %d", le16_to_cpu(pace->size))); return; } if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { cERROR(1, ("ACL too small to parse ACE")); return; } num_subauth = pace->sid.num_subauth; if (num_subauth) { int i; cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d", pace->sid.revision, pace->sid.num_subauth, pace->type, pace->flags, le16_to_cpu(pace->size))); for (i = 0; i < num_subauth; ++i) { cFYI(1, ("ACE sub_auth[%d]: 0x%x", 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 inode *inode) { 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 */ inode->i_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)) { cERROR(1, ("ACL too small to parse DACL")); return; } cFYI(DBG2, ("DACL revision %d size %d num aces %d", 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 */ inode->i_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_IRWXO; ppace = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL); 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)) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &(inode->i_mode), &user_mask); if (compare_sids(&(ppace[i]->sid), pgrpsid)) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &(inode->i_mode), &group_mask); if (compare_sids(&(ppace[i]->sid), &sid_everyone)) access_flags_to_mode(ppace[i]->access_req, ppace[i]->type, &(inode->i_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) { cERROR(1, ("ACL too small to parse SID %p", psid)); return -EINVAL; } if (psid->num_subauth) { #ifdef CONFIG_CIFS_DEBUG2 int i; cFYI(1, ("SID revision %d num_auth %d", psid->revision, psid->num_subauth)); for (i = 0; i < psid->num_subauth; i++) { cFYI(1, ("SID sub_auth[%d]: 0x%x ", 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 */ cFYI(1, ("RID 0x%x", 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_ntsd *pntsd, int acl_len, struct inode *inode) { int rc; 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 ((inode == NULL) || (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); cFYI(DBG2, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x " "sacloffset 0x%x dacloffset 0x%x", 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) return rc; rc = parse_sid(group_sid_ptr, end_of_acl); if (rc) return rc; if (dacloffset) parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, inode); else cFYI(1, ("no ACL")); /* BB grant all or default perms? */ /* cifscred->uid = owner_sid_ptr->rid; cifscred->gid = group_sid_ptr->rid; memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr, sizeof(struct cifs_sid)); memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr, sizeof(struct cifs_sid)); */ return 0; } /* Convert permission bits from mode to equivalent CIFS ACL */ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, struct inode *inode, __u64 nmode) { int rc = 0; __u32 dacloffset; __u32 ndacloffset; __u32 sidsoffset; struct cifs_sid *owner_sid_ptr, *group_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 ((inode == NULL) || (pntsd == NULL) || (pnntsd == 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); 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 security descriptor control portion and owner and group sid */ copy_sec_desc(pntsd, pnntsd, sidsoffset); return rc; } /* Retrieve an ACL from the server */ static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, const char *path, const __u16 *pfid) { struct cifsFileInfo *open_file = NULL; bool unlock_file = false; int xid; int rc = -EIO; __u16 fid; struct super_block *sb; struct cifs_sb_info *cifs_sb; struct cifs_ntsd *pntsd = NULL; cFYI(1, ("get mode from ACL for %s", path)); if (inode == NULL) return NULL; xid = GetXid(); if (pfid == NULL) open_file = find_readable_file(CIFS_I(inode)); else fid = *pfid; sb = inode->i_sb; if (sb == NULL) { FreeXid(xid); return NULL; } cifs_sb = CIFS_SB(sb); if (open_file) { unlock_file = true; fid = open_file->netfid; } else if (pfid == NULL) { int oplock = 0; /* open file */ rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, READ_CONTROL, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc != 0) { cERROR(1, ("Unable to open file to get ACL")); FreeXid(xid); return NULL; } } rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen)); if (unlock_file == true) /* find_readable_file increments ref count */ atomic_dec(&open_file->wrtPending); else if (pfid == NULL) /* if opened above we have to close the handle */ CIFSSMBClose(xid, cifs_sb->tcon, fid); /* else handle was passed in by caller */ FreeXid(xid); return pntsd; } /* Set an ACL on the server */ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, struct inode *inode, const char *path) { struct cifsFileInfo *open_file; bool unlock_file = false; int xid; int rc = -EIO; __u16 fid; struct super_block *sb; struct cifs_sb_info *cifs_sb; cFYI(DBG2, ("set ACL for %s from mode 0x%x", path, inode->i_mode)); if (!inode) return rc; sb = inode->i_sb; if (sb == NULL) return rc; cifs_sb = CIFS_SB(sb); xid = GetXid(); open_file = find_readable_file(CIFS_I(inode)); if (open_file) { unlock_file = true; fid = open_file->netfid; } else { int oplock = 0; /* open file */ rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, WRITE_DAC, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc != 0) { cERROR(1, ("Unable to open file to set ACL")); FreeXid(xid); return rc; } } rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen); cFYI(DBG2, ("SetCIFSACL rc = %d", rc)); if (unlock_file) atomic_dec(&open_file->wrtPending); else CIFSSMBClose(xid, cifs_sb->tcon, fid); FreeXid(xid); return rc; } /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */ void acl_to_uid_mode(struct inode *inode, const char *path, const __u16 *pfid) { struct cifs_ntsd *pntsd = NULL; u32 acllen = 0; int rc = 0; cFYI(DBG2, ("converting ACL to mode for %s", path)); pntsd = get_cifs_acl(&acllen, inode, path, pfid); /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ if (pntsd) rc = parse_sec_desc(pntsd, acllen, inode); if (rc) cFYI(1, ("parse sec desc failed rc = %d", rc)); kfree(pntsd); return; } /* Convert mode bits to an ACL so we can update the ACL on the server */ int mode_to_acl(struct inode *inode, const char *path, __u64 nmode) { int rc = 0; __u32 secdesclen = 0; struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ cFYI(DBG2, ("set ACL from mode for %s", path)); /* Get the security descriptor */ pntsd = get_cifs_acl(&secdesclen, inode, path, NULL); /* Add three ACEs for owner, group, everyone getting rid of other ACEs as chmod disables ACEs and set the security descriptor */ if (pntsd) { /* allocate memory for the smb header, set security descriptor request security descriptor parameters, and secuirty descriptor itself */ secdesclen = secdesclen < DEFSECDESCLEN ? DEFSECDESCLEN : secdesclen; pnntsd = kmalloc(secdesclen, GFP_KERNEL); if (!pnntsd) { cERROR(1, ("Unable to allocate security descriptor")); kfree(pntsd); return -ENOMEM; } rc = build_sec_desc(pntsd, pnntsd, inode, nmode); cFYI(DBG2, ("build_sec_desc rc: %d", rc)); if (!rc) { /* Set the security descriptor */ rc = set_cifs_acl(pnntsd, secdesclen, inode, path); cFYI(DBG2, ("set_cifs_acl rc: %d", rc)); } kfree(pnntsd); kfree(pntsd); } return rc; } #endif /* CONFIG_CIFS_EXPERIMENTAL */ cifs-test-base/cifsacl.h0000644000175000017500000000454511117756171015035 0ustar stevefstevef/* * 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 NUM_SUBAUTHS 5 /* number of sub authority fields */ #define NUM_WK_SIDS 7 /* number of well known sids */ #define SIDNAMELENGTH 20 /* long enough for the ones we care about */ #define DEFSECDESCLEN 192 /* sec desc len contaiting a dacl with three aces */ #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 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[6]; __le32 sub_auth[5]; /* sub_auth[num_subauth] */ } __attribute__((packed)); 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)); struct cifs_wksid { struct cifs_sid cifssid; char sidname[SIDNAMELENGTH]; } __attribute__((packed)); #ifdef CONFIG_CIFS_EXPERIMENTAL extern int match_sid(struct cifs_sid *); extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *); #endif /* CONFIG_CIFS_EXPERIMENTAL */ #endif /* _CIFSACL_H */ cifs-test-base/cifs_debug.c0000644000175000017500000005247211117756171015520 0ustar stevefstevef/* * 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 #include #include #include #include #include #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_DEBUG2 void cifs_dump_detail(struct smb_hdr *smb) { cERROR(1, ("Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d", smb->Command, smb->Status.CifsError, smb->Flags, smb->Flags2, smb->Mid, smb->Pid)); cERROR(1, ("smb buf %p len %d", smb, smbCalcSize_LE(smb))); } void cifs_dump_mids(struct TCP_Server_Info *server) { struct list_head *tmp; struct mid_q_entry *mid_entry; if (server == NULL) return; cERROR(1, ("Dump pending requests:")); spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); cERROR(1, ("State: %d Cmd: %d Pid: %d Tsk: %p Mid %d", mid_entry->midState, (int)mid_entry->command, mid_entry->pid, mid_entry->tsk, mid_entry->mid)); #ifdef CONFIG_CIFS_STATS2 cERROR(1, ("IsLarge: %d buf: %p time rcv: %ld now: %ld", mid_entry->largeBuf, mid_entry->resp_buf, mid_entry->when_received, jiffies)); #endif /* STATS2 */ cERROR(1, ("IsMult: %d IsEnd: %d", 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 cifsSesInfo *ses; struct cifsTconInfo *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, "Active VFS Requests: %d\n", GlobalTotalActiveXid); seq_printf(m, "Servers:"); i = 0; read_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 cifsSesInfo, 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->secMode, atomic_read(&server->inFlight)); #ifdef CONFIG_CIFS_STATS2 seq_printf(m, " In Send: %d In MaxReq Wait: %d", atomic_read(&server->inSend), 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 cifsTconInfo, 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" "\nPathComponentMax: %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 (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 tsk: %p mid %d\n", mid_entry->midState, (int)mid_entry->command, mid_entry->pid, mid_entry->tsk, mid_entry->mid); } spin_unlock(&GlobalMid_Lock); } } read_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 cifsSesInfo *ses; struct cifsTconInfo *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 */ read_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 cifsSesInfo, smb_ses_list); list_for_each(tmp3, &ses->tcon_list) { tcon = list_entry(tmp3, struct cifsTconInfo, tcon_list); atomic_set(&tcon->num_smbs_sent, 0); atomic_set(&tcon->num_writes, 0); atomic_set(&tcon->num_reads, 0); atomic_set(&tcon->num_oplock_brks, 0); atomic_set(&tcon->num_opens, 0); atomic_set(&tcon->num_closes, 0); atomic_set(&tcon->num_deletes, 0); atomic_set(&tcon->num_mkdirs, 0); atomic_set(&tcon->num_rmdirs, 0); atomic_set(&tcon->num_renames, 0); atomic_set(&tcon->num_t2renames, 0); atomic_set(&tcon->num_ffirst, 0); atomic_set(&tcon->num_fnext, 0); atomic_set(&tcon->num_fclose, 0); atomic_set(&tcon->num_hardlinks, 0); atomic_set(&tcon->num_symlinks, 0); atomic_set(&tcon->num_locks, 0); } } } read_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 cifsSesInfo *ses; struct cifsTconInfo *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", midCount.counter); 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; read_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 cifsSesInfo, smb_ses_list); list_for_each(tmp3, &ses->tcon_list) { tcon = list_entry(tmp3, struct cifsTconInfo, 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 Oplock Breaks: %d", atomic_read(&tcon->num_smbs_sent), atomic_read(&tcon->num_oplock_brks)); seq_printf(m, "\nReads: %d Bytes: %lld", atomic_read(&tcon->num_reads), (long long)(tcon->bytes_read)); seq_printf(m, "\nWrites: %d Bytes: %lld", atomic_read(&tcon->num_writes), (long long)(tcon->bytes_written)); seq_printf(m, "\nLocks: %d HardLinks: %d " "Symlinks: %d", atomic_read(&tcon->num_locks), atomic_read(&tcon->num_hardlinks), atomic_read(&tcon->num_symlinks)); seq_printf(m, "\nOpens: %d Closes: %d" "Deletes: %d", atomic_read(&tcon->num_opens), atomic_read(&tcon->num_closes), atomic_read(&tcon->num_deletes)); seq_printf(m, "\nMkdirs: %d Rmdirs: %d", atomic_read(&tcon->num_mkdirs), atomic_read(&tcon->num_rmdirs)); seq_printf(m, "\nRenames: %d T2 Renames %d", atomic_read(&tcon->num_renames), atomic_read(&tcon->num_t2renames)); seq_printf(m, "\nFindFirst: %d FNext %d " "FClose %d", atomic_read(&tcon->num_ffirst), atomic_read(&tcon->num_fnext), atomic_read(&tcon->num_fclose)); } } } read_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_oplock_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_multiuser_mount_proc_fops; static const struct file_operations cifs_security_flags_proc_fops; static const struct file_operations cifs_experimental_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_fs_cifs->owner = THIS_MODULE; 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("OplockEnabled", 0, proc_fs_cifs, &cifs_oplock_proc_fops); proc_create("Experimental", 0, proc_fs_cifs, &cifs_experimental_proc_fops); proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs, &cifs_linux_ext_proc_fops); proc_create("MultiuserMount", 0, proc_fs_cifs, &cifs_multiuser_mount_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("MultiuserMount", proc_fs_cifs); remove_proc_entry("OplockEnabled", proc_fs_cifs); remove_proc_entry("SecurityFlags", proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); remove_proc_entry("Experimental", 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_oplock_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", oplockEnabled); return 0; } static int cifs_oplock_proc_open(struct inode *inode, struct file *file) { return single_open(file, cifs_oplock_proc_show, NULL); } static ssize_t cifs_oplock_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') oplockEnabled = 0; else if (c == '1' || c == 'y' || c == 'Y') oplockEnabled = 1; return count; } static const struct file_operations cifs_oplock_proc_fops = { .owner = THIS_MODULE, .open = cifs_oplock_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = cifs_oplock_proc_write, }; static int cifs_experimental_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", experimEnabled); return 0; } static int cifs_experimental_proc_open(struct inode *inode, struct file *file) { return single_open(file, cifs_experimental_proc_show, NULL); } static ssize_t cifs_experimental_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') experimEnabled = 0; else if (c == '1' || c == 'y' || c == 'Y') experimEnabled = 1; else if (c == '2') experimEnabled = 2; return count; } static const struct file_operations cifs_experimental_proc_fops = { .owner = THIS_MODULE, .open = cifs_experimental_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = cifs_experimental_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_multiuser_mount_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", multiuser_mount); return 0; } static int cifs_multiuser_mount_proc_open(struct inode *inode, struct file *fh) { return single_open(fh, cifs_multiuser_mount_proc_show, NULL); } static ssize_t cifs_multiuser_mount_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') multiuser_mount = 0; else if (c == '1' || c == 'y' || c == 'Y') multiuser_mount = 1; return count; } static const struct file_operations cifs_multiuser_mount_proc_fops = { .owner = THIS_MODULE, .open = cifs_multiuser_mount_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = cifs_multiuser_mount_proc_write, }; static int cifs_security_flags_proc_show(struct seq_file *m, void *v) { seq_printf(m, "0x%x\n", extended_security); 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); } static ssize_t cifs_security_flags_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { 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') { extended_security = CIFSSEC_DEF; /* default */ return count; } else if (c == '1' || c == 'y' || c == 'Y') { extended_security = CIFSSEC_MAX; return count; } else if (!isdigit(c)) { cERROR(1, ("invalid flag %c", c)); return -EINVAL; } } /* else we have a number */ flags = simple_strtoul(flags_string, NULL, 0); cFYI(1, ("sec flags 0x%x", flags)); if (flags <= 0) { cERROR(1, ("invalid security flags %s", flags_string)); return -EINVAL; } if (flags & ~CIFSSEC_MASK) { cERROR(1, ("attempt to set unsupported security flags 0x%x", flags & ~CIFSSEC_MASK)); return -EINVAL; } /* flags look ok - update the global security flags for cifs module */ extended_security = flags; if (extended_security & CIFSSEC_MUST_SIGN) { /* requiring signing implies signing is allowed */ extended_security |= CIFSSEC_MAY_SIGN; cFYI(1, ("packet signing now required")); } else if ((extended_security & CIFSSEC_MAY_SIGN) == 0) { cFYI(1, ("packet signing disabled")); } /* 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 */ cifs-test-base/cifs_debug.h0000644000175000017500000000443111117756171015515 0ustar stevefstevef/* * * 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 * */ #define CIFS_DEBUG /* BB temporary */ #ifndef _H_CIFS_DEBUG #define _H_CIFS_DEBUG void cifs_dump_mem(char *label, void *data, int length); #ifdef CONFIG_CIFS_DEBUG2 #define DBG2 2 void cifs_dump_detail(struct smb_hdr *); void cifs_dump_mids(struct TCP_Server_Info *); #else #define DBG2 0 #endif extern int traceSMB; /* flag which enables the function below */ void dump_smb(struct smb_hdr *, int); #define CIFS_INFO 0x01 #define CIFS_RC 0x02 #define CIFS_TIMER 0x04 /* * debug ON * -------- */ #ifdef CIFS_DEBUG /* information message: e.g., configuration, major event */ extern int cifsFYI; #define cifsfyi(format,arg...) if (cifsFYI & CIFS_INFO) printk(KERN_DEBUG " " __FILE__ ": " format "\n" "" , ## arg) #define cFYI(button,prspec) if (button) cifsfyi prspec #define cifswarn(format, arg...) printk(KERN_WARNING ": " format "\n" , ## arg) /* debug event message: */ extern int cifsERROR; #define cEVENT(format,arg...) if (cifsERROR) printk(KERN_EVENT __FILE__ ": " format "\n" , ## arg) /* error event message: e.g., i/o error */ #define cifserror(format,arg...) if (cifsERROR) printk(KERN_ERR " CIFS VFS: " format "\n" "" , ## arg) #define cERROR(button, prspec) if (button) cifserror prspec /* * debug OFF * --------- */ #else /* _CIFS_DEBUG */ #define cERROR(button, prspec) #define cEVENT(format, arg...) #define cFYI(button, prspec) #define cifserror(format, arg...) #endif /* _CIFS_DEBUG */ #endif /* _H_CIFS_DEBUG */ cifs-test-base/cifs_dfs_ref.c0000644000175000017500000002273211120010624016012 0ustar stevefstevef/* * 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 #include #include #include #include #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(&cifs_dfs_automount_task); flush_scheduled_work(); } /** * cifs_get_share_name - extracts share name from UNC * @node_name: pointer to UNC string * * Extracts sharename form full UNC. * i.e. strips from UNC trailing path that is not part of share * name and fixup missing '\' in the begining of DFS node refferal * if neccessary. * Returns pointer to share name on success or NULL on error. * Caller is responsible for freeing returned string. */ static char *cifs_get_share_name(const char *node_name) { int len; char *UNC; char *pSep; len = strlen(node_name); UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */, GFP_KERNEL); if (!UNC) return NULL; /* get share name and server name */ if (node_name[1] != '\\') { UNC[0] = '\\'; strncpy(UNC+1, node_name, len); len++; UNC[len] = 0; } else { strncpy(UNC, node_name, len); UNC[len] = 0; } /* find server name end */ pSep = memchr(UNC+2, '\\', len-2); if (!pSep) { cERROR(1, ("%s: no server name end in node name: %s", __func__, node_name)); kfree(UNC); return NULL; } /* find sharename end */ pSep++; pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC)); if (pSep) { /* trim path up to sharename end * now we have share name in UNC */ *pSep = 0; } return UNC; } /** * compose_mount_options - creates mount options for refferral * @sb_mountdata: parent/root DFS mount options (template) * @dentry: point where we are going to mount * @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. */ static char *compose_mount_options(const char *sb_mountdata, struct dentry *dentry, const struct dfs_info3_param *ref, char **devname) { int rc; char *mountdata; int md_len; char *tkn_e; char *srvIP = NULL; char sep = ','; int off, noff; char *fullpath; if (sb_mountdata == NULL) return ERR_PTR(-EINVAL); *devname = cifs_get_share_name(ref->node_name); rc = dns_resolve_server_name_to_ip(*devname, &srvIP); if (rc != 0) { cERROR(1, ("%s: Failed to resolve server part of %s to IP", __func__, *devname)); mountdata = ERR_PTR(rc); goto compose_mount_options_out; } /* md_len = strlen(...) + 12 for 'sep+prefixpath=' * assuming that we have 'unc=' and 'ip=' in * the original sb_mountdata */ md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref->node_name) + 12; mountdata = kzalloc(md_len+1, GFP_KERNEL); if (mountdata == NULL) { mountdata = ERR_PTR(-ENOMEM); goto compose_mount_options_out; } /* 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); strncat(mountdata, &sep, 1); strcat(mountdata, "unc="); strcat(mountdata, *devname); /* find & copy prefixpath */ tkn_e = strchr(ref->node_name + 2, '\\'); if (tkn_e == NULL) /* invalid unc, missing share name*/ goto compose_mount_options_out; fullpath = build_path_from_dentry(dentry); tkn_e = strchr(tkn_e + 1, '\\'); if (tkn_e || strlen(fullpath) - (ref->path_consumed)) { strncat(mountdata, &sep, 1); strcat(mountdata, "prefixpath="); if (tkn_e) strcat(mountdata, tkn_e + 1); strcat(mountdata, fullpath + (ref->path_consumed)); } kfree(fullpath); /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ compose_mount_options_out: kfree(srvIP); return mountdata; } static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry, const struct dfs_info3_param *ref) { struct cifs_sb_info *cifs_sb; struct vfsmount *mnt; char *mountdata; char *devname = NULL; cifs_sb = CIFS_SB(dentry->d_inode->i_sb); mountdata = compose_mount_options(cifs_sb->mountdata, dentry, 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 int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, struct list_head *mntlist) { /* stolen from afs code */ int err; mntget(newmnt); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) err = do_add_mount(newmnt, nd, nd->path.mnt->mnt_flags, mntlist); #else err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags, mntlist); #endif switch (err) { case 0: path_put(&nd->path); nd->path.mnt = newmnt; nd->path.dentry = dget(newmnt->mnt_root); schedule_delayed_work(&cifs_dfs_automount_task, cifs_dfs_mountpoint_expiry_timeout); break; case -EBUSY: /* someone else made a mount here whilst we were busy */ while (d_mountpoint(nd->path.dentry) && follow_down(&nd->path.mnt, &nd->path.dentry)) ; err = 0; default: mntput(newmnt); break; } return err; } static void dump_referral(const struct dfs_info3_param *ref) { cFYI(1, ("DFS: ref path: %s", ref->path_name)); cFYI(1, ("DFS: node path: %s", ref->node_name)); cFYI(1, ("DFS: fl: %hd, srv_type: %hd", ref->flags, ref->server_type)); cFYI(1, ("DFS: ref_flags: %hd, path_consumed: %hd", ref->ref_flag, ref->path_consumed)); } static void* cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) { struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; struct cifs_sb_info *cifs_sb; struct cifsSesInfo *ses; char *full_path = NULL; int xid, i; int rc = 0; struct vfsmount *mnt = ERR_PTR(-ENOENT); cFYI(1, ("in %s", __func__)); BUG_ON(IS_ROOT(dentry)); xid = GetXid(); dput(nd->path.dentry); nd->path.dentry = dget(dentry); cifs_sb = CIFS_SB(dentry->d_inode->i_sb); ses = cifs_sb->tcon->ses; if (!ses) { rc = -EINVAL; goto out_err; } full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; goto out_err; } rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); for (i = 0; i < num_referrals; i++) { dump_referral(referrals+i); /* connect to a storage node */ if (referrals[i].flags & DFSREF_STORAGE_SERVER) { int len; len = strlen(referrals[i].node_name); if (len < 2) { cERROR(1, ("%s: Net Address path too short: %s", __func__, referrals[i].node_name)); rc = -EINVAL; goto out_err; } mnt = cifs_dfs_do_refmount(nd->path.mnt, nd->path.dentry, referrals + i); cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, referrals[i].node_name, mnt)); /* complete mount procedure if we accured submount */ if (!IS_ERR(mnt)) break; } } /* we need it cause for() above could exit without valid submount */ rc = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; nd->path.mnt->mnt_flags |= MNT_SHRINKABLE; rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list); out: FreeXid(xid); free_dfs_info_array(referrals, num_referrals); kfree(full_path); cFYI(1, ("leaving %s" , __func__)); return ERR_PTR(rc); out_err: path_put(&nd->path); goto out; } struct inode_operations cifs_dfs_referral_inode_operations = { .follow_link = cifs_dfs_follow_mountpoint, }; cifs-test-base/cifs_dfs_ref.c~0000644000175000017500000002253211117756171016232 0ustar stevefstevef/* * 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 #include #include #include #include #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(&cifs_dfs_automount_task); flush_scheduled_work(); } /** * cifs_get_share_name - extracts share name from UNC * @node_name: pointer to UNC string * * Extracts sharename form full UNC. * i.e. strips from UNC trailing path that is not part of share * name and fixup missing '\' in the begining of DFS node refferal * if neccessary. * Returns pointer to share name on success or NULL on error. * Caller is responsible for freeing returned string. */ static char *cifs_get_share_name(const char *node_name) { int len; char *UNC; char *pSep; len = strlen(node_name); UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */, GFP_KERNEL); if (!UNC) return NULL; /* get share name and server name */ if (node_name[1] != '\\') { UNC[0] = '\\'; strncpy(UNC+1, node_name, len); len++; UNC[len] = 0; } else { strncpy(UNC, node_name, len); UNC[len] = 0; } /* find server name end */ pSep = memchr(UNC+2, '\\', len-2); if (!pSep) { cERROR(1, ("%s: no server name end in node name: %s", __func__, node_name)); kfree(UNC); return NULL; } /* find sharename end */ pSep++; pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC)); if (pSep) { /* trim path up to sharename end * now we have share name in UNC */ *pSep = 0; } return UNC; } /** * compose_mount_options - creates mount options for refferral * @sb_mountdata: parent/root DFS mount options (template) * @dentry: point where we are going to mount * @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. */ static char *compose_mount_options(const char *sb_mountdata, struct dentry *dentry, const struct dfs_info3_param *ref, char **devname) { int rc; char *mountdata; int md_len; char *tkn_e; char *srvIP = NULL; char sep = ','; int off, noff; char *fullpath; if (sb_mountdata == NULL) return ERR_PTR(-EINVAL); *devname = cifs_get_share_name(ref->node_name); rc = dns_resolve_server_name_to_ip(*devname, &srvIP); if (rc != 0) { cERROR(1, ("%s: Failed to resolve server part of %s to IP", __func__, *devname)); mountdata = ERR_PTR(rc); goto compose_mount_options_out; } /* md_len = strlen(...) + 12 for 'sep+prefixpath=' * assuming that we have 'unc=' and 'ip=' in * the original sb_mountdata */ md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref->node_name) + 12; mountdata = kzalloc(md_len+1, GFP_KERNEL); if (mountdata == NULL) { mountdata = ERR_PTR(-ENOMEM); goto compose_mount_options_out; } /* 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); strncat(mountdata, &sep, 1); strcat(mountdata, "unc="); strcat(mountdata, *devname); /* find & copy prefixpath */ tkn_e = strchr(ref->node_name + 2, '\\'); if (tkn_e == NULL) /* invalid unc, missing share name*/ goto compose_mount_options_out; fullpath = build_path_from_dentry(dentry); tkn_e = strchr(tkn_e + 1, '\\'); if (tkn_e || strlen(fullpath) - (ref->path_consumed)) { strncat(mountdata, &sep, 1); strcat(mountdata, "prefixpath="); if (tkn_e) strcat(mountdata, tkn_e + 1); strcat(mountdata, fullpath + (ref->path_consumed)); } kfree(fullpath); /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ compose_mount_options_out: kfree(srvIP); return mountdata; } static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry, const struct dfs_info3_param *ref) { struct cifs_sb_info *cifs_sb; struct vfsmount *mnt; char *mountdata; char *devname = NULL; cifs_sb = CIFS_SB(dentry->d_inode->i_sb); mountdata = compose_mount_options(cifs_sb->mountdata, dentry, 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 int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, struct list_head *mntlist) { /* stolen from afs code */ int err; mntget(newmnt); err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags, mntlist); switch (err) { case 0: path_put(&nd->path); nd->path.mnt = newmnt; nd->path.dentry = dget(newmnt->mnt_root); schedule_delayed_work(&cifs_dfs_automount_task, cifs_dfs_mountpoint_expiry_timeout); break; case -EBUSY: /* someone else made a mount here whilst we were busy */ while (d_mountpoint(nd->path.dentry) && follow_down(&nd->path.mnt, &nd->path.dentry)) ; err = 0; default: mntput(newmnt); break; } return err; } static void dump_referral(const struct dfs_info3_param *ref) { cFYI(1, ("DFS: ref path: %s", ref->path_name)); cFYI(1, ("DFS: node path: %s", ref->node_name)); cFYI(1, ("DFS: fl: %hd, srv_type: %hd", ref->flags, ref->server_type)); cFYI(1, ("DFS: ref_flags: %hd, path_consumed: %hd", ref->ref_flag, ref->path_consumed)); } static void* cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) { struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; struct cifs_sb_info *cifs_sb; struct cifsSesInfo *ses; char *full_path = NULL; int xid, i; int rc = 0; struct vfsmount *mnt = ERR_PTR(-ENOENT); cFYI(1, ("in %s", __func__)); BUG_ON(IS_ROOT(dentry)); xid = GetXid(); dput(nd->path.dentry); nd->path.dentry = dget(dentry); cifs_sb = CIFS_SB(dentry->d_inode->i_sb); ses = cifs_sb->tcon->ses; if (!ses) { rc = -EINVAL; goto out_err; } full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; goto out_err; } rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); for (i = 0; i < num_referrals; i++) { dump_referral(referrals+i); /* connect to a storage node */ if (referrals[i].flags & DFSREF_STORAGE_SERVER) { int len; len = strlen(referrals[i].node_name); if (len < 2) { cERROR(1, ("%s: Net Address path too short: %s", __func__, referrals[i].node_name)); rc = -EINVAL; goto out_err; } mnt = cifs_dfs_do_refmount(nd->path.mnt, nd->path.dentry, referrals + i); cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, referrals[i].node_name, mnt)); /* complete mount procedure if we accured submount */ if (!IS_ERR(mnt)) break; } } /* we need it cause for() above could exit without valid submount */ rc = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; nd->path.mnt->mnt_flags |= MNT_SHRINKABLE; rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list); out: FreeXid(xid); free_dfs_info_array(referrals, num_referrals); kfree(full_path); cFYI(1, ("leaving %s" , __func__)); return ERR_PTR(rc); out_err: path_put(&nd->path); goto out; } struct inode_operations cifs_dfs_referral_inode_operations = { .follow_link = cifs_dfs_follow_mountpoint, }; cifs-test-base/cifsencrypt.c0000644000175000017500000003070511117756171015752 0ustar stevefstevef/* * fs/cifs/cifsencrypt.c * * Copyright (C) International Business Machines Corp., 2005,2006 * 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 #include "cifspdu.h" #include "cifsglob.h" #include "cifs_debug.h" #include "md5.h" #include "cifs_unicode.h" #include "cifsproto.h" #include #include /* 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 */ /* Note that the smb header signature field on input contains the sequence number before this function is called */ extern void mdfour(unsigned char *out, unsigned char *in, int n); extern void E_md4hash(const unsigned char *passwd, unsigned char *p16); extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24); static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, const struct mac_key *key, char *signature) { struct MD5Context context; if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL)) return -EINVAL; MD5Init(&context); MD5Update(&context, (char *)&key->data, key->len); MD5Update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length); MD5Final(signature, &context); return 0; } int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, __u32 *pexpected_response_sequence_number) { int rc = 0; char smb_signature[20]; if ((cifs_pdu == NULL) || (server == NULL)) return -EINVAL; if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) return rc; spin_lock(&GlobalMid_Lock); 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++; spin_unlock(&GlobalMid_Lock); rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key, smb_signature); if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); else memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); return rc; } static int cifs_calc_signature2(const struct kvec *iov, int n_vec, const struct mac_key *key, char *signature) { struct MD5Context context; int i; if ((iov == NULL) || (signature == NULL) || (key == NULL)) return -EINVAL; MD5Init(&context); MD5Update(&context, (char *)&key->data, key->len); for (i = 0; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) { cERROR(1, ("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 */ MD5Update(&context, iov[0].iov_base+4, iov[0].iov_len-4); } else MD5Update(&context, iov[i].iov_base, iov[i].iov_len); } MD5Final(signature, &context); return 0; } int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, __u32 *pexpected_response_sequence_number) { int rc = 0; char smb_signature[20]; struct smb_hdr *cifs_pdu = iov[0].iov_base; if ((cifs_pdu == NULL) || (server == NULL)) return -EINVAL; if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) return rc; spin_lock(&GlobalMid_Lock); 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++; spin_unlock(&GlobalMid_Lock); rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key, smb_signature); if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); else memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); return rc; } int cifs_verify_signature(struct smb_hdr *cifs_pdu, const struct mac_key *mac_key, __u32 expected_sequence_number) { unsigned int rc; char server_response_sig[8]; char what_we_think_sig_should_be[20]; if ((cifs_pdu == NULL) || (mac_key == NULL)) return -EINVAL; if (cifs_pdu->Command == SMB_COM_NEGOTIATE) 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) cFYI(1, ("dummy signature received for smb command 0x%x", 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; rc = cifs_calculate_signature(cifs_pdu, mac_key, what_we_think_sig_should_be); 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; } /* We fill in key by putting in 40 byte array which was allocated by caller */ int cifs_calculate_mac_key(struct mac_key *key, const char *rn, const char *password) { char temp_key[16]; if ((key == NULL) || (rn == NULL)) return -EINVAL; E_md4hash(password, temp_key); mdfour(key->data.ntlm, temp_key, 16); memcpy(key->data.ntlm+16, rn, CIFS_SESS_KEY_SIZE); key->len = 40; return 0; } int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses, const struct nls_table *nls_info) { char temp_hash[16]; struct HMACMD5Context ctx; char *ucase_buf; __le16 *unicode_buf; unsigned int i, user_name_len, dom_name_len; if (ses == NULL) return -EINVAL; E_md4hash(ses->password, temp_hash); hmac_md5_init_limK_to_64(temp_hash, 16, &ctx); user_name_len = strlen(ses->userName); if (user_name_len > MAX_USERNAME_SIZE) return -EINVAL; if (ses->domainName == NULL) return -EINVAL; /* BB should we use CIFS_LINUX_DOM */ dom_name_len = strlen(ses->domainName); if (dom_name_len > MAX_USERNAME_SIZE) return -EINVAL; ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL); if (ucase_buf == NULL) return -ENOMEM; unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL); if (unicode_buf == NULL) { kfree(ucase_buf); return -ENOMEM; } for (i = 0; i < user_name_len; i++) ucase_buf[i] = nls_info->charset2upper[(int)ses->userName[i]]; ucase_buf[i] = 0; user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); unicode_buf[user_name_len] = 0; user_name_len++; for (i = 0; i < dom_name_len; i++) ucase_buf[i] = nls_info->charset2upper[(int)ses->domainName[i]]; ucase_buf[i] = 0; dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); unicode_buf[user_name_len + dom_name_len] = 0; hmac_md5_update((const unsigned char *) unicode_buf, (user_name_len+dom_name_len)*2, &ctx); hmac_md5_final(ses->server->ntlmv2_hash, &ctx); kfree(ucase_buf); kfree(unicode_buf); return 0; } #ifdef CONFIG_CIFS_WEAK_PW_HASH void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, char *lnm_session_key) { int i; 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 && extended_security & CIFSSEC_MAY_PLNTXT) { memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE); memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE); return; } /* 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]); SMBencrypt(password_with_pad, cryptkey, lnm_session_key); /* clear password before we return/free memory */ memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); } #endif /* CIFS_WEAK_PW_HASH */ static int calc_ntlmv2_hash(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int rc = 0; int len; char nt_hash[16]; struct HMACMD5Context *pctxt; wchar_t *user; wchar_t *domain; pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL); if (pctxt == NULL) return -ENOMEM; /* calculate md4 hash of password */ E_md4hash(ses->password, nt_hash); /* convert Domainname to unicode and uppercase */ hmac_md5_init_limK_to_64(nt_hash, 16, pctxt); /* convert ses->userName to unicode and uppercase */ len = strlen(ses->userName); user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) goto calc_exit_2; len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); UniStrupr(user); hmac_md5_update((char *)user, 2*len, pctxt); /* convert ses->domainName to unicode and uppercase */ if (ses->domainName) { len = strlen(ses->domainName); domain = kmalloc(2 + (len * 2), GFP_KERNEL); if (domain == NULL) goto calc_exit_1; len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, nls_cp); /* the following line was removed since it didn't work well with lower cased domain name that passed as an option. Maybe converting the domain name earlier makes sense */ /* UniStrupr(domain); */ hmac_md5_update((char *)domain, 2*len, pctxt); kfree(domain); } calc_exit_1: kfree(user); calc_exit_2: /* BB FIXME what about bytes 24 through 40 of the signing key? compare with the NTLM example */ hmac_md5_final(ses->server->ntlmv2_hash, pctxt); return rc; } void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, const struct nls_table *nls_cp) { int rc; struct ntlmv2_resp *buf = (struct ntlmv2_resp *)resp_buf; struct HMACMD5Context context; 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; buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); buf->names[0].length = 0; buf->names[1].type = 0; buf->names[1].length = 0; /* calculate buf->ntlmv2_hash */ rc = calc_ntlmv2_hash(ses, nls_cp); if (rc) cERROR(1, ("could not get v2 hash rc %d", rc)); CalcNTLMv2_response(ses, resp_buf); /* now calculate the MAC key for NTLMv2 */ hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); hmac_md5_update(resp_buf, 16, &context); hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context); memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf, sizeof(struct ntlmv2_resp)); ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp); } void CalcNTLMv2_response(const struct cifsSesInfo *ses, char *v2_session_response) { struct HMACMD5Context context; /* rest of v2 struct already generated */ memcpy(v2_session_response + 8, ses->server->cryptKey, 8); hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); hmac_md5_update(v2_session_response+8, sizeof(struct ntlmv2_resp) - 8, &context); hmac_md5_final(v2_session_response, &context); /* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ } cifs-test-base/cifsencrypt.h0000644000175000017500000000235011117756171015752 0ustar stevefstevef/* * fs/cifs/cifsencrypt.h * * Copyright (c) International Business Machines Corp., 2005 * Author(s): Steve French (sfrench@us.ibm.com) * * Externs for misc. small encryption routines * so we do not have to put them in cifsproto.h * * 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 */ /* md4.c */ extern void mdfour(unsigned char *out, unsigned char *in, int n); /* smbdes.c */ extern void E_P16(unsigned char *p14, unsigned char *p16); extern void E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24); cifs-test-base/cifsfs.c0000644000175000017500000010120711120014741014652 0ustar stevefstevef/* * 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 #include #include #include #include #include #include #include #include #include #include #include #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 #include #include "dns_resolve.h" #include "cifs_spnego.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ #ifdef CONFIG_CIFS_QUOTA static struct quotactl_ops cifs_quotactl_ops; #endif /* QUOTA */ int cifsFYI = 0; int cifsERROR = 1; int traceSMB = 0; unsigned int oplockEnabled = 1; unsigned int experimEnabled = 0; unsigned int linuxExtEnabled = 1; unsigned int lookupCacheEnabled = 1; unsigned int multiuser_mount = 0; unsigned int extended_security = CIFSSEC_DEF; /* unsigned int ntlmv2_support = 0; */ unsigned int sign_CIFS_PDUs = 1; extern struct task_struct *oplockThread; /* remove sparse warning */ struct task_struct *oplockThread = NULL; /* extern struct task_struct * dnotifyThread; remove sparse warning */ #ifdef CONFIG_CIFS_EXPERIMENTAL static struct task_struct *dnotifyThread = NULL; #endif static const struct super_operations cifs_super_ops; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; module_param(CIFSMaxBufSize, int, 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, int, 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, int, 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, int, 0); MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. " "Default: 50 Range: 2 to 256"); extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; extern struct kmem_cache *cifs_oplock_cachep; static int cifs_read_super(struct super_block *sb, void *data, const char *devname, int silent) { struct inode *inode; struct cifs_sb_info *cifs_sb; int rc = 0; /* BB should we make this contingent on mount parm? */ sb->s_flags |= MS_NODIRATIME | MS_NOATIME; sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); cifs_sb = CIFS_SB(sb); if (cifs_sb == NULL) return -ENOMEM; #ifdef CONFIG_CIFS_DFS_UPCALL /* copy mount params to sb for use in submounts */ /* BB: should we move this after the mount so we * do not have to do the copy on failed mounts? * BB: May be it is better to do simple copy before * complex operation (mount), and in case of fail * just exit instead of doing mount and attempting * undo it if this copy fails?*/ if (data) { int len = strlen(data); cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL); if (cifs_sb->mountdata == NULL) { kfree(sb->s_fs_info); sb->s_fs_info = NULL; return -ENOMEM; } strncpy(cifs_sb->mountdata, data, len + 1); cifs_sb->mountdata[len] = '\0'; } #endif rc = cifs_mount(sb, cifs_sb, data, devname); if (rc) { if (!silent) cERROR(1, ("cifs_mount failed w/return code = %d", rc)); goto out_mount_failed; } sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_op = &cifs_super_ops; /* if (cifs_sb->tcon->ses->server->maxBuf > MAX_CIFS_HDR_SIZE + 512) sb->s_blocksize = cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */ #ifdef CONFIG_CIFS_QUOTA sb->s_qcop = &cifs_quotactl_ops; #endif sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ inode = cifs_iget(sb, ROOT_I); if (IS_ERR(inode)) { rc = PTR_ERR(inode); inode = NULL; goto out_no_root; } sb->s_root = d_alloc_root(inode); if (!sb->s_root) { rc = -ENOMEM; goto out_no_root; } #ifdef CONFIG_CIFS_EXPERIMENTAL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { cFYI(1, ("export ops supported")); sb->s_export_op = &cifs_export_ops; } #endif /* EXPERIMENTAL */ return 0; out_no_root: cERROR(1, ("cifs_read_super: get root inode failed")); if (inode) iput(inode); cifs_umount(sb, cifs_sb); out_mount_failed: if (cifs_sb) { #ifdef CONFIG_CIFS_DFS_UPCALL if (cifs_sb->mountdata) { kfree(cifs_sb->mountdata); cifs_sb->mountdata = NULL; } #endif if (cifs_sb->local_nls) unload_nls(cifs_sb->local_nls); kfree(cifs_sb); } return rc; } static void cifs_put_super(struct super_block *sb) { int rc = 0; struct cifs_sb_info *cifs_sb; cFYI(1, ("In cifs_put_super")); cifs_sb = CIFS_SB(sb); if (cifs_sb == NULL) { cFYI(1, ("Empty cifs superblock info passed to unmount")); return; } rc = cifs_umount(sb, cifs_sb); if (rc) cERROR(1, ("cifs_umount failed with return code %d", rc)); #ifdef CONFIG_CIFS_DFS_UPCALL if (cifs_sb->mountdata) { kfree(cifs_sb->mountdata); cifs_sb->mountdata = NULL; } #endif unload_nls(cifs_sb->local_nls); kfree(cifs_sb); return; } 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 cifsTconInfo *tcon = cifs_sb->tcon; int rc = -EOPNOTSUPP; int xid; xid = GetXid(); buf->f_type = CIFS_MAGIC_NUMBER; /* * 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 */ /* * 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); FreeXid(xid); return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd) #else static int cifs_permission(struct inode *inode, int mask) #endif { struct cifs_sb_info *cifs_sb; cifs_sb = CIFS_SB(inode->i_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) if ((mask & MAY_EXEC) && !execute_ok(inode)) return -EACCES; else #endif 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, NULL); } static struct kmem_cache *cifs_inode_cachep; static struct kmem_cache *cifs_req_cachep; static struct kmem_cache *cifs_mid_cachep; struct kmem_cache *cifs_oplock_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 */ atomic_set(&cifs_inode->inUse, 0); cifs_inode->time = 0; cifs_inode->write_behind_rc = 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_inode->clientCanCacheRead = false; cifs_inode->clientCanCacheAll = false; cifs_inode->delete_pending = false; cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ /* 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); return &cifs_inode->vfs_inode; } static void cifs_destroy_inode(struct inode *inode) { kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); } /* * 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 vfsmount *m) { struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; struct TCP_Server_Info *server; cifs_sb = CIFS_SB(m->mnt_sb); if (cifs_sb) { tcon = cifs_sb->tcon; if (tcon) { seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName); if (tcon->ses) { if (tcon->ses->userName) seq_printf(s, ",username=%s", tcon->ses->userName); if (tcon->ses->domainName) seq_printf(s, ",domain=%s", tcon->ses->domainName); server = tcon->ses->server; if (server) { seq_printf(s, ",addr="); switch (server->addr.sockAddr6. sin6_family) { case AF_INET6: seq_printf(s, NIP6_FMT, NIP6(server->addr.sockAddr6.sin6_addr)); break; case AF_INET: seq_printf(s, NIPQUAD_FMT, NIPQUAD(server->addr.sockAddr.sin_addr.s_addr)); break; } } } if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) || !(tcon->unix_ext)) seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) || !(tcon->unix_ext)) seq_printf(s, ",gid=%d", cifs_sb->mnt_gid); if (!tcon->unix_ext) { seq_printf(s, ",file_mode=0%o,dir_mode=0%o", cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode); } if (tcon->seal) seq_printf(s, ",seal"); if (tcon->nocase) seq_printf(s, ",nocase"); if (tcon->retry) seq_printf(s, ",hard"); } if (cifs_sb->prepath) seq_printf(s, ",prepath=%s", cifs_sb->prepath); 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_DIRECT_IO) seq_printf(s, ",directio"); 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 (m->mnt_sb->s_flags & MS_POSIXACL) seq_printf(s, ",acl"); seq_printf(s, ",rsize=%d", cifs_sb->rsize); seq_printf(s, ",wsize=%d", cifs_sb->wsize); } return 0; } #ifdef CONFIG_CIFS_QUOTA int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid, struct fs_disk_quota *pdquota) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); } else rc = -EIO; FreeXid(xid); return rc; } int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid, struct fs_disk_quota *pdquota) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); } else rc = -EIO; FreeXid(xid); return rc; } int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation)); } else rc = -EIO; FreeXid(xid); return rc; } int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("pqstats %p", qstats)); } else rc = -EIO; FreeXid(xid); return rc; } static struct quotactl_ops cifs_quotactl_ops = { .set_xquota = cifs_xquota_set, .get_xquota = cifs_xquota_get, .set_xstate = cifs_xstate_set, .get_xstate = cifs_xstate_get, }; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags) { struct cifs_sb_info *cifs_sb = CIFS_SB(vfsmnt->mnt_sb); #else static void cifs_umount_begin(struct super_block *sb) { struct cifs_sb_info *cifs_sb = CIFS_SB(sb); #endif struct cifsTconInfo *tcon; if (cifs_sb == NULL) return; tcon = cifs_sb->tcon; if (tcon == NULL) return; read_lock(&cifs_tcp_ses_lock); if (tcon->tc_count == 1) tcon->tidStatus = CifsExiting; read_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) { cFYI(1, ("wake up tasks now - umount begin not complete")); 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); } /* BB FIXME - finish add checks for tidStatus BB */ return; } #ifdef CONFIG_CIFS_STATS2 static int cifs_show_stats(struct seq_file *s, struct vfsmount *mnt) { /* BB FIXME */ return 0; } #endif static int cifs_remount(struct super_block *sb, int *flags, char *data) { *flags |= MS_NODIRATIME; return 0; } static const struct super_operations cifs_super_ops = { .put_super = cifs_put_super, .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, .destroy_inode = cifs_destroy_inode, /* .drop_inode = generic_delete_inode, .delete_inode = cifs_delete_inode, */ /* Do not need above two functions 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 }; static int cifs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { int rc; struct super_block *sb = sget(fs_type, NULL, set_anon_super, NULL); cFYI(1, ("Devname: %s flags: %d ", dev_name, flags)); if (IS_ERR(sb)) return PTR_ERR(sb); sb->s_flags = flags; rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0); if (rc) { up_write(&sb->s_umount); deactivate_super(sb); return rc; } sb->s_flags |= MS_ACTIVE; return simple_set_mnt(mnt, sb); } static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; ssize_t written; written = generic_file_aio_write(iocb, iov, nr_segs, pos); if (!CIFS_I(inode)->clientCanCacheAll) filemap_fdatawrite(inode->i_mapping); return written; } static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) { /* origin == SEEK_END => we must revalidate the cached file length */ if (origin == SEEK_END) { int retval; /* 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(file->f_path.dentry->d_inode)->time = 0; retval = cifs_revalidate(file->f_path.dentry); if (retval < 0) return (loff_t)retval; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) return remote_llseek(file, offset, origin); #else return generic_file_llseek_unlocked(file, offset, origin); #endif } #ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) { /* note that this is called by vfs setlease with the BKL held although I doubt that BKL is needed here in cifs */ struct inode *inode = file->f_path.dentry->d_inode; if (!(S_ISREG(inode->i_mode))) return -EINVAL; /* check if file is oplocked */ if (((arg == F_RDLCK) && (CIFS_I(inode)->clientCanCacheRead)) || ((arg == F_WRLCK) && (CIFS_I(inode)->clientCanCacheAll))) return generic_setlease(file, arg, lease); else if (CIFS_SB(inode->i_sb)->tcon->local_lease && !CIFS_I(inode)->clientCanCacheRead) /* 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; } #endif struct file_system_type cifs_fs_type = { .owner = THIS_MODULE, .name = "cifs", .get_sb = cifs_get_sb, .kill_sb = kill_anon_super, /* .fs_flags */ }; const struct inode_operations cifs_dir_inode_ops = { .create = cifs_create, .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 */ #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_file_direct_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, .write = cifs_user_write, .open = cifs_open, .release = cifs_close, .lock = cifs_lock, .fsync = cifs_fsync, .flush = cifs_flush, .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .llseek = cifs_llseek, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; 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 */ #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_file_direct_nobrl_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, .write = cifs_user_write, .open = cifs_open, .release = cifs_close, .fsync = cifs_fsync, .flush = cifs_flush, .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .llseek = cifs_llseek, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_dir_ops = { .readdir = cifs_readdir, .release = cifs_closedir, .read = generic_read_dir, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, #endif /* CONFIG_CIFS_EXPERIMENTAL */ .unlocked_ioctl = cifs_ioctl, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) .llseek = generic_file_llseek, #endif }; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) static void cifs_init_once(struct kmem_cache *cachep, void *inode) #else static void cifs_init_once(void *inode) #endif { struct cifsInodeInfo *cifsi = inode; inode_init_once(&cifsi->vfs_inode); INIT_LIST_HEAD(&cifsi->lockList); } 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) { kmem_cache_destroy(cifs_inode_cachep); } static int cifs_init_request_bufs(void) { 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*/ } /* cERROR(1,("CIFSMaxBufSize %d 0x%x",CIFSMaxBufSize,CIFSMaxBufSize)); */ cifs_req_cachep = kmem_cache_create("cifs_request", CIFSMaxBufSize + MAX_CIFS_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; cERROR(1, ("cifs_min_rcv set to maximum (64)")); } 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; cFYI(1, ("cifs_min_small set to maximum (256)")); } 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; } cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs", sizeof(struct oplock_q_entry), 0, SLAB_HWCACHE_ALIGN, NULL); if (cifs_oplock_cachep == NULL) { mempool_destroy(cifs_mid_poolp); 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); kmem_cache_destroy(cifs_oplock_cachep); } static int cifs_oplock_thread(void *dummyarg) { struct oplock_q_entry *oplock_item; struct cifsTconInfo *pTcon; struct inode *inode; __u16 netfid; int rc, waitrc = 0; set_freezable(); do { if (try_to_freeze()) continue; spin_lock(&GlobalMid_Lock); if (list_empty(&GlobalOplock_Q)) { spin_unlock(&GlobalMid_Lock); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(39*HZ); } else { oplock_item = list_entry(GlobalOplock_Q.next, struct oplock_q_entry, qhead); cFYI(1, ("found oplock item to write out")); pTcon = oplock_item->tcon; inode = oplock_item->pinode; netfid = oplock_item->netfid; spin_unlock(&GlobalMid_Lock); DeleteOplockQEntry(oplock_item); /* can not grab inode sem here since it would deadlock when oplock received on delete since vfs_unlink holds the i_mutex across the call */ /* mutex_lock(&inode->i_mutex);*/ if (S_ISREG(inode->i_mode)) { #ifdef CONFIG_CIFS_EXPERIMENTAL if (CIFS_I(inode)->clientCanCacheAll == 0) break_lease(inode, FMODE_READ); else if (CIFS_I(inode)->clientCanCacheRead == 0) break_lease(inode, FMODE_WRITE); #endif rc = filemap_fdatawrite(inode->i_mapping); if (CIFS_I(inode)->clientCanCacheRead == 0) { waitrc = filemap_fdatawait( inode->i_mapping); invalidate_remote_inode(inode); } if (rc == 0) rc = waitrc; } else rc = 0; /* mutex_unlock(&inode->i_mutex);*/ if (rc) CIFS_I(inode)->write_behind_rc = rc; cFYI(1, ("Oplock flush inode %p rc %d", inode, 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 in that case */ if (!pTcon->need_reconnect) { rc = CIFSSMBLock(0, pTcon, netfid, 0 /* len */ , 0 /* offset */, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false /* wait flag */); cFYI(1, ("Oplock release rc = %d", rc)); } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); /* yield in case q were corrupt */ } } while (!kthread_should_stop()); return 0; } #ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_dnotify_thread(void *dummyarg) { struct list_head *tmp; struct TCP_Server_Info *server; do { if (try_to_freeze()) continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(15*HZ); /* check if any stuck requests that need to be woken up and wakeq so the thread can wake up and error out */ read_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &cifs_tcp_ses_list) { server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list); if (atomic_read(&server->inFlight)) wake_up_all(&server->response_q); } read_unlock(&cifs_tcp_ses_lock); } while (!kthread_should_stop()); return 0; } #endif static int __init init_cifs(void) { int rc = 0; cifs_proc_init(); INIT_LIST_HEAD(&cifs_tcp_ses_list); INIT_LIST_HEAD(&GlobalOplock_Q); #ifdef CONFIG_CIFS_EXPERIMENTAL INIT_LIST_HEAD(&GlobalDnotifyReqList); INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); #endif /* * 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; memset(Local_System_Name, 0, 15); rwlock_init(&GlobalSMBSeslock); rwlock_init(&cifs_tcp_ses_lock); spin_lock_init(&GlobalMid_Lock); if (cifs_max_pending < 2) { cifs_max_pending = 2; cFYI(1, ("cifs_max_pending set to min of 2")); } else if (cifs_max_pending > 256) { cifs_max_pending = 256; cFYI(1, ("cifs_max_pending set to max of 256")); } rc = cifs_init_inodecache(); if (rc) goto out_clean_proc; rc = cifs_init_mids(); if (rc) goto out_destroy_inodecache; rc = cifs_init_request_bufs(); if (rc) goto out_destroy_mids; rc = register_filesystem(&cifs_fs_type); if (rc) goto out_destroy_request_bufs; #ifdef CONFIG_CIFS_UPCALL rc = register_key_type(&cifs_spnego_key_type); if (rc) goto out_unregister_filesystem; #endif #ifdef CONFIG_CIFS_DFS_UPCALL rc = register_key_type(&key_type_dns_resolver); if (rc) goto out_unregister_key_type; #endif oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); if (IS_ERR(oplockThread)) { rc = PTR_ERR(oplockThread); cERROR(1, ("error %d create oplock thread", rc)); goto out_unregister_dfs_key_type; } #ifdef CONFIG_CIFS_EXPERIMENTAL dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); if (IS_ERR(dnotifyThread)) { rc = PTR_ERR(dnotifyThread); cERROR(1, ("error %d create dnotify thread", rc)); goto out_stop_oplock_thread; } #endif return 0; #ifdef CONFIG_CIFS_EXPERIMENTAL out_stop_oplock_thread: #endif kthread_stop(oplockThread); out_unregister_dfs_key_type: #ifdef CONFIG_CIFS_DFS_UPCALL unregister_key_type(&key_type_dns_resolver); out_unregister_key_type: #endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: #endif unregister_filesystem(&cifs_fs_type); out_destroy_request_bufs: cifs_destroy_request_bufs(); out_destroy_mids: cifs_destroy_mids(); out_destroy_inodecache: cifs_destroy_inodecache(); out_clean_proc: cifs_proc_clean(); return rc; } static void __exit exit_cifs(void) { cFYI(DBG2, ("exit_cifs")); cifs_proc_clean(); #ifdef CONFIG_CIFS_DFS_UPCALL cifs_dfs_release_automount_timer(); unregister_key_type(&key_type_dns_resolver); #endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); #endif unregister_filesystem(&cifs_fs_type); cifs_destroy_inodecache(); cifs_destroy_mids(); cifs_destroy_request_bufs(); #ifdef CONFIG_CIFS_EXPERIMENTAL kthread_stop(dnotifyThread); #endif kthread_stop(oplockThread); } MODULE_AUTHOR("Steve French "); 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) cifs-test-base/cifsfs.c~0000644000175000017500000010120511120014672015051 0ustar stevefstevef/* * 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 #include #include #include #include #include #include #include #include #include #include #include #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 #include #include "dns_resolve.h" #include "cifs_spnego.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ #ifdef CONFIG_CIFS_QUOTA static struct quotactl_ops cifs_quotactl_ops; #endif /* QUOTA */ int cifsFYI = 0; int cifsERROR = 1; int traceSMB = 0; unsigned int oplockEnabled = 1; unsigned int experimEnabled = 0; unsigned int linuxExtEnabled = 1; unsigned int lookupCacheEnabled = 1; unsigned int multiuser_mount = 0; unsigned int extended_security = CIFSSEC_DEF; /* unsigned int ntlmv2_support = 0; */ unsigned int sign_CIFS_PDUs = 1; extern struct task_struct *oplockThread; /* remove sparse warning */ struct task_struct *oplockThread = NULL; /* extern struct task_struct * dnotifyThread; remove sparse warning */ #ifdef CONFIG_CIFS_EXPERIMENTAL static struct task_struct *dnotifyThread = NULL; #endif static const struct super_operations cifs_super_ops; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; module_param(CIFSMaxBufSize, int, 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, int, 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, int, 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, int, 0); MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. " "Default: 50 Range: 2 to 256"); extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; extern struct kmem_cache *cifs_oplock_cachep; static int cifs_read_super(struct super_block *sb, void *data, const char *devname, int silent) { struct inode *inode; struct cifs_sb_info *cifs_sb; int rc = 0; /* BB should we make this contingent on mount parm? */ sb->s_flags |= MS_NODIRATIME | MS_NOATIME; sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); cifs_sb = CIFS_SB(sb); if (cifs_sb == NULL) return -ENOMEM; #ifdef CONFIG_CIFS_DFS_UPCALL /* copy mount params to sb for use in submounts */ /* BB: should we move this after the mount so we * do not have to do the copy on failed mounts? * BB: May be it is better to do simple copy before * complex operation (mount), and in case of fail * just exit instead of doing mount and attempting * undo it if this copy fails?*/ if (data) { int len = strlen(data); cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL); if (cifs_sb->mountdata == NULL) { kfree(sb->s_fs_info); sb->s_fs_info = NULL; return -ENOMEM; } strncpy(cifs_sb->mountdata, data, len + 1); cifs_sb->mountdata[len] = '\0'; } #endif rc = cifs_mount(sb, cifs_sb, data, devname); if (rc) { if (!silent) cERROR(1, ("cifs_mount failed w/return code = %d", rc)); goto out_mount_failed; } sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_op = &cifs_super_ops; /* if (cifs_sb->tcon->ses->server->maxBuf > MAX_CIFS_HDR_SIZE + 512) sb->s_blocksize = cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */ #ifdef CONFIG_CIFS_QUOTA sb->s_qcop = &cifs_quotactl_ops; #endif sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ inode = cifs_iget(sb, ROOT_I); if (IS_ERR(inode)) { rc = PTR_ERR(inode); inode = NULL; goto out_no_root; } sb->s_root = d_alloc_root(inode); if (!sb->s_root) { rc = -ENOMEM; goto out_no_root; } #ifdef CONFIG_CIFS_EXPERIMENTAL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { cFYI(1, ("export ops supported")); sb->s_export_op = &cifs_export_ops; } #endif /* EXPERIMENTAL */ return 0; out_no_root: cERROR(1, ("cifs_read_super: get root inode failed")); if (inode) iput(inode); cifs_umount(sb, cifs_sb); out_mount_failed: if (cifs_sb) { #ifdef CONFIG_CIFS_DFS_UPCALL if (cifs_sb->mountdata) { kfree(cifs_sb->mountdata); cifs_sb->mountdata = NULL; } #endif if (cifs_sb->local_nls) unload_nls(cifs_sb->local_nls); kfree(cifs_sb); } return rc; } static void cifs_put_super(struct super_block *sb) { int rc = 0; struct cifs_sb_info *cifs_sb; cFYI(1, ("In cifs_put_super")); cifs_sb = CIFS_SB(sb); if (cifs_sb == NULL) { cFYI(1, ("Empty cifs superblock info passed to unmount")); return; } rc = cifs_umount(sb, cifs_sb); if (rc) cERROR(1, ("cifs_umount failed with return code %d", rc)); #ifdef CONFIG_CIFS_DFS_UPCALL if (cifs_sb->mountdata) { kfree(cifs_sb->mountdata); cifs_sb->mountdata = NULL; } #endif unload_nls(cifs_sb->local_nls); kfree(cifs_sb); return; } 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 cifsTconInfo *tcon = cifs_sb->tcon; int rc = -EOPNOTSUPP; int xid; xid = GetXid(); buf->f_type = CIFS_MAGIC_NUMBER; /* * 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 */ /* * 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); FreeXid(xid); return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd) #else static int cifs_permission(struct inode *inode, int mask) #endif { struct cifs_sb_info *cifs_sb; cifs_sb = CIFS_SB(inode->i_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) if ((mask & MAY_EXEC) && !execute_ok(inode)) return -EACCES; else #endif 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, NULL); } static struct kmem_cache *cifs_inode_cachep; static struct kmem_cache *cifs_req_cachep; static struct kmem_cache *cifs_mid_cachep; struct kmem_cache *cifs_oplock_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 */ atomic_set(&cifs_inode->inUse, 0); cifs_inode->time = 0; cifs_inode->write_behind_rc = 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_inode->clientCanCacheRead = false; cifs_inode->clientCanCacheAll = false; cifs_inode->delete_pending = false; cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ /* 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); return &cifs_inode->vfs_inode; } static void cifs_destroy_inode(struct inode *inode) { kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); } /* * 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 vfsmount *m) { struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; struct TCP_Server_Info *server; cifs_sb = CIFS_SB(m->mnt_sb); if (cifs_sb) { tcon = cifs_sb->tcon; if (tcon) { seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName); if (tcon->ses) { if (tcon->ses->userName) seq_printf(s, ",username=%s", tcon->ses->userName); if (tcon->ses->domainName) seq_printf(s, ",domain=%s", tcon->ses->domainName); server = tcon->ses->server; if (server) { seq_printf(s, ",addr="); switch (server->addr.sockAddr6. sin6_family) { case AF_INET6: seq_printf(s, NIP6_FMT, NIP6(server->addr.sockAddr6.sin6_addr)); break; case AF_INET: seq_printf(s, NIPQUAD_FMT, NIPQUAD(server->addr.sockAddr.sin_addr.s_addr)); break; } } } if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) || !(tcon->unix_ext)) seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) || !(tcon->unix_ext)) seq_printf(s, ",gid=%d", cifs_sb->mnt_gid); if (!tcon->unix_ext) { seq_printf(s, ",file_mode=0%o,dir_mode=0%o", cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode); } if (tcon->seal) seq_printf(s, ",seal"); if (tcon->nocase) seq_printf(s, ",nocase"); if (tcon->retry) seq_printf(s, ",hard"); } if (cifs_sb->prepath) seq_printf(s, ",prepath=%s", cifs_sb->prepath); 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_DIRECT_IO) seq_printf(s, ",directio"); 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 (m->mnt_sb->s_flags & MS_POSIXACL) seq_printf(s, ",acl"); seq_printf(s, ",rsize=%d", cifs_sb->rsize); seq_printf(s, ",wsize=%d", cifs_sb->wsize); } return 0; } #ifdef CONFIG_CIFS_QUOTA int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid, struct fs_disk_quota *pdquota) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); } else rc = -EIO; FreeXid(xid); return rc; } int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid, struct fs_disk_quota *pdquota) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); } else rc = -EIO; FreeXid(xid); return rc; } int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation)); } else rc = -EIO; FreeXid(xid); return rc; } int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; if (cifs_sb) pTcon = cifs_sb->tcon; else return -EIO; xid = GetXid(); if (pTcon) { cFYI(1, ("pqstats %p", qstats)); } else rc = -EIO; FreeXid(xid); return rc; } static struct quotactl_ops cifs_quotactl_ops = { .set_xquota = cifs_xquota_set, .get_xquota = cifs_xquota_get, .set_xstate = cifs_xstate_set, .get_xstate = cifs_xstate_get, }; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags) { struct cifs_sb_info *cifs_sb = CIFS_SB(vfsmnt->mnt_sb); #else static void cifs_umount_begin(struct super_block *sb) { struct cifs_sb_info *cifs_sb = CIFS_SB(sb); #end struct cifsTconInfo *tcon; if (cifs_sb == NULL) return; tcon = cifs_sb->tcon; if (tcon == NULL) return; read_lock(&cifs_tcp_ses_lock); if (tcon->tc_count == 1) tcon->tidStatus = CifsExiting; read_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) { cFYI(1, ("wake up tasks now - umount begin not complete")); 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); } /* BB FIXME - finish add checks for tidStatus BB */ return; } #ifdef CONFIG_CIFS_STATS2 static int cifs_show_stats(struct seq_file *s, struct vfsmount *mnt) { /* BB FIXME */ return 0; } #endif static int cifs_remount(struct super_block *sb, int *flags, char *data) { *flags |= MS_NODIRATIME; return 0; } static const struct super_operations cifs_super_ops = { .put_super = cifs_put_super, .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, .destroy_inode = cifs_destroy_inode, /* .drop_inode = generic_delete_inode, .delete_inode = cifs_delete_inode, */ /* Do not need above two functions 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 }; static int cifs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { int rc; struct super_block *sb = sget(fs_type, NULL, set_anon_super, NULL); cFYI(1, ("Devname: %s flags: %d ", dev_name, flags)); if (IS_ERR(sb)) return PTR_ERR(sb); sb->s_flags = flags; rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0); if (rc) { up_write(&sb->s_umount); deactivate_super(sb); return rc; } sb->s_flags |= MS_ACTIVE; return simple_set_mnt(mnt, sb); } static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; ssize_t written; written = generic_file_aio_write(iocb, iov, nr_segs, pos); if (!CIFS_I(inode)->clientCanCacheAll) filemap_fdatawrite(inode->i_mapping); return written; } static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) { /* origin == SEEK_END => we must revalidate the cached file length */ if (origin == SEEK_END) { int retval; /* 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(file->f_path.dentry->d_inode)->time = 0; retval = cifs_revalidate(file->f_path.dentry); if (retval < 0) return (loff_t)retval; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) return remote_llseek(file, offset, origin); #else return generic_file_llseek_unlocked(file, offset, origin); #endif } #ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) { /* note that this is called by vfs setlease with the BKL held although I doubt that BKL is needed here in cifs */ struct inode *inode = file->f_path.dentry->d_inode; if (!(S_ISREG(inode->i_mode))) return -EINVAL; /* check if file is oplocked */ if (((arg == F_RDLCK) && (CIFS_I(inode)->clientCanCacheRead)) || ((arg == F_WRLCK) && (CIFS_I(inode)->clientCanCacheAll))) return generic_setlease(file, arg, lease); else if (CIFS_SB(inode->i_sb)->tcon->local_lease && !CIFS_I(inode)->clientCanCacheRead) /* 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; } #endif struct file_system_type cifs_fs_type = { .owner = THIS_MODULE, .name = "cifs", .get_sb = cifs_get_sb, .kill_sb = kill_anon_super, /* .fs_flags */ }; const struct inode_operations cifs_dir_inode_ops = { .create = cifs_create, .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 */ #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_file_direct_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, .write = cifs_user_write, .open = cifs_open, .release = cifs_close, .lock = cifs_lock, .fsync = cifs_fsync, .flush = cifs_flush, .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .llseek = cifs_llseek, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; 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 */ #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_file_direct_nobrl_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, .write = cifs_user_write, .open = cifs_open, .release = cifs_close, .fsync = cifs_fsync, .flush = cifs_flush, .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .unlocked_ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ .llseek = cifs_llseek, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, .setlease = cifs_setlease, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; const struct file_operations cifs_dir_ops = { .readdir = cifs_readdir, .release = cifs_closedir, .read = generic_read_dir, #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, #endif /* CONFIG_CIFS_EXPERIMENTAL */ .unlocked_ioctl = cifs_ioctl, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) .llseek = generic_file_llseek, #endif }; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) static void cifs_init_once(struct kmem_cache *cachep, void *inode) #else static void cifs_init_once(void *inode) #endif { struct cifsInodeInfo *cifsi = inode; inode_init_once(&cifsi->vfs_inode); INIT_LIST_HEAD(&cifsi->lockList); } 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) { kmem_cache_destroy(cifs_inode_cachep); } static int cifs_init_request_bufs(void) { 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*/ } /* cERROR(1,("CIFSMaxBufSize %d 0x%x",CIFSMaxBufSize,CIFSMaxBufSize)); */ cifs_req_cachep = kmem_cache_create("cifs_request", CIFSMaxBufSize + MAX_CIFS_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; cERROR(1, ("cifs_min_rcv set to maximum (64)")); } 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; cFYI(1, ("cifs_min_small set to maximum (256)")); } 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; } cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs", sizeof(struct oplock_q_entry), 0, SLAB_HWCACHE_ALIGN, NULL); if (cifs_oplock_cachep == NULL) { mempool_destroy(cifs_mid_poolp); 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); kmem_cache_destroy(cifs_oplock_cachep); } static int cifs_oplock_thread(void *dummyarg) { struct oplock_q_entry *oplock_item; struct cifsTconInfo *pTcon; struct inode *inode; __u16 netfid; int rc, waitrc = 0; set_freezable(); do { if (try_to_freeze()) continue; spin_lock(&GlobalMid_Lock); if (list_empty(&GlobalOplock_Q)) { spin_unlock(&GlobalMid_Lock); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(39*HZ); } else { oplock_item = list_entry(GlobalOplock_Q.next, struct oplock_q_entry, qhead); cFYI(1, ("found oplock item to write out")); pTcon = oplock_item->tcon; inode = oplock_item->pinode; netfid = oplock_item->netfid; spin_unlock(&GlobalMid_Lock); DeleteOplockQEntry(oplock_item); /* can not grab inode sem here since it would deadlock when oplock received on delete since vfs_unlink holds the i_mutex across the call */ /* mutex_lock(&inode->i_mutex);*/ if (S_ISREG(inode->i_mode)) { #ifdef CONFIG_CIFS_EXPERIMENTAL if (CIFS_I(inode)->clientCanCacheAll == 0) break_lease(inode, FMODE_READ); else if (CIFS_I(inode)->clientCanCacheRead == 0) break_lease(inode, FMODE_WRITE); #endif rc = filemap_fdatawrite(inode->i_mapping); if (CIFS_I(inode)->clientCanCacheRead == 0) { waitrc = filemap_fdatawait( inode->i_mapping); invalidate_remote_inode(inode); } if (rc == 0) rc = waitrc; } else rc = 0; /* mutex_unlock(&inode->i_mutex);*/ if (rc) CIFS_I(inode)->write_behind_rc = rc; cFYI(1, ("Oplock flush inode %p rc %d", inode, 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 in that case */ if (!pTcon->need_reconnect) { rc = CIFSSMBLock(0, pTcon, netfid, 0 /* len */ , 0 /* offset */, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false /* wait flag */); cFYI(1, ("Oplock release rc = %d", rc)); } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); /* yield in case q were corrupt */ } } while (!kthread_should_stop()); return 0; } #ifdef CONFIG_CIFS_EXPERIMENTAL static int cifs_dnotify_thread(void *dummyarg) { struct list_head *tmp; struct TCP_Server_Info *server; do { if (try_to_freeze()) continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(15*HZ); /* check if any stuck requests that need to be woken up and wakeq so the thread can wake up and error out */ read_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &cifs_tcp_ses_list) { server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list); if (atomic_read(&server->inFlight)) wake_up_all(&server->response_q); } read_unlock(&cifs_tcp_ses_lock); } while (!kthread_should_stop()); return 0; } #endif static int __init init_cifs(void) { int rc = 0; cifs_proc_init(); INIT_LIST_HEAD(&cifs_tcp_ses_list); INIT_LIST_HEAD(&GlobalOplock_Q); #ifdef CONFIG_CIFS_EXPERIMENTAL INIT_LIST_HEAD(&GlobalDnotifyReqList); INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); #endif /* * 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; memset(Local_System_Name, 0, 15); rwlock_init(&GlobalSMBSeslock); rwlock_init(&cifs_tcp_ses_lock); spin_lock_init(&GlobalMid_Lock); if (cifs_max_pending < 2) { cifs_max_pending = 2; cFYI(1, ("cifs_max_pending set to min of 2")); } else if (cifs_max_pending > 256) { cifs_max_pending = 256; cFYI(1, ("cifs_max_pending set to max of 256")); } rc = cifs_init_inodecache(); if (rc) goto out_clean_proc; rc = cifs_init_mids(); if (rc) goto out_destroy_inodecache; rc = cifs_init_request_bufs(); if (rc) goto out_destroy_mids; rc = register_filesystem(&cifs_fs_type); if (rc) goto out_destroy_request_bufs; #ifdef CONFIG_CIFS_UPCALL rc = register_key_type(&cifs_spnego_key_type); if (rc) goto out_unregister_filesystem; #endif #ifdef CONFIG_CIFS_DFS_UPCALL rc = register_key_type(&key_type_dns_resolver); if (rc) goto out_unregister_key_type; #endif oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); if (IS_ERR(oplockThread)) { rc = PTR_ERR(oplockThread); cERROR(1, ("error %d create oplock thread", rc)); goto out_unregister_dfs_key_type; } #ifdef CONFIG_CIFS_EXPERIMENTAL dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); if (IS_ERR(dnotifyThread)) { rc = PTR_ERR(dnotifyThread); cERROR(1, ("error %d create dnotify thread", rc)); goto out_stop_oplock_thread; } #endif return 0; #ifdef CONFIG_CIFS_EXPERIMENTAL out_stop_oplock_thread: #endif kthread_stop(oplockThread); out_unregister_dfs_key_type: #ifdef CONFIG_CIFS_DFS_UPCALL unregister_key_type(&key_type_dns_resolver); out_unregister_key_type: #endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: #endif unregister_filesystem(&cifs_fs_type); out_destroy_request_bufs: cifs_destroy_request_bufs(); out_destroy_mids: cifs_destroy_mids(); out_destroy_inodecache: cifs_destroy_inodecache(); out_clean_proc: cifs_proc_clean(); return rc; } static void __exit exit_cifs(void) { cFYI(DBG2, ("exit_cifs")); cifs_proc_clean(); #ifdef CONFIG_CIFS_DFS_UPCALL cifs_dfs_release_automount_timer(); unregister_key_type(&key_type_dns_resolver); #endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); #endif unregister_filesystem(&cifs_fs_type); cifs_destroy_inodecache(); cifs_destroy_mids(); cifs_destroy_request_bufs(); #ifdef CONFIG_CIFS_EXPERIMENTAL kthread_stop(dnotifyThread); #endif kthread_stop(oplockThread); } MODULE_AUTHOR("Steve French "); 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) cifs-test-base/cifsfs.h0000644000175000017500000001147511117757272014711 0ustar stevefstevef/* * fs/cifs/cifsfs.h * * Copyright (c) International Business Machines Corp., 2002, 2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CIFSFS_H #define _CIFSFS_H #include #define ROOT_I 2 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 const struct super_operations cifs_super_ops;*/ extern void cifs_read_inode(struct inode *); /*extern void cifs_delete_inode(struct inode *);*/ /* BB not needed yet */ /* extern void cifs_write_inode(struct inode *); */ /* BB not needed yet */ /* Functions related to inodes */ extern const struct inode_operations cifs_dir_inode_ops; extern struct inode *cifs_iget(struct super_block *, unsigned long); extern int cifs_create(struct inode *, struct dentry *, int, struct nameidata *); extern struct dentry *cifs_lookup(struct inode *, struct dentry *, struct nameidata *); 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 *, int, dev_t); extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); extern int cifs_revalidate(struct dentry *); 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 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_nobrl_ops; extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */ 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_read(struct file *file, char __user *read_data, size_t read_size, loff_t *poffset); extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset); extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_fsync(struct file *, struct dentry *, int); extern int cifs_flush(struct file *, fl_owner_t id); extern int cifs_file_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); extern int cifs_dir_notify(struct file *, unsigned long arg); /* Functions related to dir entries */ extern struct dentry_operations cifs_dentry_ops; extern struct dentry_operations cifs_ci_dentry_ops; /* 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_EXPERIMENTAL extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ #define CIFS_VERSION "1.56" #endif /* _CIFSFS_H */ cifs-test-base/cifs_fs_sb.h0000644000175000017500000000445611117756171015532 0ustar stevefstevef/* * 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. * */ #ifndef _CIFS_FS_SB_H #define _CIFS_FS_SB_H #define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ #define CIFS_MOUNT_SET_UID 2 /* set current->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 */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ struct list_head nested_tcon_q; struct nls_table *local_nls; unsigned int rsize; unsigned int wsize; uid_t mnt_uid; gid_t mnt_gid; mode_t mnt_file_mode; mode_t mnt_dir_mode; int mnt_cifs_flags; int prepathlen; char *prepath; /* relative path under the share to mount to */ #ifdef CONFIG_CIFS_DFS_UPCALL char *mountdata; /* mount options received at mount time */ #endif }; #endif /* _CIFS_FS_SB_H */ cifs-test-base/cifsglob.h0000644000175000017500000005435111117756171015221 0ustar stevefstevef/* * 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. * */ #include #include #include "cifs_fs_sb.h" #include "cifsacl.h" /* * 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 + MAX_SERVER_SIZE + 1 + MAX_SHARE_SIZE + 1) #define MAX_SERVER_SIZE 15 #define MAX_SHARE_SIZE 64 /* used to be 20, this should still be enough */ #define MAX_USERNAME_SIZE 32 /* 32 is to allow for 15 char names + null termination then *2 for unicode versions */ #define MAX_PASSWORD_SIZE 16 #define CIFS_MIN_RCV_POOL 4 /* * MAX_REQ is the maximum number of requests that WE will send * on one socket concurently. It also matches the most common * value of max multiplex returned by servers. We may * eventually want to use the negotiated value (in case * future servers can handle more) when we are more confident that * we will not have problems oveloading the socket with pending * write data. */ #define CIFS_MAX_REQ 50 #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 #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 }; enum securityEnum { PLAINTXT = 0, /* Legacy with Plaintext passwords */ LANMAN, /* Legacy LANMAN auth */ NTLM, /* Legacy NTLM012 auth with NTLM hash */ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ RawNTLMSSP, /* NTLMSSP without SPNEGO */ NTLMSSP, /* NTLMSSP via SPNEGO */ Kerberos, /* Kerberos via SPNEGO */ MSKerberos, /* MS Kerberos via SPNEGO */ }; enum protocolEnum { TCP = 0, SCTP /* Netbios frames protocol not supported at this time */ }; struct mac_key { unsigned int len; union { char ntlm[CIFS_SESS_KEY_SIZE + 16]; char krb5[CIFS_SESS_KEY_SIZE + 16]; /* BB: length correct? */ struct { char key[16]; struct ntlmv2_resp resp; } ntlmv2; } data; }; 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 ***************************************************************** */ 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]; char *hostname; /* hostname portion of UNC string */ struct socket *ssocket; union { struct sockaddr_in sockAddr; struct sockaddr_in6 sockAddr6; } addr; 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; void *Server_NlsInfo; /* BB - placeholder for future NLS info */ unsigned short server_codepage; /* codepage for the server */ unsigned long ip_address; /* IP addr for the server if known */ enum protocolEnum protocolType; char versionMajor; char versionMinor; bool svlocal:1; /* local server or remote */ bool noblocksnd; /* use blocking sendmsg */ bool noautotune; /* do not autotune send buf sizes */ atomic_t inFlight; /* number of requests on the wire to server */ #ifdef CONFIG_CIFS_STATS2 atomic_t inSend; /* requests trying to send */ atomic_t num_waiters; /* blocked waiting to get in sendrecv */ #endif enum statusEnum tcpStatus; /* what we think the status is */ struct mutex srv_mutex; struct task_struct *tsk; char server_GUID[16]; char secMode; enum securityEnum secType; 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 */ unsigned int maxRw; /* maxRw specifies the maximum */ /* message size the server can send or receive for */ /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ char sessid[4]; /* unique token id for this session */ /* (returned on Negotiate */ int capabilities; /* allow selective disabling of caps by smb sess */ int timeAdj; /* Adjust for difference in server time zone in sec */ __u16 CurrentMid; /* multiplex id - rotating counter */ char cryptKey[CIFS_CRYPTO_KEY_SIZE]; /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ struct mac_key mac_signing_key; char ntlmv2_hash[16]; unsigned long lstrp; /* when we got last response from this server */ }; /* * The following is our shortcut to user information. We surface the uid, * and name. We always get the password on the fly in case it * has changed. We also hang a list of sessions owned by this user off here. */ struct cifsUidInfo { struct list_head userList; struct list_head sessionList; /* SMB sessions for this user */ uid_t linux_uid; char user[MAX_USERNAME_SIZE + 1]; /* ascii name of user */ /* BB may need ptr or callback for PAM or WinBind info */ }; /* * Session structure. One of these for each uid session with a particular host */ struct cifsSesInfo { struct list_head smb_ses_list; struct list_head tcon_list; struct semaphore sesSem; #if 0 struct cifsUidInfo *uidInfo; /* pointer to user info */ #endif 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 */ __u16 flags; char *serverOS; /* name of operating system underlying server */ char *serverNOS; /* name of network operating system of server */ char *serverDomain; /* security realm of server */ int Suid; /* remote smb uid */ uid_t linux_uid; /* local Linux uid */ int capabilities; char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for TCP names - will ipv6 and sctp addresses fit? */ char userName[MAX_USERNAME_SIZE + 1]; char *domainName; char *password; bool need_reconnect:1; /* connection reset, uid now invalid */ }; /* no more than one of the following three session flags may be set */ #define CIFS_SES_NT4 1 #define CIFS_SES_OS2 2 #define CIFS_SES_W9X 4 /* following flag is set for old servers such as OS2 (and Win95?) which do not negotiate NTLM or POSIX dialects, but instead negotiate one of the older LANMAN dialects */ #define CIFS_SES_LANMAN 8 /* * there is one of these for each connection to a resource on a particular * session */ struct cifsTconInfo { struct list_head tcon_list; int tc_count; struct list_head openFileList; struct cifsSesInfo *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 */ __u16 tid; /* The 2 byte tree id */ __u16 Flags; /* optional support bits */ enum statusEnum tidStatus; #ifdef CONFIG_CIFS_STATS atomic_t num_smbs_sent; atomic_t num_writes; atomic_t num_reads; atomic_t num_oplock_brks; atomic_t num_opens; atomic_t num_closes; atomic_t num_deletes; atomic_t num_mkdirs; 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; #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 need_reconnect:1; /* connection reset, tid now invalid */ /* BB add field for back pointer to sb struct(s)? */ }; /* * 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 */ __u64 offset; __u64 length; __u8 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; 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 cifsFileInfo { struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head flist; /* next fid (file instance) for this inode */ unsigned int uid; /* allows finding which FileInfo structure */ __u32 pid; /* process id who opened file */ __u16 netfid; /* file id from remote */ /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ struct file *pfile; /* needed for writepage */ struct inode *pInode; /* needed for oplock break */ struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ bool closePend:1; /* file is marked to close */ bool invalidHandle:1; /* file closed via session abend */ bool messageMode:1; /* for pipes: message vs byte mode */ atomic_t wrtPending; /* handle in use - defer close */ struct semaphore fh_sem; /* prevents reopen race after dead ses*/ struct cifs_search_info srch_inf; }; /* * One of these for each file inode */ struct cifsInodeInfo { struct list_head lockList; /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; int write_behind_rc; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ atomic_t inUse; /* num concurrent users (local openers cifs) of file*/ unsigned long time; /* jiffies of last update/check of inode */ bool clientCanCacheRead:1; /* read oplock */ bool clientCanCacheAll:1; /* read and writebehind oplock */ bool oplockPending:1; bool delete_pending:1; /* DELETE_ON_CLOSE is set */ 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 '\\'; } #ifdef CONFIG_CIFS_STATS #define cifs_stats_inc atomic_inc static inline void cifs_stats_bytes_written(struct cifsTconInfo *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 cifsTconInfo *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 /* 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 */ __u16 mid; /* multiplex id */ __u16 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 struct task_struct *tsk; /* task waiting for response */ struct smb_hdr *resp_buf; /* response buffer */ int midState; /* wish this were enum but can not pass to wait_event */ __u8 command; /* smb command code */ bool largeBuf:1; /* if valid response, is pointer to large buf */ bool multiRsp:1; /* multiple trans2 responses for one request */ bool multiEnd:1; /* both received */ }; struct oplock_q_entry { struct list_head qhead; struct inode *pinode; struct cifsTconInfo *tcon; __u16 netfid; }; /* 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; }; 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_NO_RESP_NEEDED 0x10 /* 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_STD_OP 0 /* normal request timeout */ #define CIFS_LONG_OP 1 /* long op (up to 45 sec, oplock time) */ #define CIFS_VLONG_OP 2 /* sloow op - can take up to 180 seconds */ #define CIFS_BLOCKING_OP 4 /* operation can block */ #define CIFS_ASYNC_OP 8 /* do not wait for response */ #define CIFS_TIMEOUT_MASK 0x00F /* only one of 5 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 */ /* 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_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 0x3F03F /* allows weak security but also krb5 */ #else #define CIFSSEC_MASK 0x37037 /* current flags supported if weak */ #endif /* UPCALL */ #else /* do not allow weak pw hash */ #ifdef CONFIG_CIFS_UPCALL #define CIFSSEC_MASK 0x0F00F /* flags supported if no weak allowed */ #else #define CIFSSEC_MASK 0x07007 /* flags supported if no weak allowed */ #endif /* UPCALL */ #endif /* WEAK_PW_HASH */ #define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ #define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2) #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) /* ***************************************************************** * 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 * GlobalSMBSesLock 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 rwlock_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 rwlock_t GlobalSMBSeslock; GLOBAL_EXTERN struct list_head GlobalOplock_Q; /* Outstanding dir notify requests */ GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; /* DirNotify response queue */ GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; /* * 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_EXTERN char Local_System_Name[15]; /* * 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 unsigned int multiuser_mount; /* if enabled allows new sessions to be established on existing mount if we have the uid/password or Kerberos credential or equivalent for current user */ GLOBAL_EXTERN unsigned int oplockEnabled; GLOBAL_EXTERN unsigned int experimEnabled; GLOBAL_EXTERN unsigned int lookupCacheEnabled; GLOBAL_EXTERN unsigned int extended_security; /* 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*/ cifs-test-base/cifspdu.h0000644000175000017500000024057611117756171015074 0ustar stevefstevef/* * fs/cifs/cifspdu.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 _CIFSPDU_H #define _CIFSPDU_H #include #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 */ /* * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses * (ie which include no useful data other than the SMB error code itself). * Knowing this helps 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_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_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 session key (crypto key encrypted with the password */ #define CIFS_SESS_KEY_SIZE (24) /* * 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 #define NO_CHANGE_32 0xFFFFFFFFUL /* 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 { __u32 smb_buf_length; /* big endian on wire *//* BB length is only two or three bytes - with one or two byte type preceding it that are zero - we could mask the type byte off just in case BB */ __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 the value of byte count */ #define BCC(smb_var) (*(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount))) #define BCC_LE(smb_var) (*(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount))) /* given a pointer to an smb_hdr retrieve the pointer to the byte area */ #define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount) + 2) /* * 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) 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[16]; 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; struct ntlmssp2_name names[2]; /* 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_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_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; __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ char Data[1]; } __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 defintions 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; 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 AltNameOffset; __u16 AltNameLen; __u16 TargetNameOffset; __u16 TargetNameLen; char LinkNamesBuf[1]; } __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 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; 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 typedef struct dfs_referral_level_3 { __le16 VersionNumber; __le16 Size; __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ __le16 ReferralEntryFlags; /* 0x0200 bit set only for domain or DC referral responce */ __le32 TimeToLive; __le16 DfsPathOffset; __le16 DfsAlternatePathOffset; __le16 NetworkAddressOffset; /* offset of the link target */ __le16 ServiceSiteGuid; } __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; __le16 DFSFlags; __u16 Pad2; 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 0x0001 #define DFSREF_STORAGE_SERVER 0x0002 /* IOCTL information */ /* * List of ioctl function codes that look to be of interest to remote clients * like this one. Need to do some experimentation to make sure they all work * remotely. Some of the following, such as the encryption/compression ones * would be invoked from tools via a specialized hook into the VFS rather * than via the standard vfs entry points */ #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_GET_COMPRESSION 0x0009003C #define FSCTL_SET_COMPRESSION 0x0009C040 #define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C #define FSCTL_FILESYS_GET_STATISTICS 0x00090090 #define FSCTL_SET_REPARSE_POINT 0x000900A4 #define FSCTL_GET_REPARSE_POINT 0x000900A8 #define FSCTL_DELETE_REPARSE_POINT 0x000900AC #define FSCTL_SET_SPARSE 0x000900C4 #define FSCTL_SET_ZERO_DATA 0x000900C8 #define FSCTL_SET_ENCRYPTION 0x000900D7 #define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB #define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF #define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 #define FSCTL_SIS_COPYFILE 0x00090100 #define FSCTL_SIS_LINK_FILES 0x0009C104 #define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 #define IO_REPARSE_TAG_HSM 0xC0000004 #define IO_REPARSE_TAG_SIS 0x80000007 /* ************************************************************************ * 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 /* Can not set pathnames cap yet until we send new posix create SMB since otherwise server can treat such handles opened with older ntcreatex (by a new client which knows how to send posix path ops) as non-posix handles (can affect write behavior with byte range locks. We can add back in POSIX_PATH_OPS cap when Posix Create/Mkdir finished */ /* #define CIFS_UNIX_CAP_MASK 0x000000fb */ #define CIFS_UNIX_CAP_MASK 0x000000db #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; __u64 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; __u64 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 { __u64 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 */ __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; __u64 UniqueId; __le64 Permissions; __le64 Nlinks; 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; __u64 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 8) Direct i/o 9) Misc fcntls? 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 #endif /* _CIFSPDU_H */ cifs-test-base/cifsproto.h0000644000175000017500000004013211117756171015431 0ustar stevefstevef/* * 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 struct statfs; struct smb_vol; /* ***************************************************************** * 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 int smb_send(struct socket *, struct smb_hdr *, unsigned int /* length */ , struct sockaddr *, bool); extern unsigned int _GetXid(void); extern void _FreeXid(unsigned int); #define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current->fsuid)); #define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));} extern char *build_path_from_dentry(struct dentry *); extern char *build_wildcard_path_from_dentry(struct dentry *direntry); /* extern void renew_parental_timestamps(struct dentry *direntry);*/ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); extern int SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, int flags); extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, struct kvec *, int /* nvec to send */, int * /* type of buf returned */ , const int flags); extern int SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *ptcon, struct smb_hdr *in_buf , struct smb_hdr *out_buf, int *bytes_returned); extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); extern bool is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); #ifdef CONFIG_CIFS_EXPERIMENTAL extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *); #endif extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, enum securityEnum *secType); extern int cifs_inet_pton(const int, const char *source, void *dst); extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr); extern void header_assemble(struct smb_hdr *, char /* command */ , const struct cifsTconInfo *, 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 cifsSesInfo *ses, void **request_buf); extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, const int stage, const struct nls_table *nls_cp); extern __u16 GetNextMid(struct TCP_Server_Info *server); extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16, struct cifsTconInfo *); extern void DeleteOplockQEntry(struct oplock_q_entry *); extern void DeleteTconOplockQEntries(struct cifsTconInfo *); extern struct timespec cifs_NTtimeToUnix(u64 utc_nanoseconds_since_1601); extern u64 cifs_UnixTimeToNT(struct timespec); extern __le64 cnvrtDosCifsTm(__u16 date, __u16 time); extern struct timespec cnvrtDosUnixTm(__u16 date, __u16 time); extern int cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path, FILE_ALL_INFO *pfile_info, struct super_block *sb, int xid, const __u16 *pfid); extern int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, int xid); extern void acl_to_uid_mode(struct inode *inode, const char *path, const __u16 *pfid); extern int mode_to_acl(struct inode *inode, const char *path, __u64); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); extern int cifs_umount(struct super_block *, struct cifs_sb_info *); extern void cifs_dfs_release_automount_timer(void); void cifs_proc_init(void); void cifs_proc_clean(void); extern int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table *nls_info); extern int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses); extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, const char *tree, struct cifsTconInfo *tcon, const struct nls_table *); extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, __u16 *searchHandle, struct cifs_search_info *psrch_inf, int map, const char dirsep); extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, __u16 searchHandle, struct cifs_search_info *psrch_inf); extern int CIFSFindClose(const int, struct cifsTconInfo *tcon, const __u16 search_handle); extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO *findData, int legacy /* whether to use old info level */, const struct nls_table *nls_codepage, int remap); extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO *findData, const struct nls_table *nls_codepage, int remap); extern int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_UNIX_BASIC_INFO *pFindData, const struct nls_table *nls_codepage, int remap); extern int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, struct dfs_info3_param **target_nodes, unsigned int *number_of_nodes_in_array, const struct nls_table *nls_codepage, int remap); extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, const struct nls_table *nls_codepage, unsigned int *pnum_referrals, struct dfs_info3_param **preferrals, int remap); extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, struct super_block *sb, struct smb_vol *vol); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap); extern int CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); extern int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon, const char *fileName, const FILE_BASIC_INFO *data, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener); extern int CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon, bool delete_file, __u16 fid, __u32 pid_of_opener); #if 0 extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName, __u16 dos_attributes, const struct nls_table *nls_codepage); #endif /* possibly unneeded function */ extern int CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName, __u64 size, bool setAllocationSizeFlag, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, __u16 fileHandle, __u32 opener_pid, bool AllocSizeFlag); struct cifs_unix_set_info_args { __u64 ctime; __u64 atime; __u64 mtime; __u64 mode; __u64 uid; __u64 gid; dev_t device; }; extern int CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *pTcon, char *fileName, const struct cifs_unix_set_info_args *args, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, const char *newName, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *name, __u16 type, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, int netfid, const char *target_name, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage); extern int CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *syminfo, const int buflen, const struct nls_table *nls_codepage); extern int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *symlinkinfo, const int buflen, __u16 fid, const struct nls_table *nls_codepage); extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *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 SMBLegacyOpen(const int xid, struct cifsTconInfo *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 int xid, struct cifsTconInfo *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 int xid, struct cifsTconInfo *tcon, const int smb_file_id); extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, unsigned int count, const __u64 lseek, unsigned int *nbytes, char **buf, int *return_buf_type); extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 lseek, unsigned int *nbytes, const char *buf, const char __user *ubuf, const int long_op); extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, struct kvec *iov, const int nvec, const int long_op); extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 *inode_number, const struct nls_table *nls_codepage, int remap_special_chars); extern int cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, const struct nls_table *codepage); extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 netfid, const __u64 len, const __u64 offset, const __u32 numUnlock, const __u32 numLock, const __u8 lockType, const bool waitFlag); extern int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const int get_flag, const __u64 len, struct file_lock *, const __u16 lock_type, const bool waitFlag); extern int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses); extern struct cifsSesInfo *sesInfoAlloc(void); extern void sesInfoFree(struct cifsSesInfo *); extern struct cifsTconInfo *tconInfoAlloc(void); extern void tconInfoFree(struct cifsTconInfo *); extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *, __u32 *); extern int cifs_verify_signature(struct smb_hdr *, const struct mac_key *mac_key, __u32 expected_sequence_number); extern int cifs_calculate_mac_key(struct mac_key *key, const char *rn, const char *pass); extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, const struct nls_table *); extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, const struct nls_table *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, char *lnm_session_key); #endif /* CIFS_WEAK_PW_HASH */ extern int CIFSSMBCopy(int xid, struct cifsTconInfo *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 int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, const int notify_subdirs, const __u16 netfid, __u32 filter, struct file *file, int multishot, const struct nls_table *nls_codepage); extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *EAData, size_t bufsize, const struct nls_table *nls_codepage, int remap_special_chars); extern ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, const unsigned char *ea_name, unsigned char *ea_value, size_t buf_size, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *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 int xid, struct cifsTconInfo *tcon, __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); extern int CIFSSMBSetCIFSACL(const int, struct cifsTconInfo *, __u16, struct cifs_ntsd *, __u32); extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *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 int xid, struct cifsTconInfo *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 int xid, struct cifsTconInfo *tcon, const int netfid, __u64 *pExtAttrBits, __u64 *pMask); #endif /* _CIFSPROTO_H */ cifs-test-base/cifssmb.c0000644000175000017500000052514111117756171015052 0ustar stevefstevef/* * fs/cifs/cifssmb.c * * Copyright (C) International Business Machines Corp., 2002,2008 * Author(s): Steve French (sfrench@us.ibm.com) * * Contains the routines for constructing the SMB PDUs themselves * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ /* These are mostly routines that operate on a pathname, or on a tree id */ /* (mounted volume), but there are eight handle based routines which must be */ /* treated slightly differently for reconnection purposes since we never */ /* want to reuse a stale file handle and only the caller knows the file info */ #include #include #include #include #include #include "cifspdu.h" #include "cifsglob.h" #include "cifsacl.h" #include "cifsproto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #ifdef CONFIG_CIFS_POSIX static struct { int index; char *name; } protocols[] = { #ifdef CONFIG_CIFS_WEAK_PW_HASH {LANMAN_PROT, "\2LM1.2X002"}, {LANMAN2_PROT, "\2LANMAN2.1"}, #endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {POSIX_PROT, "\2POSIX 2"}, {BAD_PROT, "\2"} }; #else static struct { int index; char *name; } protocols[] = { #ifdef CONFIG_CIFS_WEAK_PW_HASH {LANMAN_PROT, "\2LM1.2X002"}, {LANMAN2_PROT, "\2LANMAN2.1"}, #endif /* weak password hashing for legacy clients */ {CIFS_PROT, "\2NT LM 0.12"}, {BAD_PROT, "\2"} }; #endif /* define the number of elements in the cifs dialect array */ #ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_WEAK_PW_HASH #define CIFS_NUM_PROT 4 #else #define CIFS_NUM_PROT 2 #endif /* CIFS_WEAK_PW_HASH */ #else /* not posix */ #ifdef CONFIG_CIFS_WEAK_PW_HASH #define CIFS_NUM_PROT 3 #else #define CIFS_NUM_PROT 1 #endif /* CONFIG_CIFS_WEAK_PW_HASH */ #endif /* CIFS_POSIX */ /* Allocates buffer into dst and copies smb string from src to it. * caller is responsible for freeing dst if function returned 0. * returns: * on success - 0 * on failure - errno */ static int cifs_strncpy_to_host(char **dst, const char *src, const int maxlen, const bool is_unicode, const struct nls_table *nls_codepage) { int plen; if (is_unicode) { plen = UniStrnlen((wchar_t *)src, maxlen); *dst = kmalloc(plen + 2, GFP_KERNEL); if (!*dst) goto cifs_strncpy_to_host_ErrExit; cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage); } else { plen = strnlen(src, maxlen); *dst = kmalloc(plen + 2, GFP_KERNEL); if (!*dst) goto cifs_strncpy_to_host_ErrExit; strncpy(*dst, src, plen); } (*dst)[plen] = 0; (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */ return 0; cifs_strncpy_to_host_ErrExit: cERROR(1, ("Failed to allocate buffer for string\n")); return -ENOMEM; } /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) { struct cifsFileInfo *open_file = NULL; struct list_head *tmp; struct list_head *tmp1; /* list all files open on tree connection and mark them invalid */ write_lock(&GlobalSMBSeslock); list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { open_file = list_entry(tmp, struct cifsFileInfo, tlist); open_file->invalidHandle = true; } write_unlock(&GlobalSMBSeslock); /* BB Add call to invalidate_inodes(sb) for all superblocks mounted to this tcon */ } /* Allocate and return pointer to an SMB request buffer, and set basic SMB information in the SMB header. If the return code is zero, this function must have filled in request_buf pointer */ static int small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf) { int rc = 0; /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ if (tcon) { 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 ((smb_command != SMB_COM_WRITE_ANDX) && (smb_command != SMB_COM_OPEN_ANDX) && (smb_command != SMB_COM_TREE_DISCONNECT)) { cFYI(1, ("can not send cmd %d while umounting", smb_command)); return -ENODEV; } } if ((tcon->ses) && (tcon->ses->status != CifsExiting) && (tcon->ses->server)) { struct nls_table *nls_codepage; /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ while (tcon->ses->server->tcpStatus == CifsNeedReconnect) { wait_event_interruptible_timeout(tcon->ses->server->response_q, (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); if (tcon->ses->server->tcpStatus == CifsNeedReconnect) { /* on "soft" mounts we wait once */ if (!tcon->retry || (tcon->ses->status == CifsExiting)) { cFYI(1, ("gave up waiting on " "reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes back on-line */ } else /* TCP session is reestablished now */ break; } nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); if (tcon->ses->need_reconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); if (!rc && (tcon->need_reconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ if (rc == 0) { atomic_inc(&tconInfoReconnectCount); /* tell server Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) reset_cifs_unix_caps( 0 /* no xid */, tcon, NULL /* we do not know sb */, NULL /* no vol info */); } cFYI(1, ("reconnect tcon rc = %d", rc)); /* Removed call to reopen open files here. It is safer (and faster) to reopen files one at a time as needed in read and write */ /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: case SMB_COM_FIND_CLOSE2: case SMB_COM_LOCKING_ANDX: { unload_nls(nls_codepage); return -EAGAIN; } } } else { up(&tcon->ses->sesSem); } unload_nls(nls_codepage); } else { return -EIO; } } if (rc) return rc; *request_buf = cifs_small_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; } header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct); if (tcon != NULL) cifs_stats_inc(&tcon->num_smbs_sent); return rc; } int small_smb_init_no_tc(const int smb_command, const int wct, struct cifsSesInfo *ses, void **request_buf) { int rc; struct smb_hdr *buffer; rc = small_smb_init(smb_command, wct, NULL, request_buf); if (rc) return rc; buffer = (struct smb_hdr *)*request_buf; buffer->Mid = GetNextMid(ses->server); if (ses->capabilities & CAP_UNICODE) buffer->Flags2 |= SMBFLG2_UNICODE; if (ses->capabilities & CAP_STATUS32) buffer->Flags2 |= SMBFLG2_ERR_STATUS; /* uid, tid can stay at zero as set in header assemble */ /* BB add support for turning on the signing when this function is used after 1st of session setup requests */ return rc; } /* If the return code is zero, this function must fill in request_buf pointer */ static int smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf /* returned */ , void **response_buf /* returned */ ) { int rc = 0; /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ if (tcon) { 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 ((smb_command != SMB_COM_WRITE_ANDX) && (smb_command != SMB_COM_OPEN_ANDX) && (smb_command != SMB_COM_TREE_DISCONNECT)) { cFYI(1, ("can not send cmd %d while umounting", smb_command)); return -ENODEV; } } if ((tcon->ses) && (tcon->ses->status != CifsExiting) && (tcon->ses->server)) { struct nls_table *nls_codepage; /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ while (tcon->ses->server->tcpStatus == CifsNeedReconnect) { wait_event_interruptible_timeout(tcon->ses->server->response_q, (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); if (tcon->ses->server->tcpStatus == CifsNeedReconnect) { /* on "soft" mounts we wait once */ if (!tcon->retry || (tcon->ses->status == CifsExiting)) { cFYI(1, ("gave up waiting on " "reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes on-line */ } else /* TCP session is reestablished now */ break; } nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); if (tcon->ses->need_reconnect) rc = cifs_setup_session(0, tcon->ses, nls_codepage); if (!rc && (tcon->need_reconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ if (rc == 0) { atomic_inc(&tconInfoReconnectCount); /* tell server Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) reset_cifs_unix_caps( 0 /* no xid */, tcon, NULL /* do not know sb */, NULL /* no vol info */); } cFYI(1, ("reconnect tcon rc = %d", rc)); /* Removed call to reopen open files here. It is safer (and faster) to reopen files one at a time as needed in read and write */ /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: case SMB_COM_FIND_CLOSE2: case SMB_COM_LOCKING_ANDX: { unload_nls(nls_codepage); return -EAGAIN; } } } else { up(&tcon->ses->sesSem); } unload_nls(nls_codepage); } else { return -EIO; } } if (rc) return rc; *request_buf = cifs_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; } /* Although the original thought was we needed the response buf for */ /* potential retries of smb operations it turns out we can determine */ /* from the mid flags when the request buffer can be resent without */ /* having to use a second distinct buffer for the response */ if (response_buf) *response_buf = *request_buf; header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct); if (tcon != NULL) cifs_stats_inc(&tcon->num_smbs_sent); return rc; } static int validate_t2(struct smb_t2_rsp *pSMB) { int rc = -EINVAL; int total_size; char *pBCC; /* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ if (pSMB->hdr.WordCount >= 10) { if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) { /* check that bcc is at least as big as parms + data */ /* check that bcc is less than negotiated smb buffer */ total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount); if (total_size < 512) { total_size += le16_to_cpu(pSMB->t2_rsp.DataCount); /* BCC le converted in SendReceive */ pBCC = (pSMB->hdr.WordCount * 2) + sizeof(struct smb_hdr) + (char *)pSMB; if ((total_size <= (*(u16 *)pBCC)) && (total_size < CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) { return 0; } } } } cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, sizeof(struct smb_t2_rsp) + 16); return rc; } int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) { NEGOTIATE_REQ *pSMB; NEGOTIATE_RSP *pSMBr; int rc = 0; int bytes_returned; int i; struct TCP_Server_Info *server; u16 count; unsigned int secFlags; u16 dialect; if (ses->server) server = ses->server; else { rc = -EIO; return rc; } rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; /* if any of auth flags (ie not sign or seal) are overriden use them */ if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */ else /* if override flags set only sign/seal OR them with global auth */ secFlags = extended_security | ses->overrideSecFlg; cFYI(1, ("secFlags 0x%x", secFlags)); pSMB->hdr.Mid = GetNextMid(server); pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) { cFYI(1, ("Kerberos only mechanism, enable extended security")); pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; } count = 0; for (i = 0; i < CIFS_NUM_PROT; i++) { strncpy(pSMB->DialectsArray+count, protocols[i].name, 16); count += strlen(protocols[i].name) + 1; /* null at end of source and target buffers anyway */ } pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc != 0) goto neg_err_exit; dialect = le16_to_cpu(pSMBr->DialectIndex); cFYI(1, ("Dialect: %d", dialect)); /* Check wct = 1 error case */ if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) { /* core returns wct = 1, but we do not ask for core - otherwise small wct just comes when dialect index is -1 indicating we could not negotiate a common dialect */ rc = -EOPNOTSUPP; goto neg_err_exit; #ifdef CONFIG_CIFS_WEAK_PW_HASH } else if ((pSMBr->hdr.WordCount == 13) && ((dialect == LANMAN_PROT) || (dialect == LANMAN2_PROT))) { __s16 tmp; struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; if ((secFlags & CIFSSEC_MAY_LANMAN) || (secFlags & CIFSSEC_MAY_PLNTXT)) server->secType = LANMAN; else { cERROR(1, ("mount failed weak security disabled" " in /proc/fs/cifs/SecurityFlags")); rc = -EOPNOTSUPP; goto neg_err_exit; } server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode); server->maxReq = le16_to_cpu(rsp->MaxMpxCount); server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey); /* even though we do not use raw we might as well set this accurately, in case we ever find a need for it */ if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { server->maxRw = 0xFF00; server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; } else { server->maxRw = 0;/* we do not need to use raw anyway */ server->capabilities = CAP_MPX_MODE; } tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); if (tmp == -1) { /* OS/2 often does not set timezone therefore * we must use server time to calc time zone. * Could deviate slightly from the right zone. * Smallest defined timezone difference is 15 minutes * (i.e. Nepal). Rounding up/down is done to match * this requirement. */ int val, seconds, remain, result; struct timespec ts, utc; utc = CURRENT_TIME; ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date), le16_to_cpu(rsp->SrvTime.Time)); cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d", (int)ts.tv_sec, (int)utc.tv_sec, (int)(utc.tv_sec - ts.tv_sec))); val = (int)(utc.tv_sec - ts.tv_sec); seconds = abs(val); result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; remain = seconds % MIN_TZ_ADJ; if (remain >= (MIN_TZ_ADJ / 2)) result += MIN_TZ_ADJ; if (val < 0) result = -result; server->timeAdj = result; } else { server->timeAdj = (int)tmp; server->timeAdj *= 60; /* also in seconds */ } cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj)); /* BB get server time for time conversions and add code to use it and timezone since this is not UTC */ if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { memcpy(server->cryptKey, rsp->EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if (server->secMode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* need cryptkey unless plain text */ goto neg_err_exit; } cFYI(1, ("LANMAN negotiated")); /* we will not end up setting signing flags - as no signing was in LANMAN and server did not return the flags on */ goto signing_check; #else /* weak security disabled */ } else if (pSMBr->hdr.WordCount == 13) { cERROR(1, ("mount failed, cifs module not built " "with CIFS_WEAK_PW_HASH support")); rc = -EOPNOTSUPP; #endif /* WEAK_PW_HASH */ goto neg_err_exit; } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ rc = -EOPNOTSUPP; goto neg_err_exit; } /* else wct == 17 NTLM */ server->secMode = pSMBr->SecurityMode; if ((server->secMode & SECMODE_USER) == 0) cFYI(1, ("share mode security")); if ((server->secMode & SECMODE_PW_ENCRYPT) == 0) #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0) #endif /* CIFS_WEAK_PW_HASH */ cERROR(1, ("Server requests plain text password" " but client support disabled")); if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) server->secType = NTLMv2; else if (secFlags & CIFSSEC_MAY_NTLM) server->secType = NTLM; else if (secFlags & CIFSSEC_MAY_NTLMV2) server->secType = NTLMv2; else if (secFlags & CIFSSEC_MAY_KRB5) server->secType = Kerberos; else if (secFlags & CIFSSEC_MAY_LANMAN) server->secType = LANMAN; /* #ifdef CONFIG_CIFS_EXPERIMENTAL else if (secFlags & CIFSSEC_MAY_PLNTXT) server->secType = ?? #endif */ else { rc = -EOPNOTSUPP; cERROR(1, ("Invalid security type")); goto neg_err_exit; } /* else ... any others ...? */ /* one byte, so no need to convert this or EncryptionKeyLen from little endian */ server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); /* probably no need to store and check maxvcs */ server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf)); GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); server->capabilities = le32_to_cpu(pSMBr->Capabilities); server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { memcpy(server->cryptKey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (pSMBr->EncryptionKeyLength == 0)) { /* decode security blob */ } else if (server->secMode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* no crypt key only if plain text pwd */ goto neg_err_exit; } /* BB might be helpful to save off the domain of server here */ if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (server->capabilities & CAP_EXTENDED_SECURITY)) { count = pSMBr->ByteCount; if (count < 16) { rc = -EIO; goto neg_err_exit; } read_lock(&cifs_tcp_ses_lock); if (server->srv_count > 1) { read_unlock(&cifs_tcp_ses_lock); if (memcmp(server->server_GUID, pSMBr->u.extended_response. GUID, 16) != 0) { cFYI(1, ("server UID changed")); memcpy(server->server_GUID, pSMBr->u.extended_response.GUID, 16); } } else { read_unlock(&cifs_tcp_ses_lock); memcpy(server->server_GUID, pSMBr->u.extended_response.GUID, 16); } if (count == 16) { server->secType = RawNTLMSSP; } else { rc = decode_negTokenInit(pSMBr->u.extended_response. SecurityBlob, count - 16, &server->secType); if (rc == 1) rc = 0; else rc = -EINVAL; } } else server->capabilities &= ~CAP_EXTENDED_SECURITY; #ifdef CONFIG_CIFS_WEAK_PW_HASH signing_check: #endif if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { /* MUST_SIGN already includes the MAY_SIGN FLAG so if this is zero it means that signing is disabled */ cFYI(1, ("Signing disabled")); if (server->secMode & SECMODE_SIGN_REQUIRED) { cERROR(1, ("Server requires " "packet signing to be enabled in " "/proc/fs/cifs/SecurityFlags.")); rc = -EOPNOTSUPP; } server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { /* signing required */ cFYI(1, ("Must sign - secFlags 0x%x", secFlags)); if ((server->secMode & (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { cERROR(1, ("signing required but server lacks support")); rc = -EOPNOTSUPP; } else server->secMode |= SECMODE_SIGN_REQUIRED; } else { /* signing optional ie CIFSSEC_MAY_SIGN */ if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0) server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } neg_err_exit: cifs_buf_release(pSMB); cFYI(1, ("negprot rc %d", rc)); return rc; } int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) { struct smb_hdr *smb_buffer; int rc = 0; cFYI(1, ("In tree disconnect")); /* BB: do we need to check this? These should never be NULL. */ if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) return -EIO; /* * No need to return error on this operation if tid invalidated and * closed on server already e.g. due to tcp session crashing. Also, * the tcon is no longer on the list, so no need to take lock before * checking this. */ if (tcon->need_reconnect) return 0; rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, (void **)&smb_buffer); if (rc) return rc; rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); if (rc) cFYI(1, ("Tree disconnect failed %d", rc)); /* No need to return error on this operation if tid invalidated and closed on server already e.g. due to tcp session crashing */ if (rc == -EAGAIN) rc = 0; return rc; } int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) { LOGOFF_ANDX_REQ *pSMB; int rc = 0; cFYI(1, ("In SMBLogoff for session disconnect")); /* * BB: do we need to check validity of ses and server? They should * always be valid since we have an active reference. If not, that * should probably be a BUG() */ if (!ses || !ses->server) return -EIO; down(&ses->sesSem); if (ses->need_reconnect) goto session_already_dead; /* no need to send SMBlogoff if uid already closed due to reconnect */ rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); if (rc) { up(&ses->sesSem); return rc; } pSMB->hdr.Mid = GetNextMid(ses->server); if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->hdr.Uid = ses->Suid; pSMB->AndXCommand = 0xFF; rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); session_already_dead: up(&ses->sesSem); /* if session dead then we do not need to do ulogoff, since server closed smb session, no sense reporting error */ if (rc == -EAGAIN) rc = 0; return rc; } int CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, __u16 type, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; struct unlink_psx_rq *pRqD; int name_len; int rc = 0; int bytes_returned = 0; __u16 params, param_offset, offset, byte_count; cFYI(1, ("In POSIX delete")); PsxDelete: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB add path length overrun check */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } params = 6 + name_len; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = 0; /* BB double check this with jra */ pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; /* Setup pointer to Request Data (inode type) */ pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset); pRqD->type = cpu_to_le16(type); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("Posix delete returned %d", rc)); cifs_buf_release(pSMB); cifs_stats_inc(&tcon->num_deletes); if (rc == -EAGAIN) goto PsxDelete; return rc; } int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, const struct nls_table *nls_codepage, int remap) { DELETE_FILE_REQ *pSMB = NULL; DELETE_FILE_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; DelFileRetry: rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->fileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->fileName, fileName, name_len); } pSMB->SearchAttributes = cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM); pSMB->BufferFormat = 0x04; pSMB->hdr.smb_buf_length += name_len + 1; pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_deletes); if (rc) cFYI(1, ("Error in RMFile = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto DelFileRetry; return rc; } int CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName, const struct nls_table *nls_codepage, int remap) { DELETE_DIRECTORY_REQ *pSMB = NULL; DELETE_DIRECTORY_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; cFYI(1, ("In CIFSSMBRmDir")); RmDirRetry: rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ name_len = strnlen(dirName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->DirName, dirName, name_len); } pSMB->BufferFormat = 0x04; pSMB->hdr.smb_buf_length += name_len + 1; pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_rmdirs); if (rc) cFYI(1, ("Error in RMDir = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto RmDirRetry; return rc; } int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, int remap) { int rc = 0; CREATE_DIRECTORY_REQ *pSMB = NULL; CREATE_DIRECTORY_RSP *pSMBr = NULL; int bytes_returned; int name_len; cFYI(1, ("In CIFSSMBMkDir")); MkDirRetry: rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ name_len = strnlen(name, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->DirName, name, name_len); } pSMB->BufferFormat = 0x04; pSMB->hdr.smb_buf_length += name_len + 1; pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_mkdirs); if (rc) cFYI(1, ("Error in Mkdir = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto MkDirRetry; return rc; } int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags, __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, const char *name, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; int name_len; int rc = 0; int bytes_returned = 0; __u16 params, param_offset, offset, byte_count, count; OPEN_PSX_REQ *pdata; OPEN_PSX_RSP *psx_rsp; cFYI(1, ("In POSIX Create")); PsxCreat: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, name, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(name, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, name, name_len); } params = 6 + name_len; count = sizeof(OPEN_PSX_REQ); pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */ pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset); pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); pdata->Permissions = cpu_to_le64(mode); pdata->PosixOpenFlags = cpu_to_le32(posix_flags); pdata->OpenFlags = cpu_to_le32(*pOplock); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Posix create returned %d", rc)); goto psx_create_err; } cFYI(1, ("copying inode info")); rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) { rc = -EIO; /* bad smb */ goto psx_create_err; } /* copy return information to pRetData */ psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.DataOffset)); *pOplock = le16_to_cpu(psx_rsp->OplockFlags); if (netfid) *netfid = psx_rsp->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) *pOplock |= CIFS_CREATE_ACTION; /* check to make sure response data is there */ if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) { pRetData->Type = cpu_to_le32(-1); /* unknown */ cFYI(DBG2, ("unknown type")); } else { if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP) + sizeof(FILE_UNIX_BASIC_INFO)) { cERROR(1, ("Open response data too small")); pRetData->Type = cpu_to_le32(-1); goto psx_create_err; } memcpy((char *) pRetData, (char *)psx_rsp + sizeof(OPEN_PSX_RSP), sizeof(FILE_UNIX_BASIC_INFO)); } psx_create_err: cifs_buf_release(pSMB); cifs_stats_inc(&tcon->num_mkdirs); if (rc == -EAGAIN) goto PsxCreat; return rc; } static __u16 convert_disposition(int disposition) { __u16 ofun = 0; switch (disposition) { case FILE_SUPERSEDE: ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; break; case FILE_OPEN: ofun = SMBOPEN_OAPPEND; break; case FILE_CREATE: ofun = SMBOPEN_OCREATE; break; case FILE_OPEN_IF: ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; break; case FILE_OVERWRITE: ofun = SMBOPEN_OTRUNC; break; case FILE_OVERWRITE_IF: ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; break; default: cFYI(1, ("unknown disposition %d", disposition)); ofun = SMBOPEN_OAPPEND; /* regular open */ } return ofun; } static int access_flags_to_smbopen_mode(const int access_flags) { int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE); if (masked_flags == GENERIC_READ) return SMBOPEN_READ; else if (masked_flags == GENERIC_WRITE) return SMBOPEN_WRITE; /* just go for read/write */ return SMBOPEN_READWRITE; } int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int openDisposition, const int access_flags, const int create_options, __u16 *netfid, int *pOplock, FILE_ALL_INFO *pfile_info, const struct nls_table *nls_codepage, int remap) { int rc = -EACCES; OPENX_REQ *pSMB = NULL; OPENX_RSP *pSMBr = NULL; int bytes_returned; int name_len; __u16 count; OldOpenRetry: rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->AndXCommand = 0xFF; /* none */ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { count = 1; /* account for one byte pad to word boundary */ name_len = cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ count = 0; /* no pad */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->fileName, fileName, name_len); } if (*pOplock & REQ_OPLOCK) pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); else if (*pOplock & REQ_BATCHOPLOCK) pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK); pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO); pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags)); pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ /* set file as system file if special file such as fifo and server expecting SFU style and no Unix extensions */ if (create_options & CREATE_OPTION_SPECIAL) pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); else /* BB FIXME BB */ pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); if (create_options & CREATE_OPTION_READONLY) pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY); /* BB FIXME BB */ /* pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */ /* BB FIXME END BB */ pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition)); count += name_len; pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); } else { /* BB verify if wct == 15 */ /* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/ *netfid = pSMBr->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ /* BB FIXME BB */ /* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) *pOplock |= CIFS_CREATE_ACTION; */ /* BB FIXME END */ if (pfile_info) { pfile_info->CreationTime = 0; /* BB convert CreateTime*/ pfile_info->LastAccessTime = 0; /* BB fixme */ pfile_info->LastWriteTime = 0; /* BB fixme */ pfile_info->ChangeTime = 0; /* BB fixme */ pfile_info->Attributes = cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); /* the file_info buf is endian converted by caller */ pfile_info->AllocationSize = cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile)); pfile_info->EndOfFile = pfile_info->AllocationSize; pfile_info->NumberOfLinks = cpu_to_le32(1); pfile_info->DeletePending = 0; } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto OldOpenRetry; return rc; } int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int openDisposition, const int access_flags, const int create_options, __u16 *netfid, int *pOplock, FILE_ALL_INFO *pfile_info, const struct nls_table *nls_codepage, int remap) { int rc = -EACCES; OPEN_REQ *pSMB = NULL; OPEN_RSP *pSMBr = NULL; int bytes_returned; int name_len; __u16 count; openRetry: rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->AndXCommand = 0xFF; /* none */ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { count = 1; /* account for one byte pad to word boundary */ name_len = cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->NameLength = cpu_to_le16(name_len); } else { /* BB improve check for buffer overruns BB */ count = 0; /* no pad */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ pSMB->NameLength = cpu_to_le16(name_len); strncpy(pSMB->fileName, fileName, name_len); } if (*pOplock & REQ_OPLOCK) pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK); else if (*pOplock & REQ_BATCHOPLOCK) pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); pSMB->DesiredAccess = cpu_to_le32(access_flags); pSMB->AllocationSize = 0; /* set file as system file if special file such as fifo and server expecting SFU style and no Unix extensions */ if (create_options & CREATE_OPTION_SPECIAL) pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); else pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); /* XP does not handle ATTR_POSIX_SEMANTICS */ /* but it helps speed up case sensitive checks for other servers such as Samba */ if (tcon->ses->capabilities & CAP_UNIX) pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); if (create_options & CREATE_OPTION_READONLY) pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY); pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); pSMB->CreateDisposition = cpu_to_le32(openDisposition); pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); /* BB Expirement with various impersonation levels and verify */ pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); pSMB->SecurityFlags = SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY; count += name_len; pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); } else { *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */ *netfid = pSMBr->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) *pOplock |= CIFS_CREATE_ACTION; if (pfile_info) { memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, 36 /* CreationTime to Attributes */); /* the file_info buf is endian converted by caller */ pfile_info->AllocationSize = pSMBr->AllocationSize; pfile_info->EndOfFile = pSMBr->EndOfFile; pfile_info->NumberOfLinks = cpu_to_le32(1); pfile_info->DeletePending = 0; } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto openRetry; return rc; } int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 lseek, unsigned int *nbytes, char **buf, int *pbuf_type) { int rc = -EACCES; READ_REQ *pSMB = NULL; READ_RSP *pSMBr = NULL; char *pReadData = NULL; int wct; int resp_buf_type = 0; struct kvec iov[1]; cFYI(1, ("Reading %d bytes on fid %d", count, netfid)); if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 12; else { wct = 10; /* old style read */ if ((lseek >> 32) > 0) { /* can not handle this big offset for old */ return -EIO; } } *nbytes = 0; rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB); if (rc) return rc; /* tcon and ses pointer are checked in smb_init */ if (tcon->ses->server == NULL) return -ECONNABORTED; pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); if (wct == 12) pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); pSMB->Remaining = 0; pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); pSMB->MaxCountHigh = cpu_to_le32(count >> 16); if (wct == 12) pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ else { /* old style read */ struct smb_com_readx_req *pSMBW = (struct smb_com_readx_req *)pSMB; pSMBW->ByteCount = 0; } iov[0].iov_base = (char *)pSMB; iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR); cifs_stats_inc(&tcon->num_reads); pSMBr = (READ_RSP *)iov[0].iov_base; if (rc) { cERROR(1, ("Send error in read = %d", rc)); } else { int data_length = le16_to_cpu(pSMBr->DataLengthHigh); data_length = data_length << 16; data_length += le16_to_cpu(pSMBr->DataLength); *nbytes = data_length; /*check that DataLength would not go beyond end of SMB */ if ((data_length > CIFSMaxBufSize) || (data_length > count)) { cFYI(1, ("bad length %d for count %d", data_length, count)); rc = -EIO; *nbytes = 0; } else { pReadData = (char *) (&pSMBr->hdr.Protocol) + le16_to_cpu(pSMBr->DataOffset); /* if (rc = copy_to_user(buf, pReadData, data_length)) { cERROR(1,("Faulting on read rc = %d",rc)); rc = -EFAULT; }*/ /* can not use copy_to_user when using page cache*/ if (*buf) memcpy(*buf, pReadData, data_length); } } /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ if (*buf) { if (resp_buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); else if (resp_buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); } else if (resp_buf_type != CIFS_NO_BUFFER) { /* return buffer to caller to free */ *buf = iov[0].iov_base; if (resp_buf_type == CIFS_SMALL_BUFFER) *pbuf_type = CIFS_SMALL_BUFFER; else if (resp_buf_type == CIFS_LARGE_BUFFER) *pbuf_type = CIFS_LARGE_BUFFER; } /* else no valid buffer on return - leave as null */ /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, const char *buf, const char __user *ubuf, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; WRITE_RSP *pSMBr = NULL; int bytes_returned, wct; __u32 bytes_sent; __u16 byte_count; /* cFYI(1, ("write at %lld %d bytes", offset, count));*/ if (tcon->ses == NULL) return -ECONNABORTED; if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 14; else { wct = 12; if ((offset >> 32) > 0) { /* can not handle big offset for old srv */ return -EIO; } } rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; /* tcon and ses pointer are checked in smb_init */ if (tcon->ses->server == NULL) return -ECONNABORTED; pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); if (wct == 14) pSMB->OffsetHigh = cpu_to_le32(offset >> 32); pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; /* Can increase buffer size if buffer is big enough in some cases ie we can send more if LARGE_WRITE_X capability returned by the server and if our buffer is big enough or if we convert to iovecs on socket writes and eliminate the copy to the CIFS buffer */ if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) { bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); } else { bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & ~0xFF; } if (bytes_sent > count) bytes_sent = count; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); if (buf) memcpy(pSMB->Data, buf, bytes_sent); else if (ubuf) { if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) { cifs_buf_release(pSMB); return -EFAULT; } } else if (count != 0) { /* No buffer */ cifs_buf_release(pSMB); return -EINVAL; } /* else setting file size with write of zero bytes */ if (wct == 14) byte_count = bytes_sent + 1; /* pad */ else /* wct == 12 */ byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */ pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); pSMB->hdr.smb_buf_length += byte_count; if (wct == 14) pSMB->ByteCount = cpu_to_le16(byte_count); else { /* old style write has byte count 4 bytes earlier so 4 bytes pad */ struct smb_com_writex_req *pSMBW = (struct smb_com_writex_req *)pSMB; pSMBW->ByteCount = cpu_to_le16(byte_count); } rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, long_op); cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error in write = %d", rc)); *nbytes = 0; } else { *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; *nbytes += le16_to_cpu(pSMBr->Count); } cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, struct kvec *iov, int n_vec, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; int wct; int smb_hdr_len; int resp_buf_type = 0; cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count)); if (tcon->ses->capabilities & CAP_LARGE_FILES) { wct = 14; } else { wct = 12; if ((offset >> 32) > 0) { /* can not handle big offset for old srv */ return -EIO; } } rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); if (rc) return rc; /* tcon and ses pointer are checked in smb_init */ if (tcon->ses->server == NULL) return -ECONNABORTED; pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); if (wct == 14) pSMB->OffsetHigh = cpu_to_le32(offset >> 32); pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; pSMB->DataOffset = cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(count >> 16); smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ if (wct == 14) pSMB->hdr.smb_buf_length += count+1; else /* wct == 12 */ pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ if (wct == 14) pSMB->ByteCount = cpu_to_le16(count + 1); else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { struct smb_com_writex_req *pSMBW = (struct smb_com_writex_req *)pSMB; pSMBW->ByteCount = cpu_to_le16(count + 5); } iov[0].iov_base = pSMB; if (wct == 14) iov[0].iov_len = smb_hdr_len + 4; else /* wct == 12 pad bigger by four bytes */ iov[0].iov_len = smb_hdr_len + 8; rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, long_op); cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error Write2 = %d", rc)); *nbytes = 0; } else if (resp_buf_type == 0) { /* presumably this can not happen, but best to be safe */ rc = -EIO; *nbytes = 0; } else { WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base; *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; *nbytes += le16_to_cpu(pSMBr->Count); } /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ if (resp_buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); else if (resp_buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const __u64 len, const __u64 offset, const __u32 numUnlock, const __u32 numLock, const __u8 lockType, const bool waitFlag) { int rc = 0; LOCK_REQ *pSMB = NULL; /* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */ int bytes_returned; int timeout = 0; __u16 count; cFYI(1, ("CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock)); rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); if (rc) return rc; if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { timeout = CIFS_ASYNC_OP; /* no response expected */ pSMB->Timeout = 0; } else if (waitFlag) { timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ } else { pSMB->Timeout = 0; } pSMB->NumberOfLocks = cpu_to_le16(numLock); pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); pSMB->LockType = lockType; pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = smb_file_id; /* netfid stays le */ if ((numLock != 0) || (numUnlock != 0)) { pSMB->Locks[0].Pid = cpu_to_le16(current->tgid); /* BB where to store pid high? */ pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset); pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32)); count = sizeof(LOCKING_ANDX_RANGE); } else { /* oplock break */ count = 0; } pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); if (waitFlag) { rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMB, &bytes_returned); cifs_small_buf_release(pSMB); } else { rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB, timeout); /* SMB buffer freed by function above */ } cifs_stats_inc(&tcon->num_locks); if (rc) cFYI(1, ("Send error in Lock = %d", rc)); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const int get_flag, const __u64 len, struct file_lock *pLockData, const __u16 lock_type, const bool waitFlag) { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; struct cifs_posix_lock *parm_data; int rc = 0; int timeout = 0; int bytes_returned = 0; int resp_buf_type = 0; __u16 params, param_offset, offset, byte_count, count; struct kvec iov[1]; cFYI(1, ("Posix Lock")); if (pLockData == NULL) return -EINVAL; rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); if (rc) return rc; pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; count = sizeof(struct cifs_posix_lock); pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ pSMB->SetupCount = 1; pSMB->Reserved3 = 0; if (get_flag) pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); else pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); parm_data = (struct cifs_posix_lock *) (((char *) &pSMB->hdr.Protocol) + offset); parm_data->lock_type = cpu_to_le16(lock_type); if (waitFlag) { timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ parm_data->lock_flags = cpu_to_le16(1); pSMB->Timeout = cpu_to_le32(-1); } else pSMB->Timeout = 0; parm_data->pid = cpu_to_le32(current->tgid); parm_data->start = cpu_to_le64(pLockData->fl_start); parm_data->length = cpu_to_le64(len); /* normalize negative numbers */ pSMB->DataOffset = cpu_to_le16(offset); pSMB->Fid = smb_file_id; pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); if (waitFlag) { rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned); } else { iov[0].iov_base = (char *)pSMB; iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, &resp_buf_type, timeout); pSMB = NULL; /* request buf already freed by SendReceive2. Do not try to free it twice below on exit */ pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base; } if (rc) { cFYI(1, ("Send error in Posix Lock = %d", rc)); } else if (get_flag) { /* lock structure can be returned on get */ __u16 data_offset; __u16 data_count; rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) { rc = -EIO; /* bad smb */ goto plk_err_exit; } if (pLockData == NULL) { rc = -EINVAL; goto plk_err_exit; } data_offset = le16_to_cpu(pSMBr->t2.DataOffset); data_count = le16_to_cpu(pSMBr->t2.DataCount); if (data_count < sizeof(struct cifs_posix_lock)) { rc = -EIO; goto plk_err_exit; } parm_data = (struct cifs_posix_lock *) ((char *)&pSMBr->hdr.Protocol + data_offset); if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) pLockData->fl_type = F_UNLCK; } plk_err_exit: if (pSMB) cifs_small_buf_release(pSMB); if (resp_buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); else if (resp_buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) { int rc = 0; CLOSE_REQ *pSMB = NULL; cFYI(1, ("In CIFSSMBClose")); /* do not retry on dead session on close */ rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); if (rc == -EAGAIN) return 0; if (rc) return rc; pSMB->FileID = (__u16) smb_file_id; pSMB->LastWriteTime = 0xFFFFFFFF; pSMB->ByteCount = 0; rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); cifs_stats_inc(&tcon->num_closes); if (rc) { if (rc != -EINTR) { /* EINTR is expected when user ctl-c to kill app */ cERROR(1, ("Send error in Close = %d", rc)); } } /* Since session is dead, file will be closed on server already */ if (rc == -EAGAIN) rc = 0; return rc; } int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap) { int rc = 0; RENAME_REQ *pSMB = NULL; RENAME_RSP *pSMBr = NULL; int bytes_returned; int name_len, name_len2; __u16 count; cFYI(1, ("In CIFSSMBRename")); renameRetry: rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->BufferFormat = 0x04; pSMB->SearchAttributes = cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->OldFileName[name_len] = 0x04; /* pad */ /* protocol requires ASCII signature byte on Unicode string */ pSMB->OldFileName[name_len + 1] = 0x00; name_len2 = cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); name_len2 = strnlen(toName, PATH_MAX); name_len2++; /* trailing null */ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); name_len2++; /* trailing null */ name_len2++; /* signature byte */ } count = 1 /* 1st signature byte */ + name_len + name_len2; pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_renames); if (rc) cFYI(1, ("Send error in rename = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto renameRetry; return rc; } int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, int netfid, const char *target_name, const struct nls_table *nls_codepage, int remap) { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; struct set_file_rename *rename_info; char *data_offset; char dummy_string[30]; int rc = 0; int bytes_returned = 0; int len_of_str; __u16 params, param_offset, offset, count, byte_count; cFYI(1, ("Rename to File by handle")); rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; rename_info = (struct set_file_rename *) data_offset; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); byte_count = 3 /* pad */ + params; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); /* construct random name ".cifs_tmp" */ rename_info->overwrite = cpu_to_le32(1); rename_info->root_fid = 0; /* unicode only call */ if (target_name == NULL) { sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, dummy_string, 24, nls_codepage, remap); } else { len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, target_name, PATH_MAX, nls_codepage, remap); } rename_info->target_name_len = cpu_to_le32(2 * len_of_str); count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str); byte_count += count; pSMB->DataCount = cpu_to_le16(count); pSMB->TotalDataCount = pSMB->DataCount; pSMB->Fid = netfid; pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&pTcon->num_t2renames); if (rc) cFYI(1, ("Send error in Rename (by file handle) = %d", rc)); cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName, const __u16 target_tid, const char *toName, const int flags, const struct nls_table *nls_codepage, int remap) { int rc = 0; COPY_REQ *pSMB = NULL; COPY_RSP *pSMBr = NULL; int bytes_returned; int name_len, name_len2; __u16 count; cFYI(1, ("In CIFSSMBCopy")); copyRetry: rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->BufferFormat = 0x04; pSMB->Tid2 = target_tid; pSMB->Flags = cpu_to_le16(flags & COPY_TREE); if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->OldFileName[name_len] = 0x04; /* pad */ /* protocol requires ASCII signature byte on Unicode string */ pSMB->OldFileName[name_len + 1] = 0x00; name_len2 = cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); name_len2 = strnlen(toName, PATH_MAX); name_len2++; /* trailing null */ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); name_len2++; /* trailing null */ name_len2++; /* signature byte */ } count = 1 /* 1st signature byte */ + name_len + name_len2; pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in copy = %d with %d files copied", rc, le16_to_cpu(pSMBr->CopyCount))); } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto copyRetry; return rc; } int CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; char *data_offset; int name_len; int name_len_target; int rc = 0; int bytes_returned = 0; __u16 params, param_offset, offset, byte_count; cFYI(1, ("In Symlink Unix style")); createSymLinkRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fromName, name_len); } params = 6 + name_len; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len_target = cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX /* find define for this maxpathcomponent */ , nls_codepage); name_len_target++; /* trailing null */ name_len_target *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len_target = strnlen(toName, PATH_MAX); name_len_target++; /* trailing null */ strncpy(data_offset, toName, name_len_target); } pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max on data count below from sess */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + name_len_target; pSMB->DataCount = cpu_to_le16(name_len_target); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_symlinks); if (rc) cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto createSymLinkRetry; return rc; } int CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; char *data_offset; int name_len; int name_len_target; int rc = 0; int bytes_returned = 0; __u16 params, param_offset, offset, byte_count; cFYI(1, ("In Create Hard link Unix style")); createHardLinkRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(toName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, toName, name_len); } params = 6 + name_len; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len_target = cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX, nls_codepage, remap); name_len_target++; /* trailing null */ name_len_target *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len_target = strnlen(fromName, PATH_MAX); name_len_target++; /* trailing null */ strncpy(data_offset, fromName, name_len_target); } pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max on data count below from sess*/ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + name_len_target; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->DataCount = cpu_to_le16(name_len_target); pSMB->TotalDataCount = pSMB->DataCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_hardlinks); if (rc) cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto createHardLinkRetry; return rc; } int CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap) { int rc = 0; NT_RENAME_REQ *pSMB = NULL; RENAME_RSP *pSMBr = NULL; int bytes_returned; int name_len, name_len2; __u16 count; cFYI(1, ("In CIFSCreateHardLink")); winCreateHardLinkRetry: rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->SearchAttributes = cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK); pSMB->ClusterCount = 0; pSMB->BufferFormat = 0x04; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->OldFileName[name_len] = 0; /* pad */ pSMB->OldFileName[name_len + 1] = 0x04; name_len2 = cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); name_len2 = strnlen(toName, PATH_MAX); name_len2++; /* trailing null */ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); name_len2++; /* trailing null */ name_len2++; /* signature byte */ } count = 1 /* string type byte */ + name_len + name_len2; pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_hardlinks); if (rc) cFYI(1, ("Send error in hard link (NT rename) = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto winCreateHardLinkRetry; return rc; } int CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *symlinkinfo, const int buflen, const struct nls_table *nls_codepage) { /* SMB_QUERY_FILE_UNIX_LINK */ TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; __u16 params, byte_count; cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName)); querySymLinkRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifs_strtoUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max data count below from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); __u16 count = le16_to_cpu(pSMBr->t2.DataCount); if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = UniStrnlen((wchar_t *) ((char *) &pSMBr->hdr.Protocol + data_offset), min_t(const int, buflen, count) / 2); /* BB FIXME investigate remapping reserved chars here */ cifs_strfromUCS_le(symlinkinfo, (__le16 *) ((char *)&pSMBr->hdr.Protocol + data_offset), name_len, nls_codepage); } else { strncpy(symlinkinfo, (char *) &pSMBr->hdr.Protocol + data_offset, min_t(const int, buflen, count)); } symlinkinfo[buflen] = 0; /* just in case so calling code does not go off the end of buffer */ } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto querySymLinkRetry; return rc; } #ifdef CONFIG_CIFS_EXPERIMENTAL /* Initialize NT TRANSACT SMB into small smb request buffer. This assumes that all NT TRANSACTS that we init here have total parm and data under about 400 bytes (to fit in small cifs buffer size), which is the case so far, it easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of returned setup area) and MaxParameterCount (returned parms size) must be set by caller */ static int smb_init_nttransact(const __u16 sub_command, const int setup_count, const int parm_len, struct cifsTconInfo *tcon, void **ret_buf) { int rc; __u32 temp_offset; struct smb_com_ntransact_req *pSMB; rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, (void **)&pSMB); if (rc) return rc; *ret_buf = (void *)pSMB; pSMB->Reserved = 0; pSMB->TotalParameterCount = cpu_to_le32(parm_len); pSMB->TotalDataCount = 0; pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->DataCount = pSMB->TotalDataCount; temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + (setup_count * 2) - 4 /* for rfc1001 length itself */; pSMB->ParameterOffset = cpu_to_le32(temp_offset); pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ pSMB->SubCommand = cpu_to_le16(sub_command); return 0; } static int validate_ntransact(char *buf, char **ppparm, char **ppdata, __u32 *pparmlen, __u32 *pdatalen) { char *end_of_smb; __u32 data_count, data_offset, parm_count, parm_offset; struct smb_com_ntransact_rsp *pSMBr; *pdatalen = 0; *pparmlen = 0; if (buf == NULL) return -EINVAL; pSMBr = (struct smb_com_ntransact_rsp *)buf; /* ByteCount was converted from little endian in SendReceive */ end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + (char *)&pSMBr->ByteCount; data_offset = le32_to_cpu(pSMBr->DataOffset); data_count = le32_to_cpu(pSMBr->DataCount); parm_offset = le32_to_cpu(pSMBr->ParameterOffset); parm_count = le32_to_cpu(pSMBr->ParameterCount); *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; /* should we also check that parm and data areas do not overlap? */ if (*ppparm > end_of_smb) { cFYI(1, ("parms start after end of smb")); return -EINVAL; } else if (parm_count + *ppparm > end_of_smb) { cFYI(1, ("parm end after end of smb")); return -EINVAL; } else if (*ppdata > end_of_smb) { cFYI(1, ("data starts after end of smb")); return -EINVAL; } else if (data_count + *ppdata > end_of_smb) { cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p", *ppdata, data_count, (data_count + *ppdata), end_of_smb, pSMBr)); return -EINVAL; } else if (parm_count + data_count > pSMBr->ByteCount) { cFYI(1, ("parm count and data count larger than SMB")); return -EINVAL; } *pdatalen = data_count; *pparmlen = parm_count; return 0; } #endif /* CIFS_EXPERIMENTAL */ int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *symlinkinfo, const int buflen, __u16 fid, const struct nls_table *nls_codepage) { int rc = 0; int bytes_returned; int name_len; struct smb_com_transaction_ioctl_req *pSMB; struct smb_com_transaction_ioctl_rsp *pSMBr; cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName)); rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->TotalParameterCount = 0 ; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le32(2); /* BB find exact data count max from sess structure BB */ pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); pSMB->MaxSetupCount = 4; pSMB->Reserved = 0; pSMB->ParameterOffset = 0; pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 4; pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); pSMB->IsFsctl = 1; /* FSCTL */ pSMB->IsRootFlag = 0; pSMB->Fid = fid; /* file handle always le */ pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc)); } else { /* decode response */ __u32 data_offset = le32_to_cpu(pSMBr->DataOffset); __u32 data_count = le32_to_cpu(pSMBr->DataCount); if ((pSMBr->ByteCount < 2) || (data_offset > 512)) /* BB also check enough total bytes returned */ rc = -EIO; /* bad smb */ else { if (data_count && (data_count < 2048)) { char *end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + (char *)&pSMBr->ByteCount; struct reparse_data *reparse_buf = (struct reparse_data *) ((char *)&pSMBr->hdr.Protocol + data_offset); if ((char *)reparse_buf >= end_of_smb) { rc = -EIO; goto qreparse_out; } if ((reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset + reparse_buf->TargetNameLen) > end_of_smb) { cFYI(1, ("reparse buf beyond SMB")); rc = -EIO; goto qreparse_out; } if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = UniStrnlen((wchar_t *) (reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset), min(buflen/2, reparse_buf->TargetNameLen / 2)); cifs_strfromUCS_le(symlinkinfo, (__le16 *) (reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset), name_len, nls_codepage); } else { /* ASCII names */ strncpy(symlinkinfo, reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset, min_t(const int, buflen, reparse_buf->TargetNameLen)); } } else { rc = -EIO; cFYI(1, ("Invalid return data count on " "get reparse info ioctl")); } symlinkinfo[buflen] = 0; /* just in case so the caller does not go off the end of the buffer */ cFYI(1, ("readlink result - %s", symlinkinfo)); } } qreparse_out: cifs_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } #ifdef CONFIG_CIFS_POSIX /*Convert an Access Control Entry from wire format to local POSIX xattr format*/ static void cifs_convert_ace(posix_acl_xattr_entry *ace, struct cifs_posix_ace *cifs_ace) { /* u8 cifs fields do not need le conversion */ ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */ return; } /* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, const int acl_type, const int size_of_data_area) { int size = 0; int i; __u16 count; struct cifs_posix_ace *pACE; struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt; if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) return -EOPNOTSUPP; if (acl_type & ACL_TYPE_ACCESS) { count = le16_to_cpu(cifs_acl->access_entry_count); pACE = &cifs_acl->ace_array[0]; size = sizeof(struct cifs_posix_acl); size += sizeof(struct cifs_posix_ace) * count; /* check if we would go beyond end of SMB */ if (size_of_data_area < size) { cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d", size_of_data_area, size)); return -EINVAL; } } else if (acl_type & ACL_TYPE_DEFAULT) { count = le16_to_cpu(cifs_acl->access_entry_count); size = sizeof(struct cifs_posix_acl); size += sizeof(struct cifs_posix_ace) * count; /* skip past access ACEs to get to default ACEs */ pACE = &cifs_acl->ace_array[count]; count = le16_to_cpu(cifs_acl->default_entry_count); size += sizeof(struct cifs_posix_ace) * count; /* check if we would go beyond end of SMB */ if (size_of_data_area < size) return -EINVAL; } else { /* illegal type */ return -EINVAL; } size = posix_acl_xattr_size(count); if ((buflen == 0) || (local_acl == NULL)) { /* used to query ACL EA size */ } else if (size > buflen) { return -ERANGE; } else /* buffer big enough */ { local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); for (i = 0; i < count ; i++) { cifs_convert_ace(&local_acl->a_entries[i], pACE); pACE++; } } return size; } static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, const posix_acl_xattr_entry *local_ace) { __u16 rc = 0; /* 0 = ACL converted ok */ cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); /* BB is there a better way to handle the large uid? */ if (local_ace->e_id == cpu_to_le32(-1)) { /* Probably no need to le convert -1 on any arch but can not hurt */ cifs_ace->cifs_uid = cpu_to_le64(-1); } else cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/ return rc; } /* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, const int buflen, const int acl_type) { __u16 rc = 0; struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL; int count; int i; if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) return 0; count = posix_acl_xattr_count((size_t)buflen); cFYI(1, ("setting acl with %d entries from buf of length %d and " "version of %d", count, buflen, le32_to_cpu(local_acl->a_version))); if (le32_to_cpu(local_acl->a_version) != 2) { cFYI(1, ("unknown POSIX ACL version %d", le32_to_cpu(local_acl->a_version))); return 0; } cifs_acl->version = cpu_to_le16(1); if (acl_type == ACL_TYPE_ACCESS) cifs_acl->access_entry_count = cpu_to_le16(count); else if (acl_type == ACL_TYPE_DEFAULT) cifs_acl->default_entry_count = cpu_to_le16(count); else { cFYI(1, ("unknown ACL type %d", acl_type)); return 0; } for (i = 0; i < count; i++) { rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &local_acl->a_entries[i]); if (rc != 0) { /* ACE not converted */ break; } } if (rc == 0) { rc = (__u16)(count * sizeof(struct cifs_posix_ace)); rc += sizeof(struct cifs_posix_acl); /* BB add check to make sure ACL does not overflow SMB */ } return rc; } int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *acl_inf, const int buflen, const int acl_type, const struct nls_table *nls_codepage, int remap) { /* SMB_QUERY_POSIX_ACL */ TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; __u16 params, byte_count; cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName)); queryAclRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->FileName[name_len] = 0; pSMB->FileName[name_len+1] = 0; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max data count below from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16( offsetof(struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_acl_get); if (rc) { cFYI(1, ("Send error in Query POSIX ACL = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); __u16 count = le16_to_cpu(pSMBr->t2.DataCount); rc = cifs_copy_posix_acl(acl_inf, (char *)&pSMBr->hdr.Protocol+data_offset, buflen, acl_type, count); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto queryAclRetry; return rc; } int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, const unsigned char *fileName, const char *local_acl, const int buflen, const int acl_type, const struct nls_table *nls_codepage, int remap) { struct smb_com_transaction2_spi_req *pSMB = NULL; struct smb_com_transaction2_spi_rsp *pSMBr = NULL; char *parm_data; int name_len; int rc = 0; int bytes_returned = 0; __u16 params, byte_count, data_count, param_offset, offset; cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName)); setAclRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } params = 6 + name_len; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find max SMB size from sess */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; parm_data = ((char *) &pSMB->hdr.Protocol) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); /* convert to on the wire format for POSIX ACL */ data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type); if (data_count == 0) { rc = -EOPNOTSUPP; goto setACLerrorExit; } pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); byte_count = 3 /* pad */ + params + data_count; pSMB->DataCount = cpu_to_le16(data_count); pSMB->TotalDataCount = pSMB->DataCount; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("Set POSIX ACL returned %d", rc)); setACLerrorExit: cifs_buf_release(pSMB); if (rc == -EAGAIN) goto setAclRetry; return rc; } /* BB fix tabs in this function FIXME BB */ int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, const int netfid, __u64 *pExtAttrBits, __u64 *pMask) { int rc = 0; struct smb_t2_qfi_req *pSMB = NULL; struct smb_t2_qfi_rsp *pSMBr = NULL; int bytes_returned; __u16 params, byte_count; cFYI(1, ("In GetExtAttr")); if (tcon == NULL) return -ENODEV; GetExtAttrRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2 /* level */ + 2 /* fid */; pSMB->t2.TotalDataCount = 0; pSMB->t2.MaxParameterCount = cpu_to_le16(4); /* BB find exact max data count below from sess structure BB */ pSMB->t2.MaxDataCount = cpu_to_le16(4000); pSMB->t2.MaxSetupCount = 0; pSMB->t2.Reserved = 0; pSMB->t2.Flags = 0; pSMB->t2.Timeout = 0; pSMB->t2.Reserved2 = 0; pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, Fid) - 4); pSMB->t2.DataCount = 0; pSMB->t2.DataOffset = 0; pSMB->t2.SetupCount = 1; pSMB->t2.Reserved3 = 0; pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->t2.TotalParameterCount = cpu_to_le16(params); pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); pSMB->Pad = 0; pSMB->Fid = netfid; pSMB->hdr.smb_buf_length += byte_count; pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("error %d in GetExtAttr", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ /* If rc should we check for EOPNOSUPP and disable the srvino flag? or in caller? */ rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); __u16 count = le16_to_cpu(pSMBr->t2.DataCount); struct file_chattr_info *pfinfo; /* BB Do we need a cast or hash here ? */ if (count != 16) { cFYI(1, ("Illegal size ret in GetExtAttr")); rc = -EIO; goto GetExtAttrOut; } pfinfo = (struct file_chattr_info *) (data_offset + (char *) &pSMBr->hdr.Protocol); *pExtAttrBits = le64_to_cpu(pfinfo->mode); *pMask = le64_to_cpu(pfinfo->mask); } } GetExtAttrOut: cifs_buf_release(pSMB); if (rc == -EAGAIN) goto GetExtAttrRetry; return rc; } #endif /* CONFIG_POSIX */ #ifdef CONFIG_CIFS_EXPERIMENTAL /* Get Security Descriptor (by handle) from remote server for a file or dir */ int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, struct cifs_ntsd **acl_inf, __u32 *pbuflen) { int rc = 0; int buf_type = 0; QUERY_SEC_DESC_REQ *pSMB; struct kvec iov[1]; cFYI(1, ("GetCifsACL")); *pbuflen = 0; *acl_inf = NULL; rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 8 /* parm len */, tcon, (void **) &pSMB); if (rc) return rc; pSMB->MaxParameterCount = cpu_to_le32(4); /* BB TEST with big acls that might need to be e.g. larger than 16K */ pSMB->MaxSetupCount = 0; pSMB->Fid = fid; /* file handle always le */ pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP | CIFS_ACL_DACL); pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */ pSMB->hdr.smb_buf_length += 11; iov[0].iov_base = (char *)pSMB; iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, CIFS_STD_OP); cifs_stats_inc(&tcon->num_acl_get); if (rc) { cFYI(1, ("Send error in QuerySecDesc = %d", rc)); } else { /* decode response */ __le32 *parm; __u32 parm_len; __u32 acl_len; struct smb_com_ntransact_rsp *pSMBr; char *pdata; /* validate_nttransact */ rc = validate_ntransact(iov[0].iov_base, (char **)&parm, &pdata, &parm_len, pbuflen); if (rc) goto qsec_out; pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base; cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf)); if (le32_to_cpu(pSMBr->ParameterCount) != 4) { rc = -EIO; /* bad smb */ *pbuflen = 0; goto qsec_out; } /* BB check that data area is minimum length and as big as acl_len */ acl_len = le32_to_cpu(*parm); if (acl_len != *pbuflen) { cERROR(1, ("acl length %d does not match %d", acl_len, *pbuflen)); if (*pbuflen > acl_len) *pbuflen = acl_len; } /* check if buffer is big enough for the acl header followed by the smallest SID */ if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || (*pbuflen >= 64 * 1024)) { cERROR(1, ("bad acl length %d", *pbuflen)); rc = -EINVAL; *pbuflen = 0; } else { *acl_inf = kmalloc(*pbuflen, GFP_KERNEL); if (*acl_inf == NULL) { *pbuflen = 0; rc = -ENOMEM; } memcpy(*acl_inf, pdata, *pbuflen); } } qsec_out: if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ return rc; } int CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, struct cifs_ntsd *pntsd, __u32 acllen) { __u16 byte_count, param_count, data_count, param_offset, data_offset; int rc = 0; int bytes_returned = 0; SET_SEC_DESC_REQ *pSMB = NULL; NTRANSACT_RSP *pSMBr = NULL; setCifsAclRetry: rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return (rc); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; param_count = 8; param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4; data_count = acllen; data_offset = param_offset + param_count; byte_count = 3 /* pad */ + param_count; pSMB->DataCount = cpu_to_le32(data_count); pSMB->TotalDataCount = pSMB->DataCount; pSMB->MaxParameterCount = cpu_to_le32(4); pSMB->MaxDataCount = cpu_to_le32(16384); pSMB->ParameterCount = cpu_to_le32(param_count); pSMB->ParameterOffset = cpu_to_le32(param_offset); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->DataOffset = cpu_to_le32(data_offset); pSMB->SetupCount = 0; pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC); pSMB->ByteCount = cpu_to_le16(byte_count+data_count); pSMB->Fid = fid; /* file handle always le */ pSMB->Reserved2 = 0; pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL); if (pntsd && acllen) { memcpy((char *) &pSMBr->hdr.Protocol + data_offset, (char *) pntsd, acllen); pSMB->hdr.smb_buf_length += (byte_count + data_count); } else pSMB->hdr.smb_buf_length += byte_count; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc)); if (rc) cFYI(1, ("Set CIFS ACL returned %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto setCifsAclRetry; return (rc); } #endif /* CONFIG_CIFS_EXPERIMENTAL */ /* Legacy Query Path Information call for lookup to old servers such as Win9x/WinME */ int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO *pFinfo, const struct nls_table *nls_codepage, int remap) { QUERY_INFORMATION_REQ *pSMB; QUERY_INFORMATION_RSP *pSMBr; int rc = 0; int bytes_returned; int name_len; cFYI(1, ("In SMBQPath path %s", searchName)); QInfRetry: rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } pSMB->BufferFormat = 0x04; name_len++; /* account for buffer type byte */ pSMB->hdr.smb_buf_length += (__u16) name_len; pSMB->ByteCount = cpu_to_le16(name_len); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QueryInfo = %d", rc)); } else if (pFinfo) { struct timespec ts; __u32 time = le32_to_cpu(pSMBr->last_write_time); /* decode response */ /* BB FIXME - add time zone adjustment BB */ memset(pFinfo, 0, sizeof(FILE_ALL_INFO)); ts.tv_nsec = 0; ts.tv_sec = time; /* decode time fields */ pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); pFinfo->LastWriteTime = pFinfo->ChangeTime; pFinfo->LastAccessTime = 0; pFinfo->AllocationSize = cpu_to_le64(le32_to_cpu(pSMBr->size)); pFinfo->EndOfFile = pFinfo->AllocationSize; pFinfo->Attributes = cpu_to_le32(le16_to_cpu(pSMBr->attr)); } else rc = -EIO; /* bad buffer passed in */ cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QInfRetry; return rc; } int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_ALL_INFO *pFindData, int legacy /* old style infolevel */, const struct nls_table *nls_codepage, int remap) { /* level 263 SMB_QUERY_FILE_ALL_INFO */ TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; __u16 params, byte_count; /* cFYI(1, ("In QPathInfo path %s", searchName)); */ QPathInfoRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; if (legacy) pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); else pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QPathInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc) /* BB add auto retry on EOPNOTSUPP? */ rc = -EIO; else if (!legacy && (pSMBr->ByteCount < 40)) rc = -EIO; /* bad smb */ else if (legacy && (pSMBr->ByteCount < 24)) rc = -EIO; /* 24 or 26 expected but we do not read last field */ else if (pFindData) { int size; __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); /* On legacy responses we do not read the last field, EAsize, fortunately since it varies by subdialect and also note it differs on Set vs. Get, ie two bytes or 4 bytes depending but we don't care here */ if (legacy) size = sizeof(FILE_INFO_STANDARD); else size = sizeof(FILE_ALL_INFO); memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, size); } else rc = -ENOMEM; } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QPathInfoRetry; return rc; } int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, FILE_UNIX_BASIC_INFO *pFindData, const struct nls_table *nls_codepage, int remap) { /* SMB_QUERY_FILE_UNIX_BASIC */ TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned = 0; int name_len; __u16 params, byte_count; cFYI(1, ("In QPathInfo (Unix) the path %s", searchName)); UnixQPathInfoRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QPathInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) { cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n" "Unix Extensions can be disabled on mount " "by specifying the nosfu mount option.")); rc = -EIO; /* bad smb */ } else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, sizeof(FILE_UNIX_BASIC_INFO)); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto UnixQPathInfoRetry; return rc; } /* xid, tcon, searchName and codepage are input parms, rest are returned */ int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, __u16 *pnetfid, struct cifs_search_info *psrch_inf, int remap, const char dirsep) { /* level 257 SMB_ */ TRANSACTION2_FFIRST_REQ *pSMB = NULL; TRANSACTION2_FFIRST_RSP *pSMBr = NULL; T2_FFIRST_RSP_PARMS *parms; int rc = 0; int bytes_returned = 0; int name_len; __u16 params, byte_count; cFYI(1, ("In FindFirst for %s", searchName)); findFirstRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); /* We can not add the asterik earlier in case it got remapped to 0xF03A as if it were part of the directory name instead of a wildcard */ name_len *= 2; pSMB->FileName[name_len] = dirsep; pSMB->FileName[name_len+1] = 0; pSMB->FileName[name_len+2] = '*'; pSMB->FileName[name_len+3] = 0; name_len += 4; /* now the trailing null */ pSMB->FileName[name_len] = 0; /* null terminate just in case */ pSMB->FileName[name_len+1] = 0; name_len += 2; } else { /* BB add check for overrun of SMB buf BB */ name_len = strnlen(searchName, PATH_MAX); /* BB fix here and in unicode clause above ie if (name_len > buffersize-header) free buffer exit; BB */ strncpy(pSMB->FileName, searchName, name_len); pSMB->FileName[name_len] = dirsep; pSMB->FileName[name_len+1] = '*'; pSMB->FileName[name_len+2] = 0; name_len += 3; } params = 12 + name_len /* includes null */ ; pSMB->TotalDataCount = 0; /* no EAs */ pSMB->MaxParameterCount = cpu_to_le16(10); pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16( offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */ pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST); pSMB->SearchAttributes = cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); /* BB what should we set StorageType to? Does it matter? BB */ pSMB->SearchStorageType = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_ffirst); if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */ /* BB Add code to handle unsupported level rc */ cFYI(1, ("Error in FindFirst = %d", rc)); cifs_buf_release(pSMB); /* BB eventually could optimize out free and realloc of buf */ /* for this case */ if (rc == -EAGAIN) goto findFirstRetry; } else { /* decode response */ /* BB remember to free buffer if error BB */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc == 0) { unsigned int lnoff; if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) psrch_inf->unicode = true; else psrch_inf->unicode = false; psrch_inf->ntwrk_buf_start = (char *)pSMBr; psrch_inf->smallBuf = 0; psrch_inf->srch_entries_start = (char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.DataOffset); parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.ParameterOffset)); if (parms->EndofSearch) psrch_inf->endOfSearch = true; else psrch_inf->endOfSearch = false; psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + psrch_inf->entries_in_buffer; lnoff = le16_to_cpu(parms->LastNameOffset); if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE < lnoff) { cERROR(1, ("ignoring corrupt resume name")); psrch_inf->last_entry = NULL; return rc; } psrch_inf->last_entry = psrch_inf->srch_entries_start + lnoff; *pnetfid = parms->SearchHandle; } else { cifs_buf_release(pSMB); } } return rc; } int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, __u16 searchHandle, struct cifs_search_info *psrch_inf) { TRANSACTION2_FNEXT_REQ *pSMB = NULL; TRANSACTION2_FNEXT_RSP *pSMBr = NULL; T2_FNEXT_RSP_PARMS *parms; char *response_data; int rc = 0; int bytes_returned, name_len; __u16 params, byte_count; cFYI(1, ("In FindNext")); if (psrch_inf->endOfSearch) return -ENOENT; rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 14; /* includes 2 bytes of null string, converted to LE below*/ byte_count = 0; pSMB->TotalDataCount = 0; /* no EAs */ pSMB->MaxParameterCount = cpu_to_le16(8); pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16( offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); pSMB->SearchHandle = searchHandle; /* always kept as le */ pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); pSMB->ResumeKey = psrch_inf->resume_key; pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); name_len = psrch_inf->resume_name_len; params += name_len; if (name_len < PATH_MAX) { memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); byte_count += name_len; /* 14 byte parm len above enough for 2 byte null terminator */ pSMB->ResumeFileName[name_len] = 0; pSMB->ResumeFileName[name_len+1] = 0; } else { rc = -EINVAL; goto FNext2_err_exit; } byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_fnext); if (rc) { if (rc == -EBADF) { psrch_inf->endOfSearch = true; cifs_buf_release(pSMB); rc = 0; /* search probably was closed at end of search*/ } else cFYI(1, ("FindNext returned = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc == 0) { unsigned int lnoff; /* BB fixme add lock for file (srch_info) struct here */ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) psrch_inf->unicode = true; else psrch_inf->unicode = false; response_data = (char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.ParameterOffset); parms = (T2_FNEXT_RSP_PARMS *)response_data; response_data = (char *)&pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.DataOffset); if (psrch_inf->smallBuf) cifs_small_buf_release( psrch_inf->ntwrk_buf_start); else cifs_buf_release(psrch_inf->ntwrk_buf_start); psrch_inf->srch_entries_start = response_data; psrch_inf->ntwrk_buf_start = (char *)pSMB; psrch_inf->smallBuf = 0; if (parms->EndofSearch) psrch_inf->endOfSearch = true; else psrch_inf->endOfSearch = false; psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); psrch_inf->index_of_last_entry += psrch_inf->entries_in_buffer; lnoff = le16_to_cpu(parms->LastNameOffset); if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE < lnoff) { cERROR(1, ("ignoring corrupt resume name")); psrch_inf->last_entry = NULL; return rc; } else psrch_inf->last_entry = psrch_inf->srch_entries_start + lnoff; /* cFYI(1,("fnxt2 entries in buf %d index_of_last %d", psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */ /* BB fixme add unlock here */ } } /* BB On error, should we leave previous search buf (and count and last entry fields) intact or free the previous one? */ /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ FNext2_err_exit: if (rc != 0) cifs_buf_release(pSMB); return rc; } int CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle) { int rc = 0; FINDCLOSE_REQ *pSMB = NULL; cFYI(1, ("In CIFSSMBFindClose")); rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); /* no sense returning error if session restarted as file handle has been closed */ if (rc == -EAGAIN) return 0; if (rc) return rc; pSMB->FileID = searchHandle; pSMB->ByteCount = 0; rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) cERROR(1, ("Send error in FindClose = %d", rc)); cifs_stats_inc(&tcon->num_fclose); /* Since session is dead, search handle closed on server already */ if (rc == -EAGAIN) rc = 0; return rc; } int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 *inode_number, const struct nls_table *nls_codepage, int remap) { int rc = 0; TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int name_len, bytes_returned; __u16 params, byte_count; cFYI(1, ("In GetSrvInodeNum for %s", searchName)); if (tcon == NULL) return -ENODEV; GetInodeNumberRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max data count below from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("error %d in QueryInternalInfo", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ /* If rc should we check for EOPNOSUPP and disable the srvino flag? or in caller? */ rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); __u16 count = le16_to_cpu(pSMBr->t2.DataCount); struct file_internal_info *pfinfo; /* BB Do we need a cast or hash here ? */ if (count < 8) { cFYI(1, ("Illegal size ret in QryIntrnlInf")); rc = -EIO; goto GetInodeNumOut; } pfinfo = (struct file_internal_info *) (data_offset + (char *) &pSMBr->hdr.Protocol); *inode_number = pfinfo->UniqueId; } } GetInodeNumOut: cifs_buf_release(pSMB); if (rc == -EAGAIN) goto GetInodeNumberRetry; return rc; } /* computes length of UCS string converted to host codepage * @src: UCS string * @maxlen: length of the input string in UCS characters * (not in bytes) * * return: size of input string in host codepage */ static int hostlen_fromUCS(const __le16 *src, const int maxlen, const struct nls_table *nls_codepage) { int i; int hostlen = 0; char to[4]; int charlen; for (i = 0; (i < maxlen) && src[i]; ++i) { charlen = nls_codepage->uni2char(le16_to_cpu(src[i]), to, NLS_MAX_CHARSET_SIZE); hostlen += charlen > 0 ? charlen : 1; } return hostlen; } /* parses DFS refferal V3 structure * caller is responsible for freeing target_nodes * returns: * on success - 0 * on failure - errno */ static int parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, unsigned int *num_of_nodes, struct dfs_info3_param **target_nodes, const struct nls_table *nls_codepage, int remap, const char *searchName) { int i, rc = 0; char *data_end; bool is_unicode; struct dfs_referral_level_3 *ref; if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) is_unicode = true; else is_unicode = false; *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals); if (*num_of_nodes < 1) { cERROR(1, ("num_referrals: must be at least > 0," "but we get num_referrals = %d\n", *num_of_nodes)); rc = -EINVAL; goto parse_DFS_referrals_exit; } ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals); if (ref->VersionNumber != cpu_to_le16(3)) { cERROR(1, ("Referrals of V%d version are not supported," "should be V3", le16_to_cpu(ref->VersionNumber))); rc = -EINVAL; goto parse_DFS_referrals_exit; } /* get the upper boundary of the resp buffer */ data_end = (char *)(&(pSMBr->PathConsumed)) + le16_to_cpu(pSMBr->t2.DataCount); cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n", *num_of_nodes, le16_to_cpu(pSMBr->DFSFlags))); *target_nodes = kzalloc(sizeof(struct dfs_info3_param) * *num_of_nodes, GFP_KERNEL); if (*target_nodes == NULL) { cERROR(1, ("Failed to allocate buffer for target_nodes\n")); rc = -ENOMEM; goto parse_DFS_referrals_exit; } /* collect neccessary data from referrals */ for (i = 0; i < *num_of_nodes; i++) { char *temp; int max_len; struct dfs_info3_param *node = (*target_nodes)+i; node->flags = le16_to_cpu(pSMBr->DFSFlags); if (is_unicode) { __le16 *tmp = kmalloc(strlen(searchName)*2, GFP_KERNEL); cifsConvertToUCS((__le16 *) tmp, searchName, PATH_MAX, nls_codepage, remap); node->path_consumed = hostlen_fromUCS(tmp, le16_to_cpu(pSMBr->PathConsumed)/2, nls_codepage); kfree(tmp); } else node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); node->server_type = le16_to_cpu(ref->ServerType); node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); /* copy DfsPath */ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; rc = cifs_strncpy_to_host(&(node->path_name), temp, max_len, is_unicode, nls_codepage); if (rc) goto parse_DFS_referrals_exit; /* copy link target UNC */ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; rc = cifs_strncpy_to_host(&(node->node_name), temp, max_len, is_unicode, nls_codepage); if (rc) goto parse_DFS_referrals_exit; ref += le16_to_cpu(ref->Size); } parse_DFS_referrals_exit: if (rc) { free_dfs_info_array(*target_nodes, *num_of_nodes); *target_nodes = NULL; *num_of_nodes = 0; } return rc; } int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, struct dfs_info3_param **target_nodes, unsigned int *num_of_nodes, const struct nls_table *nls_codepage, int remap) { /* TRANS2_GET_DFS_REFERRAL */ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; __u16 params, byte_count; *num_of_nodes = 0; *target_nodes = NULL; cFYI(1, ("In GetDFSRefer the path %s", searchName)); if (ses == NULL) return -ENODEV; getDFSRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; /* server pointer checked in called function, but should never be null here anyway */ pSMB->hdr.Mid = GetNextMid(ses->server); pSMB->hdr.Tid = ses->ipc_tid; pSMB->hdr.Uid = ses->Suid; if (ses->capabilities & CAP_STATUS32) pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS; if (ses->capabilities & CAP_DFS) pSMB->hdr.Flags2 |= SMBFLG2_DFS; if (ses->capabilities & CAP_UNICODE) { pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; name_len = cifsConvertToUCS((__le16 *) pSMB->RequestFileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->RequestFileName, searchName, name_len); } if (ses->server) { if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } pSMB->hdr.Uid = ses->Suid; params = 2 /* level */ + name_len /*includes null */ ; pSMB->TotalDataCount = 0; pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->MaxParameterCount = 0; /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL); byte_count = params + 3 /* pad */ ; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->MaxReferralLevel = cpu_to_le16(3); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in GetDFSRefer = %d", rc)); goto GetDFSRefExit; } rc = validate_t2((struct smb_t2_rsp *)pSMBr); /* BB Also check if enough total bytes returned? */ if (rc || (pSMBr->ByteCount < 17)) { rc = -EIO; /* bad smb */ goto GetDFSRefExit; } cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d", pSMBr->ByteCount, le16_to_cpu(pSMBr->t2.DataOffset))); /* parse returned result into more usable form */ rc = parse_DFS_referrals(pSMBr, num_of_nodes, target_nodes, nls_codepage, remap, searchName); GetDFSRefExit: cifs_buf_release(pSMB); if (rc == -EAGAIN) goto getDFSRetry; return rc; } /* Query File System Info such as free space to old servers such as Win 9x */ int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData) { /* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */ TRANSACTION2_QFSI_REQ *pSMB = NULL; TRANSACTION2_QFSI_RSP *pSMBr = NULL; FILE_SYSTEM_ALLOC_INFO *response_data; int rc = 0; int bytes_returned = 0; __u16 params, byte_count; cFYI(1, ("OldQFSInfo")); oldQFSInfoRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QFSInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 18)) rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); cFYI(1, ("qfsinf resp BCC: %d Offset %d", pSMBr->ByteCount, data_offset)); response_data = (FILE_SYSTEM_ALLOC_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); FSData->f_bsize = le16_to_cpu(response_data->BytesPerSector) * le32_to_cpu(response_data-> SectorsPerAllocationUnit); FSData->f_blocks = le32_to_cpu(response_data->TotalAllocationUnits); FSData->f_bfree = FSData->f_bavail = le32_to_cpu(response_data->FreeAllocationUnits); cFYI(1, ("Blocks: %lld Free: %lld Block size %ld", (unsigned long long)FSData->f_blocks, (unsigned long long)FSData->f_bfree, FSData->f_bsize)); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto oldQFSInfoRetry; return rc; } int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData) { /* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */ TRANSACTION2_QFSI_REQ *pSMB = NULL; TRANSACTION2_QFSI_RSP *pSMBr = NULL; FILE_SYSTEM_INFO *response_data; int rc = 0; int bytes_returned = 0; __u16 params, byte_count; cFYI(1, ("In QFSInfo")); QFSInfoRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QFSInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 24)) rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); FSData->f_bsize = le32_to_cpu(response_data->BytesPerSector) * le32_to_cpu(response_data-> SectorsPerAllocationUnit); FSData->f_blocks = le64_to_cpu(response_data->TotalAllocationUnits); FSData->f_bfree = FSData->f_bavail = le64_to_cpu(response_data->FreeAllocationUnits); cFYI(1, ("Blocks: %lld Free: %lld Block size %ld", (unsigned long long)FSData->f_blocks, (unsigned long long)FSData->f_bfree, FSData->f_bsize)); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QFSInfoRetry; return rc; } int CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon) { /* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */ TRANSACTION2_QFSI_REQ *pSMB = NULL; TRANSACTION2_QFSI_RSP *pSMBr = NULL; FILE_SYSTEM_ATTRIBUTE_INFO *response_data; int rc = 0; int bytes_returned = 0; __u16 params, byte_count; cFYI(1, ("In QFSAttributeInfo")); QFSAttributeRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cERROR(1, ("Send error in QFSAttributeInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 13)) { /* BB also check if enough bytes returned */ rc = -EIO; /* bad smb */ } else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_ATTRIBUTE_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); memcpy(&tcon->fsAttrInfo, response_data, sizeof(FILE_SYSTEM_ATTRIBUTE_INFO)); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QFSAttributeRetry; return rc; } int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon) { /* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */ TRANSACTION2_QFSI_REQ *pSMB = NULL; TRANSACTION2_QFSI_RSP *pSMBr = NULL; FILE_SYSTEM_DEVICE_INFO *response_data; int rc = 0; int bytes_returned = 0; __u16 params, byte_count; cFYI(1, ("In QFSDeviceInfo")); QFSDeviceRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QFSDeviceInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO))) rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_DEVICE_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); memcpy(&tcon->fsDevInfo, response_data, sizeof(FILE_SYSTEM_DEVICE_INFO)); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QFSDeviceRetry; return rc; } int CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon) { /* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */ TRANSACTION2_QFSI_REQ *pSMB = NULL; TRANSACTION2_QFSI_RSP *pSMBr = NULL; FILE_SYSTEM_UNIX_INFO *response_data; int rc = 0; int bytes_returned = 0; __u16 params, byte_count; cFYI(1, ("In QFSUnixInfo")); QFSUnixRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(100); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof(struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cERROR(1, ("Send error in QFSUnixInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 13)) { rc = -EIO; /* bad smb */ } else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_UNIX_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); memcpy(&tcon->fsUnixInfo, response_data, sizeof(FILE_SYSTEM_UNIX_INFO)); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QFSUnixRetry; return rc; } int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) { /* level 0x200 SMB_SET_CIFS_UNIX_INFO */ TRANSACTION2_SETFSI_REQ *pSMB = NULL; TRANSACTION2_SETFSI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned = 0; __u16 params, param_offset, offset, byte_count; cFYI(1, ("In SETFSUnixInfo")); SETFSUnixRetry: /* BB switch to small buf init to save memory */ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 4; /* 2 bytes zero followed by info level. */ pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) - 4; offset = param_offset + params; pSMB->MaxParameterCount = cpu_to_le16(4); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(100); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION); byte_count = 1 /* pad */ + params + 12; pSMB->DataCount = cpu_to_le16(12); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); /* Params. */ pSMB->FileNum = 0; pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO); /* Data. */ pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION); pSMB->ClientUnixCap = cpu_to_le64(cap); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cERROR(1, ("Send error in SETFSUnixInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc) rc = -EIO; /* bad smb */ } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto SETFSUnixRetry; return rc; } int CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData) { /* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */ TRANSACTION2_QFSI_REQ *pSMB = NULL; TRANSACTION2_QFSI_RSP *pSMBr = NULL; FILE_SYSTEM_POSIX_INFO *response_data; int rc = 0; int bytes_returned = 0; __u16 params, byte_count; cFYI(1, ("In QFSPosixInfo")); QFSPosixRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; params = 2; /* level */ pSMB->TotalDataCount = 0; pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(100); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; byte_count = params + 1 /* pad */ ; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof(struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO); pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QFSUnixInfo = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 13)) { rc = -EIO; /* bad smb */ } else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); response_data = (FILE_SYSTEM_POSIX_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); FSData->f_bsize = le32_to_cpu(response_data->BlockSize); FSData->f_blocks = le64_to_cpu(response_data->TotalBlocks); FSData->f_bfree = le64_to_cpu(response_data->BlocksAvail); if (response_data->UserBlocksAvail == cpu_to_le64(-1)) { FSData->f_bavail = FSData->f_bfree; } else { FSData->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); } if (response_data->TotalFileNodes != cpu_to_le64(-1)) FSData->f_files = le64_to_cpu(response_data->TotalFileNodes); if (response_data->FreeFileNodes != cpu_to_le64(-1)) FSData->f_ffree = le64_to_cpu(response_data->FreeFileNodes); } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QFSPosixRetry; return rc; } /* We can not use write of zero bytes trick to set file size due to need for large file support. Also note that this SetPathInfo is preferred to SetFileInfo based method in next routine which is only needed to work around a sharing violation bug in Samba which this routine can run into */ int CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName, __u64 size, bool SetAllocation, const struct nls_table *nls_codepage, int remap) { struct smb_com_transaction2_spi_req *pSMB = NULL; struct smb_com_transaction2_spi_rsp *pSMBr = NULL; struct file_end_of_file_info *parm_data; int name_len; int rc = 0; int bytes_returned = 0; __u16 params, byte_count, data_count, param_offset, offset; cFYI(1, ("In SetEOF")); SetEOFRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } params = 6 + name_len; data_count = sizeof(struct file_end_of_file_info); pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(4100); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; if (SetAllocation) { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); } else /* Set File Size */ { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); } parm_data = (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + offset); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + data_count; pSMB->DataCount = cpu_to_le16(data_count); pSMB->TotalDataCount = pSMB->DataCount; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; parm_data->FileSize = cpu_to_le64(size); pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("SetPathInfo (file size) returned %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto SetEOFRetry; return rc; } int CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, __u16 fid, __u32 pid_of_opener, bool SetAllocation) { struct smb_com_transaction2_sfi_req *pSMB = NULL; char *data_offset; struct file_end_of_file_info *parm_data; int rc = 0; __u16 params, param_offset, offset, byte_count, count; cFYI(1, ("SetFileSize (via SetFileInfo) %lld", (long long)size)); rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); if (rc) return rc; pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; count = sizeof(struct file_end_of_file_info); pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); parm_data = (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + offset); pSMB->DataOffset = cpu_to_le16(offset); parm_data->FileSize = cpu_to_le64(size); pSMB->Fid = fid; if (SetAllocation) { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); } else /* Set File Size */ { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); } pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) { cFYI(1, ("Send error in SetFileInfo (SetFileSize) = %d", rc)); } /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } /* Some legacy servers such as NT4 require that the file times be set on an open handle, rather than by pathname - this is awkward due to potential access conflicts on the open, but it is unavoidable for these old servers since the only other choice is to go from 100 nanosecond DCE time and resort to the original setpathinfo level which takes the ancient DOS time format with 2 second granularity */ int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener) { struct smb_com_transaction2_sfi_req *pSMB = NULL; char *data_offset; int rc = 0; __u16 params, param_offset, offset, byte_count, count; cFYI(1, ("Set Times (via SetFileInfo)")); rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); if (rc) return rc; pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; count = sizeof(FILE_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find max SMB PDU from sess */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->Fid = fid; if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc)); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } int CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon, bool delete_file, __u16 fid, __u32 pid_of_opener) { struct smb_com_transaction2_sfi_req *pSMB = NULL; char *data_offset; int rc = 0; __u16 params, param_offset, offset, byte_count, count; cFYI(1, ("Set File Disposition (via SetFileInfo)")); rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); if (rc) return rc; pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; count = 1; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find max SMB PDU from sess */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->Fid = fid; pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); *data_offset = delete_file ? 1 : 0; rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) cFYI(1, ("Send error in SetFileDisposition = %d", rc)); return rc; } int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon, const char *fileName, const FILE_BASIC_INFO *data, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; int name_len; int rc = 0; int bytes_returned = 0; char *data_offset; __u16 params, param_offset, offset, byte_count, count; cFYI(1, ("In SetTimes")); SetTimesRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } params = 6 + name_len; count = sizeof(FILE_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("SetPathInfo (times) returned %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto SetTimesRetry; return rc; } /* Can not be used to set time stamps yet (due to old DOS time format) */ /* Can be used to set attributes */ #if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug handling it anyway and NT4 was what we thought it would be needed for Do not delete it until we prove whether needed for Win9x though */ int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName, __u16 dos_attrs, const struct nls_table *nls_codepage) { SETATTR_REQ *pSMB = NULL; SETATTR_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; cFYI(1, ("In SetAttrLegacy")); SetAttrLgcyRetry: rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = ConvertToUCS((__le16 *) pSMB->fileName, fileName, PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->fileName, fileName, name_len); } pSMB->attr = cpu_to_le16(dos_attrs); pSMB->BufferFormat = 0x04; pSMB->hdr.smb_buf_length += name_len + 1; pSMB->ByteCount = cpu_to_le16(name_len + 1); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("Error in LegacySetAttr = %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto SetAttrLgcyRetry; return rc; } #endif /* temporarily unneeded SetAttr legacy function */ int CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *tcon, char *fileName, const struct cifs_unix_set_info_args *args, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; TRANSACTION2_SPI_RSP *pSMBr = NULL; int name_len; int rc = 0; int bytes_returned = 0; FILE_UNIX_BASIC_INFO *data_offset; __u16 params, param_offset, offset, count, byte_count; __u64 mode = args->mode; cFYI(1, ("In SetUID/GID/Mode")); setPermsRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } params = 6 + name_len; count = sizeof(FILE_UNIX_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; data_offset = (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol + offset); memset(data_offset, 0, count); pSMB->DataOffset = cpu_to_le16(offset); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->ParameterCount = cpu_to_le16(params); pSMB->DataCount = cpu_to_le16(count); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->TotalDataCount = pSMB->DataCount; pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; /* Samba server ignores set of file size to zero due to bugs in some older clients, but we should be precise - we use SetFileSize to set file size and do not want to truncate file size to zero accidently as happened on one Samba server beta by putting zero instead of -1 here */ data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64); data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64); data_offset->LastStatusChange = cpu_to_le64(args->ctime); data_offset->LastAccessTime = cpu_to_le64(args->atime); data_offset->LastModificationTime = cpu_to_le64(args->mtime); data_offset->Uid = cpu_to_le64(args->uid); data_offset->Gid = cpu_to_le64(args->gid); /* better to leave device as zero when it is */ data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); data_offset->Permissions = cpu_to_le64(mode); if (S_ISREG(mode)) data_offset->Type = cpu_to_le32(UNIX_FILE); else if (S_ISDIR(mode)) data_offset->Type = cpu_to_le32(UNIX_DIR); else if (S_ISLNK(mode)) data_offset->Type = cpu_to_le32(UNIX_SYMLINK); else if (S_ISCHR(mode)) data_offset->Type = cpu_to_le32(UNIX_CHARDEV); else if (S_ISBLK(mode)) data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV); else if (S_ISFIFO(mode)) data_offset->Type = cpu_to_le32(UNIX_FIFO); else if (S_ISSOCK(mode)) data_offset->Type = cpu_to_le32(UNIX_SOCKET); pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("SetPathInfo (perms) returned %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto setPermsRetry; return rc; } int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, const int notify_subdirs, const __u16 netfid, __u32 filter, struct file *pfile, int multishot, const struct nls_table *nls_codepage) { int rc = 0; struct smb_com_transaction_change_notify_req *pSMB = NULL; struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL; struct dir_notify_req *dnotify_req; int bytes_returned; cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid)); rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; pSMB->TotalParameterCount = 0 ; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le32(2); /* BB find exact data count max from sess structure BB */ pSMB->MaxDataCount = 0; /* same in little endian or be */ /* BB VERIFY verify which is correct for above BB */ pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); pSMB->MaxSetupCount = 4; pSMB->Reserved = 0; pSMB->ParameterOffset = 0; pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 4; /* single byte does not need le conversion */ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE); pSMB->ParameterCount = pSMB->TotalParameterCount; if (notify_subdirs) pSMB->WatchTree = 1; /* one byte - no le conversion needed */ pSMB->Reserved2 = 0; pSMB->CompletionFilter = cpu_to_le32(filter); pSMB->Fid = netfid; /* file handle always le */ pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_ASYNC_OP); if (rc) { cFYI(1, ("Error in Notify = %d", rc)); } else { /* Add file to outstanding requests */ /* BB change to kmem cache alloc */ dnotify_req = kmalloc( sizeof(struct dir_notify_req), GFP_KERNEL); if (dnotify_req) { dnotify_req->Pid = pSMB->hdr.Pid; dnotify_req->PidHigh = pSMB->hdr.PidHigh; dnotify_req->Mid = pSMB->hdr.Mid; dnotify_req->Tid = pSMB->hdr.Tid; dnotify_req->Uid = pSMB->hdr.Uid; dnotify_req->netfid = netfid; dnotify_req->pfile = pfile; dnotify_req->filter = filter; dnotify_req->multishot = multishot; spin_lock(&GlobalMid_Lock); list_add_tail(&dnotify_req->lhead, &GlobalDnotifyReqList); spin_unlock(&GlobalMid_Lock); } else rc = -ENOMEM; } cifs_buf_release(pSMB); return rc; } #ifdef CONFIG_CIFS_XATTR ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *EAData, size_t buf_size, const struct nls_table *nls_codepage, int remap) { /* BB assumes one setup word */ TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; struct fea *temp_fea; char *temp_ptr; __u16 params, byte_count; cFYI(1, ("In Query All EAs path %s", searchName)); QAllEAsRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QueryAllEAs = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); /* BB also check enough total bytes returned */ /* BB we need to improve the validity checking of these trans2 responses */ if (rc || (pSMBr->ByteCount < 4)) rc = -EIO; /* bad smb */ /* else if (pFindData){ memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, kl); }*/ else { /* check that length of list is not more than bcc */ /* check that each entry does not go beyond length of list */ /* check that each element of each entry does not go beyond end of list */ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); struct fealist *ea_response_data; rc = 0; /* validate_trans2_offsets() */ /* BB check if start of smb + data_offset > &bcc+ bcc */ ea_response_data = (struct fealist *) (((char *) &pSMBr->hdr.Protocol) + data_offset); name_len = le32_to_cpu(ea_response_data->list_len); cFYI(1, ("ea length %d", name_len)); if (name_len <= 8) { /* returned EA size zeroed at top of function */ cFYI(1, ("empty EA list returned from server")); } else { /* account for ea list len */ name_len -= 4; temp_fea = ea_response_data->list; temp_ptr = (char *)temp_fea; while (name_len > 0) { __u16 value_len; name_len -= 4; temp_ptr += 4; rc += temp_fea->name_len; /* account for prefix user. and trailing null */ rc = rc + 5 + 1; if (rc < (int)buf_size) { memcpy(EAData, "user.", 5); EAData += 5; memcpy(EAData, temp_ptr, temp_fea->name_len); EAData += temp_fea->name_len; /* null terminate name */ *EAData = 0; EAData = EAData + 1; } else if (buf_size == 0) { /* skip copy - calc size only */ } else { /* stop before overrun buffer */ rc = -ERANGE; break; } name_len -= temp_fea->name_len; temp_ptr += temp_fea->name_len; /* account for trailing null */ name_len--; temp_ptr++; value_len = le16_to_cpu(temp_fea->value_len); name_len -= value_len; temp_ptr += value_len; /* BB check that temp_ptr is still within the SMB BB*/ /* no trailing null to account for in value len */ /* go on to next EA */ temp_fea = (struct fea *)temp_ptr; } } } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QAllEAsRetry; return (ssize_t)rc; } ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, const unsigned char *ea_name, unsigned char *ea_value, size_t buf_size, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len; struct fea *temp_fea; char *temp_ptr; __u16 params, byte_count; cFYI(1, ("In Query EA path %s", searchName)); QEARetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in Query EA = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); /* BB also check enough total bytes returned */ /* BB we need to improve the validity checking of these trans2 responses */ if (rc || (pSMBr->ByteCount < 4)) rc = -EIO; /* bad smb */ /* else if (pFindData){ memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, kl); }*/ else { /* check that length of list is not more than bcc */ /* check that each entry does not go beyond length of list */ /* check that each element of each entry does not go beyond end of list */ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); struct fealist *ea_response_data; rc = -ENODATA; /* validate_trans2_offsets() */ /* BB check if start of smb + data_offset > &bcc+ bcc*/ ea_response_data = (struct fealist *) (((char *) &pSMBr->hdr.Protocol) + data_offset); name_len = le32_to_cpu(ea_response_data->list_len); cFYI(1, ("ea length %d", name_len)); if (name_len <= 8) { /* returned EA size zeroed at top of function */ cFYI(1, ("empty EA list returned from server")); } else { /* account for ea list len */ name_len -= 4; temp_fea = ea_response_data->list; temp_ptr = (char *)temp_fea; /* loop through checking if we have a matching name and then return the associated value */ while (name_len > 0) { __u16 value_len; name_len -= 4; temp_ptr += 4; value_len = le16_to_cpu(temp_fea->value_len); /* BB validate that value_len falls within SMB, even though maximum for name_len is 255 */ if (memcmp(temp_fea->name, ea_name, temp_fea->name_len) == 0) { /* found a match */ rc = value_len; /* account for prefix user. and trailing null */ if (rc <= (int)buf_size) { memcpy(ea_value, temp_fea->name+temp_fea->name_len+1, rc); /* ea values, unlike ea names, are not null terminated */ } else if (buf_size == 0) { /* skip copy - calc size only */ } else { /* stop before overrun buffer */ rc = -ERANGE; } break; } name_len -= temp_fea->name_len; temp_ptr += temp_fea->name_len; /* account for trailing null */ name_len--; temp_ptr++; name_len -= value_len; temp_ptr += value_len; /* No trailing null to account for in value_len. Go on to next EA */ temp_fea = (struct fea *)temp_ptr; } } } } cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QEARetry; return (ssize_t)rc; } int CIFSSMBSetEA(const int xid, struct cifsTconInfo *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) { struct smb_com_transaction2_spi_req *pSMB = NULL; struct smb_com_transaction2_spi_rsp *pSMBr = NULL; struct fealist *parm_data; int name_len; int rc = 0; int bytes_returned = 0; __u16 params, param_offset, byte_count, offset, count; cFYI(1, ("In SetEA")); SetEARetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); } params = 6 + name_len; /* done calculating parms using name_len of file name, now use name_len to calculate length of ea name we are going to create in the inode xattrs */ if (ea_name == NULL) name_len = 0; else name_len = strnlen(ea_name, 255); count = sizeof(*parm_data) + ea_value_len + name_len; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find max SMB PDU from sess */ pSMB->MaxDataCount = cpu_to_le16(1000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_EA); parm_data = (struct fealist *) (((char *) &pSMB->hdr.Protocol) + offset); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + count; pSMB->DataCount = cpu_to_le16(count); parm_data->list_len = cpu_to_le32(count); parm_data->list[0].EA_flags = 0; /* we checked above that name len is less than 255 */ parm_data->list[0].name_len = (__u8)name_len; /* EA names are always ASCII */ if (ea_name) strncpy(parm_data->list[0].name, ea_name, name_len); parm_data->list[0].name[name_len] = 0; parm_data->list[0].value_len = cpu_to_le16(ea_value_len); /* caller ensures that ea_value_len is less than 64K but we need to ensure that it fits within the smb */ /*BB add length check to see if it would fit in negotiated SMB buffer size BB */ /* if (ea_value_len > buffer_size - 512 (enough for header)) */ if (ea_value_len) memcpy(parm_data->list[0].name+name_len+1, ea_value, ea_value_len); pSMB->TotalDataCount = pSMB->DataCount; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) cFYI(1, ("SetPathInfo (EA) returned %d", rc)); cifs_buf_release(pSMB); if (rc == -EAGAIN) goto SetEARetry; return rc; } #endif cifs-test-base/cifs_spnego.c0000644000175000017500000001061511117756171015716 0ustar stevefstevef/* * 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 #include #include #include #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, const void *data, size_t datalen) { char *payload; int ret; ret = -ENOMEM; payload = kmalloc(datalen, GFP_KERNEL); if (!payload) goto error; /* attach the data */ memcpy(payload, data, datalen); rcu_assign_pointer(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 /* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */ #define MAX_IPV6_ADDR_LEN 43 /* 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 ";user=" */ #define USER_KEY_LEN 6 /* get a key struct with a SPNEGO security blob, suitable for session setup */ struct key * cifs_get_spnego_key(struct cifsSesInfo *sesInfo) { struct TCP_Server_Info *server = sesInfo->server; 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 + MAX_IPV6_ADDR_LEN + MAX_MECH_STR_LEN + UID_KEY_LEN + (sizeof(uid_t) * 2) + USER_KEY_LEN + strlen(sesInfo->userName) + 1; 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->addr.sockAddr.sin_family == AF_INET) sprintf(dp, "ip4=" NIPQUAD_FMT, NIPQUAD(server->addr.sockAddr.sin_addr)); else if (server->addr.sockAddr.sin_family == AF_INET6) sprintf(dp, "ip6=" NIP6_SEQFMT, NIP6(server->addr.sockAddr6.sin6_addr)); else goto out; dp = description + strlen(description); /* for now, only sec=krb5 and sec=mskrb5 are valid */ if (server->secType == Kerberos) sprintf(dp, ";sec=krb5"); else if (server->secType == MSKerberos) sprintf(dp, ";sec=mskrb5"); else goto out; dp = description + strlen(description); sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); dp = description + strlen(description); sprintf(dp, ";user=%s", sesInfo->userName); cFYI(1, ("key description = %s", 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; } cifs-test-base/cifs_spnego.h0000644000175000017500000000313411117756171015721 0ustar stevefstevef/* * 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 cifsSesInfo *sesInfo); #endif /* KERNEL */ #endif /* _CIFS_SPNEGO_H */ cifs-test-base/cifs_unicode.c0000644000175000017500000000437511117756171016057 0ustar stevefstevef/* * fs/cifs/cifs_unicode.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 #include "cifs_unicode.h" #include "cifs_uniupr.h" #include "cifspdu.h" #include "cifsglob.h" #include "cifs_debug.h" /* * NAME: cifs_strfromUCS() * * FUNCTION: Convert little-endian unicode string to character string * */ int cifs_strfromUCS_le(char *to, const __le16 *from, int len, const struct nls_table *codepage) { int i; int outlen = 0; for (i = 0; (i < len) && from[i]; i++) { int charlen; /* 2.4.0 kernel or greater */ charlen = codepage->uni2char(le16_to_cpu(from[i]), &to[outlen], NLS_MAX_CHARSET_SIZE); if (charlen > 0) { outlen += charlen; } else { to[outlen++] = '?'; } } to[outlen] = 0; return outlen; } /* * NAME: cifs_strtoUCS() * * FUNCTION: Convert character string to unicode string * */ int cifs_strtoUCS(__le16 *to, const char *from, int len, const struct nls_table *codepage) { int charlen; int i; wchar_t *wchar_to = (wchar_t *)to; /* needed to quiet sparse */ for (i = 0; len && *from; i++, from += charlen, len -= charlen) { /* works for 2.4.0 kernel or later */ charlen = codepage->char2uni(from, len, &wchar_to[i]); if (charlen < 1) { cERROR(1, ("strtoUCS: char2uni of %d returned %d", (int)*from, charlen)); /* A question mark */ to[i] = cpu_to_le16(0x003f); charlen = 1; } else to[i] = cpu_to_le16(wchar_to[i]); } to[i] = 0; return i; } cifs-test-base/cifs_unicode.h0000644000175000017500000001763311117756171016065 0ustar stevefstevef/* * 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,2007 * * 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. * */ #include #include #include #define UNIUPR_NOLOWER /* Example to not expand lower case tables */ /* 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 UniLowerTable[512]; extern struct UniCaseRange UniLowerRange[]; #endif /* UNIUPR_NOLOWER */ #ifdef __KERNEL__ int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *); int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); #endif /* * UniStrcat: Concatenate the second string to the first * * Returns: * Address of the first string */ static inline wchar_t * UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) { wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ while (*ucs1++) ; /* 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 wchar_t * UniStrupr(register wchar_t *upin) { register wchar_t *up; up = upin; while (*up) { /* For all characters */ *up = UniToupper(*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(wchar_t uc) { register struct UniCaseRange *rp; if (uc < sizeof(UniLowerTable)) { /* Latin characters */ return uc + UniLowerTable[uc]; /* Use base tables */ } else { rp = UniLowerRange; /* 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 cifs-test-base/cifs_uniupr.h0000644000175000017500000003111211117756171015745 0ustar stevefstevef/* * 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 */ static 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 */ static const struct UniCaseRange CifsUniLowerRange[] = { 0x0380, 0x03ab, UniCaseRangeL0380, 0x0400, 0x042f, UniCaseRangeL0400, 0x0490, 0x04cb, UniCaseRangeL0490, 0x1e00, 0x1ff7, UniCaseRangeL1e00, 0xff20, 0xff3a, UniCaseRangeLff20, 0, 0, 0 }; #endif cifs-test-base/cn_cifs.h0000644000175000017500000000245311117756171015031 0ustar stevefstevef/* * fs/cifs/cn_cifs.h * * Copyright (c) International Business Machines Corp., 2002 * 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 _CN_CIFS_H #define _CN_CIFS_H #ifdef CONFIG_CIFS_UPCALL #include #include struct cifs_upcall { char signature[4]; /* CIFS */ enum command { CIFS_GET_IP = 0x00000001, /* get ip address for hostname */ CIFS_GET_SECBLOB = 0x00000002, /* get SPNEGO wrapped blob */ } command; /* union cifs upcall data follows */ }; #endif /* CIFS_UPCALL */ #endif /* _CN_CIFS_H */ cifs-test-base/connect.c0000644000175000017500000033671611117756171015065 0ustar stevefstevef/* * fs/cifs/connect.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" #include "ntlmssp.h" #include "nterr.h" #include "rfc1002pdu.h" #include "cn_cifs.h" #define CIFS_PORT 445 #define RFC1001_PORT 139 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); extern mempool_t *cifs_req_poolp; struct smb_vol { char *username; char *password; char *domainname; char *UNC; char *UNCip; char *in6_addr; /* ipv6 address as human readable form of in6_addr */ char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[16]; /* netbios name of client */ char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */ uid_t linux_uid; gid_t linux_gid; mode_t file_mode; mode_t dir_mode; unsigned secFlg; bool rw:1; 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 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 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; unsigned int rsize; unsigned int wsize; unsigned int sockopt; unsigned short int port; char *prepath; }; static int ipv4_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server); /* * cifs tcp session reconnection * * mark tcp session as reconnecting so temporarily locked * mark all smb sessions as reconnecting for tcp session * reconnect tcp session * wake up waiters on reconnection? - (not needed currently) */ static int cifs_reconnect(struct TCP_Server_Info *server) { int rc = 0; struct list_head *tmp, *tmp2; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; struct mid_q_entry *mid_entry; spin_lock(&GlobalMid_Lock); if (server->tcpStatus == CifsExiting) { /* the demux thread will exit normally next time through the loop */ spin_unlock(&GlobalMid_Lock); return rc; } else server->tcpStatus = CifsNeedReconnect; spin_unlock(&GlobalMid_Lock); server->maxBuf = 0; cFYI(1, ("Reconnecting tcp session")); /* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they are not used until reconnected */ read_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->need_reconnect = true; ses->ipc_tid = 0; list_for_each(tmp2, &ses->tcon_list) { tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list); tcon->need_reconnect = true; } } read_unlock(&cifs_tcp_ses_lock); /* do not want to be sending data on a socket we are freeing */ mutex_lock(&server->srv_mutex); if (server->ssocket) { cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state, server->ssocket->flags)); kernel_sock_shutdown(server->ssocket, SHUT_WR); cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx", server->ssocket->state, server->ssocket->flags)); sock_release(server->ssocket); server->ssocket = NULL; } spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if (mid_entry->midState == MID_REQUEST_SUBMITTED) { /* Mark other intransit requests as needing retry so we do not immediately mark the session bad again (ie after we reconnect below) as they timeout too */ mid_entry->midState = MID_RETRY_NEEDED; } } spin_unlock(&GlobalMid_Lock); mutex_unlock(&server->srv_mutex); while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood)) { try_to_freeze(); if (server->addr.sockAddr6.sin6_family == AF_INET6) rc = ipv6_connect(server); else rc = ipv4_connect(server); if (rc) { cFYI(1, ("reconnect error %d", rc)); msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); spin_lock(&GlobalMid_Lock); if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsGood; server->sequence_number = 0; spin_unlock(&GlobalMid_Lock); /* atomic_set(&server->inFlight,0);*/ wake_up(&server->response_q); } } return rc; } /* return codes: 0 not a transact2, or all data present >0 transact2 with that much data missing -EINVAL = invalid transact2 */ static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize) { struct smb_t2_rsp *pSMBt; int total_data_size; int data_in_this_rsp; int remaining; 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 */ cFYI(1, ("invalid transact2 word count")); return -EINVAL; } pSMBt = (struct smb_t2_rsp *)pSMB; total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount); remaining = total_data_size - data_in_this_rsp; if (remaining == 0) return 0; else if (remaining < 0) { cFYI(1, ("total data %d smaller than data in frame %d", total_data_size, data_in_this_rsp)); return -EINVAL; } else { cFYI(1, ("missing %d bytes from transact2, check next response", remaining)); if (total_data_size > maxBufSize) { cERROR(1, ("TotalDataSize %d is over maximum buffer %d", total_data_size, maxBufSize)); return -EINVAL; } return remaining; } } static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) { struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; int total_data_size; int total_in_buf; int remaining; int total_in_buf2; char *data_area_of_target; char *data_area_of_buf2; __u16 byte_count; total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) { cFYI(1, ("total data size of primary and secondary t2 differ")); } total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount); remaining = total_data_size - total_in_buf; if (remaining < 0) return -EINVAL; if (remaining == 0) /* nothing to do, ignore */ return 0; total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount); if (remaining < total_in_buf2) { cFYI(1, ("transact2 2nd response contains too much data")); } /* find end of first SMB data area */ data_area_of_target = (char *)&pSMBt->hdr.Protocol + le16_to_cpu(pSMBt->t2_rsp.DataOffset); /* validate target area */ data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol + le16_to_cpu(pSMB2->t2_rsp.DataOffset); data_area_of_target += total_in_buf; /* copy second buffer into end of first buffer */ memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); total_in_buf += total_in_buf2; pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf); byte_count = le16_to_cpu(BCC_LE(pTargetSMB)); byte_count += total_in_buf2; BCC_LE(pTargetSMB) = cpu_to_le16(byte_count); byte_count = pTargetSMB->smb_buf_length; byte_count += total_in_buf2; /* BB also add check that we are not beyond maximum buffer size */ pTargetSMB->smb_buf_length = byte_count; if (remaining == total_in_buf2) { cFYI(1, ("found the last secondary response")); return 0; /* we are done */ } else /* more responses to go */ return 1; } static int cifs_demultiplex_thread(struct TCP_Server_Info *server) { int length; unsigned int pdu_length, total_read; struct smb_hdr *smb_buffer = NULL; struct smb_hdr *bigbuf = NULL; struct smb_hdr *smallbuf = NULL; struct msghdr smb_msg; struct kvec iov; struct socket *csocket = server->ssocket; struct list_head *tmp; struct cifsSesInfo *ses; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; char temp; bool isLargeBuf = false; bool isMultiRsp; int reconnect; current->flags |= PF_MEMALLOC; cFYI(1, ("Demultiplex PID: %d", task_pid_nr(current))); length = atomic_inc_return(&tcpSesAllocCount); if (length > 1) mempool_resize(cifs_req_poolp, length + cifs_min_rcv, GFP_KERNEL); set_freezable(); while (server->tcpStatus != CifsExiting) { if (try_to_freeze()) continue; if (bigbuf == NULL) { bigbuf = cifs_buf_get(); if (!bigbuf) { cERROR(1, ("No memory for large SMB response")); msleep(3000); /* retry will check if exiting */ continue; } } else if (isLargeBuf) { /* we are reusing a dirty large buf, clear its start */ memset(bigbuf, 0, sizeof(struct smb_hdr)); } if (smallbuf == NULL) { smallbuf = cifs_small_buf_get(); if (!smallbuf) { cERROR(1, ("No memory for SMB response")); msleep(1000); /* retry will check if exiting */ continue; } /* beginning of smb buffer is cleared in our buf_get */ } else /* if existing small buf clear beginning */ memset(smallbuf, 0, sizeof(struct smb_hdr)); isLargeBuf = false; isMultiRsp = false; smb_buffer = smallbuf; iov.iov_base = smb_buffer; iov.iov_len = 4; smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; pdu_length = 4; /* enough to get RFC1001 header */ incomplete_rcv: length = kernel_recvmsg(csocket, &smb_msg, &iov, 1, pdu_length, 0 /* BB other flags? */); if (server->tcpStatus == CifsExiting) { break; } else if (server->tcpStatus == CifsNeedReconnect) { cFYI(1, ("Reconnect after server stopped responding")); cifs_reconnect(server); cFYI(1, ("call to reconnect done")); csocket = server->ssocket; continue; } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) { msleep(1); /* minimum sleep to prevent looping allowing socket to clear and app threads to set tcpStatus CifsNeedReconnect if server hung */ if (pdu_length < 4) { iov.iov_base = (4 - pdu_length) + (char *)smb_buffer; iov.iov_len = pdu_length; smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; goto incomplete_rcv; } else continue; } else if (length <= 0) { if (server->tcpStatus == CifsNew) { cFYI(1, ("tcp session abend after SMBnegprot")); /* some servers kill the TCP session rather than returning an SMB negprot error, in which case reconnecting here is not going to help, and so simply return error to mount */ break; } if (!try_to_freeze() && (length == -EINTR)) { cFYI(1, ("cifsd thread killed")); break; } cFYI(1, ("Reconnect after unexpected peek error %d", length)); cifs_reconnect(server); csocket = server->ssocket; wake_up(&server->response_q); continue; } else if (length < pdu_length) { cFYI(1, ("requested %d bytes but only got %d bytes", pdu_length, length)); pdu_length -= length; msleep(1); goto incomplete_rcv; } /* The right amount was read from socket - 4 bytes */ /* so we can now interpret the length field */ /* the first byte big endian of the length field, is actually not part of the length but the type with the most common, zero, as regular data */ temp = *((char *) smb_buffer); /* Note that FC 1001 length is big endian on the wire, but we convert it here so it is always manipulated as host byte order */ pdu_length = be32_to_cpu((__force __be32)smb_buffer->smb_buf_length); smb_buffer->smb_buf_length = pdu_length; cFYI(1, ("rfc1002 length 0x%x", pdu_length+4)); if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) { continue; } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { cFYI(1, ("Good RFC 1002 session rsp")); continue; } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { /* we get this from Windows 98 instead of an error on SMB negprot response */ cFYI(1, ("Negative RFC1002 Session Response Error 0x%x)", pdu_length)); if (server->tcpStatus == CifsNew) { /* if nack on negprot (rather than ret of smb negprot error) reconnecting not going to help, ret error to mount */ break; } else { /* give server a second to clean up before reconnect attempt */ msleep(1000); /* always try 445 first on reconnect since we get NACK on some if we ever connected to port 139 (the NACK is since we do not begin with RFC1001 session initialize frame) */ server->addr.sockAddr.sin_port = htons(CIFS_PORT); cifs_reconnect(server); csocket = server->ssocket; wake_up(&server->response_q); continue; } } else if (temp != (char) 0) { cERROR(1, ("Unknown RFC 1002 frame")); cifs_dump_mem(" Received Data: ", (char *)smb_buffer, length); cifs_reconnect(server); csocket = server->ssocket; continue; } /* else we have an SMB response */ if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) || (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) { cERROR(1, ("Invalid size SMB length %d pdu_length %d", length, pdu_length+4)); cifs_reconnect(server); csocket = server->ssocket; wake_up(&server->response_q); continue; } /* else length ok */ reconnect = 0; if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { isLargeBuf = true; memcpy(bigbuf, smallbuf, 4); smb_buffer = bigbuf; } length = 0; iov.iov_base = 4 + (char *)smb_buffer; iov.iov_len = pdu_length; for (total_read = 0; total_read < pdu_length; total_read += length) { length = kernel_recvmsg(csocket, &smb_msg, &iov, 1, pdu_length - total_read, 0); if ((server->tcpStatus == CifsExiting) || (length == -EINTR)) { /* then will exit */ reconnect = 2; break; } else if (server->tcpStatus == CifsNeedReconnect) { cifs_reconnect(server); csocket = server->ssocket; /* Reconnect wakes up rspns q */ /* Now we will reread sock */ reconnect = 1; break; } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) { msleep(1); /* minimum sleep to prevent looping, allowing socket to clear and app threads to set tcpStatus CifsNeedReconnect if server hung*/ length = 0; continue; } else if (length <= 0) { cERROR(1, ("Received no data, expecting %d", pdu_length - total_read)); cifs_reconnect(server); csocket = server->ssocket; reconnect = 1; break; } } if (reconnect == 2) break; else if (reconnect == 1) continue; length += 4; /* account for rfc1002 hdr */ dump_smb(smb_buffer, length); if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) { cifs_dump_mem("Bad SMB: ", smb_buffer, 48); continue; } task_to_wake = NULL; spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if ((mid_entry->mid == smb_buffer->Mid) && (mid_entry->midState == MID_REQUEST_SUBMITTED) && (mid_entry->command == smb_buffer->Command)) { if (check2ndT2(smb_buffer,server->maxBuf) > 0) { /* We have a multipart transact2 resp */ isMultiRsp = true; if (mid_entry->resp_buf) { /* merge response - fix up 1st*/ if (coalesce_t2(smb_buffer, mid_entry->resp_buf)) { mid_entry->multiRsp = true; break; } else { /* all parts received */ mid_entry->multiEnd = true; goto multi_t2_fnd; } } else { if (!isLargeBuf) { cERROR(1,("1st trans2 resp needs bigbuf")); /* BB maybe we can fix this up, switch to already allocated large buffer? */ } else { /* Have first buffer */ mid_entry->resp_buf = smb_buffer; mid_entry->largeBuf = true; bigbuf = NULL; } } break; } mid_entry->resp_buf = smb_buffer; mid_entry->largeBuf = isLargeBuf; multi_t2_fnd: task_to_wake = mid_entry->tsk; mid_entry->midState = MID_RESPONSE_RECEIVED; #ifdef CONFIG_CIFS_STATS2 mid_entry->when_received = jiffies; #endif /* so we do not time out requests to server which is still responding (since server could be busy but not dead) */ server->lstrp = jiffies; break; } } spin_unlock(&GlobalMid_Lock); if (task_to_wake) { /* Was previous buf put in mpx struct for multi-rsp? */ if (!isMultiRsp) { /* smb buffer will be freed by user thread */ if (isLargeBuf) bigbuf = NULL; else smallbuf = NULL; } wake_up_process(task_to_wake); } else if (!is_valid_oplock_break(smb_buffer, server) && !isMultiRsp) { cERROR(1, ("No task to wake, unknown frame received! " "NumMids %d", midCount.counter)); cifs_dump_mem("Received Data is: ", (char *)smb_buffer, sizeof(struct smb_hdr)); #ifdef CONFIG_CIFS_DEBUG2 cifs_dump_detail(smb_buffer); cifs_dump_mids(server); #endif /* CIFS_DEBUG2 */ } } /* end while !EXITING */ /* take it off the list, if it's not already */ write_lock(&cifs_tcp_ses_lock); list_del_init(&server->tcp_ses_list); write_unlock(&cifs_tcp_ses_lock); spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); wake_up_all(&server->response_q); /* check if we have blocked requests that need to free */ /* Note that cifs_max_pending is normally 50, but can be set at module install time to as little as two */ spin_lock(&GlobalMid_Lock); if (atomic_read(&server->inFlight) >= cifs_max_pending) atomic_set(&server->inFlight, cifs_max_pending - 1); /* We do not want to set the max_pending too low or we could end up with the counter going negative */ spin_unlock(&GlobalMid_Lock); /* Although there should not be any requests blocked on this queue it can not hurt to be paranoid and try to wake up requests that may haven been blocked when more than 50 at time were on the wire to the same server - they now will see the session is in exit state and get out of SendReceive. */ wake_up_all(&server->request_q); /* give those requests time to exit */ msleep(125); if (server->ssocket) { sock_release(csocket); server->ssocket = NULL; } /* buffer usuallly freed in free_mid - need to free it here on exit */ cifs_buf_release(bigbuf); if (smallbuf) /* no sense logging a debug message if NULL */ cifs_small_buf_release(smallbuf); /* * BB: we shouldn't have to do any of this. It shouldn't be * possible to exit from the thread with active SMB sessions */ read_lock(&cifs_tcp_ses_lock); if (list_empty(&server->pending_mid_q)) { /* loop through server session structures attached to this and mark them dead */ list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->status = CifsExiting; ses->server = NULL; } read_unlock(&cifs_tcp_ses_lock); } else { /* although we can not zero the server struct pointer yet, since there are active requests which may depnd on them, mark the corresponding SMB sessions as exiting too */ list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->status = CifsExiting; } spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if (mid_entry->midState == MID_REQUEST_SUBMITTED) { cFYI(1, ("Clearing Mid 0x%x - waking up ", mid_entry->mid)); task_to_wake = mid_entry->tsk; if (task_to_wake) wake_up_process(task_to_wake); } } spin_unlock(&GlobalMid_Lock); read_unlock(&cifs_tcp_ses_lock); /* 1/8th of sec is more than enough time for them to exit */ msleep(125); } if (!list_empty(&server->pending_mid_q)) { /* mpx threads have not exited yet give them at least the smb send timeout time for long ops */ /* due to delays on oplock break requests, we need to wait at least 45 seconds before giving up on a request getting a response and going ahead and killing cifsd */ cFYI(1, ("Wait for exit from demultiplex thread")); msleep(46000); /* if threads still have not exited they are probably never coming home not much else we can do but free the memory */ } /* last chance to mark ses pointers invalid if there are any pointing to this (e.g if a crazy root user tried to kill cifsd kernel thread explicitly this might happen) */ /* BB: This shouldn't be necessary, see above */ read_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->server = NULL; } read_unlock(&cifs_tcp_ses_lock); kfree(server->hostname); task_to_wake = xchg(&server->tsk, NULL); kfree(server); length = atomic_dec_return(&tcpSesAllocCount); if (length > 0) mempool_resize(cifs_req_poolp, length + cifs_min_rcv, GFP_KERNEL); /* if server->tsk was NULL then wait for a signal before exiting */ if (!task_to_wake) { set_current_state(TASK_INTERRUPTIBLE); while (!signal_pending(current)) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); } module_put_and_exit(0); } /* extract the host portion of the UNC string */ static char * extract_hostname(const char *unc) { const char *src; char *dst, *delim; unsigned int len; /* skip double chars at beginning of string */ /* BB: check validity of these bytes? */ src = unc + 2; /* delimiter between hostname and sharename is always '\\' now */ delim = strchr(src, '\\'); if (!delim) return ERR_PTR(-EINVAL); len = delim - src; dst = kmalloc((len + 1), GFP_KERNEL); if (dst == NULL) return ERR_PTR(-ENOMEM); memcpy(dst, src, len); dst[len] = '\0'; return dst; } static int cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol) { char *value; char *data; unsigned int temp_len, i, j; char separator[2]; separator[0] = ','; separator[1] = 0; if (Local_System_Name[0] != 0) memcpy(vol->source_rfc1001_name, Local_System_Name, 15); else { char *nodename = utsname()->nodename; int n = strnlen(nodename, 15); memset(vol->source_rfc1001_name, 0x20, 15); for (i = 0; i < n; i++) { /* does not have to be perfect mapping since field is informational, only used for servers that do not support port 445 and it can be overridden at mount time */ vol->source_rfc1001_name[i] = toupper(nodename[i]); } } vol->source_rfc1001_name[15] = 0; /* null target name indicates to use *SMBSERVR default called name if we end up sending RFC1001 session initialize */ vol->target_rfc1001_name[0] = 0; vol->linux_uid = current->uid; /* current->euid instead? */ vol->linux_gid = current->gid; vol->dir_mode = S_IRWXUGO; /* 2767 perms indicate mandatory locking support */ vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP); /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */ vol->rw = true; /* default is always to request posix paths. */ vol->posix_paths = 1; if (!options) return 1; if (strncmp(options, "sep=", 4) == 0) { if (options[4] != 0) { separator[0] = options[4]; options += 5; } else { cFYI(1, ("Null separator not allowed")); } } while ((data = strsep(&options, separator)) != NULL) { if (!*data) continue; if ((value = strchr(data, '=')) != NULL) *value++ = '\0'; /* Have to parse this before we parse for "user" */ if (strnicmp(data, "user_xattr", 10) == 0) { vol->no_xattr = 0; } else if (strnicmp(data, "nouser_xattr", 12) == 0) { vol->no_xattr = 1; } else if (strnicmp(data, "user", 4) == 0) { if (!value) { printk(KERN_WARNING "CIFS: invalid or missing username\n"); return 1; /* needs_arg; */ } else if (!*value) { /* null user, ie anonymous, authentication */ vol->nullauth = 1; } if (strnlen(value, 200) < 200) { vol->username = value; } else { printk(KERN_WARNING "CIFS: username too long\n"); return 1; } } else if (strnicmp(data, "pass", 4) == 0) { if (!value) { vol->password = NULL; continue; } else if (value[0] == 0) { /* check if string begins with double comma since that would mean the password really does start with a comma, and would not indicate an empty string */ if (value[1] != separator[0]) { vol->password = NULL; continue; } } temp_len = strlen(value); /* removed password length check, NTLM passwords can be arbitrarily long */ /* if comma in password, the string will be prematurely null terminated. Commas in password are specified across the cifs mount interface by a double comma ie ,, and a comma used as in other cases ie ',' as a parameter delimiter/separator is single and due to the strsep above is temporarily zeroed. */ /* NB: password legally can have multiple commas and the only illegal character in a password is null */ if ((value[temp_len] == 0) && (value[temp_len+1] == separator[0])) { /* reinsert comma */ value[temp_len] = separator[0]; temp_len += 2; /* move after second comma */ while (value[temp_len] != 0) { if (value[temp_len] == separator[0]) { if (value[temp_len+1] == separator[0]) { /* skip second comma */ temp_len++; } else { /* single comma indicating start of next parm */ break; } } temp_len++; } if (value[temp_len] == 0) { options = NULL; } else { value[temp_len] = 0; /* point option to start of next parm */ options = value + temp_len + 1; } /* go from value to value + temp_len condensing double commas to singles. Note that this ends up allocating a few bytes too many, which is ok */ vol->password = kzalloc(temp_len, GFP_KERNEL); if (vol->password == NULL) { printk(KERN_WARNING "CIFS: no memory " "for password\n"); return 1; } for (i = 0, j = 0; i < temp_len; i++, j++) { vol->password[j] = value[i]; if (value[i] == separator[0] && value[i+1] == separator[0]) { /* skip second comma */ i++; } } vol->password[j] = 0; } else { vol->password = kzalloc(temp_len+1, GFP_KERNEL); if (vol->password == NULL) { printk(KERN_WARNING "CIFS: no memory " "for password\n"); return 1; } strcpy(vol->password, value); } } else if (strnicmp(data, "ip", 2) == 0) { if (!value || !*value) { vol->UNCip = NULL; } else if (strnlen(value, 35) < 35) { vol->UNCip = value; } else { printk(KERN_WARNING "CIFS: ip address " "too long\n"); return 1; } } else if (strnicmp(data, "sec", 3) == 0) { if (!value || !*value) { cERROR(1, ("no security value specified")); continue; } else if (strnicmp(value, "krb5i", 5) == 0) { vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN; } else if (strnicmp(value, "krb5p", 5) == 0) { /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */ cERROR(1, ("Krb5 cifs privacy not supported")); return 1; } else if (strnicmp(value, "krb5", 4) == 0) { vol->secFlg |= CIFSSEC_MAY_KRB5; } else if (strnicmp(value, "ntlmv2i", 7) == 0) { vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN; } else if (strnicmp(value, "ntlmv2", 6) == 0) { vol->secFlg |= CIFSSEC_MAY_NTLMV2; } else if (strnicmp(value, "ntlmi", 5) == 0) { vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN; } else if (strnicmp(value, "ntlm", 4) == 0) { /* ntlm is default so can be turned off too */ vol->secFlg |= CIFSSEC_MAY_NTLM; } else if (strnicmp(value, "nontlm", 6) == 0) { /* BB is there a better way to do this? */ vol->secFlg |= CIFSSEC_MAY_NTLMV2; #ifdef CONFIG_CIFS_WEAK_PW_HASH } else if (strnicmp(value, "lanman", 6) == 0) { vol->secFlg |= CIFSSEC_MAY_LANMAN; #endif } else if (strnicmp(value, "none", 4) == 0) { vol->nullauth = 1; } else { cERROR(1, ("bad security option: %s", value)); return 1; } } else if ((strnicmp(data, "unc", 3) == 0) || (strnicmp(data, "target", 6) == 0) || (strnicmp(data, "path", 4) == 0)) { if (!value || !*value) { printk(KERN_WARNING "CIFS: invalid path to " "network resource\n"); return 1; /* needs_arg; */ } if ((temp_len = strnlen(value, 300)) < 300) { vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); if (vol->UNC == NULL) return 1; strcpy(vol->UNC, value); if (strncmp(vol->UNC, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { printk(KERN_WARNING "CIFS: UNC Path does not begin " "with // or \\\\ \n"); return 1; } } else { printk(KERN_WARNING "CIFS: UNC name too long\n"); return 1; } } else if ((strnicmp(data, "domain", 3) == 0) || (strnicmp(data, "workgroup", 5) == 0)) { if (!value || !*value) { printk(KERN_WARNING "CIFS: invalid domain name\n"); return 1; /* needs_arg; */ } /* BB are there cases in which a comma can be valid in a domain name and need special handling? */ if (strnlen(value, 256) < 256) { vol->domainname = value; cFYI(1, ("Domain name set")); } else { printk(KERN_WARNING "CIFS: domain name too " "long\n"); return 1; } } else if (strnicmp(data, "prefixpath", 10) == 0) { if (!value || !*value) { printk(KERN_WARNING "CIFS: invalid path prefix\n"); return 1; /* needs_argument */ } if ((temp_len = strnlen(value, 1024)) < 1024) { if (value[0] != '/') temp_len++; /* missing leading slash */ vol->prepath = kmalloc(temp_len+1, GFP_KERNEL); if (vol->prepath == NULL) return 1; if (value[0] != '/') { vol->prepath[0] = '/'; strcpy(vol->prepath+1, value); } else strcpy(vol->prepath, value); cFYI(1, ("prefix path %s", vol->prepath)); } else { printk(KERN_WARNING "CIFS: prefix too long\n"); return 1; } } else if (strnicmp(data, "iocharset", 9) == 0) { if (!value || !*value) { printk(KERN_WARNING "CIFS: invalid iocharset " "specified\n"); return 1; /* needs_arg; */ } if (strnlen(value, 65) < 65) { if (strnicmp(value, "default", 7)) vol->iocharset = value; /* if iocharset not set then load_nls_default is used by caller */ cFYI(1, ("iocharset set to %s", value)); } else { printk(KERN_WARNING "CIFS: iocharset name " "too long.\n"); return 1; } } else if (strnicmp(data, "uid", 3) == 0) { if (value && *value) { vol->linux_uid = simple_strtoul(value, &value, 0); vol->override_uid = 1; } } else if (strnicmp(data, "gid", 3) == 0) { if (value && *value) { vol->linux_gid = simple_strtoul(value, &value, 0); vol->override_gid = 1; } } else if (strnicmp(data, "file_mode", 4) == 0) { if (value && *value) { vol->file_mode = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "dir_mode", 4) == 0) { if (value && *value) { vol->dir_mode = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "dirmode", 4) == 0) { if (value && *value) { vol->dir_mode = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "port", 4) == 0) { if (value && *value) { vol->port = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "rsize", 5) == 0) { if (value && *value) { vol->rsize = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "wsize", 5) == 0) { if (value && *value) { vol->wsize = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "sockopt", 5) == 0) { if (value && *value) { vol->sockopt = simple_strtoul(value, &value, 0); } } else if (strnicmp(data, "netbiosname", 4) == 0) { if (!value || !*value || (*value == ' ')) { cFYI(1, ("invalid (empty) netbiosname")); } else { memset(vol->source_rfc1001_name, 0x20, 15); for (i = 0; i < 15; i++) { /* BB are there cases in which a comma can be valid in this workstation netbios name (and need special handling)? */ /* We do not uppercase netbiosname for user */ if (value[i] == 0) break; else vol->source_rfc1001_name[i] = value[i]; } /* The string has 16th byte zero still from set at top of the function */ if ((i == 15) && (value[i] != 0)) printk(KERN_WARNING "CIFS: netbiosname" " longer than 15 truncated.\n"); } } else if (strnicmp(data, "servern", 7) == 0) { /* servernetbiosname specified override *SMBSERVER */ if (!value || !*value || (*value == ' ')) { cFYI(1, ("empty server netbiosname specified")); } else { /* last byte, type, is 0x20 for servr type */ memset(vol->target_rfc1001_name, 0x20, 16); for (i = 0; i < 15; i++) { /* BB are there cases in which a comma can be valid in this workstation netbios name (and need special handling)? */ /* user or mount helper must uppercase the netbiosname */ if (value[i] == 0) break; else vol->target_rfc1001_name[i] = value[i]; } /* The string has 16th byte zero still from set at top of the function */ if ((i == 15) && (value[i] != 0)) printk(KERN_WARNING "CIFS: server net" "biosname longer than 15 truncated.\n"); } } else if (strnicmp(data, "credentials", 4) == 0) { /* ignore */ } else if (strnicmp(data, "version", 3) == 0) { /* ignore */ } else if (strnicmp(data, "guest", 5) == 0) { /* ignore */ } else if (strnicmp(data, "rw", 2) == 0) { vol->rw = true; } else if (strnicmp(data, "noblocksend", 11) == 0) { vol->noblocksnd = 1; } else if (strnicmp(data, "noautotune", 10) == 0) { vol->noautotune = 1; } else if ((strnicmp(data, "suid", 4) == 0) || (strnicmp(data, "nosuid", 6) == 0) || (strnicmp(data, "exec", 4) == 0) || (strnicmp(data, "noexec", 6) == 0) || (strnicmp(data, "nodev", 5) == 0) || (strnicmp(data, "noauto", 6) == 0) || (strnicmp(data, "dev", 3) == 0)) { /* The mount tool or mount.cifs helper (if present) uses these opts to set flags, and the flags are read by the kernel vfs layer before we get here (ie before read super) so there is no point trying to parse these options again and set anything and it is ok to just ignore them */ continue; } else if (strnicmp(data, "ro", 2) == 0) { vol->rw = false; } else if (strnicmp(data, "hard", 4) == 0) { vol->retry = 1; } else if (strnicmp(data, "soft", 4) == 0) { vol->retry = 0; } else if (strnicmp(data, "perm", 4) == 0) { vol->noperm = 0; } else if (strnicmp(data, "noperm", 6) == 0) { vol->noperm = 1; } else if (strnicmp(data, "mapchars", 8) == 0) { vol->remap = 1; } else if (strnicmp(data, "nomapchars", 10) == 0) { vol->remap = 0; } else if (strnicmp(data, "sfu", 3) == 0) { vol->sfu_emul = 1; } else if (strnicmp(data, "nosfu", 5) == 0) { vol->sfu_emul = 0; } else if (strnicmp(data, "nodfs", 5) == 0) { vol->nodfs = 1; } else if (strnicmp(data, "posixpaths", 10) == 0) { vol->posix_paths = 1; } else if (strnicmp(data, "noposixpaths", 12) == 0) { vol->posix_paths = 0; } else if (strnicmp(data, "nounix", 6) == 0) { vol->no_linux_ext = 1; } else if (strnicmp(data, "nolinux", 7) == 0) { vol->no_linux_ext = 1; } else if ((strnicmp(data, "nocase", 6) == 0) || (strnicmp(data, "ignorecase", 10) == 0)) { vol->nocase = 1; } else if (strnicmp(data, "brl", 3) == 0) { vol->nobrl = 0; } else if ((strnicmp(data, "nobrl", 5) == 0) || (strnicmp(data, "nolock", 6) == 0)) { vol->nobrl = 1; /* turn off mandatory locking in mode if remote locking is turned off since the local vfs will do advisory */ if (vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP))) vol->file_mode = S_IALLUGO; } else if (strnicmp(data, "forcemandatorylock", 9) == 0) { /* will take the shorter form "forcemand" as well */ /* This mount option will force use of mandatory (DOS/Windows style) byte range locks, instead of using posix advisory byte range locks, even if the Unix extensions are available and posix locks would be supported otherwise. If Unix extensions are not negotiated this has no effect since mandatory locks would be used (mandatory locks is all that those those servers support) */ vol->mand_lock = 1; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { vol->setuids = 0; } else if (strnicmp(data, "dynperm", 7) == 0) { vol->dynperm = true; } else if (strnicmp(data, "nodynperm", 9) == 0) { vol->dynperm = false; } else if (strnicmp(data, "nohard", 6) == 0) { vol->retry = 0; } else if (strnicmp(data, "nosoft", 6) == 0) { vol->retry = 1; } else if (strnicmp(data, "nointr", 6) == 0) { vol->intr = 0; } else if (strnicmp(data, "intr", 4) == 0) { vol->intr = 1; } else if (strnicmp(data, "serverino", 7) == 0) { vol->server_ino = 1; } else if (strnicmp(data, "noserverino", 9) == 0) { vol->server_ino = 0; } else if (strnicmp(data, "cifsacl", 7) == 0) { vol->cifs_acl = 1; } else if (strnicmp(data, "nocifsacl", 9) == 0) { vol->cifs_acl = 0; } else if (strnicmp(data, "acl", 3) == 0) { vol->no_psx_acl = 0; } else if (strnicmp(data, "noacl", 5) == 0) { vol->no_psx_acl = 1; #ifdef CONFIG_CIFS_EXPERIMENTAL } else if (strnicmp(data, "locallease", 6) == 0) { vol->local_lease = 1; #endif } else if (strnicmp(data, "sign", 4) == 0) { vol->secFlg |= CIFSSEC_MUST_SIGN; } else if (strnicmp(data, "seal", 4) == 0) { /* we do not do the following in secFlags because seal is a per tree connection (mount) not a per socket or per-smb connection option in the protocol */ /* vol->secFlg |= CIFSSEC_MUST_SEAL; */ vol->seal = 1; } else if (strnicmp(data, "direct", 6) == 0) { vol->direct_io = 1; } else if (strnicmp(data, "forcedirectio", 13) == 0) { vol->direct_io = 1; } else if (strnicmp(data, "in6_addr", 8) == 0) { if (!value || !*value) { vol->in6_addr = NULL; } else if (strnlen(value, 49) == 48) { vol->in6_addr = value; } else { printk(KERN_WARNING "CIFS: ip v6 address not " "48 characters long\n"); return 1; } } else if (strnicmp(data, "noac", 4) == 0) { printk(KERN_WARNING "CIFS: Mount option noac not " "supported. Instead set " "/proc/fs/cifs/LookupCacheEnabled to 0\n"); } else printk(KERN_WARNING "CIFS: Unknown mount option %s\n", data); } if (vol->UNC == NULL) { if (devname == NULL) { printk(KERN_WARNING "CIFS: Missing UNC name for mount " "target\n"); return 1; } if ((temp_len = strnlen(devname, 300)) < 300) { vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); if (vol->UNC == NULL) return 1; strcpy(vol->UNC, devname); if (strncmp(vol->UNC, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { printk(KERN_WARNING "CIFS: UNC Path does not " "begin with // or \\\\ \n"); return 1; } value = strpbrk(vol->UNC+2, "/\\"); if (value) *value = '\\'; } else { printk(KERN_WARNING "CIFS: UNC name too long\n"); return 1; } } if (vol->UNCip == NULL) vol->UNCip = &vol->UNC[2]; return 0; } static struct TCP_Server_Info * cifs_find_tcp_session(struct sockaddr *addr) { struct list_head *tmp; struct TCP_Server_Info *server; struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; write_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &cifs_tcp_ses_list) { server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list); /* * the demux thread can exit on its own while still in CifsNew * so don't accept any sockets in that state. Since the * tcpStatus never changes back to CifsNew it's safe to check * for this without a lock. */ if (server->tcpStatus == CifsNew) continue; if (addr->sa_family == AF_INET && (addr4->sin_addr.s_addr != server->addr.sockAddr.sin_addr.s_addr)) continue; else if (addr->sa_family == AF_INET6 && memcmp(&server->addr.sockAddr6.sin6_addr, &addr6->sin6_addr, sizeof(addr6->sin6_addr))) continue; ++server->srv_count; write_unlock(&cifs_tcp_ses_lock); cFYI(1, ("Existing tcp session with server found")); return server; } write_unlock(&cifs_tcp_ses_lock); return NULL; } static void cifs_put_tcp_session(struct TCP_Server_Info *server) { struct task_struct *task; write_lock(&cifs_tcp_ses_lock); if (--server->srv_count > 0) { write_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&server->tcp_ses_list); write_unlock(&cifs_tcp_ses_lock); spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); task = xchg(&server->tsk, NULL); if (task) force_sig(SIGKILL, task); } static struct TCP_Server_Info * cifs_get_tcp_session(struct smb_vol *volume_info) { struct TCP_Server_Info *tcp_ses = NULL; struct sockaddr addr; struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr; struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; int rc; memset(&addr, 0, sizeof(struct sockaddr)); if (volume_info->UNCip && volume_info->UNC) { rc = cifs_inet_pton(AF_INET, volume_info->UNCip, &sin_server->sin_addr.s_addr); if (rc <= 0) { /* not ipv4 address, try ipv6 */ rc = cifs_inet_pton(AF_INET6, volume_info->UNCip, &sin_server6->sin6_addr.in6_u); if (rc > 0) addr.sa_family = AF_INET6; } else { addr.sa_family = AF_INET; } if (rc <= 0) { /* we failed translating address */ rc = -EINVAL; goto out_err; } cFYI(1, ("UNC: %s ip: %s", volume_info->UNC, volume_info->UNCip)); } else if (volume_info->UNCip) { /* BB using ip addr as tcp_ses name to connect to the DFS root below */ cERROR(1, ("Connecting to DFS root not implemented yet")); rc = -EINVAL; goto out_err; } else /* which tcp_sess DFS root would we conect to */ { cERROR(1, ("CIFS mount error: No UNC path (e.g. -o " "unc=//192.168.1.100/public) specified")); rc = -EINVAL; goto out_err; } /* see if we already have a matching tcp_ses */ tcp_ses = cifs_find_tcp_session(&addr); if (tcp_ses) return tcp_ses; tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); if (!tcp_ses) { rc = -ENOMEM; goto out_err; } tcp_ses->hostname = extract_hostname(volume_info->UNC); if (IS_ERR(tcp_ses->hostname)) { rc = PTR_ERR(tcp_ses->hostname); goto out_err; } tcp_ses->noblocksnd = volume_info->noblocksnd; tcp_ses->noautotune = volume_info->noautotune; atomic_set(&tcp_ses->inFlight, 0); init_waitqueue_head(&tcp_ses->response_q); init_waitqueue_head(&tcp_ses->request_q); INIT_LIST_HEAD(&tcp_ses->pending_mid_q); mutex_init(&tcp_ses->srv_mutex); memcpy(tcp_ses->workstation_RFC1001_name, volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); memcpy(tcp_ses->server_RFC1001_name, volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); tcp_ses->sequence_number = 0; INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); /* * at this point we are the only ones with the pointer * to the struct since the kernel thread not created yet * no need to spinlock this init of tcpStatus or srv_count */ tcp_ses->tcpStatus = CifsNew; ++tcp_ses->srv_count; if (addr.sa_family == AF_INET6) { cFYI(1, ("attempting ipv6 connect")); /* BB should we allow ipv6 on port 139? */ /* other OS never observed in Wild doing 139 with v6 */ memcpy(&tcp_ses->addr.sockAddr6, sin_server6, sizeof(struct sockaddr_in6)); sin_server6->sin6_port = htons(volume_info->port); rc = ipv6_connect(tcp_ses); } else { memcpy(&tcp_ses->addr.sockAddr, sin_server, sizeof(struct sockaddr_in)); sin_server->sin_port = htons(volume_info->port); rc = ipv4_connect(tcp_ses); } if (rc < 0) { cERROR(1, ("Error connecting to socket. Aborting operation")); goto out_err; } /* * since we're in a cifs function already, we know that * this will succeed. No need for try_module_get(). */ __module_get(THIS_MODULE); tcp_ses->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, tcp_ses, "cifsd"); if (IS_ERR(tcp_ses->tsk)) { rc = PTR_ERR(tcp_ses->tsk); cERROR(1, ("error %d create cifsd thread", rc)); module_put(THIS_MODULE); goto out_err; } /* thread spawned, put it on the list */ write_lock(&cifs_tcp_ses_lock); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); write_unlock(&cifs_tcp_ses_lock); return tcp_ses; out_err: if (tcp_ses) { kfree(tcp_ses->hostname); if (tcp_ses->ssocket) sock_release(tcp_ses->ssocket); kfree(tcp_ses); } return ERR_PTR(rc); } static struct cifsSesInfo * cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) { struct list_head *tmp; struct cifsSesInfo *ses; write_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) continue; ++ses->ses_count; write_unlock(&cifs_tcp_ses_lock); return ses; } write_unlock(&cifs_tcp_ses_lock); return NULL; } static void cifs_put_smb_ses(struct cifsSesInfo *ses) { int xid; struct TCP_Server_Info *server = ses->server; write_lock(&cifs_tcp_ses_lock); if (--ses->ses_count > 0) { write_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&ses->smb_ses_list); write_unlock(&cifs_tcp_ses_lock); if (ses->status == CifsGood) { xid = GetXid(); CIFSSMBLogoff(xid, ses); _FreeXid(xid); } sesInfoFree(ses); cifs_put_tcp_session(server); } static struct cifsTconInfo * cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) { struct list_head *tmp; struct cifsTconInfo *tcon; write_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &ses->tcon_list) { tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); if (tcon->tidStatus == CifsExiting) continue; if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE)) continue; ++tcon->tc_count; write_unlock(&cifs_tcp_ses_lock); return tcon; } write_unlock(&cifs_tcp_ses_lock); return NULL; } static void cifs_put_tcon(struct cifsTconInfo *tcon) { int xid; struct cifsSesInfo *ses = tcon->ses; write_lock(&cifs_tcp_ses_lock); if (--tcon->tc_count > 0) { write_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&tcon->tcon_list); write_unlock(&cifs_tcp_ses_lock); xid = GetXid(); CIFSSMBTDis(xid, tcon); _FreeXid(xid); DeleteTconOplockQEntries(tcon); tconInfoFree(tcon); cifs_put_smb_ses(ses); } int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, const struct nls_table *nls_codepage, unsigned int *pnum_referrals, struct dfs_info3_param **preferrals, int remap) { char *temp_unc; int rc = 0; *pnum_referrals = 0; *preferrals = NULL; if (pSesInfo->ipc_tid == 0) { temp_unc = kmalloc(2 /* for slashes */ + strnlen(pSesInfo->serverName, SERVER_NAME_LEN_WITH_NULL * 2) + 1 + 4 /* slash IPC$ */ + 2, GFP_KERNEL); if (temp_unc == NULL) return -ENOMEM; temp_unc[0] = '\\'; temp_unc[1] = '\\'; strcpy(temp_unc + 2, pSesInfo->serverName); strcpy(temp_unc + 2 + strlen(pSesInfo->serverName), "\\IPC$"); rc = CIFSTCon(xid, pSesInfo, temp_unc, NULL, nls_codepage); cFYI(1, ("CIFS Tcon rc = %d ipc_tid = %d", rc, pSesInfo->ipc_tid)); kfree(temp_unc); } if (rc == 0) rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals, pnum_referrals, nls_codepage, remap); /* BB map targetUNCs to dfs_info3 structures, here or in CIFSGetDFSRefer BB */ return rc; } #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key cifs_key[2]; static struct lock_class_key cifs_slock_key[2]; static inline void cifs_reclassify_socket4(struct socket *sock) { struct sock *sk = sock->sk; BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); } static inline void cifs_reclassify_socket6(struct socket *sock) { struct sock *sk = sock->sk; BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); } #else static inline void cifs_reclassify_socket4(struct socket *sock) { } static inline void cifs_reclassify_socket6(struct socket *sock) { } #endif /* See RFC1001 section 14 on representation of Netbios names */ static void rfc1002mangle(char *target, char *source, unsigned int length) { unsigned int i, j; for (i = 0, j = 0; i < (length); i++) { /* mask a nibble at a time and encode */ target[j] = 'A' + (0x0F & (source[i] >> 4)); target[j+1] = 'A' + (0x0F & source[i]); j += 2; } } static int ipv4_connect(struct TCP_Server_Info *server) { int rc = 0; bool connected = false; __be16 orig_port = 0; struct socket *socket = server->ssocket; if (socket == NULL) { rc = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &socket); if (rc < 0) { cERROR(1, ("Error %d creating socket", rc)); return rc; } /* BB other socket options to set KEEPALIVE, NODELAY? */ cFYI(1, ("Socket created")); server->ssocket = socket; socket->sk->sk_allocation = GFP_NOFS; cifs_reclassify_socket4(socket); } /* user overrode default port */ if (server->addr.sockAddr.sin_port) { rc = socket->ops->connect(socket, (struct sockaddr *) &server->addr.sockAddr, sizeof(struct sockaddr_in), 0); if (rc >= 0) connected = true; } if (!connected) { /* save original port so we can retry user specified port later if fall back ports fail this time */ orig_port = server->addr.sockAddr.sin_port; /* do not retry on the same port we just failed on */ if (server->addr.sockAddr.sin_port != htons(CIFS_PORT)) { server->addr.sockAddr.sin_port = htons(CIFS_PORT); rc = socket->ops->connect(socket, (struct sockaddr *) &server->addr.sockAddr, sizeof(struct sockaddr_in), 0); if (rc >= 0) connected = true; } } if (!connected) { server->addr.sockAddr.sin_port = htons(RFC1001_PORT); rc = socket->ops->connect(socket, (struct sockaddr *) &server->addr.sockAddr, sizeof(struct sockaddr_in), 0); if (rc >= 0) connected = true; } /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { if (orig_port) server->addr.sockAddr.sin_port = orig_port; cFYI(1, ("Error %d connecting to server via ipv4", rc)); sock_release(socket); server->ssocket = NULL; return rc; } /* * Eventually check for other socket options to change from * the default. sock_setsockopt not used because it expects * user space buffer */ socket->sk->sk_rcvtimeo = 7 * HZ; socket->sk->sk_sndtimeo = 3 * HZ; /* make the bufsizes depend on wsize/rsize and max requests */ if (server->noautotune) { if (socket->sk->sk_sndbuf < (200 * 1024)) socket->sk->sk_sndbuf = 200 * 1024; if (socket->sk->sk_rcvbuf < (140 * 1024)) socket->sk->sk_rcvbuf = 140 * 1024; } cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx", socket->sk->sk_sndbuf, socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo)); /* send RFC1001 sessinit */ if (server->addr.sockAddr.sin_port == htons(RFC1001_PORT)) { /* some servers require RFC1001 sessinit before sending negprot - BB check reconnection in case where second sessinit is sent but no second negprot */ struct rfc1002_session_packet *ses_init_buf; struct smb_hdr *smb_buf; ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), GFP_KERNEL); if (ses_init_buf) { ses_init_buf->trailer.session_req.called_len = 32; if (server->server_RFC1001_name && server->server_RFC1001_name[0] != 0) rfc1002mangle(ses_init_buf->trailer. session_req.called_name, server->server_RFC1001_name, RFC1001_NAME_LEN_WITH_NULL); else rfc1002mangle(ses_init_buf->trailer. session_req.called_name, DEFAULT_CIFS_CALLED_NAME, RFC1001_NAME_LEN_WITH_NULL); ses_init_buf->trailer.session_req.calling_len = 32; /* calling name ends in null (byte 16) from old smb convention. */ if (server->workstation_RFC1001_name && server->workstation_RFC1001_name[0] != 0) rfc1002mangle(ses_init_buf->trailer. session_req.calling_name, server->workstation_RFC1001_name, RFC1001_NAME_LEN_WITH_NULL); else rfc1002mangle(ses_init_buf->trailer. session_req.calling_name, "LINUX_CIFS_CLNT", RFC1001_NAME_LEN_WITH_NULL); ses_init_buf->trailer.session_req.scope1 = 0; ses_init_buf->trailer.session_req.scope2 = 0; smb_buf = (struct smb_hdr *)ses_init_buf; /* sizeof RFC1002_SESSION_REQUEST with no scope */ smb_buf->smb_buf_length = 0x81000044; rc = smb_send(socket, smb_buf, 0x44, (struct sockaddr *) &server->addr.sockAddr, server->noblocksnd); kfree(ses_init_buf); msleep(1); /* RFC1001 layer in at least one server requires very short break before negprot presumably because not expecting negprot to follow so fast. This is a simple solution that works without complicating the code and causes no significant slowing down on mount for everyone else */ } /* else the negprot may still work without this even though malloc failed */ } return rc; } static int ipv6_connect(struct TCP_Server_Info *server) { int rc = 0; bool connected = false; __be16 orig_port = 0; struct socket *socket = server->ssocket; if (socket == NULL) { rc = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &socket); if (rc < 0) { cERROR(1, ("Error %d creating ipv6 socket", rc)); socket = NULL; return rc; } /* BB other socket options to set KEEPALIVE, NODELAY? */ cFYI(1, ("ipv6 Socket created")); server->ssocket = socket; socket->sk->sk_allocation = GFP_NOFS; cifs_reclassify_socket6(socket); } /* user overrode default port */ if (server->addr.sockAddr6.sin6_port) { rc = socket->ops->connect(socket, (struct sockaddr *) &server->addr.sockAddr6, sizeof(struct sockaddr_in6), 0); if (rc >= 0) connected = true; } if (!connected) { /* save original port so we can retry user specified port later if fall back ports fail this time */ orig_port = server->addr.sockAddr6.sin6_port; /* do not retry on the same port we just failed on */ if (server->addr.sockAddr6.sin6_port != htons(CIFS_PORT)) { server->addr.sockAddr6.sin6_port = htons(CIFS_PORT); rc = socket->ops->connect(socket, (struct sockaddr *) &server->addr.sockAddr6, sizeof(struct sockaddr_in6), 0); if (rc >= 0) connected = true; } } if (!connected) { server->addr.sockAddr6.sin6_port = htons(RFC1001_PORT); rc = socket->ops->connect(socket, (struct sockaddr *) &server->addr.sockAddr6, sizeof(struct sockaddr_in6), 0); if (rc >= 0) connected = true; } /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { if (orig_port) server->addr.sockAddr6.sin6_port = orig_port; cFYI(1, ("Error %d connecting to server via ipv6", rc)); sock_release(socket); server->ssocket = NULL; return rc; } /* * Eventually check for other socket options to change from * the default. sock_setsockopt not used because it expects * user space buffer */ socket->sk->sk_rcvtimeo = 7 * HZ; socket->sk->sk_sndtimeo = 3 * HZ; server->ssocket = socket; return rc; } void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, struct super_block *sb, struct smb_vol *vol_info) { /* if we are reconnecting then should we check to see if * any requested capabilities changed locally e.g. via * remount but we can not do much about it here * if they have (even if we could detect it by the following) * Perhaps we could add a backpointer to array of sb from tcon * or if we change to make all sb to same share the same * sb as NFS - then we only have one backpointer to sb. * What if we wanted to mount the server share twice once with * and once without posixacls or posix paths? */ __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); if (vol_info && vol_info->no_linux_ext) { tcon->fsUnixInfo.Capability = 0; tcon->unix_ext = 0; /* Unix Extensions disabled */ cFYI(1, ("Linux protocol extensions disabled")); return; } else if (vol_info) tcon->unix_ext = 1; /* Unix Extensions supported */ if (tcon->unix_ext == 0) { cFYI(1, ("Unix extensions disabled so not set on reconnect")); return; } if (!CIFSSMBQFSUnixInfo(xid, tcon)) { __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); /* check for reconnect case in which we do not want to change the mount behavior if we can avoid it */ if (vol_info == NULL) { /* turn off POSIX ACL and PATHNAMES if not set originally at mount time */ if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) cERROR(1, ("POSIXPATH support change")); cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { cERROR(1, ("possible reconnect error")); cERROR(1, ("server disabled POSIX path support")); } } cap &= CIFS_UNIX_CAP_MASK; if (vol_info && vol_info->no_psx_acl) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { cFYI(1, ("negotiated posix acl support")); if (sb) sb->s_flags |= MS_POSIXACL; } if (vol_info && vol_info->posix_paths == 0) cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { cFYI(1, ("negotiate posix pathnames")); if (sb) CIFS_SB(sb)->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; } /* We might be setting the path sep back to a different form if we are reconnecting and the server switched its posix path capability for this share */ if (sb && (CIFS_SB(sb)->prepathlen > 0)) CIFS_SB(sb)->prepath[0] = CIFS_DIR_SEP(CIFS_SB(sb)); if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) { if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) { CIFS_SB(sb)->rsize = 127 * 1024; cFYI(DBG2, ("larger reads not supported by srv")); } } cFYI(1, ("Negotiate caps 0x%x", (int)cap)); #ifdef CONFIG_CIFS_DEBUG2 if (cap & CIFS_UNIX_FCNTL_CAP) cFYI(1, ("FCNTL cap")); if (cap & CIFS_UNIX_EXTATTR_CAP) cFYI(1, ("EXTATTR cap")); if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) cFYI(1, ("POSIX path cap")); if (cap & CIFS_UNIX_XATTR_CAP) cFYI(1, ("XATTR cap")); if (cap & CIFS_UNIX_POSIX_ACL_CAP) cFYI(1, ("POSIX ACL cap")); if (cap & CIFS_UNIX_LARGE_READ_CAP) cFYI(1, ("very large read cap")); if (cap & CIFS_UNIX_LARGE_WRITE_CAP) cFYI(1, ("very large write cap")); #endif /* CIFS_DEBUG2 */ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { if (vol_info == NULL) { cFYI(1, ("resetting capabilities failed")); } else cERROR(1, ("Negotiating Unix capabilities " "with the server failed. Consider " "mounting with the Unix Extensions\n" "disabled, if problems are found, " "by specifying the nounix mount " "option.")); } } } static void convert_delimiter(char *path, char delim) { int i; char old_delim; if (path == NULL) return; if (delim == '/') old_delim = '\\'; else old_delim = '/'; for (i = 0; path[i] != '\0'; i++) { if (path[i] == old_delim) path[i] = delim; } } static void setup_cifs_sb(struct smb_vol *pvolume_info, struct cifs_sb_info *cifs_sb) { if (pvolume_info->rsize > CIFSMaxBufSize) { cERROR(1, ("rsize %d too large, using MaxBufSize", pvolume_info->rsize)); cifs_sb->rsize = CIFSMaxBufSize; } else if ((pvolume_info->rsize) && (pvolume_info->rsize <= CIFSMaxBufSize)) cifs_sb->rsize = pvolume_info->rsize; else /* default */ cifs_sb->rsize = CIFSMaxBufSize; if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { cERROR(1, ("wsize %d too large, using 4096 instead", pvolume_info->wsize)); cifs_sb->wsize = 4096; } else if (pvolume_info->wsize) cifs_sb->wsize = pvolume_info->wsize; else cifs_sb->wsize = min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, 127*1024); /* old default of CIFSMaxBufSize was too small now that SMB Write2 can send multiple pages in kvec. RFC1001 does not describe what happens when frame bigger than 128K is sent so use that as max in conjunction with 52K kvec constraint on arch with 4K page size */ if (cifs_sb->rsize < 2048) { cifs_sb->rsize = 2048; /* Windows ME may prefer this */ cFYI(1, ("readsize set to minimum: 2048")); } /* calculate prepath */ cifs_sb->prepath = pvolume_info->prepath; if (cifs_sb->prepath) { cifs_sb->prepathlen = strlen(cifs_sb->prepath); /* we can not convert the / to \ in the path separators in the prefixpath yet because we do not know (until reset_cifs_unix_caps is called later) whether POSIX PATH CAP is available. We normalize the / to \ after reset_cifs_unix_caps is called */ pvolume_info->prepath = NULL; } else cifs_sb->prepathlen = 0; cifs_sb->mnt_uid = pvolume_info->linux_uid; cifs_sb->mnt_gid = pvolume_info->linux_gid; cifs_sb->mnt_file_mode = pvolume_info->file_mode; cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; cFYI(1, ("file mode: 0x%x dir mode: 0x%x", cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); if (pvolume_info->noperm) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; if (pvolume_info->setuids) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; if (pvolume_info->server_ino) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; if (pvolume_info->remap) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; if (pvolume_info->no_xattr) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; if (pvolume_info->sfu_emul) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; if (pvolume_info->nobrl) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; if (pvolume_info->mand_lock) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; if (pvolume_info->cifs_acl) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; if (pvolume_info->override_uid) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; if (pvolume_info->override_gid) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; if (pvolume_info->dynperm) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; if (pvolume_info->direct_io) { cFYI(1, ("mounting share using direct i/o")); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) cERROR(1, ("mount option dynperm ignored if cifsacl " "mount option supported")); } int cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, char *mount_data, const char *devname) { int rc = 0; int xid; struct smb_vol *volume_info; struct cifsSesInfo *pSesInfo = NULL; struct cifsTconInfo *tcon = NULL; struct TCP_Server_Info *srvTcp = NULL; xid = GetXid(); volume_info = kzalloc(sizeof(struct smb_vol), GFP_KERNEL); if (!volume_info) { rc = -ENOMEM; goto out; } if (cifs_parse_mount_options(mount_data, devname, volume_info)) { rc = -EINVAL; goto out; } if (volume_info->nullauth) { cFYI(1, ("null user")); volume_info->username = ""; } else if (volume_info->username) { /* BB fixme parse for domain name here */ cFYI(1, ("Username: %s", volume_info->username)); } else { cifserror("No username specified"); /* In userspace mount helper we can get user name from alternate locations such as env variables and files on disk */ rc = -EINVAL; goto out; } /* this is needed for ASCII cp to Unicode converts */ if (volume_info->iocharset == NULL) { cifs_sb->local_nls = load_nls_default(); /* load_nls_default can not return null */ } else { cifs_sb->local_nls = load_nls(volume_info->iocharset); if (cifs_sb->local_nls == NULL) { cERROR(1, ("CIFS mount error: iocharset %s not found", volume_info->iocharset)); rc = -ELIBACC; goto out; } } /* get a reference to a tcp session */ srvTcp = cifs_get_tcp_session(volume_info); if (IS_ERR(srvTcp)) { rc = PTR_ERR(srvTcp); goto out; } pSesInfo = cifs_find_smb_ses(srvTcp, volume_info->username); if (pSesInfo) { cFYI(1, ("Existing smb sess found (status=%d)", pSesInfo->status)); /* * The existing SMB session already has a reference to srvTcp, * so we can put back the extra one we got before */ cifs_put_tcp_session(srvTcp); down(&pSesInfo->sesSem); if (pSesInfo->need_reconnect) { cFYI(1, ("Session needs reconnect")); rc = cifs_setup_session(xid, pSesInfo, cifs_sb->local_nls); } up(&pSesInfo->sesSem); } else if (!rc) { cFYI(1, ("Existing smb sess not found")); pSesInfo = sesInfoAlloc(); if (pSesInfo == NULL) { rc = -ENOMEM; goto mount_fail_check; } /* new SMB session uses our srvTcp ref */ pSesInfo->server = srvTcp; if (srvTcp->addr.sockAddr6.sin6_family == AF_INET6) sprintf(pSesInfo->serverName, NIP6_FMT, NIP6(srvTcp->addr.sockAddr6.sin6_addr)); else sprintf(pSesInfo->serverName, NIPQUAD_FMT, NIPQUAD(srvTcp->addr.sockAddr.sin_addr.s_addr)); write_lock(&cifs_tcp_ses_lock); list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list); write_unlock(&cifs_tcp_ses_lock); /* volume_info->password freed at unmount */ if (volume_info->password) { pSesInfo->password = kstrdup(volume_info->password, GFP_KERNEL); if (!pSesInfo->password) { rc = -ENOMEM; goto mount_fail_check; } } if (volume_info->username) strncpy(pSesInfo->userName, volume_info->username, MAX_USERNAME_SIZE); if (volume_info->domainname) { int len = strlen(volume_info->domainname); pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); if (pSesInfo->domainName) strcpy(pSesInfo->domainName, volume_info->domainname); } pSesInfo->linux_uid = volume_info->linux_uid; pSesInfo->overrideSecFlg = volume_info->secFlg; down(&pSesInfo->sesSem); /* BB FIXME need to pass vol->secFlgs BB */ rc = cifs_setup_session(xid, pSesInfo, cifs_sb->local_nls); up(&pSesInfo->sesSem); } /* search for existing tcon to this server share */ if (!rc) { setup_cifs_sb(volume_info, cifs_sb); tcon = cifs_find_tcon(pSesInfo, volume_info->UNC); if (tcon) { cFYI(1, ("Found match on UNC path")); /* existing tcon already has a reference */ cifs_put_smb_ses(pSesInfo); if (tcon->seal != volume_info->seal) cERROR(1, ("transport encryption setting " "conflicts with existing tid")); } else { tcon = tconInfoAlloc(); if (tcon == NULL) { rc = -ENOMEM; goto mount_fail_check; } tcon->ses = pSesInfo; if (volume_info->password) { tcon->password = kstrdup(volume_info->password, GFP_KERNEL); if (!tcon->password) { rc = -ENOMEM; goto mount_fail_check; } } /* check for null share name ie connect to dfs root */ if ((strchr(volume_info->UNC + 3, '\\') == NULL) && (strchr(volume_info->UNC + 3, '/') == NULL)) { /* rc = connect_to_dfs_path(...) */ cFYI(1, ("DFS root not supported")); rc = -ENODEV; goto mount_fail_check; } else { /* BB Do we need to wrap sesSem around * this TCon call and Unix SetFS as * we do on SessSetup and reconnect? */ rc = CIFSTCon(xid, pSesInfo, volume_info->UNC, tcon, cifs_sb->local_nls); cFYI(1, ("CIFS Tcon rc = %d", rc)); if (volume_info->nodfs) { tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; cFYI(1, ("DFS disabled (%d)", tcon->Flags)); } } if (rc) goto mount_fail_check; tcon->seal = volume_info->seal; write_lock(&cifs_tcp_ses_lock); list_add(&tcon->tcon_list, &pSesInfo->tcon_list); write_unlock(&cifs_tcp_ses_lock); } /* we can have only one retry value for a connection to a share so for resources mounted more than once to the same server share the last value passed in for the retry flag is used */ tcon->retry = volume_info->retry; tcon->nocase = volume_info->nocase; tcon->local_lease = volume_info->local_lease; } if (pSesInfo) { if (pSesInfo->capabilities & CAP_LARGE_FILES) { sb->s_maxbytes = (u64) 1 << 63; } else sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */ } /* BB FIXME fix time_gran to be larger for LANMAN sessions */ sb->s_time_gran = 100; mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { /* If find_unc succeeded then rc == 0 so we can not end */ /* up accidently freeing someone elses tcon struct */ if (tcon) cifs_put_tcon(tcon); else if (pSesInfo) cifs_put_smb_ses(pSesInfo); else cifs_put_tcp_session(srvTcp); goto out; } cifs_sb->tcon = tcon; /* do not care if following two calls succeed - informational */ if (!tcon->ipc) { CIFSSMBQFSDeviceInfo(xid, tcon); CIFSSMBQFSAttributeInfo(xid, tcon); } /* tell server which Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) /* reset of caps checks mount to see if unix extensions disabled for just this mount */ reset_cifs_unix_caps(xid, tcon, sb, volume_info); else tcon->unix_ext = 0; /* server does not support them */ /* convert forward to back slashes in prepath here if needed */ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { cifs_sb->rsize = 1024 * 127; cFYI(DBG2, ("no very large read support, rsize now 127K")); } if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) cifs_sb->wsize = min(cifs_sb->wsize, (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) cifs_sb->rsize = min(cifs_sb->rsize, (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); /* volume_info->password is freed above when existing session found (in which case it is not needed anymore) but when new sesion is created the password ptr is put in the new session structure (in which case the password will be freed at unmount time) */ out: /* zero out password before freeing */ if (volume_info) { if (volume_info->password != NULL) { memset(volume_info->password, 0, strlen(volume_info->password)); kfree(volume_info->password); } kfree(volume_info->UNC); kfree(volume_info->prepath); kfree(volume_info); } FreeXid(xid); return rc; } static int CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, char session_key[CIFS_SESS_KEY_SIZE], const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; struct smb_hdr *smb_buffer_response; SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; char *user; char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; int len; __u32 capabilities; __u16 count; cFYI(1, ("In sesssetup")); if (ses == NULL) return -EINVAL; user = ses->userName; domain = ses->domainName; smb_buffer = cifs_buf_get(); if (smb_buffer == NULL) return -ENOMEM; smb_buffer_response = smb_buffer; pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer; /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 13 /* wct */ ); smb_buffer->Mid = GetNextMid(ses->server); pSMB->req_no_secext.AndXCommand = 0xFF; pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq); if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; capabilities |= CAP_UNICODE; } if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; capabilities |= CAP_STATUS32; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; capabilities |= CAP_DFS; } pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.CaseInsensitivePasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); bcc_ptr = pByteArea(smb_buffer); memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */ *bcc_ptr = 0; bcc_ptr++; } if (user == NULL) bytes_returned = 0; /* skip null user */ else bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, user, 100, nls_codepage); /* convert number of 16 bit words to bytes */ bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; /* trailing null */ if (domain == NULL) bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, "CIFS_LINUX_DOM", 32, nls_codepage); else bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; } else { if (user != NULL) { strncpy(bcc_ptr, user, 200); bcc_ptr += strnlen(user, 200); } *bcc_ptr = 0; bcc_ptr++; if (domain == NULL) { strcpy(bcc_ptr, "CIFS_LINUX_DOM"); bcc_ptr += strlen("CIFS_LINUX_DOM") + 1; } else { strncpy(bcc_ptr, domain, 64); bcc_ptr += strnlen(domain, 64); *bcc_ptr = 0; bcc_ptr++; } strcpy(bcc_ptr, "Linux version "); bcc_ptr += strlen("Linux version "); strcpy(bcc_ptr, utsname()->release); bcc_ptr += strlen(utsname()->release) + 1; strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; } count = (long) bcc_ptr - (long) pByteArea(smb_buffer); smb_buffer->smb_buf_length += count; pSMB->req_no_secext.ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &bytes_returned, CIFS_LONG_OP); if (rc) { /* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */ } else if ((smb_buffer_response->WordCount == 3) || (smb_buffer_response->WordCount == 4)) { __u16 action = le16_to_cpu(pSMBr->resp.Action); __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */ ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (little endian) */ cFYI(1, ("UID = %d ", ses->Suid)); /* response can have either 3 or 4 word count - Samba sends 3 */ bcc_ptr = pByteArea(smb_buffer_response); if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) && (blob_len < pSMBr->resp.ByteCount))) { if (pSMBr->resp.hdr.WordCount == 4) bcc_ptr += blob_len; if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = (BCC(smb_buffer_response) - 1) / 2; /* Unicode strings must be word aligned */ bcc_ptr++; } else { remaining_words = BCC(smb_buffer_response) / 2; } len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words - 1); /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); if (ses->serverOS == NULL) goto sesssetup_nomem; cifs_strfromUCS_le(ses->serverOS, (__le16 *)bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); remaining_words -= len + 1; ses->serverOS[2 * len] = 0; ses->serverOS[1 + (2 * len)] = 0; if (remaining_words > 0) { len = UniStrnlen((wchar_t *)bcc_ptr, remaining_words-1); kfree(ses->serverNOS); ses->serverNOS = kzalloc(2 * (len + 1), GFP_KERNEL); if (ses->serverNOS == NULL) goto sesssetup_nomem; cifs_strfromUCS_le(ses->serverNOS, (__le16 *)bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverNOS[2 * len] = 0; ses->serverNOS[1 + (2 * len)] = 0; if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) { cFYI(1, ("NT4 server")); ses->flags |= CIFS_SES_NT4; } remaining_words -= len + 1; if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); /* last string is not always null terminated (for e.g. for Windows XP & 2000) */ if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2*(len+1), GFP_KERNEL); if (ses->serverDomain == NULL) goto sesssetup_nomem; cifs_strfromUCS_le(ses->serverDomain, (__le16 *)bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverDomain[2*len] = 0; ses->serverDomain[1+(2*len)] = 0; } else { /* else no more room so create dummy domain string */ if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2, GFP_KERNEL); } } else { /* no room so create dummy domain and NOS string */ /* if these kcallocs fail not much we can do, but better to not fail the sesssetup itself */ kfree(ses->serverDomain); ses->serverDomain = kzalloc(2, GFP_KERNEL); kfree(ses->serverNOS); ses->serverNOS = kzalloc(2, GFP_KERNEL); } } else { /* ASCII */ len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, GFP_KERNEL); if (ses->serverOS == NULL) goto sesssetup_nomem; strncpy(ses->serverOS, bcc_ptr, len); bcc_ptr += len; /* null terminate the string */ bcc_ptr[0] = 0; bcc_ptr++; len = strnlen(bcc_ptr, 1024); kfree(ses->serverNOS); ses->serverNOS = kzalloc(len + 1, GFP_KERNEL); if (ses->serverNOS == NULL) goto sesssetup_nomem; strncpy(ses->serverNOS, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; len = strnlen(bcc_ptr, 1024); if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(len + 1, GFP_KERNEL); if (ses->serverDomain == NULL) goto sesssetup_nomem; strncpy(ses->serverDomain, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; } else cFYI(1, ("Variable field of length %d " "extends beyond end of smb ", len)); } } else { cERROR(1, ("Security Blob Length extends beyond " "end of SMB")); } } else { cERROR(1, ("Invalid Word count %d: ", smb_buffer_response->WordCount)); rc = -EIO; } sesssetup_nomem: /* do not return an error on nomem for the info strings, since that could make reconnection harder, and reconnection might be needed to free memory */ cifs_buf_release(smb_buffer); return rc; } static int CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, struct cifsSesInfo *ses, bool *pNTLMv2_flag, const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; struct smb_hdr *smb_buffer_response; SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; int len; int SecurityBlobLength = sizeof(NEGOTIATE_MESSAGE); PNEGOTIATE_MESSAGE SecurityBlob; PCHALLENGE_MESSAGE SecurityBlob2; __u32 negotiate_flags, capabilities; __u16 count; cFYI(1, ("In NTLMSSP sesssetup (negotiate)")); if (ses == NULL) return -EINVAL; domain = ses->domainName; *pNTLMv2_flag = false; smb_buffer = cifs_buf_get(); if (smb_buffer == NULL) { return -ENOMEM; } smb_buffer_response = smb_buffer; pSMB = (SESSION_SETUP_ANDX *) smb_buffer; pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response; /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); pSMB->req.AndXCommand = 0xFF; pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_EXTENDED_SECURITY; if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; capabilities |= CAP_UNICODE; } if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; capabilities |= CAP_STATUS32; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; capabilities |= CAP_DFS; } pSMB->req.Capabilities = cpu_to_le32(capabilities); bcc_ptr = (char *) &pSMB->req.SecurityBlob; SecurityBlob = (PNEGOTIATE_MESSAGE) bcc_ptr; strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8); SecurityBlob->MessageType = NtLmNegotiate; negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_56 | /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128; if (sign_CIFS_PDUs) negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN; /* if (ntlmv2_support) negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/ /* setup pointers to domain name and workstation name */ bcc_ptr += SecurityBlobLength; SecurityBlob->WorkstationName.Buffer = 0; SecurityBlob->WorkstationName.Length = 0; SecurityBlob->WorkstationName.MaximumLength = 0; /* Domain not sent on first Sesssetup in NTLMSSP, instead it is sent along with username on auth request (ie the response to challenge) */ SecurityBlob->DomainName.Buffer = 0; SecurityBlob->DomainName.Length = 0; SecurityBlob->DomainName.MaximumLength = 0; if (ses->capabilities & CAP_UNICODE) { if ((long) bcc_ptr % 2) { *bcc_ptr = 0; bcc_ptr++; } bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; /* null terminate Linux version */ bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; *(bcc_ptr + 1) = 0; *(bcc_ptr + 2) = 0; bcc_ptr += 2; /* null terminate network opsys string */ *(bcc_ptr + 1) = 0; *(bcc_ptr + 2) = 0; bcc_ptr += 2; /* null domain */ } else { /* ASCII */ strcpy(bcc_ptr, "Linux version "); bcc_ptr += strlen("Linux version "); strcpy(bcc_ptr, utsname()->release); bcc_ptr += strlen(utsname()->release) + 1; strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; bcc_ptr++; /* empty domain field */ *bcc_ptr = 0; } SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags); pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength); count = (long) bcc_ptr - (long) pByteArea(smb_buffer); smb_buffer->smb_buf_length += count; pSMB->req.ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &bytes_returned, CIFS_LONG_OP); if (smb_buffer_response->Status.CifsError == cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) rc = 0; if (rc) { /* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */ } else if ((smb_buffer_response->WordCount == 3) || (smb_buffer_response->WordCount == 4)) { __u16 action = le16_to_cpu(pSMBr->resp.Action); __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) cFYI(1, ("Guest login")); /* Do we want to set anything in SesInfo struct when guest login? */ bcc_ptr = pByteArea(smb_buffer_response); /* response can have either 3 or 4 word count - Samba sends 3 */ SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr; if (SecurityBlob2->MessageType != NtLmChallenge) { cFYI(1, ("Unexpected NTLMSSP message type received %d", SecurityBlob2->MessageType)); } else if (ses) { ses->Suid = smb_buffer_response->Uid; /* UID left in le format */ cFYI(1, ("UID = %d", ses->Suid)); if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) && (blob_len < pSMBr->resp.ByteCount))) { if (pSMBr->resp.hdr.WordCount == 4) { bcc_ptr += blob_len; cFYI(1, ("Security Blob Length %d", blob_len)); } cFYI(1, ("NTLMSSP Challenge rcvd")); memcpy(ses->server->cryptKey, SecurityBlob2->Challenge, CIFS_CRYPTO_KEY_SIZE); if (SecurityBlob2->NegotiateFlags & cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2)) *pNTLMv2_flag = true; if ((SecurityBlob2->NegotiateFlags & cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) || (sign_CIFS_PDUs > 1)) ses->server->secMode |= SECMODE_SIGN_REQUIRED; if ((SecurityBlob2->NegotiateFlags & cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs)) ses->server->secMode |= SECMODE_SIGN_ENABLED; if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = (BCC(smb_buffer_response) - 1) / 2; /* Must word align unicode strings */ bcc_ptr++; } else { remaining_words = BCC (smb_buffer_response) / 2; } len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words - 1); /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses->serverOS, (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); remaining_words -= len + 1; ses->serverOS[2 * len] = 0; ses->serverOS[1 + (2 * len)] = 0; if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words - 1); kfree(ses->serverNOS); ses->serverNOS = kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses-> serverNOS, (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverNOS[2 * len] = 0; ses->serverNOS[1 + (2 * len)] = 0; remaining_words -= len + 1; if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); /* last string not always null terminated (for e.g. for Windows XP & 2000) */ kfree(ses->serverDomain); ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le (ses->serverDomain, (__le16 *)bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverDomain[2*len] = 0; ses->serverDomain [1 + (2 * len)] = 0; } /* else no more room so create dummy domain string */ else { kfree(ses->serverDomain); ses->serverDomain = kzalloc(2, GFP_KERNEL); } } else { /* no room so create dummy domain and NOS string */ kfree(ses->serverDomain); ses->serverDomain = kzalloc(2, GFP_KERNEL); kfree(ses->serverNOS); ses->serverNOS = kzalloc(2, GFP_KERNEL); } } else { /* ASCII */ len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverOS, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; /* null terminate string */ bcc_ptr++; len = strnlen(bcc_ptr, 1024); kfree(ses->serverNOS); ses->serverNOS = kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverNOS, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; len = strnlen(bcc_ptr, 1024); kfree(ses->serverDomain); ses->serverDomain = kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverDomain, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; } else cFYI(1, ("field of length %d " "extends beyond end of smb", len)); } } else { cERROR(1, ("Security Blob Length extends beyond" " end of SMB")); } } else { cERROR(1, ("No session structure passed in.")); } } else { cERROR(1, ("Invalid Word count %d:", smb_buffer_response->WordCount)); rc = -EIO; } cifs_buf_release(smb_buffer); return rc; } static int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, char *ntlm_session_key, bool ntlmv2_flag, const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; struct smb_hdr *smb_buffer_response; SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; char *user; char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; int len; int SecurityBlobLength = sizeof(AUTHENTICATE_MESSAGE); PAUTHENTICATE_MESSAGE SecurityBlob; __u32 negotiate_flags, capabilities; __u16 count; cFYI(1, ("In NTLMSSPSessSetup (Authenticate)")); if (ses == NULL) return -EINVAL; user = ses->userName; domain = ses->domainName; smb_buffer = cifs_buf_get(); if (smb_buffer == NULL) { return -ENOMEM; } smb_buffer_response = smb_buffer; pSMB = (SESSION_SETUP_ANDX *)smb_buffer; pSMBr = (SESSION_SETUP_ANDX *)smb_buffer_response; /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.AndXCommand = 0xFF; pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); pSMB->req.hdr.Uid = ses->Suid; if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_EXTENDED_SECURITY; if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; capabilities |= CAP_UNICODE; } if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; capabilities |= CAP_STATUS32; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; capabilities |= CAP_DFS; } pSMB->req.Capabilities = cpu_to_le32(capabilities); bcc_ptr = (char *)&pSMB->req.SecurityBlob; SecurityBlob = (PAUTHENTICATE_MESSAGE)bcc_ptr; strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8); SecurityBlob->MessageType = NtLmAuthenticate; bcc_ptr += SecurityBlobLength; negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO | 0x80000000 | NTLMSSP_NEGOTIATE_128; if (sign_CIFS_PDUs) negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN; if (ntlmv2_flag) negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ SecurityBlob->WorkstationName.Buffer = 0; SecurityBlob->WorkstationName.Length = 0; SecurityBlob->WorkstationName.MaximumLength = 0; SecurityBlob->SessionKey.Length = 0; SecurityBlob->SessionKey.MaximumLength = 0; SecurityBlob->SessionKey.Buffer = 0; SecurityBlob->LmChallengeResponse.Length = 0; SecurityBlob->LmChallengeResponse.MaximumLength = 0; SecurityBlob->LmChallengeResponse.Buffer = 0; SecurityBlob->NtChallengeResponse.Length = cpu_to_le16(CIFS_SESS_KEY_SIZE); SecurityBlob->NtChallengeResponse.MaximumLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE); SecurityBlob->NtChallengeResponse.Buffer = cpu_to_le32(SecurityBlobLength); SecurityBlobLength += CIFS_SESS_KEY_SIZE; bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { if (domain == NULL) { SecurityBlob->DomainName.Buffer = 0; SecurityBlob->DomainName.Length = 0; SecurityBlob->DomainName.MaximumLength = 0; } else { __u16 ln = cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64, nls_codepage); ln *= 2; SecurityBlob->DomainName.MaximumLength = cpu_to_le16(ln); SecurityBlob->DomainName.Buffer = cpu_to_le32(SecurityBlobLength); bcc_ptr += ln; SecurityBlobLength += ln; SecurityBlob->DomainName.Length = cpu_to_le16(ln); } if (user == NULL) { SecurityBlob->UserName.Buffer = 0; SecurityBlob->UserName.Length = 0; SecurityBlob->UserName.MaximumLength = 0; } else { __u16 ln = cifs_strtoUCS((__le16 *) bcc_ptr, user, 64, nls_codepage); ln *= 2; SecurityBlob->UserName.MaximumLength = cpu_to_le16(ln); SecurityBlob->UserName.Buffer = cpu_to_le32(SecurityBlobLength); bcc_ptr += ln; SecurityBlobLength += ln; SecurityBlob->UserName.Length = cpu_to_le16(ln); } /* SecurityBlob->WorkstationName.Length = cifs_strtoUCS((__le16 *) bcc_ptr, "AMACHINE",64, nls_codepage); SecurityBlob->WorkstationName.Length *= 2; SecurityBlob->WorkstationName.MaximumLength = cpu_to_le16(SecurityBlob->WorkstationName.Length); SecurityBlob->WorkstationName.Buffer = cpu_to_le32(SecurityBlobLength); bcc_ptr += SecurityBlob->WorkstationName.Length; SecurityBlobLength += SecurityBlob->WorkstationName.Length; SecurityBlob->WorkstationName.Length = cpu_to_le16(SecurityBlob->WorkstationName.Length); */ if ((long) bcc_ptr % 2) { *bcc_ptr = 0; bcc_ptr++; } bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; /* null term version string */ bytes_returned = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; *(bcc_ptr + 1) = 0; *(bcc_ptr + 2) = 0; bcc_ptr += 2; /* null terminate network opsys string */ *(bcc_ptr + 1) = 0; *(bcc_ptr + 2) = 0; bcc_ptr += 2; /* null domain */ } else { /* ASCII */ if (domain == NULL) { SecurityBlob->DomainName.Buffer = 0; SecurityBlob->DomainName.Length = 0; SecurityBlob->DomainName.MaximumLength = 0; } else { __u16 ln; negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED; strncpy(bcc_ptr, domain, 63); ln = strnlen(domain, 64); SecurityBlob->DomainName.MaximumLength = cpu_to_le16(ln); SecurityBlob->DomainName.Buffer = cpu_to_le32(SecurityBlobLength); bcc_ptr += ln; SecurityBlobLength += ln; SecurityBlob->DomainName.Length = cpu_to_le16(ln); } if (user == NULL) { SecurityBlob->UserName.Buffer = 0; SecurityBlob->UserName.Length = 0; SecurityBlob->UserName.MaximumLength = 0; } else { __u16 ln; strncpy(bcc_ptr, user, 63); ln = strnlen(user, 64); SecurityBlob->UserName.MaximumLength = cpu_to_le16(ln); SecurityBlob->UserName.Buffer = cpu_to_le32(SecurityBlobLength); bcc_ptr += ln; SecurityBlobLength += ln; SecurityBlob->UserName.Length = cpu_to_le16(ln); } /* BB fill in our workstation name if known BB */ strcpy(bcc_ptr, "Linux version "); bcc_ptr += strlen("Linux version "); strcpy(bcc_ptr, utsname()->release); bcc_ptr += strlen(utsname()->release) + 1; strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; bcc_ptr++; /* null domain */ *bcc_ptr = 0; } SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags); pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength); count = (long) bcc_ptr - (long) pByteArea(smb_buffer); smb_buffer->smb_buf_length += count; pSMB->req.ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &bytes_returned, CIFS_LONG_OP); if (rc) { /* rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */ } else if ((smb_buffer_response->WordCount == 3) || (smb_buffer_response->WordCount == 4)) { __u16 action = le16_to_cpu(pSMBr->resp.Action); __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) cFYI(1, ("Guest login")); /* BB Should we set anything in SesInfo struct ? */ /* if (SecurityBlob2->MessageType != NtLm??) { cFYI("Unexpected message type on auth response is %d")); } */ if (ses) { cFYI(1, ("Check challenge UID %d vs auth response UID %d", ses->Suid, smb_buffer_response->Uid)); /* UID left in wire format */ ses->Suid = smb_buffer_response->Uid; bcc_ptr = pByteArea(smb_buffer_response); /* response can have either 3 or 4 word count - Samba sends 3 */ if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) && (blob_len < pSMBr->resp.ByteCount))) { if (pSMBr->resp.hdr.WordCount == 4) { bcc_ptr += blob_len; cFYI(1, ("Security Blob Length %d ", blob_len)); } cFYI(1, ("NTLMSSP response to Authenticate ")); if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = (BCC(smb_buffer_response) - 1) / 2; bcc_ptr++; /* Unicode strings must be word aligned */ } else { remaining_words = BCC(smb_buffer_response) / 2; } len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words - 1); /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses->serverOS, (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); remaining_words -= len + 1; ses->serverOS[2 * len] = 0; ses->serverOS[1 + (2 * len)] = 0; if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words - 1); kfree(ses->serverNOS); ses->serverNOS = kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses-> serverNOS, (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverNOS[2 * len] = 0; ses->serverNOS[1+(2*len)] = 0; remaining_words -= len + 1; if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); /* last string not always null terminated (e.g. for Windows XP & 2000) */ if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le (ses-> serverDomain, (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); ses-> serverDomain[2 * len] = 0; ses-> serverDomain[1 + (2 * len)] = 0; } /* else no more room so create dummy domain string */ else { if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2,GFP_KERNEL); } } else { /* no room so create dummy domain and NOS string */ if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2, GFP_KERNEL); kfree(ses->serverNOS); ses->serverNOS = kzalloc(2, GFP_KERNEL); } } else { /* ASCII */ len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverOS,bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; /* null terminate the string */ bcc_ptr++; len = strnlen(bcc_ptr, 1024); kfree(ses->serverNOS); ses->serverNOS = kzalloc(len+1, GFP_KERNEL); strncpy(ses->serverNOS, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; len = strnlen(bcc_ptr, 1024); if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(len+1, GFP_KERNEL); strncpy(ses->serverDomain, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; } else cFYI(1, ("field of length %d " "extends beyond end of smb ", len)); } } else { cERROR(1, ("Security Blob extends beyond end " "of SMB")); } } else { cERROR(1, ("No session structure passed in.")); } } else { cERROR(1, ("Invalid Word count %d: ", smb_buffer_response->WordCount)); rc = -EIO; } cifs_buf_release(smb_buffer); return rc; } int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, const char *tree, struct cifsTconInfo *tcon, const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; struct smb_hdr *smb_buffer_response; TCONX_REQ *pSMB; TCONX_RSP *pSMBr; unsigned char *bcc_ptr; int rc = 0; int length; __u16 count; if (ses == NULL) return -EIO; smb_buffer = cifs_buf_get(); if (smb_buffer == NULL) { return -ENOMEM; } smb_buffer_response = smb_buffer; header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, NULL /*no tid */ , 4 /*wct */ ); smb_buffer->Mid = GetNextMid(ses->server); smb_buffer->Uid = ses->Suid; pSMB = (TCONX_REQ *) smb_buffer; pSMBr = (TCONX_RSP *) smb_buffer_response; pSMB->AndXCommand = 0xFF; pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); bcc_ptr = &pSMB->Password[0]; if ((ses->server->secMode) & SECMODE_USER) { pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ *bcc_ptr = 0; /* password is null byte */ bcc_ptr++; /* skip password */ /* already aligned so no need to do it below */ } else { pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); /* BB FIXME add code to fail this if NTLMv2 or Kerberos specified as required (when that support is added to the vfs in the future) as only NTLM or the much weaker LANMAN (which we do not send by default) is accepted by Samba (not sure whether other servers allow NTLMv2 password here) */ #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((extended_security & CIFSSEC_MAY_LANMAN) && (ses->server->secType == LANMAN)) calc_lanman_hash(tcon->password, ses->server->cryptKey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, bcc_ptr); else #endif /* CIFS_WEAK_PW_HASH */ SMBNTencrypt(tcon->password, ses->server->cryptKey, bcc_ptr); bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { /* must align unicode strings */ *bcc_ptr = 0; /* null byte password */ bcc_ptr++; } } if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; } if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; length = cifs_strtoUCS((__le16 *) bcc_ptr, tree, 6 /* max utf8 char length in bytes */ * (/* server len*/ + 256 /* share len */), nls_codepage); bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ bcc_ptr += 2; /* skip trailing null */ } else { /* ASCII */ strcpy(bcc_ptr, tree); bcc_ptr += strlen(tree) + 1; } strcpy(bcc_ptr, "?????"); bcc_ptr += strlen("?????"); bcc_ptr += 1; count = bcc_ptr - &pSMB->Password[0]; pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, CIFS_STD_OP); /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */ /* above now done in SendReceive */ if ((rc == 0) && (tcon != NULL)) { tcon->tidStatus = CifsGood; tcon->need_reconnect = false; tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); /* skip service field (NB: this field is always ASCII) */ if (length == 3) { if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && (bcc_ptr[2] == 'C')) { cFYI(1, ("IPC connection")); tcon->ipc = 1; } } else if (length == 2) { if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { /* the most common case */ cFYI(1, ("disk share connection")); } } bcc_ptr += length + 1; strncpy(tcon->treeName, tree, MAX_TREE_SIZE); if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { length = UniStrnlen((wchar_t *) bcc_ptr, 512); if ((bcc_ptr + (2 * length)) - pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { kfree(tcon->nativeFileSystem); tcon->nativeFileSystem = kzalloc(length + 2, GFP_KERNEL); if (tcon->nativeFileSystem) cifs_strfromUCS_le( tcon->nativeFileSystem, (__le16 *) bcc_ptr, length, nls_codepage); bcc_ptr += 2 * length; bcc_ptr[0] = 0; /* null terminate the string */ bcc_ptr[1] = 0; bcc_ptr += 2; } /* else do not bother copying these information fields*/ } else { length = strnlen(bcc_ptr, 1024); if ((bcc_ptr + length) - pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { kfree(tcon->nativeFileSystem); tcon->nativeFileSystem = kzalloc(length + 1, GFP_KERNEL); if (tcon->nativeFileSystem) strncpy(tcon->nativeFileSystem, bcc_ptr, length); } /* else do not bother copying these information fields*/ } if ((smb_buffer_response->WordCount == 3) || (smb_buffer_response->WordCount == 7)) /* field is in same location */ tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); else tcon->Flags = 0; cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags)); } else if ((rc == 0) && tcon == NULL) { /* all we need to save for IPC$ connection */ ses->ipc_tid = smb_buffer_response->Tid; } cifs_buf_release(smb_buffer); return rc; } int cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) { int rc = 0; char *tmp; if (cifs_sb->tcon) cifs_put_tcon(cifs_sb->tcon); cifs_sb->tcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; kfree(tmp); return rc; } int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table *nls_info) { int rc = 0; char ntlm_session_key[CIFS_SESS_KEY_SIZE]; bool ntlmv2_flag = false; int first_time = 0; struct TCP_Server_Info *server = pSesInfo->server; /* what if server changes its buffer size after dropping the session? */ if (server->maxBuf == 0) /* no need to send on reconnect */ { rc = CIFSSMBNegotiate(xid, pSesInfo); if (rc == -EAGAIN) { /* retry only once on 1st time connection */ rc = CIFSSMBNegotiate(xid, pSesInfo); if (rc == -EAGAIN) rc = -EHOSTDOWN; } if (rc == 0) { spin_lock(&GlobalMid_Lock); if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsGood; else rc = -EHOSTDOWN; spin_unlock(&GlobalMid_Lock); } first_time = 1; } if (rc) goto ss_err_exit; pSesInfo->flags = 0; pSesInfo->capabilities = server->capabilities; if (linuxExtEnabled == 0) pSesInfo->capabilities &= (~CAP_UNIX); /* pSesInfo->sequence_number = 0;*/ cFYI(1, ("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", server->secMode, server->capabilities, server->timeAdj)); if (experimEnabled < 2) rc = CIFS_SessSetup(xid, pSesInfo, first_time, nls_info); else if (extended_security && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) && (server->secType == NTLMSSP)) { rc = -EOPNOTSUPP; } else if (extended_security && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) && (server->secType == RawNTLMSSP)) { cFYI(1, ("NTLMSSP sesssetup")); rc = CIFSNTLMSSPNegotiateSessSetup(xid, pSesInfo, &ntlmv2_flag, nls_info); if (!rc) { if (ntlmv2_flag) { char *v2_response; cFYI(1, ("more secure NTLM ver2 hash")); if (CalcNTLMv2_partial_mac_key(pSesInfo, nls_info)) { rc = -ENOMEM; goto ss_err_exit; } else v2_response = kmalloc(16 + 64 /* blob*/, GFP_KERNEL); if (v2_response) { CalcNTLMv2_response(pSesInfo, v2_response); /* if (first_time) cifs_calculate_ntlmv2_mac_key */ kfree(v2_response); /* BB Put dummy sig in SessSetup PDU? */ } else { rc = -ENOMEM; goto ss_err_exit; } } else { SMBNTencrypt(pSesInfo->password, server->cryptKey, ntlm_session_key); if (first_time) cifs_calculate_mac_key( &server->mac_signing_key, ntlm_session_key, pSesInfo->password); } /* for better security the weaker lanman hash not sent in AuthSessSetup so we no longer calculate it */ rc = CIFSNTLMSSPAuthSessSetup(xid, pSesInfo, ntlm_session_key, ntlmv2_flag, nls_info); } } else { /* old style NTLM 0.12 session setup */ SMBNTencrypt(pSesInfo->password, server->cryptKey, ntlm_session_key); if (first_time) cifs_calculate_mac_key(&server->mac_signing_key, ntlm_session_key, pSesInfo->password); rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info); } if (rc) { cERROR(1, ("Send error in SessSetup = %d", rc)); } else { cFYI(1, ("CIFS Session Established successfully")); spin_lock(&GlobalMid_Lock); pSesInfo->status = CifsGood; pSesInfo->need_reconnect = false; spin_unlock(&GlobalMid_Lock); } ss_err_exit: return rc; } cifs-test-base/dir.c0000644000175000017500000004205411117756171014177 0ustar stevefstevef/* * fs/cifs/dir.c * * vfs operations that deal with dentries * * 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 #include #include #include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" #include "cifs_fs_sb.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)); } /* Note: caller must free return buffer */ char * build_path_from_dentry(struct dentry *direntry) { struct dentry *temp; int namelen; int pplen; int dfsplen; char *full_path; char dirsep; struct cifs_sb_info *cifs_sb; if (direntry == NULL) return NULL; /* not much we can do if dentry is freed and we need to reopen the file after it was closed implicitly when the server crashed */ cifs_sb = CIFS_SB(direntry->d_sb); dirsep = CIFS_DIR_SEP(cifs_sb); pplen = cifs_sb->prepathlen; if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); else dfsplen = 0; cifs_bp_rename_retry: namelen = pplen + dfsplen; for (temp = direntry; !IS_ROOT(temp);) { namelen += (1 + temp->d_name.len); temp = temp->d_parent; if (temp == NULL) { cERROR(1, ("corrupt dentry")); return NULL; } } full_path = kmalloc(namelen+1, GFP_KERNEL); if (full_path == NULL) return full_path; full_path[namelen] = 0; /* trailing null */ for (temp = direntry; !IS_ROOT(temp);) { namelen -= 1 + temp->d_name.len; if (namelen < 0) { break; } else { full_path[namelen] = dirsep; strncpy(full_path + namelen + 1, temp->d_name.name, temp->d_name.len); cFYI(0, ("name: %s", full_path + namelen)); } temp = temp->d_parent; if (temp == NULL) { cERROR(1, ("corrupt dentry")); kfree(full_path); return NULL; } } if (namelen != pplen + dfsplen) { cERROR(1, ("did not end path lookup where expected namelen is %d", namelen)); /* 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, cifs_sb->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] = '/'; } } } strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen); return full_path; } /* Inode operations in similar order to how they appear in Linux file fs.h */ int cifs_create(struct inode *inode, struct dentry *direntry, int mode, struct nameidata *nd) { int rc = -ENOENT; int xid; int create_options = CREATE_NOT_DIR; int oplock = 0; int desiredAccess = GENERIC_READ | GENERIC_WRITE; __u16 fileHandle; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; FILE_ALL_INFO *buf = NULL; struct inode *newinode = NULL; struct cifsFileInfo *pCifsFile = NULL; struct cifsInodeInfo *pCifsInode; int disposition = FILE_OVERWRITE_IF; bool write_only = false; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } if (nd && (nd->flags & LOOKUP_OPEN)) { int oflags = nd->intent.open.flags; desiredAccess = 0; if (oflags & FMODE_READ) desiredAccess |= GENERIC_READ; if (oflags & FMODE_WRITE) { desiredAccess |= GENERIC_WRITE; if (!(oflags & FMODE_READ)) write_only = true; } 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 cFYI(1, ("Create flag not set in create function")); } /* BB add processing to set equivalent of mode - e.g. via CreateX with ACLs */ if (oplockEnabled) oplock = REQ_OPLOCK; buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (buf == NULL) { kfree(full_path); FreeXid(xid); return -ENOMEM; } mode &= ~current->fs->umask; /* * if we're not using unix extensions, see if we need to set * ATTR_READONLY on the create call */ if (!pTcon->unix_ext && (mode & S_IWUGO) == 0) create_options |= CREATE_OPTION_READONLY; if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, create_options, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else rc = -EIO; /* no NT SMB support fall into legacy open below */ if (rc == -EIO) { /* old server, retry the open legacy style */ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, desiredAccess, create_options, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } if (rc) { cFYI(1, ("cifs_create returned 0x%x", rc)); } else { /* If Open reported that we actually created a file then we now have to set the mode if possible */ if ((pTcon->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 = (__u64) current->fsuid; if (inode->i_mode & S_ISGID) args.gid = (__u64) inode->i_gid; else args.gid = (__u64) current->fsgid; } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; } CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } else { /* BB implement mode setting via Windows security descriptors e.g. */ /* CIFSSMBWinSetPerms(xid,pTcon,path,mode,-1,-1,nls);*/ /* Could set r/o dos attribute if mode & 0222 == 0 */ } /* server might mask mode so we have to query for it */ 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, buf, inode->i_sb, xid, &fileHandle); if (newinode) { 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; } } } if (rc != 0) { cFYI(1, ("Create worked but get_inode_info failed rc = %d", rc)); } else { if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } if ((nd == NULL /* nfsd case - nfs srv does not set nd */) || (!(nd->flags & LOOKUP_OPEN))) { /* mknod case - do not leave file open */ CIFSSMBClose(xid, pTcon, fileHandle); } else if (newinode) { pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); if (pCifsFile == NULL) goto cifs_create_out; pCifsFile->netfid = fileHandle; pCifsFile->pid = current->tgid; pCifsFile->pInode = newinode; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; init_MUTEX(&pCifsFile->fh_sem); mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); atomic_set(&pCifsFile->wrtPending, 0); /* set the following in open now pCifsFile->pfile = file; */ write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist, &pTcon->openFileList); pCifsInode = CIFS_I(newinode); if (pCifsInode) { /* if readable file instance put first in list*/ if (write_only) { list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); } else { list_add(&pCifsFile->flist, &pCifsInode->openFileList); } if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; cFYI(1, ("Exclusive Oplock inode %p", newinode)); } else if ((oplock & 0xF) == OPLOCK_READ) pCifsInode->clientCanCacheRead = true; } write_unlock(&GlobalSMBSeslock); } } cifs_create_out: kfree(buf); kfree(full_path); FreeXid(xid); return rc; } int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, dev_t device_number) { int rc = -EPERM; int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; if (!old_valid_dev(device_number)) return -EINVAL; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) rc = -ENOMEM; else if (pTcon->unix_ext) { struct cifs_unix_set_info_args args = { .mode = mode & ~current->fs->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 = (__u64) current->fsuid; args.gid = (__u64) current->fsgid; } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; } rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (!rc) { rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; if (rc == 0) d_instantiate(direntry, newinode); } } else { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { int oplock = 0; u16 fileHandle; FILE_ALL_INFO *buf; cFYI(1, ("sfu compat create special file")); buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (buf == NULL) { kfree(full_path); FreeXid(xid); return -ENOMEM; } rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, /* fail if exists */ GENERIC_WRITE /* BB would WRITE_OWNER | WRITE_DAC be better? */, /* Create a file and set the file attribute to SYSTEM */ CREATE_NOT_DIR | CREATE_OPTION_SPECIAL, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* BB FIXME - add handling for backlevel servers which need legacy open and check for all calls to SMBOpen for fallback to SMBLeagcyOpen */ if (!rc) { /* BB Do not bother to decode buf since no local inode yet to put timestamps in, but we can reuse it safely */ unsigned int bytes_written; struct win_dev *pdev; pdev = (struct win_dev *)buf; 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, pTcon, fileHandle, sizeof(struct win_dev), 0, &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, pTcon, fileHandle, sizeof(struct win_dev), 0, &bytes_written, (char *)pdev, NULL, 0); } /* else if(S_ISFIFO */ CIFSSMBClose(xid, pTcon, fileHandle); d_drop(direntry); } kfree(buf); /* add code here to set EAs */ } } kfree(full_path); FreeXid(xid); return rc; } struct dentry * cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct nameidata *nd) { int xid; int rc = 0; /* to get around spurious gcc warning, set to zero here */ struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct inode *newInode = NULL; char *full_path = NULL; xid = GetXid(); cFYI(1, ("parent inode = 0x%p name is: %s and dentry = 0x%p", parent_dir_inode, direntry->d_name.name, direntry)); /* check whether path exists */ cifs_sb = CIFS_SB(parent_dir_inode->i_sb); pTcon = cifs_sb->tcon; /* * Don't allow the separator character in a path component. * The VFS will not allow "/", but "\" is allowed by posix. */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { int i; for (i = 0; i < direntry->d_name.len; i++) if (direntry->d_name.name[i] == '\\') { cFYI(1, ("Invalid file name")); FreeXid(xid); return ERR_PTR(-EINVAL); } } /* 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) { FreeXid(xid); return ERR_PTR(-ENOMEM); } if (direntry->d_inode != NULL) { cFYI(1, ("non-NULL inode in lookup")); } else { cFYI(1, ("NULL inode in lookup")); } cFYI(1, ("Full path: %s inode = 0x%p", 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)) { if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; 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; if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; 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) { cERROR(1, ("Unexpected lookup error %d", rc)); /* We special case check for Access Denied - since that is a common return code */ } kfree(full_path); FreeXid(xid); return ERR_PTR(rc); } static int cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) { int isValid = 1; if (direntry->d_inode) { if (cifs_revalidate(direntry)) return 0; } else { cFYI(1, ("neg dentry 0x%p name = %s", direntry, direntry->d_name.name)); if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled) { d_drop(direntry); isValid = 0; } } return isValid; } /* static int cifs_d_delete(struct dentry *direntry) { int rc = 0; cFYI(1, ("In cifs d_delete, name = %s", direntry->d_name.name)); return rc; } */ struct dentry_operations cifs_dentry_ops = { .d_revalidate = cifs_d_revalidate, /* d_delete: cifs_d_delete, */ /* not needed except for debugging */ }; static int cifs_ci_hash(struct dentry *dentry, struct qstr *q) { struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; unsigned long hash; int i; hash = init_name_hash(); for (i = 0; i < q->len; i++) hash = partial_name_hash(nls_tolower(codepage, q->name[i]), hash); q->hash = end_name_hash(hash); return 0; } static int cifs_ci_compare(struct dentry *dentry, struct qstr *a, struct qstr *b) { struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; if ((a->len == b->len) && (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) { /* * To preserve case, don't let an existing negative dentry's * case take precedence. If a is not a negative dentry, this * should have no side effects */ memcpy((void *)a->name, b->name, a->len); return 0; } return 1; } struct dentry_operations cifs_ci_dentry_ops = { .d_revalidate = cifs_d_revalidate, .d_hash = cifs_ci_hash, .d_compare = cifs_ci_compare, }; cifs-test-base/dns_resolve.c0000644000175000017500000000766111117756171015751 0ustar stevefstevef/* * fs/cifs/dns_resolve.c * * Copyright (c) 2007 Igor Mammedov * Author(s): Igor Mammedov (niallain@gmail.com) * Steve French (sfrench@us.ibm.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 #include "dns_resolve.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" /* Checks if supplied name is IP address * returns: * 1 - name is IP * 0 - name is not IP */ static int is_ip(const char *name) { int rc; struct sockaddr_in sin_server; struct sockaddr_in6 sin_server6; rc = cifs_inet_pton(AF_INET, name, &sin_server.sin_addr.s_addr); if (rc <= 0) { /* not ipv4 address, try ipv6 */ rc = cifs_inet_pton(AF_INET6, name, &sin_server6.sin6_addr.in6_u); if (rc > 0) return 1; } else { return 1; } /* we failed translating address */ return 0; } static int dns_resolver_instantiate(struct key *key, const void *data, size_t datalen) { int rc = 0; char *ip; ip = kmalloc(datalen + 1, GFP_KERNEL); if (!ip) return -ENOMEM; memcpy(ip, data, datalen); ip[datalen] = '\0'; /* make sure this looks like an address */ if (!is_ip((const char *) ip)) { kfree(ip); return -EINVAL; } key->type_data.x[0] = datalen; rcu_assign_pointer(key->payload.data, ip); return rc; } static void dns_resolver_destroy(struct key *key) { kfree(key->payload.data); } struct key_type key_type_dns_resolver = { .name = "dns_resolver", .def_datalen = sizeof(struct in_addr), .describe = user_describe, .instantiate = dns_resolver_instantiate, .destroy = dns_resolver_destroy, .match = user_match, }; /* Resolves server name to ip address. * input: * unc - server UNC * output: * *ip_addr - pointer to server ip, caller responcible for freeing it. * return 0 on success */ int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) { int rc = -EAGAIN; struct key *rkey = ERR_PTR(-EAGAIN); char *name; char *data = NULL; int len; if (!ip_addr || !unc) return -EINVAL; /* search for server name delimiter */ len = strlen(unc); if (len < 3) { cFYI(1, ("%s: unc is too short: %s", __func__, unc)); return -EINVAL; } len -= 2; name = memchr(unc+2, '\\', len); if (!name) { cFYI(1, ("%s: probably server name is whole unc: %s", __func__, unc)); } else { len = (name - unc) - 2/* leading // */; } name = kmalloc(len+1, GFP_KERNEL); if (!name) { rc = -ENOMEM; return rc; } memcpy(name, unc+2, len); name[len] = 0; if (is_ip(name)) { cFYI(1, ("%s: it is IP, skipping dns upcall: %s", __func__, name)); data = name; goto skip_upcall; } rkey = request_key(&key_type_dns_resolver, name, ""); if (!IS_ERR(rkey)) { len = rkey->type_data.x[0]; data = rkey->payload.data; } else { cERROR(1, ("%s: unable to resolve: %s", __func__, name)); goto out; } skip_upcall: if (data) { *ip_addr = kmalloc(len + 1, GFP_KERNEL); if (*ip_addr) { memcpy(*ip_addr, data, len + 1); if (!IS_ERR(rkey)) cFYI(1, ("%s: resolved: %s to %s", __func__, name, *ip_addr )); rc = 0; } else { rc = -ENOMEM; } if (!IS_ERR(rkey)) key_put(rkey); } out: kfree(name); return rc; } cifs-test-base/dns_resolve.h0000644000175000017500000000241611117756171015747 0ustar stevefstevef/* * 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__ #include extern struct key_type key_type_dns_resolver; extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); #endif /* KERNEL */ #endif /* _DNS_RESOLVE_H */ cifs-test-base/export.c0000644000175000017500000000454511117756171014745 0ustar stevefstevef/* * 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/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 #include #include "cifsglob.h" #include "cifs_debug.h" #include "cifsfs.h" #ifdef CONFIG_CIFS_EXPERIMENTAL static struct dentry *cifs_get_parent(struct dentry *dentry) { /* BB need to add code here eventually to enable export via NFSD */ cFYI(1, ("get parent for %p", 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 /* EXPERIMENTAL */ cifs-test-base/fcntl.c0000644000175000017500000000721211117756171014524 0ustar stevefstevef/* * fs/cifs/fcntl.c * * vfs operations that deal with the file control API * * Copyright (C) International Business Machines Corp., 2003,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 */ #include #include #include #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "cifsfs.h" static __u32 convert_to_cifs_notify_flags(unsigned long fcntl_notify_flags) { __u32 cifs_ntfy_flags = 0; /* No way on Linux VFS to ask to monitor xattr changes (and no stream support either */ if (fcntl_notify_flags & DN_ACCESS) cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS; if (fcntl_notify_flags & DN_MODIFY) { /* What does this mean on directories? */ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE; } if (fcntl_notify_flags & DN_CREATE) { cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE; } if (fcntl_notify_flags & DN_DELETE) cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE; if (fcntl_notify_flags & DN_RENAME) { /* BB review this - checking various server behaviors */ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME; } if (fcntl_notify_flags & DN_ATTRIB) { cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_ATTRIBUTES; } /* if (fcntl_notify_flags & DN_MULTISHOT) { cifs_ntfy_flags |= ; } */ /* BB fixme - not sure how to handle this with CIFS yet */ return cifs_ntfy_flags; } int cifs_dir_notify(struct file *file, unsigned long arg) { int xid; int rc = -EINVAL; int oplock = 0; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES; __u16 netfid; if (experimEnabled == 0) return 0; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(file->f_path.dentry); if (full_path == NULL) { rc = -ENOMEM; } else { cFYI(1, ("dir notify on file %s Arg 0x%lx", full_path, arg)); rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ | SYNCHRONIZE, 0 /* create options */, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* BB fixme - add this handle to a notify handle list */ if (rc) { cFYI(1, ("Could not open directory for notify")); } else { filter = convert_to_cifs_notify_flags(arg); if (filter != 0) { rc = CIFSSMBNotify(xid, pTcon, 0 /* no subdirs */, netfid, filter, file, arg & DN_MULTISHOT, cifs_sb->local_nls); } else { rc = -EINVAL; } /* BB add code to close file eventually (at unmount it would close automatically but may be a way to do it easily when inode freed or when notify info is cleared/changed */ cFYI(1, ("notify rc %d", rc)); } } FreeXid(xid); return rc; } cifs-test-base/file.c0000644000175000017500000016505511120007731014330 0ustar stevefstevef/* * fs/cifs/file.c * * vfs operations that deal with files * * Copyright (C) International Business Machines Corp., 2002,2007 * 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 #include #include #include #include #include #include #include #include #include #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" static inline struct cifsFileInfo *cifs_init_private( struct cifsFileInfo *private_data, struct inode *inode, struct file *file, __u16 netfid) { memset(private_data, 0, sizeof(struct cifsFileInfo)); private_data->netfid = netfid; private_data->pid = current->tgid; init_MUTEX(&private_data->fh_sem); mutex_init(&private_data->lock_mutex); INIT_LIST_HEAD(&private_data->llist); private_data->pfile = file; /* needed for writepage */ private_data->pInode = inode; private_data->invalidHandle = false; private_data->closePend = false; /* we have to track num writers to the inode, since writepages does not tell us which handle the write is for so there can be a close (overlapping with write) of the filehandle that cifs_writepages chose to use */ atomic_set(&private_data->wrtPending, 0); return private_data; } 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 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; } /* all arguments to this function must be checked for validity in caller */ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile, struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf, char *full_path, int xid) { struct timespec temp; int rc; /* want handles we can use to read with first in the list so we do not have to walk the list to search for one in write_begin */ if ((file->f_flags & O_ACCMODE) == O_WRONLY) { list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); } else { list_add(&pCifsFile->flist, &pCifsInode->openFileList); } write_unlock(&GlobalSMBSeslock); if (pCifsInode->clientCanCacheRead) { /* we have the inode open somewhere else no need to discard cache data */ goto client_can_cache; } /* BB need same check in cifs_create too? */ /* if not oplocked, invalidate inode pages if mtime or file size changed */ temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime)); if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) && (file->f_path.dentry->d_inode->i_size == (loff_t)le64_to_cpu(buf->EndOfFile))) { cFYI(1, ("inode unchanged on server")); } else { if (file->f_path.dentry->d_inode->i_mapping) { /* BB no need to lock inode until after invalidate since namei code should already have it locked? */ rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping); if (rc != 0) CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc; } cFYI(1, ("invalidating remote inode since open detected it " "changed")); invalidate_remote_inode(file->f_path.dentry->d_inode); } client_can_cache: if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode, full_path, inode->i_sb, xid); else rc = cifs_get_inode_info(&file->f_path.dentry->d_inode, full_path, buf, inode->i_sb, xid, NULL); if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; cFYI(1, ("Exclusive Oplock granted on inode %p", file->f_path.dentry->d_inode)); } else if ((*oplock & 0xF) == OPLOCK_READ) pCifsInode->clientCanCacheRead = true; return rc; } int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; int xid, oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; struct list_head *tmp; char *full_path = NULL; int desiredAccess; int disposition; __u16 netfid; FILE_ALL_INFO *buf = NULL; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; if (file->f_flags & O_CREAT) { /* search inode for this file and fill in file->private_data */ pCifsInode = CIFS_I(file->f_path.dentry->d_inode); read_lock(&GlobalSMBSeslock); list_for_each(tmp, &pCifsInode->openFileList) { pCifsFile = list_entry(tmp, struct cifsFileInfo, flist); if ((pCifsFile->pfile == NULL) && (pCifsFile->pid == current->tgid)) { /* mode set in cifs_create */ /* needed for writepage */ pCifsFile->pfile = file; file->private_data = pCifsFile; break; } } read_unlock(&GlobalSMBSeslock); if (file->private_data != NULL) { rc = 0; FreeXid(xid); return rc; } else { if (file->f_flags & O_EXCL) cERROR(1, ("could not find file instance for " "new file %p", file)); } } full_path = build_path_from_dentry(file->f_path.dentry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } cFYI(1, ("inode = 0x%p file flags are 0x%x for %s", inode, file->f_flags, full_path)); desiredAccess = cifs_convert_flags(file->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(file->f_flags); if (oplockEnabled) oplock = REQ_OPLOCK; else oplock = 0; /* BB pass O_SYNC flag through on file attributes .. BB */ /* Also refresh inode by passing in file_info buf returned by SMBOpen and calling get_inode_info with returned buf (at least helps non-Unix server case) */ /* BB we can not do this if this is the second open of a file and the first handle has writebehind data, we might be able to simply do a filemap_fdatawrite/filemap_fdatawait first */ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (!buf) { rc = -ENOMEM; goto out; } if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else rc = -EIO; /* no NT SMB support fall into legacy open below */ if (rc == -EIO) { /* Old server, try legacy style OpenX */ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } if (rc) { cFYI(1, ("cifs_open returned 0x%x", rc)); goto out; } file->private_data = kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); if (file->private_data == NULL) { rc = -ENOMEM; goto out; } pCifsFile = cifs_init_private(file->private_data, inode, file, netfid); write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist, &pTcon->openFileList); pCifsInode = CIFS_I(file->f_path.dentry->d_inode); if (pCifsInode) { rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, pTcon, &oplock, buf, full_path, xid); } else { write_unlock(&GlobalSMBSeslock); } if (oplock & CIFS_CREATE_ACTION) { /* time to set mode which we can not set earlier due to problems creating new read-only files */ if (pTcon->unix_ext) { struct cifs_unix_set_info_args args = { .mode = inode->i_mode, .uid = NO_CHANGE_64, .gid = NO_CHANGE_64, .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, .device = 0, }; CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } } out: kfree(buf); kfree(full_path); FreeXid(xid); return rc; } /* Try to reacquire byte range locks that were released when session */ /* to server was lost */ static int cifs_relock_file(struct cifsFileInfo *cifsFile) { int rc = 0; /* BB list all locks open on this file and relock */ return rc; } static int cifs_reopen_file(struct file *file, bool can_flush) { int rc = -EACCES; int xid, oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; struct inode *inode; char *full_path = NULL; int desiredAccess; int disposition = FILE_OPEN; __u16 netfid; if (file->private_data) pCifsFile = (struct cifsFileInfo *)file->private_data; else return -EBADF; xid = GetXid(); down(&pCifsFile->fh_sem); if (!pCifsFile->invalidHandle) { up(&pCifsFile->fh_sem); FreeXid(xid); return 0; } if (file->f_path.dentry == NULL) { cERROR(1, ("no valid name if dentry freed")); dump_stack(); rc = -EBADF; goto reopen_error_exit; } inode = file->f_path.dentry->d_inode; if (inode == NULL) { cERROR(1, ("inode not valid")); dump_stack(); rc = -EBADF; goto reopen_error_exit; } cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; /* 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(file->f_path.dentry); if (full_path == NULL) { rc = -ENOMEM; reopen_error_exit: up(&pCifsFile->fh_sem); FreeXid(xid); return rc; } cFYI(1, ("inode = 0x%p file flags 0x%x for %s", inode, file->f_flags, full_path)); desiredAccess = cifs_convert_flags(file->f_flags); if (oplockEnabled) oplock = REQ_OPLOCK; else oplock = 0; /* Can not refresh inode by passing in file_info buf to be returned by SMBOpen 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 = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { up(&pCifsFile->fh_sem); cFYI(1, ("cifs_open returned 0x%x", rc)); cFYI(1, ("oplock: %d", oplock)); } else { pCifsFile->netfid = netfid; pCifsFile->invalidHandle = false; up(&pCifsFile->fh_sem); pCifsInode = CIFS_I(inode); if (pCifsInode) { if (can_flush) { rc = filemap_write_and_wait(inode->i_mapping); if (rc != 0) CIFS_I(inode)->write_behind_rc = rc; /* temporarily disable caching while we go to server to get inode info */ pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheRead = false; if (pTcon->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 inod info */ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; cFYI(1, ("Exclusive Oplock granted on inode %p", file->f_path.dentry->d_inode)); } else if ((oplock & 0xF) == OPLOCK_READ) { pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheAll = false; } else { pCifsInode->clientCanCacheRead = false; pCifsInode->clientCanCacheAll = false; } cifs_relock_file(pCifsFile); } } kfree(full_path); FreeXid(xid); return rc; } int cifs_close(struct inode *inode, struct file *file) { int rc = 0; int xid, timeout; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *pSMBFile = (struct cifsFileInfo *)file->private_data; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; if (pSMBFile) { struct cifsLockInfo *li, *tmp; write_lock(&GlobalSMBSeslock); pSMBFile->closePend = true; if (pTcon) { /* no sense reconnecting to close a file that is already closed */ if (!pTcon->need_reconnect) { write_unlock(&GlobalSMBSeslock); timeout = 2; while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout <= 2048)) { /* Give write a better chance to get to server ahead of the close. We do not want to add a wait_q here as it would increase the memory utilization as the struct would be in each open file, but this should give enough time to clear the socket */ cFYI(DBG2, ("close delay, write pending")); msleep(timeout); timeout *= 4; } if (atomic_read(&pSMBFile->wrtPending)) cERROR(1, ("close with pending write")); if (!pTcon->need_reconnect && !pSMBFile->invalidHandle) rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); } else write_unlock(&GlobalSMBSeslock); } else write_unlock(&GlobalSMBSeslock); /* Delete any outstanding lock records. We'll lose them when the file is closed anyway. */ mutex_lock(&pSMBFile->lock_mutex); list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) { list_del(&li->llist); kfree(li); } mutex_unlock(&pSMBFile->lock_mutex); write_lock(&GlobalSMBSeslock); list_del(&pSMBFile->flist); list_del(&pSMBFile->tlist); write_unlock(&GlobalSMBSeslock); timeout = 10; /* We waited above to give the SMBWrite a chance to issue on the wire (so we do not get SMBWrite returning EBADF if writepages is racing with close. Note that writepages does not specify a file handle, so it is possible for a file to be opened twice, and the application close the "wrong" file handle - in these cases we delay long enough to allow the SMBWrite to get on the wire before the SMB Close. We allow total wait here over 45 seconds, more than oplock break time, and more than enough to allow any write to complete on the server, or to time out on the client */ while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout <= 50000)) { cERROR(1, ("writes pending, delay free of handle")); msleep(timeout); timeout *= 8; } kfree(file->private_data); file->private_data = NULL; } else rc = -EBADF; read_lock(&GlobalSMBSeslock); if (list_empty(&(CIFS_I(inode)->openFileList))) { cFYI(1, ("closing last open instance for inode %p", inode)); /* if the file is not open we do not know if we can cache info on this inode, much less write behind and read ahead */ CIFS_I(inode)->clientCanCacheRead = false; CIFS_I(inode)->clientCanCacheAll = false; } read_unlock(&GlobalSMBSeslock); if ((rc == 0) && CIFS_I(inode)->write_behind_rc) rc = CIFS_I(inode)->write_behind_rc; FreeXid(xid); return rc; } int cifs_closedir(struct inode *inode, struct file *file) { int rc = 0; int xid; struct cifsFileInfo *pCFileStruct = (struct cifsFileInfo *)file->private_data; char *ptmp; cFYI(1, ("Closedir inode = 0x%p", inode)); xid = GetXid(); if (pCFileStruct) { struct cifsTconInfo *pTcon; struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; cFYI(1, ("Freeing private data in close dir")); write_lock(&GlobalSMBSeslock); if (!pCFileStruct->srch_inf.endOfSearch && !pCFileStruct->invalidHandle) { pCFileStruct->invalidHandle = true; write_unlock(&GlobalSMBSeslock); rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); cFYI(1, ("Closing uncompleted readdir with rc %d", rc)); /* not much we can do if it fails anyway, ignore rc */ rc = 0; } else write_unlock(&GlobalSMBSeslock); ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; if (ptmp) { cFYI(1, ("closedir free smb buf in srch struct")); pCFileStruct->srch_inf.ntwrk_buf_start = NULL; if (pCFileStruct->srch_inf.smallBuf) cifs_small_buf_release(ptmp); else cifs_buf_release(ptmp); } kfree(file->private_data); file->private_data = NULL; } /* BB can we lock the filestruct while this is going on? */ FreeXid(xid); return rc; } static int store_file_lock(struct cifsFileInfo *fid, __u64 len, __u64 offset, __u8 lockType) { struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); if (li == NULL) return -ENOMEM; li->offset = offset; li->length = len; li->type = lockType; mutex_lock(&fid->lock_mutex); list_add(&li->llist, &fid->llist); mutex_unlock(&fid->lock_mutex); return 0; } int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) { int rc, xid; __u32 numLock = 0; __u32 numUnlock = 0; __u64 length; bool wait_flag = false; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; __u16 netfid; __u8 lockType = LOCKING_ANDX_LARGE_FILES; bool posix_locking = 0; length = 1 + pfLock->fl_end - pfLock->fl_start; rc = -EACCES; xid = GetXid(); cFYI(1, ("Lock parm: 0x%x flockflags: " "0x%x flocktype: 0x%x start: %lld end: %lld", cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start, pfLock->fl_end)); if (pfLock->fl_flags & FL_POSIX) cFYI(1, ("Posix")); if (pfLock->fl_flags & FL_FLOCK) cFYI(1, ("Flock")); if (pfLock->fl_flags & FL_SLEEP) { cFYI(1, ("Blocking lock")); wait_flag = true; } if (pfLock->fl_flags & FL_ACCESS) cFYI(1, ("Process suspended by mandatory locking - " "not implemented yet")); if (pfLock->fl_flags & FL_LEASE) cFYI(1, ("Lease on file - not implemented yet")); if (pfLock->fl_flags & (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE))) cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags)); if (pfLock->fl_type == F_WRLCK) { cFYI(1, ("F_WRLCK ")); numLock = 1; } else if (pfLock->fl_type == F_UNLCK) { cFYI(1, ("F_UNLCK")); numUnlock = 1; /* Check if unlock includes more than one lock range */ } else if (pfLock->fl_type == F_RDLCK) { cFYI(1, ("F_RDLCK")); lockType |= LOCKING_ANDX_SHARED_LOCK; numLock = 1; } else if (pfLock->fl_type == F_EXLCK) { cFYI(1, ("F_EXLCK")); numLock = 1; } else if (pfLock->fl_type == F_SHLCK) { cFYI(1, ("F_SHLCK")); lockType |= LOCKING_ANDX_SHARED_LOCK; numLock = 1; } else cFYI(1, ("Unknown type of lock")); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); tcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } netfid = ((struct cifsFileInfo *)file->private_data)->netfid; if ((tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) posix_locking = 1; /* 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)) { if (posix_locking) { int posix_lock_type; if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; rc = CIFSSMBPosixLock(xid, tcon, netfid, 1 /* get */, length, pfLock, posix_lock_type, wait_flag); FreeXid(xid); return rc; } /* BB we could chain these into one lock request BB */ rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, 1, lockType, 0 /* wait flag */ ); if (rc == 0) { rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 1 /* numUnlock */ , 0 /* numLock */ , lockType, 0 /* wait flag */ ); pfLock->fl_type = F_UNLCK; if (rc != 0) cERROR(1, ("Error unlocking previously locked " "range %d during test of lock", rc)); rc = 0; } else { /* if rc == ERR_SHARING_VIOLATION ? */ rc = 0; /* do not change lock type to unlock since range in use */ } FreeXid(xid); return rc; } if (!numLock && !numUnlock) { /* if no lock or unlock then nothing to do since we do not know what it is */ FreeXid(xid); return -EOPNOTSUPP; } if (posix_locking) { int posix_lock_type; if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; if (numUnlock == 1) posix_lock_type = CIFS_UNLCK; rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */, length, pfLock, posix_lock_type, wait_flag); } else { struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data; if (numLock) { rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, numLock, lockType, wait_flag); if (rc == 0) { /* For Windows locks we must store them. */ rc = store_file_lock(fid, length, pfLock->fl_start, lockType); } } else if (numUnlock) { /* For each stored lock that this unlock overlaps completely, unlock it. */ int stored_rc = 0; struct cifsLockInfo *li, *tmp; rc = 0; mutex_lock(&fid->lock_mutex); list_for_each_entry_safe(li, tmp, &fid->llist, llist) { if (pfLock->fl_start <= li->offset && (pfLock->fl_start + length) >= (li->offset + li->length)) { stored_rc = CIFSSMBLock(xid, tcon, netfid, li->length, li->offset, 1, 0, li->type, false); if (stored_rc) rc = stored_rc; list_del(&li->llist); kfree(li); } } mutex_unlock(&fid->lock_mutex); } } if (pfLock->fl_flags & FL_POSIX) posix_lock_file_wait(file, pfLock); FreeXid(xid); return rc; } ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset) { int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; /* cFYI(1, (" write %d bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); */ if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *) file->private_data; rc = generic_write_checks(file, poffset, &write_size, 0); if (rc) return rc; xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = CIFS_VLONG_OP; /* writes past EOF take long time */ else long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { if (file->private_data == NULL) { /* file has been closed on us */ FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; } if (open_file->closePend) { FreeXid(xid); if (total_written) return total_written; else return -EBADF; } 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(file, false); if (rc != 0) break; } rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, NULL, write_data + total_written, long_op); } if (rc || (bytes_written == 0)) { if (total_written) break; else { FreeXid(xid); return rc; } } else *poffset += bytes_written; long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { struct inode *inode = file->f_path.dentry->d_inode; /* Do not update local mtime - server will set its actual value on write * inode->i_ctime = inode->i_mtime = * current_fs_time(inode->i_sb);*/ if (total_written > 0) { spin_lock(&inode->i_lock); if (*poffset > file->f_path.dentry->d_inode->i_size) i_size_write(file->f_path.dentry->d_inode, *poffset); spin_unlock(&inode->i_lock); } mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written; } static ssize_t cifs_write(struct file *file, const char *write_data, size_t write_size, loff_t *poffset) { int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; cFYI(1, ("write %zd bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *)file->private_data; xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */ else long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { if (file->private_data == NULL) { /* file has been closed on us */ FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; } if (open_file->closePend) { FreeXid(xid); if (total_written) return total_written; else return -EBADF; } 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(file, false); if (rc != 0) break; } if (experimEnabled || (pTcon->ses->server && ((pTcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0))) { struct kvec iov[2]; unsigned int len; 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; rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, len, *poffset, &bytes_written, iov, 1, long_op); } else rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, write_data + total_written, NULL, long_op); } if (rc || (bytes_written == 0)) { if (total_written) break; else { FreeXid(xid); return rc; } } else *poffset += bytes_written; long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { /*BB We could make this contingent on superblock ATIME flag too */ /* file->f_path.dentry->d_inode->i_ctime = file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/ if (total_written > 0) { spin_lock(&file->f_path.dentry->d_inode->i_lock); if (*poffset > file->f_path.dentry->d_inode->i_size) i_size_write(file->f_path.dentry->d_inode, *poffset); spin_unlock(&file->f_path.dentry->d_inode->i_lock); } mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written; } #ifdef CONFIG_CIFS_EXPERIMENTAL struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file = NULL; read_lock(&GlobalSMBSeslock); /* 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 (open_file->closePend) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_RDONLY))) { if (!open_file->invalidHandle) { /* found a good file */ /* lock it so it will not be closed on us */ atomic_inc(&open_file->wrtPending); read_unlock(&GlobalSMBSeslock); 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 */ } read_unlock(&GlobalSMBSeslock); return NULL; } #endif struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; bool any_available = false; int rc; /* 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) { cERROR(1, ("Null inode passed to cifs_writeable_file")); dump_stack(); return NULL; } read_lock(&GlobalSMBSeslock); refind_writable: list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend || (!any_available && open_file->pid != current->tgid)) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { atomic_inc(&open_file->wrtPending); if (!open_file->invalidHandle) { /* found a good writable file */ read_unlock(&GlobalSMBSeslock); return open_file; } read_unlock(&GlobalSMBSeslock); /* Had to unlock since following call can block */ rc = cifs_reopen_file(open_file->pfile, false); if (!rc) { if (!open_file->closePend) return open_file; else { /* start over in case this was deleted */ /* since the list could be modified */ read_lock(&GlobalSMBSeslock); atomic_dec(&open_file->wrtPending); goto refind_writable; } } /* if it fails, try another handle if possible - (we can not do this if closePending since loop could be modified - in which case we have to start at the beginning of the list again. Note that it would be bad to hold up writepages here (rather than in caller) with continuous retries */ cFYI(1, ("wp failed on reopen file")); read_lock(&GlobalSMBSeslock); /* can not use this handle, no write pending on this one after all */ atomic_dec(&open_file->wrtPending); if (open_file->closePend) /* list could have changed */ goto refind_writable; /* else we simply continue to the next entry. Thus we do not loop on reopen errors. If we can not reopen the file, for example if we reconnected to a server with another client racing to delete or lock the file we would not make progress if we restarted before the beginning of the loop here. */ } } /* couldn't find useable FH with same pid, try any available */ if (!any_available) { any_available = true; goto refind_writable; } read_unlock(&GlobalSMBSeslock); 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 cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct inode *inode; struct cifsFileInfo *open_file; if (!mapping || !mapping->host) return -EFAULT; inode = page->mapping->host; cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; 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)); if (open_file) { bytes_written = cifs_write(open_file->pfile, write_data, to-from, &offset); atomic_dec(&open_file->wrtPending); /* 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 { cFYI(1, ("No writeable filehandles for inode")); rc = -EIO; } kunmap(page); return rc; } static int cifs_writepages(struct address_space *mapping, struct writeback_control *wbc) { struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned int bytes_to_write; unsigned int bytes_written; struct cifs_sb_info *cifs_sb; int done = 0; pgoff_t end; pgoff_t index; int range_whole = 0; struct kvec *iov; int len; int n_iov = 0; pgoff_t next; int nr_pages; __u64 offset = 0; struct cifsFileInfo *open_file; struct page *page; struct pagevec pvec; int rc = 0; int scanned = 0; int xid; cifs_sb = CIFS_SB(mapping->host->i_sb); /* * If wsize is smaller that 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 ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server)) if (cifs_sb->tcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) if (!experimEnabled) return generic_writepages(mapping, wbc); iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL); if (iov == NULL) return generic_writepages(mapping, wbc); /* * BB: Is this meaningful for a non-block-device file system? * If it is, we should test it again after we do I/O */ if (wbc->nonblocking && bdi_write_congested(bdi)) { wbc->encountered_congestion = 1; kfree(iov); return 0; } xid = GetXid(); pagevec_init(&pvec, 0); 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 = 1; scanned = 1; } retry: while (!done && (index <= end) && (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, PAGECACHE_TAG_DIRTY, min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) { int first; unsigned int i; first = -1; next = 0; n_iov = 0; bytes_to_write = 0; for (i = 0; i < nr_pages; i++) { page = pvec.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 (first < 0) lock_page(page); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) else if (TestSetPageLocked(page)) #else else if (!trylock_page(page)) #endif break; if (unlikely(page->mapping != mapping)) { unlock_page(page); break; } if (!wbc->range_cyclic && page->index > end) { done = 1; 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) >= mapping->host->i_size) { done = 1; unlock_page(page); end_page_writeback(page); break; } /* * BB can we get rid of this? pages are held by pvec */ page_cache_get(page); len = min(mapping->host->i_size - page_offset(page), (loff_t)PAGE_CACHE_SIZE); /* reserve iov[0] for the smb header */ n_iov++; iov[n_iov].iov_base = kmap(page); iov[n_iov].iov_len = len; bytes_to_write += len; if (first < 0) { first = i; offset = page_offset(page); } next = page->index + 1; if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize) break; } if (n_iov) { /* Search for a writable handle every time we call * CIFSSMBWrite2. We can't rely on the last handle * we used to still be valid */ open_file = find_writable_file(CIFS_I(mapping->host)); if (!open_file) { cERROR(1, ("No writable handles for inode")); rc = -EBADF; } else { rc = CIFSSMBWrite2(xid, cifs_sb->tcon, open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, CIFS_LONG_OP); atomic_dec(&open_file->wrtPending); if (rc || bytes_written < bytes_to_write) { cERROR(1, ("Write2 ret %d, wrote %d", rc, bytes_written)); /* BB what if continued retry is requested via mount flags? */ if (rc == -ENOSPC) set_bit(AS_ENOSPC, &mapping->flags); else set_bit(AS_EIO, &mapping->flags); } else { cifs_stats_bytes_written(cifs_sb->tcon, bytes_written); } } for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; /* Should we also set page error on success rc but too little data written? */ /* BB investigate retry logic on temporary server crash cases and how recovery works when page marked as error */ if (rc) SetPageError(page); kunmap(page); unlock_page(page); end_page_writeback(page); page_cache_release(page); } if ((wbc->nr_to_write -= n_iov) <= 0) done = 1; index = next; } else /* Need to re-find the pages we skipped */ index = pvec.pages[0]->index + 1; pagevec_release(&pvec); } 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 = 1; index = 0; goto retry; } if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = index; FreeXid(xid); kfree(iov); return rc; } static int cifs_writepage(struct page *page, struct writeback_control *wbc) { int rc = -EFAULT; int xid; xid = GetXid(); /* BB add check for wbc flags */ page_cache_get(page); if (!PageUptodate(page)) cFYI(1, ("ppw - page not up to date")); /* * 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); rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); /* BB add check for error and Clearuptodate? */ unlock_page(page); end_page_writeback(page); page_cache_release(page); FreeXid(xid); 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; cFYI(1, ("write_end for page %p from pos %lld with %d bytes", 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); int xid; xid = GetXid(); /* 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(file, page_data + offset, copied, &pos); /* if (rc < 0) should we set writebehind rc? */ kunmap(page); FreeXid(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_fsync(struct file *file, struct dentry *dentry, int datasync) { int xid; int rc = 0; struct inode *inode = file->f_path.dentry->d_inode; xid = GetXid(); cFYI(1, ("Sync file - name: %s datasync: 0x%x", dentry->d_name.name, datasync)); rc = filemap_write_and_wait(inode->i_mapping); if (rc == 0) { rc = CIFS_I(inode)->write_behind_rc; CIFS_I(inode)->write_behind_rc = 0; } FreeXid(xid); return rc; } /* static void cifs_sync_page(struct page *page) { struct address_space *mapping; struct inode *inode; unsigned long index = page->index; unsigned int rpages = 0; int rc = 0; cFYI(1, ("sync page %p",page)); mapping = page->mapping; if (!mapping) return 0; inode = mapping->host; if (!inode) return; */ /* fill in rpages then result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ /* cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index)); #if 0 if (rc < 0) return rc; return 0; #endif } */ /* * 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->f_path.dentry->d_inode; int rc = 0; /* Rather than do the steps manually: lock the inode for writing loop through pages looking for write behind data (dirty pages) coalesce into contiguous 16K (or smaller) chunks to write to server send to server (prefer in parallel) deal with writebehind errors unlock inode for writing filemapfdatawrite appears easier for the time being */ rc = filemap_fdatawrite(inode->i_mapping); /* reset wb rc if we were able to write out dirty pages */ if (!rc) { rc = CIFS_I(inode)->write_behind_rc; CIFS_I(inode)->write_behind_rc = 0; } cFYI(1, ("Flush inode %p file %p rc %d", inode, file, rc)); return rc; } ssize_t cifs_user_read(struct file *file, char __user *read_data, size_t read_size, loff_t *poffset) { int rc = -EACCES; unsigned int bytes_read = 0; unsigned int total_read = 0; unsigned int current_read_size; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid; struct cifsFileInfo *open_file; char *smb_read_data; char __user *current_offset; struct smb_com_read_rsp *pSMBr; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); for (total_read = 0, current_offset = read_data; read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); rc = -EAGAIN; smb_read_data = NULL; while (rc == -EAGAIN) { int buf_type = CIFS_NO_BUFFER; if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, true); if (rc != 0) break; } rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, &smb_read_data, &buf_type); pSMBr = (struct smb_com_read_rsp *)smb_read_data; if (smb_read_data) { if (copy_to_user(current_offset, smb_read_data + 4 /* RFC1001 length field */ + le16_to_cpu(pSMBr->DataOffset), bytes_read)) rc = -EFAULT; if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } } if (rc || (bytes_read == 0)) { if (total_read) { break; } else { FreeXid(xid); return rc; } } else { cifs_stats_bytes_read(pTcon, bytes_read); *poffset += bytes_read; } } FreeXid(xid); return total_read; } static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *poffset) { int rc = -EACCES; unsigned int bytes_read = 0; unsigned int total_read; unsigned int current_read_size; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid; char *current_offset; struct cifsFileInfo *open_file; int buf_type = CIFS_NO_BUFFER; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); for (total_read = 0, current_offset = read_data; read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); /* For windows me and 9x we do not want to request more than it negotiated since it will refuse the read then */ if ((pTcon->ses) && !(pTcon->ses->capabilities & CAP_LARGE_FILES)) { current_read_size = min_t(const int, current_read_size, pTcon->ses->server->maxBuf - 128); } rc = -EAGAIN; while (rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, true); if (rc != 0) break; } rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, ¤t_offset, &buf_type); } if (rc || (bytes_read == 0)) { if (total_read) { break; } else { FreeXid(xid); return rc; } } else { cifs_stats_bytes_read(pTcon, total_read); *poffset += bytes_read; } } FreeXid(xid); return total_read; } int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct dentry *dentry = file->f_path.dentry; int rc, xid; xid = GetXid(); rc = cifs_revalidate(dentry); if (rc) { cFYI(1, ("Validation prior to mmap failed, error=%d", rc)); FreeXid(xid); return rc; } rc = generic_file_mmap(file, vma); FreeXid(xid); return rc; } static void cifs_copy_cache_pages(struct address_space *mapping, struct list_head *pages, int bytes_read, char *data, struct pagevec *plru_pvec) { struct page *page; char *target; while (bytes_read > 0) { if (list_empty(pages)) break; page = list_entry(pages->prev, struct page, lru); list_del(&page->lru); if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { page_cache_release(page); cFYI(1, ("Add page cache failed")); data += PAGE_CACHE_SIZE; bytes_read -= PAGE_CACHE_SIZE; continue; } target = kmap_atomic(page, KM_USER0); if (PAGE_CACHE_SIZE > bytes_read) { memcpy(target, data, bytes_read); /* zero the tail end of this partial page */ memset(target + bytes_read, 0, PAGE_CACHE_SIZE - bytes_read); bytes_read = 0; } else { memcpy(target, data, PAGE_CACHE_SIZE); bytes_read -= PAGE_CACHE_SIZE; } kunmap_atomic(target, KM_USER0); flush_dcache_page(page); SetPageUptodate(page); unlock_page(page); if (!pagevec_add(plru_pvec, page)) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) __pagevec_lru_add(plru_pvec); #else __pagevec_lru_add_file(plru_pvec); #endif data += PAGE_CACHE_SIZE; } return; } static int cifs_readpages(struct file *file, struct address_space *mapping, struct list_head *page_list, unsigned num_pages) { int rc = -EACCES; int xid; loff_t offset; struct page *page; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; unsigned int bytes_read = 0; unsigned int read_size, i; char *smb_read_data = NULL; struct smb_com_read_rsp *pSMBr; struct pagevec lru_pvec; struct cifsFileInfo *open_file; int buf_type = CIFS_NO_BUFFER; xid = GetXid(); if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; pagevec_init(&lru_pvec, 0); cFYI(DBG2, ("rpages: num pages %d", num_pages)); for (i = 0; i < num_pages; ) { unsigned contig_pages; struct page *tmp_page; unsigned long expected_index; if (list_empty(page_list)) break; page = list_entry(page_list->prev, struct page, lru); offset = (loff_t)page->index << PAGE_CACHE_SHIFT; /* count adjacent pages that we will read into */ contig_pages = 0; expected_index = list_entry(page_list->prev, struct page, lru)->index; list_for_each_entry_reverse(tmp_page, page_list, lru) { if (tmp_page->index == expected_index) { contig_pages++; expected_index++; } else break; } if (contig_pages + i > num_pages) contig_pages = num_pages - i; /* for reads over a certain size could initiate async read ahead */ read_size = contig_pages * PAGE_CACHE_SIZE; /* Read size needs to be in multiples of one page */ read_size = min_t(const unsigned int, read_size, cifs_sb->rsize & PAGE_CACHE_MASK); cFYI(DBG2, ("rpages: read size 0x%x contiguous pages %d", read_size, contig_pages)); rc = -EAGAIN; while (rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, true); if (rc != 0) break; } rc = CIFSSMBRead(xid, pTcon, open_file->netfid, read_size, offset, &bytes_read, &smb_read_data, &buf_type); /* BB more RC checks ? */ if (rc == -EAGAIN) { if (smb_read_data) { if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } } } if ((rc < 0) || (smb_read_data == NULL)) { cFYI(1, ("Read error in readpages: %d", rc)); break; } else if (bytes_read > 0) { task_io_account_read(bytes_read); pSMBr = (struct smb_com_read_rsp *)smb_read_data; cifs_copy_cache_pages(mapping, page_list, bytes_read, smb_read_data + 4 /* RFC1001 hdr */ + le16_to_cpu(pSMBr->DataOffset), &lru_pvec); i += bytes_read >> PAGE_CACHE_SHIFT; cifs_stats_bytes_read(pTcon, bytes_read); if ((bytes_read & PAGE_CACHE_MASK) != bytes_read) { i++; /* account for partial page */ /* server copy of file can have smaller size than client */ /* BB do we need to verify this common case ? this case is ok - if we are at server EOF we will hit it on next read */ /* break; */ } } else { cFYI(1, ("No bytes read (%d) at offset %lld . " "Cleaning remaining pages from readahead list", bytes_read, offset)); /* BB turn off caching and do new lookup on file size at server? */ break; } if (smb_read_data) { if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } bytes_read = 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) pagevec_lru_add(&lru_pvec); #else pagevec_lru_add_file(&lru_pvec); #endif /* need to free smb_read_data buf before exit */ if (smb_read_data) { if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } FreeXid(xid); return rc; } static int cifs_readpage_worker(struct file *file, struct page *page, loff_t *poffset) { char *read_data; int rc; 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 cFYI(1, ("Bytes read %d", rc)); file->f_path.dentry->d_inode->i_atime = current_fs_time(file->f_path.dentry->d_inode->i_sb); if (PAGE_CACHE_SIZE > rc) memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc); flush_dcache_page(page); SetPageUptodate(page); rc = 0; io_error: kunmap(page); page_cache_release(page); 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; int xid; xid = GetXid(); if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } cFYI(1, ("readpage %p at offset %d 0x%x\n", page, (int)offset, (int)offset)); rc = cifs_readpage_worker(file, page, &offset); unlock_page(page); FreeXid(xid); return rc; } static int is_inode_writable(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; read_lock(&GlobalSMBSeslock); list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { read_unlock(&GlobalSMBSeslock); return 1; } } read_unlock(&GlobalSMBSeslock); 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; cFYI(1, ("write_begin from %lld len %d", (long long)pos, len)); page = __grab_cache_page(mapping, index); 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_I(mapping->host)->clientCanCacheRead) { 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; } 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, /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; /* * 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, /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; cifs-test-base/file.c~0000644000175000017500000016471311117761244014542 0ustar stevefstevef/* * fs/cifs/file.c * * vfs operations that deal with files * * Copyright (C) International Business Machines Corp., 2002,2007 * 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 #include #include #include #include #include #include #include #include #include #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" static inline struct cifsFileInfo *cifs_init_private( struct cifsFileInfo *private_data, struct inode *inode, struct file *file, __u16 netfid) { memset(private_data, 0, sizeof(struct cifsFileInfo)); private_data->netfid = netfid; private_data->pid = current->tgid; init_MUTEX(&private_data->fh_sem); mutex_init(&private_data->lock_mutex); INIT_LIST_HEAD(&private_data->llist); private_data->pfile = file; /* needed for writepage */ private_data->pInode = inode; private_data->invalidHandle = false; private_data->closePend = false; /* we have to track num writers to the inode, since writepages does not tell us which handle the write is for so there can be a close (overlapping with write) of the filehandle that cifs_writepages chose to use */ atomic_set(&private_data->wrtPending, 0); return private_data; } 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 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; } /* all arguments to this function must be checked for validity in caller */ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile, struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf, char *full_path, int xid) { struct timespec temp; int rc; /* want handles we can use to read with first in the list so we do not have to walk the list to search for one in write_begin */ if ((file->f_flags & O_ACCMODE) == O_WRONLY) { list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); } else { list_add(&pCifsFile->flist, &pCifsInode->openFileList); } write_unlock(&GlobalSMBSeslock); if (pCifsInode->clientCanCacheRead) { /* we have the inode open somewhere else no need to discard cache data */ goto client_can_cache; } /* BB need same check in cifs_create too? */ /* if not oplocked, invalidate inode pages if mtime or file size changed */ temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime)); if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) && (file->f_path.dentry->d_inode->i_size == (loff_t)le64_to_cpu(buf->EndOfFile))) { cFYI(1, ("inode unchanged on server")); } else { if (file->f_path.dentry->d_inode->i_mapping) { /* BB no need to lock inode until after invalidate since namei code should already have it locked? */ rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping); if (rc != 0) CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc; } cFYI(1, ("invalidating remote inode since open detected it " "changed")); invalidate_remote_inode(file->f_path.dentry->d_inode); } client_can_cache: if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode, full_path, inode->i_sb, xid); else rc = cifs_get_inode_info(&file->f_path.dentry->d_inode, full_path, buf, inode->i_sb, xid, NULL); if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; cFYI(1, ("Exclusive Oplock granted on inode %p", file->f_path.dentry->d_inode)); } else if ((*oplock & 0xF) == OPLOCK_READ) pCifsInode->clientCanCacheRead = true; return rc; } int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; int xid, oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; struct list_head *tmp; char *full_path = NULL; int desiredAccess; int disposition; __u16 netfid; FILE_ALL_INFO *buf = NULL; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; if (file->f_flags & O_CREAT) { /* search inode for this file and fill in file->private_data */ pCifsInode = CIFS_I(file->f_path.dentry->d_inode); read_lock(&GlobalSMBSeslock); list_for_each(tmp, &pCifsInode->openFileList) { pCifsFile = list_entry(tmp, struct cifsFileInfo, flist); if ((pCifsFile->pfile == NULL) && (pCifsFile->pid == current->tgid)) { /* mode set in cifs_create */ /* needed for writepage */ pCifsFile->pfile = file; file->private_data = pCifsFile; break; } } read_unlock(&GlobalSMBSeslock); if (file->private_data != NULL) { rc = 0; FreeXid(xid); return rc; } else { if (file->f_flags & O_EXCL) cERROR(1, ("could not find file instance for " "new file %p", file)); } } full_path = build_path_from_dentry(file->f_path.dentry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } cFYI(1, ("inode = 0x%p file flags are 0x%x for %s", inode, file->f_flags, full_path)); desiredAccess = cifs_convert_flags(file->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(file->f_flags); if (oplockEnabled) oplock = REQ_OPLOCK; else oplock = 0; /* BB pass O_SYNC flag through on file attributes .. BB */ /* Also refresh inode by passing in file_info buf returned by SMBOpen and calling get_inode_info with returned buf (at least helps non-Unix server case) */ /* BB we can not do this if this is the second open of a file and the first handle has writebehind data, we might be able to simply do a filemap_fdatawrite/filemap_fdatawait first */ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (!buf) { rc = -ENOMEM; goto out; } if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else rc = -EIO; /* no NT SMB support fall into legacy open below */ if (rc == -EIO) { /* Old server, try legacy style OpenX */ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } if (rc) { cFYI(1, ("cifs_open returned 0x%x", rc)); goto out; } file->private_data = kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); if (file->private_data == NULL) { rc = -ENOMEM; goto out; } pCifsFile = cifs_init_private(file->private_data, inode, file, netfid); write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist, &pTcon->openFileList); pCifsInode = CIFS_I(file->f_path.dentry->d_inode); if (pCifsInode) { rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, pTcon, &oplock, buf, full_path, xid); } else { write_unlock(&GlobalSMBSeslock); } if (oplock & CIFS_CREATE_ACTION) { /* time to set mode which we can not set earlier due to problems creating new read-only files */ if (pTcon->unix_ext) { struct cifs_unix_set_info_args args = { .mode = inode->i_mode, .uid = NO_CHANGE_64, .gid = NO_CHANGE_64, .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, .device = 0, }; CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } } out: kfree(buf); kfree(full_path); FreeXid(xid); return rc; } /* Try to reacquire byte range locks that were released when session */ /* to server was lost */ static int cifs_relock_file(struct cifsFileInfo *cifsFile) { int rc = 0; /* BB list all locks open on this file and relock */ return rc; } static int cifs_reopen_file(struct file *file, bool can_flush) { int rc = -EACCES; int xid, oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; struct inode *inode; char *full_path = NULL; int desiredAccess; int disposition = FILE_OPEN; __u16 netfid; if (file->private_data) pCifsFile = (struct cifsFileInfo *)file->private_data; else return -EBADF; xid = GetXid(); down(&pCifsFile->fh_sem); if (!pCifsFile->invalidHandle) { up(&pCifsFile->fh_sem); FreeXid(xid); return 0; } if (file->f_path.dentry == NULL) { cERROR(1, ("no valid name if dentry freed")); dump_stack(); rc = -EBADF; goto reopen_error_exit; } inode = file->f_path.dentry->d_inode; if (inode == NULL) { cERROR(1, ("inode not valid")); dump_stack(); rc = -EBADF; goto reopen_error_exit; } cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; /* 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(file->f_path.dentry); if (full_path == NULL) { rc = -ENOMEM; reopen_error_exit: up(&pCifsFile->fh_sem); FreeXid(xid); return rc; } cFYI(1, ("inode = 0x%p file flags 0x%x for %s", inode, file->f_flags, full_path)); desiredAccess = cifs_convert_flags(file->f_flags); if (oplockEnabled) oplock = REQ_OPLOCK; else oplock = 0; /* Can not refresh inode by passing in file_info buf to be returned by SMBOpen 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 = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { up(&pCifsFile->fh_sem); cFYI(1, ("cifs_open returned 0x%x", rc)); cFYI(1, ("oplock: %d", oplock)); } else { pCifsFile->netfid = netfid; pCifsFile->invalidHandle = false; up(&pCifsFile->fh_sem); pCifsInode = CIFS_I(inode); if (pCifsInode) { if (can_flush) { rc = filemap_write_and_wait(inode->i_mapping); if (rc != 0) CIFS_I(inode)->write_behind_rc = rc; /* temporarily disable caching while we go to server to get inode info */ pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheRead = false; if (pTcon->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 inod info */ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; cFYI(1, ("Exclusive Oplock granted on inode %p", file->f_path.dentry->d_inode)); } else if ((oplock & 0xF) == OPLOCK_READ) { pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheAll = false; } else { pCifsInode->clientCanCacheRead = false; pCifsInode->clientCanCacheAll = false; } cifs_relock_file(pCifsFile); } } kfree(full_path); FreeXid(xid); return rc; } int cifs_close(struct inode *inode, struct file *file) { int rc = 0; int xid, timeout; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *pSMBFile = (struct cifsFileInfo *)file->private_data; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; if (pSMBFile) { struct cifsLockInfo *li, *tmp; write_lock(&GlobalSMBSeslock); pSMBFile->closePend = true; if (pTcon) { /* no sense reconnecting to close a file that is already closed */ if (!pTcon->need_reconnect) { write_unlock(&GlobalSMBSeslock); timeout = 2; while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout <= 2048)) { /* Give write a better chance to get to server ahead of the close. We do not want to add a wait_q here as it would increase the memory utilization as the struct would be in each open file, but this should give enough time to clear the socket */ cFYI(DBG2, ("close delay, write pending")); msleep(timeout); timeout *= 4; } if (atomic_read(&pSMBFile->wrtPending)) cERROR(1, ("close with pending write")); if (!pTcon->need_reconnect && !pSMBFile->invalidHandle) rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); } else write_unlock(&GlobalSMBSeslock); } else write_unlock(&GlobalSMBSeslock); /* Delete any outstanding lock records. We'll lose them when the file is closed anyway. */ mutex_lock(&pSMBFile->lock_mutex); list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) { list_del(&li->llist); kfree(li); } mutex_unlock(&pSMBFile->lock_mutex); write_lock(&GlobalSMBSeslock); list_del(&pSMBFile->flist); list_del(&pSMBFile->tlist); write_unlock(&GlobalSMBSeslock); timeout = 10; /* We waited above to give the SMBWrite a chance to issue on the wire (so we do not get SMBWrite returning EBADF if writepages is racing with close. Note that writepages does not specify a file handle, so it is possible for a file to be opened twice, and the application close the "wrong" file handle - in these cases we delay long enough to allow the SMBWrite to get on the wire before the SMB Close. We allow total wait here over 45 seconds, more than oplock break time, and more than enough to allow any write to complete on the server, or to time out on the client */ while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout <= 50000)) { cERROR(1, ("writes pending, delay free of handle")); msleep(timeout); timeout *= 8; } kfree(file->private_data); file->private_data = NULL; } else rc = -EBADF; read_lock(&GlobalSMBSeslock); if (list_empty(&(CIFS_I(inode)->openFileList))) { cFYI(1, ("closing last open instance for inode %p", inode)); /* if the file is not open we do not know if we can cache info on this inode, much less write behind and read ahead */ CIFS_I(inode)->clientCanCacheRead = false; CIFS_I(inode)->clientCanCacheAll = false; } read_unlock(&GlobalSMBSeslock); if ((rc == 0) && CIFS_I(inode)->write_behind_rc) rc = CIFS_I(inode)->write_behind_rc; FreeXid(xid); return rc; } int cifs_closedir(struct inode *inode, struct file *file) { int rc = 0; int xid; struct cifsFileInfo *pCFileStruct = (struct cifsFileInfo *)file->private_data; char *ptmp; cFYI(1, ("Closedir inode = 0x%p", inode)); xid = GetXid(); if (pCFileStruct) { struct cifsTconInfo *pTcon; struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; cFYI(1, ("Freeing private data in close dir")); write_lock(&GlobalSMBSeslock); if (!pCFileStruct->srch_inf.endOfSearch && !pCFileStruct->invalidHandle) { pCFileStruct->invalidHandle = true; write_unlock(&GlobalSMBSeslock); rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); cFYI(1, ("Closing uncompleted readdir with rc %d", rc)); /* not much we can do if it fails anyway, ignore rc */ rc = 0; } else write_unlock(&GlobalSMBSeslock); ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; if (ptmp) { cFYI(1, ("closedir free smb buf in srch struct")); pCFileStruct->srch_inf.ntwrk_buf_start = NULL; if (pCFileStruct->srch_inf.smallBuf) cifs_small_buf_release(ptmp); else cifs_buf_release(ptmp); } kfree(file->private_data); file->private_data = NULL; } /* BB can we lock the filestruct while this is going on? */ FreeXid(xid); return rc; } static int store_file_lock(struct cifsFileInfo *fid, __u64 len, __u64 offset, __u8 lockType) { struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); if (li == NULL) return -ENOMEM; li->offset = offset; li->length = len; li->type = lockType; mutex_lock(&fid->lock_mutex); list_add(&li->llist, &fid->llist); mutex_unlock(&fid->lock_mutex); return 0; } int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) { int rc, xid; __u32 numLock = 0; __u32 numUnlock = 0; __u64 length; bool wait_flag = false; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; __u16 netfid; __u8 lockType = LOCKING_ANDX_LARGE_FILES; bool posix_locking = 0; length = 1 + pfLock->fl_end - pfLock->fl_start; rc = -EACCES; xid = GetXid(); cFYI(1, ("Lock parm: 0x%x flockflags: " "0x%x flocktype: 0x%x start: %lld end: %lld", cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start, pfLock->fl_end)); if (pfLock->fl_flags & FL_POSIX) cFYI(1, ("Posix")); if (pfLock->fl_flags & FL_FLOCK) cFYI(1, ("Flock")); if (pfLock->fl_flags & FL_SLEEP) { cFYI(1, ("Blocking lock")); wait_flag = true; } if (pfLock->fl_flags & FL_ACCESS) cFYI(1, ("Process suspended by mandatory locking - " "not implemented yet")); if (pfLock->fl_flags & FL_LEASE) cFYI(1, ("Lease on file - not implemented yet")); if (pfLock->fl_flags & (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE))) cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags)); if (pfLock->fl_type == F_WRLCK) { cFYI(1, ("F_WRLCK ")); numLock = 1; } else if (pfLock->fl_type == F_UNLCK) { cFYI(1, ("F_UNLCK")); numUnlock = 1; /* Check if unlock includes more than one lock range */ } else if (pfLock->fl_type == F_RDLCK) { cFYI(1, ("F_RDLCK")); lockType |= LOCKING_ANDX_SHARED_LOCK; numLock = 1; } else if (pfLock->fl_type == F_EXLCK) { cFYI(1, ("F_EXLCK")); numLock = 1; } else if (pfLock->fl_type == F_SHLCK) { cFYI(1, ("F_SHLCK")); lockType |= LOCKING_ANDX_SHARED_LOCK; numLock = 1; } else cFYI(1, ("Unknown type of lock")); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); tcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } netfid = ((struct cifsFileInfo *)file->private_data)->netfid; if ((tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) posix_locking = 1; /* 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)) { if (posix_locking) { int posix_lock_type; if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; rc = CIFSSMBPosixLock(xid, tcon, netfid, 1 /* get */, length, pfLock, posix_lock_type, wait_flag); FreeXid(xid); return rc; } /* BB we could chain these into one lock request BB */ rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, 1, lockType, 0 /* wait flag */ ); if (rc == 0) { rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 1 /* numUnlock */ , 0 /* numLock */ , lockType, 0 /* wait flag */ ); pfLock->fl_type = F_UNLCK; if (rc != 0) cERROR(1, ("Error unlocking previously locked " "range %d during test of lock", rc)); rc = 0; } else { /* if rc == ERR_SHARING_VIOLATION ? */ rc = 0; /* do not change lock type to unlock since range in use */ } FreeXid(xid); return rc; } if (!numLock && !numUnlock) { /* if no lock or unlock then nothing to do since we do not know what it is */ FreeXid(xid); return -EOPNOTSUPP; } if (posix_locking) { int posix_lock_type; if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; if (numUnlock == 1) posix_lock_type = CIFS_UNLCK; rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */, length, pfLock, posix_lock_type, wait_flag); } else { struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data; if (numLock) { rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, numLock, lockType, wait_flag); if (rc == 0) { /* For Windows locks we must store them. */ rc = store_file_lock(fid, length, pfLock->fl_start, lockType); } } else if (numUnlock) { /* For each stored lock that this unlock overlaps completely, unlock it. */ int stored_rc = 0; struct cifsLockInfo *li, *tmp; rc = 0; mutex_lock(&fid->lock_mutex); list_for_each_entry_safe(li, tmp, &fid->llist, llist) { if (pfLock->fl_start <= li->offset && (pfLock->fl_start + length) >= (li->offset + li->length)) { stored_rc = CIFSSMBLock(xid, tcon, netfid, li->length, li->offset, 1, 0, li->type, false); if (stored_rc) rc = stored_rc; list_del(&li->llist); kfree(li); } } mutex_unlock(&fid->lock_mutex); } } if (pfLock->fl_flags & FL_POSIX) posix_lock_file_wait(file, pfLock); FreeXid(xid); return rc; } ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset) { int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; /* cFYI(1, (" write %d bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); */ if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *) file->private_data; rc = generic_write_checks(file, poffset, &write_size, 0); if (rc) return rc; xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = CIFS_VLONG_OP; /* writes past EOF take long time */ else long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { if (file->private_data == NULL) { /* file has been closed on us */ FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; } if (open_file->closePend) { FreeXid(xid); if (total_written) return total_written; else return -EBADF; } 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(file, false); if (rc != 0) break; } rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, NULL, write_data + total_written, long_op); } if (rc || (bytes_written == 0)) { if (total_written) break; else { FreeXid(xid); return rc; } } else *poffset += bytes_written; long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { struct inode *inode = file->f_path.dentry->d_inode; /* Do not update local mtime - server will set its actual value on write * inode->i_ctime = inode->i_mtime = * current_fs_time(inode->i_sb);*/ if (total_written > 0) { spin_lock(&inode->i_lock); if (*poffset > file->f_path.dentry->d_inode->i_size) i_size_write(file->f_path.dentry->d_inode, *poffset); spin_unlock(&inode->i_lock); } mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written; } static ssize_t cifs_write(struct file *file, const char *write_data, size_t write_size, loff_t *poffset) { int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; cFYI(1, ("write %zd bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *)file->private_data; xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */ else long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { if (file->private_data == NULL) { /* file has been closed on us */ FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; } if (open_file->closePend) { FreeXid(xid); if (total_written) return total_written; else return -EBADF; } 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(file, false); if (rc != 0) break; } if (experimEnabled || (pTcon->ses->server && ((pTcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0))) { struct kvec iov[2]; unsigned int len; 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; rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, len, *poffset, &bytes_written, iov, 1, long_op); } else rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, write_data + total_written, NULL, long_op); } if (rc || (bytes_written == 0)) { if (total_written) break; else { FreeXid(xid); return rc; } } else *poffset += bytes_written; long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { /*BB We could make this contingent on superblock ATIME flag too */ /* file->f_path.dentry->d_inode->i_ctime = file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/ if (total_written > 0) { spin_lock(&file->f_path.dentry->d_inode->i_lock); if (*poffset > file->f_path.dentry->d_inode->i_size) i_size_write(file->f_path.dentry->d_inode, *poffset); spin_unlock(&file->f_path.dentry->d_inode->i_lock); } mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written; } #ifdef CONFIG_CIFS_EXPERIMENTAL struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file = NULL; read_lock(&GlobalSMBSeslock); /* 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 (open_file->closePend) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_RDONLY))) { if (!open_file->invalidHandle) { /* found a good file */ /* lock it so it will not be closed on us */ atomic_inc(&open_file->wrtPending); read_unlock(&GlobalSMBSeslock); 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 */ } read_unlock(&GlobalSMBSeslock); return NULL; } #endif struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; bool any_available = false; int rc; /* 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) { cERROR(1, ("Null inode passed to cifs_writeable_file")); dump_stack(); return NULL; } read_lock(&GlobalSMBSeslock); refind_writable: list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend || (!any_available && open_file->pid != current->tgid)) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { atomic_inc(&open_file->wrtPending); if (!open_file->invalidHandle) { /* found a good writable file */ read_unlock(&GlobalSMBSeslock); return open_file; } read_unlock(&GlobalSMBSeslock); /* Had to unlock since following call can block */ rc = cifs_reopen_file(open_file->pfile, false); if (!rc) { if (!open_file->closePend) return open_file; else { /* start over in case this was deleted */ /* since the list could be modified */ read_lock(&GlobalSMBSeslock); atomic_dec(&open_file->wrtPending); goto refind_writable; } } /* if it fails, try another handle if possible - (we can not do this if closePending since loop could be modified - in which case we have to start at the beginning of the list again. Note that it would be bad to hold up writepages here (rather than in caller) with continuous retries */ cFYI(1, ("wp failed on reopen file")); read_lock(&GlobalSMBSeslock); /* can not use this handle, no write pending on this one after all */ atomic_dec(&open_file->wrtPending); if (open_file->closePend) /* list could have changed */ goto refind_writable; /* else we simply continue to the next entry. Thus we do not loop on reopen errors. If we can not reopen the file, for example if we reconnected to a server with another client racing to delete or lock the file we would not make progress if we restarted before the beginning of the loop here. */ } } /* couldn't find useable FH with same pid, try any available */ if (!any_available) { any_available = true; goto refind_writable; } read_unlock(&GlobalSMBSeslock); 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 cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct inode *inode; struct cifsFileInfo *open_file; if (!mapping || !mapping->host) return -EFAULT; inode = page->mapping->host; cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; 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)); if (open_file) { bytes_written = cifs_write(open_file->pfile, write_data, to-from, &offset); atomic_dec(&open_file->wrtPending); /* 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 { cFYI(1, ("No writeable filehandles for inode")); rc = -EIO; } kunmap(page); return rc; } static int cifs_writepages(struct address_space *mapping, struct writeback_control *wbc) { struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned int bytes_to_write; unsigned int bytes_written; struct cifs_sb_info *cifs_sb; int done = 0; pgoff_t end; pgoff_t index; int range_whole = 0; struct kvec *iov; int len; int n_iov = 0; pgoff_t next; int nr_pages; __u64 offset = 0; struct cifsFileInfo *open_file; struct page *page; struct pagevec pvec; int rc = 0; int scanned = 0; int xid; cifs_sb = CIFS_SB(mapping->host->i_sb); /* * If wsize is smaller that 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 ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server)) if (cifs_sb->tcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) if (!experimEnabled) return generic_writepages(mapping, wbc); iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL); if (iov == NULL) return generic_writepages(mapping, wbc); /* * BB: Is this meaningful for a non-block-device file system? * If it is, we should test it again after we do I/O */ if (wbc->nonblocking && bdi_write_congested(bdi)) { wbc->encountered_congestion = 1; kfree(iov); return 0; } xid = GetXid(); pagevec_init(&pvec, 0); 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 = 1; scanned = 1; } retry: while (!done && (index <= end) && (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, PAGECACHE_TAG_DIRTY, min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) { int first; unsigned int i; first = -1; next = 0; n_iov = 0; bytes_to_write = 0; for (i = 0; i < nr_pages; i++) { page = pvec.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 (first < 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 = 1; 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) >= mapping->host->i_size) { done = 1; unlock_page(page); end_page_writeback(page); break; } /* * BB can we get rid of this? pages are held by pvec */ page_cache_get(page); len = min(mapping->host->i_size - page_offset(page), (loff_t)PAGE_CACHE_SIZE); /* reserve iov[0] for the smb header */ n_iov++; iov[n_iov].iov_base = kmap(page); iov[n_iov].iov_len = len; bytes_to_write += len; if (first < 0) { first = i; offset = page_offset(page); } next = page->index + 1; if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize) break; } if (n_iov) { /* Search for a writable handle every time we call * CIFSSMBWrite2. We can't rely on the last handle * we used to still be valid */ open_file = find_writable_file(CIFS_I(mapping->host)); if (!open_file) { cERROR(1, ("No writable handles for inode")); rc = -EBADF; } else { rc = CIFSSMBWrite2(xid, cifs_sb->tcon, open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, CIFS_LONG_OP); atomic_dec(&open_file->wrtPending); if (rc || bytes_written < bytes_to_write) { cERROR(1, ("Write2 ret %d, wrote %d", rc, bytes_written)); /* BB what if continued retry is requested via mount flags? */ if (rc == -ENOSPC) set_bit(AS_ENOSPC, &mapping->flags); else set_bit(AS_EIO, &mapping->flags); } else { cifs_stats_bytes_written(cifs_sb->tcon, bytes_written); } } for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; /* Should we also set page error on success rc but too little data written? */ /* BB investigate retry logic on temporary server crash cases and how recovery works when page marked as error */ if (rc) SetPageError(page); kunmap(page); unlock_page(page); end_page_writeback(page); page_cache_release(page); } if ((wbc->nr_to_write -= n_iov) <= 0) done = 1; index = next; } else /* Need to re-find the pages we skipped */ index = pvec.pages[0]->index + 1; pagevec_release(&pvec); } 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 = 1; index = 0; goto retry; } if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = index; FreeXid(xid); kfree(iov); return rc; } static int cifs_writepage(struct page *page, struct writeback_control *wbc) { int rc = -EFAULT; int xid; xid = GetXid(); /* BB add check for wbc flags */ page_cache_get(page); if (!PageUptodate(page)) cFYI(1, ("ppw - page not up to date")); /* * 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); rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); /* BB add check for error and Clearuptodate? */ unlock_page(page); end_page_writeback(page); page_cache_release(page); FreeXid(xid); 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; cFYI(1, ("write_end for page %p from pos %lld with %d bytes", 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); int xid; xid = GetXid(); /* 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(file, page_data + offset, copied, &pos); /* if (rc < 0) should we set writebehind rc? */ kunmap(page); FreeXid(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_fsync(struct file *file, struct dentry *dentry, int datasync) { int xid; int rc = 0; struct inode *inode = file->f_path.dentry->d_inode; xid = GetXid(); cFYI(1, ("Sync file - name: %s datasync: 0x%x", dentry->d_name.name, datasync)); rc = filemap_write_and_wait(inode->i_mapping); if (rc == 0) { rc = CIFS_I(inode)->write_behind_rc; CIFS_I(inode)->write_behind_rc = 0; } FreeXid(xid); return rc; } /* static void cifs_sync_page(struct page *page) { struct address_space *mapping; struct inode *inode; unsigned long index = page->index; unsigned int rpages = 0; int rc = 0; cFYI(1, ("sync page %p",page)); mapping = page->mapping; if (!mapping) return 0; inode = mapping->host; if (!inode) return; */ /* fill in rpages then result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ /* cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index)); #if 0 if (rc < 0) return rc; return 0; #endif } */ /* * 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->f_path.dentry->d_inode; int rc = 0; /* Rather than do the steps manually: lock the inode for writing loop through pages looking for write behind data (dirty pages) coalesce into contiguous 16K (or smaller) chunks to write to server send to server (prefer in parallel) deal with writebehind errors unlock inode for writing filemapfdatawrite appears easier for the time being */ rc = filemap_fdatawrite(inode->i_mapping); /* reset wb rc if we were able to write out dirty pages */ if (!rc) { rc = CIFS_I(inode)->write_behind_rc; CIFS_I(inode)->write_behind_rc = 0; } cFYI(1, ("Flush inode %p file %p rc %d", inode, file, rc)); return rc; } ssize_t cifs_user_read(struct file *file, char __user *read_data, size_t read_size, loff_t *poffset) { int rc = -EACCES; unsigned int bytes_read = 0; unsigned int total_read = 0; unsigned int current_read_size; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid; struct cifsFileInfo *open_file; char *smb_read_data; char __user *current_offset; struct smb_com_read_rsp *pSMBr; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); for (total_read = 0, current_offset = read_data; read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); rc = -EAGAIN; smb_read_data = NULL; while (rc == -EAGAIN) { int buf_type = CIFS_NO_BUFFER; if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, true); if (rc != 0) break; } rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, &smb_read_data, &buf_type); pSMBr = (struct smb_com_read_rsp *)smb_read_data; if (smb_read_data) { if (copy_to_user(current_offset, smb_read_data + 4 /* RFC1001 length field */ + le16_to_cpu(pSMBr->DataOffset), bytes_read)) rc = -EFAULT; if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } } if (rc || (bytes_read == 0)) { if (total_read) { break; } else { FreeXid(xid); return rc; } } else { cifs_stats_bytes_read(pTcon, bytes_read); *poffset += bytes_read; } } FreeXid(xid); return total_read; } static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *poffset) { int rc = -EACCES; unsigned int bytes_read = 0; unsigned int total_read; unsigned int current_read_size; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid; char *current_offset; struct cifsFileInfo *open_file; int buf_type = CIFS_NO_BUFFER; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); for (total_read = 0, current_offset = read_data; read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); /* For windows me and 9x we do not want to request more than it negotiated since it will refuse the read then */ if ((pTcon->ses) && !(pTcon->ses->capabilities & CAP_LARGE_FILES)) { current_read_size = min_t(const int, current_read_size, pTcon->ses->server->maxBuf - 128); } rc = -EAGAIN; while (rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, true); if (rc != 0) break; } rc = CIFSSMBRead(xid, pTcon, open_file->netfid, current_read_size, *poffset, &bytes_read, ¤t_offset, &buf_type); } if (rc || (bytes_read == 0)) { if (total_read) { break; } else { FreeXid(xid); return rc; } } else { cifs_stats_bytes_read(pTcon, total_read); *poffset += bytes_read; } } FreeXid(xid); return total_read; } int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct dentry *dentry = file->f_path.dentry; int rc, xid; xid = GetXid(); rc = cifs_revalidate(dentry); if (rc) { cFYI(1, ("Validation prior to mmap failed, error=%d", rc)); FreeXid(xid); return rc; } rc = generic_file_mmap(file, vma); FreeXid(xid); return rc; } static void cifs_copy_cache_pages(struct address_space *mapping, struct list_head *pages, int bytes_read, char *data, struct pagevec *plru_pvec) { struct page *page; char *target; while (bytes_read > 0) { if (list_empty(pages)) break; page = list_entry(pages->prev, struct page, lru); list_del(&page->lru); if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { page_cache_release(page); cFYI(1, ("Add page cache failed")); data += PAGE_CACHE_SIZE; bytes_read -= PAGE_CACHE_SIZE; continue; } target = kmap_atomic(page, KM_USER0); if (PAGE_CACHE_SIZE > bytes_read) { memcpy(target, data, bytes_read); /* zero the tail end of this partial page */ memset(target + bytes_read, 0, PAGE_CACHE_SIZE - bytes_read); bytes_read = 0; } else { memcpy(target, data, PAGE_CACHE_SIZE); bytes_read -= PAGE_CACHE_SIZE; } kunmap_atomic(target, KM_USER0); flush_dcache_page(page); SetPageUptodate(page); unlock_page(page); if (!pagevec_add(plru_pvec, page)) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) __pagevec_lru_add(plru_pvec); #else __pagevec_lru_add_file(plru_pvec); #endif data += PAGE_CACHE_SIZE; } return; } static int cifs_readpages(struct file *file, struct address_space *mapping, struct list_head *page_list, unsigned num_pages) { int rc = -EACCES; int xid; loff_t offset; struct page *page; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; unsigned int bytes_read = 0; unsigned int read_size, i; char *smb_read_data = NULL; struct smb_com_read_rsp *pSMBr; struct pagevec lru_pvec; struct cifsFileInfo *open_file; int buf_type = CIFS_NO_BUFFER; xid = GetXid(); if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } open_file = (struct cifsFileInfo *)file->private_data; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; pagevec_init(&lru_pvec, 0); cFYI(DBG2, ("rpages: num pages %d", num_pages)); for (i = 0; i < num_pages; ) { unsigned contig_pages; struct page *tmp_page; unsigned long expected_index; if (list_empty(page_list)) break; page = list_entry(page_list->prev, struct page, lru); offset = (loff_t)page->index << PAGE_CACHE_SHIFT; /* count adjacent pages that we will read into */ contig_pages = 0; expected_index = list_entry(page_list->prev, struct page, lru)->index; list_for_each_entry_reverse(tmp_page, page_list, lru) { if (tmp_page->index == expected_index) { contig_pages++; expected_index++; } else break; } if (contig_pages + i > num_pages) contig_pages = num_pages - i; /* for reads over a certain size could initiate async read ahead */ read_size = contig_pages * PAGE_CACHE_SIZE; /* Read size needs to be in multiples of one page */ read_size = min_t(const unsigned int, read_size, cifs_sb->rsize & PAGE_CACHE_MASK); cFYI(DBG2, ("rpages: read size 0x%x contiguous pages %d", read_size, contig_pages)); rc = -EAGAIN; while (rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, true); if (rc != 0) break; } rc = CIFSSMBRead(xid, pTcon, open_file->netfid, read_size, offset, &bytes_read, &smb_read_data, &buf_type); /* BB more RC checks ? */ if (rc == -EAGAIN) { if (smb_read_data) { if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } } } if ((rc < 0) || (smb_read_data == NULL)) { cFYI(1, ("Read error in readpages: %d", rc)); break; } else if (bytes_read > 0) { task_io_account_read(bytes_read); pSMBr = (struct smb_com_read_rsp *)smb_read_data; cifs_copy_cache_pages(mapping, page_list, bytes_read, smb_read_data + 4 /* RFC1001 hdr */ + le16_to_cpu(pSMBr->DataOffset), &lru_pvec); i += bytes_read >> PAGE_CACHE_SHIFT; cifs_stats_bytes_read(pTcon, bytes_read); if ((bytes_read & PAGE_CACHE_MASK) != bytes_read) { i++; /* account for partial page */ /* server copy of file can have smaller size than client */ /* BB do we need to verify this common case ? this case is ok - if we are at server EOF we will hit it on next read */ /* break; */ } } else { cFYI(1, ("No bytes read (%d) at offset %lld . " "Cleaning remaining pages from readahead list", bytes_read, offset)); /* BB turn off caching and do new lookup on file size at server? */ break; } if (smb_read_data) { if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } bytes_read = 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) pagevec_lru_add(&lru_pvec); #else pagevec_lru_add_file(&lru_pvec); #endif /* need to free smb_read_data buf before exit */ if (smb_read_data) { if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } FreeXid(xid); return rc; } static int cifs_readpage_worker(struct file *file, struct page *page, loff_t *poffset) { char *read_data; int rc; 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 cFYI(1, ("Bytes read %d", rc)); file->f_path.dentry->d_inode->i_atime = current_fs_time(file->f_path.dentry->d_inode->i_sb); if (PAGE_CACHE_SIZE > rc) memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc); flush_dcache_page(page); SetPageUptodate(page); rc = 0; io_error: kunmap(page); page_cache_release(page); 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; int xid; xid = GetXid(); if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } cFYI(1, ("readpage %p at offset %d 0x%x\n", page, (int)offset, (int)offset)); rc = cifs_readpage_worker(file, page, &offset); unlock_page(page); FreeXid(xid); return rc; } static int is_inode_writable(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; read_lock(&GlobalSMBSeslock); list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { read_unlock(&GlobalSMBSeslock); return 1; } } read_unlock(&GlobalSMBSeslock); 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; cFYI(1, ("write_begin from %lld len %d", (long long)pos, len)); page = __grab_cache_page(mapping, index); 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_I(mapping->host)->clientCanCacheRead) { 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; } 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, /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; /* * 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, /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; cifs-test-base/inode.c0000644000175000017500000015545111117756171014525 0ustar stevefstevef/* * fs/cifs/inode.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 #include #include #include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) { 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_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->tcon->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_dfs_referral) { 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; } } static void cifs_unix_info_to_inode(struct inode *inode, FILE_UNIX_BASIC_INFO *info, int force_uid_gid) { struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsInodeInfo *cifsInfo = CIFS_I(inode); __u64 num_of_bytes = le64_to_cpu(info->NumOfBytes); __u64 end_of_file = le64_to_cpu(info->EndOfFile); inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(info->LastAccessTime)); inode->i_mtime = cifs_NTtimeToUnix(le64_to_cpu(info->LastModificationTime)); inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(info->LastStatusChange)); inode->i_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. */ inode->i_mode &= ~S_IFMT; switch (le32_to_cpu(info->Type)) { case UNIX_FILE: inode->i_mode |= S_IFREG; break; case UNIX_SYMLINK: inode->i_mode |= S_IFLNK; break; case UNIX_DIR: inode->i_mode |= S_IFDIR; break; case UNIX_CHARDEV: inode->i_mode |= S_IFCHR; inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor), le64_to_cpu(info->DevMinor) & MINORMASK); break; case UNIX_BLOCKDEV: inode->i_mode |= S_IFBLK; inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor), le64_to_cpu(info->DevMinor) & MINORMASK); break; case UNIX_FIFO: inode->i_mode |= S_IFIFO; break; case UNIX_SOCKET: inode->i_mode |= S_IFSOCK; break; default: /* safest to call it a file if we do not know */ inode->i_mode |= S_IFREG; cFYI(1, ("unknown type %d", le32_to_cpu(info->Type))); break; } if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) && !force_uid_gid) inode->i_uid = cifs_sb->mnt_uid; else inode->i_uid = le64_to_cpu(info->Uid); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) && !force_uid_gid) inode->i_gid = cifs_sb->mnt_gid; else inode->i_gid = le64_to_cpu(info->Gid); inode->i_nlink = le64_to_cpu(info->Nlinks); spin_lock(&inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* * We can not safely change the file size here if the client * is writing to it due to potential races. */ i_size_write(inode, end_of_file); /* * 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 + num_of_bytes) >> 9; } spin_unlock(&inode->i_lock); } /* * Needed to setup inode 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 fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat, struct super_block *sb) { struct inode *pinode = NULL; memset(pfnd_dat, 0, sizeof(FILE_UNIX_BASIC_INFO)); /* __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); __le64 pfnd_dat->NumOfBytes = cpu_to_le64(0); __u64 UniqueId = 0; */ pfnd_dat->LastStatusChange = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->LastAccessTime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->LastModificationTime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->Type = cpu_to_le32(UNIX_DIR); pfnd_dat->Permissions = cpu_to_le64(S_IXUGO | S_IRWXU); pfnd_dat->Nlinks = cpu_to_le64(2); if (sb->s_root) pinode = sb->s_root->d_inode; if (pinode == NULL) return; /* fill in default values for the remaining based on root inode since we can not query the server for this inode info */ pfnd_dat->DevMajor = cpu_to_le64(MAJOR(pinode->i_rdev)); pfnd_dat->DevMinor = cpu_to_le64(MINOR(pinode->i_rdev)); pfnd_dat->Uid = cpu_to_le64(pinode->i_uid); pfnd_dat->Gid = cpu_to_le64(pinode->i_gid); } int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *full_path, struct super_block *sb, int xid) { int rc = 0; FILE_UNIX_BASIC_INFO find_data; struct cifsTconInfo *pTcon; struct inode *inode; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); bool is_dfs_referral = false; struct cifsInodeInfo *cifsInfo; __u64 num_of_bytes; __u64 end_of_file; pTcon = cifs_sb->tcon; cFYI(1, ("Getting info on %s", full_path)); /* could have done a find first instead but this returns more info */ rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc == -EREMOTE && !is_dfs_referral) { is_dfs_referral = true; cFYI(DBG2, ("DFS ref")); /* for DFS, server does not give us real inode data */ fill_fake_finddataunix(&find_data, sb); rc = 0; } else if (rc) goto cgiiu_exit; num_of_bytes = le64_to_cpu(find_data.NumOfBytes); end_of_file = le64_to_cpu(find_data.EndOfFile); /* get new inode */ if (*pinode == NULL) { *pinode = new_inode(sb); if (*pinode == NULL) { rc = -ENOMEM; goto cgiiu_exit; } /* Is an i_ino of zero legal? */ /* note ino incremented to unique num in new_inode */ /* Are there sanity checks we can use to ensure that the server is really filling in that field? */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) (*pinode)->i_ino = (unsigned long)find_data.UniqueId; if (sb->s_flags & MS_NOATIME) (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; insert_inode_hash(*pinode); } inode = *pinode; cifsInfo = CIFS_I(inode); cFYI(1, ("Old time %ld", cifsInfo->time)); cifsInfo->time = jiffies; cFYI(1, ("New time %ld", cifsInfo->time)); /* this is ok to set on every inode revalidate */ atomic_set(&cifsInfo->inUse, 1); cifs_unix_info_to_inode(inode, &find_data, 0); if (num_of_bytes < end_of_file) cFYI(1, ("allocation size less than end of file")); cFYI(1, ("Size %ld and blocks %llu", (unsigned long) inode->i_size, (unsigned long long)inode->i_blocks)); cifs_set_ops(inode, is_dfs_referral); cgiiu_exit: return rc; } static int decode_sfu_inode(struct inode *inode, __u64 size, const unsigned char *path, struct cifs_sb_info *cifs_sb, int xid) { int rc; int oplock = 0; __u16 netfid; struct cifsTconInfo *pTcon = cifs_sb->tcon; char buf[24]; unsigned int bytes_read; char *pbuf; pbuf = buf; if (size == 0) { inode->i_mode |= S_IFIFO; return 0; } else if (size < 8) { return -EINVAL; /* EOPNOTSUPP? */ } rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, 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 */ rc = CIFSSMBRead(xid, pTcon, netfid, 24 /* length */, 0 /* offset */, &bytes_read, &pbuf, &buf_type); if ((rc == 0) && (bytes_read >= 8)) { if (memcmp("IntxBLK", pbuf, 8) == 0) { cFYI(1, ("Block device")); inode->i_mode |= S_IFBLK; 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)); inode->i_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxCHR", pbuf, 8) == 0) { cFYI(1, ("Char device")); inode->i_mode |= S_IFCHR; 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)); inode->i_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxLNK", pbuf, 7) == 0) { cFYI(1, ("Symlink")); inode->i_mode |= S_IFLNK; } else { inode->i_mode |= S_IFREG; /* file? */ rc = -EOPNOTSUPP; } } else { inode->i_mode |= S_IFREG; /* then it is a file */ rc = -EOPNOTSUPP; /* or some unknown SFU type */ } CIFSSMBClose(xid, pTcon, netfid); } return rc; } #define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ static int get_sfu_mode(struct inode *inode, const unsigned char *path, struct cifs_sb_info *cifs_sb, int xid) { #ifdef CONFIG_CIFS_XATTR ssize_t rc; char ea_value[4]; __u32 mode; rc = CIFSSMBQueryEA(xid, cifs_sb->tcon, path, "SETFILEBITS", ea_value, 4 /* size of buf */, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc < 0) return (int)rc; else if (rc > 3) { mode = le32_to_cpu(*((__le32 *)ea_value)); inode->i_mode &= ~SFBITS_MASK; cFYI(1, ("special bits 0%o org mode 0%o", mode, inode->i_mode)); inode->i_mode = (mode & SFBITS_MASK) | inode->i_mode; cFYI(1, ("special mode bits 0%o", mode)); return 0; } else { return 0; } #else return -EOPNOTSUPP; #endif } /* * Needed to setup inode 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 fill_fake_finddata(FILE_ALL_INFO *pfnd_dat, struct super_block *sb) { memset(pfnd_dat, 0, sizeof(FILE_ALL_INFO)); /* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0); __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); __u8 pfnd_dat->DeletePending = 0; __u8 pfnd_data->Directory = 0; __le32 pfnd_dat->EASize = 0; __u64 pfnd_dat->IndexNumber = 0; __u64 pfnd_dat->IndexNumber1 = 0; */ pfnd_dat->CreationTime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->LastAccessTime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->LastWriteTime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY); pfnd_dat->NumberOfLinks = cpu_to_le32(2); } int cifs_get_inode_info(struct inode **pinode, const unsigned char *full_path, FILE_ALL_INFO *pfindData, struct super_block *sb, int xid, const __u16 *pfid) { int rc = 0; __u32 attr; struct cifsInodeInfo *cifsInfo; struct cifsTconInfo *pTcon; struct inode *inode; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *buf = NULL; bool adjustTZ = false; bool is_dfs_referral = false; umode_t default_mode; pTcon = cifs_sb->tcon; cFYI(1, ("Getting info on %s", full_path)); if ((pfindData == NULL) && (*pinode != NULL)) { if (CIFS_I(*pinode)->clientCanCacheRead) { cFYI(1, ("No need to revalidate cached inode sizes")); return rc; } } /* if file info not passed in then get it from server */ if (pfindData == NULL) { buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (buf == NULL) return -ENOMEM; pfindData = (FILE_ALL_INFO *)buf; /* could do find first instead but this returns more info */ rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData, 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, pTcon, full_path, pfindData, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); adjustTZ = true; } } /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ if (rc == -EREMOTE) { is_dfs_referral = true; fill_fake_finddata(pfindData, sb); rc = 0; } else if (rc) goto cgii_exit; attr = le32_to_cpu(pfindData->Attributes); /* get new inode */ if (*pinode == NULL) { *pinode = new_inode(sb); if (*pinode == NULL) { rc = -ENOMEM; goto cgii_exit; } /* 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? */ /* We can not use the IndexNumber field by default from Windows or Samba (in ALL_INFO buf) but we can request it explicitly. It may not be unique presumably if the server has multiple devices mounted under one share */ /* There may be higher info levels that work but are there Windows server or network appliances for which IndexNumber field is not guaranteed unique? */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { int rc1 = 0; __u64 inode_num; rc1 = CIFSGetSrvInodeNumber(xid, pTcon, full_path, &inode_num, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc1) { cFYI(1, ("GetSrvInodeNum rc %d", rc1)); /* BB EOPNOSUPP disable SERVER_INUM? */ } else /* do we need cast or hash to ino? */ (*pinode)->i_ino = inode_num; } /* else ino incremented to unique num in new_inode*/ if (sb->s_flags & MS_NOATIME) (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; insert_inode_hash(*pinode); } inode = *pinode; cifsInfo = CIFS_I(inode); cifsInfo->cifsAttrs = attr; cifsInfo->delete_pending = pfindData->DeletePending ? true : false; cFYI(1, ("Old time %ld", cifsInfo->time)); cifsInfo->time = jiffies; cFYI(1, ("New time %ld", cifsInfo->time)); /* blksize needs to be multiple of two. So safer to default to blksize and blkbits set in superblock so 2**blkbits and blksize will match rather than setting to: (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ /* Linux can not store file creation time so ignore it */ if (pfindData->LastAccessTime) inode->i_atime = cifs_NTtimeToUnix (le64_to_cpu(pfindData->LastAccessTime)); else /* do not need to use current_fs_time - time not stored */ inode->i_atime = CURRENT_TIME; inode->i_mtime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); cFYI(DBG2, ("Attributes came in as 0x%x", attr)); if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; } /* get default inode mode */ if (attr & ATTR_DIRECTORY) default_mode = cifs_sb->mnt_dir_mode; else default_mode = cifs_sb->mnt_file_mode; /* set permission bits */ if (atomic_read(&cifsInfo->inUse) == 0 || (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) inode->i_mode = default_mode; else { /* just reenable write bits if !ATTR_READONLY */ if ((inode->i_mode & S_IWUGO) == 0 && (attr & ATTR_READONLY) == 0) inode->i_mode |= (S_IWUGO & default_mode); inode->i_mode &= ~S_IFMT; } /* clear write bits if ATTR_READONLY is set */ if (attr & ATTR_READONLY) inode->i_mode &= ~S_IWUGO; /* set inode type */ if ((attr & ATTR_SYSTEM) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) { /* no need to fix endianness on 0 */ if (pfindData->EndOfFile == 0) inode->i_mode |= S_IFIFO; else if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile), full_path, cifs_sb, xid)) cFYI(1, ("unknown SFU file type\n")); } else { if (attr & ATTR_DIRECTORY) inode->i_mode |= S_IFDIR; else inode->i_mode |= S_IFREG; } spin_lock(&inode->i_lock); if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) { /* can not safely shrink the file size here if the client is writing to it due to potential races */ i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); /* 512 bytes (2**9) is the fake blocksize that must be used for this calculation */ inode->i_blocks = (512 - 1 + le64_to_cpu( pfindData->AllocationSize)) >> 9; } spin_unlock(&inode->i_lock); inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); /* BB fill in uid and gid here? with help from winbind? or retrieve from NTFS stream extended attribute */ #ifdef CONFIG_CIFS_EXPERIMENTAL /* fill in 0777 bits from ACL */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { cFYI(1, ("Getting mode bits from ACL")); acl_to_uid_mode(inode, full_path, pfid); } #endif if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { /* fill in remaining high mode bits e.g. SUID, VTX */ get_sfu_mode(inode, full_path, cifs_sb, xid); } else if (atomic_read(&cifsInfo->inUse) == 0) { inode->i_uid = cifs_sb->mnt_uid; inode->i_gid = cifs_sb->mnt_gid; /* set so we do not keep refreshing these fields with bad data after user has changed them in memory */ atomic_set(&cifsInfo->inUse, 1); } cifs_set_ops(inode, is_dfs_referral); cgii_exit: kfree(buf); return rc; } static const struct inode_operations cifs_ipc_inode_ops = { .lookup = cifs_lookup, }; static char *build_path_to_root(struct cifs_sb_info *cifs_sb) { int pplen = cifs_sb->prepathlen; int dfsplen; char *full_path = NULL; /* if no prefix path, simply set path to the root of share to "" */ if (pplen == 0) { full_path = kmalloc(1, GFP_KERNEL); if (full_path) full_path[0] = 0; return full_path; } if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) dfsplen = strnlen(cifs_sb->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, cifs_sb->tcon->treeName, dfsplen); /* switch slash direction in prepath depending on whether * windows or posix style path names */ 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] = '/'; } } } strncpy(full_path + dfsplen, cifs_sb->prepath, pplen); full_path[dfsplen + pplen] = 0; /* add trailing null */ return full_path; } /* gets root inode */ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) { int xid; struct cifs_sb_info *cifs_sb; struct inode *inode; long rc; char *full_path; inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; cifs_sb = CIFS_SB(inode->i_sb); full_path = build_path_to_root(cifs_sb); if (full_path == NULL) return ERR_PTR(-ENOMEM); xid = GetXid(); if (cifs_sb->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); if (rc && cifs_sb->tcon->ipc) { cFYI(1, ("ipc connection - fake read inode")); inode->i_mode |= S_IFDIR; inode->i_nlink = 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; } else if (rc) { kfree(full_path); _FreeXid(xid); iget_failed(inode); return ERR_PTR(rc); } unlock_new_inode(inode); kfree(full_path); /* can not call macro FreeXid here since in a void func * TODO: This is no longer true */ _FreeXid(xid); return inode; } static int cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, char *full_path, __u32 dosattr) { int rc; int oplock = 0; __u16 netfid; __u32 netpid; bool set_time = false; struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsTconInfo *pTcon = cifs_sb->tcon; FILE_BASIC_INFO info_buf; 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)) { cFYI(1, ("CIFS - CTIME changed")); 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); /* * If the file is already open for write, just use that fileid */ open_file = find_writable_file(cifsInode); if (open_file) { netfid = open_file->netfid; netpid = open_file->pid; goto set_via_filehandle; } /* * NT4 apparently returns success on this call, but it doesn't * really work. */ if (!(pTcon->ses->flags & CIFS_SES_NT4)) { rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, &info_buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc == 0) { cifsInode->cifsAttrs = dosattr; goto out; } else if (rc != -EOPNOTSUPP && rc != -EINVAL) goto out; } cFYI(1, ("calling SetFileInfo since SetPathInfo for " "times not supported by this server")); rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, 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, pTcon, &info_buf, netfid, netpid); if (!rc) cifsInode->cifsAttrs = dosattr; if (open_file == NULL) CIFSSMBClose(xid, pTcon, netfid); else atomic_dec(&open_file->wrtPending); out: return rc; } /* * 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. */ static int cifs_rename_pending_delete(char *full_path, struct dentry *dentry, 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 cifsTconInfo *tcon = cifs_sb->tcon; __u32 dosattr, origattr; FILE_BASIC_INFO *info_buf = NULL; rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, DELETE|FILE_WRITE_ATTRIBUTES, 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 != 0) 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); 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; } int cifs_unlink(struct inode *dir, struct dentry *dentry) { int rc = 0; int xid; char *full_path = NULL; struct inode *inode = dentry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *tcon = cifs_sb->tcon; struct iattr *attrs = NULL; __u32 dosattr = 0, origattr = 0; cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry)); xid = GetXid(); /* 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) { FreeXid(xid); return -ENOMEM; } if ((tcon->ses->capabilities & CAP_UNIX) && (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); cFYI(1, ("posix del rc %d", rc)); if ((rc == 0) || (rc == -ENOENT)) goto psx_del_no_retry; } retry_std_delete: rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); psx_del_no_retry: if (!rc) { if (inode) drop_nlink(inode); } else if (rc == -ENOENT) { d_drop(dentry); } else if (rc == -ETXTBSY) { rc = cifs_rename_pending_delete(full_path, dentry, xid); if (rc == 0) drop_nlink(inode); } else if (rc == -EACCES && dosattr == 0) { attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); if (attrs == NULL) { rc = -ENOMEM; goto out_reval; } /* try to reset dos attributes */ origattr = cifsInode->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) { cifsInode = CIFS_I(inode); cifsInode->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); cifsInode = CIFS_I(dir); CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ kfree(full_path); kfree(attrs); FreeXid(xid); return rc; } static void posix_fill_in_inode(struct inode *tmp_inode, FILE_UNIX_BASIC_INFO *pData, int isNewInode) { struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode); loff_t local_size; struct timespec local_mtime; cifsInfo->time = jiffies; atomic_inc(&cifsInfo->inUse); /* save mtime and size */ local_mtime = tmp_inode->i_mtime; local_size = tmp_inode->i_size; cifs_unix_info_to_inode(tmp_inode, pData, 1); cifs_set_ops(tmp_inode, false); if (!S_ISREG(tmp_inode->i_mode)) return; /* * No sense invalidating pages for new inode * since we we have not started caching * readahead file data yet. */ if (isNewInode) return; if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { cFYI(1, ("inode exists but unchanged")); } else { /* file may have changed on server */ cFYI(1, ("invalidate inode, readdir detected change")); invalidate_remote_inode(tmp_inode); } } int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) { int rc = 0, tmprc; int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode)); xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } if ((pTcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(pTcon->fsUnixInfo.Capability))) { u32 oplock = 0; FILE_UNIX_BASIC_INFO *pInfo = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); if (pInfo == NULL) { rc = -ENOMEM; goto mkdir_out; } mode &= ~current->fs->umask; rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, NULL /* netfid */, pInfo, &oplock, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc == -EOPNOTSUPP) { kfree(pInfo); goto mkdir_retry_old; } else if (rc) { cFYI(1, ("posix mkdir returned 0x%x", rc)); d_drop(direntry); } else { if (pInfo->Type == cpu_to_le32(-1)) { /* no return info, go query for it */ kfree(pInfo); goto mkdir_get_info; } /*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need to set uid/gid */ inc_nlink(inode); if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; newinode = new_inode(inode->i_sb); if (newinode == NULL) { kfree(pInfo); goto mkdir_get_info; } /* Is an i_ino of zero legal? */ /* Are there sanity checks we can use to ensure that the server is really filling in that field? */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { newinode->i_ino = (unsigned long)pInfo->UniqueId; } /* note ino incremented to unique num in new_inode */ if (inode->i_sb->s_flags & MS_NOATIME) newinode->i_flags |= S_NOATIME | S_NOCMTIME; newinode->i_nlink = 2; insert_inode_hash(newinode); d_instantiate(direntry, newinode); /* we already checked in POSIXCreate whether frame was long enough */ posix_fill_in_inode(direntry->d_inode, pInfo, 1 /* NewInode */); #ifdef CONFIG_CIFS_DEBUG2 cFYI(1, ("instantiated dentry %p %s to inode %p", direntry, direntry->d_name.name, newinode)); if (newinode->i_nlink != 2) cFYI(1, ("unexpected number of links %d", newinode->i_nlink)); #endif } kfree(pInfo); goto mkdir_out; } mkdir_retry_old: /* BB add setting the equivalent of mode via CreateX w/ACLs */ rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { cFYI(1, ("cifs_mkdir returned 0x%x", rc)); d_drop(direntry); } else { mkdir_get_info: inc_nlink(inode); 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 (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); /* setting nlink not necessary except in cases where we * failed to get it from the server or was set bogus */ if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2)) direntry->d_inode->i_nlink = 2; mode &= ~current->fs->umask; /* must turn on setgid bit if parent dir has it */ if (inode->i_mode & S_ISGID) mode |= S_ISGID; if (pTcon->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 = (__u64)current->fsuid; if (inode->i_mode & S_ISGID) args.gid = (__u64)inode->i_gid; else args.gid = (__u64)current->fsgid; } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; } CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } else { if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && (mode & S_IWUGO) == 0) { FILE_BASIC_INFO pInfo; struct cifsInodeInfo *cifsInode; u32 dosattrs; memset(&pInfo, 0, sizeof(pInfo)); cifsInode = CIFS_I(newinode); dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; pInfo.Attributes = cpu_to_le32(dosattrs); tmprc = CIFSSMBSetPathInfo(xid, pTcon, full_path, &pInfo, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (tmprc == 0) cifsInode->cifsAttrs = dosattrs; } if (direntry->d_inode) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) direntry->d_inode->i_mode = (mode | S_IFDIR); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { direntry->d_inode->i_uid = current->fsuid; if (inode->i_mode & S_ISGID) direntry->d_inode->i_gid = inode->i_gid; else direntry->d_inode->i_gid = current->fsgid; } } } } mkdir_out: kfree(full_path); FreeXid(xid); return rc; } int cifs_rmdir(struct inode *inode, struct dentry *direntry) { int rc = 0; int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; struct cifsInodeInfo *cifsInode; cFYI(1, ("cifs_rmdir, inode = 0x%p", inode)); xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (!rc) { drop_nlink(inode); 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); cifsInode->time = 0; /* force revalidate to go get info when needed */ direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb); kfree(full_path); FreeXid(xid); return rc; } static int cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, struct dentry *to_dentry, const char *toPath) { struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); struct cifsTconInfo *pTcon = cifs_sb->tcon; __u16 srcfid; int oplock, rc; /* try path-based rename first */ rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* * 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) return rc; /* open the file to be renamed -- we need DELETE perms */ rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE, 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, pTcon, srcfid, (const char *) to_dentry->d_name.name, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); CIFSSMBClose(xid, pTcon, srcfid); } return rc; } int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, struct inode *target_dir, struct dentry *target_dentry) { char *fromName = NULL; char *toName = NULL; struct cifs_sb_info *cifs_sb_source; struct cifs_sb_info *cifs_sb_target; struct cifsTconInfo *tcon; FILE_UNIX_BASIC_INFO *info_buf_source = NULL; FILE_UNIX_BASIC_INFO *info_buf_target; int xid, rc, tmprc; cifs_sb_target = CIFS_SB(target_dir->i_sb); cifs_sb_source = CIFS_SB(source_dir->i_sb); tcon = cifs_sb_source->tcon; xid = GetXid(); /* * BB: this might be allowed if same server, but different share. * Consider adding support for this */ if (tcon != cifs_sb_target->tcon) { rc = -EXDEV; goto cifs_rename_exit; } /* * we already have the rename sem so we do not need to * grab it again here to protect the path integrity */ fromName = build_path_from_dentry(source_dentry); if (fromName == NULL) { rc = -ENOMEM; goto cifs_rename_exit; } toName = build_path_from_dentry(target_dentry); if (toName == NULL) { rc = -ENOMEM; goto cifs_rename_exit; } rc = cifs_do_rename(xid, source_dentry, fromName, target_dentry, toName); 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, fromName, info_buf_source, cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (tmprc != 0) goto unlink_target; tmprc = CIFSSMBUnixQPathInfo(xid, tcon, toName, info_buf_target, cifs_sb_target->local_nls, /* remap based on source sb */ cifs_sb_source->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: if ((rc == -EACCES) || (rc == -EEXIST)) { tmprc = cifs_unlink(target_dir, target_dentry); if (tmprc) goto cifs_rename_exit; rc = cifs_do_rename(xid, source_dentry, fromName, target_dentry, toName); } cifs_rename_exit: kfree(info_buf_source); kfree(fromName); kfree(toName); FreeXid(xid); return rc; } int cifs_revalidate(struct dentry *direntry) { int xid; int rc = 0, wbrc = 0; char *full_path; struct cifs_sb_info *cifs_sb; struct cifsInodeInfo *cifsInode; loff_t local_size; struct timespec local_mtime; bool invalidate_inode = false; if (direntry->d_inode == NULL) return -ENOENT; cifsInode = CIFS_I(direntry->d_inode); if (cifsInode == NULL) return -ENOENT; /* no sense revalidating inode info on file that no one can write */ if (CIFS_I(direntry->d_inode)->clientCanCacheRead) return rc; xid = GetXid(); cifs_sb = CIFS_SB(direntry->d_sb); /* can not safely grab the rename sem here if rename calls revalidate since that would deadlock */ full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " "jiffies %ld", full_path, direntry->d_inode, direntry->d_inode->i_count.counter, direntry, direntry->d_time, jiffies)); if (cifsInode->time == 0) { /* was set to zero previously to force revalidate */ } else if (time_before(jiffies, cifsInode->time + HZ) && lookupCacheEnabled) { if ((S_ISREG(direntry->d_inode->i_mode) == 0) || (direntry->d_inode->i_nlink == 1)) { kfree(full_path); FreeXid(xid); return rc; } else { cFYI(1, ("Have to revalidate file due to hardlinks")); } } /* save mtime and size */ local_mtime = direntry->d_inode->i_mtime; local_size = direntry->d_inode->i_size; if (cifs_sb->tcon->unix_ext) { rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path, direntry->d_sb, xid); if (rc) { cFYI(1, ("error on getting revalidate info %d", rc)); /* if (rc != -ENOENT) rc = 0; */ /* BB should we cache info on certain errors? */ } } else { rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL, direntry->d_sb, xid, NULL); if (rc) { cFYI(1, ("error on getting revalidate info %d", rc)); /* if (rc != -ENOENT) rc = 0; */ /* BB should we cache info on certain errors? */ } } /* should we remap certain errors, access denied?, to zero */ /* if not oplocked, we invalidate inode pages if mtime or file size had changed on server */ if (timespec_equal(&local_mtime, &direntry->d_inode->i_mtime) && (local_size == direntry->d_inode->i_size)) { cFYI(1, ("cifs_revalidate - inode unchanged")); } else { /* file may have changed on server */ if (cifsInode->clientCanCacheRead) { /* no need to invalidate inode pages since we were the only ones who could have modified the file and the server copy is staler than ours */ } else { invalidate_inode = true; } } /* can not grab this sem since kernel filesys locking documentation indicates i_mutex may be taken by the kernel on lookup and rename which could deadlock if we grab the i_mutex here as well */ /* mutex_lock(&direntry->d_inode->i_mutex);*/ /* need to write out dirty pages here */ if (direntry->d_inode->i_mapping) { /* do we need to lock inode until after invalidate completes below? */ wbrc = filemap_fdatawrite(direntry->d_inode->i_mapping); if (wbrc) CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; } if (invalidate_inode) { /* shrink_dcache not necessary now that cifs dentry ops are exported for negative dentries */ /* if (S_ISDIR(direntry->d_inode->i_mode)) shrink_dcache_parent(direntry); */ if (S_ISREG(direntry->d_inode->i_mode)) { if (direntry->d_inode->i_mapping) { wbrc = filemap_fdatawait(direntry->d_inode->i_mapping); if (wbrc) CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; } /* may eventually have to do this for open files too */ if (list_empty(&(cifsInode->openFileList))) { /* changed on server - flush read ahead pages */ cFYI(1, ("Invalidating read ahead data on " "closed file")); invalidate_remote_inode(direntry->d_inode); } } } /* mutex_unlock(&direntry->d_inode->i_mutex); */ kfree(full_path); FreeXid(xid); return rc; } int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { int err = cifs_revalidate(dentry); if (!err) { generic_fillattr(dentry->d_inode, stat); stat->blksize = CIFS_MAX_MSGSIZE; } return err; } 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 int cifs_vmtruncate(struct inode *inode, loff_t offset) { struct address_space *mapping = inode->i_mapping; unsigned long limit; spin_lock(&inode->i_lock); if (inode->i_size < offset) goto do_expand; /* * truncation of in-use swapfiles is disallowed - it would cause * subsequent swapout to scribble on the now-freed blocks. */ if (IS_SWAPFILE(inode)) { spin_unlock(&inode->i_lock); goto out_busy; } i_size_write(inode, offset); spin_unlock(&inode->i_lock); /* * unmap_mapping_range is called twice, first simply for efficiency * so that truncate_inode_pages does fewer single-page unmaps. However * after this first call, and before truncate_inode_pages finishes, * it is possible for private pages to be COWed, which remain after * truncate_inode_pages finishes, hence the second unmap_mapping_range * call must be made for correctness. */ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); truncate_inode_pages(mapping, offset); unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); goto out_truncate; do_expand: limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; if (limit != RLIM_INFINITY && offset > limit) { spin_unlock(&inode->i_lock); goto out_sig; } if (offset > inode->i_sb->s_maxbytes) { spin_unlock(&inode->i_lock); goto out_big; } i_size_write(inode, offset); spin_unlock(&inode->i_lock); out_truncate: if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); return 0; out_sig: send_sig(SIGXFSZ, current, 0); out_big: return -EFBIG; out_busy: return -ETXTBSY; } static int cifs_set_file_size(struct inode *inode, struct iattr *attrs, 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 cifsTconInfo *pTcon = cifs_sb->tcon; /* * 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); if (open_file) { __u16 nfid = open_file->netfid; __u32 npid = open_file->pid; rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, false); atomic_dec(&open_file->wrtPending); cFYI(1, ("SetFSize for attrs rc = %d", rc)); if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { unsigned int bytes_written; rc = CIFSSMBWrite(xid, pTcon, nfid, 0, attrs->ia_size, &bytes_written, NULL, NULL, 1); cFYI(1, ("Wrt seteof rc %d", rc)); } } else rc = -EINVAL; if (rc != 0) { /* 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 */ rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, false, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc)); if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { __u16 netfid; int oplock = 0; rc = SMBLegacyOpen(xid, pTcon, 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; rc = CIFSSMBWrite(xid, pTcon, netfid, 0, attrs->ia_size, &bytes_written, NULL, NULL, 1); cFYI(1, ("wrt seteof rc %d", rc)); CIFSSMBClose(xid, pTcon, netfid); } } } if (rc == 0) { rc = cifs_vmtruncate(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; 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 cifsTconInfo *pTcon = cifs_sb->tcon; struct cifs_unix_set_info_args *args = NULL; cFYI(1, ("setattr_unix on file %s attrs->ia_valid=0x%x", direntry->d_name.name, attrs->ia_valid)); xid = GetXid(); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { /* check if we have permission to change attrs */ rc = inode_change_ok(inode, attrs); if (rc < 0) goto out; else rc = 0; } full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; goto out; } if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { /* Flush data before changing file size or changing the last write time of the file on the server. If the flush returns error, store it to report later and continue. BB: This should be smarter. Why bother flushing pages that will be truncated anyway? Also, should we error out here if the flush returns error? */ rc = filemap_write_and_wait(inode->i_mapping); if (rc != 0) { cifsInode->write_behind_rc = 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 = NO_CHANGE_64; if (attrs->ia_valid & ATTR_GID) args->gid = attrs->ia_gid; else args->gid = NO_CHANGE_64; 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; rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path, args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (!rc) rc = inode_setattr(inode, attrs); out: kfree(args); kfree(full_path); FreeXid(xid); return rc; } static int cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) { int xid; 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 = GetXid(); cFYI(1, ("setattr on file %s attrs->iavalid 0x%x", direntry->d_name.name, attrs->ia_valid)); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { /* check if we have permission to change attrs */ rc = inode_change_ok(inode, attrs); if (rc < 0) { FreeXid(xid); return rc; } else rc = 0; } full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { /* Flush data before changing file size or changing the last write time of the file on the server. If the flush returns error, store it to report later and continue. BB: This should be smarter. Why bother flushing pages that will be truncated anyway? Also, should we error out here if the flush returns error? */ rc = filemap_write_and_wait(inode->i_mapping); if (rc != 0) { cifsInode->write_behind_rc = 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; } /* * Without unix extensions we can't send ownership changes to the * server, so silently ignore them. This is consistent with how * local DOS/Windows filesystems behave (VFAT, NTFS, etc). With * CIFSACL support + proper Windows to Unix idmapping, we may be * able to support this in the future. */ 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) { cFYI(1, ("Mode changed to 0%o", attrs->ia_mode)); mode = attrs->ia_mode; } if (attrs->ia_valid & ATTR_MODE) { rc = 0; #ifdef CONFIG_CIFS_EXPERIMENTAL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) rc = mode_to_acl(inode, full_path, mode); else #endif 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) rc = inode_setattr(inode, attrs); cifs_setattr_exit: kfree(full_path); FreeXid(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 cifsTconInfo *pTcon = cifs_sb->tcon; 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) { cFYI(1, ("In cifs_delete_inode, inode = 0x%p", inode)); /* may have to add back in if and when safe distributed caching of directories added e.g. via FindNotify */ } #endif cifs-test-base/ioctl.c0000644000175000017500000000554711117756171014541 0ustar stevefstevef/* * 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 #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" #include "cifsfs.h" #define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2) long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) { struct inode *inode = filep->f_dentry->d_inode; int rc = -ENOTTY; /* strange error - but the precedent */ int xid; struct cifs_sb_info *cifs_sb; #ifdef CONFIG_CIFS_POSIX __u64 ExtAttrBits = 0; __u64 ExtAttrMask = 0; __u64 caps; struct cifsTconInfo *tcon; struct cifsFileInfo *pSMBFile = (struct cifsFileInfo *)filep->private_data; #endif /* CONFIG_CIFS_POSIX */ xid = GetXid(); cFYI(1, ("ioctl file %p cmd %u arg %lu", filep, command, arg)); cifs_sb = CIFS_SB(inode->i_sb); #ifdef CONFIG_CIFS_POSIX tcon = cifs_sb->tcon; if (tcon) caps = le64_to_cpu(tcon->fsUnixInfo.Capability); else { rc = -EIO; FreeXid(xid); return -EIO; } #endif /* CONFIG_CIFS_POSIX */ switch (command) { case CIFS_IOC_CHECKUMOUNT: cFYI(1, ("User unmount attempted")); if (cifs_sb->mnt_uid == current->uid) rc = 0; else { rc = -EACCES; cFYI(1, ("uids do not match")); } break; #ifdef CONFIG_CIFS_POSIX case FS_IOC_GETFLAGS: if (CIFS_UNIX_EXTATTR_CAP & caps) { if (pSMBFile == NULL) break; rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid, &ExtAttrBits, &ExtAttrMask); if (rc == 0) rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, (int __user *)arg); } break; case FS_IOC_SETFLAGS: if (CIFS_UNIX_EXTATTR_CAP & caps) { if (get_user(ExtAttrBits, (int __user *)arg)) { rc = -EFAULT; break; } if (pSMBFile == NULL) break; /* rc= CIFSGetExtAttr(xid,tcon,pSMBFile->netfid, extAttrBits, &ExtAttrMask);*/ } cFYI(1, ("set flags not implemented yet")); break; #endif /* CONFIG_CIFS_POSIX */ default: cFYI(1, ("unsupported ioctl")); break; } FreeXid(xid); return rc; } cifs-test-base/Kconfig0000644000175000017500000001411311117756171014553 0ustar stevefstevefconfig CIFS tristate "CIFS support (advanced network filesystem, SMBFS successor)" depends on INET select NLS 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, 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 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 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_DEBUG2 bool "Enable additional CIFS debugging routines" depends on CIFS 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_EXPERIMENTAL bool "CIFS Experimental Features (EXPERIMENTAL)" depends on CIFS && EXPERIMENTAL help Enables cifs features under testing. These features are experimental and currently include DFS support and directory change notification ie fcntl(F_DNOTIFY), as well as the upcall mechanism which will be used for Kerberos session negotiation and uid remapping. Some of these features also may depend on setting a value of 1 to the pseudo-file /proc/fs/cifs/Experimental (which is disabled by default). See the file fs/cifs/README for more details. If unsure, say N. config CIFS_DFS_UPCALL bool "DFS feature support (EXPERIMENTAL)" depends on CIFS_EXPERIMENTAL depends on KEYS help 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. cifs-test-base/link.c0000644000175000017500000002116411117756171014355 0ustar stevefstevef/* * 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 #include #include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" int cifs_hardlink(struct dentry *old_file, struct inode *inode, struct dentry *direntry) { int rc = -EACCES; int xid; char *fromName = NULL; char *toName = NULL; struct cifs_sb_info *cifs_sb_target; struct cifsTconInfo *pTcon; struct cifsInodeInfo *cifsInode; xid = GetXid(); cifs_sb_target = CIFS_SB(inode->i_sb); pTcon = cifs_sb_target->tcon; /* No need to check for cross device links since server will do that BB note DFS case in future though (when we may have to check) */ fromName = build_path_from_dentry(old_file); toName = build_path_from_dentry(direntry); if ((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; goto cifs_hl_exit; } /* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ if (pTcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, cifs_sb_target->local_nls, cifs_sb_target->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else { rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, cifs_sb_target->local_nls, cifs_sb_target->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 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) { old_file->d_inode->i_nlink++; /* 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(fromName); kfree(toName); FreeXid(xid); return rc; } void * cifs_follow_link(struct dentry *direntry, struct nameidata *nd) { struct inode *inode = direntry->d_inode; int rc = -EACCES; int xid; char *full_path = NULL; char *target_path = ERR_PTR(-ENOMEM); struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; xid = GetXid(); full_path = build_path_from_dentry(direntry); if (!full_path) goto out_no_free; cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; target_path = kmalloc(PATH_MAX, GFP_KERNEL); if (!target_path) { target_path = ERR_PTR(-ENOMEM); goto out; } /* We could change this to: if (pTcon->unix_ext) but there does not seem any point in refusing to get symlink info if we can, even if unix extensions turned off for this mount */ if (pTcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, target_path, PATH_MAX-1, cifs_sb->local_nls); else { /* BB add read reparse point symlink code here */ /* rc = CIFSSMBQueryReparseLinkInfo */ /* BB Add code to Query ReparsePoint info */ /* BB Add MAC style xsymlink check here if enabled */ } if (rc == 0) { /* BB Add special case check for Samba DFS symlinks */ target_path[PATH_MAX-1] = 0; } else { kfree(target_path); target_path = ERR_PTR(rc); } out: kfree(full_path); out_no_free: FreeXid(xid); nd_set_link(nd, target_path); return NULL; /* No cookie */ } int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) { int rc = -EOPNOTSUPP; int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } cFYI(1, ("Full path: %s", full_path)); cFYI(1, ("symname is %s", symname)); /* BB what if DFS and this volume is on different share? BB */ 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) { cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d", rc)); } else { if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; else direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } } kfree(full_path); FreeXid(xid); return rc; } int cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) { struct inode *inode = direntry->d_inode; int rc = -EACCES; int xid; int oplock = 0; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; char *tmpbuffer; int len; __u16 fid; xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; /* BB would it be safe against deadlock to grab this sem even though rename itself grabs the sem and calls lookup? */ /* mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/ full_path = build_path_from_dentry(direntry); /* mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/ if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } cFYI(1, ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d", full_path, inode, pBuffer, buflen)); if (buflen > PATH_MAX) len = PATH_MAX; else len = buflen; tmpbuffer = kmalloc(len, GFP_KERNEL); if (tmpbuffer == NULL) { kfree(full_path); FreeXid(xid); return -ENOMEM; } /* BB add read reparse point symlink code and Unix extensions symlink code here BB */ /* We could disable this based on pTcon->unix_ext flag instead ... but why? */ if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, tmpbuffer, len - 1, cifs_sb->local_nls); else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { cERROR(1, ("SFU style symlinks not implemented yet")); /* add open and read as in fs/cifs/inode.c */ } else { rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, OPEN_REPARSE_POINT, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (!rc) { rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path, tmpbuffer, len - 1, fid, cifs_sb->local_nls); if (CIFSSMBClose(xid, pTcon, fid)) { cFYI(1, ("Error closing junction point " "(open for ioctl)")); } /* If it is a DFS junction earlier we would have gotten PATH_NOT_COVERED returned from server so we do not need to request the DFS info here */ } } /* BB Anything else to do to handle recursive links? */ /* BB Should we be using page ops here? */ /* BB null terminate returned string in pBuffer? BB */ if (rc == 0) { rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer); cFYI(1, ("vfs_readlink called from cifs_readlink returned %d", rc)); } kfree(tmpbuffer); kfree(full_path); FreeXid(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); } cifs-test-base/Makefile0000644000175000017500000000067311117756171014716 0ustar stevefstevef# # Makefile for Linux CIFS VFS client # obj-$(CONFIG_CIFS) += cifs.o cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \ md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o \ readdir.o ioctl.o sess.o export.o cifsacl.o cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o cifs-test-base/md4.c0000644000175000017500000001075211117756171014105 0ustar stevefstevef/* Unix SMB/Netbios implementation. Version 1.9. a implementation of MD4 designed for use in the SMB authentication protocol Copyright (C) Andrew Tridgell 1997-1998. 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 #include #include "cifsencrypt.h" /* NOTE: This code makes no attempt to be fast! */ static __u32 F(__u32 X, __u32 Y, __u32 Z) { return (X & Y) | ((~X) & Z); } static __u32 G(__u32 X, __u32 Y, __u32 Z) { return (X & Y) | (X & Z) | (Y & Z); } static __u32 H(__u32 X, __u32 Y, __u32 Z) { return X ^ Y ^ Z; } static __u32 lshift(__u32 x, int s) { x &= 0xFFFFFFFF; return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); } #define ROUND1(a,b,c,d,k,s) (*a) = lshift((*a) + F(*b,*c,*d) + X[k], s) #define ROUND2(a,b,c,d,k,s) (*a) = lshift((*a) + G(*b,*c,*d) + X[k] + (__u32)0x5A827999,s) #define ROUND3(a,b,c,d,k,s) (*a) = lshift((*a) + H(*b,*c,*d) + X[k] + (__u32)0x6ED9EBA1,s) /* this applies md4 to 64 byte chunks */ static void mdfour64(__u32 *M, __u32 *A, __u32 *B, __u32 *C, __u32 *D) { int j; __u32 AA, BB, CC, DD; __u32 X[16]; for (j = 0; j < 16; j++) X[j] = M[j]; AA = *A; BB = *B; CC = *C; DD = *D; ROUND1(A, B, C, D, 0, 3); ROUND1(D, A, B, C, 1, 7); ROUND1(C, D, A, B, 2, 11); ROUND1(B, C, D, A, 3, 19); ROUND1(A, B, C, D, 4, 3); ROUND1(D, A, B, C, 5, 7); ROUND1(C, D, A, B, 6, 11); ROUND1(B, C, D, A, 7, 19); ROUND1(A, B, C, D, 8, 3); ROUND1(D, A, B, C, 9, 7); ROUND1(C, D, A, B, 10, 11); ROUND1(B, C, D, A, 11, 19); ROUND1(A, B, C, D, 12, 3); ROUND1(D, A, B, C, 13, 7); ROUND1(C, D, A, B, 14, 11); ROUND1(B, C, D, A, 15, 19); ROUND2(A, B, C, D, 0, 3); ROUND2(D, A, B, C, 4, 5); ROUND2(C, D, A, B, 8, 9); ROUND2(B, C, D, A, 12, 13); ROUND2(A, B, C, D, 1, 3); ROUND2(D, A, B, C, 5, 5); ROUND2(C, D, A, B, 9, 9); ROUND2(B, C, D, A, 13, 13); ROUND2(A, B, C, D, 2, 3); ROUND2(D, A, B, C, 6, 5); ROUND2(C, D, A, B, 10, 9); ROUND2(B, C, D, A, 14, 13); ROUND2(A, B, C, D, 3, 3); ROUND2(D, A, B, C, 7, 5); ROUND2(C, D, A, B, 11, 9); ROUND2(B, C, D, A, 15, 13); ROUND3(A, B, C, D, 0, 3); ROUND3(D, A, B, C, 8, 9); ROUND3(C, D, A, B, 4, 11); ROUND3(B, C, D, A, 12, 15); ROUND3(A, B, C, D, 2, 3); ROUND3(D, A, B, C, 10, 9); ROUND3(C, D, A, B, 6, 11); ROUND3(B, C, D, A, 14, 15); ROUND3(A, B, C, D, 1, 3); ROUND3(D, A, B, C, 9, 9); ROUND3(C, D, A, B, 5, 11); ROUND3(B, C, D, A, 13, 15); ROUND3(A, B, C, D, 3, 3); ROUND3(D, A, B, C, 11, 9); ROUND3(C, D, A, B, 7, 11); ROUND3(B, C, D, A, 15, 15); *A += AA; *B += BB; *C += CC; *D += DD; *A &= 0xFFFFFFFF; *B &= 0xFFFFFFFF; *C &= 0xFFFFFFFF; *D &= 0xFFFFFFFF; for (j = 0; j < 16; j++) X[j] = 0; } static void copy64(__u32 *M, unsigned char *in) { int i; for (i = 0; i < 16; i++) M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) | (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0); } static void copy4(unsigned char *out, __u32 x) { out[0] = x & 0xFF; out[1] = (x >> 8) & 0xFF; out[2] = (x >> 16) & 0xFF; out[3] = (x >> 24) & 0xFF; } /* produce a md4 message digest from data of length n bytes */ void mdfour(unsigned char *out, unsigned char *in, int n) { unsigned char buf[128]; __u32 M[16]; __u32 b = n * 8; int i; __u32 A = 0x67452301; __u32 B = 0xefcdab89; __u32 C = 0x98badcfe; __u32 D = 0x10325476; while (n > 64) { copy64(M, in); mdfour64(M, &A, &B, &C, &D); in += 64; n -= 64; } for (i = 0; i < 128; i++) buf[i] = 0; memcpy(buf, in, n); buf[n] = 0x80; if (n <= 55) { copy4(buf + 56, b); copy64(M, buf); mdfour64(M, &A, &B, &C, &D); } else { copy4(buf + 120, b); copy64(M, buf); mdfour64(M, &A, &B, &C, &D); copy64(M, buf + 64); mdfour64(M, &A, &B, &C, &D); } for (i = 0; i < 128; i++) buf[i] = 0; copy64(M, buf); copy4(out, A); copy4(out + 4, B); copy4(out + 8, C); copy4(out + 12, D); A = B = C = D = 0; } cifs-test-base/md5.c0000644000175000017500000002522311117756171014105 0ustar stevefstevef/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ /* This code slightly modified to fit into Samba by abartlet@samba.org Jun 2001 and to fit the cifs vfs by Steve French sfrench@us.ibm.com */ #include #include "md5.h" static void MD5Transform(__u32 buf[4], __u32 const in[16]); /* * Note: this code is harmless on little-endian machines. */ static void byteReverse(unsigned char *buf, unsigned longs) { __u32 t; do { t = (__u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(__u32 *) buf = t; buf += 4; } while (--longs); } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { register __u32 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((__u32) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { memmove(p, buf, len); return; } memmove(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (__u32 *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memmove(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (__u32 *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memmove(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned int count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (__u32 *) ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((__u32 *) ctx->in)[14] = ctx->bits[0]; ((__u32 *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (__u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memmove(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ (w += f(x, y, z) + data, w = w<>(32-s), w += x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void MD5Transform(__u32 buf[4], __u32 const in[16]) { register __u32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #if 0 /* currently unused */ /*********************************************************************** the rfc 2104 version of hmac_md5 initialisation. ***********************************************************************/ static void hmac_md5_init_rfc2104(unsigned char *key, int key_len, struct HMACMD5Context *ctx) { int i; /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { unsigned char tk[16]; struct MD5Context tctx; MD5Init(&tctx); MD5Update(&tctx, key, key_len); MD5Final(tk, &tctx); key = tk; key_len = 16; } /* start out by storing key in pads */ memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad)); memset(ctx->k_opad, 0, sizeof(ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i = 0; i < 64; i++) { ctx->k_ipad[i] ^= 0x36; ctx->k_opad[i] ^= 0x5c; } MD5Init(&ctx->ctx); MD5Update(&ctx->ctx, ctx->k_ipad, 64); } #endif /*********************************************************************** the microsoft version of hmac_md5 initialisation. ***********************************************************************/ void hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, struct HMACMD5Context *ctx) { int i; /* if key is longer than 64 bytes truncate it */ if (key_len > 64) key_len = 64; /* start out by storing key in pads */ memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad)); memset(ctx->k_opad, 0, sizeof(ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i = 0; i < 64; i++) { ctx->k_ipad[i] ^= 0x36; ctx->k_opad[i] ^= 0x5c; } MD5Init(&ctx->ctx); MD5Update(&ctx->ctx, ctx->k_ipad, 64); } /*********************************************************************** update hmac_md5 "inner" buffer ***********************************************************************/ void hmac_md5_update(const unsigned char *text, int text_len, struct HMACMD5Context *ctx) { MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */ } /*********************************************************************** finish off hmac_md5 "inner" buffer and generate outer one. ***********************************************************************/ void hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx) { struct MD5Context ctx_o; MD5Final(digest, &ctx->ctx); MD5Init(&ctx_o); MD5Update(&ctx_o, ctx->k_opad, 64); MD5Update(&ctx_o, digest, 16); MD5Final(digest, &ctx_o); } /*********************************************************** single function to calculate an HMAC MD5 digest from data. use the microsoft hmacmd5 init method because the key is 16 bytes. ************************************************************/ #if 0 /* currently unused */ static void hmac_md5(unsigned char key[16], unsigned char *data, int data_len, unsigned char *digest) { struct HMACMD5Context ctx; hmac_md5_init_limK_to_64(key, 16, &ctx); if (data_len != 0) hmac_md5_update(data, data_len, &ctx); hmac_md5_final(digest, &ctx); } #endif cifs-test-base/md5.h0000644000175000017500000000211211117756171014102 0ustar stevefstevef#ifndef MD5_H #define MD5_H #ifndef HEADER_MD5_H /* Try to avoid clashes with OpenSSL */ #define HEADER_MD5_H #endif struct MD5Context { __u32 buf[4]; __u32 bits[2]; unsigned char in[64]; }; #endif /* !MD5_H */ #ifndef _HMAC_MD5_H struct HMACMD5Context { struct MD5Context ctx; unsigned char k_ipad[65]; unsigned char k_opad[65]; }; #endif /* _HMAC_MD5_H */ void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); /* The following definitions come from lib/hmacmd5.c */ /* void hmac_md5_init_rfc2104(unsigned char *key, int key_len, struct HMACMD5Context *ctx);*/ void hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, struct HMACMD5Context *ctx); void hmac_md5_update(const unsigned char *text, int text_len, struct HMACMD5Context *ctx); void hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx); /* void hmac_md5(unsigned char key[16], unsigned char *data, int data_len, unsigned char *digest);*/ cifs-test-base/misc.c0000644000175000017500000005572411117756171014364 0ustar stevefstevef/* * 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 #include #include #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" #include "smberr.h" #include "nterr.h" #include "cifs_unicode.h" extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern struct task_struct *oplockThread; /* 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 _GetXid(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) cFYI(1, ("warning: more than 65000 requests active")); xid = GlobalCurrentXid++; spin_unlock(&GlobalMid_Lock); return xid; } void _FreeXid(unsigned int xid) { spin_lock(&GlobalMid_Lock); /* if (GlobalTotalActiveXid == 0) BUG(); */ GlobalTotalActiveXid--; spin_unlock(&GlobalMid_Lock); } struct cifsSesInfo * sesInfoAlloc(void) { struct cifsSesInfo *ret_buf; ret_buf = kzalloc(sizeof(struct cifsSesInfo), 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); init_MUTEX(&ret_buf->sesSem); } return ret_buf; } void sesInfoFree(struct cifsSesInfo *buf_to_free) { if (buf_to_free == NULL) { cFYI(1, ("Null buffer passed to sesInfoFree")); 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->domainName); kfree(buf_to_free); } struct cifsTconInfo * tconInfoAlloc(void) { struct cifsTconInfo *ret_buf; ret_buf = kzalloc(sizeof(struct cifsTconInfo), 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 cifsTconInfo *buf_to_free) { if (buf_to_free == NULL) { cFYI(1, ("Null buffer passed to tconInfoFree")); 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; /* 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, sizeof(struct smb_hdr) + 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) { /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/ 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) { cFYI(1, ("Null buffer passed to cifs_small_buf_release")); return; } mempool_free(buf_to_free, cifs_sm_req_poolp); atomic_dec(&smBufAllocCount); return; } /* 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. */ __u16 GetNextMid(struct TCP_Server_Info *server) { __u16 mid = 0; __u16 last_mid; int collision; if (server == NULL) return mid; spin_lock(&GlobalMid_Lock); last_mid = server->CurrentMid; /* we do not want to loop forever */ server->CurrentMid++; /* 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 (server->CurrentMid != last_mid) { struct list_head *tmp; struct mid_q_entry *mid_entry; collision = 0; if (server->CurrentMid == 0) server->CurrentMid++; list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if ((mid_entry->mid == server->CurrentMid) && (mid_entry->midState == MID_REQUEST_SUBMITTED)) { /* This mid is in use, try a different one */ collision = 1; break; } } if (collision == 0) { mid = server->CurrentMid; break; } server->CurrentMid++; } spin_unlock(&GlobalMid_Lock); return mid; } /* 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 cifsTconInfo *treeCon, int word_count /* length of fixed section (word count) in two byte units */) { struct list_head *temp_item; struct cifsSesInfo *ses; char *temp = (char *) buffer; memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ buffer->smb_buf_length = (2 * word_count) + sizeof(struct smb_hdr) - 4 /* RFC 1001 length field does not count */ + 2 /* for bcc field itself */ ; /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */ 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 = GetNextMid(treeCon->ses->server); if (multiuser_mount != 0) { /* For the multiuser case, there are few obvious technically */ /* possible mechanisms to match the local linux user (uid) */ /* to a valid remote smb user (smb_uid): */ /* 1) Query Winbind (or other local pam/nss daemon */ /* for userid/password/logon_domain or credential */ /* 2) Query Winbind for uid to sid to username mapping */ /* and see if we have a matching password for existing*/ /* session for that user perhas getting password by */ /* adding a new pam_cifs module that stores passwords */ /* so that the cifs vfs can get at that for all logged*/ /* on users */ /* 3) (Which is the mechanism we have chosen) */ /* Search through sessions to the same server for a */ /* a match on the uid that was passed in on mount */ /* with the current processes uid (or euid?) and use */ /* that smb uid. If no existing smb session for */ /* that uid found, use the default smb session ie */ /* the smb session for the volume mounted which is */ /* the same as would be used if the multiuser mount */ /* flag were disabled. */ /* BB Add support for establishing new tCon and SMB Session */ /* with userid/password pairs found on the smb session */ /* for other target tcp/ip addresses BB */ if (current->fsuid != treeCon->ses->linux_uid) { cFYI(1, ("Multiuser mode and UID " "did not match tcon uid")); read_lock(&cifs_tcp_ses_lock); list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list); if (ses->linux_uid == current->fsuid) { if (ses->server == treeCon->ses->server) { cFYI(1, ("found matching uid substitute right smb_uid")); buffer->Uid = ses->Suid; break; } else { /* BB eventually call cifs_setup_session here */ cFYI(1, ("local UID found but no smb sess with this server exists")); } } } read_unlock(&cifs_tcp_ses_lock); } } } 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->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } /* endian conversion of flags is now done just before sending */ buffer->WordCount = (char) word_count; return; } static int checkSMBhdr(struct smb_hdr *smb, __u16 mid) { /* Make sure that this really is an SMB, that it is a response, and that the message ids match */ if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) && (mid == smb->Mid)) { if (smb->Flags & SMBFLG_RESPONSE) return 0; else { /* only one valid case where server sends us request */ if (smb->Command == SMB_COM_LOCKING_ANDX) return 0; else cERROR(1, ("Received Request not response")); } } else { /* bad signature or mid */ if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) cERROR(1, ("Bad protocol string signature header %x", *(unsigned int *) smb->Protocol)); if (mid != smb->Mid) cERROR(1, ("Mids do not match")); } cERROR(1, ("bad smb detected. The Mid=%d", smb->Mid)); return 1; } int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) { __u32 len = smb->smb_buf_length; __u32 clc_len; /* calculated length */ cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); if (length < 2 + sizeof(struct smb_hdr)) { if ((length >= sizeof(struct smb_hdr) - 1) && (smb->Status.CifsError != 0)) { smb->WordCount = 0; /* some error cases do not return wct and bcc */ return 0; } else if ((length == 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; } cERROR(1, ("rcvd invalid byte count (bcc)")); } else { cERROR(1, ("Length less than smb header size")); } return 1; } if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, ("smb length greater than MaxBufSize, mid=%d", smb->Mid)); return 1; } if (checkSMBhdr(smb, mid)) return 1; clc_len = smbCalcSize_LE(smb); if (4 + len != length) { cERROR(1, ("Length read does not match RFC1001 length %d", len)); return 1; } if (4 + len != clc_len) { /* check if bcc wrapped around for large read responses */ if ((len > 64 * 1024) && (len > clc_len)) { /* check if lengths match mod 64K */ if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF)) return 0; /* bcc wrapped */ } cFYI(1, ("Calculated size %d vs length %d mismatch for mid %d", clc_len, 4 + len, smb->Mid)); /* Windows XP can return a few bytes too much, presumably an illegal pad, at the end of byte range lock responses so we allow for that three byte pad, as long as actual received length is as long or longer than calculated length */ /* We have now had to extend this more, since there is a case in which it needs to be bigger still to handle a malformed response to transact2 findfirst from WinXP when access denied is returned and thus bcc and wct are zero but server says length is 0x21 bytes too long as if the server forget to reset the smb rfc1001 length when it reset the wct and bcc to minimum size and drop the t2 parms and data */ if ((4+len > clc_len) && (len <= clc_len + 512)) return 0; else { cERROR(1, ("RFC1001 size %d bigger than SMB for Mid=%d", len, smb->Mid)); return 1; } } return 0; } bool is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) { struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct list_head *tmp, *tmp1, *tmp2; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; struct cifsInodeInfo *pCifsInode; struct cifsFileInfo *netfile; cFYI(1, ("Checking for oplock break or dnotify response")); 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 (pSMBr->ByteCount > sizeof(struct file_notify_information)) { data_offset = le32_to_cpu(pSMBr->DataOffset); pnotify = (struct file_notify_information *) ((char *)&pSMBr->hdr.Protocol + data_offset); cFYI(1, ("dnotify on %s Action: 0x%x", pnotify->FileName, pnotify->Action)); /* cifs_dump_mem("Rcvd notify Data: ",buf, sizeof(struct smb_hdr)+60); */ return true; } if (pSMBr->hdr.Status.CifsError) { cFYI(1, ("notify err 0x%d", 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)) { cFYI(1, ("invalid handle on oplock break")); 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; cFYI(1, ("oplock type 0x%d level 0x%d", pSMB->LockType, pSMB->OplockLevel)); if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) return false; /* look up tcon based on tid & uid */ read_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &srv->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); list_for_each(tmp1, &ses->tcon_list) { tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list); if (tcon->tid != buf->Tid) continue; cifs_stats_inc(&tcon->num_oplock_brks); write_lock(&GlobalSMBSeslock); list_for_each(tmp2, &tcon->openFileList) { netfile = list_entry(tmp2, struct cifsFileInfo, tlist); if (pSMB->Fid != netfile->netfid) continue; write_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock); cFYI(1, ("file id match, oplock break")); pCifsInode = CIFS_I(netfile->pInode); pCifsInode->clientCanCacheAll = false; if (pSMB->OplockLevel == 0) pCifsInode->clientCanCacheRead = false; pCifsInode->oplockPending = true; AllocOplockQEntry(netfile->pInode, netfile->netfid, tcon); cFYI(1, ("about to wake up oplock thread")); if (oplockThread) wake_up_process(oplockThread); return true; } write_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock); cFYI(1, ("No matching file for oplock break")); return true; } } read_unlock(&cifs_tcp_ses_lock); cFYI(1, ("Can not process oplock break for non-existent connection")); return true; } void dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) { int i, j; char debug_line[17]; unsigned char *buffer; if (traceSMB == 0) return; buffer = (unsigned char *) smb_buf; 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; } /* 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_ASTERIK (__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) /* Convert 16 bit Unicode pathname from wire format to string in current code page. Conversion may involve remapping up the seven 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 cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, const struct nls_table *cp) { int i, j, len; __u16 src_char; for (i = 0, j = 0; i < maxlen; i++) { src_char = le16_to_cpu(source[i]); switch (src_char) { case 0: goto cUCS_out; /* BB check this BB */ case UNI_COLON: target[j] = ':'; break; case UNI_ASTERIK: target[j] = '*'; break; case UNI_QUESTION: target[j] = '?'; break; /* BB We can not handle remapping slash until all the calls to build_path_from_dentry are modified, as they use slash as separator BB */ /* case UNI_SLASH: target[j] = '\\'; break;*/ case UNI_PIPE: target[j] = '|'; break; case UNI_GRTRTHAN: target[j] = '>'; break; case UNI_LESSTHAN: target[j] = '<'; break; default: len = cp->uni2char(src_char, &target[j], NLS_MAX_CHARSET_SIZE); if (len > 0) { j += len; continue; } else { target[j] = '?'; } } j++; /* make sure we do not overrun callers allocated temp buffer */ if (j >= (2 * NAME_MAX)) break; } cUCS_out: target[j] = 0; return j; } /* Convert 16 bit Unicode pathname to wire format from string in current code page. Conversion may involve remapping up the seven 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 cifsConvertToUCS(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars) { int i, j, charlen; int len_remaining = maxlen; char src_char; __u16 temp; if (!mapChars) return cifs_strtoUCS(target, source, PATH_MAX, cp); for (i = 0, j = 0; i < maxlen; j++) { src_char = source[i]; switch (src_char) { case 0: target[j] = 0; goto ctoUCS_out; case ':': target[j] = cpu_to_le16(UNI_COLON); break; case '*': target[j] = cpu_to_le16(UNI_ASTERIK); break; case '?': target[j] = cpu_to_le16(UNI_QUESTION); break; case '<': target[j] = cpu_to_le16(UNI_LESSTHAN); break; case '>': target[j] = cpu_to_le16(UNI_GRTRTHAN); break; case '|': target[j] = cpu_to_le16(UNI_PIPE); break; /* BB We can not handle remapping slash until all the calls to build_path_from_dentry are modified, as they use slash as separator BB */ /* case '\\': target[j] = cpu_to_le16(UNI_SLASH); break;*/ default: charlen = cp->char2uni(source+i, len_remaining, &temp); /* if no match, use question mark, which at least in some cases servers as wild card */ if (charlen < 1) { target[j] = cpu_to_le16(0x003f); charlen = 1; } else target[j] = cpu_to_le16(temp); len_remaining -= charlen; /* character may take more than one byte in the the source string, but will take exactly two bytes in the target string */ i += charlen; continue; } i++; /* move to next char in source string */ len_remaining--; } ctoUCS_out: return i; } cifs-test-base/Module.symvers0000644000175000017500000000000011117756171016115 0ustar stevefstevefcifs-test-base/netmisc.c0000644000175000017500000011314611117756171015064 0ustar stevefstevef/* * 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 #include #include #include #include #include #include #include #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}, {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}, {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, -ENOBUFS}, {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 string containing dotted ip address to binary form */ /* returns 0 if invalid address */ int cifs_inet_pton(const int address_family, const char *cp, void *dst) { int ret = 0; /* calculate length by finding first slash or NULL */ if (address_family == AF_INET) ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL); else if (address_family == AF_INET6) ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); cFYI(DBG2, ("address conversion returned %d for %s", ret, cp)); if (ret > 0) ret = 1; return ret; } /***************************************************************************** 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, ERRnomem, 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, ERRnomem, 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, 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(struct smb_hdr *smb, int logErr) { 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 */ cFYI(1, ("Mapping smb error code %d to POSIX err %d", smberrcode, 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(struct smb_hdr *ptr) { return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + 2 /* size of the bcc field */ + BCC(ptr)); } unsigned int smbCalcSize_LE(struct smb_hdr *ptr) { return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + 2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(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(u64 ntutc) { struct timespec ts; /* BB what about the timezone? BB */ /* Subtract the NTFS time offset, then convert to 1s intervals. */ u64 t; t = 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}; __le64 cnvrtDosCifsTm(__u16 date, __u16 time) { return cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(date, time))); } struct timespec cnvrtDosUnixTm(__u16 date, __u16 time) { struct timespec ts; int sec, min, days, month, year; SMB_TIME *st = (SMB_TIME *)&time; SMB_DATE *sd = (SMB_DATE *)&date; cFYI(1, ("date %d time %d", date, time)); sec = 2 * st->TwoSeconds; min = st->Minutes; if ((sec > 59) || (min > 59)) cERROR(1, ("illegal time min %d sec %d", min, sec)); sec += (min * 60); sec += 60 * 60 * st->Hours; if (st->Hours > 24) cERROR(1, ("illegal hours %d", st->Hours)); days = sd->Day; month = sd->Month; if ((days > 31) || (month > 12)) { cERROR(1, ("illegal date, month %d day: %d", 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; /* cFYI(1,("sec after cnvrt dos to unix time %d",sec)); */ ts.tv_nsec = 0; return ts; } cifs-test-base/nterr.c0000644000175000017500000010277111117756171014556 0ustar stevefstevef/* * 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 #include #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}, {"STATUS_BUFFER_OVERFLOW", 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}, {"STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES}, {"STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED}, {NULL, 0} }; cifs-test-base/nterr.h0000644000175000017500000007337611117756171014573 0ustar stevefstevef/* 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 STATUS_BUFFER_OVERFLOW 0x80000005 #define STATUS_MORE_ENTRIES 0x0105 #define ERROR_INVALID_PARAMETER 0x0057 #define ERROR_INSUFFICIENT_BUFFER 0x007a #define STATUS_1804 0x070c #define 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 STATUS_SOME_UNMAPPED 0x0107 #define STATUS_BUFFER_OVERFLOW 0x80000005 #define NT_STATUS_NO_MORE_ENTRIES 0x8000001a #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 */ cifs-test-base/ntlmssp.h0000644000175000017500000001000211117756171015112 0ustar stevefstevef/* * 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 in unicode */ #define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ #define NTLMSSP_REQUEST_TARGET 0x04 /* Server return its auth realm */ #define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signature capability */ #define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ #define NTLMSSP_NEGOTIATE_DGRAM 0x0040 #define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Sign/seal use LM session key */ #define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ #define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 #define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 #define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server on same machine */ #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign for all security levels */ #define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 #define NTLMSSP_TARGET_TYPE_SERVER 0x20000 #define NTLMSSP_TARGET_TYPE_SHARE 0x40000 #define NTLMSSP_NEGOTIATE_NTLMV2 0x80000 #define NTLMSSP_REQUEST_INIT_RESP 0x100000 #define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 #define NTLMSSP_REQUEST_NOT_NT_KEY 0x400000 #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 #define NTLMSSP_NEGOTIATE_128 0x20000000 #define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 #define NTLMSSP_NEGOTIATE_56 0x80000000 /* 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 Buffer; /* offset to buffer */ } __attribute__((packed)) SECURITY_BUFFER; typedef struct _NEGOTIATE_MESSAGE { __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; __le32 MessageType; /* 1 */ __le32 NegotiateFlags; SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ char DomainString[0]; /* followed by WorkstationString */ } __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE; typedef struct _CHALLENGE_MESSAGE { __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; __le32 MessageType; /* 2 */ SECURITY_BUFFER TargetName; __le32 NegotiateFlags; __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; __u8 Reserved[8]; SECURITY_BUFFER TargetInfoArray; } __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE; typedef struct _AUTHENTICATE_MESSAGE { __u8 Signature[sizeof (NTLMSSP_SIGNATURE)]; __le32 MessageType; /* 3 */ SECURITY_BUFFER LmChallengeResponse; SECURITY_BUFFER NtChallengeResponse; SECURITY_BUFFER DomainName; SECURITY_BUFFER UserName; SECURITY_BUFFER WorkstationName; SECURITY_BUFFER SessionKey; __le32 NegotiateFlags; char UserString[0]; } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; cifs-test-base/readdir.c0000644000175000017500000010451011117756171015027 0ustar stevefstevef/* * fs/cifs/readdir.c * * Directory search handling * * Copyright (C) International Business Machines Corp., 2004, 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 #include #include #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" #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) { cFYI(1, ("empty cifs private file data")); return; } if (cf->invalidHandle) cFYI(1, ("invalid handle")); if (cf->srch_inf.endOfSearch) cFYI(1, ("end of search")); if (cf->srch_inf.emptyDir) cFYI(1, ("empty dir")); } } #else static inline void dump_cifs_file_struct(struct file *file, char *label) { } #endif /* DEBUG2 */ /* Returns one if new inode created (which therefore needs to be hashed) */ /* Might check in the future if inode number changed so we can rehash inode */ static int construct_dentry(struct qstr *qstring, struct file *file, struct inode **ptmp_inode, struct dentry **pnew_dentry) { struct dentry *tmp_dentry; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int rc = 0; cFYI(1, ("For %s", qstring->name)); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; qstring->hash = full_name_hash(qstring->name, qstring->len); tmp_dentry = d_lookup(file->f_path.dentry, qstring); if (tmp_dentry) { cFYI(0, ("existing dentry with inode 0x%p", tmp_dentry->d_inode)); *ptmp_inode = tmp_dentry->d_inode; /* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/ if (*ptmp_inode == NULL) { *ptmp_inode = new_inode(file->f_path.dentry->d_sb); if (*ptmp_inode == NULL) return rc; rc = 1; } if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME) (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME; } else { tmp_dentry = d_alloc(file->f_path.dentry, qstring); if (tmp_dentry == NULL) { cERROR(1, ("Failed allocating dentry")); *ptmp_inode = NULL; return rc; } *ptmp_inode = new_inode(file->f_path.dentry->d_sb); if (pTcon->nocase) tmp_dentry->d_op = &cifs_ci_dentry_ops; else tmp_dentry->d_op = &cifs_dentry_ops; if (*ptmp_inode == NULL) return rc; if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME) (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME; rc = 2; } tmp_dentry->d_time = jiffies; *pnew_dentry = tmp_dentry; return rc; } static void AdjustForTZ(struct cifsTconInfo *tcon, struct inode *inode) { if ((tcon) && (tcon->ses) && (tcon->ses->server)) { inode->i_ctime.tv_sec += tcon->ses->server->timeAdj; inode->i_mtime.tv_sec += tcon->ses->server->timeAdj; inode->i_atime.tv_sec += tcon->ses->server->timeAdj; } return; } static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, char *buf, unsigned int *pobject_type, int isNewInode) { loff_t local_size; struct timespec local_mtime; struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode); struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb); __u32 attr; __u64 allocation_size; __u64 end_of_file; umode_t default_mode; /* save mtime and size */ local_mtime = tmp_inode->i_mtime; local_size = tmp_inode->i_size; if (new_buf_type) { FILE_DIRECTORY_INFO *pfindData = (FILE_DIRECTORY_INFO *)buf; attr = le32_to_cpu(pfindData->ExtFileAttributes); allocation_size = le64_to_cpu(pfindData->AllocationSize); end_of_file = le64_to_cpu(pfindData->EndOfFile); tmp_inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime)); tmp_inode->i_mtime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); tmp_inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); } else { /* legacy, OS2 and DOS style */ /* struct timespec ts;*/ FIND_FILE_STANDARD_INFO *pfindData = (FIND_FILE_STANDARD_INFO *)buf; tmp_inode->i_mtime = cnvrtDosUnixTm( le16_to_cpu(pfindData->LastWriteDate), le16_to_cpu(pfindData->LastWriteTime)); tmp_inode->i_atime = cnvrtDosUnixTm( le16_to_cpu(pfindData->LastAccessDate), le16_to_cpu(pfindData->LastAccessTime)); tmp_inode->i_ctime = cnvrtDosUnixTm( le16_to_cpu(pfindData->LastWriteDate), le16_to_cpu(pfindData->LastWriteTime)); AdjustForTZ(cifs_sb->tcon, tmp_inode); attr = le16_to_cpu(pfindData->Attributes); allocation_size = le32_to_cpu(pfindData->AllocationSize); end_of_file = le32_to_cpu(pfindData->DataSize); } /* Linux can not store file creation time unfortunately so ignore it */ cifsInfo->cifsAttrs = attr; #ifdef CONFIG_CIFS_EXPERIMENTAL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { /* get more accurate mode via ACL - so force inode refresh */ cifsInfo->time = 0; } else #endif /* CONFIG_CIFS_EXPERIMENTAL */ cifsInfo->time = jiffies; /* treat dos attribute of read-only as read-only mode bit e.g. 555? */ /* 2767 perms - indicate mandatory locking */ /* BB fill in uid and gid here? with help from winbind? or retrieve from NTFS stream extended attribute */ if (atomic_read(&cifsInfo->inUse) == 0) { tmp_inode->i_uid = cifs_sb->mnt_uid; tmp_inode->i_gid = cifs_sb->mnt_gid; } if (attr & ATTR_DIRECTORY) default_mode = cifs_sb->mnt_dir_mode; else default_mode = cifs_sb->mnt_file_mode; /* set initial permissions */ if ((atomic_read(&cifsInfo->inUse) == 0) || (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) tmp_inode->i_mode = default_mode; else { /* just reenable write bits if !ATTR_READONLY */ if ((tmp_inode->i_mode & S_IWUGO) == 0 && (attr & ATTR_READONLY) == 0) tmp_inode->i_mode |= (S_IWUGO & default_mode); tmp_inode->i_mode &= ~S_IFMT; } /* clear write bits if ATTR_READONLY is set */ if (attr & ATTR_READONLY) tmp_inode->i_mode &= ~S_IWUGO; /* set inode type */ if ((attr & ATTR_SYSTEM) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) { if (end_of_file == 0) { tmp_inode->i_mode |= S_IFIFO; *pobject_type = DT_FIFO; } else { /* * trying to get the type can be slow, so just call * this a regular file for now, and mark for reval */ tmp_inode->i_mode |= S_IFREG; *pobject_type = DT_REG; cifsInfo->time = 0; } } else { if (attr & ATTR_DIRECTORY) { tmp_inode->i_mode |= S_IFDIR; *pobject_type = DT_DIR; } else { tmp_inode->i_mode |= S_IFREG; *pobject_type = DT_REG; } } /* can not fill in nlink here as in qpathinfo version and Unx search */ if (atomic_read(&cifsInfo->inUse) == 0) atomic_set(&cifsInfo->inUse, 1); spin_lock(&tmp_inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(tmp_inode, end_of_file); /* 512 bytes (2**9) is the fake blocksize that must be used */ /* for this calculation, even though the reported blocksize is larger */ tmp_inode->i_blocks = (512 - 1 + allocation_size) >> 9; } spin_unlock(&tmp_inode->i_lock); if (allocation_size < end_of_file) cFYI(1, ("May be sparse file, allocation less than file size")); cFYI(1, ("File Size %ld and blocks %llu", (unsigned long)tmp_inode->i_size, (unsigned long long)tmp_inode->i_blocks)); if (S_ISREG(tmp_inode->i_mode)) { cFYI(1, ("File inode")); tmp_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) tmp_inode->i_fop = &cifs_file_direct_nobrl_ops; else tmp_inode->i_fop = &cifs_file_direct_ops; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop = &cifs_file_nobrl_ops; else tmp_inode->i_fop = &cifs_file_ops; if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)) tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else tmp_inode->i_data.a_ops = &cifs_addr_ops; if (isNewInode) return; /* No sense invalidating pages for new inode since have not started caching readahead file data yet */ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { cFYI(1, ("inode exists but unchanged")); } else { /* file may have changed on server */ cFYI(1, ("invalidate inode, readdir detected change")); invalidate_remote_inode(tmp_inode); } } else if (S_ISDIR(tmp_inode->i_mode)) { cFYI(1, ("Directory inode")); tmp_inode->i_op = &cifs_dir_inode_ops; tmp_inode->i_fop = &cifs_dir_ops; } else if (S_ISLNK(tmp_inode->i_mode)) { cFYI(1, ("Symbolic Link inode")); tmp_inode->i_op = &cifs_symlink_inode_ops; } else { cFYI(1, ("Init special inode")); init_special_inode(tmp_inode, tmp_inode->i_mode, tmp_inode->i_rdev); } } static void unix_fill_in_inode(struct inode *tmp_inode, FILE_UNIX_INFO *pfindData, unsigned int *pobject_type, int isNewInode) { loff_t local_size; struct timespec local_mtime; struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode); struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb); __u32 type = le32_to_cpu(pfindData->Type); __u64 num_of_bytes = le64_to_cpu(pfindData->NumOfBytes); __u64 end_of_file = le64_to_cpu(pfindData->EndOfFile); cifsInfo->time = jiffies; atomic_inc(&cifsInfo->inUse); /* save mtime and size */ local_mtime = tmp_inode->i_mtime; local_size = tmp_inode->i_size; tmp_inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime)); tmp_inode->i_mtime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastModificationTime)); tmp_inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastStatusChange)); tmp_inode->i_mode = le64_to_cpu(pfindData->Permissions); /* since we set the inode type below we need to mask off type to avoid strange results if bits above were corrupt */ tmp_inode->i_mode &= ~S_IFMT; if (type == UNIX_FILE) { *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; } else if (type == UNIX_SYMLINK) { *pobject_type = DT_LNK; tmp_inode->i_mode |= S_IFLNK; } else if (type == UNIX_DIR) { *pobject_type = DT_DIR; tmp_inode->i_mode |= S_IFDIR; } else if (type == UNIX_CHARDEV) { *pobject_type = DT_CHR; tmp_inode->i_mode |= S_IFCHR; tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor), le64_to_cpu(pfindData->DevMinor) & MINORMASK); } else if (type == UNIX_BLOCKDEV) { *pobject_type = DT_BLK; tmp_inode->i_mode |= S_IFBLK; tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor), le64_to_cpu(pfindData->DevMinor) & MINORMASK); } else if (type == UNIX_FIFO) { *pobject_type = DT_FIFO; tmp_inode->i_mode |= S_IFIFO; } else if (type == UNIX_SOCKET) { *pobject_type = DT_SOCK; tmp_inode->i_mode |= S_IFSOCK; } else { /* safest to just call it a file */ *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; cFYI(1, ("unknown inode type %d", type)); } if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) tmp_inode->i_uid = cifs_sb->mnt_uid; else tmp_inode->i_uid = le64_to_cpu(pfindData->Uid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) tmp_inode->i_gid = cifs_sb->mnt_gid; else tmp_inode->i_gid = le64_to_cpu(pfindData->Gid); tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks); spin_lock(&tmp_inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(tmp_inode, end_of_file); /* 512 bytes (2**9) is the fake blocksize that must be used */ /* for this calculation, not the real blocksize */ tmp_inode->i_blocks = (512 - 1 + num_of_bytes) >> 9; } spin_unlock(&tmp_inode->i_lock); if (S_ISREG(tmp_inode->i_mode)) { cFYI(1, ("File inode")); tmp_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) tmp_inode->i_fop = &cifs_file_direct_nobrl_ops; else tmp_inode->i_fop = &cifs_file_direct_ops; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop = &cifs_file_nobrl_ops; else tmp_inode->i_fop = &cifs_file_ops; if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)) tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else tmp_inode->i_data.a_ops = &cifs_addr_ops; if (isNewInode) return; /* No sense invalidating pages for new inode since we have not started caching readahead file data for it yet */ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { cFYI(1, ("inode exists but unchanged")); } else { /* file may have changed on server */ cFYI(1, ("invalidate inode, readdir detected change")); invalidate_remote_inode(tmp_inode); } } else if (S_ISDIR(tmp_inode->i_mode)) { cFYI(1, ("Directory inode")); tmp_inode->i_op = &cifs_dir_inode_ops; tmp_inode->i_fop = &cifs_dir_ops; } else if (S_ISLNK(tmp_inode->i_mode)) { cFYI(1, ("Symbolic Link inode")); tmp_inode->i_op = &cifs_symlink_inode_ops; /* tmp_inode->i_fop = *//* do not need to set to anything */ } else { cFYI(1, ("Special inode")); init_special_inode(tmp_inode, tmp_inode->i_mode, tmp_inode->i_rdev); } } static int initiate_cifs_search(const int xid, struct file *file) { int rc = 0; char *full_path; struct cifsFileInfo *cifsFile; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; if (file->private_data == NULL) { file->private_data = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); } if (file->private_data == NULL) return -ENOMEM; cifsFile = file->private_data; cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); if (cifs_sb == NULL) return -EINVAL; pTcon = cifs_sb->tcon; if (pTcon == NULL) return -EINVAL; full_path = build_path_from_dentry(file->f_path.dentry); if (full_path == NULL) return -ENOMEM; cFYI(1, ("Full path: %s start at: %lld", 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 (pTcon->ses->capabilities & CAP_UNIX) { */ if (pTcon->unix_ext) cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; else if ((pTcon->ses->capabilities & (CAP_NT_SMBS | 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; } rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls, &cifsFile->netfid, &cifsFile->srch_inf, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); if (rc == 0) cifsFile->invalidHandle = false; if ((rc == -EOPNOTSUPP) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; goto ffirst_retry; } kfree(full_path); return rc; } /* return length of unicode string in bytes */ static int cifs_unicode_bytelen(char *str) { int len; __le16 *ustr = (__le16 *)str; for (len = 0; len <= PATH_MAX; len++) { if (ustr[len] == 0) return len << 1; } cFYI(1, ("Unicode string longer than PATH_MAX found")); 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); cFYI(1, ("new entry %p old entry %p", new_entry, old_entry)); /* validate that new_entry is not past end of SMB */ if (new_entry >= end_of_smb) { cERROR(1, ("search entry %p began after end of SMB %p old entry %p", 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))) { cERROR(1, ("search entry %p extends after end of SMB %p", new_entry, end_of_smb)); return NULL; } else return new_entry; } #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(char *current_entry, struct cifsFileInfo *cfile) { int rc = 0; char *filename = NULL; int len = 0; if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) { FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; filename = &pFindData->FileName[0]; if (cfile->srch_inf.unicode) { len = cifs_unicode_bytelen(filename); } else { /* BB should we make this strnlen of PATH_MAX? */ len = strnlen(filename, 5); } } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) { FILE_DIRECTORY_INFO *pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { FILE_FULL_DIRECTORY_INFO *pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { SEARCH_ID_FULL_DIR_INFO *pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { FILE_BOTH_DIRECTORY_INFO *pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) { FIND_FILE_STANDARD_INFO *pFindData = (FIND_FILE_STANDARD_INFO *)current_entry; filename = &pFindData->FileName[0]; len = pFindData->FileNameLength; } else { cFYI(1, ("Unknown findfirst level %d", cfile->srch_inf.info_level)); } if (filename) { if (cfile->srch_inf.unicode) { __le16 *ufilename = (__le16 *)filename; if (len == 2) { /* check for . */ if (ufilename[0] == UNICODE_DOT) rc = 1; } else if (len == 4) { /* check for .. */ if ((ufilename[0] == UNICODE_DOT) && (ufilename[1] == UNICODE_DOT)) rc = 2; } } else /* ASCII */ { if (len == 1) { if (filename[0] == '.') rc = 1; } else if (len == 2) { if ((filename[0] == '.') && (filename[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->f_path.dentry->d_inode; 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 *cifsFile) { int rc = 0; unsigned int len = 0; __u16 level; char *filename; if ((cifsFile == NULL) || (current_entry == NULL)) return -EINVAL; level = cifsFile->srch_inf.info_level; if (level == SMB_FIND_FILE_UNIX) { FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; filename = &pFindData->FileName[0]; if (cifsFile->srch_inf.unicode) { len = cifs_unicode_bytelen(filename); } else { /* BB should we make this strnlen of PATH_MAX? */ len = strnlen(filename, PATH_MAX); } cifsFile->srch_inf.resume_key = pFindData->ResumeKey; } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { FILE_DIRECTORY_INFO *pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { FILE_FULL_DIRECTORY_INFO *pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { SEARCH_ID_FULL_DIR_INFO *pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { FILE_BOTH_DIRECTORY_INFO *pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; } else if (level == SMB_FIND_FILE_INFO_STANDARD) { FIND_FILE_STANDARD_INFO *pFindData = (FIND_FILE_STANDARD_INFO *)current_entry; filename = &pFindData->FileName[0]; /* one byte length, no name conversion */ len = (unsigned int)pFindData->FileNameLength; cifsFile->srch_inf.resume_key = pFindData->ResumeKey; } else { cFYI(1, ("Unknown findfirst level %d", level)); return -EINVAL; } cifsFile->srch_inf.resume_name_len = len; cifsFile->srch_inf.presume_name = filename; 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 int xid, struct cifsTconInfo *pTcon, struct file *file, char **ppCurrentEntry, int *num_to_ret) { int rc = 0; int pos_in_buf = 0; loff_t first_entry_in_buffer; loff_t index_to_find = file->f_pos; struct cifsFileInfo *cifsFile = file->private_data; /* check if index in the buffer */ if ((cifsFile == NULL) || (ppCurrentEntry == NULL) || (num_to_ret == NULL)) return -ENOENT; *ppCurrentEntry = NULL; first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry - cifsFile->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 < cifsFile->srch_inf.index_of_last_entry) && is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { /* close and restart search */ cFYI(1, ("search backing up - close and restart search")); write_lock(&GlobalSMBSeslock); if (!cifsFile->srch_inf.endOfSearch && !cifsFile->invalidHandle) { cifsFile->invalidHandle = true; write_unlock(&GlobalSMBSeslock); CIFSFindClose(xid, pTcon, cifsFile->netfid); } else write_unlock(&GlobalSMBSeslock); if (cifsFile->srch_inf.ntwrk_buf_start) { cFYI(1, ("freeing SMB ff cache buf on search rewind")); if (cifsFile->srch_inf.smallBuf) cifs_small_buf_release(cifsFile->srch_inf. ntwrk_buf_start); else cifs_buf_release(cifsFile->srch_inf. ntwrk_buf_start); cifsFile->srch_inf.ntwrk_buf_start = NULL; } rc = initiate_cifs_search(xid, file); if (rc) { cFYI(1, ("error %d reinitiating a search on rewind", rc)); return rc; } cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile); } while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && (rc == 0) && !cifsFile->srch_inf.endOfSearch) { cFYI(1, ("calling findnext2")); rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, &cifsFile->srch_inf); cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile); if (rc) return -ENOENT; } if (index_to_find < cifsFile->srch_inf.index_of_last_entry) { /* we found the buffer that contains the entry */ /* scan and find it */ int i; char *current_entry; char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + smbCalcSize((struct smb_hdr *) cifsFile->srch_inf.ntwrk_buf_start); current_entry = cifsFile->srch_inf.srch_entries_start; first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry - cifsFile->srch_inf.entries_in_buffer; pos_in_buf = index_to_find - first_entry_in_buffer; cFYI(1, ("found entry - pos_in_buf %d", pos_in_buf)); for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) { /* go entry by entry figuring out which is first */ current_entry = nxt_dir_entry(current_entry, end_of_smb, cifsFile->srch_inf.info_level); } if ((current_entry == NULL) && (i < pos_in_buf)) { /* BB fixme - check if we should flag this error */ cERROR(1, ("reached end of buf searching for pos in buf" " %d index to find %lld rc %d", pos_in_buf, index_to_find, rc)); } rc = 0; *ppCurrentEntry = current_entry; } else { cFYI(1, ("index not in buffer - could not findnext into it")); return 0; } if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { cFYI(1, ("can not return entries pos_in_buf beyond last")); *num_to_ret = 0; } else *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; return rc; } /* inode num, inode type and filename returned */ static int cifs_get_name_from_search_buf(struct qstr *pqst, char *current_entry, __u16 level, unsigned int unicode, struct cifs_sb_info *cifs_sb, int max_len, ino_t *pinum) { int rc = 0; unsigned int len = 0; char *filename; struct nls_table *nlt = cifs_sb->local_nls; *pinum = 0; if (level == SMB_FIND_FILE_UNIX) { FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; filename = &pFindData->FileName[0]; if (unicode) { len = cifs_unicode_bytelen(filename); } else { /* BB should we make this strnlen of PATH_MAX? */ len = strnlen(filename, PATH_MAX); } /* BB fixme - hash low and high 32 bits if not 64 bit arch BB */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) *pinum = pFindData->UniqueId; } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { FILE_DIRECTORY_INFO *pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { FILE_FULL_DIRECTORY_INFO *pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { SEARCH_ID_FULL_DIR_INFO *pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); *pinum = pFindData->UniqueId; } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { FILE_BOTH_DIRECTORY_INFO *pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (level == SMB_FIND_FILE_INFO_STANDARD) { FIND_FILE_STANDARD_INFO *pFindData = (FIND_FILE_STANDARD_INFO *)current_entry; filename = &pFindData->FileName[0]; /* one byte length, no name conversion */ len = (unsigned int)pFindData->FileNameLength; } else { cFYI(1, ("Unknown findfirst level %d", level)); return -EINVAL; } if (len > max_len) { cERROR(1, ("bad search response length %d past smb end", len)); return -EINVAL; } if (unicode) { /* BB fixme - test with long names */ /* Note converted filename can be longer than in unicode */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) pqst->len = cifs_convertUCSpath((char *)pqst->name, (__le16 *)filename, len/2, nlt); else pqst->len = cifs_strfromUCS_le((char *)pqst->name, (__le16 *)filename, len/2, nlt); } else { pqst->name = filename; pqst->len = len; } pqst->hash = full_name_hash(pqst->name, pqst->len); /* cFYI(1, ("filldir on %s",pqst->name)); */ return rc; } static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir, void *direntry, char *scratch_buf, int max_len) { int rc = 0; struct qstr qstring; struct cifsFileInfo *pCifsF; unsigned int obj_type; ino_t inum; struct cifs_sb_info *cifs_sb; struct inode *tmp_inode; struct dentry *tmp_dentry; /* get filename and len into qstring */ /* get dentry */ /* decide whether to create and populate ionde */ if ((direntry == NULL) || (file == NULL)) return -EINVAL; pCifsF = file->private_data; if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL)) return -ENOENT; rc = cifs_entry_is_dot(pfindEntry, pCifsF); /* skip . and .. since we added them first */ if (rc != 0) return 0; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); qstring.name = scratch_buf; rc = cifs_get_name_from_search_buf(&qstring, pfindEntry, pCifsF->srch_inf.info_level, pCifsF->srch_inf.unicode, cifs_sb, max_len, &inum /* returned */); if (rc) return rc; rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry); if ((tmp_inode == NULL) || (tmp_dentry == NULL)) return -ENOMEM; if (rc) { /* inode created, we need to hash it with right inode number */ if (inum != 0) { /* BB fixme - hash the 2 32 quantities bits together if * necessary BB */ tmp_inode->i_ino = inum; } insert_inode_hash(tmp_inode); } /* we pass in rc below, indicating whether it is a new inode, so we can figure out whether to invalidate the inode cached data if the file has changed */ if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) unix_fill_in_inode(tmp_inode, (FILE_UNIX_INFO *)pfindEntry, &obj_type, rc); else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */, pfindEntry, &obj_type, rc); else fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc); if (rc) /* new inode - needs to be tied to dentry */ { d_instantiate(tmp_dentry, tmp_inode); if (rc == 2) d_rehash(tmp_dentry); } rc = filldir(direntry, qstring.name, qstring.len, file->f_pos, tmp_inode->i_ino, obj_type); if (rc) { cFYI(1, ("filldir rc = %d", rc)); /* we can not return filldir errors to the caller since they are "normal" when the stat blocksize is too small - we return remapped error instead */ rc = -EOVERFLOW; } dput(tmp_dentry); return rc; } int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) { int rc = 0; int xid, i; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *cifsFile = NULL; char *current_entry; int num_to_fill = 0; char *tmp_buf = NULL; char *end_of_smb; int max_len; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; if (pTcon == NULL) return -EINVAL; switch ((int) file->f_pos) { case 0: if (filldir(direntry, ".", 1, file->f_pos, file->f_path.dentry->d_inode->i_ino, DT_DIR) < 0) { cERROR(1, ("Filldir for current dir failed")); rc = -ENOMEM; break; } file->f_pos++; case 1: if (filldir(direntry, "..", 2, file->f_pos, file->f_path.dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { cERROR(1, ("Filldir for parent dir failed")); 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 = initiate_cifs_search(xid, file); cFYI(1, ("initiate cifs search rc %d", rc)); if (rc) { FreeXid(xid); return rc; } } if (file->private_data == NULL) { rc = -EINVAL; FreeXid(xid); return rc; } cifsFile = file->private_data; if (cifsFile->srch_inf.endOfSearch) { if (cifsFile->srch_inf.emptyDir) { cFYI(1, ("End of search, empty dir")); rc = 0; break; } } /* else { cifsFile->invalidHandle = true; CIFSFindClose(xid, pTcon, cifsFile->netfid); } */ rc = find_cifs_entry(xid, pTcon, file, ¤t_entry, &num_to_fill); if (rc) { cFYI(1, ("fce error %d", rc)); goto rddir2_exit; } else if (current_entry != NULL) { cFYI(1, ("entry %lld found", file->f_pos)); } else { cFYI(1, ("could not find entry")); goto rddir2_exit; } cFYI(1, ("loop through %d times filling dir for net buf %p", num_to_fill, cifsFile->srch_inf.ntwrk_buf_start)); max_len = smbCalcSize((struct smb_hdr *) cifsFile->srch_inf.ntwrk_buf_start); end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; /* 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. cifs_unicode.c, which actually does the conversion, has the same limit */ tmp_buf = kmalloc((2 * NAME_MAX) + 4, GFP_KERNEL); for (i = 0; (i < num_to_fill) && (rc == 0); i++) { if (current_entry == NULL) { /* evaluate whether this case is an error */ cERROR(1, ("past SMB end, num to fill %d i %d", 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) { cFYI(1, ("last entry in buf at pos %lld %s", 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: FreeXid(xid); return rc; } cifs-test-base/README0000644000175000017500000010706311117756171014137 0ustar stevefstevefThe CIFS VFS support for Linux supports many advanced network filesystem features such as hierarchical dfs like namespace, hardlinks, locking and more. It was designed to comply with the SNIA CIFS Technical Reference (which supersedes the 1992 X/Open SMB Standard) as well as to perform best practice practical interoperability with Windows 2000, Windows XP, Samba and equivalent servers. This code was developed in participation with the Protocol Freedom Information Foundation. Please see http://protocolfreedom.org/ and http://samba.org/samba/PFIF/ for more details. For questions or bug reports please contact: sfrench@samba.org (sfrench@us.ibm.com) Build instructions: ================== For Linux 2.4: 1) Get the kernel source (e.g.from http://www.kernel.org) and download the cifs vfs source (see the project page at http://us1.samba.org/samba/Linux_CIFS_client.html) and change directory into the top of the kernel directory then patch the kernel (e.g. "patch -p1 < cifs_24.patch") to add the cifs vfs to your kernel configure options if it has not already been added (e.g. current SuSE and UL users do not need to apply the cifs_24.patch since the cifs vfs is already in the kernel configure menu) and then mkdir linux/fs/cifs and then copy the current cifs vfs files from the cifs download to your kernel build directory e.g. cp /fs/cifs/* to /fs/cifs 2) make menuconfig (or make xconfig) 3) select cifs from within the network filesystem choices 4) save and exit 5) make dep 6) make modules (or "make" if CIFS VFS not to be built as a module) For Linux 2.6: 1) Download the kernel (e.g. from http://www.kernel.org) and change directory into the top of the kernel directory tree (e.g. /usr/src/linux-2.5.73) 2) make menuconfig (or make xconfig) 3) select cifs from within the network filesystem choices 4) save and exit 5) make Installation instructions: ========================= If you have built the CIFS vfs as module (successfully) simply type "make modules_install" (or if you prefer, manually copy the file to the modules directory e.g. /lib/modules/2.4.10-4GB/kernel/fs/cifs/cifs.o). If you have built the CIFS vfs into the kernel itself, follow the instructions for your distribution on how to install a new kernel (usually you would simply type "make install"). If you do not have the utility mount.cifs (in the Samba 3.0 source tree and on the CIFS VFS web site) copy it to the same directory in which mount.smbfs and similar files reside (usually /sbin). Although the helper software is not required, mount.cifs is recommended. Eventually the Samba 3.0 utility program "net" may also be helpful since it may someday provide easier mount syntax for users who are used to Windows e.g. net use Note that running the Winbind pam/nss module (logon service) on all of your Linux clients is useful in mapping Uids and Gids consistently across the domain to the proper network user. The mount.cifs mount helper can be trivially built from Samba 3.0 or later source e.g. by executing: gcc samba/source/client/mount.cifs.c -o mount.cifs If cifs is built as a module, then the size and number of network buffers and maximum number of simultaneous requests to one server can be configured. Changing these from their defaults is not recommended. By executing modinfo modinfo kernel/fs/cifs/cifs.ko on kernel/fs/cifs/cifs.ko the list of configuration changes that can be made at module initialization time (by running insmod cifs.ko) can be seen. Allowing User Mounts ==================== To permit users to mount and unmount over directories they own is possible with the cifs vfs. A way to enable such mounting is to mark the mount.cifs utility as suid (e.g. "chmod +s /sbin/mount.cifs). To enable users to umount shares they mount requires 1) mount.cifs version 1.4 or later 2) an entry for the share in /etc/fstab indicating that a user may unmount it e.g. //server/usersharename /mnt/username cifs user 0 0 Note that when the mount.cifs utility is run suid (allowing user mounts), in order to reduce risks, the "nosuid" mount flag is passed in on mount to disallow execution of an suid program mounted on the remote target. When mount is executed as root, nosuid is not passed in by default, and execution of suid programs on the remote target would be enabled by default. This can be changed, as with nfs and other filesystems, by simply specifying "nosuid" among the mount options. For user mounts though to be able to pass the suid flag to mount requires rebuilding mount.cifs with the following flag: gcc samba/source/client/mount.cifs.c -DCIFS_ALLOW_USR_SUID -o mount.cifs There is a corresponding manual page for cifs mounting in the Samba 3.0 and later source tree in docs/manpages/mount.cifs.8 Allowing User Unmounts ====================== To permit users to ummount directories that they have user mounted (see above), the utility umount.cifs may be used. It may be invoked directly, or if umount.cifs is placed in /sbin, umount can invoke the cifs umount helper (at least for most versions of the umount utility) for umount of cifs mounts, unless umount is invoked with -i (which will avoid invoking a umount helper). As with mount.cifs, to enable user unmounts umount.cifs must be marked as suid (e.g. "chmod +s /sbin/umount.cifs") or equivalent (some distributions allow adding entries to a file to the /etc/permissions file to achieve the equivalent suid effect). For this utility to succeed the target path must be a cifs mount, and the uid of the current user must match the uid of the user who mounted the resource. Also note that the customary way of allowing user mounts and unmounts is (instead of using mount.cifs and unmount.cifs as suid) to add a line to the file /etc/fstab for each //server/share you wish to mount, but this can become unwieldy when potential mount targets include many or unpredictable UNC names. Samba Considerations ==================== To get the maximum benefit from the CIFS VFS, we recommend using a server that supports the SNIA CIFS Unix Extensions standard (e.g. Samba 2.2.5 or later or Samba 3.0) but the CIFS vfs works fine with a wide variety of CIFS servers. Note that uid, gid and file permissions will display default values if you do not have a server that supports the Unix extensions for CIFS (such as Samba 2.2.5 or later). To enable the Unix CIFS Extensions in the Samba server, add the line: unix extensions = yes to your smb.conf file on the server. Note that the following smb.conf settings are also useful (on the Samba server) when the majority of clients are Unix or Linux: case sensitive = yes delete readonly = yes ea support = yes Note that server ea support is required for supporting xattrs from the Linux cifs client, and that EA support is present in later versions of Samba (e.g. 3.0.6 and later (also EA support works in all versions of Windows, at least to shares on NTFS filesystems). Extended Attribute (xattr) support is an optional feature of most Linux filesystems which may require enabling via make menuconfig. Client support for extended attributes (user xattr) can be disabled on a per-mount basis by specifying "nouser_xattr" on mount. The CIFS client can get and set POSIX ACLs (getfacl, setfacl) to Samba servers version 3.10 and later. Setting POSIX ACLs requires enabling both XATTR and then POSIX support in the CIFS configuration options when building the cifs module. POSIX ACL support can be disabled on a per mount basic by specifying "noacl" on mount. Some administrators may want to change Samba's smb.conf "map archive" and "create mask" parameters from the default. Unless the create mask is changed newly created files can end up with an unnecessarily restrictive default mode, which may not be what you want, although if the CIFS Unix extensions are enabled on the server and client, subsequent setattr calls (e.g. chmod) can fix the mode. Note that creating special devices (mknod) remotely may require specifying a mkdev function to Samba if you are not using Samba 3.0.6 or later. For more information on these see the manual pages ("man smb.conf") on the Samba server system. Note that the cifs vfs, unlike the smbfs vfs, does not read the smb.conf on the client system (the few optional settings are passed in on mount via -o parameters instead). Note that Samba 2.2.7 or later includes a fix that allows the CIFS VFS to delete open files (required for strict POSIX compliance). Windows Servers already supported this feature. Samba server does not allow symlinks that refer to files outside of the share, so in Samba versions prior to 3.0.6, most symlinks to files with absolute paths (ie beginning with slash) such as: ln -s /mnt/foo bar would be forbidden. Samba 3.0.6 server or later includes the ability to create such symlinks safely by converting unsafe symlinks (ie symlinks to server files that are outside of the share) to a samba specific format on the server that is ignored by local server applications and non-cifs clients and that will not be traversed by the Samba server). This is opaque to the Linux client application using the cifs vfs. Absolute symlinks will work to Samba 3.0.5 or later, but only for remote clients using the CIFS Unix extensions, and will be invisbile to Windows clients and typically will not affect local applications running on the same server as Samba. Use instructions: ================ Once the CIFS VFS support is built into the kernel or installed as a module (cifs.o), you can use mount syntax like the following to access Samba or Windows servers: mount -t cifs //9.53.216.11/e$ /mnt -o user=myname,pass=mypassword Before -o the option -v may be specified to make the mount.cifs mount helper display the mount steps more verbosely. After -o the following commonly used cifs vfs specific options are supported: user= pass= domain= Other cifs mount options are described below. Use of TCP names (in addition to ip addresses) is available if the mount helper (mount.cifs) is installed. If you do not trust the server to which are mounted, or if you do not have cifs signing enabled (and the physical network is insecure), consider use of the standard mount options "noexec" and "nosuid" to reduce the risk of running an altered binary on your local system (downloaded from a hostile server or altered by a hostile router). Although mounting using format corresponding to the CIFS URL specification is not possible in mount.cifs yet, it is possible to use an alternate format for the server and sharename (which is somewhat similar to NFS style mount syntax) instead of the more widely used UNC format (i.e. \\server\share): mount -t cifs tcp_name_of_server:share_name /mnt -o user=myname,pass=mypasswd When using the mount helper mount.cifs, passwords may be specified via alternate mechanisms, instead of specifying it after -o using the normal "pass=" syntax on the command line: 1) By including it in a credential file. Specify credentials=filename as one of the mount options. Credential files contain two lines username=someuser password=your_password 2) By specifying the password in the PASSWD environment variable (similarly the user name can be taken from the USER environment variable). 3) By specifying the password in a file by name via PASSWD_FILE 4) By specifying the password in a file by file descriptor via PASSWD_FD If no password is provided, mount.cifs will prompt for password entry Restrictions ============ Servers must support either "pure-TCP" (port 445 TCP/IP CIFS connections) or RFC 1001/1002 support for "Netbios-Over-TCP/IP." This is not likely to be a problem as most servers support this. Valid filenames differ between Windows and Linux. Windows typically restricts filenames which contain certain reserved characters (e.g.the character : which is used to delimit the beginning of a stream name by Windows), while Linux allows a slightly wider set of valid characters in filenames. Windows servers can remap such characters when an explicit mapping is specified in the Server's registry. Samba starting with version 3.10 will allow such filenames (ie those which contain valid Linux characters, which normally would be forbidden for Windows/CIFS semantics) as long as the server is configured for Unix Extensions (and the client has not disabled /proc/fs/cifs/LinuxExtensionsEnabled). CIFS VFS Mount Options ====================== A partial list of the supported mount options follows: user The user name to use when trying to establish the CIFS session. password The user password. If the mount helper is installed, the user will be prompted for password if not supplied. ip The ip address of the target server unc The target server Universal Network Name (export) to mount. domain Set the SMB/CIFS workgroup name prepended to the username during CIFS session establishment uid Set the default uid for inodes. For mounts to servers which do support the CIFS Unix extensions, such as a properly configured Samba server, the server provides the uid, gid and mode so this parameter should not be specified unless the server and clients uid and gid numbering differ. If the server and client are in the same domain (e.g. running winbind or nss_ldap) and the server supports the Unix Extensions then the uid and gid can be retrieved from the server (and uid and gid would not have to be specifed on the mount. For servers which do not support the CIFS Unix extensions, the default uid (and gid) returned on lookup of existing files will be the uid (gid) of the person who executed the mount (root, except when mount.cifs is configured setuid for user mounts) unless the "uid=" (gid) mount option is specified. For the uid (gid) of newly created files and directories, ie files created since the last mount of the server share, the expected uid (gid) is cached as long as the inode remains in memory on the client. Also note that permission checks (authorization checks) on accesses to a file occur at the server, but there are cases in which an administrator may want to restrict at the client as well. For those servers which do not report a uid/gid owner (such as Windows), permissions can also be checked at the client, and a crude form of client side permission checking can be enabled by specifying file_mode and dir_mode on the client. Note that the mount.cifs helper must be at version 1.10 or higher to support specifying the uid (or gid) in non-numeric form. gid Set the default gid for inodes (similar to above). file_mode If CIFS Unix extensions are not supported by the server this overrides the default mode for file inodes. dir_mode If CIFS Unix extensions are not supported by the server this overrides the default mode for directory inodes. port attempt to contact the server on this tcp port, before trying the usual ports (port 445, then 139). iocharset Codepage used to convert local path names to and from Unicode. Unicode is used by default for network path names if the server supports it. If iocharset is not specified then the nls_default specified during the local client kernel build will be used. If server does not support Unicode, this parameter is unused. rsize default read size (usually 16K). The client currently can not use rsize larger than CIFSMaxBufSize. CIFSMaxBufSize defaults to 16K and may be changed (from 8K to the maximum kmalloc size allowed by your kernel) at module install time for cifs.ko. Setting CIFSMaxBufSize to a very large value will cause cifs to use more memory and may reduce performance in some cases. To use rsize greater than 127K (the original cifs protocol maximum) also requires that the server support a new Unix Capability flag (for very large read) which some newer servers (e.g. Samba 3.0.26 or later) do. rsize can be set from a minimum of 2048 to a maximum of 130048 (127K or CIFSMaxBufSize, whichever is smaller) wsize default write size (default 57344) maximum wsize currently allowed by CIFS is 57344 (fourteen 4096 byte pages) rw mount the network share read-write (note that the server may still consider the share read-only) ro mount network share read-only version used to distinguish different versions of the mount helper utility (not typically needed) sep if first mount option (after the -o), overrides the comma as the separator between the mount parms. e.g. -o user=myname,password=mypassword,domain=mydom could be passed instead with period as the separator by -o sep=.user=myname.password=mypassword.domain=mydom this might be useful when comma is contained within username or password or domain. This option is less important when the cifs mount helper cifs.mount (version 1.1 or later) is used. nosuid Do not allow remote executables with the suid bit program to be executed. This is only meaningful for mounts to servers such as Samba which support the CIFS Unix Extensions. If you do not trust the servers in your network (your mount targets) it is recommended that you specify this option for greater security. exec Permit execution of binaries on the mount. noexec Do not permit execution of binaries on the mount. dev Recognize block devices on the remote mount. nodev Do not recognize devices on the remote mount. suid Allow remote files on this mountpoint with suid enabled to be executed (default for mounts when executed as root, nosuid is default for user mounts). credentials Although ignored by the cifs kernel component, it is used by the mount helper, mount.cifs. When mount.cifs is installed it opens and reads the credential file specified in order to obtain the userid and password arguments which are passed to the cifs vfs. guest Although ignored by the kernel component, the mount.cifs mount helper will not prompt the user for a password if guest is specified on the mount options. If no password is specified a null password will be used. perm Client does permission checks (vfs_permission check of uid and gid of the file against the mode and desired operation), Note that this is in addition to the normal ACL check on the target machine done by the server software. Client permission checking is enabled by default. noperm Client does not do permission checks. This can expose files on this mount to access by other users on the local client system. It is typically only needed when the server supports the CIFS Unix Extensions but the UIDs/GIDs on the client and server system do not match closely enough to allow access by the user doing the mount, but it may be useful with non CIFS Unix Extension mounts for cases in which the default mode is specified on the mount but is not to be enforced on the client (e.g. perhaps when MultiUserMount is enabled) Note that this does not affect the normal ACL check on the target machine done by the server software (of the server ACL against the user name provided at mount time). serverino Use server's inode numbers instead of generating automatically incrementing inode numbers on the client. Although this will make it easier to spot hardlinked files (as they will have the same inode numbers) and inode numbers may be persistent, note that the server does not guarantee that the inode numbers are unique if multiple server side mounts are exported under a single share (since inode numbers on the servers might not be unique if multiple filesystems are mounted under the same shared higher level directory). Note that some older (e.g. pre-Windows 2000) do not support returning UniqueIDs or the CIFS Unix Extensions equivalent and for those this mount option will have no effect. Exporting cifs mounts under nfsd requires this mount option on the cifs mount. noserverino Client generates inode numbers (rather than using the actual one from the server) by default. setuids If the CIFS Unix extensions are negotiated with the server the client will attempt to set the effective uid and gid of the local process on newly created files, directories, and devices (create, mkdir, mknod). If the CIFS Unix Extensions are not negotiated, for newly created files and directories instead of using the default uid and gid specified on the mount, cache the new file's uid and gid locally which means that the uid for the file can change when the inode is reloaded (or the user remounts the share). nosetuids The client will not attempt to set the uid and gid on on newly created files, directories, and devices (create, mkdir, mknod) which will result in the server setting the uid and gid to the default (usually the server uid of the user who mounted the share). Letting the server (rather than the client) set the uid and gid is the default. If the CIFS Unix Extensions are not negotiated then the uid and gid for new files will appear to be the uid (gid) of the mounter or the uid (gid) parameter specified on the mount. netbiosname When mounting to servers via port 139, specifies the RFC1001 source name to use to represent the client netbios machine name when doing the RFC1001 netbios session initialize. direct Do not do inode data caching on files opened on this mount. This precludes mmaping files on this mount. In some cases with fast networks and little or no caching benefits on the client (e.g. when the application is doing large sequential reads bigger than page size without rereading the same data) this can provide better performance than the default behavior which caches reads (readahead) and writes (writebehind) through the local Linux client pagecache if oplock (caching token) is granted and held. Note that direct allows write operations larger than page size to be sent to the server. acl Allow setfacl and getfacl to manage posix ACLs if server supports them. (default) noacl Do not allow setfacl and getfacl calls on this mount user_xattr Allow getting and setting user xattrs (those attributes whose name begins with "user." or "os2.") as OS/2 EAs (extended attributes) to the server. This allows support of the setfattr and getfattr utilities. (default) nouser_xattr Do not allow getfattr/setfattr to get/set/list xattrs mapchars Translate six of the seven reserved characters (not backslash) *?<>|: to the remap range (above 0xF000), which also allows the CIFS client to recognize files created with such characters by Windows's POSIX emulation. This can also be useful when mounting to most versions of Samba (which also forbids creating and opening files whose names contain any of these seven characters). This has no effect if the server does not support Unicode on the wire. nomapchars Do not translate any of these seven characters (default). nocase Request case insensitive path name matching (case sensitive is the default if the server suports it). (mount option "ignorecase" is identical to "nocase") posixpaths If CIFS Unix extensions are supported, attempt to negotiate posix path name support which allows certain characters forbidden in typical CIFS filenames, without requiring remapping. (default) noposixpaths If CIFS Unix extensions are supported, do not request posix path name support (this may cause servers to reject creatingfile with certain reserved characters). nounix Disable the CIFS Unix Extensions for this mount (tree connection). This is rarely needed, but it may be useful in order to turn off multiple settings all at once (ie posix acls, posix locks, posix paths, symlink support and retrieving uids/gids/mode from the server) or to work around a bug in server which implement the Unix Extensions. nobrl Do not send byte range lock requests to the server. This is necessary for certain applications that break with cifs style mandatory byte range locks (and most cifs servers do not yet support requesting advisory byte range locks). forcemandatorylock Even if the server supports posix (advisory) byte range locking, send only mandatory lock requests. For some (presumably rare) applications, originally coded for DOS/Windows, which require Windows style mandatory byte range locking, they may be able to take advantage of this option, forcing the cifs client to only send mandatory locks even if the cifs server would support posix advisory locks. "forcemand" is accepted as a shorter form of this mount option. nodfs Disable DFS (global name space support) even if the server claims to support it. This can help work around a problem with parsing of DFS paths with Samba server versions 3.0.24 and 3.0.25. remount remount the share (often used to change from ro to rw mounts or vice versa) cifsacl Report mode bits (e.g. on stat) based on the Windows ACL for the file. (EXPERIMENTAL) servern Specify the server 's netbios name (RFC1001 name) to use when attempting to setup a session to the server. This is needed for mounting to some older servers (such as OS/2 or Windows 98 and Windows ME) since they do not support a default server name. A server name can be up to 15 characters long and is usually uppercased. sfu When the CIFS Unix Extensions are not negotiated, attempt to create device files and fifos in a format compatible with Services for Unix (SFU). In addition retrieve bits 10-12 of the mode via the SETFILEBITS extended attribute (as SFU does). In the future the bottom 9 bits of the mode also will be emulated using queries of the security descriptor (ACL). sign Must use packet signing (helps avoid unwanted data modification by intermediate systems in the route). Note that signing does not work with lanman or plaintext authentication. seal Must seal (encrypt) all data on this mounted share before sending on the network. Requires support for Unix Extensions. Note that this differs from the sign mount option in that it causes encryption of data sent over this mounted share but other shares mounted to the same server are unaffected. locallease This option is rarely needed. Fcntl F_SETLEASE is used by some applications such as Samba and NFSv4 server to check to see whether a file is cacheable. CIFS has no way to explicitly request a lease, but can check whether a file is cacheable (oplocked). Unfortunately, even if a file is not oplocked, it could still be cacheable (ie cifs client could grant fcntl leases if no other local processes are using the file) for cases for example such as when the server does not support oplocks and the user is sure that the only updates to the file will be from this client. Specifying this mount option will allow the cifs client to check for leases (only) locally for files which are not oplocked instead of denying leases in that case. (EXPERIMENTAL) sec Security mode. Allowed values are: none attempt to connection as a null user (no name) krb5 Use Kerberos version 5 authentication krb5i Use Kerberos authentication and packet signing ntlm Use NTLM password hashing (default) ntlmi Use NTLM password hashing with signing (if /proc/fs/cifs/PacketSigningEnabled on or if server requires signing also can be the default) ntlmv2 Use NTLMv2 password hashing ntlmv2i Use NTLMv2 password hashing with packet signing lanman (if configured in kernel config) use older lanman hash hard Retry file operations if server is not responding soft Limit retries to unresponsive servers (usually only one retry) before returning an error. (default) The mount.cifs mount helper also accepts a few mount options before -o including: -S take password from stdin (equivalent to setting the environment variable "PASSWD_FD=0" -V print mount.cifs version -? display simple usage information With most 2.6 kernel versions of modutils, the version of the cifs kernel module can be displayed via modinfo. Misc /proc/fs/cifs Flags and Debug Info ======================================= Informational pseudo-files: DebugData Displays information about active CIFS sessions and shares, as well as the cifs.ko version. Stats Lists summary resource usage information as well as per share statistics, if CONFIG_CIFS_STATS in enabled in the kernel configuration. Configuration pseudo-files: MultiuserMount If set to one, more than one CIFS session to the same server ip address can be established if more than one uid accesses the same mount point and if the uids user/password mapping information is available. (default is 0) PacketSigningEnabled If set to one, cifs packet signing is enabled and will be used if the server requires it. If set to two, cifs packet signing is required even if the server considers packet signing optional. (default 1) SecurityFlags Flags which control security negotiation and also packet signing. Authentication (may/must) flags (e.g. for NTLM and/or NTLMv2) may be combined with the signing flags. Specifying two different password hashing mechanisms (as "must use") on the other hand does not make much sense. Default flags are 0x07007 (NTLM, NTLMv2 and packet signing allowed). The maximum allowable flags if you want to allow mounts to servers using weaker password hashes is 0x37037 (lanman, plaintext, ntlm, ntlmv2, signing allowed). Some SecurityFlags require the corresponding menuconfig options to be enabled (lanman and plaintext require CONFIG_CIFS_WEAK_PW_HASH for example). Enabling plaintext authentication currently requires also enabling lanman authentication in the security flags because the cifs module only supports sending laintext passwords using the older lanman dialect form of the session setup SMB. (e.g. for authentication using plain text passwords, set the SecurityFlags to 0x30030): may use packet signing 0x00001 must use packet signing 0x01001 may use NTLM (most common password hash) 0x00002 must use NTLM 0x02002 may use NTLMv2 0x00004 must use NTLMv2 0x04004 may use Kerberos security 0x00008 must use Kerberos 0x08008 may use lanman (weak) password hash 0x00010 must use lanman password hash 0x10010 may use plaintext passwords 0x00020 must use plaintext passwords 0x20020 (reserved for future packet encryption) 0x00040 cifsFYI If set to non-zero value, additional debug information will be logged to the system error log. This field contains three flags controlling different classes of debugging entries. The maximum value it can be set to is 7 which enables all debugging points (default 0). Some debugging statements are not compiled into the cifs kernel unless CONFIG_CIFS_DEBUG2 is enabled in the kernel configuration. cifsFYI may be set to one or nore of the following flags (7 sets them all): log cifs informational messages 0x01 log return codes from cifs entry points 0x02 log slow responses (ie which take longer than 1 second) CONFIG_CIFS_STATS2 must be enabled in .config 0x04 traceSMB If set to one, debug information is logged to the system error log with the start of smb requests and responses (default 0) LookupCacheEnable If set to one, inode information is kept cached for one second improving performance of lookups (default 1) OplockEnabled If set to one, safe distributed caching enabled. (default 1) LinuxExtensionsEnabled If set to one then the client will attempt to use the CIFS "UNIX" extensions which are optional protocol enhancements that allow CIFS servers to return accurate UID/GID information as well as support symbolic links. If you use servers such as Samba that support the CIFS Unix extensions but do not want to use symbolic link support and want to map the uid and gid fields to values supplied at mount (rather than the actual values, then set this to zero. (default 1) Experimental When set to 1 used to enable certain experimental features (currently enables multipage writes when signing is enabled, the multipage write performance enhancement was disabled when signing turned on in case buffer was modified just before it was sent, also this flag will be used to use the new experimental directory change notification code). These experimental features and tracing can be enabled by changing flags in /proc/fs/cifs (after the cifs module has been installed or built into the kernel, e.g. insmod cifs). To enable a feature set it to 1 e.g. to enable tracing to the kernel message log type: echo 7 > /proc/fs/cifs/cifsFYI cifsFYI functions as a bit mask. Setting it to 1 enables additional kernel logging of various informational messages. 2 enables logging of non-zero SMB return codes while 4 enables logging of requests that take longer than one second to complete (except for byte range lock requests). Setting it to 4 requires defining CONFIG_CIFS_STATS2 manually in the source code (typically by setting it in the beginning of cifsglob.h), and setting it to seven enables all three. Finally, tracing the start of smb requests and responses can be enabled via: echo 1 > /proc/fs/cifs/traceSMB Two other experimental features are under development. To test these requires enabling CONFIG_CIFS_EXPERIMENTAL cifsacl support needed to retrieve approximated mode bits based on the contents on the CIFS ACL. lease support: cifs will check the oplock state before calling into the vfs to see if we can grant a lease on a file. DNOTIFY fcntl: needed for support of directory change notification and perhaps later for file leases) Per share (per client mount) statistics are available in /proc/fs/cifs/Stats if the kernel was configured with cifs statistics enabled. The statistics represent the number of successful (ie non-zero return code from the server) SMB responses to some of the more common commands (open, delete, mkdir etc.). Also recorded is the total bytes read and bytes written to the server for that share. Note that due to client caching effects this can be less than the number of bytes read and written by the application running on the client. The statistics for the number of total SMBs and oplock breaks are different in that they represent all for that share, not just those for which the server returned success. Also note that "cat /proc/fs/cifs/DebugData" will display information about the active sessions and the shares that are mounted. Enabling Kerberos (extended security) works but requires version 1.2 or later of the helper program cifs.upcall to be present and to be configured in the /etc/request-key.conf file. The cifs.upcall helper program is from the Samba project(http://www.samba.org). NTLM and NTLMv2 and LANMAN support do not require this helper. Note that NTLMv2 security (which does not require the cifs.upcall helper program), instead of using Kerberos, is sufficient for some use cases. Enabling DFS support (used to access shares transparently in an MS-DFS global name space) requires that CONFIG_CIFS_EXPERIMENTAL be enabled. In addition, DFS support for target shares which are specified as UNC names which begin with host names (rather than IP addresses) requires a user space helper (such as cifs.upcall) to be present in order to translate host names to ip address, and the user space helper must also be configured in the file /etc/request-key.conf To use cifs Kerberos and DFS support, the Linux keyutils package should be installed and something like the following lines should be added to the /etc/request-key.conf file: create cifs.spnego * * /usr/local/sbin/cifs.upcall %k create dns_resolver * * /usr/local/sbin/cifs.upcall %k cifs-test-base/rfc1002pdu.h0000644000175000017500000000550111117756172015211 0ustar stevefstevef/* * 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 " cifs-test-base/sess.c0000644000175000017500000004437211117762256014404 0ustar stevefstevef/* * fs/cifs/sess.c * * SMB/CIFS session setup handling routines * * Copyright (c) International Business Machines Corp., 2006, 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 #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "ntlmssp.h" #include "nterr.h" #include #include "cifs_spnego.h" extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) { __u32 capabilities = 0; /* init fields common to all four types of SessSetup */ /* note that header is initialized to zero in header_assemble */ pSMB->req.AndXCommand = 0xFF; pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); /* 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->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) 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; /* BB check whether to init vcnum BB */ 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_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32, nls_cp); bcc_ptr += 2 * bytes_ret; bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release, 32, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* trailing null */ bytes_ret = cifs_strtoUCS((__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 cifsSesInfo *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_strtoUCS((__le16 *) bcc_ptr, ses->domainName, 256, 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 cifsSesInfo *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->userName == NULL) { /* null user mount */ *bcc_ptr = 0; *(bcc_ptr+1) = 0; } else { /* 300 should be long enough for any conceivable user name */ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName, 300, 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 cifsSesInfo *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->userName == NULL) { /* BB what about null user mounts - check that we do this BB */ } else { /* 300 should be long enough for any conceivable user name */ strncpy(bcc_ptr, ses->userName, 300); } /* BB improve check for overflow */ bcc_ptr += strnlen(ses->userName, 300); *bcc_ptr = 0; bcc_ptr++; /* account for null termination */ /* copy domain */ if (ses->domainName != NULL) { strncpy(bcc_ptr, ses->domainName, 256); bcc_ptr += strnlen(ses->domainName, 256); } /* 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 int decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int rc = 0; int words_left, len; char *data = *pbcc_area; cFYI(1, ("bleft %d", bleft)); /* SMB header is unaligned, so cifs servers word align start of Unicode strings */ data++; bleft--; /* Windows servers do not always double null terminate their final Unicode string - in which case we now will not attempt to decode the byte of junk which follows it */ words_left = bleft / 2; /* save off server operating system */ len = UniStrnlen((wchar_t *) data, words_left); /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ if (len >= words_left) return rc; kfree(ses->serverOS); /* UTF-8 string will not grow more than four times as big as UCS-16 */ ses->serverOS = kzalloc(4 * len, GFP_KERNEL); if (ses->serverOS != NULL) cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp); data += 2 * (len + 1); words_left -= len + 1; /* save off server network operating system */ len = UniStrnlen((wchar_t *) data, words_left); if (len >= words_left) return rc; kfree(ses->serverNOS); ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */ if (ses->serverNOS != NULL) { cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len, nls_cp); if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) { cFYI(1, ("NT4 server")); ses->flags |= CIFS_SES_NT4; } } data += 2 * (len + 1); words_left -= len + 1; /* save off server domain */ len = UniStrnlen((wchar_t *) data, words_left); if (len > words_left) return rc; kfree(ses->serverDomain); ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */ if (ses->serverDomain != NULL) { cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len, nls_cp); ses->serverDomain[2*len] = 0; ses->serverDomain[(2*len) + 1] = 0; } data += 2 * (len + 1); words_left -= len + 1; cFYI(1, ("words left: %d", words_left)); return rc; } static int decode_ascii_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int rc = 0; int len; char *bcc_ptr = *pbcc_area; cFYI(1, ("decode sessetup ascii. bleft %d", bleft)); len = strnlen(bcc_ptr, bleft); if (len >= bleft) return rc; 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) { cFYI(1, ("OS/2 server")); ses->flags |= CIFS_SES_OS2; } bcc_ptr += len + 1; bleft -= len + 1; len = strnlen(bcc_ptr, bleft); if (len >= bleft) return rc; 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 rc; /* 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 */ cFYI(1, ("ascii: bytes left %d", bleft)); return rc; } int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, 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; int count; int resp_buf_type; struct kvec iov[3]; enum securityEnum type; __u16 action; int bytes_remaining; struct key *spnego_key = NULL; if (ses == NULL) return -EINVAL; type = ses->server->secType; cFYI(1, ("sess setup type %d", type)); 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 = 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; ses->flags &= ~CIFS_SES_LANMAN; iov[1].iov_base = NULL; iov[1].iov_len = 0; if (type == LANMAN) { #ifdef CONFIG_CIFS_WEAK_PW_HASH char lnm_session_key[CIFS_SESS_KEY_SIZE]; pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; /* no capabilities flags in old lanman negotiation */ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); /* BB calculate hash with password */ /* and copy into bcc */ calc_lanman_hash(ses->password, ses->server->cryptKey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, lnm_session_key); ses->flags |= CIFS_SES_LANMAN; memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_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? */ cFYI(1, ("Negotiating LANMAN setting up strings")); /* Unicode not allowed for LANMAN dialects */ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); #endif } else if (type == NTLM) { char ntlm_session_key[CIFS_SESS_KEY_SIZE]; pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.CaseInsensitivePasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); /* calculate session key */ SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key); if (first_time) /* should this be moved into common code with similar ntlmv2 path? */ cifs_calculate_mac_key(&ses->server->mac_signing_key, ntlm_session_key, ses->password); /* copy session key */ memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_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) { char *v2_sess_key = kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL); /* BB FIXME change all users of v2_sess_key to struct ntlmv2_resp */ if (v2_sess_key == NULL) { rc = -ENOMEM; goto ssetup_exit; } pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); /* LM2 password would be here if we supported it */ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; /* cpu_to_le16(LM2_SESS_KEY_SIZE); */ pSMB->req_no_secext.CaseSensitivePasswordLength = cpu_to_le16(sizeof(struct ntlmv2_resp)); /* calculate session key */ setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); if (first_time) /* should this be moved into common code with similar ntlmv2 path? */ /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key, response BB FIXME, v2_sess_key); */ /* copy session key */ /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE); bcc_ptr += LM2_SESS_KEY_SIZE; */ memcpy(bcc_ptr, (char *)v2_sess_key, sizeof(struct ntlmv2_resp)); bcc_ptr += sizeof(struct ntlmv2_resp); kfree(v2_sess_key); 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 || type == MSKerberos) { #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) { cERROR(1, ("incorrect version of cifs.upcall (expected" " %d but got %d)", CIFS_SPNEGO_UPCALL_VERSION, msg->version)); rc = -EKEYREJECTED; goto ssetup_exit; } /* bail out if key is too long */ if (msg->sesskey_len > sizeof(ses->server->mac_signing_key.data.krb5)) { cERROR(1, ("Kerberos signing key too long (%u bytes)", msg->sesskey_len)); rc = -EOVERFLOW; goto ssetup_exit; } if (first_time) { ses->server->mac_signing_key.len = msg->sesskey_len; memcpy(ses->server->mac_signing_key.data.krb5, msg->data, 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 */ cERROR(1, ("Kerberos negotiated but upcall support disabled!")); rc = -ENOSYS; goto ssetup_exit; #endif /* CONFIG_CIFS_UPCALL */ } else { cERROR(1, ("secType %d not supported!", 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 += count; BCC_LE(smb_buf) = cpu_to_le16(count); rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); /* SMB request buf freed in SendReceive2 */ cFYI(1, ("ssetup rc from sendrecv2 is %d", rc)); if (rc) goto ssetup_exit; pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; smb_buf = (struct smb_hdr *)iov[0].iov_base; if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) { rc = -EIO; cERROR(1, ("bad word count %d", smb_buf->WordCount)); goto ssetup_exit; } action = le16_to_cpu(pSMB->resp.Action); if (action & GUEST_LOGIN) cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ cFYI(1, ("UID = %d ", ses->Suid)); /* response can have either 3 or 4 word count - Samba sends 3 */ /* and lanman response is 3 */ bytes_remaining = BCC(smb_buf); bcc_ptr = pByteArea(smb_buf); if (smb_buf->WordCount == 4) { __u16 blob_len; blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); bcc_ptr += blob_len; if (blob_len > bytes_remaining) { cERROR(1, ("bad security blob length %d", blob_len)); rc = -EINVAL; goto ssetup_exit; } bytes_remaining -= blob_len; } /* BB check if Unicode and decode strings */ if (smb_buf->Flags2 & SMBFLG2_UNICODE) rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); else rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); ssetup_exit: if (spnego_key) { key_revoke(spnego_key); key_put(spnego_key); } kfree(str_area); if (resp_buf_type == CIFS_SMALL_BUFFER) { cFYI(1, ("ssetup freeing small buf %p", 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); return rc; } cifs-test-base/smbdes.c0000644000175000017500000002411511117756172014675 0ustar stevefstevef/* Unix SMB/Netbios implementation. Version 1.9. a partial implementation of DES designed for use in the SMB authentication protocol Copyright (C) Andrew Tridgell 1998 Modified by Steve French (sfrench@us.ibm.com) 2002,2004 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. */ /* NOTES: This code makes no attempt to be fast! In fact, it is a very slow implementation This code is NOT a complete DES implementation. It implements only the minimum necessary for SMB authentication, as used by all SMB products (including every copy of Microsoft Windows95 ever sold) In particular, it can only do a unchained forward DES pass. This means it is not possible to use this code for encryption/decryption of data, instead it is only useful as a "hash" algorithm. There is no entry point into this code that allows normal DES operation. I believe this means that this code does not come under ITAR regulations but this is NOT a legal opinion. If you are concerned about the applicability of ITAR regulations to this code then you should confirm it for yourself (and maybe let me know if you come up with a different answer to the one above) */ #include #include "cifsencrypt.h" #define uchar unsigned char static uchar perm1[56] = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 }; static uchar perm2[48] = { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 }; static uchar perm3[64] = { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 }; static uchar perm4[48] = { 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 }; static uchar perm5[32] = { 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 }; static uchar perm6[64] = { 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 }; static uchar sc[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; static uchar sbox[8][4][16] = { {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13} }, {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9} }, {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12} }, {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14} }, {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14}, {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3} }, {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11}, {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8}, {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6}, {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13} }, {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6}, {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2}, {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12} }, {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7}, {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8}, {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11} } }; static void permute(char *out, char *in, uchar *p, int n) { int i; for (i = 0; i < n; i++) out[i] = in[p[i] - 1]; } static void lshift(char *d, int count, int n) { char out[64]; int i; for (i = 0; i < n; i++) out[i] = d[(i + count) % n]; for (i = 0; i < n; i++) d[i] = out[i]; } static void concat(char *out, char *in1, char *in2, int l1, int l2) { while (l1--) *out++ = *in1++; while (l2--) *out++ = *in2++; } static void xor(char *out, char *in1, char *in2, int n) { int i; for (i = 0; i < n; i++) out[i] = in1[i] ^ in2[i]; } static void dohash(char *out, char *in, char *key, int forw) { int i, j, k; char *pk1; char c[28]; char d[28]; char *cd; char (*ki)[48]; char *pd1; char l[32], r[32]; char *rl; /* Have to reduce stack usage */ pk1 = kmalloc(56+56+64+64, GFP_KERNEL); if (pk1 == NULL) return; ki = kmalloc(16*48, GFP_KERNEL); if (ki == NULL) { kfree(pk1); return; } cd = pk1 + 56; pd1 = cd + 56; rl = pd1 + 64; permute(pk1, key, perm1, 56); for (i = 0; i < 28; i++) c[i] = pk1[i]; for (i = 0; i < 28; i++) d[i] = pk1[i + 28]; for (i = 0; i < 16; i++) { lshift(c, sc[i], 28); lshift(d, sc[i], 28); concat(cd, c, d, 28, 28); permute(ki[i], cd, perm2, 48); } permute(pd1, in, perm3, 64); for (j = 0; j < 32; j++) { l[j] = pd1[j]; r[j] = pd1[j + 32]; } for (i = 0; i < 16; i++) { char *er; /* er[48] */ char *erk; /* erk[48] */ char b[8][6]; char *cb; /* cb[32] */ char *pcb; /* pcb[32] */ char *r2; /* r2[32] */ er = kmalloc(48+48+32+32+32, GFP_KERNEL); if (er == NULL) { kfree(pk1); kfree(ki); return; } erk = er+48; cb = erk+48; pcb = cb+32; r2 = pcb+32; permute(er, r, perm4, 48); xor(erk, er, ki[forw ? i : 15 - i], 48); for (j = 0; j < 8; j++) for (k = 0; k < 6; k++) b[j][k] = erk[j * 6 + k]; for (j = 0; j < 8; j++) { int m, n; m = (b[j][0] << 1) | b[j][5]; n = (b[j][1] << 3) | (b[j][2] << 2) | (b[j][3] << 1) | b[j][4]; for (k = 0; k < 4; k++) b[j][k] = (sbox[j][m][n] & (1 << (3 - k))) ? 1 : 0; } for (j = 0; j < 8; j++) for (k = 0; k < 4; k++) cb[j * 4 + k] = b[j][k]; permute(pcb, cb, perm5, 32); xor(r2, l, pcb, 32); for (j = 0; j < 32; j++) l[j] = r[j]; for (j = 0; j < 32; j++) r[j] = r2[j]; kfree(er); } concat(rl, r, l, 32, 32); permute(out, rl, perm6, 64); kfree(pk1); kfree(ki); } 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 void smbhash(unsigned char *out, const unsigned char *in, unsigned char *key, int forw) { int i; char *outb; /* outb[64] */ char *inb; /* inb[64] */ char *keyb; /* keyb[64] */ unsigned char key2[8]; outb = kmalloc(64 * 3, GFP_KERNEL); if (outb == NULL) return; inb = outb + 64; keyb = inb + 64; str_to_key(key, key2); for (i = 0; i < 64; i++) { inb[i] = (in[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0; keyb[i] = (key2[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0; outb[i] = 0; } dohash(outb, inb, keyb, forw); for (i = 0; i < 8; i++) out[i] = 0; for (i = 0; i < 64; i++) { if (outb[i]) out[i / 8] |= (1 << (7 - (i % 8))); } kfree(outb); } void E_P16(unsigned char *p14, unsigned char *p16) { unsigned char sp8[8] = { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; smbhash(p16, sp8, p14, 1); smbhash(p16 + 8, sp8, p14 + 7, 1); } void E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) { smbhash(p24, c8, p21, 1); smbhash(p24 + 8, c8, p21 + 7, 1); smbhash(p24 + 16, c8, p21 + 14, 1); } #if 0 /* currently unsued */ static void D_P16(unsigned char *p14, unsigned char *in, unsigned char *out) { smbhash(out, in, p14, 0); smbhash(out + 8, in + 8, p14 + 7, 0); } static void E_old_pw_hash(unsigned char *p14, unsigned char *in, unsigned char *out) { smbhash(out, in, p14, 1); smbhash(out + 8, in + 8, p14 + 7, 1); } /* these routines are currently unneeded, but may be needed later */ void cred_hash1(unsigned char *out, unsigned char *in, unsigned char *key) { unsigned char buf[8]; smbhash(buf, in, key, 1); smbhash(out, buf, key + 9, 1); } void cred_hash2(unsigned char *out, unsigned char *in, unsigned char *key) { unsigned char buf[8]; static unsigned char key2[8]; smbhash(buf, in, key, 1); key2[0] = key[7]; smbhash(out, buf, key2, 1); } void cred_hash3(unsigned char *out, unsigned char *in, unsigned char *key, int forw) { static unsigned char key2[8]; smbhash(out, in, key, forw); key2[0] = key[7]; smbhash(out + 8, in + 8, key2, forw); } #endif /* unneeded routines */ cifs-test-base/smbencrypt.c0000644000175000017500000001673411117756172015616 0ustar stevefstevef/* 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 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 #include #include #include #include #include "cifs_unicode.h" #include "cifspdu.h" #include "cifsglob.h" #include "md5.h" #include "cifs_debug.h" #include "cifsencrypt.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))) /*The following definitions come from libsmb/smbencrypt.c */ void SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24); void E_md4hash(const unsigned char *passwd, unsigned char *p16); static void SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8, unsigned char p24[24]); void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); /* 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 */ void SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24) { unsigned char p14[15], p21[21]; memset(p21, '\0', 21); memset(p14, '\0', 14); strncpy((char *) p14, (char *) passwd, 14); /* strupper((char *)p14); *//* BB at least uppercase the easy range */ E_P16(p14, p21); SMBOWFencrypt(p21, c8, p24); memset(p14, 0, 15); memset(p21, 0, 21); } /* Routines for Windows NT MD4 Hash functions. */ static int _my_wcslen(__u16 *str) { int len = 0; while (*str++ != 0) len++; return len; } /* * Convert a string into an NT UNICODE string. * Note that regardless of processor type * this must be in intel (little-endian) * format. */ static int _my_mbstowcs(__u16 *dst, const unsigned char *src, int len) { /* BB not a very good conversion routine - change/fix */ int i; __u16 val; for (i = 0; i < len; i++) { val = *src; SSVAL(dst, 0, val); dst++; src++; if (val == 0) break; } return i; } /* * Creates the MD4 Hash of the users password in NT UNICODE. */ void E_md4hash(const unsigned char *passwd, unsigned char *p16) { int len; __u16 wpwd[129]; /* Password cannot be longer than 128 characters */ if (passwd) { len = strlen((char *) passwd); if (len > 128) len = 128; /* Password must be converted to NT unicode */ _my_mbstowcs(wpwd, passwd, len); } else len = 0; wpwd[len] = 0; /* Ensure string is null terminated */ /* Calculate length in bytes */ len = _my_wcslen(wpwd) * sizeof(__u16); mdfour(p16, (unsigned char *) wpwd, len); memset(wpwd, 0, 129 * 2); } #if 0 /* currently unused */ /* Does both the NT and LM owfs of a user's password */ static void nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16]) { char passwd[514]; memset(passwd, '\0', 514); if (strlen(pwd) < 513) strcpy(passwd, pwd); else memcpy(passwd, pwd, 512); /* Calculate the MD4 hash (NT compatible) of the password */ memset(nt_p16, '\0', 16); E_md4hash(passwd, nt_p16); /* Mangle the passwords into Lanman format */ passwd[14] = '\0'; /* strupper(passwd); */ /* Calculate the SMB (lanman) hash functions of the password */ memset(p16, '\0', 16); E_P16((unsigned char *) passwd, (unsigned char *) p16); /* clear out local copy of user's password (just being paranoid). */ memset(passwd, '\0', sizeof(passwd)); } #endif /* Does the NTLMv2 owfs of a user's password */ #if 0 /* function not needed yet - but will be soon */ static void ntv2_owf_gen(const unsigned char owf[16], const char *user_n, const char *domain_n, unsigned char kr_buf[16], const struct nls_table *nls_codepage) { wchar_t *user_u; wchar_t *dom_u; int user_l, domain_l; struct HMACMD5Context ctx; /* might as well do one alloc to hold both (user_u and dom_u) */ user_u = kmalloc(2048 * sizeof(wchar_t), GFP_KERNEL); if (user_u == NULL) return; dom_u = user_u + 1024; /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ /* BB user and domain may need to be uppercased */ user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage); domain_l = cifs_strtoUCS(dom_u, domain_n, 511, nls_codepage); user_l++; /* trailing null */ domain_l++; hmac_md5_init_limK_to_64(owf, 16, &ctx); hmac_md5_update((const unsigned char *) user_u, user_l * 2, &ctx); hmac_md5_update((const unsigned char *) dom_u, domain_l * 2, &ctx); hmac_md5_final(kr_buf, &ctx); kfree(user_u); } #endif /* Does the des encryption from the NT or LM MD4 hash. */ static void SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8, unsigned char p24[24]) { unsigned char p21[21]; memset(p21, '\0', 21); memcpy(p21, passwd, 16); E_P24(p21, c8, p24); } /* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */ #if 0 /* currently unused */ static void NTLMSSPOWFencrypt(unsigned char passwd[8], unsigned char *ntlmchalresp, unsigned char p24[24]) { unsigned char p21[21]; memset(p21, '\0', 21); memcpy(p21, passwd, 8); memset(p21 + 8, 0xbd, 8); E_P24(p21, ntlmchalresp, p24); } #endif /* Does the NT MD4 hash then des encryption. */ void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24) { unsigned char p21[21]; memset(p21, '\0', 21); E_md4hash(passwd, p21); SMBOWFencrypt(p21, c8, p24); } /* Does the md5 encryption from the NT hash for NTLMv2. */ /* These routines will be needed later */ #if 0 static void SMBOWFencrypt_ntv2(const unsigned char kr[16], const struct data_blob *srv_chal, const struct data_blob *cli_chal, unsigned char resp_buf[16]) { struct HMACMD5Context ctx; hmac_md5_init_limK_to_64(kr, 16, &ctx); hmac_md5_update(srv_chal->data, srv_chal->length, &ctx); hmac_md5_update(cli_chal->data, cli_chal->length, &ctx); hmac_md5_final(resp_buf, &ctx); } static void SMBsesskeygen_ntv2(const unsigned char kr[16], const unsigned char *nt_resp, __u8 sess_key[16]) { struct HMACMD5Context ctx; hmac_md5_init_limK_to_64(kr, 16, &ctx); hmac_md5_update(nt_resp, 16, &ctx); hmac_md5_final((unsigned char *) sess_key, &ctx); } static void SMBsesskeygen_ntv1(const unsigned char kr[16], const unsigned char *nt_resp, __u8 sess_key[16]) { mdfour((unsigned char *) sess_key, (unsigned char *) kr, 16); } #endif cifs-test-base/smberr.h0000644000175000017500000001660411117756172014723 0ustar stevefstevef/* * 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 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 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 cifs-test-base/TODO0000644000175000017500000001234511117756172013746 0ustar stevefstevefVersion 1.53 May 20, 2008 A Partial List of Missing Features ================================== Contributions are welcome. There are plenty of opportunities for visible, important contributions to this module. Here is a partial list of the known problems and missing features: a) Support for SecurityDescriptors(Windows/CIFS ACLs) for chmod/chgrp/chown so that these operations can be supported to Windows servers b) Mapping POSIX ACLs (and eventually NFSv4 ACLs) to CIFS SecurityDescriptors c) Better pam/winbind integration (e.g. to handle uid mapping better) d) Cleanup now unneeded SessSetup code in fs/cifs/connect.c and add back in NTLMSSP code if any servers need it e) fix NTLMv2 signing when two mounts with different users to same server. f) Directory entry caching relies on a 1 second timer, rather than using FindNotify or equivalent. - (started) g) quota support (needs minor kernel change since quota calls to make it to network filesystems or deviceless filesystems) h) investigate sync behavior (including syncpage) and check for proper behavior of intr/nointr i) improve support for very old servers (OS/2 and Win9x for example) Including support for changing the time remotely (utimes command). j) hook lower into the sockets api (as NFS/SunRPC does) to avoid the extra copy in/out of the socket buffers in some cases. k) Better optimize open (and pathbased setfilesize) to reduce the oplock breaks coming from windows srv. Piggyback identical file opens on top of each other by incrementing reference count rather than resending (helps reduce server resource utilization and avoid spurious oplock breaks). l) Improve performance of readpages by sending more than one read at a time when 8 pages or more are requested. In conjuntion add support for async_cifs_readpages. m) Add support for storing symlink info to Windows servers in the Extended Attribute format their SFU clients would recognize. n) Finish fcntl D_NOTIFY support so kde and gnome file list windows will autorefresh (partially complete by Asser). Needs minor kernel vfs change to support removing D_NOTIFY on a file. o) Add GUI tool to configure /proc/fs/cifs settings and for display of the CIFS statistics (started) p) implement support for security and trusted categories of xattrs (requires minor protocol extension) to enable better support for SELINUX q) Implement O_DIRECT flag on open (already supported on mount) r) Create UID mapping facility so server UIDs can be mapped on a per mount or a per server basis to client UIDs or nobody if no mapping exists. This is helpful when Unix extensions are negotiated to allow better permission checking when UIDs differ on the server and client. Add new protocol request to the CIFS protocol standard for asking the server for the corresponding name of a particular uid. s) Add support for CIFS Unix and also the newer POSIX extensions to the server side for Samba 4. t) In support for OS/2 (LANMAN 1.2 and LANMAN2.1 based SMB servers) need to add ability to set time to server (utimes command) u) DOS attrs - returned as pseudo-xattr in Samba format (check VFAT and NTFS for this too) v) mount check for unmatched uids w) Add support for new vfs entry points for setlease and fallocate x) Fix Samba 3 server to handle Linux kernel aio so dbench with lots of processes can proceed better in parallel (on the server) y) Fix Samba 3 to handle reads/writes over 127K (and remove the cifs mount restriction of wsize max being 127K) KNOWN BUGS (updated April 24, 2007) ==================================== See http://bugzilla.samba.org - search on product "CifsVFS" for current bug list. 1) existing symbolic links (Windows reparse points) are recognized but can not be created remotely. They are implemented for Samba and those that support the CIFS Unix extensions, although earlier versions of Samba overly restrict the pathnames. 2) follow_link and readdir code does not follow dfs junctions but recognizes them 3) create of new files to FAT partitions on Windows servers can succeed but still return access denied (appears to be Windows server not cifs client problem) and has not been reproduced recently. NTFS partitions do not have this problem. 4) Unix/POSIX capabilities are reset after reconnection, and affect a few fields in the tree connection but we do do not know which superblocks to apply these changes to. We should probably walk the list of superblocks to set these. Also need to check the flags on the second mount to the same share, and see if we can do the same trick that NFS does to remount duplicate shares. Misc testing to do ================== 1) check out max path names and max path name components against various server types. Try nested symlinks (8 deep). Return max path name in stat -f information 2) Modify file portion of ltp so it can run against a mounted network share and run it against cifs vfs in automated fashion. 3) Additional performance testing and optimization using iozone and similar - there are some easy changes that can be done to parallelize sequential writes, and when signing is disabled to request larger read sizes (larger than negotiated size) and send larger write sizes to modern servers. 4) More exhaustively test against less common servers. More testing against Windows 9x, Windows ME servers. cifs-test-base/transport.c0000644000175000017500000007205711117756172015464 0ustar stevefstevef/* * 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 #include #include #include #include #include #include #include #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" extern mempool_t *cifs_mid_poolp; extern struct kmem_cache *cifs_oplock_cachep; static struct mid_q_entry * AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) { struct mid_q_entry *temp; if (server == NULL) { cERROR(1, ("Null TCP session in AllocMidQEntry")); 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 = smb_buffer->Command; cFYI(1, ("For smb_command %d", temp->command)); /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* when mid allocated can be before when sent */ temp->when_alloc = jiffies; temp->tsk = current; } spin_lock(&GlobalMid_Lock); list_add_tail(&temp->qhead, &server->pending_mid_q); atomic_inc(&midCount); temp->midState = MID_REQUEST_ALLOCATED; spin_unlock(&GlobalMid_Lock); return temp; } static void DeleteMidQEntry(struct mid_q_entry *midEntry) { #ifdef CONFIG_CIFS_STATS2 unsigned long now; #endif spin_lock(&GlobalMid_Lock); midEntry->midState = MID_FREE; list_del(&midEntry->qhead); atomic_dec(&midCount); spin_unlock(&GlobalMid_Lock); if (midEntry->largeBuf) 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 != SMB_COM_LOCKING_ANDX)) { printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d", 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); } struct oplock_q_entry * AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon) { struct oplock_q_entry *temp; if ((pinode == NULL) || (tcon == NULL)) { cERROR(1, ("Null parms passed to AllocOplockQEntry")); return NULL; } temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep, GFP_KERNEL); if (temp == NULL) return temp; else { temp->pinode = pinode; temp->tcon = tcon; temp->netfid = fid; spin_lock(&GlobalMid_Lock); list_add_tail(&temp->qhead, &GlobalOplock_Q); spin_unlock(&GlobalMid_Lock); } return temp; } void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry) { spin_lock(&GlobalMid_Lock); /* should we check if list empty first? */ list_del(&oplockEntry->qhead); spin_unlock(&GlobalMid_Lock); kmem_cache_free(cifs_oplock_cachep, oplockEntry); } void DeleteTconOplockQEntries(struct cifsTconInfo *tcon) { struct oplock_q_entry *temp; if (tcon == NULL) return; spin_lock(&GlobalMid_Lock); list_for_each_entry(temp, &GlobalOplock_Q, qhead) { if ((temp->tcon) && (temp->tcon == tcon)) { list_del(&temp->qhead); kmem_cache_free(cifs_oplock_cachep, temp); } } spin_unlock(&GlobalMid_Lock); } int smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, unsigned int smb_buf_length, struct sockaddr *sin, bool noblocksnd) { int rc = 0; int i = 0; struct msghdr smb_msg; struct kvec iov; unsigned len = smb_buf_length + 4; if (ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ iov.iov_base = smb_buffer; iov.iov_len = len; smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof(struct sockaddr); smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; if (noblocksnd) smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; else smb_msg.msg_flags = MSG_NOSIGNAL; /* smb header is converted in header_assemble. bcc and rest of SMB word area, and byte area if necessary, is converted to littleendian in cifssmb.c and RFC1001 len is converted to bigendian in smb_send Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); cFYI(1, ("Sending smb of length %d", smb_buf_length)); dump_smb(smb_buffer, len); while (len > 0) { rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; /* smaller timeout here than send2 since smaller size */ /* Although it may not be required, this also is smaller oplock break time */ if (i > 12) { cERROR(1, ("sends on sock %p stuck for 7 seconds", ssocket)); rc = -EAGAIN; break; } msleep(1 << i); continue; } if (rc < 0) break; else i = 0; /* reset i after each successful send */ iov.iov_base += rc; iov.iov_len -= rc; len -= rc; } if (rc < 0) { cERROR(1, ("Error %d sending data on socket to server", rc)); } else { rc = 0; } /* Don't want to modify the buffer as a side effect of this call. */ smb_buffer->smb_buf_length = smb_buf_length; return rc; } static int smb_send2(struct TCP_Server_Info *server, struct kvec *iov, int n_vec, struct sockaddr *sin, bool noblocksnd) { int rc = 0; int i = 0; struct msghdr smb_msg; struct smb_hdr *smb_buffer = iov[0].iov_base; unsigned int len = iov[0].iov_len; unsigned int total_len; int first_vec = 0; unsigned int smb_buf_length = smb_buffer->smb_buf_length; struct socket *ssocket = server->ssocket; if (ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof(struct sockaddr); smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; if (noblocksnd) smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; else smb_msg.msg_flags = MSG_NOSIGNAL; /* smb header is converted in header_assemble. bcc and rest of SMB word area, and byte area if necessary, is converted to littleendian in cifssmb.c and RFC1001 len is converted to bigendian in smb_send Flags2 is converted in SendReceive */ total_len = 0; for (i = 0; i < n_vec; i++) total_len += iov[i].iov_len; smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); cFYI(1, ("Sending smb: total_len %d", total_len)); dump_smb(smb_buffer, len); i = 0; while (total_len) { rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec], n_vec - first_vec, total_len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; if (i >= 14) { cERROR(1, ("sends on sock %p stuck for 15 seconds", ssocket)); rc = -EAGAIN; break; } msleep(1 << i); continue; } if (rc < 0) break; if (rc == total_len) { total_len = 0; break; } else if (rc > total_len) { cERROR(1, ("sent %d requested %d", rc, total_len)); break; } if (rc == 0) { /* should never happen, letting socket clear before retrying is our only obvious option here */ cERROR(1, ("tcp sent no data")); msleep(500); continue; } total_len -= 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 */ } if ((total_len > 0) && (total_len != smb_buf_length + 4)) { cFYI(1, ("partial send (%d remaining), terminating session", 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) { cERROR(1, ("Error %d sending data on socket to server", rc)); } else rc = 0; /* Don't want to modify the buffer as a side effect of this call. */ smb_buffer->smb_buf_length = smb_buf_length; return rc; } static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) { if (long_op == CIFS_ASYNC_OP) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); return 0; } spin_lock(&GlobalMid_Lock); while (1) { if (atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->num_waiters); #endif wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < cifs_max_pending); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->num_waiters); #endif spin_lock(&GlobalMid_Lock); } else { if (ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_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 (long_op != CIFS_BLOCKING_OP) atomic_inc(&ses->server->inFlight); spin_unlock(&GlobalMid_Lock); break; } } return 0; } static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct mid_q_entry **ppmidQ) { if (ses->server->tcpStatus == CifsExiting) { return -ENOENT; } if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1, ("tcp session dead - return to caller to retry")); 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; return 0; } static int wait_for_response(struct cifsSesInfo *ses, struct mid_q_entry *midQ, unsigned long timeout, unsigned long time_to_wait) { unsigned long curr_timeout; for (;;) { curr_timeout = timeout + jiffies; wait_event_timeout(ses->server->response_q, midQ->midState != MID_REQUEST_SUBMITTED, timeout); if (time_after(jiffies, curr_timeout) && (midQ->midState == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { unsigned long lrt; /* We timed out. Is the server still sending replies ? */ spin_lock(&GlobalMid_Lock); lrt = ses->server->lstrp; spin_unlock(&GlobalMid_Lock); /* Calculate time_to_wait past last receive time. Although we prefer not to time out if the server is still responding - we will time out if the server takes more than 15 (or 45 or 180) seconds to respond to this request and has not responded to any request from other threads on the client within 10 seconds */ lrt += time_to_wait; if (time_after(jiffies, lrt)) { /* No replies for time_to_wait. */ cERROR(1, ("server not responding")); return -1; } } else { return 0; } } } /* * * 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 cifsSesInfo *ses, struct smb_hdr *in_buf, int flags) { int rc; struct kvec iov[1]; int resp_buf_type; iov[0].iov_base = (char *)in_buf; iov[0].iov_len = in_buf->smb_buf_length + 4; flags |= CIFS_NO_RESP; rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags); cFYI(DBG2, ("SendRcvNoRsp flags %d rc %d", flags, rc)); return rc; } int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, const int flags) { int rc = 0; int long_op; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; long_op = flags & CIFS_TIMEOUT_MASK; *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(in_buf); cERROR(1, ("Null session")); return -EIO; } if (ses->server->tcpStatus == CifsExiting) { cifs_small_buf_release(in_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, long_op); if (rc) { cifs_small_buf_release(in_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); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); goto out; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send2(ses->server, iov, n_vec, (struct sockaddr *) &(ses->server->addr.sockAddr), ses->server->noblocksnd); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); if (rc < 0) goto out; if (long_op == CIFS_STD_OP) timeout = 15 * HZ; else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */ timeout = 180 * HZ; else if (long_op == CIFS_LONG_OP) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else if (long_op == CIFS_ASYNC_OP) goto out; else if (long_op == CIFS_BLOCKING_OP) timeout = 0x7FFFFFFF; /* large, but not so large as to wrap */ else { cERROR(1, ("unknown timeout flag %d", long_op)); rc = -EIO; goto out; } /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1, ("No response to cmd %d mid %d", midQ->command, midQ->mid)); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, ("marking request for retry")); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { iov[0].iov_base = (char *)midQ->resp_buf; if (midQ->largeBuf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; iov[0].iov_len = receive_len + 4; dump_smb(midQ->resp_buf, 80); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(midQ->resp_buf, &ses->server->mac_signing_key, midQ->sequence_number+1); if (rc) { cERROR(1, ("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(midQ->resp_buf, flags & CIFS_LOG_ERROR); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) BCC(midQ->resp_buf) = le16_to_cpu(BCC_LE(midQ->resp_buf)); if ((flags & CIFS_NO_RESP) == 0) midQ->resp_buf = NULL; /* mark it so buf will not be freed by DeleteMidQEntry */ } else { rc = -EIO; cFYI(1, ("Bad MID state?")); } } out: DeleteMidQEntry(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned, const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1, ("Null smb session")); return -EIO; } if (ses->server == NULL) { cERROR(1, ("Null tcp session")); 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 (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, ("Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length)); return -EIO; } rc = wait_for_free_request(ses, long_op); 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 */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); goto out; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr), ses->server->noblocksnd); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (long_op == CIFS_STD_OP) timeout = 15 * HZ; /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ else if (long_op == CIFS_ASYNC_OP) goto out; else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */ timeout = 180 * HZ; else if (long_op == CIFS_LONG_OP) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else if (long_op == CIFS_BLOCKING_OP) timeout = 0x7FFFFFFF; /* large but no so large as to wrap */ else { cERROR(1, ("unknown timeout flag %d", long_op)); rc = -EIO; goto out; } if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1, ("No response for cmd %d mid %d", midQ->command, midQ->mid)); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, ("marking request for retry")); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, &ses->server->mac_signing_key, midQ->sequence_number+1); if (rc) { cERROR(1, ("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; cERROR(1, ("Bad MID state?")); } } out: DeleteMidQEntry(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } /* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */ static int send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct mid_q_entry *midQ) { int rc = 0; struct cifsSesInfo *ses = tcon->ses; __u16 mid = in_buf->Mid; header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); in_buf->Mid = mid; mutex_lock(&ses->server->srv_mutex); rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); return rc; } rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr), ses->server->noblocksnd); mutex_unlock(&ses->server->srv_mutex); 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 cifsTconInfo *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf) { int bytes_returned; struct cifsSesInfo *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 = GetNextMid(ses->server); return SendReceive(xid, ses, in_buf, out_buf, &bytes_returned, CIFS_STD_OP); } int SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned) { int rc = 0; int rstart = 0; unsigned int receive_len; struct mid_q_entry *midQ; struct cifsSesInfo *ses; if (tcon == NULL || tcon->ses == NULL) { cERROR(1, ("Null smb session")); return -EIO; } ses = tcon->ses; if (ses->server == NULL) { cERROR(1, ("Null tcp session")); 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 (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, ("Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length)); return -EIO; } rc = wait_for_free_request(ses, CIFS_BLOCKING_OP); 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) { DeleteMidQEntry(midQ); mutex_unlock(&ses->server->srv_mutex); return rc; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr), ses->server->noblocksnd); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { DeleteMidQEntry(midQ); return rc; } /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, (!(midQ->midState == MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && (midQ->midState == 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_nt_cancel(tcon, in_buf, midQ); if (rc) { DeleteMidQEntry(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) { DeleteMidQEntry(midQ); return rc; } } /* Wait 5 seconds for the response. */ if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) { /* We got the response - restart system call. */ rstart = 1; } } spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1, ("No response for cmd %d mid %d", midQ->command, midQ->mid)); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, ("marking request for retry")); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); return rc; } if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, &ses->server->mac_signing_key, midQ->sequence_number+1); if (rc) { cERROR(1, ("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; cERROR(1, ("Bad MID state?")); } } DeleteMidQEntry(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; return rc; } cifs-test-base/xattr.c0000644000175000017500000002500711117756172014563 0ustar stevefstevef/* * 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 #include #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_USER_PREFIX "user." #define CIFS_XATTR_SYSTEM_PREFIX "system." #define CIFS_XATTR_OS2_PREFIX "os2." #define CIFS_XATTR_SECURITY_PREFIX ".security" #define CIFS_XATTR_TRUSTED_PREFIX "trusted." #define XATTR_TRUSTED_PREFIX_LEN 8 #define XATTR_SECURITY_PREFIX_LEN 9 /* 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 int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *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; xid = GetXid(); cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } if (ea_name == NULL) { cFYI(1, ("Null xattr names not supported")); } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) && (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) { cFYI(1, ("illegal xattr request %s (only user namespace supported)", 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 += 5; /* 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); FreeXid(xid); #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 int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *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; xid = GetXid(); cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } /* 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) { cFYI(1, ("size of EA value too large")); kfree(full_path); FreeXid(xid); return -EOPNOTSUPP; } if (ea_name == NULL) { cFYI(1, ("Null xattr names not supported")); } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 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) cFYI(1, ("attempt to set cifs inode metadata")); ea_name += 5; /* 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, CIFS_XATTR_OS2_PREFIX, 4) == 0) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto set_ea_exit; ea_name += 4; /* 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 { 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); cFYI(1, ("set POSIX ACL rc %d", rc)); #else cFYI(1, ("set POSIX ACL not supported")); #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); cFYI(1, ("set POSIX default ACL rc %d", rc)); #else cFYI(1, ("set default POSIX ACL not supported")); #endif } else { cFYI(1, ("illegal xattr request %s (only user namespace" " supported)", 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); FreeXid(xid); #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 int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *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; xid = GetXid(); cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ if (ea_name == NULL) { cFYI(1, ("Null xattr names not supported")); } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 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) { cFYI(1, ("attempt to query cifs inode metadata")); /* revalidate/getattr then populate from inode */ } /* BB add else when above is implemented */ ea_name += 5; /* skip past user. prefix */ rc = CIFSSMBQueryEA(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, CIFS_XATTR_OS2_PREFIX, 4) == 0) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto get_ea_exit; ea_name += 4; /* skip past os2. prefix */ rc = CIFSSMBQueryEA(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); #ifdef CONFIG_CIFS_EXPERIMENTAL else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { __u16 fid; int oplock = 0; struct cifs_ntsd *pacl = NULL; __u32 buflen = 0; if (experimEnabled) rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* else rc is EOPNOTSUPP from above */ if (rc == 0) { rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl, &buflen); CIFSSMBClose(xid, pTcon, fid); } } #endif /* EXPERIMENTAL */ #else cFYI(1, ("query POSIX ACL not supported yet")); #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 cFYI(1, ("query POSIX default ACL not supported yet")); #endif } else if (strncmp(ea_name, CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { cFYI(1, ("Trusted xattr namespace not supported yet")); } else if (strncmp(ea_name, CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { cFYI(1, ("Security xattr namespace not supported yet")); } else cFYI(1, ("illegal xattr request %s (only user namespace supported)", 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); FreeXid(xid); #endif return rc; } ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) { ssize_t rc = -EOPNOTSUPP; #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *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); pTcon = cifs_sb->tcon; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) return -EOPNOTSUPP; xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } /* 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, data, buf_size, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(full_path); FreeXid(xid); #endif return rc; }