diff options
Diffstat (limited to 'fs/cifs')
64 files changed, 27815 insertions, 12592 deletions
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS deleted file mode 100644 index 7f7fa3c302a..00000000000 --- a/fs/cifs/AUTHORS +++ /dev/null @@ -1,55 +0,0 @@ -Original 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. diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES deleted file mode 100644 index bc0025cdd1c..00000000000 --- a/fs/cifs/CHANGES +++ /dev/null @@ -1,1065 +0,0 @@ -Version 1.62 ------------- -Add sockopt=TCP_NODELAY mount option. EA (xattr) routines hardened -to more strictly handle corrupt frames. - -Version 1.61 ------------- -Fix append problem to Samba servers (files opened with O_APPEND could -have duplicated data). Fix oops in cifs_lookup. Workaround problem -mounting to OS/400 Netserve. Fix oops in cifs_get_tcp_session. -Disable use of server inode numbers when server only -partially supports them (e.g. for one server querying inode numbers on -FindFirst fails but QPathInfo queries works). Fix oops with dfs in  -cifs_put_smb_ses. Fix mmap to work on directio mounts (needed -for OpenOffice when on forcedirectio mount e.g.) - -Version 1.60 -------------- -Fix memory leak in reconnect.  Fix oops in DFS mount error path. -Set s_maxbytes to smaller (the max that vfs can handle) so that -sendfile will now work over cifs mounts again.  Add noforcegid -and noforceuid mount parameters. Fix small mem leak when using -ntlmv2. Fix 2nd mount to same server but with different port to -be allowed (rather than reusing the 1st port) - only when the -user explicitly overrides the port on the 2nd mount. - -Version 1.59 ------------- -Client uses server inode numbers (which are persistent) rather than -client generated ones by default (mount option "serverino" turned -on by default if server supports it).  Add forceuid and forcegid -mount options (so that when negotiating unix extensions specifying -which uid mounted does not immediately force the server's reported -uids to be overridden).  Add support for scope mount parm. Improve -hard link detection to use same inode for both.  Do not set -read-only dos attribute on directories (for chmod) since Windows -explorer special cases this attribute bit for directories for -a different purpose. - -Version 1.58 ------------- -Guard against buffer overruns in various UCS-2 to UTF-8 string conversions -when the UTF-8 string is composed of unusually long (more than 4 byte) converted -characters. Add support for mounting root of a share which redirects immediately -to DFS target. Convert string conversion functions from Unicode to more -accurately mark string length before allocating memory (which may help the -rare cases where a UTF-8 string is much larger than the UCS2 string that -we converted from).  Fix endianness of the vcnum field used during -session setup to distinguish multiple mounts to same server from different -userids. Raw NTLMSSP fixed (it requires /proc/fs/cifs/experimental -flag to be set to 2, and mount must enable krb5 to turn on extended security). -Performance of file create to Samba improved (posix create on lookup -removes 1 of 2 network requests sent on file create) -  -Version 1.57 ------------- -Improve support for multiple security contexts to the same server. We -used to use the same "vcnumber" for all connections which could cause -the server to treat subsequent connections, especially those that -are authenticated as guest, as reconnections, invalidating the earlier -user's smb session.  This fix allows cifs to mount multiple times to the -same server with different userids without risking invalidating earlier -established security contexts.  fsync now sends SMB Flush operation -to better ensure that we wait for server to write all of the data to -server disk (not just write it over the network).  Add new mount -parameter to allow user to disable sending the (slow) SMB flush on -fsync if desired (fsync still flushes all cached write data to the server). -Posix file open support added (turned off after one attempt if server -fails to support it properly, as with Samba server versions prior to 3.3.2) -Fix "redzone overwritten" bug in cifs_put_tcon (CIFSTcon may allocate too -little memory for the "nativeFileSystem" field returned by the server -during mount).  Endian convert inode numbers if necessary (makes it easier -to compare inode numbers on network files from big endian systems).  - -Version 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.  Fix problem in 2.6.28 resolving DFS paths to -Samba servers (worked to Windows).  Fix rmdir so that pending search -(readdir) requests do not get invalid results which include the now -removed directory.  Fix oops in cifs_dfs_ref.c when prefixpath is not reachable -when using DFS.  Add better file create support to servers which support -the CIFS POSIX protocol extensions (this adds support for new flags -on create, and improves semantics for write of locked ranges). - -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 - diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 0ed213970ce..603f18a65c1 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -3,15 +3,21 @@ config CIFS  	depends on INET  	select NLS  	select CRYPTO +	select CRYPTO_MD4  	select CRYPTO_MD5 +	select CRYPTO_HMAC  	select CRYPTO_ARC4 +	select CRYPTO_ECB +	select CRYPTO_DES +	select CRYPTO_SHA256 +	select CRYPTO_CMAC  	help  	  This is the client VFS module for the Common Internet File System  	  (CIFS) protocol which is the successor to the Server Message Block  	  (SMB) protocol, the native file sharing mechanism for most early  	  PC operating systems.  The CIFS protocol is fully supported by -	  file servers such as Windows 2000 (including Windows 2003, NT 4 -	  and Windows XP) as well by Samba (which provides excellent CIFS +	  file servers such as Windows 2000 (including Windows 2003, Windows 2008, +	  NT 4 and Windows XP) as well by Samba (which provides excellent CIFS  	  server support for Linux and many other operating systems). Limited  	  support for OS/2 and Windows ME and similar servers is provided as  	  well. @@ -110,9 +116,24 @@ config CIFS_POSIX  	  (such as Samba 3.10 and later) which can negotiate  	  CIFS POSIX ACL support.  If unsure, say N. +config CIFS_ACL +	  bool "Provide CIFS ACL support" +	  depends on CIFS_XATTR && KEYS +	  help +	    Allows fetching CIFS/NTFS ACL from the server.  The DACL blob +	    is handed over to the application/caller. + +config CIFS_DEBUG +	bool "Enable CIFS debugging routines" +	default y +	depends on CIFS +	help +	   Enabling this option adds helpful debugging messages to +	   the cifs code which increases the size of the cifs module. +	   If unsure, say Y.  config CIFS_DEBUG2  	bool "Enable additional CIFS debugging routines" -	depends on CIFS +	depends on CIFS_DEBUG  	help  	   Enabling this option adds a few more debugging routines  	   to the cifs code which slightly increases the size of @@ -134,25 +155,37 @@ config CIFS_DFS_UPCALL  	    IP addresses) which is needed for implicit mounts of DFS junction  	    points. If unsure, say N. +config CIFS_NFSD_EXPORT +	  bool "Allow nfsd to export CIFS file system" +	  depends on CIFS && BROKEN +	  help +	   Allows NFS server to export a CIFS mounted share (nfsd over cifs) + +config CIFS_SMB2 +	bool "SMB2 network file system support" +	depends on CIFS && INET +	select NLS +	select KEYS +	select FSCACHE +	select DNS_RESOLVER + +	help +	  This enables experimental support for the SMB2 (Server Message Block +	  version 2) protocol. The SMB2 protocol is the successor to the +	  popular CIFS and SMB network file sharing protocols. SMB2 is the +	  native file sharing mechanism for recent versions of Windows +	  operating systems (since Vista).  SMB2 enablement will eventually +	  allow users better performance, security and features, than would be +	  possible with cifs. Note that smb2 mount options also are simpler +	  (compared to cifs) due to protocol improvements. + +	  Unless you are a developer or tester, say N. +  config CIFS_FSCACHE -	  bool "Provide CIFS client caching support (EXPERIMENTAL)" -	  depends on EXPERIMENTAL +	  bool "Provide CIFS client caching support"  	  depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y  	  help  	    Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data  	    to be cached locally on disk through the general filesystem cache  	    manager. If unsure, say N. -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. - diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index adefa60a9bd..1964d212ab0 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -4,12 +4,17 @@  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 \ -	  readdir.o ioctl.o sess.o export.o cifsacl.o +	  link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \ +	  cifs_unicode.o nterr.o xattr.o cifsencrypt.o \ +	  readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o + +cifs-$(CONFIG_CIFS_ACL) += cifsacl.o  cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o  cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o  cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o + +cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o \ +			    smb2misc.o smb2pdu.o smb2inode.o smb2file.o diff --git a/fs/cifs/README b/fs/cifs/README deleted file mode 100644 index ee68d103654..00000000000 --- a/fs/cifs/README +++ /dev/null @@ -1,759 +0,0 @@ -The 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 <cifs_download_dir>/fs/cifs/* to <kernel_download_dir>/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 <mount point> <UNC name or cifs URL> -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=<username> -  pass=<password> -  domain=<domain name> -   -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 -  forceuid	Set the default uid for inodes to the uid -		passed in on mount. 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. 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.  (default) -  forcegid	(similar to above but for the groupid instead of uid) (default) -  noforceuid	Fill in file owner information (uid) by requesting it from -		the server if possible. With this option, the value given in -		the uid= option (on mount) will only be used if the server -		can not support returning uids on inodes. -  noforcegid	(similar to above but for the group owner, gid, instead of uid) -  uid		Set the default uid for inodes, and indicate to the -		cifs kernel driver which local user mounted. If the server -		supports the unix extensions the default uid is -		not used to fill in the owner fields of inodes (files) -		unless the "forceuid" parameter is specified. -  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. -  fsc		Enable local disk caching using FS-Cache (off by default). This -  		option could be useful to improve performance on a slow link, -		heavily loaded server and/or network where reading from the -		disk is faster than reading from the server (over the network). -		This could also impact scalability positively as the -		number of calls to the server are reduced. However, local -		caching is not suitable for all workloads for e.g. read-once -		type workloads. So, you need to consider carefully your -		workload/scenario before using this option. Currently, local -		disk caching is functional for CIFS files opened as read-only. -  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. -		This is now the default if server supports the  -		required network operation. -  noserverino   Client generates inode numbers (rather than using the actual one -		from the server). These inode numbers will vary after -		unmount or reboot which can confuse some applications, -		but not all server filesystems support unique inode -		numbers. -  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 mmapping 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. - nostrictsync   If this mount option is set, when an application does an -		fsync call then the cifs client does not send an SMB Flush -		to the server (to force the server to write all dirty data -		for this file immediately to disk), although cifs still sends -		all dirty (cached) file data to the server and waits for the -		server to respond to the write.  Since SMB Flush can be -		very slow, and some servers may be reliable enough (to risk -		delaying slightly flushing the data to disk on the server), -		turning on this option may be useful to improve performance for -		applications that fsync too much, at a small risk of server -		crash.  If this mount option is not set, by default cifs will -		send an SMB flush request (and wait for a response) on every -		fsync call. - 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). - mfsymlinks     Enable support for Minshall+French symlinks -		(see http://wiki.samba.org/index.php/UNIX_Extensions#Minshall.2BFrench_symlinks) -		This option is ignored when specified together with the -		'sfu' option. Minshall+French symlinks are used even if -		the server supports the CIFS Unix Extensions. - 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, features enabled 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).  When set to 2 enables -			an additional experimental feature, "raw ntlmssp" -			session establishment support (which allows -			specifying "sec=ntlmssp" on mount). The Linux cifs -			module will use ntlmv2 authentication encapsulated -			in "raw ntlmssp" (not using SPNEGO) when -			"sec=ntlmssp" is specified on mount. -			This support also requires building cifs with -			the CONFIG_CIFS_EXPERIMENTAL configuration flag. - -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. - -DFS support allows transparent redirection to shares in an MS-DFS name space. -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.  Samba, Windows servers and -many NAS appliances support DFS as a way of constructing a global name -space to ease network configuration and improve reliability. - -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 - - diff --git a/fs/cifs/TODO b/fs/cifs/TODO deleted file mode 100644 index 355abcdcda9..00000000000 --- a/fs/cifs/TODO +++ /dev/null @@ -1,129 +0,0 @@ -Version 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 point for 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. - diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index cfd1ce34e0b..a3b56544c21 100644 --- a/fs/cifs/asn1.c +++ b/fs/cifs/asn1.c @@ -506,11 +506,11 @@ decode_negTokenInit(unsigned char *security_blob, int length,  	/* GSSAPI header */  	if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { -		cFYI(1, "Error decoding negTokenInit header"); +		cifs_dbg(FYI, "Error decoding negTokenInit header\n");  		return 0;  	} else if ((cls != ASN1_APL) || (con != ASN1_CON)  		   || (tag != ASN1_EOC)) { -		cFYI(1, "cls = %d con = %d tag = %d", cls, con, tag); +		cifs_dbg(FYI, "cls = %d con = %d tag = %d\n", cls, con, tag);  		return 0;  	} @@ -531,52 +531,52 @@ decode_negTokenInit(unsigned char *security_blob, int length,  	/* SPNEGO OID not present or garbled -- bail out */  	if (!rc) { -		cFYI(1, "Error decoding negTokenInit header"); +		cifs_dbg(FYI, "Error decoding negTokenInit header\n");  		return 0;  	}  	/* SPNEGO */  	if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { -		cFYI(1, "Error decoding negTokenInit"); +		cifs_dbg(FYI, "Error decoding negTokenInit\n");  		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); +		cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", +			 cls, con, tag, end, *end);  		return 0;  	}  	/* negTokenInit */  	if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { -		cFYI(1, "Error decoding negTokenInit"); +		cifs_dbg(FYI, "Error decoding negTokenInit\n");  		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); +		cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", +			 cls, con, tag, end, *end);  		return 0;  	}  	/* sequence */  	if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { -		cFYI(1, "Error decoding 2nd part of negTokenInit"); +		cifs_dbg(FYI, "Error decoding 2nd part of negTokenInit\n");  		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); +		cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p (%d) exit 0\n", +			 cls, con, tag, end, *end);  		return 0;  	}  	/* sequence of */  	if (asn1_header_decode  	    (&ctx, &sequence_end, &cls, &con, &tag) == 0) { -		cFYI(1, "Error decoding 2nd part of negTokenInit"); +		cifs_dbg(FYI, "Error decoding 2nd part of negTokenInit\n");  		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); +		cifs_dbg(FYI, "cls = %d con = %d tag = %d end = %p (%d) exit 1\n", +			 cls, con, tag, end, *end);  		return 0;  	} @@ -584,15 +584,15 @@ decode_negTokenInit(unsigned char *security_blob, int length,  	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"); +			cifs_dbg(FYI, "Error decoding negTokenInit hdr exit2\n");  			return 0;  		}  		if ((tag == ASN1_OJI) && (con == ASN1_PRI)) {  			if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) { -				cFYI(1, "OID len = %d oid = 0x%lx 0x%lx " -					"0x%lx 0x%lx", oidlen, *oid, -					*(oid + 1), *(oid + 2), *(oid + 3)); +				cifs_dbg(FYI, "OID len = %d oid = 0x%lx 0x%lx 0x%lx 0x%lx\n", +					 oidlen, *oid, *(oid + 1), *(oid + 2), +					 *(oid + 3));  				if (compare_oid(oid, oidlen, MSKRB5_OID,  						MSKRB5_OID_LEN)) @@ -610,57 +610,14 @@ decode_negTokenInit(unsigned char *security_blob, int length,  				kfree(oid);  			}  		} else { -			cFYI(1, "Should be an oid what is going on?"); +			cifs_dbg(FYI, "Should be an oid what is going on?\n");  		}  	} -	/* mechlistMIC */ -	if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { -		/* Check if we have reached the end of the blob, but with -		   no mechListMic (e.g. NTLMSSP instead of KRB5) */ -		if (ctx.error == ASN1_ERR_DEC_EMPTY) -			goto decode_negtoken_exit; -		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; -	} - -	/* sequence */ -	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); -	} - -	/* sequence of */ -	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; -	} - -	/* general string */ -	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? */ -decode_negtoken_exit: +	/* +	 * We currently ignore anything at the end of the SPNEGO blob after +	 * the mechTypes have been parsed, since none of that info is +	 * used at the moment. +	 */  	return 1;  } diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index 224d7bbd1fc..6c665bf4a27 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -50,7 +50,7 @@ void cifs_fscache_unregister(void)   */  struct cifs_server_key {  	uint16_t	family;		/* address family */ -	uint16_t	port;		/* IP port */ +	__be16		port;		/* IP port */  	union {  		struct in_addr	ipv4_addr;  		struct in6_addr	ipv6_addr; @@ -64,7 +64,9 @@ static uint16_t cifs_server_get_key(const void *cookie_netfs_data,  				   void *buffer, uint16_t maxbuf)  {  	const struct TCP_Server_Info *server = cookie_netfs_data; -	const struct sockaddr *sa = (struct sockaddr *) &server->addr.sockAddr; +	const struct sockaddr *sa = (struct sockaddr *) &server->dstaddr; +	const struct sockaddr_in *addr = (struct sockaddr_in *) sa; +	const struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) sa;  	struct cifs_server_key *key = buffer;  	uint16_t key_len = sizeof(struct cifs_server_key); @@ -76,21 +78,21 @@ static uint16_t cifs_server_get_key(const void *cookie_netfs_data,  	 */  	switch (sa->sa_family) {  	case AF_INET: -		key->family = server->addr.sockAddr.sin_family; -		key->port = server->addr.sockAddr.sin_port; -		key->addr[0].ipv4_addr = server->addr.sockAddr.sin_addr; +		key->family = sa->sa_family; +		key->port = addr->sin_port; +		key->addr[0].ipv4_addr = addr->sin_addr;  		key_len += sizeof(key->addr[0].ipv4_addr);  		break;  	case AF_INET6: -		key->family = server->addr.sockAddr6.sin6_family; -		key->port = server->addr.sockAddr6.sin6_port; -		key->addr[0].ipv6_addr = server->addr.sockAddr6.sin6_addr; +		key->family = sa->sa_family; +		key->port = addr6->sin6_port; +		key->addr[0].ipv6_addr = addr6->sin6_addr;  		key_len += sizeof(key->addr[0].ipv6_addr);  		break;  	default: -		cERROR(1, "CIFS: Unknown network family '%d'", sa->sa_family); +		cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);  		key_len = 0;  		break;  	} @@ -144,13 +146,13 @@ static char *extract_sharename(const char *treename)  static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer,  				   uint16_t maxbuf)  { -	const struct cifsTconInfo *tcon = cookie_netfs_data; +	const struct cifs_tcon *tcon = cookie_netfs_data;  	char *sharename;  	uint16_t len;  	sharename = extract_sharename(tcon->treeName);  	if (IS_ERR(sharename)) { -		cFYI(1, "CIFS: couldn't extract sharename\n"); +		cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);  		sharename = NULL;  		return 0;  	} @@ -171,7 +173,7 @@ cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer,  			   uint16_t maxbuf)  {  	struct cifs_fscache_super_auxdata auxdata; -	const struct cifsTconInfo *tcon = cookie_netfs_data; +	const struct cifs_tcon *tcon = cookie_netfs_data;  	memset(&auxdata, 0, sizeof(auxdata));  	auxdata.resource_id = tcon->resource_id; @@ -190,7 +192,7 @@ fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,  					      uint16_t datalen)  {  	struct cifs_fscache_super_auxdata auxdata; -	const struct cifsTconInfo *tcon = cookie_netfs_data; +	const struct cifs_tcon *tcon = cookie_netfs_data;  	if (datalen != sizeof(auxdata))  		return FSCACHE_CHECKAUX_OBSOLETE; @@ -300,7 +302,7 @@ static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data)  	pagevec_init(&pvec, 0);  	first = 0; -	cFYI(1, "cifs inode 0x%p now uncached", cifsi); +	cifs_dbg(FYI, "%s: cifs inode 0x%p now uncached\n", __func__, cifsi);  	for (;;) {  		nr_pages = pagevec_lookup(&pvec, diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 103ab8b605b..f3ac4154cbb 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -57,43 +57,63 @@ cifs_dump_mem(char *label, void *data, int length)  	}  } -#ifdef CONFIG_CIFS_DEBUG2 -void cifs_dump_detail(struct smb_hdr *smb) +#ifdef CONFIG_CIFS_DEBUG +void cifs_vfs_err(const char *fmt, ...)  { -	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)); +	struct va_format vaf; +	va_list args; + +	va_start(args, fmt); + +	vaf.fmt = fmt; +	vaf.va = &args; + +	printk(KERN_ERR "CIFS VFS: %pV", &vaf); + +	va_end(args);  } +#endif + +void cifs_dump_detail(void *buf) +{ +#ifdef CONFIG_CIFS_DEBUG2 +	struct smb_hdr *smb = (struct smb_hdr *)buf; +	cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n", +		 smb->Command, smb->Status.CifsError, +		 smb->Flags, smb->Flags2, smb->Mid, smb->Pid); +	cifs_dbg(VFS, "smb buf %p len %u\n", smb, smbCalcSize(smb)); +#endif /* CONFIG_CIFS_DEBUG2 */ +}  void cifs_dump_mids(struct TCP_Server_Info *server)  { +#ifdef CONFIG_CIFS_DEBUG2  	struct list_head *tmp;  	struct mid_q_entry *mid_entry;  	if (server == NULL)  		return; -	cERROR(1, "Dump pending requests:"); +	cifs_dbg(VFS, "Dump pending requests:\n");  	spin_lock(&GlobalMid_Lock);  	list_for_each(tmp, &server->pending_mid_q) {  		mid_entry = list_entry(tmp, struct mid_q_entry, qhead); -		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); +		cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n", +			 mid_entry->mid_state, +			 le16_to_cpu(mid_entry->command), +			 mid_entry->pid, +			 mid_entry->callback_data, +			 mid_entry->mid);  #ifdef CONFIG_CIFS_STATS2 -		cERROR(1, "IsLarge: %d buf: %p time rcv: %ld now: %ld", -			mid_entry->largeBuf, -			mid_entry->resp_buf, -			mid_entry->when_received, -			jiffies); +		cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n", +			 mid_entry->large_buf, +			 mid_entry->resp_buf, +			 mid_entry->when_received, +			 jiffies);  #endif /* STATS2 */ -		cERROR(1, "IsMult: %d IsEnd: %d", mid_entry->multiRsp, -			  mid_entry->multiEnd); +		cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n", +			 mid_entry->multiRsp, mid_entry->multiEnd);  		if (mid_entry->resp_buf) {  			cifs_dump_detail(mid_entry->resp_buf);  			cifs_dump_mem("existing buf: ", @@ -101,8 +121,8 @@ void cifs_dump_mids(struct TCP_Server_Info *server)  		}  	}  	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) @@ -110,8 +130,8 @@ 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; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon;  	int i, j;  	__u32 dev_type; @@ -119,29 +139,27 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)  		    "Display Internal CIFS Data Structures for Debugging\n"  		    "---------------------------------------------------\n");  	seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); -	seq_printf(m, "Features: "); +	seq_printf(m, "Features:");  #ifdef CONFIG_CIFS_DFS_UPCALL -	seq_printf(m, "dfs"); -	seq_putc(m, ' '); +	seq_printf(m, " dfs");  #endif  #ifdef CONFIG_CIFS_FSCACHE -	seq_printf(m, "fscache"); -	seq_putc(m, ' '); +	seq_printf(m, " fscache");  #endif  #ifdef CONFIG_CIFS_WEAK_PW_HASH -	seq_printf(m, "lanman"); -	seq_putc(m, ' '); +	seq_printf(m, " lanman");  #endif  #ifdef CONFIG_CIFS_POSIX -	seq_printf(m, "posix"); -	seq_putc(m, ' '); +	seq_printf(m, " posix");  #endif  #ifdef CONFIG_CIFS_UPCALL -	seq_printf(m, "spnego"); -	seq_putc(m, ' '); +	seq_printf(m, " spnego");  #endif  #ifdef CONFIG_CIFS_XATTR -	seq_printf(m, "xattr"); +	seq_printf(m, " xattr"); +#endif +#ifdef CONFIG_CIFS_ACL +	seq_printf(m, " acl");  #endif  	seq_putc(m, '\n');  	seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); @@ -154,7 +172,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)  				    tcp_ses_list);  		i++;  		list_for_each(tmp2, &server->smb_ses_list) { -			ses = list_entry(tmp2, struct cifsSesInfo, +			ses = list_entry(tmp2, struct cifs_ses,  					 smb_ses_list);  			if ((ses->serverDomain == NULL) ||  				(ses->serverOS == NULL) || @@ -173,19 +191,18 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)  			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)); +				   server->sec_mode, in_flight(server));  #ifdef CONFIG_CIFS_STATS2  			seq_printf(m, " In Send: %d In MaxReq Wait: %d", -				atomic_read(&server->inSend), +				atomic_read(&server->in_send),  				atomic_read(&server->num_waiters));  #endif  			seq_puts(m, "\n\tShares:");  			j = 0;  			list_for_each(tmp3, &ses->tcon_list) { -				tcon = list_entry(tmp3, struct cifsTconInfo, +				tcon = list_entry(tmp3, struct cifs_tcon,  						  tcon_list);  				++j;  				dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); @@ -196,7 +213,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)  						   tcon->nativeFileSystem);  				}  				seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" -					"\nPathComponentMax: %d Status: 0x%d", +					"\n\tPathComponentMax: %d Status: 0x%d",  					le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),  					le32_to_cpu(tcon->fsAttrInfo.Attributes),  					le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), @@ -207,6 +224,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)  					seq_puts(m, " type: CDROM ");  				else  					seq_printf(m, " type: %d ", dev_type); +				if (server->ops->dump_share_caps) +					server->ops->dump_share_caps(m, tcon);  				if (tcon->need_reconnect)  					seq_puts(m, "\tDISCONNECTED "); @@ -220,12 +239,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)  				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); +					      " %d cbdata: %p mid %llu\n", +					      mid_entry->mid_state, +					      le16_to_cpu(mid_entry->command), +					      mid_entry->pid, +					      mid_entry->callback_data, +					      mid_entry->mid);  			}  			spin_unlock(&GlobalMid_Lock);  		} @@ -258,8 +277,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,  	int rc;  	struct list_head *tmp1, *tmp2, *tmp3;  	struct TCP_Server_Info *server; -	struct cifsSesInfo *ses; -	struct cifsTconInfo *tcon; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon;  	rc = get_user(c, buffer);  	if (rc) @@ -275,31 +294,15 @@ static ssize_t cifs_stats_proc_write(struct file *file,  			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, +				ses = list_entry(tmp2, struct cifs_ses,  						 smb_ses_list);  				list_for_each(tmp3, &ses->tcon_list) {  					tcon = list_entry(tmp3, -							  struct cifsTconInfo, +							  struct cifs_tcon,  							  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_posixopens, 0); -					atomic_set(&tcon->num_posixmkdirs, 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); +					if (server->ops->clear_stats) +						server->ops->clear_stats(tcon);  				}  			}  		} @@ -314,8 +317,8 @@ 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; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon;  	seq_printf(m,  			"Resources in use\nCIFS Session: %d\n", @@ -333,7 +336,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)  				atomic_read(&totSmBufAllocCount));  #endif /* CONFIG_CIFS_STATS2 */ -	seq_printf(m, "Operations (MIDs): %d\n", midCount.counter); +	seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&midCount));  	seq_printf(m,  		"\n%d session %d share reconnects\n",  		tcpSesReconnectCount.counter, tconInfoReconnectCount.counter); @@ -348,52 +351,20 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)  		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, +			ses = list_entry(tmp2, struct cifs_ses,  					 smb_ses_list);  			list_for_each(tmp3, &ses->tcon_list) {  				tcon = list_entry(tmp3, -						  struct cifsTconInfo, +						  struct cifs_tcon,  						  tcon_list);  				i++;  				seq_printf(m, "\n%d) %s", i, tcon->treeName);  				if (tcon->need_reconnect)  					seq_puts(m, "\tDISCONNECTED "); -				seq_printf(m, "\nSMBs: %d 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, "\nFlushes: %d", -					atomic_read(&tcon->num_flushes)); -				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, "\nPosix Opens: %d " -					      "Posix Mkdirs: %d", -					atomic_read(&tcon->num_posixopens), -					atomic_read(&tcon->num_posixmkdirs)); -				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)); +				seq_printf(m, "\nSMBs: %d", +					   atomic_read(&tcon->num_smbs_sent)); +				if (server->ops->print_stats) +					server->ops->print_stats(m, tcon);  			}  		}  	} @@ -420,12 +391,9 @@ static const struct file_operations cifs_stats_proc_fops = {  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 @@ -442,13 +410,8 @@ cifs_proc_init(void)  #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, @@ -467,11 +430,8 @@ cifs_proc_clean(void)  #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);  } @@ -515,82 +475,6 @@ static const struct file_operations cifsFYI_proc_fops = {  	.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); @@ -702,57 +586,47 @@ static const struct file_operations traceSMB_proc_fops = {  	.write		= traceSMB_proc_write,  }; -static int cifs_multiuser_mount_proc_show(struct seq_file *m, void *v) +static int cifs_security_flags_proc_show(struct seq_file *m, void *v)  { -	seq_printf(m, "%d\n", multiuser_mount); +	seq_printf(m, "0x%x\n", global_secflags);  	return 0;  } -static int cifs_multiuser_mount_proc_open(struct inode *inode, struct file *fh) +static int cifs_security_flags_proc_open(struct inode *inode, struct file *file)  { -	return single_open(fh, cifs_multiuser_mount_proc_show, NULL); +	return single_open(file, cifs_security_flags_proc_show, NULL);  } -static ssize_t cifs_multiuser_mount_proc_write(struct file *file, -		const char __user *buffer, size_t count, loff_t *ppos) +/* + * Ensure that if someone sets a MUST flag, that we disable all other MAY + * flags except for the ones corresponding to the given MUST flag. If there are + * multiple MUST flags, then try to prefer more secure ones. + */ +static void +cifs_security_flags_handle_must_flags(unsigned int *flags)  { -	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; +	unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; -	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, -}; +	if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) +		*flags = CIFSSEC_MUST_KRB5; +	else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) +		*flags = CIFSSEC_MUST_NTLMSSP; +	else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) +		*flags = CIFSSEC_MUST_NTLMV2; +	else if ((*flags & CIFSSEC_MUST_NTLM) == CIFSSEC_MUST_NTLM) +		*flags = CIFSSEC_MUST_NTLM; +	else if ((*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN) +		*flags = CIFSSEC_MUST_LANMAN; +	else if ((*flags & CIFSSEC_MUST_PLNTXT) == CIFSSEC_MUST_PLNTXT) +		*flags = CIFSSEC_MUST_PLNTXT; -static int cifs_security_flags_proc_show(struct seq_file *m, void *v) -{ -	seq_printf(m, "0x%x\n", global_secflags); -	return 0; -} - -static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) -{ -	return single_open(file, cifs_security_flags_proc_show, NULL); +	*flags |= signflags;  }  static ssize_t cifs_security_flags_proc_write(struct file *file,  		const char __user *buffer, size_t count, loff_t *ppos)  { +	int rc;  	unsigned int flags;  	char flags_string[12];  	char c; @@ -775,34 +649,43 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,  			global_secflags = CIFSSEC_MAX;  			return count;  		} else if (!isdigit(c)) { -			cERROR(1, "invalid flag %c", c); +			cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", +					flags_string);  			return -EINVAL;  		}  	} -	/* else we have a number */ -	flags = simple_strtoul(flags_string, NULL, 0); +	/* else we have a number */ +	rc = kstrtouint(flags_string, 0, &flags); +	if (rc) { +		cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", +				flags_string); +		return rc; +	} -	cFYI(1, "sec flags 0x%x", flags); +	cifs_dbg(FYI, "sec flags 0x%x\n", flags); -	if (flags <= 0)  { -		cERROR(1, "invalid security flags %s", flags_string); +	if (flags == 0)  { +		cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string);  		return -EINVAL;  	}  	if (flags & ~CIFSSEC_MASK) { -		cERROR(1, "attempt to set unsupported security flags 0x%x", -			flags & ~CIFSSEC_MASK); +		cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", +			 flags & ~CIFSSEC_MASK);  		return -EINVAL;  	} + +	cifs_security_flags_handle_must_flags(&flags); +  	/* flags look ok - update the global security flags for cifs module */  	global_secflags = flags;  	if (global_secflags & CIFSSEC_MUST_SIGN) {  		/* requiring signing implies signing is allowed */  		global_secflags |= CIFSSEC_MAY_SIGN; -		cFYI(1, "packet signing now required"); +		cifs_dbg(FYI, "packet signing now required\n");  	} else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) { -		cFYI(1, "packet signing disabled"); +		cifs_dbg(FYI, "packet signing disabled\n");  	}  	/* BB should we turn on MAY flags for other MUST options? */  	return count; diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 8942b28cf80..c99b40fb609 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -18,68 +18,49 @@   *   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_detail(void *);  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); +void dump_smb(void *, int);  #define CIFS_INFO	0x01  #define CIFS_RC		0x02  #define CIFS_TIMER	0x04 +#define VFS 1 +#define FYI 2 +extern int cifsFYI; +#ifdef CONFIG_CIFS_DEBUG2 +#define NOISY 4 +#else +#define NOISY 0 +#endif +  /*   *	debug ON   *	--------   */ -#ifdef CIFS_DEBUG - -/* information message: e.g., configuration, major event */ -extern int cifsFYI; -#define cifsfyi(fmt, arg...)						\ -do {									\ -	if (cifsFYI & CIFS_INFO)					\ -		printk(KERN_DEBUG "%s: " fmt "\n", __FILE__, ##arg);	\ -} while (0) - -#define cFYI(set, fmt, arg...)			\ -do {						\ -	if (set)				\ -		cifsfyi(fmt, ##arg);		\ -} while (0) +#ifdef CONFIG_CIFS_DEBUG -#define cifswarn(fmt, arg...)			\ -	printk(KERN_WARNING fmt "\n", ##arg) +__printf(1, 2) void cifs_vfs_err(const char *fmt, ...); -/* debug event message: */ -extern int cifsERROR; - -#define cEVENT(fmt, arg...)						\ +/* information message: e.g., configuration, major event */ +#define cifs_dbg(type, fmt, ...)					\  do {									\ -	if (cifsERROR)							\ -		printk(KERN_EVENT "%s: " fmt "\n", __FILE__, ##arg);	\ -} while (0) - -/* error event message: e.g., i/o error */ -#define cifserror(fmt, arg...)					\ -do {								\ -	if (cifsERROR)						\ -		printk(KERN_ERR "CIFS VFS: " fmt "\n", ##arg);	\ -} while (0) - -#define cERROR(set, fmt, arg...)		\ -do {						\ -	if (set)				\ -		cifserror(fmt, ##arg);		\ +	if (type == FYI) {						\ +		if (cifsFYI & CIFS_INFO) {				\ +			printk(KERN_DEBUG "%s: " fmt,			\ +			       __FILE__, ##__VA_ARGS__);		\ +		}							\ +	} else if (type == VFS) {					\ +		cifs_vfs_err(fmt, ##__VA_ARGS__);			\ +	} else if (type == NOISY && type != 0) {			\ +		printk(KERN_DEBUG fmt, ##__VA_ARGS__);			\ +	}								\  } while (0)  /* @@ -87,10 +68,11 @@ do {						\   *	---------   */  #else		/* _CIFS_DEBUG */ -#define cERROR(set, fmt, arg...) -#define cEVENT(fmt, arg...) -#define cFYI(set, fmt, arg...) -#define cifserror(fmt, arg...) -#endif		/* _CIFS_DEBUG */ +#define cifs_dbg(type, fmt, ...)					\ +do {									\ +	if (0)								\ +		printk(KERN_DEBUG fmt, ##__VA_ARGS__);			\ +} while (0) +#endif  #endif				/* _H_CIFS_DEBUG */ diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index c68a056f27f..58df174deb1 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -18,6 +18,7 @@  #include <linux/slab.h>  #include <linux/vfs.h>  #include <linux/fs.h> +#include <linux/inet.h>  #include "cifsglob.h"  #include "cifsproto.h"  #include "cifsfs.h" @@ -48,58 +49,74 @@ void cifs_dfs_release_automount_timer(void)  }  /** - * cifs_get_share_name	-	extracts share name from UNC - * @node_name:	pointer to UNC string + * cifs_build_devname - build a devicename from a UNC and optional prepath + * @nodename:	pointer to UNC string + * @prepath:	pointer to prefixpath (or NULL if there isn't one)   * - * 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 necessary. - * Returns pointer to share name on success or ERR_PTR on error. - * Caller is responsible for freeing returned string. + * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer + * big enough to hold the final thing. Copy the UNC from the nodename, and + * concatenate the prepath onto the end of it if there is one. + * + * Returns pointer to the built string, or a ERR_PTR. Caller is responsible + * for freeing the returned string.   */ -static char *cifs_get_share_name(const char *node_name) +static char * +cifs_build_devname(char *nodename, const char *prepath)  { -	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 ERR_PTR(-ENOMEM); +	size_t pplen; +	size_t unclen; +	char *dev; +	char *pos; + +	/* skip over any preceding delimiters */ +	nodename += strspn(nodename, "\\"); +	if (!*nodename) +		return ERR_PTR(-EINVAL); -	/* get 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; -	} +	/* get length of UNC and set pos to last char */ +	unclen = strlen(nodename); +	pos = nodename + unclen - 1; -	/* 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 ERR_PTR(-EINVAL); +	/* trim off any trailing delimiters */ +	while (*pos == '\\') { +		--pos; +		--unclen;  	} -	/* 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; +	/* allocate a buffer: +	 * +2 for preceding "//" +	 * +1 for delimiter between UNC and prepath +	 * +1 for trailing NULL +	 */ +	pplen = prepath ? strlen(prepath) : 0; +	dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); +	if (!dev) +		return ERR_PTR(-ENOMEM); + +	pos = dev; +	/* add the initial "//" */ +	*pos = '/'; +	++pos; +	*pos = '/'; +	++pos; + +	/* copy in the UNC portion from referral */ +	memcpy(pos, nodename, unclen); +	pos += unclen; + +	/* copy the prefixpath remainder (if there is one) */ +	if (pplen) { +		*pos = '/'; +		++pos; +		memcpy(pos, prepath, pplen); +		pos += pplen;  	} -	return UNC; +	/* NULL terminator */ +	*pos = '\0'; + +	convert_delimiter(dev, '/'); +	return dev;  } @@ -123,6 +140,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,  {  	int rc;  	char *mountdata = NULL; +	const char *prepath = NULL;  	int md_len;  	char *tkn_e;  	char *srvIP = NULL; @@ -132,7 +150,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,  	if (sb_mountdata == NULL)  		return ERR_PTR(-EINVAL); -	*devname = cifs_get_share_name(ref->node_name); +	if (strlen(fullpath) - ref->path_consumed) +		prepath = fullpath + ref->path_consumed; + +	*devname = cifs_build_devname(ref->node_name, prepath);  	if (IS_ERR(*devname)) {  		rc = PTR_ERR(*devname);  		*devname = NULL; @@ -141,16 +162,19 @@ char *cifs_compose_mount_options(const char *sb_mountdata,  	rc = dns_resolve_server_name_to_ip(*devname, &srvIP);  	if (rc < 0) { -		cERROR(1, "%s: Failed to resolve server part of %s to IP: %d", -			  __func__, *devname, rc); +		cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", +			 __func__, *devname, rc);  		goto compose_mount_options_err;  	} -	/* md_len = strlen(...) + 12 for 'sep+prefixpath=' -	 * assuming that we have 'unc=' and 'ip=' in -	 * the original sb_mountdata + +	/* +	 * In most cases, we'll be building a shorter string than the original, +	 * but we do have to assume that the address in the ip= option may be +	 * much longer than the original. Add the max length of an address +	 * string to the length of the original string to allow for worst case.  	 */ -	md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12; -	mountdata = kzalloc(md_len+1, GFP_KERNEL); +	md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; +	mountdata = kzalloc(md_len + 1, GFP_KERNEL);  	if (mountdata == NULL) {  		rc = -ENOMEM;  		goto compose_mount_options_err; @@ -194,29 +218,9 @@ char *cifs_compose_mount_options(const char *sb_mountdata,  		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*/ -		rc = -EINVAL; -		goto compose_mount_options_err; -	} - -	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); -	} -	/*cFYI(1, "%s: parent mountdata: %s", __func__,sb_mountdata);*/ -	/*cFYI(1, "%s: submount mountdata: %s", __func__, mountdata );*/ +	/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ +	/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/  compose_mount_options_out:  	kfree(srvIP); @@ -225,6 +229,8 @@ compose_mount_options_out:  compose_mount_options_err:  	kfree(mountdata);  	mountdata = ERR_PTR(rc); +	kfree(*devname); +	*devname = NULL;  	goto compose_mount_options_out;  } @@ -255,65 +261,34 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb,  } -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 | MNT_SHRINKABLE, 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)) -			; -		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); +	cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name); +	cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name); +	cifs_dbg(FYI, "DFS: fl: %hd, srv_type: %hd\n", +		 ref->flags, ref->server_type); +	cifs_dbg(FYI, "DFS: ref_flags: %hd, path_consumed: %hd\n", +		 ref->ref_flag, ref->path_consumed);  } - -static void* -cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +/* + * Create a vfsmount that we can automount + */ +static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)  {  	struct dfs_info3_param *referrals = NULL;  	unsigned int num_referrals = 0;  	struct cifs_sb_info *cifs_sb; -	struct cifsSesInfo *ses; -	char *full_path = NULL; -	int xid, i; -	int rc = 0; -	struct vfsmount *mnt = ERR_PTR(-ENOENT); +	struct cifs_ses *ses; +	char *full_path; +	unsigned int xid; +	int i; +	int rc; +	struct vfsmount *mnt;  	struct tcon_link *tlink; -	cFYI(1, "in %s", __func__); -	BUG_ON(IS_ROOT(dentry)); - -	xid = GetXid(); - -	dput(nd->path.dentry); -	nd->path.dentry = dget(dentry); +	cifs_dbg(FYI, "in %s\n", __func__); +	BUG_ON(IS_ROOT(mntpt));  	/*  	 * The MSDFS spec states that paths in DFS referral requests and @@ -321,66 +296,83 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)  	 * the double backslashes usually used in the UNC. This function  	 * gives us the latter, so we must adjust the result.  	 */ -	full_path = build_path_from_dentry(dentry); -	if (full_path == NULL) { -		rc = -ENOMEM; -		goto out_err; -	} +	mnt = ERR_PTR(-ENOMEM); +	full_path = build_path_from_dentry(mntpt); +	if (full_path == NULL) +		goto cdda_exit; -	cifs_sb = CIFS_SB(dentry->d_inode->i_sb); +	cifs_sb = CIFS_SB(mntpt->d_inode->i_sb);  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) { -		rc = PTR_ERR(tlink); -		goto out_err; +		mnt = ERR_CAST(tlink); +		goto free_full_path;  	}  	ses = tlink_tcon(tlink)->ses; +	xid = get_xid();  	rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,  		&num_referrals, &referrals,  		cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	free_xid(xid);  	cifs_put_tlink(tlink); +	mnt = ERR_PTR(-ENOENT);  	for (i = 0; i < num_referrals; i++) {  		int len; -		dump_referral(referrals+i); +		dump_referral(referrals + i);  		/* connect to a node */  		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; +			cifs_dbg(VFS, "%s: Net Address path too short: %s\n", +				 __func__, referrals[i].node_name); +			mnt = ERR_PTR(-EINVAL); +			break;  		}  		mnt = cifs_dfs_do_refmount(cifs_sb,  				full_path, referrals + i); -		cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, -					referrals[i].node_name, mnt); - -		/* complete mount procedure if we accured submount */ +		cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", +			 __func__, referrals[i].node_name, mnt);  		if (!IS_ERR(mnt)) -			break; +			goto success;  	} -	/* we need it cause for() above could exit without valid submount */ -	rc = PTR_ERR(mnt); -	if (IS_ERR(mnt)) -		goto out_err; - -	rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list); +	/* no valid submounts were found; return error from get_dfs_path() by +	 * preference */ +	if (rc != 0) +		mnt = ERR_PTR(rc); -out: -	FreeXid(xid); +success:  	free_dfs_info_array(referrals, num_referrals); +free_full_path:  	kfree(full_path); -	cFYI(1, "leaving %s" , __func__); -	return ERR_PTR(rc); -out_err: -	path_put(&nd->path); -	goto out; +cdda_exit: +	cifs_dbg(FYI, "leaving %s\n" , __func__); +	return mnt; +} + +/* + * Attempt to automount the referral + */ +struct vfsmount *cifs_dfs_d_automount(struct path *path) +{ +	struct vfsmount *newmnt; + +	cifs_dbg(FYI, "in %s\n", __func__); + +	newmnt = cifs_dfs_do_automount(path->dentry); +	if (IS_ERR(newmnt)) { +		cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); +		return newmnt; +	} + +	mntget(newmnt); /* prevent immediate expiration */ +	mnt_set_expiry(newmnt, &cifs_dfs_automount_list); +	schedule_delayed_work(&cifs_dfs_automount_task, +			      cifs_dfs_mountpoint_expiry_timeout); +	cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); +	return newmnt;  }  const struct inode_operations cifs_dfs_referral_inode_operations = { -	.follow_link = cifs_dfs_follow_mountpoint,  }; - diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index e9a393c9c2c..9409fa10bd5 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -40,6 +40,11 @@  #define CIFS_MOUNT_FSCACHE	0x8000 /* local caching enabled */  #define CIFS_MOUNT_MF_SYMLINKS	0x10000 /* Minshall+French Symlinks enabled */  #define CIFS_MOUNT_MULTIUSER	0x20000 /* multiuser mount */ +#define CIFS_MOUNT_STRICT_IO	0x40000 /* strict cache mode */ +#define CIFS_MOUNT_RWPIDFORWARD	0x80000 /* use pid forwarding for rw */ +#define CIFS_MOUNT_POSIXACL	0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */ +#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ +#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */  struct cifs_sb_info {  	struct rb_root tlink_tree; @@ -48,18 +53,18 @@ struct cifs_sb_info {  	struct nls_table *local_nls;  	unsigned int rsize;  	unsigned int wsize; +	unsigned long actimeo; /* attribute cache timeout (jiffies) */  	atomic_t active; -	uid_t	mnt_uid; -	gid_t	mnt_gid; -	mode_t	mnt_file_mode; -	mode_t	mnt_dir_mode; +	kuid_t	mnt_uid; +	kgid_t	mnt_gid; +	kuid_t	mnt_backupuid; +	kgid_t	mnt_backupgid; +	umode_t	mnt_file_mode; +	umode_t	mnt_dir_mode;  	unsigned int mnt_cifs_flags; -	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 +	char   *mountdata; /* options received at mount time or via DFS refs */  	struct backing_dev_info bdi;  	struct delayed_work prune_tlinks; +	struct rcu_head rcu;  };  #endif				/* _CIFS_FS_SB_H */ diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 87044906cd1..a3e93254761 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -31,18 +31,17 @@  /* create a new cifs key */  static int -cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen) +cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)  {  	char *payload;  	int ret;  	ret = -ENOMEM; -	payload = kmalloc(datalen, GFP_KERNEL); +	payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);  	if (!payload)  		goto error;  	/* attach the data */ -	memcpy(payload, data, datalen);  	key->payload.data = payload;  	ret = 0; @@ -95,9 +94,11 @@ struct key_type cifs_spnego_key_type = {  /* get a key struct with a SPNEGO security blob, suitable for session setup */  struct key * -cifs_get_spnego_key(struct cifsSesInfo *sesInfo) +cifs_get_spnego_key(struct cifs_ses *sesInfo)  {  	struct TCP_Server_Info *server = sesInfo->server; +	struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; +	struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;  	char *description, *dp;  	size_t desc_len;  	struct key *spnego_key; @@ -111,9 +112,11 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)  		   MAX_MECH_STR_LEN +  		   UID_KEY_LEN + (sizeof(uid_t) * 2) +  		   CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + -		   USER_KEY_LEN + strlen(sesInfo->userName) +  		   PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; +	if (sesInfo->user_name) +		desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); +  	spnego_key = ERR_PTR(-ENOMEM);  	description = kzalloc(desc_len, GFP_KERNEL);  	if (description == NULL) @@ -127,10 +130,10 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)  	dp = description + strlen(description);  	/* add the server address */ -	if (server->addr.sockAddr.sin_family == AF_INET) -		sprintf(dp, "ip4=%pI4", &server->addr.sockAddr.sin_addr); -	else if (server->addr.sockAddr.sin_family == AF_INET6) -		sprintf(dp, "ip6=%pI6", &server->addr.sockAddr6.sin6_addr); +	if (server->dstaddr.ss_family == AF_INET) +		sprintf(dp, "ip4=%pI4", &sa->sin_addr); +	else if (server->dstaddr.ss_family == AF_INET6) +		sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);  	else  		goto out; @@ -145,18 +148,22 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)  		goto out;  	dp = description + strlen(description); -	sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); +	sprintf(dp, ";uid=0x%x", +		from_kuid_munged(&init_user_ns, sesInfo->linux_uid));  	dp = description + strlen(description); -	sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); +	sprintf(dp, ";creduid=0x%x", +		from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); -	dp = description + strlen(description); -	sprintf(dp, ";user=%s", sesInfo->userName); +	if (sesInfo->user_name) { +		dp = description + strlen(description); +		sprintf(dp, ";user=%s", sesInfo->user_name); +	}  	dp = description + strlen(description);  	sprintf(dp, ";pid=0x%x", current->pid); -	cFYI(1, "key description = %s", description); +	cifs_dbg(FYI, "key description = %s\n", description);  	spnego_key = request_key(&cifs_spnego_key_type, description, "");  #ifdef CONFIG_CIFS_DEBUG2 diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h index e4041ec4d71..31bef9ee078 100644 --- a/fs/cifs/cifs_spnego.h +++ b/fs/cifs/cifs_spnego.h @@ -41,7 +41,7 @@ struct cifs_spnego_msg {  #ifdef __KERNEL__  extern struct key_type cifs_spnego_key_type; -extern struct key *cifs_get_spnego_key(struct cifsSesInfo *sesInfo); +extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo);  #endif /* KERNEL */  #endif /* _CIFS_SPNEGO_H */ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 430f510a172..15e9505aa35 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -27,27 +27,31 @@  #include "cifs_debug.h"  /* - * cifs_ucs2_bytes - how long will a string be after conversion? - * @ucs - pointer to input string + * cifs_utf16_bytes - how long will a string be after conversion? + * @utf16 - pointer to input string   * @maxbytes - don't go past this many bytes of input string   * @codepage - destination codepage   * - * Walk a ucs2le string and return the number of bytes that the string will + * Walk a utf16le string and return the number of bytes that the string will   * be after being converted to the given charset, not including any null   * termination required. Don't walk past maxbytes in the source buffer.   */  int -cifs_ucs2_bytes(const __le16 *from, int maxbytes, +cifs_utf16_bytes(const __le16 *from, int maxbytes,  		const struct nls_table *codepage)  {  	int i;  	int charlen, outlen = 0;  	int maxwords = maxbytes / 2;  	char tmp[NLS_MAX_CHARSET_SIZE]; +	__u16 ftmp; -	for (i = 0; i < maxwords && from[i]; i++) { -		charlen = codepage->uni2char(le16_to_cpu(from[i]), tmp, -					     NLS_MAX_CHARSET_SIZE); +	for (i = 0; i < maxwords; i++) { +		ftmp = get_unaligned_le16(&from[i]); +		if (ftmp == 0) +			break; + +		charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);  		if (charlen > 0)  			outlen += charlen;  		else @@ -58,9 +62,9 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes,  }  /* - * cifs_mapchar - convert a little-endian char to proper char in codepage + * cifs_mapchar - convert a host-endian char to proper char in codepage   * @target - where converted character should be copied - * @src_char - 2 byte little-endian source character + * @src_char - 2 byte host-endian source character   * @cp - codepage to which character should be converted   * @mapchar - should character be mapped according to mapchars mount option?   * @@ -69,7 +73,7 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes,   * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).   */  static int -cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp, +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,  	     bool mapchar)  {  	int len = 1; @@ -82,11 +86,11 @@ cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp,  	 *     build_path_from_dentry are modified, as they use slash as  	 *     separator.  	 */ -	switch (le16_to_cpu(src_char)) { +	switch (src_char) {  	case UNI_COLON:  		*target = ':';  		break; -	case UNI_ASTERIK: +	case UNI_ASTERISK:  		*target = '*';  		break;  	case UNI_QUESTION: @@ -109,8 +113,7 @@ out:  	return len;  cp_convert: -	len = cp->uni2char(le16_to_cpu(src_char), target, -			   NLS_MAX_CHARSET_SIZE); +	len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);  	if (len <= 0) {  		*target = '?';  		len = 1; @@ -119,7 +122,7 @@ cp_convert:  }  /* - * cifs_from_ucs2 - convert utf16le string to local charset + * cifs_from_utf16 - convert utf16le string to local charset   * @to - destination buffer   * @from - source buffer   * @tolen - destination buffer size (in bytes) @@ -127,7 +130,7 @@ cp_convert:   * @codepage - codepage to which characters should be converted   * @mapchar - should characters be remapped according to the mapchars option?   * - * Convert a little-endian ucs2le string (as sent by the server) to a string + * Convert a little-endian utf16le string (as sent by the server) to a string   * in the provided codepage. The tolen and fromlen parameters are to ensure   * that the code doesn't walk off of the end of the buffer (which is always   * a danger if the alignment of the source buffer is off). The destination @@ -136,12 +139,12 @@ cp_convert:   * null terminator).   *   * Note that some windows versions actually send multiword UTF-16 characters - * instead of straight UCS-2. The linux nls routines however aren't able to + * instead of straight UTF16-2. The linux nls routines however aren't able to   * deal with those characters properly. In the event that we get some of   * those characters, they won't be translated properly.   */  int -cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, +cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,  		 const struct nls_table *codepage, bool mapchar)  {  	int i, charlen, safelen; @@ -149,6 +152,7 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen,  	int nullsize = nls_nullsize(codepage);  	int fromwords = fromlen / 2;  	char tmp[NLS_MAX_CHARSET_SIZE]; +	__u16 ftmp;  	/*  	 * because the chars can be of varying widths, we need to take care @@ -158,19 +162,23 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen,  	 */  	safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); -	for (i = 0; i < fromwords && from[i]; i++) { +	for (i = 0; i < fromwords; i++) { +		ftmp = get_unaligned_le16(&from[i]); +		if (ftmp == 0) +			break; +  		/*  		 * check to see if converting this character might make the  		 * conversion bleed into the null terminator  		 */  		if (outlen >= safelen) { -			charlen = cifs_mapchar(tmp, from[i], codepage, mapchar); +			charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar);  			if ((outlen + charlen) > (tolen - nullsize))  				break;  		}  		/* put converted char into 'to' buffer */ -		charlen = cifs_mapchar(&to[outlen], from[i], codepage, mapchar); +		charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);  		outlen += charlen;  	} @@ -182,40 +190,60 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen,  }  /* - * NAME:	cifs_strtoUCS() + * NAME:	cifs_strtoUTF16()   *   * FUNCTION:	Convert character string to unicode string   *   */  int -cifs_strtoUCS(__le16 *to, const char *from, int len, +cifs_strtoUTF16(__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 */ +	wchar_t wchar_to; /* needed to quiet sparse */ -	for (i = 0; len && *from; i++, from += charlen, len -= charlen) { +	/* special case for utf8 to handle no plane0 chars */ +	if (!strcmp(codepage->charset, "utf8")) { +		/* +		 * convert utf8 -> utf16, we assume we have enough space +		 * as caller should have assumed conversion does not overflow +		 * in destination len is length in wchar_t units (16bits) +		 */ +		i  = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, +				       (wchar_t *) to, len); -		/* works for 2.4.0 kernel or later */ -		charlen = codepage->char2uni(from, len, &wchar_to[i]); +		/* if success terminate and exit */ +		if (i >= 0) +			goto success; +		/* +		 * if fails fall back to UCS encoding as this +		 * function should not return negative values +		 * currently can fail only if source contains +		 * invalid encoded characters +		 */ +	} + +	for (i = 0; len && *from; i++, from += charlen, len -= charlen) { +		charlen = codepage->char2uni(from, len, &wchar_to);  		if (charlen < 1) { -			cERROR(1, "strtoUCS: char2uni of %d returned %d", -				(int)*from, charlen); +			cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n", +				 *from, charlen);  			/* A question mark */ -			to[i] = cpu_to_le16(0x003f); +			wchar_to = 0x003f;  			charlen = 1; -		} else -			to[i] = cpu_to_le16(wchar_to[i]); - +		} +		put_unaligned_le16(wchar_to, &to[i]);  	} -	to[i] = 0; +success: +	put_unaligned_le16(0, &to[i]);  	return i;  }  /* - * cifs_strndup_from_ucs - copy a string from wire format to the local codepage + * cifs_strndup_from_utf16 - copy a string from wire format to the local + * codepage   * @src - source string   * @maxlen - don't walk past this many bytes in the source string   * @is_unicode - is this a unicode string? @@ -226,19 +254,19 @@ cifs_strtoUCS(__le16 *to, const char *from, int len,   * error.   */  char * -cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, -	     const struct nls_table *codepage) +cifs_strndup_from_utf16(const char *src, const int maxlen, +			const bool is_unicode, const struct nls_table *codepage)  {  	int len;  	char *dst;  	if (is_unicode) { -		len = cifs_ucs2_bytes((__le16 *) src, maxlen, codepage); +		len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage);  		len += nls_nullsize(codepage);  		dst = kmalloc(len, GFP_KERNEL);  		if (!dst)  			return NULL; -		cifs_from_ucs2(dst, (__le16 *) src, len, maxlen, codepage, +		cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage,  			       false);  	} else {  		len = strnlen(src, maxlen); @@ -252,3 +280,137 @@ cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode,  	return dst;  } +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +int +cifsConvertToUTF16(__le16 *target, const char *source, int srclen, +		 const struct nls_table *cp, int mapChars) +{ +	int i, charlen; +	int j = 0; +	char src_char; +	__le16 dst_char; +	wchar_t tmp; + +	if (!mapChars) +		return cifs_strtoUTF16(target, source, PATH_MAX, cp); + +	for (i = 0; i < srclen; j++) { +		src_char = source[i]; +		charlen = 1; +		switch (src_char) { +		case 0: +			goto ctoUTF16_out; +		case ':': +			dst_char = cpu_to_le16(UNI_COLON); +			break; +		case '*': +			dst_char = cpu_to_le16(UNI_ASTERISK); +			break; +		case '?': +			dst_char = cpu_to_le16(UNI_QUESTION); +			break; +		case '<': +			dst_char = cpu_to_le16(UNI_LESSTHAN); +			break; +		case '>': +			dst_char = cpu_to_le16(UNI_GRTRTHAN); +			break; +		case '|': +			dst_char = cpu_to_le16(UNI_PIPE); +			break; +		/* +		 * FIXME: We can not handle remapping backslash (UNI_SLASH) +		 * until all the calls to build_path_from_dentry are modified, +		 * as they use backslash as separator. +		 */ +		default: +			charlen = cp->char2uni(source + i, srclen - i, &tmp); +			dst_char = cpu_to_le16(tmp); + +			/* +			 * if no match, use question mark, which at least in +			 * some cases serves as wild card +			 */ +			if (charlen < 1) { +				dst_char = cpu_to_le16(0x003f); +				charlen = 1; +			} +		} +		/* +		 * character may take more than one byte in the source string, +		 * but will take exactly two bytes in the target string +		 */ +		i += charlen; +		put_unaligned(dst_char, &target[j]); +	} + +ctoUTF16_out: +	put_unaligned(0, &target[j]); /* Null terminate target unicode string */ +	return j; +} + +#ifdef CONFIG_CIFS_SMB2 +/* + * cifs_local_to_utf16_bytes - how long will a string be after conversion? + * @from - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - source codepage + * + * Walk a string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ + +static int +cifs_local_to_utf16_bytes(const char *from, int len, +			  const struct nls_table *codepage) +{ +	int charlen; +	int i; +	wchar_t wchar_to; + +	for (i = 0; len && *from; i++, from += charlen, len -= charlen) { +		charlen = codepage->char2uni(from, len, &wchar_to); +		/* Failed conversion defaults to a question mark */ +		if (charlen < 1) +			charlen = 1; +	} +	return 2 * i; /* UTF16 characters are two bytes */ +} + +/* + * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @utf16_len - the length of the allocated string in bytes (including null) + * @cp - source codepage + * @remap - map special chars + * + * Take a string convert it from the local codepage to UTF16 and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +__le16 * +cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, +		      const struct nls_table *cp, int remap) +{ +	int len; +	__le16 *dst; + +	len = cifs_local_to_utf16_bytes(src, maxlen, cp); +	len += 2; /* NULL */ +	dst = kmalloc(len, GFP_KERNEL); +	if (!dst) { +		*utf16_len = 0; +		return NULL; +	} +	cifsConvertToUTF16(dst, src, strlen(src), cp, remap); +	*utf16_len = len; +	return dst; +} +#endif /* CONFIG_CIFS_SMB2 */ diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 7fe6b52df50..d8eac3b6cef 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -44,7 +44,7 @@   * reserved symbols (along with \ and /), otherwise illegal to store   * in filenames in NTFS   */ -#define UNI_ASTERIK     (__u16) ('*' + 0xF000) +#define UNI_ASTERISK    (__u16) ('*' + 0xF000)  #define UNI_QUESTION    (__u16) ('?' + 0xF000)  #define UNI_COLON       (__u16) (':' + 0xF000)  #define UNI_GRTRTHAN    (__u16) ('>' + 0xF000) @@ -74,16 +74,25 @@ extern const struct UniCaseRange CifsUniLowerRange[];  #endif				/* UNIUPR_NOLOWER */  #ifdef __KERNEL__ -int cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, -		   const struct nls_table *codepage, bool mapchar); -int cifs_ucs2_bytes(const __le16 *from, int maxbytes, -		    const struct nls_table *codepage); -int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); -char *cifs_strndup_from_ucs(const char *src, const int maxlen, -			    const bool is_unicode, -			    const struct nls_table *codepage); +int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, +		    const struct nls_table *codepage, bool mapchar); +int cifs_utf16_bytes(const __le16 *from, int maxbytes, +		     const struct nls_table *codepage); +int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); +char *cifs_strndup_from_utf16(const char *src, const int maxlen, +			      const bool is_unicode, +			      const struct nls_table *codepage); +extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, +			      const struct nls_table *cp, int mapChars); +#ifdef CONFIG_CIFS_SMB2 +extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, +				     int *utf16_len, const struct nls_table *cp, +				     int remap); +#endif /* CONFIG_CIFS_SMB2 */  #endif +wchar_t cifs_toupper(wchar_t in); +  /*   * UniStrcat:  Concatenate the second string to the first   * @@ -320,14 +329,14 @@ UniToupper(register wchar_t uc)  /*   * UniStrupr:  Upper case a unicode string   */ -static inline wchar_t * -UniStrupr(register wchar_t *upin) +static inline __le16 * +UniStrupr(register __le16 *upin)  { -	register wchar_t *up; +	register __le16 *up;  	up = upin;  	while (*up) {		/* For all characters */ -		*up = UniToupper(*up); +		*up = cpu_to_le16(UniToupper(le16_to_cpu(*up)));  		up++;  	}  	return upin;		/* Return input pointer */ diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index c9b4792ae82..7ff866dbb89 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -23,96 +23,147 @@  #include <linux/fs.h>  #include <linux/slab.h> +#include <linux/string.h> +#include <linux/keyctl.h> +#include <linux/key-type.h> +#include <keys/user-type.h>  #include "cifspdu.h"  #include "cifsglob.h"  #include "cifsacl.h"  #include "cifsproto.h"  #include "cifs_debug.h" - -#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 */ +/* security id for everyone/world system group */  static const struct cifs_sid sid_everyone = {  	1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct cifs_sid sid_authusers = { +	1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(11)} };  /* group users */  static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; +static const struct cred *root_cred; -int match_sid(struct cifs_sid *ctsid) +static int +cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)  { -	int i, j; -	int num_subauth, num_sat, num_saw; -	struct cifs_sid *cwsid; +	char *payload; + +	/* +	 * If the payload is less than or equal to the size of a pointer, then +	 * an allocation here is wasteful. Just copy the data directly to the +	 * payload.value union member instead. +	 * +	 * With this however, you must check the datalen before trying to +	 * dereference payload.data! +	 */ +	if (prep->datalen <= sizeof(key->payload)) { +		key->payload.value = 0; +		memcpy(&key->payload.value, prep->data, prep->datalen); +		key->datalen = prep->datalen; +		return 0; +	} +	payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); +	if (!payload) +		return -ENOMEM; -	if (!ctsid) -		return -1; +	key->payload.data = payload; +	key->datalen = prep->datalen; +	return 0; +} -	for (i = 0; i < NUM_WK_SIDS; ++i) { -		cwsid = &(wksidarr[i].cifssid); +static inline void +cifs_idmap_key_destroy(struct key *key) +{ +	if (key->datalen > sizeof(key->payload)) +		kfree(key->payload.data); +} -		/* compare the revision */ -		if (ctsid->revision != cwsid->revision) -			continue; +static struct key_type cifs_idmap_key_type = { +	.name        = "cifs.idmap", +	.instantiate = cifs_idmap_key_instantiate, +	.destroy     = cifs_idmap_key_destroy, +	.describe    = user_describe, +	.match       = user_match, +}; -		/* 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 */ -		} +static char * +sid_to_key_str(struct cifs_sid *sidptr, unsigned int type) +{ +	int i, len; +	unsigned int saval; +	char *sidstr, *strptr; +	unsigned long long id_auth_val; + +	/* 3 bytes for prefix */ +	sidstr = kmalloc(3 + SID_STRING_BASE_SIZE + +			 (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth), +			 GFP_KERNEL); +	if (!sidstr) +		return sidstr; + +	strptr = sidstr; +	len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g', +			sidptr->revision); +	strptr += len; + +	/* The authority field is a single 48-bit number */ +	id_auth_val = (unsigned long long)sidptr->authority[5]; +	id_auth_val |= (unsigned long long)sidptr->authority[4] << 8; +	id_auth_val |= (unsigned long long)sidptr->authority[3] << 16; +	id_auth_val |= (unsigned long long)sidptr->authority[2] << 24; +	id_auth_val |= (unsigned long long)sidptr->authority[1] << 32; +	id_auth_val |= (unsigned long long)sidptr->authority[0] << 48; + +	/* +	 * MS-DTYP states that if the authority is >= 2^32, then it should be +	 * expressed as a hex value. +	 */ +	if (id_auth_val <= UINT_MAX) +		len = sprintf(strptr, "-%llu", id_auth_val); +	else +		len = sprintf(strptr, "-0x%llx", id_auth_val); + +	strptr += len; -		cFYI(1, "matching sid: %s\n", wksidarr[i].sidname); -		return 0; /* sids compare/match */ +	for (i = 0; i < sidptr->num_subauth; ++i) { +		saval = le32_to_cpu(sidptr->sub_auth[i]); +		len = sprintf(strptr, "-%u", saval); +		strptr += len;  	} -	cFYI(1, "No matching sid"); -	return -1; +	return sidstr;  } -/* 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) +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +static int +compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)  {  	int i;  	int num_subauth, num_sat, num_saw;  	if ((!ctsid) || (!cwsid)) -		return 0; +		return 1;  	/* compare the revision */ -	if (ctsid->revision != cwsid->revision) -		return 0; +	if (ctsid->revision != cwsid->revision) { +		if (ctsid->revision > cwsid->revision) +			return 1; +		else +			return -1; +	}  	/* compare all of the six auth values */ -	for (i = 0; i < 6; ++i) { -		if (ctsid->authority[i] != cwsid->authority[i]) -			return 0; +	for (i = 0; i < NUM_AUTHS; ++i) { +		if (ctsid->authority[i] != cwsid->authority[i]) { +			if (ctsid->authority[i] > cwsid->authority[i]) +				return 1; +			else +				return -1; +		}  	}  	/* compare all of the subauth values if any */ @@ -121,21 +172,236 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)  	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; +			if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { +				if (le32_to_cpu(ctsid->sub_auth[i]) > +					le32_to_cpu(cwsid->sub_auth[i])) +					return 1; +				else +					return -1; +			}  		}  	} -	return 1; /* sids compare/match */ +	return 0; /* sids compare/match */ +} + +static void +cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) +{ +	int i; + +	dst->revision = src->revision; +	dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); +	for (i = 0; i < NUM_AUTHS; ++i) +		dst->authority[i] = src->authority[i]; +	for (i = 0; i < dst->num_subauth; ++i) +		dst->sub_auth[i] = src->sub_auth[i]; +} + +static int +id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) +{ +	int rc; +	struct key *sidkey; +	struct cifs_sid *ksid; +	unsigned int ksid_size; +	char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */ +	const struct cred *saved_cred; + +	rc = snprintf(desc, sizeof(desc), "%ci:%u", +			sidtype == SIDOWNER ? 'o' : 'g', cid); +	if (rc >= sizeof(desc)) +		return -EINVAL; + +	rc = 0; +	saved_cred = override_creds(root_cred); +	sidkey = request_key(&cifs_idmap_key_type, desc, ""); +	if (IS_ERR(sidkey)) { +		rc = -EINVAL; +		cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n", +			 __func__, sidtype == SIDOWNER ? 'u' : 'g', cid); +		goto out_revert_creds; +	} else if (sidkey->datalen < CIFS_SID_BASE_SIZE) { +		rc = -EIO; +		cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", +			 __func__, sidkey->datalen); +		goto invalidate_key; +	} + +	/* +	 * A sid is usually too large to be embedded in payload.value, but if +	 * there are no subauthorities and the host has 8-byte pointers, then +	 * it could be. +	 */ +	ksid = sidkey->datalen <= sizeof(sidkey->payload) ? +		(struct cifs_sid *)&sidkey->payload.value : +		(struct cifs_sid *)sidkey->payload.data; + +	ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); +	if (ksid_size > sidkey->datalen) { +		rc = -EIO; +		cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n", +			 __func__, sidkey->datalen, ksid_size); +		goto invalidate_key; +	} + +	cifs_copy_sid(ssid, ksid); +out_key_put: +	key_put(sidkey); +out_revert_creds: +	revert_creds(saved_cred); +	return rc; + +invalidate_key: +	key_invalidate(sidkey); +	goto out_key_put; +} + +static int +sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, +		struct cifs_fattr *fattr, uint sidtype) +{ +	int rc; +	struct key *sidkey; +	char *sidstr; +	const struct cred *saved_cred; +	kuid_t fuid = cifs_sb->mnt_uid; +	kgid_t fgid = cifs_sb->mnt_gid; + +	/* +	 * If we have too many subauthorities, then something is really wrong. +	 * Just return an error. +	 */ +	if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { +		cifs_dbg(FYI, "%s: %u subauthorities is too many!\n", +			 __func__, psid->num_subauth); +		return -EIO; +	} + +	sidstr = sid_to_key_str(psid, sidtype); +	if (!sidstr) +		return -ENOMEM; + +	saved_cred = override_creds(root_cred); +	sidkey = request_key(&cifs_idmap_key_type, sidstr, ""); +	if (IS_ERR(sidkey)) { +		rc = -EINVAL; +		cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n", +			 __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g'); +		goto out_revert_creds; +	} + +	/* +	 * FIXME: Here we assume that uid_t and gid_t are same size. It's +	 * probably a safe assumption but might be better to check based on +	 * sidtype. +	 */ +	BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); +	if (sidkey->datalen != sizeof(uid_t)) { +		rc = -EIO; +		cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", +			 __func__, sidkey->datalen); +		key_invalidate(sidkey); +		goto out_key_put; +	} + +	if (sidtype == SIDOWNER) { +		kuid_t uid; +		uid_t id; +		memcpy(&id, &sidkey->payload.value, sizeof(uid_t)); +		uid = make_kuid(&init_user_ns, id); +		if (uid_valid(uid)) +			fuid = uid; +	} else { +		kgid_t gid; +		gid_t id; +		memcpy(&id, &sidkey->payload.value, sizeof(gid_t)); +		gid = make_kgid(&init_user_ns, id); +		if (gid_valid(gid)) +			fgid = gid; +	} + +out_key_put: +	key_put(sidkey); +out_revert_creds: +	revert_creds(saved_cred); +	kfree(sidstr); + +	/* +	 * Note that we return 0 here unconditionally. If the mapping +	 * fails then we just fall back to using the mnt_uid/mnt_gid. +	 */ +	if (sidtype == SIDOWNER) +		fattr->cf_uid = fuid; +	else +		fattr->cf_gid = fgid; +	return 0;  } +int +init_cifs_idmap(void) +{ +	struct cred *cred; +	struct key *keyring; +	int ret; + +	cifs_dbg(FYI, "Registering the %s key type\n", +		 cifs_idmap_key_type.name); + +	/* create an override credential set with a special thread keyring in +	 * which requests are cached +	 * +	 * this is used to prevent malicious redirections from being installed +	 * with add_key(). +	 */ +	cred = prepare_kernel_cred(NULL); +	if (!cred) +		return -ENOMEM; + +	keyring = keyring_alloc(".cifs_idmap", +				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, +				(KEY_POS_ALL & ~KEY_POS_SETATTR) | +				KEY_USR_VIEW | KEY_USR_READ, +				KEY_ALLOC_NOT_IN_QUOTA, NULL); +	if (IS_ERR(keyring)) { +		ret = PTR_ERR(keyring); +		goto failed_put_cred; +	} + +	ret = register_key_type(&cifs_idmap_key_type); +	if (ret < 0) +		goto failed_put_key; + +	/* instruct request_key() to use this special keyring as a cache for +	 * the results it looks up */ +	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); +	cred->thread_keyring = keyring; +	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; +	root_cred = cred; + +	cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring)); +	return 0; + +failed_put_key: +	key_put(keyring); +failed_put_cred: +	put_cred(cred); +	return ret; +} + +void +exit_cifs_idmap(void) +{ +	key_revoke(root_cred->thread_keyring); +	unregister_key_type(&cifs_idmap_key_type); +	put_cred(root_cred); +	cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name); +}  /* copy ntsd, owner sid, and group sid from a security descriptor to another */  static void copy_sec_desc(const struct cifs_ntsd *pntsd,  				struct cifs_ntsd *pnntsd, __u32 sidsoffset)  { -	int i; -  	struct cifs_sid *owner_sid_ptr, *group_sid_ptr;  	struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; @@ -151,26 +417,14 @@ static void copy_sec_desc(const struct cifs_ntsd *pntsd,  	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]; +	cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);  	/* copy group sid */  	group_sid_ptr = (struct cifs_sid *)((char *)pntsd +  				le32_to_cpu(pntsd->gsidoffset));  	ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +  					sizeof(struct cifs_sid)); - -	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]; +	cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);  	return;  } @@ -208,14 +462,14 @@ static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,  			*pbits_to_set &= ~S_IXUGO;  		return;  	} else if (type != ACCESS_ALLOWED) { -		cERROR(1, "unknown access control type %d", type); +		cifs_dbg(VFS, "unknown access control type %d\n", type);  		return;  	}  	/* else ACCESS_ALLOWED type */  	if (flags & GENERIC_ALL) {  		*pmode |= (S_IRWXUGO & (*pbits_to_set)); -		cFYI(DBG2, "all perms"); +		cifs_dbg(NOISY, "all perms\n");  		return;  	}  	if ((flags & GENERIC_WRITE) || @@ -228,7 +482,7 @@ static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,  			((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); +	cifs_dbg(NOISY, "access flags 0x%x mode now 0x%x\n", flags, *pmode);  	return;  } @@ -257,7 +511,8 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,  	if (mode & S_IXUGO)  		*pace_flags |= SET_FILE_EXEC_RIGHTS; -	cFYI(DBG2, "mode: 0x%x, access flags now 0x%x", mode, *pace_flags); +	cifs_dbg(NOISY, "mode: 0x%x, access flags now 0x%x\n", +		 mode, *pace_flags);  	return;  } @@ -277,7 +532,7 @@ static __u16 fill_ace_for_sid(struct cifs_ace *pntace,  	pntace->sid.revision = psid->revision;  	pntace->sid.num_subauth = psid->num_subauth; -	for (i = 0; i < 6; i++) +	for (i = 0; i < NUM_AUTHS; i++)  		pntace->sid.authority[i] = psid->authority[i];  	for (i = 0; i < psid->num_subauth; i++)  		pntace->sid.sub_auth[i] = psid->sub_auth[i]; @@ -297,24 +552,24 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)  	/* 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)); +		cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size));  		return;  	}  	if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { -		cERROR(1, "ACL too small to parse ACE"); +		cifs_dbg(VFS, "ACL too small to parse ACE\n");  		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)); +		cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n", +			 pace->sid.revision, pace->sid.num_subauth, pace->type, +			 pace->flags, le16_to_cpu(pace->size));  		for (i = 0; i < num_subauth; ++i) { -			cFYI(1, "ACE sub_auth[%d]: 0x%x", i, -				le32_to_cpu(pace->sid.sub_auth[i])); +			cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n", +				 i, le32_to_cpu(pace->sid.sub_auth[i]));  		}  		/* BB add length check to make sure that we do not have huge @@ -347,13 +602,13 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,  	/* 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"); +		cifs_dbg(VFS, "ACL too small to parse DACL\n");  		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)); +	cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", +		 le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), +		 le32_to_cpu(pdacl->num_aces));  	/* reset rwx permissions for user/group/other.  	   Also, if num_aces is 0 i.e. DACL has no ACEs, @@ -364,35 +619,45 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,  	acl_size = sizeof(struct cifs_acl);  	num_aces = le32_to_cpu(pdacl->num_aces); -	if (num_aces  > 0) { +	if (num_aces > 0) {  		umode_t user_mask = S_IRWXU;  		umode_t group_mask = S_IRWXG; -		umode_t other_mask = S_IRWXO; +		umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO; +		if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) +			return;  		ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),  				GFP_KERNEL); +		if (!ppace) +			return;  		for (i = 0; i < num_aces; ++i) {  			ppace[i] = (struct cifs_ace *) (acl_base + acl_size);  #ifdef CONFIG_CIFS_DEBUG2  			dump_ace(ppace[i], end_of_acl);  #endif -			if (compare_sids(&(ppace[i]->sid), pownersid)) +			if (compare_sids(&(ppace[i]->sid), pownersid) == 0)  				access_flags_to_mode(ppace[i]->access_req,  						     ppace[i]->type,  						     &fattr->cf_mode,  						     &user_mask); -			if (compare_sids(&(ppace[i]->sid), pgrpsid)) +			if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)  				access_flags_to_mode(ppace[i]->access_req,  						     ppace[i]->type,  						     &fattr->cf_mode,  						     &group_mask); -			if (compare_sids(&(ppace[i]->sid), &sid_everyone)) +			if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) +				access_flags_to_mode(ppace[i]->access_req, +						     ppace[i]->type, +						     &fattr->cf_mode, +						     &other_mask); +			if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)  				access_flags_to_mode(ppace[i]->access_req,  						     ppace[i]->type,  						     &fattr->cf_mode,  						     &other_mask); +  /*			memcpy((void *)(&(cifscred->aces[i])),  				(void *)ppace[i],  				sizeof(struct cifs_ace)); */ @@ -437,37 +702,37 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)  	/* 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); +		cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid);  		return -EINVAL;  	} -	if (psid->num_subauth) {  #ifdef CONFIG_CIFS_DEBUG2 +	if (psid->num_subauth) {  		int i; -		cFYI(1, "SID revision %d num_auth %d", -			psid->revision, psid->num_subauth); +		cifs_dbg(FYI, "SID revision %d num_auth %d\n", +			 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])); +			cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n", +				 i, le32_to_cpu(psid->sub_auth[i]));  		}  		/* BB add length check to make sure that we do not have huge  			num auths and therefore go off the end */ -		cFYI(1, "RID 0x%x", -			le32_to_cpu(psid->sub_auth[psid->num_subauth-1])); -#endif +		cifs_dbg(FYI, "RID 0x%x\n", +			 le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));  	} +#endif  	return 0;  }  /* Convert CIFS ACL to POSIX form */ -static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, -			  struct cifs_fattr *fattr) +static int parse_sec_desc(struct cifs_sb_info *cifs_sb, +		struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)  { -	int rc; +	int rc = 0;  	struct cifs_sid *owner_sid_ptr, *group_sid_ptr;  	struct cifs_acl *dacl_ptr; /* no need for SACL ptr */  	char *end_of_acl = ((char *)pntsd) + acl_len; @@ -482,93 +747,145 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,  				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", +	cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",  		 pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),  		 le32_to_cpu(pntsd->gsidoffset),  		 le32_to_cpu(pntsd->sacloffset), dacloffset);  /*	cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */  	rc = parse_sid(owner_sid_ptr, end_of_acl); -	if (rc) +	if (rc) { +		cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc);  		return rc; +	} +	rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); +	if (rc) { +		cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", +			 __func__, rc); +		return rc; +	}  	rc = parse_sid(group_sid_ptr, end_of_acl); -	if (rc) +	if (rc) { +		cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n", +			 __func__, rc);  		return rc; +	} +	rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); +	if (rc) { +		cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", +			 __func__, rc); +		return rc; +	}  	if (dacloffset)  		parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,  			   group_sid_ptr, fattr);  	else -		cFYI(1, "no ACL"); /* BB grant all or default perms? */ +		cifs_dbg(FYI, "no ACL\n"); /* 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; +	return rc;  } -  /* 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) +	__u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag)  {  	int rc = 0;  	__u32 dacloffset;  	__u32 ndacloffset;  	__u32 sidsoffset;  	struct cifs_sid *owner_sid_ptr, *group_sid_ptr; +	struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;  	struct cifs_acl *dacl_ptr = NULL;  /* no need for SACL ptr */  	struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ -	if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL)) -		return -EIO; - -	owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + +	if (nmode != NO_CHANGE_64) { /* chmod */ +		owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +  				le32_to_cpu(pntsd->osidoffset)); -	group_sid_ptr = (struct cifs_sid *)((char *)pntsd + +		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); +		dacloffset = le32_to_cpu(pntsd->dacloffset); +		dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); +		ndacloffset = sizeof(struct cifs_ntsd); +		ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); +		ndacl_ptr->revision = dacl_ptr->revision; +		ndacl_ptr->size = 0; +		ndacl_ptr->num_aces = 0; + +		rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, +					nmode); +		sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); +		/* copy sec desc control portion & owner and group sids */ +		copy_sec_desc(pntsd, pnntsd, sidsoffset); +		*aclflag = CIFS_ACL_DACL; +	} else { +		memcpy(pnntsd, pntsd, secdesclen); +		if (uid_valid(uid)) { /* chown */ +			uid_t id; +			owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + +					le32_to_cpu(pnntsd->osidoffset)); +			nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid), +								GFP_KERNEL); +			if (!nowner_sid_ptr) +				return -ENOMEM; +			id = from_kuid(&init_user_ns, uid); +			rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); +			if (rc) { +				cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", +					 __func__, rc, id); +				kfree(nowner_sid_ptr); +				return rc; +			} +			cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr); +			kfree(nowner_sid_ptr); +			*aclflag = CIFS_ACL_OWNER; +		} +		if (gid_valid(gid)) { /* chgrp */ +			gid_t id; +			group_sid_ptr = (struct cifs_sid *)((char *)pnntsd + +					le32_to_cpu(pnntsd->gsidoffset)); +			ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid), +								GFP_KERNEL); +			if (!ngroup_sid_ptr) +				return -ENOMEM; +			id = from_kgid(&init_user_ns, gid); +			rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); +			if (rc) { +				cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", +					 __func__, rc, id); +				kfree(ngroup_sid_ptr); +				return rc; +			} +			cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr); +			kfree(ngroup_sid_ptr); +			*aclflag = CIFS_ACL_GROUP; +		} +	}  	return rc;  } -static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, -		__u16 fid, u32 *pacllen) +struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, +		const struct cifs_fid *cifsfid, u32 *pacllen)  {  	struct cifs_ntsd *pntsd = NULL; -	int xid, rc; +	unsigned int xid; +	int rc;  	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) -		return NULL; +		return ERR_CAST(tlink); -	xid = GetXid(); -	rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen); -	FreeXid(xid); +	xid = get_xid(); +	rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, +				pacllen); +	free_xid(xid);  	cifs_put_tlink(tlink); -	cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); +	cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); +	if (rc) +		return ERR_PTR(rc);  	return pntsd;  } @@ -577,37 +894,48 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,  {  	struct cifs_ntsd *pntsd = NULL;  	int oplock = 0; -	int xid, rc; -	__u16 fid; -	struct cifsTconInfo *tcon; +	unsigned int xid; +	int rc, create_options = 0; +	struct cifs_tcon *tcon;  	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	if (IS_ERR(tlink)) -		return NULL; +		return ERR_CAST(tlink);  	tcon = tlink_tcon(tlink); -	xid = GetXid(); - -	rc = CIFSSMBOpen(xid, 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) { -		cERROR(1, "Unable to open file to get ACL"); -		goto out; +	xid = get_xid(); + +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = READ_CONTROL; +	oparms.create_options = create_options; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (!rc) { +		rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); +		CIFSSMBClose(xid, tcon, fid.netfid);  	} -	rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen); -	cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); - -	CIFSSMBClose(xid, tcon, fid); - out:  	cifs_put_tlink(tlink); -	FreeXid(xid); +	free_xid(xid); + +	cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); +	if (rc) +		return ERR_PTR(rc);  	return pntsd;  }  /* Retrieve an ACL from the server */ -static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, +struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,  				      struct inode *inode, const char *path,  				      u32 *pacllen)  { @@ -619,151 +947,174 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,  	if (!open_file)  		return get_cifs_acl_by_path(cifs_sb, path, pacllen); -	pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen); +	pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen);  	cifsFileInfo_put(open_file);  	return pntsd;  } -static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid, -		struct cifs_ntsd *pnntsd, u32 acllen) -{ -	int xid, rc; -	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - -	if (IS_ERR(tlink)) -		return PTR_ERR(tlink); - -	xid = GetXid(); -	rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen); -	FreeXid(xid); -	cifs_put_tlink(tlink); - -	cFYI(DBG2, "SetCIFSACL rc = %d", rc); -	return rc; -} - -static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, -		struct cifs_ntsd *pnntsd, u32 acllen) + /* Set an ACL on the server */ +int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, +			struct inode *inode, const char *path, int aclflag)  {  	int oplock = 0; -	int xid, rc; -	__u16 fid; -	struct cifsTconInfo *tcon; +	unsigned int xid; +	int rc, access_flags, create_options = 0; +	struct cifs_tcon *tcon; +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	if (IS_ERR(tlink))  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid(); -	rc = CIFSSMBOpen(xid, 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 (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) +		access_flags = WRITE_OWNER; +	else +		access_flags = WRITE_DAC; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = access_flags; +	oparms.create_options = create_options; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc) { -		cERROR(1, "Unable to open file to set ACL"); +		cifs_dbg(VFS, "Unable to open file to set ACL\n");  		goto out;  	} -	rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen); -	cFYI(DBG2, "SetCIFSACL rc = %d", rc); +	rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag); +	cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); -	CIFSSMBClose(xid, tcon, fid); +	CIFSSMBClose(xid, tcon, fid.netfid);  out: -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  	return rc;  } -/* Set an ACL on the server */ -static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, -				struct inode *inode, const char *path) -{ -	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); -	struct cifsFileInfo *open_file; -	int rc; - -	cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode); - -	open_file = find_readable_file(CIFS_I(inode), true); -	if (!open_file) -		return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen); - -	rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen); -	cifsFileInfo_put(open_file); -	return rc; -} -  /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */ -void +int  cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, -		  struct inode *inode, const char *path, const __u16 *pfid) +		  struct inode *inode, const char *path, +		  const struct cifs_fid *pfid)  {  	struct cifs_ntsd *pntsd = NULL;  	u32 acllen = 0;  	int rc = 0; +	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_tcon *tcon; -	cFYI(DBG2, "converting ACL to mode for %s", path); +	cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); -	if (pfid) -		pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen); -	else -		pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen); +	if (IS_ERR(tlink)) +		return PTR_ERR(tlink); +	tcon = tlink_tcon(tlink); +	if (pfid && (tcon->ses->server->ops->get_acl_by_fid)) +		pntsd = tcon->ses->server->ops->get_acl_by_fid(cifs_sb, pfid, +							  &acllen); +	else if (tcon->ses->server->ops->get_acl) +		pntsd = tcon->ses->server->ops->get_acl(cifs_sb, inode, path, +							&acllen); +	else { +		cifs_put_tlink(tlink); +		return -EOPNOTSUPP; +	}  	/* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ -	if (pntsd) -		rc = parse_sec_desc(pntsd, acllen, fattr); -	if (rc) -		cFYI(1, "parse sec desc failed rc = %d", rc); +	if (IS_ERR(pntsd)) { +		rc = PTR_ERR(pntsd); +		cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); +	} else { +		rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr); +		kfree(pntsd); +		if (rc) +			cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); +	} -	kfree(pntsd); -	return; +	cifs_put_tlink(tlink); + +	return rc;  }  /* 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 +id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, +			kuid_t uid, kgid_t gid)  {  	int rc = 0; +	int aclflag = CIFS_ACL_DACL; /* default flag to set */  	__u32 secdesclen = 0;  	struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */  	struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_tcon *tcon; -	cFYI(DBG2, "set ACL from mode for %s", path); +	if (IS_ERR(tlink)) +		return PTR_ERR(tlink); +	tcon = tlink_tcon(tlink); -	/* Get the security descriptor */ -	pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen); - -	/* 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; -		} +	cifs_dbg(NOISY, "set ACL from mode for %s\n", path); -		rc = build_sec_desc(pntsd, pnntsd, inode, nmode); +	/* Get the security descriptor */ -		cFYI(DBG2, "build_sec_desc rc: %d", rc); +	if (tcon->ses->server->ops->get_acl == NULL) { +		cifs_put_tlink(tlink); +		return -EOPNOTSUPP; +	} -		if (!rc) { -			/* Set the security descriptor */ -			rc = set_cifs_acl(pnntsd, secdesclen, inode, path); -			cFYI(DBG2, "set_cifs_acl rc: %d", rc); -		} +	pntsd = tcon->ses->server->ops->get_acl(cifs_sb, inode, path, +						&secdesclen); +	if (IS_ERR(pntsd)) { +		rc = PTR_ERR(pntsd); +		cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); +		cifs_put_tlink(tlink); +		return rc; +	} -		kfree(pnntsd); +	/* +	 * Add three ACEs for owner, group, everyone getting rid of other ACEs +	 * as chmod disables ACEs and set the security descriptor. Allocate +	 * memory for the smb header, set security descriptor request security +	 * descriptor parameters, and secuirty descriptor itself +	 */ +	secdesclen = max_t(u32, secdesclen, DEFAULT_SEC_DESC_LEN); +	pnntsd = kmalloc(secdesclen, GFP_KERNEL); +	if (!pnntsd) {  		kfree(pntsd); +		cifs_put_tlink(tlink); +		return -ENOMEM;  	} +	rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid, +				&aclflag); + +	cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); + +	if (tcon->ses->server->ops->set_acl == NULL) +		rc = -EOPNOTSUPP; + +	if (!rc) { +		/* Set the security descriptor */ +		rc = tcon->ses->server->ops->set_acl(pnntsd, secdesclen, inode, +						     path, aclflag); +		cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); +	} +	cifs_put_tlink(tlink); + +	kfree(pnntsd); +	kfree(pntsd);  	return rc;  } -#endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 6c8096cf515..4f388483526 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -23,11 +23,8 @@  #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 NUM_AUTHS (6)	/* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */  #define READ_BIT        0x4  #define WRITE_BIT       0x2 @@ -39,6 +36,35 @@  #define ACCESS_ALLOWED	0  #define ACCESS_DENIED	1 +#define SIDOWNER 1 +#define SIDGROUP 2 + +/* + * Security Descriptor length containing DACL with 3 ACEs (one each for + * owner, group and world). + */ +#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ +			      sizeof(struct cifs_acl) + \ +			      (sizeof(struct cifs_ace) * 3)) + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8:  max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ +  struct cifs_ntsd {  	__le16 revision; /* revision level */  	__le16 type; @@ -51,10 +77,13 @@ struct cifs_ntsd {  struct cifs_sid {  	__u8 revision; /* revision level */  	__u8 num_subauth; -	__u8 authority[6]; -	__le32 sub_auth[5]; /* sub_auth[num_subauth] */ +	__u8 authority[NUM_AUTHS]; +	__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */  } __attribute__((packed)); +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) +  struct cifs_acl {  	__le16 revision; /* revision level */  	__le16 size; @@ -69,16 +98,4 @@ struct cifs_ace {  	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 */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index f856732161a..4934347321d 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -1,7 +1,7 @@  /*   *   fs/cifs/cifsencrypt.c   * - *   Copyright (C) International Business Machines  Corp., 2005,2006 + *   Copyright (C) International Business Machines  Corp., 2005,2013   *   Author(s): Steve French (sfrench@us.ibm.com)   *   *   This library is free software; you can redistribute it and/or modify @@ -24,112 +24,88 @@  #include "cifspdu.h"  #include "cifsglob.h"  #include "cifs_debug.h" -#include "md5.h"  #include "cifs_unicode.h"  #include "cifsproto.h"  #include "ntlmssp.h"  #include <linux/ctype.h>  #include <linux/random.h> +#include <linux/highmem.h> -/* 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, -				struct TCP_Server_Info *server, char *signature) +static int +cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server)  {  	int rc; +	unsigned int size; -	if (cifs_pdu == NULL || signature == NULL || server == NULL) -		return -EINVAL; - -	if (!server->secmech.sdescmd5) { -		cERROR(1, "%s: Can't generate signature\n", __func__); -		return -1; -	} +	if (server->secmech.sdescmd5 != NULL) +		return 0; /* already allocated */ -	rc = crypto_shash_init(&server->secmech.sdescmd5->shash); -	if (rc) { -		cERROR(1, "%s: Oould not init md5\n", __func__); +	server->secmech.md5 = crypto_alloc_shash("md5", 0, 0); +	if (IS_ERR(server->secmech.md5)) { +		cifs_dbg(VFS, "could not allocate crypto md5\n"); +		rc = PTR_ERR(server->secmech.md5); +		server->secmech.md5 = NULL;  		return rc;  	} -	crypto_shash_update(&server->secmech.sdescmd5->shash, -		server->session_key.response, server->session_key.len); - -	crypto_shash_update(&server->secmech.sdescmd5->shash, -		cifs_pdu->Protocol, cifs_pdu->smb_buf_length); - -	rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); +	size = sizeof(struct shash_desc) + +			crypto_shash_descsize(server->secmech.md5); +	server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL); +	if (!server->secmech.sdescmd5) { +		crypto_free_shash(server->secmech.md5); +		server->secmech.md5 = NULL; +		return -ENOMEM; +	} +	server->secmech.sdescmd5->shash.tfm = server->secmech.md5; +	server->secmech.sdescmd5->shash.flags = 0x0;  	return 0;  } -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, 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, -				struct TCP_Server_Info *server, char *signature) +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ +static int cifs_calc_signature(struct smb_rqst *rqst, +			struct TCP_Server_Info *server, char *signature)  {  	int i;  	int rc; +	struct kvec *iov = rqst->rq_iov; +	int n_vec = rqst->rq_nvec;  	if (iov == NULL || signature == NULL || server == NULL)  		return -EINVAL;  	if (!server->secmech.sdescmd5) { -		cERROR(1, "%s: Can't generate signature\n", __func__); -		return -1; +		rc = cifs_crypto_shash_md5_allocate(server); +		if (rc) { +			cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__); +			return -1; +		}  	}  	rc = crypto_shash_init(&server->secmech.sdescmd5->shash);  	if (rc) { -		cERROR(1, "%s: Oould not init md5\n", __func__); +		cifs_dbg(VFS, "%s: Could not init md5\n", __func__);  		return rc;  	} -	crypto_shash_update(&server->secmech.sdescmd5->shash, +	rc = crypto_shash_update(&server->secmech.sdescmd5->shash,  		server->session_key.response, server->session_key.len); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with response\n", __func__); +		return rc; +	}  	for (i = 0; i < n_vec; i++) {  		if (iov[i].iov_len == 0)  			continue;  		if (iov[i].iov_base == NULL) { -			cERROR(1, "null iovec entry"); +			cifs_dbg(VFS, "null iovec entry\n");  			return -EIO;  		}  		/* The first entry includes a length field (which does not get @@ -137,41 +113,66 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec,  		if (i == 0) {  			if (iov[0].iov_len <= 8) /* cmd field at offset 9 */  				break; /* nothing to sign or corrupt header */ +			rc =  			crypto_shash_update(&server->secmech.sdescmd5->shash,  				iov[i].iov_base + 4, iov[i].iov_len - 4); -		} else +		} else { +			rc =  			crypto_shash_update(&server->secmech.sdescmd5->shash,  				iov[i].iov_base, iov[i].iov_len); +		} +		if (rc) { +			cifs_dbg(VFS, "%s: Could not update with payload\n", +				 __func__); +			return rc; +		} +	} + +	/* now hash over the rq_pages array */ +	for (i = 0; i < rqst->rq_npages; i++) { +		struct kvec p_iov; + +		cifs_rqst_page_to_kvec(rqst, i, &p_iov); +		crypto_shash_update(&server->secmech.sdescmd5->shash, +					p_iov.iov_base, p_iov.iov_len); +		kunmap(rqst->rq_pages[i]);  	}  	rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);  	return rc;  } -int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, +/* must be called with server->srv_mutex held */ +int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,  		   __u32 *pexpected_response_sequence_number)  {  	int rc = 0;  	char smb_signature[20]; -	struct smb_hdr *cifs_pdu = iov[0].iov_base; +	struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;  	if ((cifs_pdu == NULL) || (server == NULL))  		return -EINVAL; -	if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) +	if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || +	    server->tcpStatus == CifsNeedNegotiate)  		return rc; -	spin_lock(&GlobalMid_Lock); +	if (!server->session_estab) { +		memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); +		return rc; +	} +  	cifs_pdu->Signature.Sequence.SequenceNumber =  				cpu_to_le32(server->sequence_number);  	cifs_pdu->Signature.Sequence.Reserved = 0; -	*pexpected_response_sequence_number = server->sequence_number++; -	server->sequence_number++; -	spin_unlock(&GlobalMid_Lock); +	*pexpected_response_sequence_number = ++server->sequence_number; +	++server->sequence_number; -	rc = cifs_calc_signature2(iov, n_vec, server, smb_signature); +	rc = cifs_calc_signature(rqst, server, smb_signature);  	if (rc)  		memset(cifs_pdu->Signature.SecuritySignature, 0, 8);  	else @@ -180,18 +181,41 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,  	return rc;  } -int cifs_verify_signature(struct smb_hdr *cifs_pdu, +int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, +		   __u32 *pexpected_response_sequence) +{ +	struct smb_rqst rqst = { .rq_iov = iov, +				 .rq_nvec = n_vec }; + +	return cifs_sign_rqst(&rqst, server, pexpected_response_sequence); +} + +/* must be called with server->srv_mutex held */ +int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, +		  __u32 *pexpected_response_sequence_number) +{ +	struct kvec iov; + +	iov.iov_base = cifs_pdu; +	iov.iov_len = be32_to_cpu(cifs_pdu->smb_buf_length) + 4; + +	return cifs_sign_smbv(&iov, 1, server, +			      pexpected_response_sequence_number); +} + +int cifs_verify_signature(struct smb_rqst *rqst,  			  struct TCP_Server_Info *server,  			  __u32 expected_sequence_number)  {  	unsigned int rc;  	char server_response_sig[8];  	char what_we_think_sig_should_be[20]; +	struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;  	if (cifs_pdu == NULL || server == NULL)  		return -EINVAL; -	if (cifs_pdu->Command == SMB_COM_NEGOTIATE) +	if (!server->session_estab)  		return 0;  	if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { @@ -206,8 +230,8 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,  	/* 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); +		cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", +			 cifs_pdu->Command);  	/* save off the origiginal signature so we can modify the smb and check  		its signature against what the server sent */ @@ -217,8 +241,9 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,  					cpu_to_le32(expected_sequence_number);  	cifs_pdu->Signature.Sequence.Reserved = 0; -	rc = cifs_calculate_signature(cifs_pdu, server, -		what_we_think_sig_should_be); +	mutex_lock(&server->srv_mutex); +	rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); +	mutex_unlock(&server->srv_mutex);  	if (rc)  		return rc; @@ -234,8 +259,9 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,  }  /* first calculate 24 bytes ntlm response and then 16 byte session key */ -int setup_ntlm_response(struct cifsSesInfo *ses) +int setup_ntlm_response(struct cifs_ses *ses, const struct nls_table *nls_cp)  { +	int rc = 0;  	unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE;  	char temp_key[CIFS_SESS_KEY_SIZE]; @@ -243,26 +269,40 @@ int setup_ntlm_response(struct cifsSesInfo *ses)  		return -EINVAL;  	ses->auth_key.response = kmalloc(temp_len, GFP_KERNEL); -	if (!ses->auth_key.response) { -		cERROR(1, "NTLM can't allocate (%u bytes) memory", temp_len); +	if (!ses->auth_key.response)  		return -ENOMEM; -	} +  	ses->auth_key.len = temp_len; -	SMBNTencrypt(ses->password, ses->server->cryptkey, -			ses->auth_key.response + CIFS_SESS_KEY_SIZE); +	rc = SMBNTencrypt(ses->password, ses->server->cryptkey, +			ses->auth_key.response + CIFS_SESS_KEY_SIZE, nls_cp); +	if (rc) { +		cifs_dbg(FYI, "%s Can't generate NTLM response, error: %d\n", +			 __func__, rc); +		return rc; +	} -	E_md4hash(ses->password, temp_key); -	mdfour(ses->auth_key.response, temp_key, CIFS_SESS_KEY_SIZE); +	rc = E_md4hash(ses->password, temp_key, nls_cp); +	if (rc) { +		cifs_dbg(FYI, "%s Can't generate NT hash, error: %d\n", +			 __func__, rc); +		return rc; +	} -	return 0; +	rc = mdfour(ses->auth_key.response, temp_key, CIFS_SESS_KEY_SIZE); +	if (rc) +		cifs_dbg(FYI, "%s Can't generate NTLM session key, error: %d\n", +			 __func__, rc); + +	return rc;  }  #ifdef CONFIG_CIFS_WEAK_PW_HASH -void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, +int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,  			char *lnm_session_key)  {  	int i; +	int rc;  	char password_with_pad[CIFS_ENCPWD_SIZE];  	memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); @@ -270,10 +310,9 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,  		strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE);  	if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) { -		memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE);  		memcpy(lnm_session_key, password_with_pad,  			CIFS_ENCPWD_SIZE); -		return; +		return 0;  	}  	/* calculate old style session key */ @@ -290,10 +329,9 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,  	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); +	rc = SMBencrypt(password_with_pad, cryptkey, lnm_session_key); -	/* clear password before we return/free memory */ -	memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); +	return rc;  }  #endif /* CIFS_WEAK_PW_HASH */ @@ -303,12 +341,10 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,   * Allocate domain name which gets freed when session struct is deallocated.   */  static int -build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp) +build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)  {  	unsigned int dlen; -	unsigned int wlen; -	unsigned int size = 6 * sizeof(struct ntlmssp2_name); -	__le64  curtime; +	unsigned int size = 2 * sizeof(struct ntlmssp2_name);  	char *defdmname = "WORKGROUP";  	unsigned char *blobptr;  	struct ntlmssp2_name *attrptr; @@ -320,62 +356,31 @@ build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp)  	}  	dlen = strlen(ses->domainName); -	wlen = strlen(ses->server->hostname); -	/* The length of this blob is a size which is -	 * six times the size of a structure which holds name/size + -	 * two times the unicode length of a domain name + -	 * two times the unicode length of a server name + -	 * size of a timestamp (which is 8 bytes). +	/* +	 * The length of this blob is two times the size of a +	 * structure (av pair) which holds name/size +	 * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) + +	 * unicode length of a netbios domain name  	 */ -	ses->auth_key.len = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8; +	ses->auth_key.len = size + 2 * dlen;  	ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL);  	if (!ses->auth_key.response) {  		ses->auth_key.len = 0; -		cERROR(1, "Challenge target info allocation failure");  		return -ENOMEM;  	}  	blobptr = ses->auth_key.response;  	attrptr = (struct ntlmssp2_name *) blobptr; +	/* +	 * As defined in MS-NTLM 3.3.2, just this av pair field +	 * is sufficient as part of the temp +	 */  	attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME);  	attrptr->length = cpu_to_le16(2 * dlen);  	blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); -	cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); - -	blobptr += 2 * dlen; -	attrptr = (struct ntlmssp2_name *) blobptr; - -	attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_COMPUTER_NAME); -	attrptr->length = cpu_to_le16(2 * wlen); -	blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); -	cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); - -	blobptr += 2 * wlen; -	attrptr = (struct ntlmssp2_name *) blobptr; - -	attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_DOMAIN_NAME); -	attrptr->length = cpu_to_le16(2 * dlen); -	blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); -	cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); - -	blobptr += 2 * dlen; -	attrptr = (struct ntlmssp2_name *) blobptr; - -	attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_COMPUTER_NAME); -	attrptr->length = cpu_to_le16(2 * wlen); -	blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); -	cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); - -	blobptr += 2 * wlen; -	attrptr = (struct ntlmssp2_name *) blobptr; - -	attrptr->type = cpu_to_le16(NTLMSSP_AV_TIMESTAMP); -	attrptr->length = cpu_to_le16(sizeof(__le64)); -	blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); -	curtime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); -	memcpy(blobptr, &curtime, sizeof(__le64)); +	cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp);  	return 0;  } @@ -391,7 +396,7 @@ build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp)   * about target string i.e. for some, just user name might suffice.   */  static int -find_domain_name(struct cifsSesInfo *ses, const struct nls_table *nls_cp) +find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)  {  	unsigned int attrsize;  	unsigned int type; @@ -417,14 +422,14 @@ find_domain_name(struct cifsSesInfo *ses, const struct nls_table *nls_cp)  		if (blobptr + attrsize > blobend)  			break;  		if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { -			if (!attrsize) +			if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN)  				break;  			if (!ses->domainName) {  				ses->domainName =  					kmalloc(attrsize + 1, GFP_KERNEL);  				if (!ses->domainName)  						return -ENOMEM; -				cifs_from_ucs2(ses->domainName, +				cifs_from_utf16(ses->domainName,  					(__le16 *)blobptr, attrsize, attrsize,  					nls_cp, false);  				break; @@ -436,46 +441,59 @@ find_domain_name(struct cifsSesInfo *ses, const struct nls_table *nls_cp)  	return 0;  } -static int calc_ntlmv2_hash(struct cifsSesInfo *ses, char *ntlmv2_hash, +static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,  			    const struct nls_table *nls_cp)  {  	int rc = 0;  	int len;  	char nt_hash[CIFS_NTHASH_SIZE]; -	wchar_t *user; +	__le16 *user;  	wchar_t *domain;  	wchar_t *server;  	if (!ses->server->secmech.sdeschmacmd5) { -		cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); +		cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);  		return -1;  	}  	/* calculate md4 hash of password */ -	E_md4hash(ses->password, nt_hash); +	E_md4hash(ses->password, nt_hash, nls_cp); -	crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash, +	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash,  				CIFS_NTHASH_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__); +		return rc; +	}  	rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);  	if (rc) { -		cERROR(1, "calc_ntlmv2_hash: could not init hmacmd5\n"); +		cifs_dbg(VFS, "%s: could not init hmacmd5\n", __func__);  		return rc;  	} -	/* convert ses->userName to unicode and uppercase */ -	len = strlen(ses->userName); +	/* convert ses->user_name to unicode */ +	len = ses->user_name ? strlen(ses->user_name) : 0;  	user = kmalloc(2 + (len * 2), GFP_KERNEL);  	if (user == NULL) { -		cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n");  		rc = -ENOMEM; -		goto calc_exit_2; +		return rc; +	} + +	if (len) { +		len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); +		UniStrupr(user); +	} else { +		memset(user, '\0', 2);  	} -	len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); -	UniStrupr(user); -	crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,  				(char *)user, 2 * len); +	kfree(user); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with user\n", __func__); +		return rc; +	}  	/* convert ses->domainName to unicode and uppercase */  	if (ses->domainName) { @@ -483,98 +501,156 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses, char *ntlmv2_hash,  		domain = kmalloc(2 + (len * 2), GFP_KERNEL);  		if (domain == NULL) { -			cERROR(1, "calc_ntlmv2_hash: domain mem alloc failure");  			rc = -ENOMEM; -			goto calc_exit_1; +			return rc;  		} -		len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, -					nls_cp); +		len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len, +				      nls_cp); +		rc =  		crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,  					(char *)domain, 2 * len);  		kfree(domain); +		if (rc) { +			cifs_dbg(VFS, "%s: Could not update with domain\n", +				 __func__); +			return rc; +		}  	} else if (ses->serverName) {  		len = strlen(ses->serverName);  		server = kmalloc(2 + (len * 2), GFP_KERNEL);  		if (server == NULL) { -			cERROR(1, "calc_ntlmv2_hash: server mem alloc failure");  			rc = -ENOMEM; -			goto calc_exit_1; +			return rc;  		} -		len = cifs_strtoUCS((__le16 *)server, ses->serverName, len, +		len = cifs_strtoUTF16((__le16 *)server, ses->serverName, len,  					nls_cp); +		rc =  		crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,  					(char *)server, 2 * len);  		kfree(server); +		if (rc) { +			cifs_dbg(VFS, "%s: Could not update with server\n", +				 __func__); +			return rc; +		}  	}  	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,  					ntlmv2_hash); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); -calc_exit_1: -	kfree(user); -calc_exit_2:  	return rc;  }  static int -CalcNTLMv2_response(const struct cifsSesInfo *ses, char *ntlmv2_hash) +CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)  {  	int rc; -	unsigned int offset = CIFS_SESS_KEY_SIZE + 8; +	struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) +	    (ses->auth_key.response + CIFS_SESS_KEY_SIZE); +	unsigned int hash_len; + +	/* The MD5 hash starts at challenge_key.key */ +	hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + +		offsetof(struct ntlmv2_resp, challenge.key[0]));  	if (!ses->server->secmech.sdeschmacmd5) { -		cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); +		cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__);  		return -1;  	} -	crypto_shash_setkey(ses->server->secmech.hmacmd5, -				ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); +	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, +				 ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", +			 __func__); +		return rc; +	}  	rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);  	if (rc) { -		cERROR(1, "CalcNTLMv2_response: could not init hmacmd5"); +		cifs_dbg(VFS, "%s: could not init hmacmd5\n", __func__);  		return rc;  	} -	if (ses->server->secType == RawNTLMSSP) -		memcpy(ses->auth_key.response + offset, -			ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); +	if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) +		memcpy(ntlmv2->challenge.key, +		       ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);  	else -		memcpy(ses->auth_key.response + offset, -			ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); -	crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, -		ses->auth_key.response + offset, ses->auth_key.len - offset); +		memcpy(ntlmv2->challenge.key, +		       ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, +				 ntlmv2->challenge.key, hash_len); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with response\n", __func__); +		return rc; +	} +	/* Note that the MD5 digest over writes anon.challenge_key.key */  	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, -		ses->auth_key.response + CIFS_SESS_KEY_SIZE); +				ntlmv2->ntlmv2_hash); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);  	return rc;  } +static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server) +{ +	int rc; +	unsigned int size; + +	/* check if already allocated */ +	if (server->secmech.sdeschmacmd5) +		return 0; + +	server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0); +	if (IS_ERR(server->secmech.hmacmd5)) { +		cifs_dbg(VFS, "could not allocate crypto hmacmd5\n"); +		rc = PTR_ERR(server->secmech.hmacmd5); +		server->secmech.hmacmd5 = NULL; +		return rc; +	} + +	size = sizeof(struct shash_desc) + +			crypto_shash_descsize(server->secmech.hmacmd5); +	server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); +	if (!server->secmech.sdeschmacmd5) { +		crypto_free_shash(server->secmech.hmacmd5); +		server->secmech.hmacmd5 = NULL; +		return -ENOMEM; +	} +	server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5; +	server->secmech.sdeschmacmd5->shash.flags = 0x0; + +	return 0; +}  int -setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp) +setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)  {  	int rc;  	int baselen;  	unsigned int tilen; -	struct ntlmv2_resp *buf; +	struct ntlmv2_resp *ntlmv2;  	char ntlmv2_hash[16];  	unsigned char *tiblob = NULL; /* target info blob */ -	if (ses->server->secType == RawNTLMSSP) { +	if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {  		if (!ses->domainName) {  			rc = find_domain_name(ses, nls_cp);  			if (rc) { -				cERROR(1, "error %d finding domain name", rc); +				cifs_dbg(VFS, "error %d finding domain name\n", +					 rc);  				goto setup_ntlmv2_rsp_ret;  			}  		}  	} else {  		rc = build_avpair_blob(ses, nls_cp);  		if (rc) { -			cERROR(1, "error %d building av pair blob", rc); +			cifs_dbg(VFS, "error %d building av pair blob\n", rc);  			goto setup_ntlmv2_rsp_ret;  		}  	} @@ -587,51 +663,68 @@ setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp)  	if (!ses->auth_key.response) {  		rc = ENOMEM;  		ses->auth_key.len = 0; -		cERROR(1, "%s: Can't allocate auth blob", __func__);  		goto setup_ntlmv2_rsp_ret;  	}  	ses->auth_key.len += baselen; -	buf = (struct ntlmv2_resp *) +	ntlmv2 = (struct ntlmv2_resp *)  			(ses->auth_key.response + CIFS_SESS_KEY_SIZE); -	buf->blob_signature = cpu_to_le32(0x00000101); -	buf->reserved = 0; -	buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); -	get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); -	buf->reserved2 = 0; +	ntlmv2->blob_signature = cpu_to_le32(0x00000101); +	ntlmv2->reserved = 0; +	/* Must be within 5 minutes of the server */ +	ntlmv2->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); +	get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); +	ntlmv2->reserved2 = 0;  	memcpy(ses->auth_key.response + baselen, tiblob, tilen); +	rc = crypto_hmacmd5_alloc(ses->server); +	if (rc) { +		cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc); +		goto setup_ntlmv2_rsp_ret; +	} +  	/* calculate ntlmv2_hash */  	rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);  	if (rc) { -		cERROR(1, "could not get v2 hash rc %d", rc); +		cifs_dbg(VFS, "could not get v2 hash rc %d\n", rc);  		goto setup_ntlmv2_rsp_ret;  	}  	/* calculate first part of the client response (CR1) */  	rc = CalcNTLMv2_response(ses, ntlmv2_hash);  	if (rc) { -		cERROR(1, "Could not calculate CR1  rc: %d", rc); +		cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc);  		goto setup_ntlmv2_rsp_ret;  	}  	/* now calculate the session key for NTLMv2 */ -	crypto_shash_setkey(ses->server->secmech.hmacmd5, +	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,  		ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", +			 __func__); +		goto setup_ntlmv2_rsp_ret; +	}  	rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);  	if (rc) { -		cERROR(1, "%s: Could not init hmacmd5\n", __func__); +		cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);  		goto setup_ntlmv2_rsp_ret;  	} -	crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, -		ses->auth_key.response + CIFS_SESS_KEY_SIZE, +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, +		ntlmv2->ntlmv2_hash,  		CIFS_HMAC_MD5_HASH_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with response\n", __func__); +		goto setup_ntlmv2_rsp_ret; +	}  	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,  		ses->auth_key.response); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);  setup_ntlmv2_rsp_ret:  	kfree(tiblob); @@ -640,7 +733,7 @@ setup_ntlmv2_rsp_ret:  }  int -calc_seckey(struct cifsSesInfo *ses) +calc_seckey(struct cifs_ses *ses)  {  	int rc;  	struct crypto_blkcipher *tfm_arc4; @@ -651,22 +744,28 @@ calc_seckey(struct cifsSesInfo *ses)  	get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE);  	tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); -	if (!tfm_arc4 || IS_ERR(tfm_arc4)) { -		cERROR(1, "could not allocate crypto API arc4\n"); -		return PTR_ERR(tfm_arc4); +	if (IS_ERR(tfm_arc4)) { +		rc = PTR_ERR(tfm_arc4); +		cifs_dbg(VFS, "could not allocate crypto API arc4\n"); +		return rc;  	}  	desc.tfm = tfm_arc4; -	crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response, +	rc = crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response,  					CIFS_SESS_KEY_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not set response as a key\n", +			 __func__); +		return rc; +	}  	sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE);  	sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);  	rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, CIFS_CPHTXT_SIZE);  	if (rc) { -		cERROR(1, "could not encrypt session key rc: %d\n", rc); +		cifs_dbg(VFS, "could not encrypt session key rc: %d\n", rc);  		crypto_free_blkcipher(tfm_arc4);  		return rc;  	} @@ -678,76 +777,38 @@ calc_seckey(struct cifsSesInfo *ses)  	crypto_free_blkcipher(tfm_arc4); -	return 0; +	return rc;  }  void  cifs_crypto_shash_release(struct TCP_Server_Info *server)  { -	if (server->secmech.md5) -		crypto_free_shash(server->secmech.md5); - -	if (server->secmech.hmacmd5) -		crypto_free_shash(server->secmech.hmacmd5); - -	kfree(server->secmech.sdeschmacmd5); - -	kfree(server->secmech.sdescmd5); -} - -int -cifs_crypto_shash_allocate(struct TCP_Server_Info *server) -{ -	int rc; -	unsigned int size; - -	server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0); -	if (!server->secmech.hmacmd5 || -			IS_ERR(server->secmech.hmacmd5)) { -		cERROR(1, "could not allocate crypto hmacmd5\n"); -		return PTR_ERR(server->secmech.hmacmd5); +	if (server->secmech.cmacaes) { +		crypto_free_shash(server->secmech.cmacaes); +		server->secmech.cmacaes = NULL;  	} -	server->secmech.md5 = crypto_alloc_shash("md5", 0, 0); -	if (!server->secmech.md5 || IS_ERR(server->secmech.md5)) { -		cERROR(1, "could not allocate crypto md5\n"); -		rc = PTR_ERR(server->secmech.md5); -		goto crypto_allocate_md5_fail; +	if (server->secmech.hmacsha256) { +		crypto_free_shash(server->secmech.hmacsha256); +		server->secmech.hmacsha256 = NULL;  	} -	size = sizeof(struct shash_desc) + -			crypto_shash_descsize(server->secmech.hmacmd5); -	server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); -	if (!server->secmech.sdeschmacmd5) { -		cERROR(1, "cifs_crypto_shash_allocate: can't alloc hmacmd5\n"); -		rc = -ENOMEM; -		goto crypto_allocate_hmacmd5_sdesc_fail; +	if (server->secmech.md5) { +		crypto_free_shash(server->secmech.md5); +		server->secmech.md5 = NULL;  	} -	server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5; -	server->secmech.sdeschmacmd5->shash.flags = 0x0; - -	size = sizeof(struct shash_desc) + -			crypto_shash_descsize(server->secmech.md5); -	server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL); -	if (!server->secmech.sdescmd5) { -		cERROR(1, "cifs_crypto_shash_allocate: can't alloc md5\n"); -		rc = -ENOMEM; -		goto crypto_allocate_md5_sdesc_fail; +	if (server->secmech.hmacmd5) { +		crypto_free_shash(server->secmech.hmacmd5); +		server->secmech.hmacmd5 = NULL;  	} -	server->secmech.sdescmd5->shash.tfm = server->secmech.md5; -	server->secmech.sdescmd5->shash.flags = 0x0; -	return 0; - -crypto_allocate_md5_sdesc_fail: +	kfree(server->secmech.sdesccmacaes); +	server->secmech.sdesccmacaes = NULL; +	kfree(server->secmech.sdeschmacsha256); +	server->secmech.sdeschmacsha256 = NULL;  	kfree(server->secmech.sdeschmacmd5); - -crypto_allocate_hmacmd5_sdesc_fail: -	crypto_free_shash(server->secmech.md5); - -crypto_allocate_md5_fail: -	crypto_free_shash(server->secmech.hmacmd5); - -	return rc; +	server->secmech.sdeschmacmd5 = NULL; +	kfree(server->secmech.sdescmd5); +	server->secmech.sdescmd5 = NULL;  } diff --git a/fs/cifs/cifsencrypt.h b/fs/cifs/cifsencrypt.h deleted file mode 100644 index 15d2ec00647..00000000000 --- a/fs/cifs/cifsencrypt.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - *   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); - - - diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 9c3789762ab..88839806742 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -35,6 +35,8 @@  #include <linux/delay.h>  #include <linux/kthread.h>  #include <linux/freezer.h> +#include <linux/namei.h> +#include <linux/random.h>  #include <net/ipv6.h>  #include "cifsfs.h"  #include "cifspdu.h" @@ -47,41 +49,50 @@  #include <linux/key-type.h>  #include "cifs_spnego.h"  #include "fscache.h" -#define CIFS_MAGIC_NUMBER 0xFF534D42	/* the first four bytes of SMB PDUs */ +#ifdef CONFIG_CIFS_SMB2 +#include "smb2pdu.h" +#endif  int cifsFYI = 0; -int cifsERROR = 1;  int traceSMB = 0; -unsigned int oplockEnabled = 1; -unsigned int experimEnabled = 0; +bool enable_oplocks = true;  unsigned int linuxExtEnabled = 1;  unsigned int lookupCacheEnabled = 1; -unsigned int multiuser_mount = 0;  unsigned int global_secflags = CIFSSEC_DEF;  /* unsigned int ntlmv2_support = 0; */  unsigned int sign_CIFS_PDUs = 1;  static const struct super_operations cifs_super_ops;  unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; -module_param(CIFSMaxBufSize, int, 0); +module_param(CIFSMaxBufSize, uint, 0);  MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "  				 "Default: 16384 Range: 8192 to 130048");  unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; -module_param(cifs_min_rcv, int, 0); +module_param(cifs_min_rcv, uint, 0);  MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "  				"1 to 64");  unsigned int cifs_min_small = 30; -module_param(cifs_min_small, int, 0); +module_param(cifs_min_small, uint, 0);  MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "  				 "Range: 2 to 256");  unsigned int cifs_max_pending = CIFS_MAX_REQ; -module_param(cifs_max_pending, int, 0); +module_param(cifs_max_pending, uint, 0444);  MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. " -				   "Default: 50 Range: 2 to 256"); +				   "Default: 32767 Range: 2 to 32767."); +module_param(enable_oplocks, bool, 0644); +MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1");  extern mempool_t *cifs_sm_req_poolp;  extern mempool_t *cifs_req_poolp;  extern mempool_t *cifs_mid_poolp; +struct workqueue_struct	*cifsiod_wq; + +/* + * Bumps refcount for cifs super block. + * Note that it should be only called if a referece to VFS super block is + * already held, e.g. in open-type syscalls context. Otherwise it can race with + * atomic_dec_and_test in deactivate_locked_super. + */  void  cifs_sb_active(struct super_block *sb)  { @@ -101,136 +112,69 @@ cifs_sb_deactive(struct super_block *sb)  }  static int -cifs_read_super(struct super_block *sb, void *data, -		const char *devname, int silent) +cifs_read_super(struct super_block *sb)  {  	struct inode *inode;  	struct cifs_sb_info *cifs_sb; +	struct cifs_tcon *tcon;  	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; +	tcon = cifs_sb_master_tcon(cifs_sb); -	spin_lock_init(&cifs_sb->tlink_tree_lock); -	cifs_sb->tlink_tree = RB_ROOT; +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL) +		sb->s_flags |= MS_POSIXACL; -	rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY); -	if (rc) { -		kfree(cifs_sb); -		return rc; -	} - -#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) { -			bdi_destroy(&cifs_sb->bdi); -			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 +	if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files) +		sb->s_maxbytes = MAX_LFS_FILESIZE; +	else +		sb->s_maxbytes = MAX_NON_LFS; -	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; -	} +	/* BB FIXME fix time_gran to be larger for LANMAN sessions */ +	sb->s_time_gran = 100;  	sb->s_magic = CIFS_MAGIC_NUMBER;  	sb->s_op = &cifs_super_ops;  	sb->s_bdi = &cifs_sb->bdi;  	sb->s_blocksize = CIFS_MAX_MSGSIZE;  	sb->s_blocksize_bits = 14;	/* default 2**14 = CIFS_MAX_MSGSIZE */ -	inode = cifs_root_iget(sb, ROOT_I); +	inode = cifs_root_iget(sb);  	if (IS_ERR(inode)) {  		rc = PTR_ERR(inode); -		inode = NULL;  		goto out_no_root;  	} -	sb->s_root = d_alloc_root(inode); +	if (tcon->nocase) +		sb->s_d_op = &cifs_ci_dentry_ops; +	else +		sb->s_d_op = &cifs_dentry_ops; +	sb->s_root = d_make_root(inode);  	if (!sb->s_root) {  		rc = -ENOMEM;  		goto out_no_root;  	} -#ifdef CONFIG_CIFS_EXPERIMENTAL +#ifdef CONFIG_CIFS_NFSD_EXPORT  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { -		cFYI(1, "export ops supported"); +		cifs_dbg(FYI, "export ops supported\n");  		sb->s_export_op = &cifs_export_ops;  	} -#endif /* EXPERIMENTAL */ +#endif /* CONFIG_CIFS_NFSD_EXPORT */  	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 -		unload_nls(cifs_sb->local_nls); -		bdi_destroy(&cifs_sb->bdi); -		kfree(cifs_sb); -	} +	cifs_dbg(VFS, "%s: get root inode failed\n", __func__);  	return rc;  } -static void -cifs_put_super(struct super_block *sb) +static void cifs_kill_sb(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); -	bdi_destroy(&cifs_sb->bdi); -	kfree(cifs_sb); +	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); +	kill_anon_super(sb); +	cifs_umount(cifs_sb);  }  static int @@ -238,13 +182,12 @@ 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_master_tcon(cifs_sb); -	int rc = -EOPNOTSUPP; -	int xid; - -	xid = GetXid(); +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); +	struct TCP_Server_Info *server = tcon->ses->server; +	unsigned int xid; +	int rc = 0; -	buf->f_type = CIFS_MAGIC_NUMBER; +	xid = get_xid();  	/*  	 * PATH_MAX may be too long - it would presumably be total path, @@ -257,29 +200,10 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)  	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); +	if (server->ops->queryfs) +		rc = server->ops->queryfs(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); +	free_xid(xid);  	return 0;  } @@ -298,7 +222,7 @@ static int cifs_permission(struct inode *inode, int mask)  		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); +		return generic_permission(inode, mask);  }  static struct kmem_cache *cifs_inode_cachep; @@ -318,76 +242,154 @@ cifs_alloc_inode(struct super_block *sb)  		return NULL;  	cifs_inode->cifsAttrs = 0x20;	/* default */  	cifs_inode->time = 0; -	/* Until the file is open and we have gotten oplock -	info back from the server, can not assume caching of -	file data or metadata */ +	/* +	 * Until the file is open and we have gotten oplock info back from the +	 * server, can not assume caching of file data or metadata. +	 */  	cifs_set_oplock_level(cifs_inode, 0); -	cifs_inode->delete_pending = false; -	cifs_inode->invalid_mapping = false; +	cifs_inode->flags = 0; +	spin_lock_init(&cifs_inode->writers_lock); +	cifs_inode->writers = 0;  	cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */  	cifs_inode->server_eof = 0; - -	/* 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;*/ +	cifs_inode->uniqueid = 0; +	cifs_inode->createtime = 0; +	cifs_inode->epoch = 0; +#ifdef CONFIG_CIFS_SMB2 +	get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); +#endif +	/* +	 * Can not set i_flags here - they get immediately overwritten to zero +	 * by the VFS. +	 */ +	/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */  	INIT_LIST_HEAD(&cifs_inode->openFileList); +	INIT_LIST_HEAD(&cifs_inode->llist);  	return &cifs_inode->vfs_inode;  } +static void cifs_i_callback(struct rcu_head *head) +{ +	struct inode *inode = container_of(head, struct inode, i_rcu); +	kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); +} +  static void  cifs_destroy_inode(struct inode *inode)  { -	kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); +	call_rcu(&inode->i_rcu, cifs_i_callback);  }  static void  cifs_evict_inode(struct inode *inode)  { -	truncate_inode_pages(&inode->i_data, 0); -	end_writeback(inode); +	truncate_inode_pages_final(&inode->i_data); +	clear_inode(inode);  	cifs_fscache_release_inode_cookie(inode);  }  static void  cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server)  { -	seq_printf(s, ",addr="); +	struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; +	struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; + +	seq_puts(s, ",addr="); -	switch (server->addr.sockAddr.sin_family) { +	switch (server->dstaddr.ss_family) {  	case AF_INET: -		seq_printf(s, "%pI4", &server->addr.sockAddr.sin_addr.s_addr); +		seq_printf(s, "%pI4", &sa->sin_addr.s_addr);  		break;  	case AF_INET6: -		seq_printf(s, "%pI6", -			   &server->addr.sockAddr6.sin6_addr.s6_addr); -		if (server->addr.sockAddr6.sin6_scope_id) -			seq_printf(s, "%%%u", -				   server->addr.sockAddr6.sin6_scope_id); +		seq_printf(s, "%pI6", &sa6->sin6_addr.s6_addr); +		if (sa6->sin6_scope_id) +			seq_printf(s, "%%%u", sa6->sin6_scope_id);  		break;  	default: -		seq_printf(s, "(unknown)"); +		seq_puts(s, "(unknown)");  	}  } +static void +cifs_show_security(struct seq_file *s, struct cifs_ses *ses) +{ +	if (ses->sectype == Unspecified) +		return; + +	seq_puts(s, ",sec="); + +	switch (ses->sectype) { +	case LANMAN: +		seq_puts(s, "lanman"); +		break; +	case NTLMv2: +		seq_puts(s, "ntlmv2"); +		break; +	case NTLM: +		seq_puts(s, "ntlm"); +		break; +	case Kerberos: +		seq_puts(s, "krb5"); +		break; +	case RawNTLMSSP: +		seq_puts(s, "ntlmssp"); +		break; +	default: +		/* shouldn't ever happen */ +		seq_puts(s, "unknown"); +		break; +	} + +	if (ses->sign) +		seq_puts(s, "i"); +} + +static void +cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) +{ +	seq_puts(s, ",cache="); + +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) +		seq_puts(s, "strict"); +	else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) +		seq_puts(s, "none"); +	else +		seq_puts(s, "loose"); +} + +static void +cifs_show_nls(struct seq_file *s, struct nls_table *cur) +{ +	struct nls_table *def; + +	/* Display iocharset= option if it's not default charset */ +	def = load_nls_default(); +	if (def != cur) +		seq_printf(s, ",iocharset=%s", cur->charset); +	unload_nls(def); +} +  /*   * cifs_show_options() is for displaying mount options in /proc/mounts.   * Not all settable options are displayed but most of the important   * ones are.   */  static int -cifs_show_options(struct seq_file *s, struct vfsmount *m) +cifs_show_options(struct seq_file *s, struct dentry *root)  { -	struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); +	struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);  	struct sockaddr *srcaddr;  	srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; -	seq_printf(s, ",unc=%s", tcon->treeName); +	seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string); +	cifs_show_security(s, tcon->ses); +	cifs_show_cache_flavor(s, cifs_sb);  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) -		seq_printf(s, ",multiuser"); -	else if (tcon->ses->userName) -		seq_printf(s, ",username=%s", tcon->ses->userName); +		seq_puts(s, ",multiuser"); +	else if (tcon->ses->user_name) +		seq_printf(s, ",username=%s", tcon->ses->user_name);  	if (tcon->ses->domainName)  		seq_printf(s, ",domain=%s", tcon->ses->domainName); @@ -408,59 +410,84 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)  				   (int)(srcaddr->sa_family));  	} -	seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); +	seq_printf(s, ",uid=%u", +		   from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid));  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) -		seq_printf(s, ",forceuid"); +		seq_puts(s, ",forceuid");  	else -		seq_printf(s, ",noforceuid"); +		seq_puts(s, ",noforceuid"); -	seq_printf(s, ",gid=%d", cifs_sb->mnt_gid); +	seq_printf(s, ",gid=%u", +		   from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid));  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) -		seq_printf(s, ",forcegid"); +		seq_puts(s, ",forcegid");  	else -		seq_printf(s, ",noforcegid"); +		seq_puts(s, ",noforcegid");  	cifs_show_address(s, tcon->ses->server);  	if (!tcon->unix_ext) -		seq_printf(s, ",file_mode=0%o,dir_mode=0%o", +		seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho",  					   cifs_sb->mnt_file_mode,  					   cifs_sb->mnt_dir_mode); + +	cifs_show_nls(s, cifs_sb->local_nls); +  	if (tcon->seal) -		seq_printf(s, ",seal"); +		seq_puts(s, ",seal");  	if (tcon->nocase) -		seq_printf(s, ",nocase"); +		seq_puts(s, ",nocase");  	if (tcon->retry) -		seq_printf(s, ",hard"); -	if (cifs_sb->prepath) -		seq_printf(s, ",prepath=%s", cifs_sb->prepath); +		seq_puts(s, ",hard"); +	if (tcon->unix_ext) +		seq_puts(s, ",unix"); +	else +		seq_puts(s, ",nounix");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) -		seq_printf(s, ",posixpaths"); +		seq_puts(s, ",posixpaths");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) -		seq_printf(s, ",setuids"); +		seq_puts(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"); +		seq_puts(s, ",serverino"); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) +		seq_puts(s, ",rwpidforward"); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) +		seq_puts(s, ",forcemand");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) -		seq_printf(s, ",nouser_xattr"); +		seq_puts(s, ",nouser_xattr");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) -		seq_printf(s, ",mapchars"); +		seq_puts(s, ",mapchars");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) -		seq_printf(s, ",sfu"); +		seq_puts(s, ",sfu");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) -		seq_printf(s, ",nobrl"); +		seq_puts(s, ",nobrl");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) -		seq_printf(s, ",cifsacl"); +		seq_puts(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_puts(s, ",dynperm"); +	if (root->d_sb->s_flags & MS_POSIXACL) +		seq_puts(s, ",acl");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) -		seq_printf(s, ",mfsymlinks"); - -	seq_printf(s, ",rsize=%d", cifs_sb->rsize); -	seq_printf(s, ",wsize=%d", cifs_sb->wsize); +		seq_puts(s, ",mfsymlinks"); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) +		seq_puts(s, ",fsc"); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC) +		seq_puts(s, ",nostrictsync"); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) +		seq_puts(s, ",noperm"); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) +		seq_printf(s, ",backupuid=%u", +			   from_kuid_munged(&init_user_ns, +					    cifs_sb->mnt_backupuid)); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) +		seq_printf(s, ",backupgid=%u", +			   from_kgid_munged(&init_user_ns, +					    cifs_sb->mnt_backupgid)); + +	seq_printf(s, ",rsize=%u", cifs_sb->rsize); +	seq_printf(s, ",wsize=%u", cifs_sb->wsize); +	/* convert actimeo and display it in seconds */ +	seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);  	return 0;  } @@ -468,7 +495,7 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)  static void cifs_umount_begin(struct super_block *sb)  {  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	if (cifs_sb == NULL)  		return; @@ -489,7 +516,7 @@ static void cifs_umount_begin(struct super_block *sb)  	/* 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"); +		cifs_dbg(FYI, "wake up tasks now - umount begin not complete\n");  		wake_up_all(&tcon->ses->server->request_q);  		wake_up_all(&tcon->ses->server->response_q);  		msleep(1); /* yield */ @@ -502,7 +529,7 @@ static void cifs_umount_begin(struct super_block *sb)  }  #ifdef CONFIG_CIFS_STATS2 -static int cifs_show_stats(struct seq_file *s, struct vfsmount *mnt) +static int cifs_show_stats(struct seq_file *s, struct dentry *root)  {  	/* BB FIXME */  	return 0; @@ -511,6 +538,7 @@ static int cifs_show_stats(struct seq_file *s, struct vfsmount *mnt)  static int cifs_remount(struct super_block *sb, int *flags, char *data)  { +	sync_filesystem(sb);  	*flags |= MS_NODIRATIME;  	return 0;  } @@ -525,7 +553,6 @@ static int cifs_drop_inode(struct inode *inode)  }  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, @@ -543,86 +570,262 @@ static const struct super_operations cifs_super_ops = {  #endif  }; +/* + * Get root dentry from superblock according to prefix path mount option. + * Return dentry with refcount + 1 on success and NULL otherwise. + */ +static struct dentry * +cifs_get_root(struct smb_vol *vol, struct super_block *sb) +{ +	struct dentry *dentry; +	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); +	char *full_path = NULL; +	char *s, *p; +	char sep; + +	full_path = cifs_build_path_to_root(vol, cifs_sb, +					    cifs_sb_master_tcon(cifs_sb)); +	if (full_path == NULL) +		return ERR_PTR(-ENOMEM); + +	cifs_dbg(FYI, "Get root dentry for %s\n", full_path); + +	sep = CIFS_DIR_SEP(cifs_sb); +	dentry = dget(sb->s_root); +	p = s = full_path; + +	do { +		struct inode *dir = dentry->d_inode; +		struct dentry *child; + +		if (!dir) { +			dput(dentry); +			dentry = ERR_PTR(-ENOENT); +			break; +		} +		if (!S_ISDIR(dir->i_mode)) { +			dput(dentry); +			dentry = ERR_PTR(-ENOTDIR); +			break; +		} + +		/* skip separators */ +		while (*s == sep) +			s++; +		if (!*s) +			break; +		p = s++; +		/* next separator */ +		while (*s && *s != sep) +			s++; + +		mutex_lock(&dir->i_mutex); +		child = lookup_one_len(p, dentry, s - p); +		mutex_unlock(&dir->i_mutex); +		dput(dentry); +		dentry = child; +	} while (!IS_ERR(dentry)); +	kfree(full_path); +	return dentry; +} + +static int cifs_set_super(struct super_block *sb, void *data) +{ +	struct cifs_mnt_data *mnt_data = data; +	sb->s_fs_info = mnt_data->cifs_sb; +	return set_anon_super(sb, NULL); +} +  static struct dentry *  cifs_do_mount(struct file_system_type *fs_type, -	    int flags, const char *dev_name, void *data) +	      int flags, const char *dev_name, void *data)  {  	int rc;  	struct super_block *sb; +	struct cifs_sb_info *cifs_sb; +	struct smb_vol *volume_info; +	struct cifs_mnt_data mnt_data; +	struct dentry *root; -	sb = sget(fs_type, NULL, set_anon_super, NULL); +	cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags); -	cFYI(1, "Devname: %s flags: %d ", dev_name, flags); +	volume_info = cifs_get_volume_info((char *)data, dev_name); +	if (IS_ERR(volume_info)) +		return ERR_CAST(volume_info); + +	cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); +	if (cifs_sb == NULL) { +		root = ERR_PTR(-ENOMEM); +		goto out_nls; +	} -	if (IS_ERR(sb)) -		return ERR_CAST(sb); +	cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL); +	if (cifs_sb->mountdata == NULL) { +		root = ERR_PTR(-ENOMEM); +		goto out_cifs_sb; +	} -	sb->s_flags = flags; +	cifs_setup_cifs_sb(volume_info, cifs_sb); -	rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0); +	rc = cifs_mount(cifs_sb, volume_info);  	if (rc) { -		deactivate_locked_super(sb); -		return ERR_PTR(rc); +		if (!(flags & MS_SILENT)) +			cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n", +				 rc); +		root = ERR_PTR(rc); +		goto out_mountdata;  	} -	sb->s_flags |= MS_ACTIVE; -	return dget(sb->s_root); + +	mnt_data.vol = volume_info; +	mnt_data.cifs_sb = cifs_sb; +	mnt_data.flags = flags; + +	/* BB should we make this contingent on mount parm? */ +	flags |= MS_NODIRATIME | MS_NOATIME; + +	sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data); +	if (IS_ERR(sb)) { +		root = ERR_CAST(sb); +		cifs_umount(cifs_sb); +		goto out; +	} + +	if (sb->s_root) { +		cifs_dbg(FYI, "Use existing superblock\n"); +		cifs_umount(cifs_sb); +	} else { +		rc = cifs_read_super(sb); +		if (rc) { +			root = ERR_PTR(rc); +			goto out_super; +		} + +		sb->s_flags |= MS_ACTIVE; +	} + +	root = cifs_get_root(volume_info, sb); +	if (IS_ERR(root)) +		goto out_super; + +	cifs_dbg(FYI, "dentry root is: %p\n", root); +	goto out; + +out_super: +	deactivate_locked_super(sb); +out: +	cifs_cleanup_volume_info(volume_info); +	return root; + +out_mountdata: +	kfree(cifs_sb->mountdata); +out_cifs_sb: +	kfree(cifs_sb); +out_nls: +	unload_nls(volume_info->local_nls); +	goto out;  } -static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, -				   unsigned long nr_segs, loff_t pos) +static ssize_t +cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)  { -	struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; -	ssize_t written; +	ssize_t rc; +	struct inode *inode = file_inode(iocb->ki_filp); -	written = generic_file_aio_write(iocb, iov, nr_segs, pos); -	if (!CIFS_I(inode)->clientCanCacheAll) -		filemap_fdatawrite(inode->i_mapping); -	return written; +	rc = cifs_revalidate_mapping(inode); +	if (rc) +		return rc; + +	return generic_file_read_iter(iocb, iter);  } -static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) +static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)  { -	/* origin == SEEK_END => we must revalidate the cached file length */ -	if (origin == SEEK_END) { -		int retval; +	struct inode *inode = file_inode(iocb->ki_filp); +	struct cifsInodeInfo *cinode = CIFS_I(inode); +	ssize_t written; +	int rc; + +	written = cifs_get_writer(cinode); +	if (written) +		return written; + +	written = generic_file_write_iter(iocb, from); + +	if (CIFS_CACHE_WRITE(CIFS_I(inode))) +		goto out; + +	rc = filemap_fdatawrite(inode->i_mapping); +	if (rc) +		cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n", +			 rc, inode); -		/* 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; +out: +	cifs_put_writer(cinode); +	return written; +} -		retval = cifs_revalidate_file(file); -		if (retval < 0) -			return (loff_t)retval; +static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) +{ +	/* +	 * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate +	 * the cached file length +	 */ +	if (whence != SEEK_SET && whence != SEEK_CUR) { +		int rc; +		struct inode *inode = file_inode(file); + +		/* +		 * We need to be sure that all dirty pages are written and the +		 * server has the newest file length. +		 */ +		if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && +		    inode->i_mapping->nrpages != 0) { +			rc = filemap_fdatawait(inode->i_mapping); +			if (rc) { +				mapping_set_error(inode->i_mapping, rc); +				return rc; +			} +		} +		/* +		 * Some applications poll for the file length in this strange +		 * way so we must seek to end on non-oplocked files by +		 * setting the revalidate time to zero. +		 */ +		CIFS_I(inode)->time = 0; + +		rc = cifs_revalidate_file_attr(file); +		if (rc < 0) +			return (loff_t)rc;  	} -	return generic_file_llseek_unlocked(file, offset, origin); +	return generic_file_llseek(file, offset, whence);  }  static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)  { -	/* note that this is called by vfs setlease with lock_flocks held -	   to protect *lease from going away */ -	struct inode *inode = file->f_path.dentry->d_inode; +	/* +	 * Note that this is called by vfs setlease with i_lock held to +	 * protect *lease from going away. +	 */ +	struct inode *inode = file_inode(file);  	struct cifsFileInfo *cfile = file->private_data;  	if (!(S_ISREG(inode->i_mode)))  		return -EINVAL;  	/* check if file is oplocked */ -	if (((arg == F_RDLCK) && -		(CIFS_I(inode)->clientCanCacheRead)) || -	    ((arg == F_WRLCK) && -		(CIFS_I(inode)->clientCanCacheAll))) +	if (((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || +	    ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode))))  		return generic_setlease(file, arg, lease);  	else if (tlink_tcon(cfile->tlink)->local_lease && -		 !CIFS_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 */ +		 !CIFS_CACHE_READ(CIFS_I(inode))) +		/* +		 * If the server claims to support oplock on this file, then we +		 * still need to check oplock even if the local_lease mount +		 * option is set, but there are servers which do not support +		 * oplock for which this mount option may be useful if the user +		 * knows that the file won't be changed on the server by anyone +		 * else. +		 */  		return generic_setlease(file, arg, lease);  	else  		return -EAGAIN; @@ -632,11 +835,13 @@ struct file_system_type cifs_fs_type = {  	.owner = THIS_MODULE,  	.name = "cifs",  	.mount = cifs_do_mount, -	.kill_sb = kill_anon_super, +	.kill_sb = cifs_kill_sb,  	/*  .fs_flags */  }; +MODULE_ALIAS_FS("cifs");  const struct inode_operations cifs_dir_inode_ops = {  	.create = cifs_create, +	.atomic_open = cifs_atomic_open,  	.lookup = cifs_lookup,  	.getattr = cifs_getattr,  	.unlink = cifs_unlink, @@ -661,7 +866,6 @@ 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, @@ -674,7 +878,7 @@ const struct inode_operations cifs_file_inode_ops = {  const struct inode_operations cifs_symlink_inode_ops = {  	.readlink = generic_readlink,  	.follow_link = cifs_follow_link, -	.put_link = cifs_put_link, +	.put_link = kfree_put_link,  	.permission = cifs_permission,  	/* BB add the following two eventually */  	/* revalidate: cifs_revalidate, @@ -688,10 +892,10 @@ const struct inode_operations cifs_symlink_inode_ops = {  };  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, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_loose_read_iter, +	.write_iter = cifs_file_write_iter,  	.open = cifs_open,  	.release = cifs_close,  	.lock = cifs_lock, @@ -706,11 +910,31 @@ const struct file_operations cifs_file_ops = {  	.setlease = cifs_setlease,  }; +const struct file_operations cifs_file_strict_ops = { +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_strict_readv, +	.write_iter = cifs_strict_writev, +	.open = cifs_open, +	.release = cifs_close, +	.lock = cifs_lock, +	.fsync = cifs_strict_fsync, +	.flush = cifs_flush, +	.mmap = cifs_file_strict_mmap, +	.splice_read = generic_file_splice_read, +	.llseek = cifs_llseek, +#ifdef CONFIG_CIFS_POSIX +	.unlocked_ioctl	= cifs_ioctl, +#endif /* CONFIG_CIFS_POSIX */ +	.setlease = cifs_setlease, +}; +  const struct file_operations cifs_file_direct_ops = { -	/* no aio, no readv - -	   BB reevaluate whether they can be done with directio, no cache */ -	.read = cifs_user_read, -	.write = cifs_user_write, +	/* BB reevaluate whether they can be done with directio, no cache */ +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_user_readv, +	.write_iter = cifs_user_writev,  	.open = cifs_open,  	.release = cifs_close,  	.lock = cifs_lock, @@ -724,11 +948,12 @@ const struct file_operations cifs_file_direct_ops = {  	.llseek = cifs_llseek,  	.setlease = cifs_setlease,  }; +  const struct file_operations cifs_file_nobrl_ops = { -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = generic_file_aio_read, -	.aio_write = cifs_file_aio_write, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_loose_read_iter, +	.write_iter = cifs_file_write_iter,  	.open = cifs_open,  	.release = cifs_close,  	.fsync = cifs_fsync, @@ -742,11 +967,30 @@ const struct file_operations cifs_file_nobrl_ops = {  	.setlease = cifs_setlease,  }; +const struct file_operations cifs_file_strict_nobrl_ops = { +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_strict_readv, +	.write_iter = cifs_strict_writev, +	.open = cifs_open, +	.release = cifs_close, +	.fsync = cifs_strict_fsync, +	.flush = cifs_flush, +	.mmap = cifs_file_strict_mmap, +	.splice_read = generic_file_splice_read, +	.llseek = cifs_llseek, +#ifdef CONFIG_CIFS_POSIX +	.unlocked_ioctl	= cifs_ioctl, +#endif /* CONFIG_CIFS_POSIX */ +	.setlease = cifs_setlease, +}; +  const struct file_operations cifs_file_direct_nobrl_ops = { -	/* 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, +	/* BB reevaluate whether they can be done with directio, no cache */ +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_user_readv, +	.write_iter = cifs_user_writev,  	.open = cifs_open,  	.release = cifs_close,  	.fsync = cifs_fsync, @@ -761,7 +1005,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {  };  const struct file_operations cifs_dir_ops = { -	.readdir = cifs_readdir, +	.iterate = cifs_readdir,  	.release = cifs_closedir,  	.read    = generic_read_dir,  	.unlocked_ioctl  = cifs_ioctl, @@ -774,10 +1018,10 @@ cifs_init_once(void *inode)  	struct cifsInodeInfo *cifsi = inode;  	inode_init_once(&cifsi->vfs_inode); -	INIT_LIST_HEAD(&cifsi->lockList); +	init_rwsem(&cifsi->lock_sem);  } -static int +static int __init  cifs_init_inodecache(void)  {  	cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", @@ -794,12 +1038,25 @@ cifs_init_inodecache(void)  static void  cifs_destroy_inodecache(void)  { +	/* +	 * Make sure all delayed rcu free inodes are flushed before we +	 * destroy cache. +	 */ +	rcu_barrier();  	kmem_cache_destroy(cifs_inode_cachep);  }  static int  cifs_init_request_bufs(void)  { +	size_t max_hdr_size = MAX_CIFS_HDR_SIZE; +#ifdef CONFIG_CIFS_SMB2 +	/* +	 * SMB2 maximum header size is bigger than CIFS one - no problems to +	 * allocate some more bytes for CIFS. +	 */ +	max_hdr_size = MAX_SMB2_HDR_SIZE; +#endif  	if (CIFSMaxBufSize < 8192) {  	/* Buffer size can not be smaller than 2 * PATH_MAX since maximum  	Unicode path name has to fit in any SMB/CIFS path based frames */ @@ -809,10 +1066,12 @@ cifs_init_request_bufs(void)  	} else {  		CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/  	} -/*	cERROR(1, "CIFSMaxBufSize %d 0x%x",CIFSMaxBufSize,CIFSMaxBufSize); */ +/* +	cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n", +		 CIFSMaxBufSize, CIFSMaxBufSize); +*/  	cifs_req_cachep = kmem_cache_create("cifs_request", -					    CIFSMaxBufSize + -					    MAX_CIFS_HDR_SIZE, 0, +					    CIFSMaxBufSize + max_hdr_size, 0,  					    SLAB_HWCACHE_ALIGN, NULL);  	if (cifs_req_cachep == NULL)  		return -ENOMEM; @@ -821,7 +1080,7 @@ cifs_init_request_bufs(void)  		cifs_min_rcv = 1;  	else if (cifs_min_rcv > 64) {  		cifs_min_rcv = 64; -		cERROR(1, "cifs_min_rcv set to maximum (64)"); +		cifs_dbg(VFS, "cifs_min_rcv set to maximum (64)\n");  	}  	cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, @@ -852,7 +1111,7 @@ cifs_init_request_bufs(void)  		cifs_min_small = 2;  	else if (cifs_min_small > 256) {  		cifs_min_small = 256; -		cFYI(1, "cifs_min_small set to maximum (256)"); +		cifs_dbg(FYI, "cifs_min_small set to maximum (256)\n");  	}  	cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, @@ -909,10 +1168,10 @@ init_cifs(void)  	int rc = 0;  	cifs_proc_init();  	INIT_LIST_HEAD(&cifs_tcp_ses_list); -#ifdef CONFIG_CIFS_EXPERIMENTAL +#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */  	INIT_LIST_HEAD(&GlobalDnotifyReqList);  	INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); -#endif +#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */  /*   *  Initialize Global counters   */ @@ -933,22 +1192,28 @@ init_cifs(void)  	GlobalCurrentXid = 0;  	GlobalTotalActiveXid = 0;  	GlobalMaxActiveXid = 0; -	memset(Local_System_Name, 0, 15);  	spin_lock_init(&cifs_tcp_ses_lock);  	spin_lock_init(&cifs_file_list_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"); +		cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); +	} else if (cifs_max_pending > CIFS_MAX_REQ) { +		cifs_max_pending = CIFS_MAX_REQ; +		cifs_dbg(FYI, "cifs_max_pending set to max of %u\n", +			 CIFS_MAX_REQ); +	} + +	cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); +	if (!cifsiod_wq) { +		rc = -ENOMEM; +		goto out_clean_proc;  	}  	rc = cifs_fscache_register();  	if (rc) -		goto out_clean_proc; +		goto out_destroy_wq;  	rc = cifs_init_inodecache();  	if (rc) @@ -962,22 +1227,33 @@ init_cifs(void)  	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 +		goto out_destroy_request_bufs; +#endif /* CONFIG_CIFS_UPCALL */ + +#ifdef CONFIG_CIFS_ACL +	rc = init_cifs_idmap(); +	if (rc) +		goto out_register_key_type; +#endif /* CONFIG_CIFS_ACL */ + +	rc = register_filesystem(&cifs_fs_type); +	if (rc) +		goto out_init_cifs_idmap;  	return 0; -#ifdef CONFIG_CIFS_UPCALL -out_unregister_filesystem: -	unregister_filesystem(&cifs_fs_type); +out_init_cifs_idmap: +#ifdef CONFIG_CIFS_ACL +	exit_cifs_idmap(); +out_register_key_type:  #endif +#ifdef CONFIG_CIFS_UPCALL +	unregister_key_type(&cifs_spnego_key_type);  out_destroy_request_bufs: +#endif  	cifs_destroy_request_bufs();  out_destroy_mids:  	cifs_destroy_mids(); @@ -985,6 +1261,8 @@ out_destroy_inodecache:  	cifs_destroy_inodecache();  out_unreg_fscache:  	cifs_fscache_unregister(); +out_destroy_wq: +	destroy_workqueue(cifsiod_wq);  out_clean_proc:  	cifs_proc_clean();  	return rc; @@ -993,19 +1271,21 @@ out_clean_proc:  static void __exit  exit_cifs(void)  { -	cFYI(DBG2, "exit_cifs"); -	cifs_proc_clean(); -	cifs_fscache_unregister(); -#ifdef CONFIG_CIFS_DFS_UPCALL +	cifs_dbg(NOISY, "exit_cifs\n"); +	unregister_filesystem(&cifs_fs_type);  	cifs_dfs_release_automount_timer(); +#ifdef CONFIG_CIFS_ACL +	exit_cifs_idmap();  #endif  #ifdef CONFIG_CIFS_UPCALL  	unregister_key_type(&cifs_spnego_key_type);  #endif -	unregister_filesystem(&cifs_fs_type); -	cifs_destroy_inodecache(); -	cifs_destroy_mids();  	cifs_destroy_request_bufs(); +	cifs_destroy_mids(); +	cifs_destroy_inodecache(); +	cifs_fscache_unregister(); +	destroy_workqueue(cifsiod_wq); +	cifs_proc_clean();  }  MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 897b2b2b28b..70f178a7c75 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -22,20 +22,28 @@  #ifndef _CIFSFS_H  #define _CIFSFS_H +#include <linux/hash.h> +  #define ROOT_I 2  /*   * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down - * so that it will fit. + * so that it will fit. We use hash_64 to convert the value to 31 bits, and + * then add 1, to ensure that we don't end up with a 0 as the value.   */ +#if BITS_PER_LONG == 64 +static inline ino_t +cifs_uniqueid_to_ino_t(u64 fileid) +{ +	return (ino_t)fileid; +} +#else  static inline ino_t  cifs_uniqueid_to_ino_t(u64 fileid)  { -	ino_t ino = (ino_t) fileid; -	if (sizeof(ino_t) < sizeof(u64)) -		ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8; -	return ino; +	return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1;  } +#endif  extern struct file_system_type cifs_fs_type;  extern const struct address_space_operations cifs_addr_ops; @@ -47,20 +55,28 @@ extern void cifs_sb_deactive(struct super_block *sb);  /* Functions related to inodes */  extern const struct inode_operations cifs_dir_inode_ops; -extern struct inode *cifs_root_iget(struct super_block *, unsigned long); -extern int cifs_create(struct inode *, struct dentry *, int, -		       struct nameidata *); +extern struct inode *cifs_root_iget(struct super_block *); +extern int cifs_create(struct inode *, struct dentry *, umode_t, +		       bool excl); +extern int cifs_atomic_open(struct inode *, struct dentry *, +			    struct file *, unsigned, umode_t, +			    int *);  extern struct dentry *cifs_lookup(struct inode *, struct dentry *, -				  struct nameidata *); +				  unsigned int);  extern int cifs_unlink(struct inode *dir, struct dentry *dentry);  extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); -extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t); -extern int cifs_mkdir(struct inode *, struct dentry *, int); +extern int cifs_mknod(struct inode *, struct dentry *, umode_t, dev_t); +extern int cifs_mkdir(struct inode *, struct dentry *, umode_t);  extern int cifs_rmdir(struct inode *, struct dentry *);  extern int cifs_rename(struct inode *, struct dentry *, struct inode *,  		       struct dentry *); +extern int cifs_revalidate_file_attr(struct file *filp); +extern int cifs_revalidate_dentry_attr(struct dentry *);  extern int cifs_revalidate_file(struct file *filp);  extern int cifs_revalidate_dentry(struct dentry *); +extern int cifs_invalidate_mapping(struct inode *inode); +extern int cifs_revalidate_mapping(struct inode *inode); +extern int cifs_zap_mapping(struct inode *inode);  extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);  extern int cifs_setattr(struct dentry *, struct iattr *); @@ -72,31 +88,39 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations;  /* Functions related to files and directories */  extern const struct file_operations cifs_file_ops;  extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ -extern const struct file_operations cifs_file_nobrl_ops; -extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */ +extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ +extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ +extern const struct file_operations cifs_file_direct_nobrl_ops; +extern const struct file_operations cifs_file_strict_nobrl_ops;  extern int cifs_open(struct inode *inode, struct file *file);  extern int cifs_close(struct inode *inode, struct file *file);  extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_user_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 ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); +extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);  extern int cifs_lock(struct file *, int, struct file_lock *); -extern int cifs_fsync(struct file *, int); +extern int cifs_fsync(struct file *, loff_t, loff_t, int); +extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);  extern int cifs_flush(struct file *, fl_owner_t id);  extern int cifs_file_mmap(struct file * , struct vm_area_struct *); +extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);  extern const struct file_operations cifs_dir_ops;  extern int cifs_dir_open(struct inode *inode, struct file *file); -extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); +extern int cifs_readdir(struct file *file, struct dir_context *ctx);  /* Functions related to dir entries */  extern const struct dentry_operations cifs_dentry_ops;  extern const struct dentry_operations cifs_ci_dentry_ops; +#ifdef CONFIG_CIFS_DFS_UPCALL +extern struct vfsmount *cifs_dfs_d_automount(struct path *path); +#else +#define cifs_dfs_d_automount NULL +#endif +  /* Functions related to symlinks */  extern 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, @@ -108,9 +132,9 @@ 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 +#ifdef CONFIG_CIFS_NFSD_EXPORT  extern const struct export_operations cifs_export_ops; -#endif /* EXPERIMENTAL */ +#endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION   "1.68" +#define CIFS_VERSION   "2.03"  #endif				/* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b577bf0a1bb..de6aed8c78e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -22,11 +22,18 @@  #include <linux/in.h>  #include <linux/in6.h>  #include <linux/slab.h> +#include <linux/mempool.h>  #include <linux/workqueue.h>  #include "cifs_fs_sb.h"  #include "cifsacl.h"  #include <crypto/internal/hash.h>  #include <linux/scatterlist.h> +#include <uapi/linux/cifs/cifs_mount.h> +#ifdef CONFIG_CIFS_SMB2 +#include "smb2pdu.h" +#endif + +#define CIFS_MAGIC_NUMBER 0xFF534D42      /* the first four bytes of SMB PDUs */  /*   * The sizes of various internal tables and strings @@ -35,25 +42,26 @@  #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 512  /* max for windows seems to be 256 wide chars */ +#define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1)  #define CIFS_MIN_RCV_POOL 4 +#define MAX_REOPEN_ATT	5 /* these many maximum attempts to reopen a file */ +/* + * default attribute cache timeout (jiffies) + */ +#define CIFS_DEF_ACTIMEO (1 * HZ) + +/* + * max attribute cache timeout (jiffies) - 2^30 + */ +#define CIFS_MAX_ACTIMEO (1 << 30) +  /*   * MAX_REQ is the maximum number of requests that WE will send - * on one socket concurrently. 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. + * on one socket concurrently.   */ -#define CIFS_MAX_REQ 50 +#define CIFS_MAX_REQ 32767  #define RFC1001_NAME_LEN 15  #define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1) @@ -67,6 +75,9 @@  /*           (max path length + 1 for null) * 2 for unicode    */  #define MAX_NAME 514 +/* SMB echo "timeout" -- FIXME: tunable? */ +#define SMB_ECHO_INTERVAL (60 * HZ) +  #include "cifspdu.h"  #ifndef XATTR_DOS_ATTRIB @@ -82,24 +93,19 @@ enum statusEnum {  	CifsNew = 0,  	CifsGood,  	CifsExiting, -	CifsNeedReconnect +	CifsNeedReconnect, +	CifsNeedNegotiate  };  enum securityEnum { -	LANMAN = 0,			/* Legacy LANMAN auth */ +	Unspecified = 0,	/* not specified */ +	LANMAN,			/* Legacy LANMAN auth */  	NTLM,			/* Legacy NTLM012 auth with NTLM hash */  	NTLMv2,			/* Legacy NTLM auth with NTLMv2 hash */  	RawNTLMSSP,		/* NTLMSSP without SPNEGO, NTLMv2 hash */ -/*	NTLMSSP, */ /* can use rawNTLMSSP instead of NTLMSSP via SPNEGO */  	Kerberos,		/* Kerberos via SPNEGO */  }; -enum protocolEnum { -	TCP = 0, -	SCTP -	/* Netbios frames protocol not supported at this time */ -}; -  struct session_key {  	unsigned int len;  	char *response; @@ -115,12 +121,17 @@ struct sdesc {  struct cifs_secmech {  	struct crypto_shash *hmacmd5; /* hmac-md5 hash function */  	struct crypto_shash *md5; /* md5 hash function */ +	struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */ +	struct crypto_shash *cmacaes; /* block-cipher based MAC function */  	struct sdesc *sdeschmacmd5;  /* ctxt to generate ntlmv2 hash, CR1 */  	struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ +	struct sdesc *sdeschmacsha256;  /* ctxt to generate smb2 signature */ +	struct sdesc *sdesccmacaes;  /* ctxt to generate smb3 signature */  };  /* per smb session structure/fields */  struct ntlmssp_auth { +	bool sesskey_per_smbsess; /* whether session key is per smb session */  	__u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */  	__u32 server_flags; /* sent by server in type 2 ntlmssp exchange */  	unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ @@ -145,81 +156,603 @@ struct cifs_cred {   *****************************************************************   */ +/* + * A smb_rqst represents a complete request to be issued to a server. It's + * formed by a kvec array, followed by an array of pages. Page data is assumed + * to start at the beginning of the first page. + */ +struct smb_rqst { +	struct kvec	*rq_iov;	/* array of kvecs */ +	unsigned int	rq_nvec;	/* number of kvecs in array */ +	struct page	**rq_pages;	/* pointer to array of page ptrs */ +	unsigned int	rq_npages;	/* number pages in array */ +	unsigned int	rq_pagesz;	/* page size to use */ +	unsigned int	rq_tailsz;	/* length of last page */ +}; + +enum smb_version { +	Smb_1 = 1, +	Smb_20, +	Smb_21, +	Smb_30, +	Smb_302, +}; + +struct mid_q_entry; +struct TCP_Server_Info; +struct cifsFileInfo; +struct cifs_ses; +struct cifs_tcon; +struct dfs_info3_param; +struct cifs_fattr; +struct smb_vol; +struct cifs_fid; +struct cifs_readdata; +struct cifs_writedata; +struct cifs_io_parms; +struct cifs_search_info; +struct cifsInodeInfo; +struct cifs_open_parms; + +struct smb_version_operations { +	int (*send_cancel)(struct TCP_Server_Info *, void *, +			   struct mid_q_entry *); +	bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); +	/* setup request: allocate mid, sign message */ +	struct mid_q_entry *(*setup_request)(struct cifs_ses *, +						struct smb_rqst *); +	/* setup async request: allocate mid, sign message */ +	struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *, +						struct smb_rqst *); +	/* check response: verify signature, map error */ +	int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, +			     bool); +	void (*add_credits)(struct TCP_Server_Info *, const unsigned int, +			    const int); +	void (*set_credits)(struct TCP_Server_Info *, const int); +	int * (*get_credits_field)(struct TCP_Server_Info *, const int); +	unsigned int (*get_credits)(struct mid_q_entry *); +	__u64 (*get_next_mid)(struct TCP_Server_Info *); +	/* data offset from read response message */ +	unsigned int (*read_data_offset)(char *); +	/* data length from read response message */ +	unsigned int (*read_data_length)(char *); +	/* map smb to linux error */ +	int (*map_error)(char *, bool); +	/* find mid corresponding to the response message */ +	struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *); +	void (*dump_detail)(void *); +	void (*clear_stats)(struct cifs_tcon *); +	void (*print_stats)(struct seq_file *m, struct cifs_tcon *); +	void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); +	/* verify the message */ +	int (*check_message)(char *, unsigned int); +	bool (*is_oplock_break)(char *, struct TCP_Server_Info *); +	void (*downgrade_oplock)(struct TCP_Server_Info *, +					struct cifsInodeInfo *, bool); +	/* process transaction2 response */ +	bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, +			     char *, int); +	/* check if we need to negotiate */ +	bool (*need_neg)(struct TCP_Server_Info *); +	/* negotiate to the server */ +	int (*negotiate)(const unsigned int, struct cifs_ses *); +	/* set negotiated write size */ +	unsigned int (*negotiate_wsize)(struct cifs_tcon *, struct smb_vol *); +	/* set negotiated read size */ +	unsigned int (*negotiate_rsize)(struct cifs_tcon *, struct smb_vol *); +	/* setup smb sessionn */ +	int (*sess_setup)(const unsigned int, struct cifs_ses *, +			  const struct nls_table *); +	/* close smb session */ +	int (*logoff)(const unsigned int, struct cifs_ses *); +	/* connect to a server share */ +	int (*tree_connect)(const unsigned int, struct cifs_ses *, const char *, +			    struct cifs_tcon *, const struct nls_table *); +	/* close tree connecion */ +	int (*tree_disconnect)(const unsigned int, struct cifs_tcon *); +	/* get DFS referrals */ +	int (*get_dfs_refer)(const unsigned int, struct cifs_ses *, +			     const char *, struct dfs_info3_param **, +			     unsigned int *, const struct nls_table *, int); +	/* informational QFS call */ +	void (*qfs_tcon)(const unsigned int, struct cifs_tcon *); +	/* check if a path is accessible or not */ +	int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, +				  struct cifs_sb_info *, const char *); +	/* query path data from the server */ +	int (*query_path_info)(const unsigned int, struct cifs_tcon *, +			       struct cifs_sb_info *, const char *, +			       FILE_ALL_INFO *, bool *, bool *); +	/* query file data from the server */ +	int (*query_file_info)(const unsigned int, struct cifs_tcon *, +			       struct cifs_fid *, FILE_ALL_INFO *); +	/* get server index number */ +	int (*get_srv_inum)(const unsigned int, struct cifs_tcon *, +			    struct cifs_sb_info *, const char *, +			    u64 *uniqueid, FILE_ALL_INFO *); +	/* set size by path */ +	int (*set_path_size)(const unsigned int, struct cifs_tcon *, +			     const char *, __u64, struct cifs_sb_info *, bool); +	/* set size by file handle */ +	int (*set_file_size)(const unsigned int, struct cifs_tcon *, +			     struct cifsFileInfo *, __u64, bool); +	/* set attributes */ +	int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, +			     const unsigned int); +	int (*set_compression)(const unsigned int, struct cifs_tcon *, +			       struct cifsFileInfo *); +	/* check if we can send an echo or nor */ +	bool (*can_echo)(struct TCP_Server_Info *); +	/* send echo request */ +	int (*echo)(struct TCP_Server_Info *); +	/* create directory */ +	int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *, +		     struct cifs_sb_info *); +	/* set info on created directory */ +	void (*mkdir_setinfo)(struct inode *, const char *, +			      struct cifs_sb_info *, struct cifs_tcon *, +			      const unsigned int); +	/* remove directory */ +	int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *, +		     struct cifs_sb_info *); +	/* unlink file */ +	int (*unlink)(const unsigned int, struct cifs_tcon *, const char *, +		      struct cifs_sb_info *); +	/* open, rename and delete file */ +	int (*rename_pending_delete)(const char *, struct dentry *, +				     const unsigned int); +	/* send rename request */ +	int (*rename)(const unsigned int, struct cifs_tcon *, const char *, +		      const char *, struct cifs_sb_info *); +	/* send create hardlink request */ +	int (*create_hardlink)(const unsigned int, struct cifs_tcon *, +			       const char *, const char *, +			       struct cifs_sb_info *); +	/* query symlink target */ +	int (*query_symlink)(const unsigned int, struct cifs_tcon *, +			     const char *, char **, struct cifs_sb_info *); +	/* open a file for non-posix mounts */ +	int (*open)(const unsigned int, struct cifs_open_parms *, +		    __u32 *, FILE_ALL_INFO *); +	/* set fid protocol-specific info */ +	void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); +	/* close a file */ +	void (*close)(const unsigned int, struct cifs_tcon *, +		      struct cifs_fid *); +	/* send a flush request to the server */ +	int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *); +	/* async read from the server */ +	int (*async_readv)(struct cifs_readdata *); +	/* async write to the server */ +	int (*async_writev)(struct cifs_writedata *, +			    void (*release)(struct kref *)); +	/* sync read from the server */ +	int (*sync_read)(const unsigned int, struct cifsFileInfo *, +			 struct cifs_io_parms *, unsigned int *, char **, +			 int *); +	/* sync write to the server */ +	int (*sync_write)(const unsigned int, struct cifsFileInfo *, +			  struct cifs_io_parms *, unsigned int *, struct kvec *, +			  unsigned long); +	/* open dir, start readdir */ +	int (*query_dir_first)(const unsigned int, struct cifs_tcon *, +			       const char *, struct cifs_sb_info *, +			       struct cifs_fid *, __u16, +			       struct cifs_search_info *); +	/* continue readdir */ +	int (*query_dir_next)(const unsigned int, struct cifs_tcon *, +			      struct cifs_fid *, +			      __u16, struct cifs_search_info *srch_inf); +	/* close dir */ +	int (*close_dir)(const unsigned int, struct cifs_tcon *, +			 struct cifs_fid *); +	/* calculate a size of SMB message */ +	unsigned int (*calc_smb_size)(void *); +	/* check for STATUS_PENDING and process it in a positive case */ +	bool (*is_status_pending)(char *, struct TCP_Server_Info *, int); +	/* send oplock break response */ +	int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *, +			       struct cifsInodeInfo *); +	/* query remote filesystem */ +	int (*queryfs)(const unsigned int, struct cifs_tcon *, +		       struct kstatfs *); +	/* send mandatory brlock to the server */ +	int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64, +			 __u64, __u32, int, int, bool); +	/* unlock range of mandatory locks */ +	int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *, +				 const unsigned int); +	/* push brlocks from the cache to the server */ +	int (*push_mand_locks)(struct cifsFileInfo *); +	/* get lease key of the inode */ +	void (*get_lease_key)(struct inode *, struct cifs_fid *); +	/* set lease key of the inode */ +	void (*set_lease_key)(struct inode *, struct cifs_fid *); +	/* generate new lease key */ +	void (*new_lease_key)(struct cifs_fid *); +	int (*generate_signingkey)(struct cifs_ses *); +	int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); +	int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, +				struct cifs_sb_info *, const unsigned char *, +				char *, unsigned int *); +	int (*create_mf_symlink)(unsigned int, struct cifs_tcon *, +				 struct cifs_sb_info *, const unsigned char *, +				 char *, unsigned int *); +	/* if we can do cache read operations */ +	bool (*is_read_op)(__u32); +	/* set oplock level for the inode */ +	void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, +				 bool *); +	/* create lease context buffer for CREATE request */ +	char * (*create_lease_buf)(u8 *, u8); +	/* parse lease context buffer and return oplock/epoch info */ +	__u8 (*parse_lease_buf)(void *, unsigned int *); +	int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, +			struct cifsFileInfo *target_file, u64 src_off, u64 len, +			u64 dest_off); +	int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); +	ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, +			const unsigned char *, const unsigned char *, char *, +			size_t, const struct nls_table *, int); +	int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *, +			const char *, const void *, const __u16, +			const struct nls_table *, int); +	struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *, +			const char *, u32 *); +	struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *, +			const struct cifs_fid *, u32 *); +	int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, +			int); +}; + +struct smb_version_values { +	char		*version_string; +	__u16		protocol_id; +	__u32		req_capabilities; +	__u32		large_lock_type; +	__u32		exclusive_lock_type; +	__u32		shared_lock_type; +	__u32		unlock_lock_type; +	size_t		header_size; +	size_t		max_header_size; +	size_t		read_rsp_size; +	__le16		lock_cmd; +	unsigned int	cap_unix; +	unsigned int	cap_nt_find; +	unsigned int	cap_large_files; +	__u16		signing_enabled; +	__u16		signing_required; +	size_t		create_lease_size; +}; + +#define HEADER_SIZE(server) (server->vals->header_size) +#define MAX_HEADER_SIZE(server) (server->vals->max_header_size) + +struct smb_vol { +	char *username; +	char *password; +	char *domainname; +	char *UNC; +	char *iocharset;  /* local code page for mapping to and from Unicode */ +	char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ +	char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ +	kuid_t cred_uid; +	kuid_t linux_uid; +	kgid_t linux_gid; +	kuid_t backupuid; +	kgid_t backupgid; +	umode_t file_mode; +	umode_t dir_mode; +	enum securityEnum sectype; /* sectype requested via mnt opts */ +	bool sign; /* was signing requested via mnt opts? */ +	bool retry:1; +	bool intr:1; +	bool setuids:1; +	bool override_uid:1; +	bool override_gid:1; +	bool dynperm:1; +	bool noperm:1; +	bool no_psx_acl:1; /* set if posix acl support should be disabled */ +	bool cifs_acl:1; +	bool backupuid_specified; /* mount option  backupuid  is specified */ +	bool backupgid_specified; /* mount option  backupgid  is specified */ +	bool no_xattr:1;   /* set if xattr (EA) support should be disabled*/ +	bool server_ino:1; /* use inode numbers from server ie UniqueId */ +	bool direct_io:1; +	bool strict_io:1; /* strict cache behavior */ +	bool remap:1;      /* set to remap seven reserved chars in filenames */ +	bool posix_paths:1; /* unset to not ask for posix pathnames. */ +	bool no_linux_ext:1; +	bool sfu_emul:1; +	bool nullauth:1;   /* attempt to authenticate with null user */ +	bool nocase:1;     /* request case insensitive filenames */ +	bool nobrl:1;      /* disable sending byte range locks to srv */ +	bool mand_lock:1;  /* send mandatory not posix byte range lock reqs */ +	bool seal:1;       /* request transport encryption on share */ +	bool nodfs:1;      /* Do not request DFS, even if available */ +	bool local_lease:1; /* check leases only on local system, not remote */ +	bool noblocksnd:1; +	bool noautotune:1; +	bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ +	bool fsc:1;	/* enable fscache */ +	bool mfsymlinks:1; /* use Minshall+French Symlinks */ +	bool multiuser:1; +	bool rwpidforward:1; /* pid forward for read/write operations */ +	bool nosharesock; +	unsigned int rsize; +	unsigned int wsize; +	bool sockopt_tcp_nodelay:1; +	unsigned long actimeo; /* attribute cache timeout (jiffies) */ +	struct smb_version_operations *ops; +	struct smb_version_values *vals; +	char *prepath; +	struct sockaddr_storage dstaddr; /* destination address */ +	struct sockaddr_storage srcaddr; /* allow binding to a local IP */ +	struct nls_table *local_nls; +}; + +#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ +			 CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \ +			 CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \ +			 CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \ +			 CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \ +			 CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ +			 CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ +			 CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ +			 CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ +			 CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID) + +#define CIFS_MS_MASK (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \ +		      MS_NODEV | MS_SYNCHRONOUS) + +struct cifs_mnt_data { +	struct cifs_sb_info *cifs_sb; +	struct smb_vol *vol; +	int flags; +}; + +static inline unsigned int +get_rfc1002_length(void *buf) +{ +	return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void +inc_rfc1001_len(void *buf, int count) +{ +	be32_add_cpu((__be32 *)buf, count); +} +  struct TCP_Server_Info {  	struct list_head tcp_ses_list;  	struct list_head smb_ses_list;  	int srv_count; /* reference counter */  	/* 15 character server name + 0x20 16th byte indicating type = srv */  	char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; +	struct smb_version_operations	*ops; +	struct smb_version_values	*vals; +	enum statusEnum tcpStatus; /* what we think the status is */  	char *hostname; /* hostname portion of UNC string */  	struct socket *ssocket; -	union { -		struct sockaddr_in sockAddr; -		struct sockaddr_in6 sockAddr6; -	} addr; +	struct sockaddr_storage dstaddr;  	struct sockaddr_storage srcaddr; /* locally bind to this IP */ +#ifdef CONFIG_NET_NS +	struct net *net; +#endif  	wait_queue_head_t response_q;  	wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/  	struct list_head pending_mid_q; -	void *Server_NlsInfo;	/* BB - placeholder for future NLS info  */ -	unsigned short server_codepage;	/* codepage for the server    */ -	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 */  	bool tcp_nodelay; -	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 */ +	int credits;  /* send no more requests at once */ +	unsigned int in_flight;  /* number of requests on the wire to server */ +	spinlock_t req_lock;  /* protect the two values above */  	struct mutex srv_mutex;  	struct task_struct *tsk;  	char server_GUID[16]; -	char secMode; -	enum securityEnum secType; +	__u16 sec_mode; +	bool sign; /* is signing enabled on this connection? */ +	bool session_estab; /* mark when very first sess is established */ +#ifdef CONFIG_CIFS_SMB2 +	int echo_credits;  /* echo reserved slots */ +	int oplock_credits;  /* oplock break reserved slots */ +	bool echoes:1; /* enable echoes */ +	__u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */ +#endif +	u16 dialect; /* dialect index that server chose */ +	bool oplocks:1; /* enable oplocks */  	unsigned int maxReq;	/* Clients should submit no more */  	/* than maxReq distinct unanswered SMBs to the server when using  */  	/* multiplexed reads or writes */  	unsigned int maxBuf;	/* maxBuf specifies the maximum */  	/* message size the server can send or receive for non-raw SMBs */ +	/* maxBuf is returned by SMB NegotiateProtocol so maxBuf is only 0 */ +	/* when socket is setup (and during reconnect) before NegProt sent */  	unsigned int max_rw;	/* maxRw specifies the maximum */  	/* message size the server can send or receive for */  	/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ -	unsigned int max_vcs;	/* maximum number of smb sessions, at least -				   those that can be specified uniquely with -				   vcnumbers */ -	char sessid[4];		/* unique token id for this session */ -	/* (returned on Negotiate */ -	int capabilities; /* allow selective disabling of caps by smb sess */ +	unsigned int capabilities; /* selective disabling of caps by smb sess */  	int timeAdj;  /* Adjust for difference in server time zone in sec */ -	__u16 CurrentMid;         /* multiplex id - rotating counter */ +	__u64 CurrentMid;         /* multiplex id - rotating counter */  	char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */  	/* 16th byte of RFC1001 workstation name is always null */  	char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; -	__u32 sequence_number; /* needed for CIFS PDU signature */ +	__u32 sequence_number; /* for signing, protected by srv_mutex */  	struct session_key session_key;  	unsigned long lstrp; /* when we got last response from this server */ -	u16 dialect; /* dialect index that server chose */  	struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ +#define	CIFS_NEGFLAVOR_LANMAN	0	/* wct == 13, LANMAN */ +#define	CIFS_NEGFLAVOR_UNENCAP	1	/* wct == 17, but no ext_sec */ +#define	CIFS_NEGFLAVOR_EXTENDED	2	/* wct == 17, ext_sec bit set */ +	char	negflavor;	/* NEGOTIATE response flavor */  	/* extended security flavors that server supports */ +	bool	sec_ntlmssp;		/* supports NTLMSSP */ +	bool	sec_kerberosu2u;	/* supports U2U Kerberos */  	bool	sec_kerberos;		/* supports plain Kerberos */  	bool	sec_mskerberos;		/* supports legacy MS Kerberos */ -	bool	sec_kerberosu2u;	/* supports U2U Kerberos */ -	bool	sec_ntlmssp;		/* supports NTLMSSP */ -	bool session_estab; /* mark when very first sess is established */ +	bool	large_buf;		/* is current buffer large? */ +	struct delayed_work	echo; /* echo ping workqueue job */ +	struct kvec *iov;	/* reusable kvec array for receives */ +	unsigned int nr_iov;	/* number of kvecs in array */ +	char	*smallbuf;	/* pointer to current "small" buffer */ +	char	*bigbuf;	/* pointer to current "big" buffer */ +	unsigned int total_read; /* total amount of data read in this pass */  #ifdef CONFIG_CIFS_FSCACHE  	struct fscache_cookie   *fscache; /* client index cache cookie */  #endif +#ifdef CONFIG_CIFS_STATS2 +	atomic_t in_send; /* requests trying to send */ +	atomic_t num_waiters;   /* blocked waiting to get in sendrecv */ +#endif +#ifdef CONFIG_CIFS_SMB2 +	unsigned int	max_read; +	unsigned int	max_write; +#endif /* CONFIG_CIFS_SMB2 */  }; +static inline unsigned int +in_flight(struct TCP_Server_Info *server) +{ +	unsigned int num; +	spin_lock(&server->req_lock); +	num = server->in_flight; +	spin_unlock(&server->req_lock); +	return num; +} + +static inline bool +has_credits(struct TCP_Server_Info *server, int *credits) +{ +	int num; +	spin_lock(&server->req_lock); +	num = *credits; +	spin_unlock(&server->req_lock); +	return num > 0; +} + +static inline void +add_credits(struct TCP_Server_Info *server, const unsigned int add, +	    const int optype) +{ +	server->ops->add_credits(server, add, optype); +} + +static inline void +set_credits(struct TCP_Server_Info *server, const int val) +{ +	server->ops->set_credits(server, val); +} + +static inline __u64 +get_next_mid64(struct TCP_Server_Info *server) +{ +	return server->ops->get_next_mid(server); +} + +static inline __le16 +get_next_mid(struct TCP_Server_Info *server) +{ +	__u16 mid = get_next_mid64(server); +	/* +	 * The value in the SMB header should be little endian for easy +	 * on-the-wire decoding. +	 */ +	return cpu_to_le16(mid); +} + +static inline __u16 +get_mid(const struct smb_hdr *smb) +{ +	return le16_to_cpu(smb->Mid); +} + +static inline bool +compare_mid(__u16 mid, const struct smb_hdr *smb) +{ +	return mid == le16_to_cpu(smb->Mid); +} + +/* + * When the server supports very large reads and writes via POSIX extensions, + * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not + * including the RFC1001 length. + * + * Note that this might make for "interesting" allocation problems during + * writeback however as we have to allocate an array of pointers for the + * pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096. + * + * For reads, there is a similar problem as we need to allocate an array + * of kvecs to handle the receive, though that should only need to be done + * once. + */ +#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4) +#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4) + +/* + * When the server doesn't allow large posix writes, only allow a rsize/wsize + * of 2^17-1 minus the size of the call header. That allows for a read or + * write up to the maximum size described by RFC1002. + */ +#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4) +#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4) + +/* + * The default wsize is 1M. find_get_pages seems to return a maximum of 256 + * pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill + * a single wsize request with a single call. + */ +#define CIFS_DEFAULT_IOSIZE (1024 * 1024) + +/* + * Windows only supports a max of 60kb reads and 65535 byte writes. Default to + * those values when posix extensions aren't in force. In actuality here, we + * use 65536 to allow for a write that is a multiple of 4k. Most servers seem + * to be ok with the extra byte even though Windows doesn't send writes that + * are that large. + * + * Citation: + * + * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx + */ +#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) +#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) + +/* + * Macros to allow the TCP_Server_Info->net field and related code to drop out + * when CONFIG_NET_NS isn't set. + */ + +#ifdef CONFIG_NET_NS + +static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) +{ +	return srv->net; +} + +static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) +{ +	srv->net = net; +} + +#else + +static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) +{ +	return &init_net; +} + +static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) +{ +} + +#endif +  /*   * Session structure.  One of these for each uid session with a particular host   */ -struct cifsSesInfo { +struct cifs_ses {  	struct list_head smb_ses_list;  	struct list_head tcon_list;  	struct mutex session_mutex; @@ -228,70 +761,84 @@ struct cifsSesInfo {  	enum statusEnum status;  	unsigned overrideSecFlg;  /* if non-zero override global sec flags */  	__u16 ipc_tid;		/* special tid for connection to IPC share */ -	__u16 flags; -	__u16 vcnum;  	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;        /* overriding owner of files on the mount */ -	uid_t cred_uid;		/* owner of credentials */ -	int capabilities; +	__u64 Suid;		/* remote smb uid  */ +	kuid_t linux_uid;	/* overriding owner of files on the mount */ +	kuid_t cred_uid;	/* owner of credentials */ +	unsigned int capabilities;  	char serverName[SERVER_NAME_LEN_WITH_NULL * 2];	/* BB make bigger for  				TCP names - will ipv6 and sctp addresses fit? */ -	char userName[MAX_USERNAME_SIZE + 1]; +	char *user_name;	/* must not be null except during init of sess +				   and after mount option parsing we fill it */  	char *domainName;  	char *password;  	struct session_key auth_key;  	struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ +	enum securityEnum sectype; /* what security flavor was specified? */ +	bool sign;		/* is signing required? */  	bool need_reconnect:1; /* connection reset, uid now invalid */ +#ifdef CONFIG_CIFS_SMB2 +	__u16 session_flags; +	char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */ +#endif /* CONFIG_CIFS_SMB2 */  }; -/* 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 + +static inline bool +cap_unix(struct cifs_ses *ses) +{ +	return ses->server->vals->cap_unix & ses->capabilities; +} +  /*   * there is one of these for each connection to a resource on a particular   * session   */ -struct cifsTconInfo { +struct cifs_tcon {  	struct list_head tcon_list;  	int tc_count;  	struct list_head openFileList; -	struct cifsSesInfo *ses;	/* pointer to session associated with */ +	struct cifs_ses *ses;	/* pointer to session associated with */  	char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */  	char *nativeFileSystem;  	char *password;		/* for share-level security */ -	__u16 tid;		/* The 2 byte tree id */ +	__u32 tid;		/* The 4 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_flushes; -	atomic_t num_oplock_brks; -	atomic_t num_opens; -	atomic_t num_closes; -	atomic_t num_deletes; -	atomic_t num_mkdirs; -	atomic_t num_posixopens; -	atomic_t num_posixmkdirs; -	atomic_t num_rmdirs; -	atomic_t num_renames; -	atomic_t num_t2renames; -	atomic_t num_ffirst; -	atomic_t num_fnext; -	atomic_t num_fclose; -	atomic_t num_hardlinks; -	atomic_t num_symlinks; -	atomic_t num_locks; -	atomic_t num_acl_get; -	atomic_t num_acl_set; +	union { +		struct { +			atomic_t num_writes; +			atomic_t num_reads; +			atomic_t num_flushes; +			atomic_t num_oplock_brks; +			atomic_t num_opens; +			atomic_t num_closes; +			atomic_t num_deletes; +			atomic_t num_mkdirs; +			atomic_t num_posixopens; +			atomic_t num_posixmkdirs; +			atomic_t num_rmdirs; +			atomic_t num_renames; +			atomic_t num_t2renames; +			atomic_t num_ffirst; +			atomic_t num_fnext; +			atomic_t num_fclose; +			atomic_t num_hardlinks; +			atomic_t num_symlinks; +			atomic_t num_locks; +			atomic_t num_acl_get; +			atomic_t num_acl_set; +		} cifs_stats; +#ifdef CONFIG_CIFS_SMB2 +		struct { +			atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS]; +			atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; +		} smb2_stats; +#endif /* CONFIG_CIFS_SMB2 */ +	} stats;  #ifdef CONFIG_CIFS_STATS2  	unsigned long long time_writes;  	unsigned long long time_reads; @@ -322,10 +869,25 @@ struct cifsTconInfo {  	bool local_lease:1; /* check leases (only) on local system not remote */  	bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */  	bool need_reconnect:1; /* connection reset, tid now invalid */ +#ifdef CONFIG_CIFS_SMB2 +	bool print:1;		/* set if connection to printer share */ +	bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ +	__le32 capabilities; +	__u32 share_flags; +	__u32 maximal_access; +	__u32 vol_serial_number; +	__le64 vol_create_time; +	__u32 ss_flags;		/* sector size flags */ +	__u32 perf_sector_size; /* best sector size for perf */ +	__u32 max_chunks; +	__u32 max_bytes_chunk; +	__u32 max_bytes_copy; +#endif /* CONFIG_CIFS_SMB2 */  #ifdef CONFIG_CIFS_FSCACHE  	u64 resource_id;		/* server resource id */  	struct fscache_cookie *fscache;	/* cookie for share */  #endif +	struct list_head pending_opens;	/* list of incomplete opens */  	/* BB add field for back pointer to sb struct(s)? */  }; @@ -337,19 +899,19 @@ struct cifsTconInfo {   */  struct tcon_link {  	struct rb_node		tl_rbnode; -	uid_t			tl_uid; +	kuid_t			tl_uid;  	unsigned long		tl_flags;  #define TCON_LINK_MASTER	0  #define TCON_LINK_PENDING	1  #define TCON_LINK_IN_TREE	2  	unsigned long		tl_time;  	atomic_t		tl_count; -	struct cifsTconInfo	*tl_tcon; +	struct cifs_tcon	*tl_tcon;  };  extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); -static inline struct cifsTconInfo * +static inline struct cifs_tcon *  tlink_tcon(struct tcon_link *tlink)  {  	return tlink->tl_tcon; @@ -366,7 +928,16 @@ cifs_get_tlink(struct tcon_link *tlink)  }  /* This function is always expected to succeed */ -extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); +extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); + +#define CIFS_OPLOCK_NO_CHANGE 0xfe + +struct cifs_pending_open { +	struct list_head olist; +	struct tcon_link *tlink; +	__u8 lease_key[16]; +	__u32 oplock; +};  /*   * This info hangs off the cifsFileInfo structure, pointed to by llist. @@ -374,9 +945,12 @@ extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);   */  struct cifsLockInfo {  	struct list_head llist;	/* pointer to next cifsLockInfo */ +	struct list_head blist; /* pointer to locks blocked on this */ +	wait_queue_head_t block_q;  	__u64 offset;  	__u64 length; -	__u8 type; +	__u32 pid; +	__u32 type;  };  /* @@ -390,7 +964,7 @@ struct cifs_search_info {  	char *ntwrk_buf_start;  	char *srch_entries_start;  	char *last_entry; -	char *presume_name; +	const char *presume_name;  	unsigned int resume_name_len;  	bool endOfSearch:1;  	bool emptyDir:1; @@ -398,19 +972,47 @@ struct cifs_search_info {  	bool smallBuf:1; /* so we know which buf_release function to call */  }; +struct cifs_open_parms { +	struct cifs_tcon *tcon; +	struct cifs_sb_info *cifs_sb; +	int disposition; +	int desired_access; +	int create_options; +	const char *path; +	struct cifs_fid *fid; +	bool reconnect:1; +}; + +struct cifs_fid { +	__u16 netfid; +#ifdef CONFIG_CIFS_SMB2 +	__u64 persistent_fid;	/* persist file id for smb2 */ +	__u64 volatile_fid;	/* volatile file id for smb2 */ +	__u8 lease_key[SMB2_LEASE_KEY_SIZE];	/* lease key for smb2 */ +#endif +	struct cifs_pending_open *pending_open; +	unsigned int epoch; +	bool purge_cache; +}; + +struct cifs_fid_locks { +	struct list_head llist; +	struct cifsFileInfo *cfile;	/* fid that owns locks */ +	struct list_head locks;		/* locks held by fid above */ +}; +  struct cifsFileInfo {  	struct list_head tlist;	/* pointer to next fid owned by tcon */  	struct list_head flist;	/* next fid (file instance) for this inode */ -	unsigned int uid;	/* allows finding which FileInfo structure */ +	struct cifs_fid_locks *llist;	/* brlocks held by this fid */ +	kuid_t uid;		/* allows finding which FileInfo structure */  	__u32 pid;		/* process id who opened file */ -	__u16 netfid;		/* file id from remote */ +	struct cifs_fid fid;	/* file id from remote */  	/* BB add lock scope info here if needed */ ;  	/* lock scope id (0 if none) */  	struct dentry *dentry;  	unsigned int f_flags;  	struct tcon_link *tlink; -	struct mutex lock_mutex; -	struct list_head llist; /* list of byte range locks we have. */  	bool invalidHandle:1;	/* file closed via session abend */  	bool oplock_break_cancelled:1;  	int count;		/* refcount protected by cifs_file_list_lock */ @@ -419,33 +1021,115 @@ struct cifsFileInfo {  	struct work_struct oplock_break; /* work for oplock breaks */  }; +struct cifs_io_parms { +	__u16 netfid; +#ifdef CONFIG_CIFS_SMB2 +	__u64 persistent_fid;	/* persist file id for smb2 */ +	__u64 volatile_fid;	/* volatile file id for smb2 */ +#endif +	__u32 pid; +	__u64 offset; +	unsigned int length; +	struct cifs_tcon *tcon; +}; + +struct cifs_readdata; + +/* asynchronous read support */ +struct cifs_readdata { +	struct kref			refcount; +	struct list_head		list; +	struct completion		done; +	struct cifsFileInfo		*cfile; +	struct address_space		*mapping; +	__u64				offset; +	unsigned int			bytes; +	pid_t				pid; +	int				result; +	struct work_struct		work; +	int (*read_into_pages)(struct TCP_Server_Info *server, +				struct cifs_readdata *rdata, +				unsigned int len); +	struct kvec			iov; +	unsigned int			pagesz; +	unsigned int			tailsz; +	unsigned int			nr_pages; +	struct page			*pages[]; +}; + +struct cifs_writedata; + +/* asynchronous write support */ +struct cifs_writedata { +	struct kref			refcount; +	struct list_head		list; +	struct completion		done; +	enum writeback_sync_modes	sync_mode; +	struct work_struct		work; +	struct cifsFileInfo		*cfile; +	__u64				offset; +	pid_t				pid; +	unsigned int			bytes; +	int				result; +	unsigned int			pagesz; +	unsigned int			tailsz; +	unsigned int			nr_pages; +	struct page			*pages[]; +}; +  /*   * Take a reference on the file private data. Must be called with   * cifs_file_list_lock held.   */ -static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) +static inline void +cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)  {  	++cifs_file->count;  } +struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);  void cifsFileInfo_put(struct cifsFileInfo *cifs_file); +#define CIFS_CACHE_READ_FLG	1 +#define CIFS_CACHE_HANDLE_FLG	2 +#define CIFS_CACHE_RH_FLG	(CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG) +#define CIFS_CACHE_WRITE_FLG	4 +#define CIFS_CACHE_RW_FLG	(CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) +#define CIFS_CACHE_RHW_FLG	(CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) + +#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG) +#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) +#define CIFS_CACHE_WRITE(cinode) (cinode->oplock & CIFS_CACHE_WRITE_FLG) +  /*   * One of these for each file inode   */  struct cifsInodeInfo { -	struct list_head lockList; +	bool can_cache_brlcks; +	struct list_head llist;	/* locks helb by this inode */ +	struct rw_semaphore lock_sem;	/* protect the fields above */  	/* BB add in lists for dirty pages i.e. write caching info for oplock */  	struct list_head openFileList;  	__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ -	unsigned long time;	/* jiffies of last update/check of inode */ -	bool clientCanCacheRead:1;	/* read oplock */ -	bool clientCanCacheAll:1;	/* read and writebehind oplock */ -	bool delete_pending:1;		/* DELETE_ON_CLOSE is set */ -	bool invalid_mapping:1;		/* pagecache is invalid */ -	u64  server_eof;		/* current file size on server */ +	unsigned int oplock;		/* oplock/lease level we have */ +	unsigned int epoch;		/* used to track lease state changes */ +#define CIFS_INODE_PENDING_OPLOCK_BREAK   (0) /* oplock break in progress */ +#define CIFS_INODE_PENDING_WRITERS	  (1) /* Writes in progress */ +#define CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2 (2) /* Downgrade oplock to L2 */ +#define CIFS_INO_DELETE_PENDING		  (3) /* delete pending on server */ +#define CIFS_INO_INVALID_MAPPING	  (4) /* pagecache is invalid */ +#define CIFS_INO_LOCK			  (5) /* lock bit for synchronization */ +	unsigned long flags; +	spinlock_t writers_lock; +	unsigned int writers;		/* Number of writers on this inode */ +	unsigned long time;		/* jiffies of last update of inode */ +	u64  server_eof;		/* current file size on server -- protected by i_lock */  	u64  uniqueid;			/* server inode number */ +	u64  createtime;		/* creation time on server */ +#ifdef CONFIG_CIFS_SMB2 +	__u8 lease_key[SMB2_LEASE_KEY_SIZE];	/* lease key for this inode */ +#endif  #ifdef CONFIG_CIFS_FSCACHE  	struct fscache_cookie *fscache;  #endif @@ -472,10 +1156,25 @@ static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)  		return '\\';  } +static inline void +convert_delimiter(char *path, char delim) +{ +	char old_delim, *pos; + +	if (delim == '/') +		old_delim = '\\'; +	else +		old_delim = '/'; + +	pos = path; +	while ((pos = strchr(pos, old_delim))) +		*pos = delim; +} +  #ifdef CONFIG_CIFS_STATS  #define cifs_stats_inc atomic_inc -static inline void cifs_stats_bytes_written(struct cifsTconInfo *tcon, +static inline void cifs_stats_bytes_written(struct cifs_tcon *tcon,  					    unsigned int bytes)  {  	if (bytes) { @@ -485,7 +1184,7 @@ static inline void cifs_stats_bytes_written(struct cifsTconInfo *tcon,  	}  } -static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon, +static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,  					 unsigned int bytes)  {  	spin_lock(&tcon->stat_lock); @@ -500,32 +1199,103 @@ static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon,  #endif + +/* + * This is the prototype for the mid receive function. This function is for + * receiving the rest of the SMB frame, starting with the WordCount (which is + * just after the MID in struct smb_hdr). Note: + * + * - This will be called by cifsd, with no locks held. + * - The mid will still be on the pending_mid_q. + * - mid->resp_buf will point to the current buffer. + * + * Returns zero on a successful receive, or an error. The receive state in + * the TCP_Server_Info will also be updated. + */ +typedef int (mid_receive_t)(struct TCP_Server_Info *server, +			    struct mid_q_entry *mid); + +/* + * This is the prototype for the mid callback function. This is called once the + * mid has been received off of the socket. When creating one, take special + * care to avoid deadlocks. Things to bear in mind: + * + * - it will be called by cifsd, with no locks held + * - the mid will be removed from any lists + */ +typedef void (mid_callback_t)(struct mid_q_entry *mid); +  /* one of these for every pending CIFS request to the server */  struct mid_q_entry {  	struct list_head qhead;	/* mids waiting on reply from this server */ -	__u16 mid;		/* multiplex id */ -	__u16 pid;		/* process id */ +	struct TCP_Server_Info *server;	/* server corresponding to this mid */ +	__u64 mid;		/* multiplex id */ +	__u32 pid;		/* process id */  	__u32 sequence_number;  /* for CIFS signing */  	unsigned long when_alloc;  /* when mid was created */  #ifdef CONFIG_CIFS_STATS2  	unsigned long when_sent; /* time when smb send finished */  	unsigned long when_received; /* when demux complete (taken off wire) */  #endif -	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 */ +	mid_receive_t *receive; /* call receive callback */ +	mid_callback_t *callback; /* call completion callback */ +	void *callback_data;	  /* general purpose pointer for callback */ +	void *resp_buf;		/* pointer to received SMB header */ +	int mid_state;	/* wish this were enum but can not pass to wait_event */ +	__le16 command;		/* smb command code */ +	bool large_buf:1;	/* if valid response, is pointer to large buf */  	bool multiRsp:1;	/* multiple trans2 responses for one request  */  	bool multiEnd:1;	/* both received */  }; -struct oplock_q_entry { -	struct list_head qhead; -	struct inode *pinode; -	struct cifsTconInfo *tcon; -	__u16 netfid; -}; +/*	Make code in transport.c a little cleaner by moving +	update of optional stats into function below */ +#ifdef CONFIG_CIFS_STATS2 + +static inline void cifs_in_send_inc(struct TCP_Server_Info *server) +{ +	atomic_inc(&server->in_send); +} + +static inline void cifs_in_send_dec(struct TCP_Server_Info *server) +{ +	atomic_dec(&server->in_send); +} + +static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server) +{ +	atomic_inc(&server->num_waiters); +} + +static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) +{ +	atomic_dec(&server->num_waiters); +} + +static inline void cifs_save_when_sent(struct mid_q_entry *mid) +{ +	mid->when_sent = jiffies; +} +#else +static inline void cifs_in_send_inc(struct TCP_Server_Info *server) +{ +} +static inline void cifs_in_send_dec(struct TCP_Server_Info *server) +{ +} + +static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server) +{ +} + +static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) +{ +} + +static inline void cifs_save_when_sent(struct mid_q_entry *mid) +{ +} +#endif  /* for pending dnotify requests */  struct dir_notify_req { @@ -559,6 +1329,7 @@ struct dfs_info3_param {  #define CIFS_FATTR_DELETE_PENDING	0x2  #define CIFS_FATTR_NEED_REVAL		0x4  #define CIFS_FATTR_INO_COLLISION	0x8 +#define CIFS_FATTR_UNKNOWN_NLINK	0x10  struct cifs_fattr {  	u32		cf_flags; @@ -566,8 +1337,9 @@ struct cifs_fattr {  	u64		cf_uniqueid;  	u64		cf_eof;  	u64		cf_bytes; -	uid_t		cf_uid; -	gid_t		cf_gid; +	u64		cf_createtime; +	kuid_t		cf_uid; +	kgid_t		cf_gid;  	umode_t		cf_mode;  	dev_t		cf_rdev;  	unsigned int	cf_nlink; @@ -604,7 +1376,8 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,  #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 +#define   MID_RESPONSE_MALFORMED 0x10 +#define   MID_SHUTDOWN		 0x20  /* Types of response buffer returned from SendReceive2 */  #define   CIFS_NO_BUFFER        0    /* Response buffer not returned */ @@ -613,16 +1386,19 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,  #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_BLOCKING_OP      1    /* operation can block */ +#define   CIFS_ASYNC_OP         2    /* do not wait for response */ +#define   CIFS_TIMEOUT_MASK 0x003    /* only one of above set in req */  #define   CIFS_LOG_ERROR    0x010    /* log NT STATUS if non-zero */  #define   CIFS_LARGE_BUF_OP 0x020    /* large request buffer */  #define   CIFS_NO_RESP      0x040    /* no response buffer required */ +/* Type of request operation */ +#define   CIFS_ECHO_OP      0x080    /* echo request */ +#define   CIFS_OBREAK_OP   0x0100    /* oplock break request */ +#define   CIFS_NEG_OP      0x0200    /* negotiate request */ +#define   CIFS_OP_MASK     0x0380    /* mask request type */ +  /* Security Flags: indicate type of session setup needed */  #define   CIFSSEC_MAY_SIGN	0x00001  #define   CIFSSEC_MAY_NTLM	0x00002 @@ -654,6 +1430,8 @@ require use of the stronger protocol */  #define   CIFSSEC_MASK          0xB70B7 /* current flags supported if weak */  #endif /* UPCALL */  #else /* do not allow weak pw hash */ +#define   CIFSSEC_MUST_LANMAN	0 +#define   CIFSSEC_MUST_PLNTXT	0  #ifdef CONFIG_CIFS_UPCALL  #define   CIFSSEC_MASK          0x8F08F /* flags supported if no weak allowed */  #else @@ -663,7 +1441,7 @@ require use of the stronger protocol */  #define   CIFSSEC_MUST_SEAL	0x40040 /* not supported yet */  #define   CIFSSEC_MUST_NTLMSSP	0x80080 /* raw ntlmssp with ntlmv2 */ -#define   CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2) +#define   CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP)  #define   CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)  #define   CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)  /* @@ -733,10 +1511,12 @@ GLOBAL_EXTERN spinlock_t		cifs_tcp_ses_lock;   */  GLOBAL_EXTERN spinlock_t	cifs_file_list_lock; +#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */  /* Outstanding dir notify requests */  GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;  /* DirNotify response queue */  GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; +#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */  /*   * Global transaction id (XID) information @@ -746,8 +1526,6 @@ 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   */ @@ -767,12 +1545,7 @@ 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 bool enable_oplocks; /* enable or disable oplocks */  GLOBAL_EXTERN unsigned int lookupCacheEnabled;  GLOBAL_EXTERN unsigned int global_secflags;	/* if on, session setup sent  				with more secure ntlmssp2 challenge/resp */ @@ -783,10 +1556,38 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv;    /* min size of big ntwrk buf pool */  GLOBAL_EXTERN unsigned int cifs_min_small;  /* min size of small buf pool */  GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ +#ifdef CONFIG_CIFS_ACL +GLOBAL_EXTERN struct rb_root uidtree; +GLOBAL_EXTERN struct rb_root gidtree; +GLOBAL_EXTERN spinlock_t siduidlock; +GLOBAL_EXTERN spinlock_t sidgidlock; +GLOBAL_EXTERN struct rb_root siduidtree; +GLOBAL_EXTERN struct rb_root sidgidtree; +GLOBAL_EXTERN spinlock_t uidsidlock; +GLOBAL_EXTERN spinlock_t gidsidlock; +#endif /* CONFIG_CIFS_ACL */ +  void cifs_oplock_break(struct work_struct *work); -void cifs_oplock_break_get(struct cifsFileInfo *cfile); -void cifs_oplock_break_put(struct cifsFileInfo *cfile);  extern const struct slow_work_ops cifs_oplock_break_ops; - +extern struct workqueue_struct *cifsiod_wq; + +extern mempool_t *cifs_mid_poolp; + +/* Operations for different SMB versions */ +#define SMB1_VERSION_STRING	"1.0" +extern struct smb_version_operations smb1_operations; +extern struct smb_version_values smb1_values; +#define SMB20_VERSION_STRING	"2.0" +extern struct smb_version_operations smb20_operations; +extern struct smb_version_values smb20_values; +#define SMB21_VERSION_STRING	"2.1" +extern struct smb_version_operations smb21_operations; +extern struct smb_version_values smb21_values; +#define SMB30_VERSION_STRING	"3.0" +extern struct smb_version_operations smb30_operations; +extern struct smb_version_values smb30_values; +#define SMB302_VERSION_STRING	"3.02" +/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ +extern struct smb_version_values smb302_values;  #endif	/* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index de36b09763a..33df36ef9d5 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -23,6 +23,7 @@  #define _CIFSPDU_H  #include <net/sock.h> +#include <asm/unaligned.h>  #include "smbfsctl.h"  #ifdef CONFIG_CIFS_WEAK_PW_HASH @@ -50,6 +51,7 @@  #define SMB_COM_SETATTR               0x09 /* trivial response */  #define SMB_COM_LOCKING_ANDX          0x24 /* trivial response */  #define SMB_COM_COPY                  0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_ECHO                  0x2B /* echo request */  #define SMB_COM_OPEN_ANDX             0x2D /* Legacy open for old servers */  #define SMB_COM_READ_ANDX             0x2E  #define SMB_COM_WRITE_ANDX            0x2F @@ -140,6 +142,11 @@   */  #define CIFS_SESS_KEY_SIZE (16) +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE (16) +  #define CIFS_CLIENT_CHALLENGE_SIZE (8)  #define CIFS_SERVER_CHALLENGE_SIZE (8)  #define CIFS_HMAC_MD5_HASH_SIZE (16) @@ -275,7 +282,6 @@  #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" @@ -395,9 +401,9 @@  #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 */ +	__be32 smb_buf_length;	/* BB length is only two (rarely three) bytes, +		with one or two byte "type" preceding it that will be +		zero - we could mask the type byte off */  	__u8 Protocol[4];  	__u8 Command;  	union { @@ -422,14 +428,37 @@ struct smb_hdr {  	__u16 Tid;  	__le16 Pid;  	__u16 Uid; -	__u16 Mid; +	__le16 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 a void pointer to the ByteCount */ +static inline void * +BCC(struct smb_hdr *smb) +{ +	return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; +} +  /* given a pointer to an smb_hdr retrieve the pointer to the byte area */ -#define pByteArea(smb_var) ((unsigned char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount) + 2) +#define pByteArea(smb_var) (BCC(smb_var) + 2) + +/* get the unconverted ByteCount for a SMB packet and return it */ +static inline __u16 +get_bcc(struct smb_hdr *hdr) +{ +	__le16 *bc_ptr = (__le16 *)BCC(hdr); + +	return get_unaligned_le16(bc_ptr); +} + +/* set the ByteCount for a SMB packet in little-endian */ +static inline void +put_bcc(__u16 count, struct smb_hdr *hdr) +{ +	__le16 *bc_ptr = (__le16 *)BCC(hdr); + +	put_unaligned_le16(count, bc_ptr); +}  /*   * Computer Name Length (since Netbios name was length 16 with last byte 0x20) @@ -507,7 +536,7 @@ typedef struct lanman_neg_rsp {  #define READ_RAW_ENABLE 1  #define WRITE_RAW_ENABLE 2  #define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) - +#define SMB1_CLIENT_GUID_SIZE (16)  typedef struct negotiate_rsp {  	struct smb_hdr hdr;	/* wct = 17 */  	__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ @@ -529,7 +558,7 @@ typedef struct negotiate_rsp {  		/* followed by 16 bytes of server GUID */  		/* then security blob if cap_extended_security negotiated */  		struct { -			unsigned char GUID[16]; +			unsigned char GUID[SMB1_CLIENT_GUID_SIZE];  			unsigned char SecurityBlob[1];  		} __attribute__((packed)) extended_response;  	} __attribute__((packed)) u; @@ -668,7 +697,13 @@ struct ntlmssp2_name {  } __attribute__((packed));  struct ntlmv2_resp { -	char ntlmv2_hash[CIFS_ENCPWD_SIZE]; +	union { +	    char ntlmv2_hash[CIFS_ENCPWD_SIZE]; +	    struct { +		__u8 reserved[8]; +		__u8 key[CIFS_SERVER_CHALLENGE_SIZE]; +	    } __attribute__((packed)) challenge; +	} __attribute__((packed));  	__le32 blob_signature;  	__u32  reserved;  	__le64  time; @@ -760,6 +795,20 @@ typedef struct smb_com_tconx_rsp_ext {   *   */ +typedef struct smb_com_echo_req { +	struct	smb_hdr hdr; +	__le16	EchoCount; +	__le16	ByteCount; +	char	Data[1]; +} __attribute__((packed)) ECHO_REQ; + +typedef struct smb_com_echo_rsp { +	struct	smb_hdr hdr; +	__le16	SequenceNumber; +	__le16	ByteCount; +	char	Data[1]; +} __attribute__((packed)) ECHO_RSP; +  typedef struct smb_com_logoff_andx_req {  	struct smb_hdr hdr;	/* wct = 2 */  	__u8 AndXCommand; @@ -1050,9 +1099,7 @@ typedef struct smb_com_read_rsp {  	__le16 DataLengthHigh;  	__u64 Reserved2;  	__u16 ByteCount; -	__u8 Pad;		/* BB check for whether padded to DWORD -				   boundary and optimum performance here */ -	char Data[1]; +	/* read response data immediately follows */  } __attribute__((packed)) READ_RSP;  typedef struct locking_andx_range { @@ -1279,6 +1326,14 @@ typedef struct smb_com_ntransact_rsp {  	/* parms and data follow */  } __attribute__((packed)) NTRANSACT_RSP; +/* See MS-SMB 2.2.7.2.1.1 */ +struct srv_copychunk { +	__le64 SourceOffset; +	__le64 DestinationOffset; +	__le32 CopyLength; +	__u32  Reserved; +} __packed; +  typedef struct smb_com_transaction_ioctl_req {  	struct smb_hdr hdr;	/* wct = 23 */  	__u8 MaxSetupCount; @@ -1303,6 +1358,35 @@ typedef struct smb_com_transaction_ioctl_req {  	__u8 Data[1];  } __attribute__((packed)) TRANSACT_IOCTL_REQ; +typedef struct smb_com_transaction_compr_ioctl_req { +	struct smb_hdr hdr;	/* wct = 23 */ +	__u8 MaxSetupCount; +	__u16 Reserved; +	__le32 TotalParameterCount; +	__le32 TotalDataCount; +	__le32 MaxParameterCount; +	__le32 MaxDataCount; +	__le32 ParameterCount; +	__le32 ParameterOffset; +	__le32 DataCount; +	__le32 DataOffset; +	__u8 SetupCount; /* four setup words follow subcommand */ +	/* SNIA spec incorrectly included spurious pad here */ +	__le16 SubCommand; /* 2 = IOCTL/FSCTL */ +	__le32 FunctionCode; +	__u16 Fid; +	__u8 IsFsctl;  /* 1 = File System Control 0 = device control (IOCTL) */ +	__u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ +	__le16 ByteCount; +	__u8 Pad[3]; +	__le16 compression_state;  /* See below for valid flags */ +} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; + +/* compression state flags */ +#define COMPRESSION_FORMAT_NONE		0x0000 +#define COMPRESSION_FORMAT_DEFAULT	0x0001 +#define COMPRESSION_FORMAT_LZNT1	0x0002 +  typedef struct smb_com_transaction_ioctl_rsp {  	struct smb_hdr hdr;	/* wct = 19 */  	__u8 Reserved[3]; @@ -1442,15 +1526,31 @@ struct file_notify_information {  	__u8  FileName[0];  } __attribute__((packed)); -struct reparse_data { -	__u32	ReparseTag; -	__u16	ReparseDataLength; +/* For IO_REPARSE_TAG_SYMLINK */ +struct reparse_symlink_data { +	__le32	ReparseTag; +	__le16	ReparseDataLength; +	__u16	Reserved; +	__le16	SubstituteNameOffset; +	__le16	SubstituteNameLength; +	__le16	PrintNameOffset; +	__le16	PrintNameLength; +	__le32	Flags; +	char	PathBuffer[0]; +} __attribute__((packed)); + +/* For IO_REPARSE_TAG_NFS */ +#define NFS_SPECFILE_LNK	0x00000000014B4E4C +#define NFS_SPECFILE_CHR	0x0000000000524843 +#define NFS_SPECFILE_BLK	0x00000000004B4C42 +#define NFS_SPECFILE_FIFO	0x000000004F464946 +#define NFS_SPECFILE_SOCK	0x000000004B434F53 +struct reparse_posix_data { +	__le32	ReparseTag; +	__le16	ReparseDataLength;  	__u16	Reserved; -	__u16	AltNameOffset; -	__u16	AltNameLen; -	__u16	TargetNameOffset; -	__u16	TargetNameLen; -	char	LinkNamesBuf[1]; +	__le64	InodeType; /* LNK, FIFO, CHR etc. */ +	char	PathBuffer[0];  } __attribute__((packed));  struct cifs_quota_data { @@ -1874,6 +1974,10 @@ typedef struct whoami_rsp_data { /* Query level 0x202 */  /* SETFSInfo Levels */  #define SMB_SET_CIFS_UNIX_INFO    0x200 +/* level 0x203 is defined above in list of QFS info levels */ +/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ + +/* Level 0x200 request structure follows */  typedef struct smb_com_transaction2_setfsi_req {  	struct smb_hdr hdr;	/* wct = 15 */  	__le16 TotalParameterCount; @@ -1901,13 +2005,39 @@ typedef struct smb_com_transaction2_setfsi_req {  	__le64 ClientUnixCap;   /* Data end */  } __attribute__((packed)) TRANSACTION2_SETFSI_REQ; +/* level 0x203 request structure follows */ +typedef struct smb_com_transaction2_setfs_enc_req { +	struct smb_hdr hdr;	/* wct = 15 */ +	__le16 TotalParameterCount; +	__le16 TotalDataCount; +	__le16 MaxParameterCount; +	__le16 MaxDataCount; +	__u8 MaxSetupCount; +	__u8 Reserved; +	__le16 Flags; +	__le32 Timeout; +	__u16 Reserved2; +	__le16 ParameterCount;	/* 4 */ +	__le16 ParameterOffset; +	__le16 DataCount;	/* 12 */ +	__le16 DataOffset; +	__u8 SetupCount;	/* one */ +	__u8 Reserved3; +	__le16 SubCommand;	/* TRANS2_SET_FS_INFORMATION */ +	__le16 ByteCount; +	__u8 Pad; +	__u16  Reserved4;	/* Parameters start. */ +	__le16 InformationLevel;/* Parameters end. */ +	/* NTLMSSP Blob, Data start. */ +} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ; + +/* response for setfsinfo levels 0x200 and 0x203 */  typedef struct smb_com_transaction2_setfsi_rsp {  	struct smb_hdr hdr;	/* wct = 10 */  	struct trans2_resp t2;  	__u16 ByteCount;  } __attribute__((packed)) TRANSACTION2_SETFSI_RSP; -  typedef struct smb_com_transaction2_get_dfs_refer_req {  	struct smb_hdr hdr;	/* wct = 15 */  	__le16 TotalParameterCount; @@ -2059,13 +2189,13 @@ typedef struct {  #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 */ +/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send +   LockingX instead of posix locking call on unix sess (and we do not expect +   LockingX to use different (ie Windows) semantics than posix locking on +   the same session (if WINE needs to do this later, we can add this cap +   back in later */  /* #define CIFS_UNIX_CAP_MASK              0x000000fb */ -#define CIFS_UNIX_CAP_MASK              0x000000db +#define CIFS_UNIX_CAP_MASK              0x000003db  #else  #define CIFS_UNIX_CAP_MASK              0x00000013  #endif /* CONFIG_CIFS_POSIX */ @@ -2120,6 +2250,9 @@ typedef struct {  	__le32 DeviceCharacteristics;  } __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ +/* minimum includes first three fields, and empty FS Name */ +#define MIN_FS_ATTR_INFO_SIZE 12 +  typedef struct {  	__le32 Attributes;  	__le32 MaxPathNameComponentLength; @@ -2143,7 +2276,7 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */  	__u8 DeletePending;  	__u8 Directory;  	__u16 Pad2; -	__u64 IndexNumber; +	__le64 IndexNumber;  	__le32 EASize;  	__le32 AccessFlags;  	__u64 IndexNumber1; @@ -2572,26 +2705,7 @@ typedef struct file_xattr_info {  } __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 +/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */  typedef struct file_chattr_info {  	__le64	mask; /* list of all possible attribute bits */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7ed69b6b5fe..ca7980a1e30 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -24,6 +24,7 @@  struct statfs;  struct smb_vol; +struct smb_rqst;  /*   ***************************************************************** @@ -35,384 +36,475 @@ extern struct smb_hdr *cifs_buf_get(void);  extern void cifs_buf_release(void *);  extern struct smb_hdr *cifs_small_buf_get(void);  extern void cifs_small_buf_release(void *); +extern void cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx, +					struct kvec *iov);  extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,  			unsigned int /* length */); -extern unsigned int _GetXid(void); -extern void _FreeXid(unsigned int); -#define GetXid()						\ +extern unsigned int _get_xid(void); +extern void _free_xid(unsigned int); +#define get_xid()						\  ({								\ -	int __xid = (int)_GetXid();				\ -	cFYI(1, "CIFS VFS: in %s as Xid: %d with uid: %d",	\ -	     __func__, __xid, current_fsuid());			\ +	unsigned int __xid = _get_xid();				\ +	cifs_dbg(FYI, "CIFS VFS: in %s as Xid: %u with uid: %d\n",	\ +		 __func__, __xid,					\ +		 from_kuid(&init_user_ns, current_fsuid()));		\  	__xid;							\  }) -#define FreeXid(curr_xid)					\ +#define free_xid(curr_xid)					\  do {								\ -	_FreeXid(curr_xid);					\ -	cFYI(1, "CIFS VFS: leaving %s (xid = %d) rc = %d",	\ -	     __func__, curr_xid, (int)rc);			\ +	_free_xid(curr_xid);					\ +	cifs_dbg(FYI, "CIFS VFS: leaving %s (xid = %u) rc = %d\n",	\ +		 __func__, curr_xid, (int)rc);				\  } while (0) +extern int init_cifs_idmap(void); +extern void exit_cifs_idmap(void);  extern char *build_path_from_dentry(struct dentry *); -extern char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb); +extern char *cifs_build_path_to_root(struct smb_vol *vol, +				     struct cifs_sb_info *cifs_sb, +				     struct cifs_tcon *tcon);  extern char *build_wildcard_path_from_dentry(struct dentry *direntry);  extern char *cifs_compose_mount_options(const char *sb_mountdata,  		const char *fullpath, const struct dfs_info3_param *ref,  		char **devname);  /* extern void renew_parental_timestamps(struct dentry *direntry);*/ -extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, +extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer, +					struct TCP_Server_Info *server); +extern void DeleteMidQEntry(struct mid_q_entry *midEntry); +extern void cifs_delete_mid(struct mid_q_entry *mid); +extern void cifs_wake_up_task(struct mid_q_entry *mid); +extern int cifs_call_async(struct TCP_Server_Info *server, +			struct smb_rqst *rqst, +			mid_receive_t *receive, mid_callback_t *callback, +			void *cbdata, const int flags); +extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,  			struct smb_hdr * /* input */ ,  			struct smb_hdr * /* out */ , -			int * /* bytes returned */ , const int 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 *, +			int * /* bytes returned */ , const int); +extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, +			    char *in_buf, int flags); +extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *, +				struct smb_rqst *); +extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, +						struct smb_rqst *); +extern int cifs_check_receive(struct mid_q_entry *mid, +			struct TCP_Server_Info *server, bool log_error); +extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,  			struct kvec *, int /* nvec to send */,  			int * /* type of buf returned */ , const int flags);  extern int SendReceiveBlockingLock(const unsigned int xid, -			struct cifsTconInfo *ptcon, +			struct cifs_tcon *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 int cifs_reconnect(struct TCP_Server_Info *server); +extern int checkSMB(char *buf, unsigned int length); +extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); +extern bool backup_cred(struct cifs_sb_info *);  extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); +extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, +			    unsigned int bytes_written);  extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); -#ifdef CONFIG_CIFS_EXPERIMENTAL  extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); -#endif -extern unsigned int smbCalcSize(struct smb_hdr *ptr); -extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); +extern unsigned int smbCalcSize(void *buf);  extern int decode_negTokenInit(unsigned char *security_blob, int length,  			struct TCP_Server_Info *server);  extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); -extern int cifs_set_port(struct sockaddr *addr, const unsigned short int port); -extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, -				const unsigned short int port); -extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr); +extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port); +extern int map_smb_to_linux_error(char *buf, bool logErr);  extern void header_assemble(struct smb_hdr *, char /* command */ , -			    const struct cifsTconInfo *, int /* length of +			    const struct cifs_tcon *, int /* length of  			    fixed section (word count) in two byte units */);  extern int small_smb_init_no_tc(const int smb_cmd, const int wct, -				struct cifsSesInfo *ses, +				struct cifs_ses *ses,  				void **request_buf); -extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, -			     const struct nls_table *nls_cp); -extern __u16 GetNextMid(struct TCP_Server_Info *server); +extern enum securityEnum select_sectype(struct TCP_Server_Info *server, +				enum securityEnum requested); +extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, +			  const struct nls_table *nls_cp);  extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);  extern u64 cifs_UnixTimeToNT(struct timespec);  extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,  				      int offset);  extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); +extern int cifs_get_writer(struct cifsInodeInfo *cinode); +extern void cifs_put_writer(struct cifsInodeInfo *cinode); +extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode); +extern int cifs_unlock_range(struct cifsFileInfo *cfile, +			     struct file_lock *flock, const unsigned int xid); +extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); -extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle, -				struct file *file, struct tcon_link *tlink, -				__u32 oplock); -extern int cifs_posix_open(char *full_path, struct inode **pinode, -				struct super_block *sb, -				int mode, unsigned int f_flags, -				__u32 *poplock, __u16 *pnetfid, int xid); +extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, +					      struct file *file, +					      struct tcon_link *tlink, +					      __u32 oplock); +extern int cifs_posix_open(char *full_path, struct inode **inode, +			   struct super_block *sb, int mode, +			   unsigned int f_flags, __u32 *oplock, __u16 *netfid, +			   unsigned int xid);  void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr);  extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,  				     FILE_UNIX_BASIC_INFO *info,  				     struct cifs_sb_info *cifs_sb); +extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *, +					struct cifs_sb_info *);  extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);  extern struct inode *cifs_iget(struct super_block *sb,  			       struct cifs_fattr *fattr); -extern int cifs_get_file_info(struct file *filp); -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_file_info_unix(struct file *filp); +extern int cifs_get_inode_info(struct inode **inode, const char *full_path, +			       FILE_ALL_INFO *data, struct super_block *sb, +			       int xid, const struct cifs_fid *fid);  extern int cifs_get_inode_info_unix(struct inode **pinode,  			const unsigned char *search_path, -			struct super_block *sb, int xid); -extern void cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, +			struct super_block *sb, unsigned int xid); +extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs, +			      unsigned int xid, char *full_path, __u32 dosattr); +extern int cifs_rename_pending_delete(const char *full_path, +				      struct dentry *dentry, +				      const unsigned int xid); +extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,  			      struct cifs_fattr *fattr, struct inode *inode, -			      const char *path, const __u16 *pfid); -extern int mode_to_acl(struct inode *inode, const char *path, __u64); +			      const char *path, const struct cifs_fid *pfid); +extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64, +					kuid_t, kgid_t); +extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, +					const char *, u32 *); +extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *, +						const struct cifs_fid *, u32 *); +extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, +				const char *, int); + +extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); +extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, +		     unsigned int to_read); +extern int cifs_readv_from_socket(struct TCP_Server_Info *server, +		struct kvec *iov_orig, unsigned int nr_segs, +		unsigned int to_read); +extern void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, +			       struct cifs_sb_info *cifs_sb); +extern int cifs_match_super(struct super_block *, void *); +extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info); +extern struct smb_vol *cifs_get_volume_info(char *mount_data, +					    const char *devname); +extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); +extern void cifs_umount(struct cifs_sb_info *); +extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); +extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, +				    __u64 length, __u8 type, +				    struct cifsLockInfo **conf_lock, +				    int rw_check); +extern void cifs_add_pending_open(struct cifs_fid *fid, +				  struct tcon_link *tlink, +				  struct cifs_pending_open *open); +extern void cifs_add_pending_open_locked(struct cifs_fid *fid, +					 struct tcon_link *tlink, +					 struct cifs_pending_open *open); +extern void cifs_del_pending_open(struct cifs_pending_open *open); -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 *); +#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)  extern void cifs_dfs_release_automount_timer(void); +#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ +#define cifs_dfs_release_automount_timer()	do { } while (0) +#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ +  void cifs_proc_init(void);  void cifs_proc_clean(void); -extern int cifs_negotiate_protocol(unsigned int xid, -				  struct cifsSesInfo *ses); -extern int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, -			struct nls_table *nls_info); -extern int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses); +extern void cifs_move_llist(struct list_head *source, struct list_head *dest); +extern void cifs_free_llist(struct list_head *llist); +extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); -extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, -			const char *tree, struct cifsTconInfo *tcon, -			const struct nls_table *); +extern int cifs_negotiate_protocol(const unsigned int xid, +				   struct cifs_ses *ses); +extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, +			      struct nls_table *nls_info); +extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); +extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses); -extern int 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 CIFSTCon(const unsigned int xid, struct cifs_ses *ses, +		    const char *tree, struct cifs_tcon *tcon, +		    const struct nls_table *); -extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, -		__u16 searchHandle, struct cifs_search_info *psrch_inf); +extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, +		const char *searchName, struct cifs_sb_info *cifs_sb, +		__u16 *searchHandle, __u16 search_flags, +		struct cifs_search_info *psrch_inf, +		bool msearch); -extern int CIFSFindClose(const int, struct cifsTconInfo *tcon, +extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, +		__u16 searchHandle, __u16 search_flags, +		struct cifs_search_info *psrch_inf); + +extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,  			const __u16 search_handle); -extern int CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  			u16 netfid, FILE_ALL_INFO *pFindData); -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 CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, +			    const char *search_Name, FILE_ALL_INFO *data, +			    int legacy /* whether to use old info level */, +			    const struct nls_table *nls_codepage, int remap); +extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, +			       const char *search_name, FILE_ALL_INFO *data, +			       const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  			u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); -extern int CIFSSMBUnixQPathInfo(const int xid, -			struct cifsTconInfo *tcon, +extern int CIFSSMBUnixQPathInfo(const unsigned int xid, +			struct cifs_tcon *tcon,  			const unsigned char *searchName,  			FILE_UNIX_BASIC_INFO *pFindData,  			const struct nls_table *nls_codepage, int remap); -extern int CIFSGetDFSRefer(const 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 CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, +			   const char *search_name, +			   struct dfs_info3_param **target_nodes, +			   unsigned int *num_of_nodes, +			   const struct nls_table *nls_codepage, int remap); -extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, +extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,  			const char *old_path,  			const struct nls_table *nls_codepage, -			unsigned int *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, +			unsigned int *num_referrals, +			struct dfs_info3_param **referrals, int remap); +extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, +				 struct cifs_sb_info *cifs_sb, +				 struct smb_vol *vol); +extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,  			struct kstatfs *FSData); -extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, +extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,  			struct kstatfs *FSData); -extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *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, +extern int CIFSSMBQFSAttributeInfo(const unsigned int xid, +			struct cifs_tcon *tcon); +extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,  			struct kstatfs *FSData); -extern int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,  			const char *fileName, const FILE_BASIC_INFO *data,  			const struct nls_table *nls_codepage,  			int remap_special_chars); -extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  			const FILE_BASIC_INFO *data, __u16 fid,  			__u32 pid_of_opener); -extern int CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon, -			bool delete_file, __u16 fid, __u32 pid_of_opener); +extern int CIFSSMBSetFileDisposition(const unsigned int xid, +				     struct cifs_tcon *tcon, +				     bool delete_file, __u16 fid, +				     __u32 pid_of_opener);  #if 0 -extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon,  			char *fileName, __u16 dos_attributes,  			const struct nls_table *nls_codepage);  #endif /* possibly unneeded function */ -extern int CIFSSMBSetEOF(const 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); +extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, +			 const char *file_name, __u64 size, +			 struct cifs_sb_info *cifs_sb, bool set_allocation); +extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, +			      struct cifsFileInfo *cfile, __u64 size, +			      bool set_allocation);  struct cifs_unix_set_info_args {  	__u64	ctime;  	__u64	atime;  	__u64	mtime;  	__u64	mode; -	__u64	uid; -	__u64	gid; +	kuid_t	uid; +	kgid_t	gid;  	dev_t	device;  }; -extern int CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, +				  struct cifs_tcon *tcon,  				  const struct cifs_unix_set_info_args *args,  				  u16 fid, u32 pid_of_opener); -extern int CIFSSMBUnixSetPathInfo(const 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 CIFSSMBUnixSetPathInfo(const unsigned int xid, +				  struct cifs_tcon *tcon, const char *file_name, +				  const struct cifs_unix_set_info_args *args, +				  const struct nls_table *nls_codepage, +				  int remap); -extern int CIFSSMBMkDir(const 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, +extern int CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon, +			const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, +			const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,  			const char *name, __u16 type,  			const struct nls_table *nls_codepage,  			int remap_special_chars); -extern int CIFSSMBDelFile(const 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, +extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, +			  const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, +			 const char *from_name, const char *to_name, +			 struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon, +				 int netfid, const char *target_name, +				 const struct nls_table *nls_codepage, +				 int remap_special_chars); +extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, +			      const char *from_name, const char *to_name, +			      struct cifs_sb_info *cifs_sb); +extern int CIFSUnixCreateHardLink(const unsigned int xid, +			struct cifs_tcon *tcon,  			const char *fromName, const char *toName,  			const struct nls_table *nls_codepage,  			int remap_special_chars); -extern int CIFSUnixCreateSymLink(const int xid, -			struct cifsTconInfo *tcon, +extern int CIFSUnixCreateSymLink(const unsigned int xid, +			struct cifs_tcon *tcon,  			const char *fromName, const char *toName,  			const struct nls_table *nls_codepage); -extern int CIFSSMBUnixQuerySymLink(const int xid, -			struct cifsTconInfo *tcon, +extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, +			struct cifs_tcon *tcon,  			const unsigned char *searchName, char **syminfo,  			const struct nls_table *nls_codepage); -extern int 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, +extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, +			       __u16 fid, char **symlinkinfo, +			       const struct nls_table *nls_codepage); +extern int CIFSSMB_set_compression(const unsigned int xid, +				   struct cifs_tcon *tcon, __u16 fid); +extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, +		     int *oplock, FILE_ALL_INFO *buf); +extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,  			const char *fileName, const int disposition,  			const int access_flags, const int omode,  			__u16 *netfid, int *pOplock, FILE_ALL_INFO *,  			const struct nls_table *nls_codepage, int remap); -extern int 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, +extern int CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,  			u32 posix_flags, __u64 mode, __u16 *netfid,  			FILE_UNIX_BASIC_INFO *pRetData,  			__u32 *pOplock, const char *name,  			const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon,  			const int smb_file_id); -extern int CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *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, +extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, +			unsigned int *nbytes, char **buf,  			int *return_buf_type); -extern int CIFSSMBWrite(const 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 cifsConvertToUCS(__le16 *target, const char *source, int maxlen, -			const struct nls_table *cp, int mapChars); +extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, +			unsigned int *nbytes, const char *buf, +			const char __user *ubuf, const int long_op); +extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, +			unsigned int *nbytes, struct kvec *iov, const int nvec); +extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, +				 const char *search_name, __u64 *inode_number, +				 const struct nls_table *nls_codepage, +				 int remap); -extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, -			const __u16 netfid, const __u64 len, +extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, +		      const __u16 netfid, const __u8 lock_type, +		      const __u32 num_unlock, const __u32 num_lock, +		      LOCKING_ANDX_RANGE *buf); +extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, +			const __u16 netfid, const __u32 netpid, const __u64 len,  			const __u64 offset, const __u32 numUnlock,  			const __u32 numLock, const __u8 lockType, +			const bool waitFlag, const __u8 oplock_level); +extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, +			const __u16 smb_file_id, const __u32 netpid, +			const loff_t start_offset, const __u64 len, +			struct file_lock *, const __u16 lock_type,  			const bool waitFlag); -extern int 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 CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBEcho(struct TCP_Server_Info *server); +extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); -extern 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 *, +extern struct cifs_ses *sesInfoAlloc(void); +extern void sesInfoFree(struct cifs_ses *); +extern struct cifs_tcon *tconInfoAlloc(void); +extern void tconInfoFree(struct cifs_tcon *); + +extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, +		   __u32 *pexpected_response_sequence_number); +extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *,  			  __u32 *); -extern int cifs_verify_signature(struct smb_hdr *, +extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); +extern int cifs_verify_signature(struct smb_rqst *rqst,  				 struct TCP_Server_Info *server,  				__u32 expected_sequence_number); -extern void SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *); -extern int setup_ntlm_response(struct cifsSesInfo *); -extern int setup_ntlmv2_rsp(struct cifsSesInfo *, const struct nls_table *); -extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); +extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *, +			const struct nls_table *); +extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); +extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);  extern void cifs_crypto_shash_release(struct TCP_Server_Info *); -extern int calc_seckey(struct cifsSesInfo *); +extern int calc_seckey(struct cifs_ses *); +extern int generate_smb3signingkey(struct cifs_ses *);  #ifdef CONFIG_CIFS_WEAK_PW_HASH -extern void calc_lanman_hash(const char *password, const char *cryptkey, +extern int 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, +#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */ +extern int CIFSSMBNotify(const unsigned int xid, struct cifs_tcon *tcon, +			const int notify_subdirs, const __u16 netfid, +			__u32 filter, struct file *file, int multishot, +			const struct nls_table *nls_codepage); +#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */ +extern int CIFSSMBCopy(unsigned int xid, +			struct cifs_tcon *source_tcon,  			const char *fromName,  			const __u16 target_tid,  			const char *toName, const int flags,  			const struct nls_table *nls_codepage,  			int remap_special_chars); -extern 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, +extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,  			const unsigned char *searchName,  			const unsigned char *ea_name, char *EAData,  			size_t bufsize, const struct nls_table *nls_codepage,  			int remap_special_chars); -extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,  		const char *fileName, const char *ea_name,  		const void *ea_value, const __u16 ea_value_len,  		const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *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, +extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16, +			struct cifs_ntsd *, __u32, int); +extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,  		const unsigned char *searchName,  		char *acl_inf, const int buflen, const int acl_type,  		const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, +extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,  		const unsigned char *fileName,  		const char *local_acl, const int buflen, const int acl_type,  		const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, +extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,  			const int netfid, __u64 *pExtAttrBits, __u64 *pMask);  extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); -extern bool CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr); -extern int CIFSCheckMFSymlink(struct cifs_fattr *fattr, -		const unsigned char *path, -		struct cifs_sb_info *cifs_sb, int xid); +extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); +extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +			      struct cifs_sb_info *cifs_sb, +			      struct cifs_fattr *fattr, +			      const unsigned char *path); +extern int mdfour(unsigned char *, unsigned char *, int); +extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, +			const struct nls_table *codepage); +extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, +			unsigned char *p24); + +void cifs_readdata_release(struct kref *refcount); +int cifs_async_readv(struct cifs_readdata *rdata); +int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); + +int cifs_async_writev(struct cifs_writedata *wdata, +		      void (*release)(struct kref *kref)); +void cifs_writev_complete(struct work_struct *work); +struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, +						work_func_t complete); +void cifs_writedata_release(struct kref *refcount); +int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +			  struct cifs_sb_info *cifs_sb, +			  const unsigned char *path, char *pbuf, +			  unsigned int *pbytes_read); +int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +			   struct cifs_sb_info *cifs_sb, +			   const unsigned char *path, char *pbuf, +			   unsigned int *pbytes_written);  #endif			/* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 2f2632b6df5..6ce4e0954b9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -32,6 +32,9 @@  #include <linux/vfs.h>  #include <linux/slab.h>  #include <linux/posix_acl_xattr.h> +#include <linux/pagemap.h> +#include <linux/swap.h> +#include <linux/task_io_accounting_ops.h>  #include <asm/uaccess.h>  #include "cifspdu.h"  #include "cifsglob.h" @@ -39,6 +42,7 @@  #include "cifsproto.h"  #include "cifs_unicode.h"  #include "cifs_debug.h" +#include "fscache.h"  #ifdef CONFIG_CIFS_POSIX  static struct { @@ -82,32 +86,37 @@ static struct {  #endif /* CONFIG_CIFS_WEAK_PW_HASH */  #endif /* CIFS_POSIX */ -/* Mark as invalid, all open files on tree connections since they -   were closed when session to server was lost */ -static void mark_open_files_invalid(struct cifsTconInfo *pTcon) +/* + * Mark as invalid, all open files on tree connections since they + * were closed when session to server was lost. + */ +void +cifs_mark_open_files_invalid(struct cifs_tcon *tcon)  {  	struct cifsFileInfo *open_file = NULL;  	struct list_head *tmp;  	struct list_head *tmp1; -/* list all files open on tree connection and mark them invalid */ +	/* list all files open on tree connection and mark them invalid */  	spin_lock(&cifs_file_list_lock); -	list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { +	list_for_each_safe(tmp, tmp1, &tcon->openFileList) {  		open_file = list_entry(tmp, struct cifsFileInfo, tlist);  		open_file->invalidHandle = true;  		open_file->oplock_break_cancelled = true;  	}  	spin_unlock(&cifs_file_list_lock); -	/* BB Add call to invalidate_inodes(sb) for all superblocks mounted -	   to this tcon */ +	/* +	 * BB Add call to invalidate_inodes(sb) for all superblocks mounted +	 * to this tcon. +	 */  }  /* reconnect the socket, tcon, and smb session if needed */  static int -cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command) +cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)  { -	int rc = 0; -	struct cifsSesInfo *ses; +	int rc; +	struct cifs_ses *ses;  	struct TCP_Server_Info *server;  	struct nls_table *nls_codepage; @@ -130,24 +139,21 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)  		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); +			cifs_dbg(FYI, "can not send cmd %d while umounting\n", +				 smb_command);  			return -ENODEV;  		}  	} -	if (ses->status == CifsExiting) -		return -EIO; -  	/*  	 * Give demultiplex thread up to 10 seconds to reconnect, should be  	 * greater than cifs socket timeout which is 7 seconds  	 */  	while (server->tcpStatus == CifsNeedReconnect) {  		wait_event_interruptible_timeout(server->response_q, -			(server->tcpStatus == CifsGood), 10 * HZ); +			(server->tcpStatus != CifsNeedReconnect), 10 * HZ); -		/* is TCP session is reestablished now ?*/ +		/* are we still trying to reconnect? */  		if (server->tcpStatus != CifsNeedReconnect)  			break; @@ -156,8 +162,8 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)  		 * retrying until process is killed or server comes  		 * back on-line  		 */ -		if (!tcon->retry || ses->status == CifsExiting) { -			cFYI(1, "gave up waiting on reconnect in smb_init"); +		if (!tcon->retry) { +			cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");  			return -EHOSTDOWN;  		}  	} @@ -182,10 +188,10 @@ cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)  		goto out;  	} -	mark_open_files_invalid(tcon); +	cifs_mark_open_files_invalid(tcon);  	rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);  	mutex_unlock(&ses->session_mutex); -	cFYI(1, "reconnect tcon rc = %d", rc); +	cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);  	if (rc)  		goto out; @@ -229,7 +235,7 @@ out:     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, +small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon,  		void **request_buf)  {  	int rc; @@ -255,7 +261,7 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,  int  small_smb_init_no_tc(const int smb_command, const int wct, -		     struct cifsSesInfo *ses, void **request_buf) +		     struct cifs_ses *ses, void **request_buf)  {  	int rc;  	struct smb_hdr *buffer; @@ -265,7 +271,7 @@ small_smb_init_no_tc(const int smb_command, const int wct,  		return rc;  	buffer = (struct smb_hdr *)*request_buf; -	buffer->Mid = GetNextMid(ses->server); +	buffer->Mid = get_next_mid(ses->server);  	if (ses->capabilities & CAP_UNICODE)  		buffer->Flags2 |= SMBFLG2_UNICODE;  	if (ses->capabilities & CAP_STATUS32) @@ -281,7 +287,7 @@ small_smb_init_no_tc(const int smb_command, const int wct,  /* 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, +__smb_init(int smb_command, int wct, struct cifs_tcon *tcon,  			void **request_buf, void **response_buf)  {  	*request_buf = cifs_buf_get(); @@ -307,7 +313,7 @@ __smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,  /* 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, +smb_init(int smb_command, int wct, struct cifs_tcon *tcon,  	 void **request_buf, void **response_buf)  {  	int rc; @@ -320,7 +326,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,  }  static int -smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon, +smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon,  			void **request_buf, void **response_buf)  {  	if (tcon->ses->need_reconnect || tcon->need_reconnect) @@ -331,85 +337,242 @@ smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,  static int validate_t2(struct smb_t2_rsp *pSMB)  { -	int rc = -EINVAL; -	int total_size; -	char *pBCC; +	unsigned int total_size; + +	/* check for plausible wct */ +	if (pSMB->hdr.WordCount < 10) +		goto vt2_err; -	/* 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; -				} -			} -		} -	} +	if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 || +	    get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) +		goto vt2_err; + +	total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); +	if (total_size >= 512) +		goto vt2_err; + +	/* check that bcc is at least as big as parms + data, and that it is +	 * less than negotiated smb buffer +	 */ +	total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); +	if (total_size > get_bcc(&pSMB->hdr) || +	    total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) +		goto vt2_err; + +	return 0; +vt2_err:  	cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,  		sizeof(struct smb_t2_rsp) + 16); -	return rc; +	return -EINVAL; +} + +static int +decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) +{ +	int	rc = 0; +	u16	count; +	char	*guid = pSMBr->u.extended_response.GUID; +	struct TCP_Server_Info *server = ses->server; + +	count = get_bcc(&pSMBr->hdr); +	if (count < SMB1_CLIENT_GUID_SIZE) +		return -EIO; + +	spin_lock(&cifs_tcp_ses_lock); +	if (server->srv_count > 1) { +		spin_unlock(&cifs_tcp_ses_lock); +		if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { +			cifs_dbg(FYI, "server UID changed\n"); +			memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); +		} +	} else { +		spin_unlock(&cifs_tcp_ses_lock); +		memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); +	} + +	if (count == SMB1_CLIENT_GUID_SIZE) { +		server->sec_ntlmssp = true; +	} else { +		count -= SMB1_CLIENT_GUID_SIZE; +		rc = decode_negTokenInit( +			pSMBr->u.extended_response.SecurityBlob, count, server); +		if (rc != 1) +			return -EINVAL; +	} + +	return 0; +} + +int +cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) +{ +	bool srv_sign_required = server->sec_mode & server->vals->signing_required; +	bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; +	bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN; + +	/* +	 * Is signing required by mnt options? If not then check +	 * global_secflags to see if it is there. +	 */ +	if (!mnt_sign_required) +		mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == +						CIFSSEC_MUST_SIGN); + +	/* +	 * If signing is required then it's automatically enabled too, +	 * otherwise, check to see if the secflags allow it. +	 */ +	mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : +				(global_secflags & CIFSSEC_MAY_SIGN); + +	/* If server requires signing, does client allow it? */ +	if (srv_sign_required) { +		if (!mnt_sign_enabled) { +			cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!"); +			return -ENOTSUPP; +		} +		server->sign = true; +	} + +	/* If client requires signing, does server allow it? */ +	if (mnt_sign_required) { +		if (!srv_sign_enabled) { +			cifs_dbg(VFS, "Server does not support signing!"); +			return -ENOTSUPP; +		} +		server->sign = true; +	} + +	return 0; +} + +#ifdef CONFIG_CIFS_WEAK_PW_HASH +static int +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) +{ +	__s16 tmp; +	struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; + +	if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT) +		return -EOPNOTSUPP; + +	server->sec_mode = le16_to_cpu(rsp->SecurityMode); +	server->maxReq = min_t(unsigned int, +			       le16_to_cpu(rsp->MaxMpxCount), +			       cifs_max_pending); +	set_credits(server, server->maxReq); +	server->maxBuf = le16_to_cpu(rsp->MaxBufSize); +	/* even though we do not use raw we might as well set this +	accurately, in case we ever find a need for it */ +	if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { +		server->max_rw = 0xFF00; +		server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; +	} else { +		server->max_rw = 0;/* do not need to use raw anyway */ +		server->capabilities = CAP_MPX_MODE; +	} +	tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); +	if (tmp == -1) { +		/* OS/2 often does not set timezone therefore +		 * we must use server time to calc time zone. +		 * Could deviate slightly from the right zone. +		 * Smallest defined timezone difference is 15 minutes +		 * (i.e. Nepal).  Rounding up/down is done to match +		 * this requirement. +		 */ +		int val, seconds, remain, result; +		struct timespec ts, utc; +		utc = CURRENT_TIME; +		ts = cnvrtDosUnixTm(rsp->SrvTime.Date, +				    rsp->SrvTime.Time, 0); +		cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", +			 (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 */ +	} +	cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj); + + +	/* BB get server time for time conversions and add +	code to use it and timezone since this is not UTC */ + +	if (rsp->EncryptionKeyLength == +			cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { +		memcpy(server->cryptkey, rsp->EncryptionKey, +			CIFS_CRYPTO_KEY_SIZE); +	} else if (server->sec_mode & SECMODE_PW_ENCRYPT) { +		return -EIO; /* need cryptkey unless plain text */ +	} + +	cifs_dbg(FYI, "LANMAN negotiated\n"); +	return 0;  } +#else +static inline int +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) +{ +	cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); +	return -EOPNOTSUPP; +} +#endif + +static bool +should_set_ext_sec_flag(enum securityEnum sectype) +{ +	switch (sectype) { +	case RawNTLMSSP: +	case Kerberos: +		return true; +	case Unspecified: +		if (global_secflags & +		    (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) +			return true; +		/* Fallthrough */ +	default: +		return false; +	} +} +  int -CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) +CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)  {  	NEGOTIATE_REQ *pSMB;  	NEGOTIATE_RSP *pSMBr;  	int rc = 0;  	int bytes_returned;  	int i; -	struct TCP_Server_Info *server; +	struct TCP_Server_Info *server = ses->server;  	u16 count; -	unsigned int secFlags; -	if (ses->server) -		server = ses->server; -	else { -		rc = -EIO; -		return rc; +	if (!server) { +		WARN(1, "%s: server is NULL!\n", __func__); +		return -EIO;  	} +  	rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,  		      (void **) &pSMB, (void **) &pSMBr);  	if (rc)  		return rc; -	/* 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 = global_secflags | ses->overrideSecFlg; - -	cFYI(1, "secFlags 0x%x", secFlags); - -	pSMB->hdr.Mid = GetNextMid(server); +	pSMB->hdr.Mid = get_next_mid(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"); +	if (should_set_ext_sec_flag(ses->sectype)) { +		cifs_dbg(FYI, "Requesting extended security.");  		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;  	} -#ifdef CONFIG_CIFS_EXPERIMENTAL -	else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) -		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; -	else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) { -		cFYI(1, "NTLMSSP only mechanism, enable extended security"); -		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; -	} -#endif  	count = 0;  	for (i = 0; i < CIFS_NUM_PROT; i++) { @@ -417,7 +580,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)  		count += strlen(protocols[i].name) + 1;  		/* null at end of source and target buffers anyway */  	} -	pSMB->hdr.smb_buf_length += count; +	inc_rfc1001_len(pSMB, count);  	pSMB->ByteCount = cpu_to_le16(count);  	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, @@ -426,7 +589,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)  		goto neg_err_exit;  	server->dialect = le16_to_cpu(pSMBr->DialectIndex); -	cFYI(1, "Dialect: %d", server->dialect); +	cifs_dbg(FYI, "Dialect: %d\n", server->dialect);  	/* Check wct = 1 error case */  	if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {  		/* core returns wct = 1, but we do not ask for core - otherwise @@ -434,250 +597,67 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)  		could not negotiate a common dialect */  		rc = -EOPNOTSUPP;  		goto neg_err_exit; -#ifdef CONFIG_CIFS_WEAK_PW_HASH -	} else if ((pSMBr->hdr.WordCount == 13) -			&& ((server->dialect == LANMAN_PROT) -				|| (server->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); -		server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); -		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->max_rw = 0xFF00; -			server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; -		} else { -			server->max_rw = 0;/* do not need to use raw anyway */ -			server->capabilities = CAP_MPX_MODE; -		} -		tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); -		if (tmp == -1) { -			/* OS/2 often does not set timezone therefore -			 * we must use server time to calc time zone. -			 * Could deviate slightly from the right zone. -			 * Smallest defined timezone difference is 15 minutes -			 * (i.e. Nepal).  Rounding up/down is done to match -			 * this requirement. -			 */ -			int val, seconds, remain, result; -			struct timespec ts, utc; -			utc = CURRENT_TIME; -			ts = cnvrtDosUnixTm(rsp->SrvTime.Date, -					    rsp->SrvTime.Time, 0); -			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(ses->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; +		server->negflavor = CIFS_NEGFLAVOR_LANMAN; +		rc = decode_lanman_negprot_rsp(server, pSMBr); +		goto signing_check;  	} else if (pSMBr->hdr.WordCount != 17) {  		/* unknown wct */  		rc = -EOPNOTSUPP;  		goto neg_err_exit;  	} -	/* else wct == 17 NTLM */ -	server->secMode = pSMBr->SecurityMode; -	if ((server->secMode & SECMODE_USER) == 0) -		cFYI(1, "share mode security"); +	/* else wct == 17, NTLM or better */ -	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_NTLMSSP) -		server->secType = RawNTLMSSP; -	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 ...? */ +	server->sec_mode = pSMBr->SecurityMode; +	if ((server->sec_mode & SECMODE_USER) == 0) +		cifs_dbg(FYI, "share mode security\n");  	/* one byte, so no need to convert this or EncryptionKeyLen from  	   little endian */ -	server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount); +	server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), +			       cifs_max_pending); +	set_credits(server, server->maxReq);  	/* probably no need to store and check maxvcs */ -	server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), -			(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); +	server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);  	server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); -	cFYI(DBG2, "Max buf = %d", ses->server->maxBuf); -	GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); +	cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf);  	server->capabilities = le32_to_cpu(pSMBr->Capabilities);  	server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);  	server->timeAdj *= 60; +  	if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { +		server->negflavor = CIFS_NEGFLAVOR_UNENCAP;  		memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,  		       CIFS_CRYPTO_KEY_SIZE); -	} else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) -			&& (pSMBr->EncryptionKeyLength == 0)) { -		/* decode security blob */ -	} else if (server->secMode & SECMODE_PW_ENCRYPT) { +	} else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || +			server->capabilities & CAP_EXTENDED_SECURITY) && +				(pSMBr->EncryptionKeyLength == 0)) { +		server->negflavor = CIFS_NEGFLAVOR_EXTENDED; +		rc = decode_ext_sec_blob(ses, pSMBr); +	} else if (server->sec_mode & SECMODE_PW_ENCRYPT) {  		rc = -EIO; /* no crypt key only if plain text pwd */ -		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; -		} -		spin_lock(&cifs_tcp_ses_lock); -		if (server->srv_count > 1) { -			spin_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 { -			spin_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); -			if (rc == 1) -				rc = 0; -			else -				rc = -EINVAL; -			if (server->secType == Kerberos) { -				if (!server->sec_kerberos && -						!server->sec_mskerberos) -					rc = -EOPNOTSUPP; -			} else if (server->secType == RawNTLMSSP) { -				if (!server->sec_ntlmssp) -					rc = -EOPNOTSUPP; -			} else -					rc = -EOPNOTSUPP; -		} -	} 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); +		server->negflavor = CIFS_NEGFLAVOR_UNENCAP; +		server->capabilities &= ~CAP_EXTENDED_SECURITY;  	} +signing_check: +	if (!rc) +		rc = cifs_enable_signing(server, ses->sign);  neg_err_exit:  	cifs_buf_release(pSMB); -	cFYI(1, "negprot rc %d", rc); +	cifs_dbg(FYI, "negprot rc %d\n", rc);  	return rc;  }  int -CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) +CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)  {  	struct smb_hdr *smb_buffer;  	int rc = 0; -	cFYI(1, "In tree disconnect"); +	cifs_dbg(FYI, "In tree disconnect\n");  	/* BB: do we need to check this? These should never be NULL. */  	if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) @@ -697,9 +677,9 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)  	if (rc)  		return rc; -	rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);  	if (rc) -		cFYI(1, "Tree disconnect failed %d", rc); +		cifs_dbg(FYI, "Tree disconnect failed %d\n", rc);  	/* No need to return error on this operation if tid invalidated and  	   closed on server already e.g. due to tcp session crashing */ @@ -709,13 +689,64 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)  	return rc;  } +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +cifs_echo_callback(struct mid_q_entry *mid) +{ +	struct TCP_Server_Info *server = mid->callback_data; + +	DeleteMidQEntry(mid); +	add_credits(server, 1, CIFS_ECHO_OP); +} +  int -CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) +CIFSSMBEcho(struct TCP_Server_Info *server) +{ +	ECHO_REQ *smb; +	int rc = 0; +	struct kvec iov; +	struct smb_rqst rqst = { .rq_iov = &iov, +				 .rq_nvec = 1 }; + +	cifs_dbg(FYI, "In echo request\n"); + +	rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb); +	if (rc) +		return rc; + +	/* set up echo request */ +	smb->hdr.Tid = 0xffff; +	smb->hdr.WordCount = 1; +	put_unaligned_le16(1, &smb->EchoCount); +	put_bcc(1, &smb->hdr); +	smb->Data[0] = 'a'; +	inc_rfc1001_len(smb, 3); +	iov.iov_base = smb; +	iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; + +	rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, +			     server, CIFS_ASYNC_OP | CIFS_ECHO_OP); +	if (rc) +		cifs_dbg(FYI, "Echo request failed: %d\n", rc); + +	cifs_small_buf_release(smb); + +	return rc; +} + +int +CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)  {  	LOGOFF_ANDX_REQ *pSMB;  	int rc = 0; -	cFYI(1, "In SMBLogoff for session disconnect"); +	cifs_dbg(FYI, "In SMBLogoff for session disconnect\n");  	/*  	 * BB: do we need to check validity of ses and server? They should @@ -735,16 +766,15 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)  		return rc;  	} -	pSMB->hdr.Mid = GetNextMid(ses->server); +	pSMB->hdr.Mid = get_next_mid(ses->server); -	if (ses->server->secMode & -		   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) -			pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; +	if (ses->server->sign) +		pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;  	pSMB->hdr.Uid = ses->Suid;  	pSMB->AndXCommand = 0xFF; -	rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); +	rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);  session_already_dead:  	mutex_unlock(&ses->session_mutex); @@ -757,8 +787,9 @@ session_already_dead:  }  int -CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, -		 __u16 type, const struct nls_table *nls_codepage, int remap) +CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, +		 const char *fileName, __u16 type, +		 const struct nls_table *nls_codepage, int remap)  {  	TRANSACTION2_SPI_REQ *pSMB = NULL;  	TRANSACTION2_SPI_RSP *pSMBr = NULL; @@ -768,7 +799,7 @@ CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,  	int bytes_returned = 0;  	__u16 params, param_offset, offset, byte_count; -	cFYI(1, "In POSIX delete"); +	cifs_dbg(FYI, "In POSIX delete\n");  PsxDelete:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -777,8 +808,8 @@ PsxDelete:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else { /* BB add path length overrun check */ @@ -815,15 +846,15 @@ PsxDelete:  	pSMB->TotalParameterCount = pSMB->ParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) -		cFYI(1, "Posix delete returned %d", rc); +		cifs_dbg(FYI, "Posix delete returned %d\n", rc);  	cifs_buf_release(pSMB); -	cifs_stats_inc(&tcon->num_deletes); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);  	if (rc == -EAGAIN)  		goto PsxDelete; @@ -832,14 +863,15 @@ PsxDelete:  }  int -CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, -	       const struct nls_table *nls_codepage, int remap) +CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +	       struct cifs_sb_info *cifs_sb)  {  	DELETE_FILE_REQ *pSMB = NULL;  	DELETE_FILE_RSP *pSMBr = NULL;  	int rc = 0;  	int bytes_returned;  	int name_len; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;  DelFileRetry:  	rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, @@ -848,26 +880,26 @@ DelFileRetry:  		return rc;  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->fileName, fileName, -				     PATH_MAX, nls_codepage, remap); +		name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name, +					      PATH_MAX, cifs_sb->local_nls, +					      remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {		/* BB improve check for buffer overruns BB */ -		name_len = strnlen(fileName, PATH_MAX); +		name_len = strnlen(name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->fileName, fileName, name_len); +		strncpy(pSMB->fileName, name, name_len);  	}  	pSMB->SearchAttributes =  	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);  	pSMB->BufferFormat = 0x04; -	pSMB->hdr.smb_buf_length += name_len + 1; +	inc_rfc1001_len(pSMB, name_len + 1);  	pSMB->ByteCount = cpu_to_le16(name_len + 1);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_deletes); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);  	if (rc) -		cFYI(1, "Error in RMFile = %d", rc); +		cifs_dbg(FYI, "Error in RMFile = %d\n", rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -877,16 +909,17 @@ DelFileRetry:  }  int -CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName, -	     const struct nls_table *nls_codepage, int remap) +CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +	     struct cifs_sb_info *cifs_sb)  {  	DELETE_DIRECTORY_REQ *pSMB = NULL;  	DELETE_DIRECTORY_RSP *pSMBr = NULL;  	int rc = 0;  	int bytes_returned;  	int name_len; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; -	cFYI(1, "In CIFSSMBRmDir"); +	cifs_dbg(FYI, "In CIFSSMBRmDir\n");  RmDirRetry:  	rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -894,24 +927,25 @@ RmDirRetry:  		return rc;  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName, -					 PATH_MAX, nls_codepage, remap); +		name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, +					      PATH_MAX, cifs_sb->local_nls, +					      remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {		/* BB improve check for buffer overruns BB */ -		name_len = strnlen(dirName, PATH_MAX); +		name_len = strnlen(name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->DirName, dirName, name_len); +		strncpy(pSMB->DirName, name, name_len);  	}  	pSMB->BufferFormat = 0x04; -	pSMB->hdr.smb_buf_length += name_len + 1; +	inc_rfc1001_len(pSMB, name_len + 1);  	pSMB->ByteCount = cpu_to_le16(name_len + 1);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_rmdirs); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_rmdirs);  	if (rc) -		cFYI(1, "Error in RMDir = %d", rc); +		cifs_dbg(FYI, "Error in RMDir = %d\n", rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -920,16 +954,17 @@ RmDirRetry:  }  int -CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, -	     const char *name, const struct nls_table *nls_codepage, int remap) +CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +	     struct cifs_sb_info *cifs_sb)  {  	int rc = 0;  	CREATE_DIRECTORY_REQ *pSMB = NULL;  	CREATE_DIRECTORY_RSP *pSMBr = NULL;  	int bytes_returned;  	int name_len; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; -	cFYI(1, "In CIFSSMBMkDir"); +	cifs_dbg(FYI, "In CIFSSMBMkDir\n");  MkDirRetry:  	rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -937,8 +972,9 @@ MkDirRetry:  		return rc;  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name, -					    PATH_MAX, nls_codepage, remap); +		name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, +					      PATH_MAX, cifs_sb->local_nls, +					      remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {		/* BB improve check for buffer overruns BB */ @@ -948,13 +984,13 @@ MkDirRetry:  	}  	pSMB->BufferFormat = 0x04; -	pSMB->hdr.smb_buf_length += name_len + 1; +	inc_rfc1001_len(pSMB, name_len + 1);  	pSMB->ByteCount = cpu_to_le16(name_len + 1);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_mkdirs); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_mkdirs);  	if (rc) -		cFYI(1, "Error in Mkdir = %d", rc); +		cifs_dbg(FYI, "Error in Mkdir = %d\n", rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -963,10 +999,11 @@ MkDirRetry:  }  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) +CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, +		__u32 posix_flags, __u64 mode, __u16 *netfid, +		FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, +		const char *name, const struct nls_table *nls_codepage, +		int remap)  {  	TRANSACTION2_SPI_REQ *pSMB = NULL;  	TRANSACTION2_SPI_RSP *pSMBr = NULL; @@ -977,7 +1014,7 @@ CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,  	OPEN_PSX_REQ *pdata;  	OPEN_PSX_RSP *psx_rsp; -	cFYI(1, "In POSIX Create"); +	cifs_dbg(FYI, "In POSIX Create\n");  PsxCreat:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -986,8 +1023,8 @@ PsxCreat:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, name, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, name, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -1026,19 +1063,19 @@ PsxCreat:  	pSMB->TotalParameterCount = pSMB->ParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Posix create returned %d", rc); +		cifs_dbg(FYI, "Posix create returned %d\n", rc);  		goto psx_create_err;  	} -	cFYI(1, "copying inode info"); +	cifs_dbg(FYI, "copying inode info\n");  	rc = validate_t2((struct smb_t2_rsp *)pSMBr); -	if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) { +	if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) {  		rc = -EIO;	/* bad smb */  		goto psx_create_err;  	} @@ -1057,11 +1094,11 @@ PsxCreat:  	/* 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"); +		cifs_dbg(NOISY, "unknown type\n");  	} else { -		if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP) +		if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)  					+ sizeof(FILE_UNIX_BASIC_INFO)) { -			cERROR(1, "Open response data too small"); +			cifs_dbg(VFS, "Open response data too small\n");  			pRetData->Type = cpu_to_le32(-1);  			goto psx_create_err;  		} @@ -1074,9 +1111,9 @@ psx_create_err:  	cifs_buf_release(pSMB);  	if (posix_flags & SMB_O_DIRECTORY) -		cifs_stats_inc(&tcon->num_posixmkdirs); +		cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs);  	else -		cifs_stats_inc(&tcon->num_posixopens); +		cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens);  	if (rc == -EAGAIN)  		goto PsxCreat; @@ -1108,7 +1145,7 @@ static __u16 convert_disposition(int disposition)  			ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;  			break;  		default: -			cFYI(1, "unknown disposition %d", disposition); +			cifs_dbg(FYI, "unknown disposition %d\n", disposition);  			ofun =  SMBOPEN_OAPPEND; /* regular open */  	}  	return ofun; @@ -1129,7 +1166,7 @@ access_flags_to_smbopen_mode(const int access_flags)  }  int -SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, +SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,  	    const char *fileName, const int openDisposition,  	    const int access_flags, const int create_options, __u16 *netfid,  	    int *pOplock, FILE_ALL_INFO *pfile_info, @@ -1153,8 +1190,8 @@ OldOpenRetry:  	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); +		   cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), +				      fileName, PATH_MAX, nls_codepage, remap);  		name_len++;     /* trailing null */  		name_len *= 2;  	} else {                /* BB improve check for buffer overruns BB */ @@ -1191,15 +1228,15 @@ OldOpenRetry:  	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; +	inc_rfc1001_len(pSMB, 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); +			(struct smb_hdr *)pSMBr, &bytes_returned, 0); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);  	if (rc) { -		cFYI(1, "Error in Open = %d", rc); +		cifs_dbg(FYI, "Error in Open = %d\n", rc);  	} else {  	/* BB verify if wct == 15 */ @@ -1236,111 +1273,377 @@ OldOpenRetry:  }  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) +CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, +	  FILE_ALL_INFO *buf)  {  	int rc = -EACCES; -	OPEN_REQ *pSMB = NULL; -	OPEN_RSP *pSMBr = NULL; +	OPEN_REQ *req = NULL; +	OPEN_RSP *rsp = NULL;  	int bytes_returned;  	int name_len;  	__u16 count; +	struct cifs_sb_info *cifs_sb = oparms->cifs_sb; +	struct cifs_tcon *tcon = oparms->tcon; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; +	const struct nls_table *nls = cifs_sb->local_nls; +	int create_options = oparms->create_options; +	int desired_access = oparms->desired_access; +	int disposition = oparms->disposition; +	const char *path = oparms->path;  openRetry: -	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB, -		      (void **) &pSMBr); +	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, +		      (void **)&rsp);  	if (rc)  		return rc; -	pSMB->AndXCommand = 0xFF;	/* none */ +	/* no commands go after this */ +	req->AndXCommand = 0xFF; -	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 */ +	if (req->hdr.Flags2 & SMBFLG2_UNICODE) { +		/* account for one byte pad to word boundary */ +		count = 1; +		name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), +					      path, PATH_MAX, nls, remap); +		/* trailing null */ +		name_len++;  		name_len *= 2; -		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); +		req->NameLength = cpu_to_le16(name_len); +	} else { +		/* BB improve check for buffer overruns BB */ +		/* no pad */ +		count = 0; +		name_len = strnlen(path, PATH_MAX); +		/* trailing null */ +		name_len++; +		req->NameLength = cpu_to_le16(name_len); +		strncpy(req->fileName, path, name_len);  	} -	if (*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 (*oplock & REQ_OPLOCK) +		req->OpenFlags = cpu_to_le32(REQ_OPLOCK); +	else if (*oplock & REQ_BATCHOPLOCK) +		req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); + +	req->DesiredAccess = cpu_to_le32(desired_access); +	req->AllocationSize = 0; + +	/* +	 * Set file as system file if special file such as fifo and server +	 * expecting SFU style and no Unix extensions. +	 */  	if (create_options & CREATE_OPTION_SPECIAL) -		pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); +		req->FileAttributes = cpu_to_le32(ATTR_SYSTEM);  	else -		pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); +		req->FileAttributes = cpu_to_le32(ATTR_NORMAL); -	/* XP does not handle ATTR_POSIX_SEMANTICS */ -	/* but it helps speed up case sensitive checks for other -	servers such as Samba */ +	/* +	 * 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); +		req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);  	if (create_options & CREATE_OPTION_READONLY) -		pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY); +		req->FileAttributes |= cpu_to_le32(ATTR_READONLY); + +	req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); +	req->CreateDisposition = cpu_to_le32(disposition); +	req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); -	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; +	req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); +	req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;  	count += name_len; -	pSMB->hdr.smb_buf_length += count; +	inc_rfc1001_len(req, 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); +	req->ByteCount = cpu_to_le16(count); +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, +			 (struct smb_hdr *)rsp, &bytes_returned, 0); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);  	if (rc) { -		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_dbg(FYI, "Error in Open = %d\n", rc); +		cifs_buf_release(req); +		if (rc == -EAGAIN) +			goto openRetry; +		return rc; +	} + +	/* 1 byte no need to le_to_cpu */ +	*oplock = rsp->OplockLevel; +	/* cifs fid stays in le */ +	oparms->fid->netfid = rsp->Fid; + +	/* Let caller know file was created so we can set the mode. */ +	/* Do we care about the CreateAction in any other cases? */ +	if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) +		*oplock |= CIFS_CREATE_ACTION; + +	if (buf) { +		/* copy from CreationTime to Attributes */ +		memcpy((char *)buf, (char *)&rsp->CreationTime, 36); +		/* the file_info buf is endian converted by caller */ +		buf->AllocationSize = rsp->AllocationSize; +		buf->EndOfFile = rsp->EndOfFile; +		buf->NumberOfLinks = cpu_to_le32(1); +		buf->DeletePending = 0; +	} + +	cifs_buf_release(req); +	return rc; +} + +/* + * Discard any remaining data in the current SMB. To do this, we borrow the + * current bigbuf. + */ +static int +cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ +	unsigned int rfclen = get_rfc1002_length(server->smallbuf); +	int remaining = rfclen + 4 - server->total_read; +	struct cifs_readdata *rdata = mid->callback_data; + +	while (remaining > 0) { +		int length; + +		length = cifs_read_from_socket(server, server->bigbuf, +				min_t(unsigned int, remaining, +				    CIFSMaxBufSize + MAX_HEADER_SIZE(server))); +		if (length < 0) +			return length; +		server->total_read += length; +		remaining -= length; +	} + +	dequeue_mid(mid, rdata->result); +	return 0; +} + +int +cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ +	int length, len; +	unsigned int data_offset, data_len; +	struct cifs_readdata *rdata = mid->callback_data; +	char *buf = server->smallbuf; +	unsigned int buflen = get_rfc1002_length(buf) + 4; + +	cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n", +		 __func__, mid->mid, rdata->offset, rdata->bytes); + +	/* +	 * read the rest of READ_RSP header (sans Data array), or whatever we +	 * can if there's not enough data. At this point, we've read down to +	 * the Mid. +	 */ +	len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - +							HEADER_SIZE(server) + 1; + +	rdata->iov.iov_base = buf + HEADER_SIZE(server) - 1; +	rdata->iov.iov_len = len; + +	length = cifs_readv_from_socket(server, &rdata->iov, 1, len); +	if (length < 0) +		return length; +	server->total_read += length; + +	/* Was the SMB read successful? */ +	rdata->result = server->ops->map_error(buf, false); +	if (rdata->result != 0) { +		cifs_dbg(FYI, "%s: server returned error %d\n", +			 __func__, rdata->result); +		return cifs_readv_discard(server, mid); +	} + +	/* Is there enough to get to the rest of the READ_RSP header? */ +	if (server->total_read < server->vals->read_rsp_size) { +		cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n", +			 __func__, server->total_read, +			 server->vals->read_rsp_size); +		rdata->result = -EIO; +		return cifs_readv_discard(server, mid); +	} + +	data_offset = server->ops->read_data_offset(buf) + 4; +	if (data_offset < server->total_read) { +		/* +		 * win2k8 sometimes sends an offset of 0 when the read +		 * is beyond the EOF. Treat it as if the data starts just after +		 * the header. +		 */ +		cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", +			 __func__, data_offset); +		data_offset = server->total_read; +	} else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { +		/* data_offset is beyond the end of smallbuf */ +		cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", +			 __func__, data_offset); +		rdata->result = -EIO; +		return cifs_readv_discard(server, mid); +	} + +	cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n", +		 __func__, server->total_read, data_offset); + +	len = data_offset - server->total_read; +	if (len > 0) { +		/* read any junk before data into the rest of smallbuf */ +		rdata->iov.iov_base = buf + server->total_read; +		rdata->iov.iov_len = len; +		length = cifs_readv_from_socket(server, &rdata->iov, 1, len); +		if (length < 0) +			return length; +		server->total_read += length; +	} + +	/* set up first iov for signature check */ +	rdata->iov.iov_base = buf; +	rdata->iov.iov_len = server->total_read; +	cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", +		 rdata->iov.iov_base, rdata->iov.iov_len); + +	/* how much data is in the response? */ +	data_len = server->ops->read_data_length(buf); +	if (data_offset + data_len > buflen) { +		/* data_len is corrupt -- discard frame */ +		rdata->result = -EIO; +		return cifs_readv_discard(server, mid); +	} + +	length = rdata->read_into_pages(server, rdata, data_len); +	if (length < 0) +		return length; + +	server->total_read += length; +	rdata->bytes = length; + +	cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n", +		 server->total_read, buflen, data_len); + +	/* discard anything left over */ +	if (server->total_read < buflen) +		return cifs_readv_discard(server, mid); + +	dequeue_mid(mid, false); +	return length; +} + +static void +cifs_readv_callback(struct mid_q_entry *mid) +{ +	struct cifs_readdata *rdata = mid->callback_data; +	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server; +	struct smb_rqst rqst = { .rq_iov = &rdata->iov, +				 .rq_nvec = 1, +				 .rq_pages = rdata->pages, +				 .rq_npages = rdata->nr_pages, +				 .rq_pagesz = rdata->pagesz, +				 .rq_tailsz = rdata->tailsz }; + +	cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", +		 __func__, mid->mid, mid->mid_state, rdata->result, +		 rdata->bytes); + +	switch (mid->mid_state) { +	case MID_RESPONSE_RECEIVED: +		/* result already set, check signature */ +		if (server->sign) { +			int rc = 0; + +			rc = cifs_verify_signature(&rqst, server, +						  mid->sequence_number); +			if (rc) +				cifs_dbg(VFS, "SMB signature verification returned error = %d\n", +					 rc);  		} +		/* FIXME: should this be counted toward the initiating task? */ +		task_io_account_read(rdata->bytes); +		cifs_stats_bytes_read(tcon, rdata->bytes); +		break; +	case MID_REQUEST_SUBMITTED: +	case MID_RETRY_NEEDED: +		rdata->result = -EAGAIN; +		break; +	default: +		rdata->result = -EIO;  	} -	cifs_buf_release(pSMB); -	if (rc == -EAGAIN) -		goto openRetry; +	queue_work(cifsiod_wq, &rdata->work); +	DeleteMidQEntry(mid); +	add_credits(server, 1, 0); +} + +/* cifs_async_readv - send an async write, and set up mid to handle result */ +int +cifs_async_readv(struct cifs_readdata *rdata) +{ +	int rc; +	READ_REQ *smb = NULL; +	int wct; +	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); +	struct smb_rqst rqst = { .rq_iov = &rdata->iov, +				 .rq_nvec = 1 }; + +	cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", +		 __func__, rdata->offset, rdata->bytes); + +	if (tcon->ses->capabilities & CAP_LARGE_FILES) +		wct = 12; +	else { +		wct = 10; /* old style read */ +		if ((rdata->offset >> 32) > 0)  { +			/* can not handle this big offset for old */ +			return -EIO; +		} +	} + +	rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb); +	if (rc) +		return rc; + +	smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid); +	smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16)); + +	smb->AndXCommand = 0xFF;	/* none */ +	smb->Fid = rdata->cfile->fid.netfid; +	smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF); +	if (wct == 12) +		smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32); +	smb->Remaining = 0; +	smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF); +	smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16); +	if (wct == 12) +		smb->ByteCount = 0; +	else { +		/* old style read */ +		struct smb_com_readx_req *smbr = +			(struct smb_com_readx_req *)smb; +		smbr->ByteCount = 0; +	} + +	/* 4 for RFC1001 length + 1 for BCC */ +	rdata->iov.iov_base = smb; +	rdata->iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; + +	kref_get(&rdata->refcount); +	rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, +			     cifs_readv_callback, rdata, 0); + +	if (rc == 0) +		cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); +	else +		kref_put(&rdata->refcount, cifs_readdata_release); + +	cifs_small_buf_release(smb);  	return rc;  }  int -CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, -	    const unsigned int count, const __u64 lseek, unsigned int *nbytes, -	    char **buf, int *pbuf_type) +CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, +	    unsigned int *nbytes, char **buf, int *pbuf_type)  {  	int rc = -EACCES;  	READ_REQ *pSMB = NULL; @@ -1349,13 +1652,18 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,  	int wct;  	int resp_buf_type = 0;  	struct kvec iov[1]; +	__u32 pid = io_parms->pid; +	__u16 netfid = io_parms->netfid; +	__u64 offset = io_parms->offset; +	struct cifs_tcon *tcon = io_parms->tcon; +	unsigned int count = io_parms->length; -	cFYI(1, "Reading %d bytes on fid %d", count, netfid); +	cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid);  	if (tcon->ses->capabilities & CAP_LARGE_FILES)  		wct = 12;  	else {  		wct = 10; /* old style read */ -		if ((lseek >> 32) > 0)  { +		if ((offset >> 32) > 0)  {  			/* can not handle this big offset for old */  			return -EIO;  		} @@ -1366,15 +1674,18 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,  	if (rc)  		return rc; +	pSMB->hdr.Pid = cpu_to_le16((__u16)pid); +	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); +  	/* tcon and ses pointer are checked in smb_init */  	if (tcon->ses->server == NULL)  		return -ECONNABORTED;  	pSMB->AndXCommand = 0xFF;       /* none */  	pSMB->Fid = netfid; -	pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); +	pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);  	if (wct == 12) -		pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); +		pSMB->OffsetHigh = cpu_to_le32(offset >> 32);  	pSMB->Remaining = 0;  	pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); @@ -1389,13 +1700,13 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,  	}  	iov[0].iov_base = (char *)pSMB; -	iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; +	iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;  	rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, -			 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR); -	cifs_stats_inc(&tcon->num_reads); +			 &resp_buf_type, CIFS_LOG_ERROR); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);  	pSMBr = (READ_RSP *)iov[0].iov_base;  	if (rc) { -		cERROR(1, "Send error in read = %d", rc); +		cifs_dbg(VFS, "Send error in read = %d\n", rc);  	} else {  		int data_length = le16_to_cpu(pSMBr->DataLengthHigh);  		data_length = data_length << 16; @@ -1405,7 +1716,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,  		/*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", +			cifs_dbg(FYI, "bad length %d for count %d\n",  				 data_length, count);  			rc = -EIO;  			*nbytes = 0; @@ -1413,7 +1724,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,  			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); +				cifs_dbg(VFS, "Faulting on read rc = %d\n",rc);  				rc = -EFAULT;  			}*/ /* can not use copy_to_user when using page cache*/  			if (*buf) @@ -1443,9 +1754,8 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,  int -CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, -	     const int netfid, const unsigned int count, -	     const __u64 offset, unsigned int *nbytes, const char *buf, +CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, +	     unsigned int *nbytes, const char *buf,  	     const char __user *ubuf, const int long_op)  {  	int rc = -EACCES; @@ -1454,10 +1764,15 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned, wct;  	__u32 bytes_sent;  	__u16 byte_count; +	__u32 pid = io_parms->pid; +	__u16 netfid = io_parms->netfid; +	__u64 offset = io_parms->offset; +	struct cifs_tcon *tcon = io_parms->tcon; +	unsigned int count = io_parms->length;  	*nbytes = 0; -	/* cFYI(1, "write at %lld %d bytes", offset, count);*/ +	/* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/  	if (tcon->ses == NULL)  		return -ECONNABORTED; @@ -1475,6 +1790,10 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,  		      (void **) &pSMBr);  	if (rc)  		return rc; + +	pSMB->hdr.Pid = cpu_to_le16((__u16)pid); +	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); +  	/* tcon and ses pointer are checked in smb_init */  	if (tcon->ses->server == NULL)  		return -ECONNABORTED; @@ -1523,7 +1842,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,  	pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);  	pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	if (wct == 14)  		pSMB->ByteCount = cpu_to_le16(byte_count); @@ -1536,9 +1855,9 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, long_op); -	cifs_stats_inc(&tcon->num_writes); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);  	if (rc) { -		cFYI(1, "Send error in write = %d", rc); +		cifs_dbg(FYI, "Send error in write = %d\n", rc);  	} else {  		*nbytes = le16_to_cpu(pSMBr->CountHigh);  		*nbytes = (*nbytes) << 16; @@ -1561,21 +1880,251 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,  	return rc;  } +void +cifs_writedata_release(struct kref *refcount) +{ +	struct cifs_writedata *wdata = container_of(refcount, +					struct cifs_writedata, refcount); + +	if (wdata->cfile) +		cifsFileInfo_put(wdata->cfile); + +	kfree(wdata); +} + +/* + * Write failed with a retryable error. Resend the write request. It's also + * possible that the page was redirtied so re-clean the page. + */ +static void +cifs_writev_requeue(struct cifs_writedata *wdata) +{ +	int i, rc; +	struct inode *inode = wdata->cfile->dentry->d_inode; +	struct TCP_Server_Info *server; + +	for (i = 0; i < wdata->nr_pages; i++) { +		lock_page(wdata->pages[i]); +		clear_page_dirty_for_io(wdata->pages[i]); +	} + +	do { +		server = tlink_tcon(wdata->cfile->tlink)->ses->server; +		rc = server->ops->async_writev(wdata, cifs_writedata_release); +	} while (rc == -EAGAIN); + +	for (i = 0; i < wdata->nr_pages; i++) { +		unlock_page(wdata->pages[i]); +		if (rc != 0) { +			SetPageError(wdata->pages[i]); +			end_page_writeback(wdata->pages[i]); +			page_cache_release(wdata->pages[i]); +		} +	} + +	mapping_set_error(inode->i_mapping, rc); +	kref_put(&wdata->refcount, cifs_writedata_release); +} + +void +cifs_writev_complete(struct work_struct *work) +{ +	struct cifs_writedata *wdata = container_of(work, +						struct cifs_writedata, work); +	struct inode *inode = wdata->cfile->dentry->d_inode; +	int i = 0; + +	if (wdata->result == 0) { +		spin_lock(&inode->i_lock); +		cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); +		spin_unlock(&inode->i_lock); +		cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), +					 wdata->bytes); +	} else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) +		return cifs_writev_requeue(wdata); + +	for (i = 0; i < wdata->nr_pages; i++) { +		struct page *page = wdata->pages[i]; +		if (wdata->result == -EAGAIN) +			__set_page_dirty_nobuffers(page); +		else if (wdata->result < 0) +			SetPageError(page); +		end_page_writeback(page); +		page_cache_release(page); +	} +	if (wdata->result != -EAGAIN) +		mapping_set_error(inode->i_mapping, wdata->result); +	kref_put(&wdata->refcount, cifs_writedata_release); +} + +struct cifs_writedata * +cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) +{ +	struct cifs_writedata *wdata; + +	/* writedata + number of page pointers */ +	wdata = kzalloc(sizeof(*wdata) + +			sizeof(struct page *) * nr_pages, GFP_NOFS); +	if (wdata != NULL) { +		kref_init(&wdata->refcount); +		INIT_LIST_HEAD(&wdata->list); +		init_completion(&wdata->done); +		INIT_WORK(&wdata->work, complete); +	} +	return wdata; +} + +/* + * Check the mid_state and signature on received buffer (if any), and queue the + * workqueue completion task. + */ +static void +cifs_writev_callback(struct mid_q_entry *mid) +{ +	struct cifs_writedata *wdata = mid->callback_data; +	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); +	unsigned int written; +	WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; + +	switch (mid->mid_state) { +	case MID_RESPONSE_RECEIVED: +		wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); +		if (wdata->result != 0) +			break; + +		written = le16_to_cpu(smb->CountHigh); +		written <<= 16; +		written += le16_to_cpu(smb->Count); +		/* +		 * Mask off high 16 bits when bytes written as returned +		 * by the server is greater than bytes requested by the +		 * client. OS/2 servers are known to set incorrect +		 * CountHigh values. +		 */ +		if (written > wdata->bytes) +			written &= 0xFFFF; + +		if (written < wdata->bytes) +			wdata->result = -ENOSPC; +		else +			wdata->bytes = written; +		break; +	case MID_REQUEST_SUBMITTED: +	case MID_RETRY_NEEDED: +		wdata->result = -EAGAIN; +		break; +	default: +		wdata->result = -EIO; +		break; +	} + +	queue_work(cifsiod_wq, &wdata->work); +	DeleteMidQEntry(mid); +	add_credits(tcon->ses->server, 1, 0); +} + +/* cifs_async_writev - send an async write, and set up mid to handle result */ +int +cifs_async_writev(struct cifs_writedata *wdata, +		  void (*release)(struct kref *kref)) +{ +	int rc = -EACCES; +	WRITE_REQ *smb = NULL; +	int wct; +	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); +	struct kvec iov; +	struct smb_rqst rqst = { }; + +	if (tcon->ses->capabilities & CAP_LARGE_FILES) { +		wct = 14; +	} else { +		wct = 12; +		if (wdata->offset >> 32 > 0) { +			/* can not handle big offset for old srv */ +			return -EIO; +		} +	} + +	rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb); +	if (rc) +		goto async_writev_out; + +	smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid); +	smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16)); + +	smb->AndXCommand = 0xFF;	/* none */ +	smb->Fid = wdata->cfile->fid.netfid; +	smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF); +	if (wct == 14) +		smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32); +	smb->Reserved = 0xFFFFFFFF; +	smb->WriteMode = 0; +	smb->Remaining = 0; + +	smb->DataOffset = +	    cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + +	/* 4 for RFC1001 length + 1 for BCC */ +	iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1; +	iov.iov_base = smb; + +	rqst.rq_iov = &iov; +	rqst.rq_nvec = 1; +	rqst.rq_pages = wdata->pages; +	rqst.rq_npages = wdata->nr_pages; +	rqst.rq_pagesz = wdata->pagesz; +	rqst.rq_tailsz = wdata->tailsz; + +	cifs_dbg(FYI, "async write at %llu %u bytes\n", +		 wdata->offset, wdata->bytes); + +	smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF); +	smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16); + +	if (wct == 14) { +		inc_rfc1001_len(&smb->hdr, wdata->bytes + 1); +		put_bcc(wdata->bytes + 1, &smb->hdr); +	} else { +		/* wct == 12 */ +		struct smb_com_writex_req *smbw = +				(struct smb_com_writex_req *)smb; +		inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); +		put_bcc(wdata->bytes + 5, &smbw->hdr); +		iov.iov_len += 4; /* pad bigger by four bytes */ +	} + +	kref_get(&wdata->refcount); +	rc = cifs_call_async(tcon->ses->server, &rqst, NULL, +				cifs_writev_callback, wdata, 0); + +	if (rc == 0) +		cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); +	else +		kref_put(&wdata->refcount, release); + +async_writev_out: +	cifs_small_buf_release(smb); +	return rc; +} +  int -CIFSSMBWrite2(const 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) +CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, +	      unsigned int *nbytes, struct kvec *iov, int n_vec)  {  	int rc = -EACCES;  	WRITE_REQ *pSMB = NULL;  	int wct;  	int smb_hdr_len;  	int resp_buf_type = 0; +	__u32 pid = io_parms->pid; +	__u16 netfid = io_parms->netfid; +	__u64 offset = io_parms->offset; +	struct cifs_tcon *tcon = io_parms->tcon; +	unsigned int count = io_parms->length;  	*nbytes = 0; -	cFYI(1, "write2 at %lld %d bytes", (long long)offset, count); +	cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count);  	if (tcon->ses->capabilities & CAP_LARGE_FILES) {  		wct = 14; @@ -1589,6 +2138,10 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,  	rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);  	if (rc)  		return rc; + +	pSMB->hdr.Pid = cpu_to_le16((__u16)pid); +	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); +  	/* tcon and ses pointer are checked in smb_init */  	if (tcon->ses->server == NULL)  		return -ECONNABORTED; @@ -1607,11 +2160,12 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,  	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 */ +	/* header + 1 byte pad */ +	smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1;  	if (wct == 14) -		pSMB->hdr.smb_buf_length += count+1; +		inc_rfc1001_len(pSMB, count + 1);  	else /* wct == 12 */ -		pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ +		inc_rfc1001_len(pSMB, count + 5); /* smb data starts later */  	if (wct == 14)  		pSMB->ByteCount = cpu_to_le16(count + 1);  	else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { @@ -1626,11 +2180,10 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,  		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); +	rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);  	if (rc) { -		cFYI(1, "Send error Write2 = %d", rc); +		cifs_dbg(FYI, "Send error Write2 = %d\n", rc);  	} else if (resp_buf_type == 0) {  		/* presumably this can not happen, but best to be safe */  		rc = -EIO; @@ -1661,31 +2214,75 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,  	return rc;  } +int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, +	       const __u16 netfid, const __u8 lock_type, const __u32 num_unlock, +	       const __u32 num_lock, LOCKING_ANDX_RANGE *buf) +{ +	int rc = 0; +	LOCK_REQ *pSMB = NULL; +	struct kvec iov[2]; +	int resp_buf_type; +	__u16 count; + +	cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n", +		 num_lock, num_unlock); + +	rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); +	if (rc) +		return rc; + +	pSMB->Timeout = 0; +	pSMB->NumberOfLocks = cpu_to_le16(num_lock); +	pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock); +	pSMB->LockType = lock_type; +	pSMB->AndXCommand = 0xFF; /* none */ +	pSMB->Fid = netfid; /* netfid stays le */ + +	count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); +	inc_rfc1001_len(pSMB, count); +	pSMB->ByteCount = cpu_to_le16(count); + +	iov[0].iov_base = (char *)pSMB; +	iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 - +			 (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); +	iov[1].iov_base = (char *)buf; +	iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + +	cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); +	rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP); +	if (rc) +		cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); + +	return rc; +}  int -CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, -	    const __u16 smb_file_id, const __u64 len, +CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, +	    const __u16 smb_file_id, const __u32 netpid, const __u64 len,  	    const __u64 offset, const __u32 numUnlock, -	    const __u32 numLock, const __u8 lockType, const bool waitFlag) +	    const __u32 numLock, const __u8 lockType, +	    const bool waitFlag, const __u8 oplock_level)  {  	int rc = 0;  	LOCK_REQ *pSMB = NULL;  /*	LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */  	int bytes_returned; -	int timeout = 0; +	int flags = 0;  	__u16 count; -	cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock); +	cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n", +		 (int)waitFlag, numLock);  	rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);  	if (rc)  		return rc;  	if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { -		timeout = CIFS_ASYNC_OP; /* no response expected */ +		/* no response expected */ +		flags = CIFS_ASYNC_OP | CIFS_OBREAK_OP;  		pSMB->Timeout = 0;  	} else if (waitFlag) { -		timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ +		flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */  		pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */  	} else {  		pSMB->Timeout = 0; @@ -1694,11 +2291,12 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,  	pSMB->NumberOfLocks = cpu_to_le16(numLock);  	pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);  	pSMB->LockType = lockType; +	pSMB->OplockLevel = oplock_level;  	pSMB->AndXCommand = 0xFF;	/* none */  	pSMB->Fid = smb_file_id; /* netfid stays le */  	if ((numLock != 0) || (numUnlock != 0)) { -		pSMB->Locks[0].Pid = cpu_to_le16(current->tgid); +		pSMB->Locks[0].Pid = cpu_to_le16(netpid);  		/* BB where to store pid high? */  		pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);  		pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); @@ -1709,7 +2307,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,  		/* oplock break */  		count = 0;  	} -	pSMB->hdr.smb_buf_length += count; +	inc_rfc1001_len(pSMB, count);  	pSMB->ByteCount = cpu_to_le16(count);  	if (waitFlag) { @@ -1717,13 +2315,12 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,  			(struct smb_hdr *) pSMB, &bytes_returned);  		cifs_small_buf_release(pSMB);  	} else { -		rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB, -				      timeout); +		rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags);  		/* SMB buffer freed by function above */  	} -	cifs_stats_inc(&tcon->num_locks); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);  	if (rc) -		cFYI(1, "Send error in Lock = %d", rc); +		cifs_dbg(FYI, "Send error in Lock = %d\n", rc);  	/* Note: On -EAGAIN error only caller can retry on handle based calls  	since file handle passed in no longer valid */ @@ -1731,8 +2328,9 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,  }  int -CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, -		const __u16 smb_file_id, const int get_flag, const __u64 len, +CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, +		const __u16 smb_file_id, const __u32 netpid, +		const loff_t start_offset, const __u64 len,  		struct file_lock *pLockData, const __u16 lock_type,  		const bool waitFlag)  { @@ -1746,10 +2344,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,  	__u16 params, param_offset, offset, byte_count, count;  	struct kvec iov[1]; -	cFYI(1, "Posix Lock"); - -	if (pLockData == NULL) -		return -EINVAL; +	cifs_dbg(FYI, "Posix Lock\n");  	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); @@ -1771,7 +2366,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,  	pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */  	pSMB->SetupCount = 1;  	pSMB->Reserved3 = 0; -	if (get_flag) +	if (pLockData)  		pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);  	else  		pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); @@ -1792,22 +2387,22 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,  	} else  		pSMB->Timeout = 0; -	parm_data->pid = cpu_to_le32(current->tgid); -	parm_data->start = cpu_to_le64(pLockData->fl_start); +	parm_data->pid = cpu_to_le32(netpid); +	parm_data->start = cpu_to_le64(start_offset);  	parm_data->length = cpu_to_le64(len);  /* normalize negative numbers */  	pSMB->DataOffset = cpu_to_le16(offset);  	pSMB->Fid = smb_file_id;  	pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	if (waitFlag) {  		rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,  			(struct smb_hdr *) pSMBr, &bytes_returned);  	} else {  		iov[0].iov_base = (char *)pSMB; -		iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; +		iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;  		rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,  				&resp_buf_type, timeout);  		pSMB = NULL; /* request buf already freed by SendReceive2. Do @@ -1816,14 +2411,14 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,  	}  	if (rc) { -		cFYI(1, "Send error in Posix Lock = %d", rc); -	} else if (get_flag) { +		cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc); +	} else if (pLockData) {  		/* lock structure can be returned on get */  		__u16 data_offset;  		__u16 data_count;  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) { +		if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) {  			rc = -EIO;      /* bad smb */  			goto plk_err_exit;  		} @@ -1845,10 +2440,10 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,  					__constant_cpu_to_le16(CIFS_WRLCK))  				pLockData->fl_type = F_WRLCK; -			pLockData->fl_start = parm_data->start; -			pLockData->fl_end = parm_data->start + -						parm_data->length - 1; -			pLockData->fl_pid = parm_data->pid; +			pLockData->fl_start = le64_to_cpu(parm_data->start); +			pLockData->fl_end = pLockData->fl_start + +					le64_to_cpu(parm_data->length) - 1; +			pLockData->fl_pid = le32_to_cpu(parm_data->pid);  		}  	} @@ -1869,11 +2464,11 @@ plk_err_exit:  int -CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) +CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)  {  	int rc = 0;  	CLOSE_REQ *pSMB = NULL; -	cFYI(1, "In CIFSSMBClose"); +	cifs_dbg(FYI, "In CIFSSMBClose\n");  /* do not retry on dead session on close */  	rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); @@ -1885,12 +2480,12 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)  	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); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); +	cifs_stats_inc(&tcon->stats.cifs_stats.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); +			cifs_dbg(VFS, "Send error in Close = %d\n", rc);  		}  	} @@ -1902,11 +2497,11 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)  }  int -CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id) +CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)  {  	int rc = 0;  	FLUSH_REQ *pSMB = NULL; -	cFYI(1, "In CIFSSMBFlush"); +	cifs_dbg(FYI, "In CIFSSMBFlush\n");  	rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);  	if (rc) @@ -1914,18 +2509,18 @@ CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)  	pSMB->FileID = (__u16) smb_file_id;  	pSMB->ByteCount = 0; -	rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); -	cifs_stats_inc(&tcon->num_flushes); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes);  	if (rc) -		cERROR(1, "Send error in Flush = %d", rc); +		cifs_dbg(VFS, "Send error in Flush = %d\n", rc);  	return rc;  }  int -CIFSSMBRename(const int xid, struct cifsTconInfo *tcon, -	      const char *fromName, const char *toName, -	      const struct nls_table *nls_codepage, int remap) +CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, +	      const char *from_name, const char *to_name, +	      struct cifs_sb_info *cifs_sb)  {  	int rc = 0;  	RENAME_REQ *pSMB = NULL; @@ -1933,8 +2528,9 @@ CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned;  	int name_len, name_len2;  	__u16 count; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; -	cFYI(1, "In CIFSSMBRename"); +	cifs_dbg(FYI, "In CIFSSMBRename\n");  renameRetry:  	rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -1947,40 +2543,41 @@ renameRetry:  			ATTR_DIRECTORY);  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, -				     PATH_MAX, nls_codepage, remap); +		name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, +					      from_name, PATH_MAX, +					      cifs_sb->local_nls, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  		pSMB->OldFileName[name_len] = 0x04;	/* pad */  	/* protocol requires ASCII signature byte on Unicode string */  		pSMB->OldFileName[name_len + 1] = 0x00;  		name_len2 = -		    cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], -				     toName, PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], +				       to_name, PATH_MAX, cifs_sb->local_nls, +				       remap);  		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;  		name_len2 *= 2;	/* convert to bytes */  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(fromName, PATH_MAX); +		name_len = strnlen(from_name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->OldFileName, fromName, name_len); -		name_len2 = strnlen(toName, PATH_MAX); +		strncpy(pSMB->OldFileName, from_name, name_len); +		name_len2 = strnlen(to_name, PATH_MAX);  		name_len2++;	/* trailing null */  		pSMB->OldFileName[name_len] = 0x04;  /* 2nd buffer format */ -		strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); +		strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);  		name_len2++;	/* trailing null */  		name_len2++;	/* signature byte */  	}  	count = 1 /* 1st signature byte */  + name_len + name_len2; -	pSMB->hdr.smb_buf_length += count; +	inc_rfc1001_len(pSMB, count);  	pSMB->ByteCount = cpu_to_le16(count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_renames); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_renames);  	if (rc) -		cFYI(1, "Send error in rename = %d", rc); +		cifs_dbg(FYI, "Send error in rename = %d\n", rc);  	cifs_buf_release(pSMB); @@ -1990,7 +2587,7 @@ renameRetry:  	return rc;  } -int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, +int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,  		int netfid, const char *target_name,  		const struct nls_table *nls_codepage, int remap)  { @@ -2004,7 +2601,7 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,  	int len_of_str;  	__u16 params, param_offset, offset, count, byte_count; -	cFYI(1, "Rename to File by handle"); +	cifs_dbg(FYI, "Rename to File by handle\n");  	rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,  			(void **) &pSMBr);  	if (rc) @@ -2037,10 +2634,12 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,  	/* unicode only call */  	if (target_name == NULL) {  		sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); -		len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, +		len_of_str = +			cifsConvertToUTF16((__le16 *)rename_info->target_name,  					dummy_string, 24, nls_codepage, remap);  	} else { -		len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, +		len_of_str = +			cifsConvertToUTF16((__le16 *)rename_info->target_name,  					target_name, PATH_MAX, nls_codepage,  					remap);  	} @@ -2053,13 +2652,14 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,  	pSMB->InformationLevel =  		cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&pTcon->num_t2renames); +	cifs_stats_inc(&pTcon->stats.cifs_stats.num_t2renames);  	if (rc) -		cFYI(1, "Send error in Rename (by file handle) = %d", rc); +		cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n", +			 rc);  	cifs_buf_release(pSMB); @@ -2070,9 +2670,9 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,  }  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) +CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon, +	    const char *fromName, const __u16 target_tid, const char *toName, +	    const int flags, const struct nls_table *nls_codepage, int remap)  {  	int rc = 0;  	COPY_REQ *pSMB = NULL; @@ -2081,7 +2681,7 @@ CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,  	int name_len, name_len2;  	__u16 count; -	cFYI(1, "In CIFSSMBCopy"); +	cifs_dbg(FYI, "In CIFSSMBCopy\n");  copyRetry:  	rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,  			(void **) &pSMBr); @@ -2094,17 +2694,17 @@ copyRetry:  	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 = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, +					      fromName, PATH_MAX, nls_codepage, +					      remap);  		name_len++;     /* trailing null */  		name_len *= 2;  		pSMB->OldFileName[name_len] = 0x04;     /* pad */  		/* protocol requires ASCII signature byte on Unicode string */  		pSMB->OldFileName[name_len + 1] = 0x00;  		name_len2 = -		    cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], -				toName, PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], +				       toName, PATH_MAX, nls_codepage, remap);  		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;  		name_len2 *= 2; /* convert to bytes */  	} else { 	/* BB improve the check for buffer overruns BB */ @@ -2120,14 +2720,14 @@ copyRetry:  	}  	count = 1 /* 1st signature byte */  + name_len + name_len2; -	pSMB->hdr.smb_buf_length += count; +	inc_rfc1001_len(pSMB, count);  	pSMB->ByteCount = cpu_to_le16(count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  		(struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in copy = %d with %d files copied", -			rc, le16_to_cpu(pSMBr->CopyCount)); +		cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n", +			 rc, le16_to_cpu(pSMBr->CopyCount));  	}  	cifs_buf_release(pSMB); @@ -2138,7 +2738,7 @@ copyRetry:  }  int -CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon, +CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon,  		      const char *fromName, const char *toName,  		      const struct nls_table *nls_codepage)  { @@ -2151,7 +2751,7 @@ CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned = 0;  	__u16 params, param_offset, offset, byte_count; -	cFYI(1, "In Symlink Unix style"); +	cifs_dbg(FYI, "In Symlink Unix style\n");  createSymLinkRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -2160,9 +2760,9 @@ createSymLinkRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX -				  /* find define for this maxpathcomponent */ -				  , nls_codepage); +		    cifs_strtoUTF16((__le16 *) pSMB->FileName, fromName, +				    /* find define for this maxpathcomponent */ +				    PATH_MAX, nls_codepage);  		name_len++;	/* trailing null */  		name_len *= 2; @@ -2184,9 +2784,9 @@ createSymLinkRetry:  	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); +		    cifs_strtoUTF16((__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 */ @@ -2210,13 +2810,14 @@ createSymLinkRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_symlinks); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks);  	if (rc) -		cFYI(1, "Send error in SetPathInfo create symlink = %d", rc); +		cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n", +			 rc);  	cifs_buf_release(pSMB); @@ -2227,7 +2828,7 @@ createSymLinkRetry:  }  int -CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon, +CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,  		       const char *fromName, const char *toName,  		       const struct nls_table *nls_codepage, int remap)  { @@ -2240,7 +2841,7 @@ CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned = 0;  	__u16 params, param_offset, offset, byte_count; -	cFYI(1, "In Create Hard link Unix style"); +	cifs_dbg(FYI, "In Create Hard link Unix style\n");  createHardLinkRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -2248,8 +2849,8 @@ createHardLinkRetry:  		return rc;  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName, -					    PATH_MAX, nls_codepage, remap); +		name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName, +					      PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2; @@ -2271,8 +2872,8 @@ createHardLinkRetry:  	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); +		    cifsConvertToUTF16((__le16 *) data_offset, fromName, +				       PATH_MAX, nls_codepage, remap);  		name_len_target++;	/* trailing null */  		name_len_target *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -2296,13 +2897,14 @@ createHardLinkRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_hardlinks); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);  	if (rc) -		cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc); +		cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n", +			 rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -2312,9 +2914,9 @@ createHardLinkRetry:  }  int -CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon, -		   const char *fromName, const char *toName, -		   const struct nls_table *nls_codepage, int remap) +CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, +		   const char *from_name, const char *to_name, +		   struct cifs_sb_info *cifs_sb)  {  	int rc = 0;  	NT_RENAME_REQ *pSMB = NULL; @@ -2322,8 +2924,9 @@ CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned;  	int name_len, name_len2;  	__u16 count; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; -	cFYI(1, "In CIFSCreateHardLink"); +	cifs_dbg(FYI, "In CIFSCreateHardLink\n");  winCreateHardLinkRetry:  	rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB, @@ -2341,8 +2944,8 @@ winCreateHardLinkRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name, +				       PATH_MAX, cifs_sb->local_nls, remap);  		name_len++;	/* trailing null */  		name_len *= 2; @@ -2350,31 +2953,32 @@ winCreateHardLinkRetry:  		pSMB->OldFileName[name_len] = 0x04;  		pSMB->OldFileName[name_len + 1] = 0x00; /* pad */  		name_len2 = -		    cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], -				     toName, PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], +				       to_name, PATH_MAX, cifs_sb->local_nls, +				       remap);  		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;  		name_len2 *= 2;	/* convert to bytes */  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(fromName, PATH_MAX); +		name_len = strnlen(from_name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->OldFileName, fromName, name_len); -		name_len2 = strnlen(toName, PATH_MAX); +		strncpy(pSMB->OldFileName, from_name, name_len); +		name_len2 = strnlen(to_name, PATH_MAX);  		name_len2++;	/* trailing null */  		pSMB->OldFileName[name_len] = 0x04;	/* 2nd buffer format */ -		strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2); +		strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);  		name_len2++;	/* trailing null */  		name_len2++;	/* signature byte */  	}  	count = 1 /* string type byte */  + name_len + name_len2; -	pSMB->hdr.smb_buf_length += count; +	inc_rfc1001_len(pSMB, count);  	pSMB->ByteCount = cpu_to_le16(count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_hardlinks); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);  	if (rc) -		cFYI(1, "Send error in hard link (NT rename) = %d", rc); +		cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -2384,7 +2988,7 @@ winCreateHardLinkRetry:  }  int -CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon, +CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,  			const unsigned char *searchName, char **symlinkinfo,  			const struct nls_table *nls_codepage)  { @@ -2397,7 +3001,7 @@ CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,  	__u16 params, byte_count;  	char *data_start; -	cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName); +	cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName);  querySymLinkRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, @@ -2407,8 +3011,8 @@ querySymLinkRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifs_strtoUCS((__le16 *) pSMB->FileName, searchName, -				  PATH_MAX, nls_codepage); +			cifs_strtoUTF16((__le16 *) pSMB->FileName, searchName, +					PATH_MAX, nls_codepage);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -2438,19 +3042,19 @@ querySymLinkRetry:  	pSMB->ParameterCount = pSMB->TotalParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QuerySymLinkInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc);  	} else {  		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr);  		/* BB also check enough total bytes returned */ -		if (rc || (pSMBr->ByteCount < 2)) +		if (rc || get_bcc(&pSMBr->hdr) < 2)  			rc = -EIO;  		else {  			bool is_unicode; @@ -2465,8 +3069,8 @@ querySymLinkRetry:  				is_unicode = false;  			/* BB FIXME investigate remapping reserved chars here */ -			*symlinkinfo = cifs_strndup_from_ucs(data_start, count, -						    is_unicode, nls_codepage); +			*symlinkinfo = cifs_strndup_from_utf16(data_start, +					count, is_unicode, nls_codepage);  			if (!*symlinkinfo)  				rc = -ENOMEM;  		} @@ -2477,108 +3081,34 @@ 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) past smb end %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; -} - +/* + *	Recent Windows versions now create symlinks more frequently + *	and they use the "reparse point" mechanism below.  We can of course + *	do symlinks nicely to Samba and other servers which support the + *	CIFS Unix Extensions and we can also do SFU symlinks and "client only" + *	"MF" symlinks optionally, but for recent Windows we really need to + *	reenable the code below and fix the cifs_symlink callers to handle this. + *	In the interim this code has been moved to its own config option so + *	it is not compiled in by default until callers fixed up and more tested. + */  int -CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, -			const unsigned char *searchName, -			char *symlinkinfo, const int buflen, __u16 fid, -			const struct nls_table *nls_codepage) +CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, +		    __u16 fid, char **symlinkinfo, +		    const struct nls_table *nls_codepage)  {  	int rc = 0;  	int bytes_returned;  	struct smb_com_transaction_ioctl_req *pSMB;  	struct smb_com_transaction_ioctl_rsp *pSMBr; +	bool is_unicode; +	unsigned int sub_len; +	char *sub_start; +	struct reparse_symlink_data *reparse_buf; +	struct reparse_posix_data *posix_buf; +	__u32 data_offset, data_count; +	char *end_of_smb; -	cFYI(1, "In Windows reparse style QueryLink for path %s", searchName); +	cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);  	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,  		      (void **) &pSMBr);  	if (rc) @@ -2588,8 +3118,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,  	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->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);  	pSMB->MaxSetupCount = 4;  	pSMB->Reserved = 0;  	pSMB->ParameterOffset = 0; @@ -2607,68 +3136,137 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,  	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)) { +		cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); +		goto qreparse_out; +	} + +	data_offset = le32_to_cpu(pSMBr->DataOffset); +	data_count = le32_to_cpu(pSMBr->DataCount); +	if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {  		/* BB also check enough total bytes returned */ -			rc = -EIO;	/* bad smb */ +		rc = -EIO;	/* bad smb */ +		goto qreparse_out; +	} +	if (!data_count || (data_count > 2048)) { +		rc = -EIO; +		cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); +		goto qreparse_out; +	} +	end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; +	reparse_buf = (struct reparse_symlink_data *) +				((char *)&pSMBr->hdr.Protocol + data_offset); +	if ((char *)reparse_buf >= end_of_smb) { +		rc = -EIO; +		goto qreparse_out; +	} +	if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { +		cifs_dbg(FYI, "NFS style reparse tag\n"); +		posix_buf =  (struct reparse_posix_data *)reparse_buf; + +		if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { +			cifs_dbg(FYI, "unsupported file type 0x%llx\n", +				 le64_to_cpu(posix_buf->InodeType)); +			rc = -EOPNOTSUPP;  			goto qreparse_out;  		} -		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) { -				cifs_from_ucs2(symlinkinfo, (__le16 *) -						(reparse_buf->LinkNamesBuf + -						reparse_buf->TargetNameOffset), -						buflen, -						reparse_buf->TargetNameLen, -						nls_codepage, 0); -			} else { /* ASCII names */ -				strncpy(symlinkinfo, -					reparse_buf->LinkNamesBuf + -					reparse_buf->TargetNameOffset, -					min_t(const int, buflen, -					   reparse_buf->TargetNameLen)); -			} -		} else { +		is_unicode = true; +		sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); +		if (posix_buf->PathBuffer + sub_len > end_of_smb) { +			cifs_dbg(FYI, "reparse buf beyond SMB\n");  			rc = -EIO; -			cFYI(1, "Invalid return data count on " -				 "get reparse info ioctl"); +			goto qreparse_out;  		} -		symlinkinfo[buflen] = 0; /* just in case so the caller -					does not go off the end of the buffer */ -		cFYI(1, "readlink result - %s", symlinkinfo); +		*symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, +				sub_len, is_unicode, nls_codepage); +		goto qreparse_out; +	} else if (reparse_buf->ReparseTag != +			cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { +		rc = -EOPNOTSUPP; +		goto qreparse_out;  	} +	/* Reparse tag is NTFS symlink */ +	sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + +				reparse_buf->PathBuffer; +	sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); +	if (sub_start + sub_len > end_of_smb) { +		cifs_dbg(FYI, "reparse buf beyond SMB\n"); +		rc = -EIO; +		goto qreparse_out; +	} +	if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) +		is_unicode = true; +	else +		is_unicode = false; + +	/* BB FIXME investigate remapping reserved chars here */ +	*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, +					       nls_codepage); +	if (!*symlinkinfo) +		rc = -ENOMEM;  qreparse_out:  	cifs_buf_release(pSMB); -	/* Note: On -EAGAIN error only caller can retry on handle based calls -		since file handle passed in no longer valid */ +	/* +	 * Note: On -EAGAIN error only caller can retry on handle based calls +	 * since file handle passed in no longer valid. +	 */ +	return rc; +} + +int +CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		    __u16 fid) +{ +	int rc = 0; +	int bytes_returned; +	struct smb_com_transaction_compr_ioctl_req *pSMB; +	struct smb_com_transaction_ioctl_rsp *pSMBr; + +	cifs_dbg(FYI, "Set compression for %u\n", fid); +	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, +		      (void **) &pSMBr); +	if (rc) +		return rc; + +	pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); +	pSMB->TotalParameterCount = 0; +	pSMB->TotalDataCount = __constant_cpu_to_le32(2); +	pSMB->MaxParameterCount = 0; +	pSMB->MaxDataCount = 0; +	pSMB->MaxSetupCount = 4; +	pSMB->Reserved = 0; +	pSMB->ParameterOffset = 0; +	pSMB->DataCount = __constant_cpu_to_le32(2); +	pSMB->DataOffset = +		cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, +				compression_state) - 4);  /* 84 */ +	pSMB->SetupCount = 4; +	pSMB->SubCommand = __constant_cpu_to_le16(NT_TRANSACT_IOCTL); +	pSMB->ParameterCount = 0; +	pSMB->FunctionCode = __constant_cpu_to_le32(FSCTL_SET_COMPRESSION); +	pSMB->IsFsctl = 1; /* FSCTL */ +	pSMB->IsRootFlag = 0; +	pSMB->Fid = fid; /* file handle always le */ +	/* 3 byte pad, followed by 2 byte compress state */ +	pSMB->ByteCount = __constant_cpu_to_le16(5); +	inc_rfc1001_len(pSMB, 5); + +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, +			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); +	if (rc) +		cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); + +	cifs_buf_release(pSMB); + +	/* +	 * Note: On -EAGAIN error only caller can retry on handle based calls +	 * since file handle passed in no longer valid. +	 */  	return rc;  } -#endif /* CIFS_EXPERIMENTAL */ +  #ifdef CONFIG_CIFS_POSIX @@ -2680,7 +3278,10 @@ static void cifs_convert_ace(posix_acl_xattr_entry *ace,  	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); */ +/* +	cifs_dbg(FYI, "perm %d tag %d id %d\n", +		 ace->e_perm, ace->e_tag, ace->e_id); +*/  	return;  } @@ -2706,8 +3307,8 @@ static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,  		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); +			cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n", +				 size_of_data_area, size);  			return -EINVAL;  		}  	} else if (acl_type & ACL_TYPE_DEFAULT) { @@ -2754,7 +3355,10 @@ static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,  		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);*/ +/* +	cifs_dbg(FYI, "perm %d tag %d id %d\n", +		 ace->e_perm, ace->e_tag, ace->e_id); +*/  	return rc;  } @@ -2772,21 +3376,22 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,  		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)); +	cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n", +		 count, buflen, le32_to_cpu(local_acl->a_version));  	if (le32_to_cpu(local_acl->a_version) != 2) { -		cFYI(1, "unknown POSIX ACL version %d", -		     le32_to_cpu(local_acl->a_version)); +		cifs_dbg(FYI, "unknown POSIX ACL version %d\n", +			 le32_to_cpu(local_acl->a_version));  		return 0;  	}  	cifs_acl->version = cpu_to_le16(1); -	if (acl_type == ACL_TYPE_ACCESS) +	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 = __constant_cpu_to_le16(0xFFFF); +	} 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); +		cifs_acl->access_entry_count = __constant_cpu_to_le16(0xFFFF); +	} else { +		cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);  		return 0;  	}  	for (i = 0; i < count; i++) { @@ -2806,7 +3411,7 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,  }  int -CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, +CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,  		   const unsigned char *searchName,  		   char *acl_inf, const int buflen, const int acl_type,  		   const struct nls_table *nls_codepage, int remap) @@ -2819,7 +3424,7 @@ CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,  	int name_len;  	__u16 params, byte_count; -	cFYI(1, "In GetPosixACL (Unix) for path %s", searchName); +	cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName);  queryAclRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, @@ -2829,8 +3434,9 @@ queryAclRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -			cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -					 PATH_MAX, nls_codepage, remap); +			cifsConvertToUTF16((__le16 *) pSMB->FileName, +					   searchName, PATH_MAX, nls_codepage, +					   remap);  		name_len++;     /* trailing null */  		name_len *= 2;  		pSMB->FileName[name_len] = 0; @@ -2864,20 +3470,20 @@ queryAclRetry:  	pSMB->ParameterCount = pSMB->TotalParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  		(struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_acl_get); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);  	if (rc) { -		cFYI(1, "Send error in Query POSIX ACL = %d", rc); +		cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", 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 || get_bcc(&pSMBr->hdr) < 2)  			rc = -EIO;      /* bad smb */  		else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -2894,7 +3500,7 @@ queryAclRetry:  }  int -CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, +CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,  		   const unsigned char *fileName,  		   const char *local_acl, const int buflen,  		   const int acl_type, @@ -2908,7 +3514,7 @@ CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned = 0;  	__u16 params, byte_count, data_count, param_offset, offset; -	cFYI(1, "In SetPosixACL (Unix) for path %s", fileName); +	cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);  setAclRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -2916,8 +3522,8 @@ setAclRetry:  		return rc;  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -			cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, -				      PATH_MAX, nls_codepage, remap); +			cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, +					   PATH_MAX, nls_codepage, remap);  		name_len++;     /* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -2958,12 +3564,12 @@ setAclRetry:  	pSMB->ParameterCount = cpu_to_le16(params);  	pSMB->TotalParameterCount = pSMB->ParameterCount;  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) -		cFYI(1, "Set POSIX ACL returned %d", rc); +		cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc);  setACLerrorExit:  	cifs_buf_release(pSMB); @@ -2974,7 +3580,7 @@ setACLerrorExit:  /* BB fix tabs in this function FIXME BB */  int -CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, +CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,  	       const int netfid, __u64 *pExtAttrBits, __u64 *pMask)  {  	int rc = 0; @@ -2983,7 +3589,7 @@ CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned;  	__u16 params, byte_count; -	cFYI(1, "In GetExtAttr"); +	cifs_dbg(FYI, "In GetExtAttr\n");  	if (tcon == NULL)  		return -ENODEV; @@ -3016,18 +3622,18 @@ GetExtAttrRetry:  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);  	pSMB->Pad = 0;  	pSMB->Fid = netfid; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->t2.ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "error %d in GetExtAttr", rc); +		cifs_dbg(FYI, "error %d in GetExtAttr\n", 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 || get_bcc(&pSMBr->hdr) < 2)  			/* If rc should we check for EOPNOSUPP and  			   disable the srvino flag? or in caller? */  			rc = -EIO;      /* bad smb */ @@ -3037,7 +3643,7 @@ GetExtAttrRetry:  			struct file_chattr_info *pfinfo;  			/* BB Do we need a cast or hash here ? */  			if (count != 16) { -				cFYI(1, "Illegal size ret in GetExtAttr"); +				cifs_dbg(FYI, "Illegal size ret in GetExtAttr\n");  				rc = -EIO;  				goto GetExtAttrOut;  			} @@ -3056,10 +3662,100 @@ GetExtAttrOut:  #endif /* CONFIG_POSIX */ -#ifdef CONFIG_CIFS_EXPERIMENTAL +#ifdef CONFIG_CIFS_ACL +/* + * Initialize NT TRANSACT SMB into small smb request buffer.  This assumes that + * all NT TRANSACTS that we init here have total parm and data under about 400 + * bytes (to fit in small cifs buffer size), which is the case so far, it + * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of + * returned setup area) and MaxParameterCount (returned parms size) must be set + * by caller + */ +static int +smb_init_nttransact(const __u16 sub_command, const int setup_count, +		   const int parm_len, struct cifs_tcon *tcon, +		   void **ret_buf) +{ +	int rc; +	__u32 temp_offset; +	struct smb_com_ntransact_req *pSMB; + +	rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, +				(void **)&pSMB); +	if (rc) +		return rc; +	*ret_buf = (void *)pSMB; +	pSMB->Reserved = 0; +	pSMB->TotalParameterCount = cpu_to_le32(parm_len); +	pSMB->TotalDataCount  = 0; +	pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); +	pSMB->ParameterCount = pSMB->TotalParameterCount; +	pSMB->DataCount  = pSMB->TotalDataCount; +	temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + +			(setup_count * 2) - 4 /* for rfc1001 length itself */; +	pSMB->ParameterOffset = cpu_to_le32(temp_offset); +	pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); +	pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ +	pSMB->SubCommand = cpu_to_le16(sub_command); +	return 0; +} + +static int +validate_ntransact(char *buf, char **ppparm, char **ppdata, +		   __u32 *pparmlen, __u32 *pdatalen) +{ +	char *end_of_smb; +	__u32 data_count, data_offset, parm_count, parm_offset; +	struct smb_com_ntransact_rsp *pSMBr; +	u16 bcc; + +	*pdatalen = 0; +	*pparmlen = 0; + +	if (buf == NULL) +		return -EINVAL; + +	pSMBr = (struct smb_com_ntransact_rsp *)buf; + +	bcc = get_bcc(&pSMBr->hdr); +	end_of_smb = 2 /* sizeof byte count */ + bcc + +			(char *)&pSMBr->ByteCount; + +	data_offset = le32_to_cpu(pSMBr->DataOffset); +	data_count = le32_to_cpu(pSMBr->DataCount); +	parm_offset = le32_to_cpu(pSMBr->ParameterOffset); +	parm_count = le32_to_cpu(pSMBr->ParameterCount); + +	*ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; +	*ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; + +	/* should we also check that parm and data areas do not overlap? */ +	if (*ppparm > end_of_smb) { +		cifs_dbg(FYI, "parms start after end of smb\n"); +		return -EINVAL; +	} else if (parm_count + *ppparm > end_of_smb) { +		cifs_dbg(FYI, "parm end after end of smb\n"); +		return -EINVAL; +	} else if (*ppdata > end_of_smb) { +		cifs_dbg(FYI, "data starts after end of smb\n"); +		return -EINVAL; +	} else if (data_count + *ppdata > end_of_smb) { +		cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n", +			 *ppdata, data_count, (data_count + *ppdata), +			 end_of_smb, pSMBr); +		return -EINVAL; +	} else if (parm_count + data_count > bcc) { +		cifs_dbg(FYI, "parm count and data count larger than SMB\n"); +		return -EINVAL; +	} +	*pdatalen = data_count; +	*pparmlen = parm_count; +	return 0; +} +  /* Get Security Descriptor (by handle) from remote server for a file or dir */  int -CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, +CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,  		  struct cifs_ntsd **acl_inf, __u32 *pbuflen)  {  	int rc = 0; @@ -3067,7 +3763,7 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,  	QUERY_SEC_DESC_REQ *pSMB;  	struct kvec iov[1]; -	cFYI(1, "GetCifsACL"); +	cifs_dbg(FYI, "GetCifsACL\n");  	*pbuflen = 0;  	*acl_inf = NULL; @@ -3084,15 +3780,15 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,  	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; +	inc_rfc1001_len(pSMB, 11);  	iov[0].iov_base = (char *)pSMB; -	iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; +	iov[0].iov_len = be32_to_cpu(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); +			 0); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);  	if (rc) { -		cFYI(1, "Send error in QuerySecDesc = %d", rc); +		cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc);  	} else {                /* decode response */  		__le32 *parm;  		__u32 parm_len; @@ -3107,7 +3803,8 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,  			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); +		cifs_dbg(FYI, "smb %p parm %p data %p\n", +			 pSMBr, parm, *acl_inf);  		if (le32_to_cpu(pSMBr->ParameterCount) != 4) {  			rc = -EIO;      /* bad smb */ @@ -3119,8 +3816,8 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,  		acl_len = le32_to_cpu(*parm);  		if (acl_len != *pbuflen) { -			cERROR(1, "acl length %d does not match %d", -				   acl_len, *pbuflen); +			cifs_dbg(VFS, "acl length %d does not match %d\n", +				 acl_len, *pbuflen);  			if (*pbuflen > acl_len)  				*pbuflen = acl_len;  		} @@ -3129,16 +3826,15 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,  		   header followed by the smallest SID */  		if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||  		    (*pbuflen >= 64 * 1024)) { -			cERROR(1, "bad acl length %d", *pbuflen); +			cifs_dbg(VFS, "bad acl length %d\n", *pbuflen);  			rc = -EINVAL;  			*pbuflen = 0;  		} else { -			*acl_inf = kmalloc(*pbuflen, GFP_KERNEL); +			*acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL);  			if (*acl_inf == NULL) {  				*pbuflen = 0;  				rc = -ENOMEM;  			} -			memcpy(*acl_inf, pdata, *pbuflen);  		}  	}  qsec_out: @@ -3151,20 +3847,19 @@ qsec_out:  }  int -CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, -			struct cifs_ntsd *pntsd, __u32 acllen) +CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, +			struct cifs_ntsd *pntsd, __u32 acllen, int aclflag)  {  	__u16 byte_count, param_count, data_count, param_offset, data_offset;  	int rc = 0;  	int bytes_returned = 0;  	SET_SEC_DESC_REQ *pSMB = NULL; -	NTRANSACT_RSP *pSMBr = NULL; +	void *pSMBr;  setCifsAclRetry: -	rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, -			(void **) &pSMBr); +	rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr);  	if (rc) -			return (rc); +		return rc;  	pSMB->MaxSetupCount = 0;  	pSMB->Reserved = 0; @@ -3189,23 +3884,22 @@ setCifsAclRetry:  	pSMB->Fid = fid; /* file handle always le */  	pSMB->Reserved2 = 0; -	pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL); +	pSMB->AclFlags = cpu_to_le32(aclflag);  	if (pntsd && acllen) { -		memcpy((char *) &pSMBr->hdr.Protocol + data_offset, -			(char *) pntsd, -			acllen); -		pSMB->hdr.smb_buf_length += (byte_count + data_count); - +		memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + +				data_offset, pntsd, acllen); +		inc_rfc1001_len(pSMB, byte_count + data_count);  	} else -		pSMB->hdr.smb_buf_length += byte_count; +		inc_rfc1001_len(pSMB, 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); +	cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n", +		 bytes_returned, rc);  	if (rc) -		cFYI(1, "Set CIFS ACL returned %d", rc); +		cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -3214,14 +3908,14 @@ setCifsAclRetry:  	return (rc);  } -#endif /* CONFIG_CIFS_EXPERIMENTAL */ +#endif /* CONFIG_CIFS_ACL */  /* 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) +int +SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, +		    const char *search_name, FILE_ALL_INFO *data, +		    const struct nls_table *nls_codepage, int remap)  {  	QUERY_INFORMATION_REQ *pSMB;  	QUERY_INFORMATION_RSP *pSMBr; @@ -3229,7 +3923,7 @@ int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned;  	int name_len; -	cFYI(1, "In SMBQPath path %s", searchName); +	cifs_dbg(FYI, "In SMBQPath path %s\n", search_name);  QInfRetry:  	rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -3238,41 +3932,42 @@ QInfRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -			cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -					PATH_MAX, nls_codepage, remap); +			cifsConvertToUTF16((__le16 *) pSMB->FileName, +					   search_name, PATH_MAX, nls_codepage, +					   remap);  		name_len++;     /* trailing null */  		name_len *= 2;  	} else { -		name_len = strnlen(searchName, PATH_MAX); +		name_len = strnlen(search_name, PATH_MAX);  		name_len++;     /* trailing null */ -		strncpy(pSMB->FileName, searchName, name_len); +		strncpy(pSMB->FileName, search_name, name_len);  	}  	pSMB->BufferFormat = 0x04;  	name_len++; /* account for buffer type byte */ -	pSMB->hdr.smb_buf_length += (__u16) name_len; +	inc_rfc1001_len(pSMB, (__u16)name_len);  	pSMB->ByteCount = cpu_to_le16(name_len);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QueryInfo = %d", rc); -	} else if (pFinfo) { +		cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); +	} else if (data) {  		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)); +		memset(data, 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 = +		data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); +		data->LastWriteTime = data->ChangeTime; +		data->LastAccessTime = 0; +		data->AllocationSize =  			cpu_to_le64(le32_to_cpu(pSMBr->size)); -		pFinfo->EndOfFile = pFinfo->AllocationSize; -		pFinfo->Attributes = +		data->EndOfFile = data->AllocationSize; +		data->Attributes =  			cpu_to_le32(le16_to_cpu(pSMBr->attr));  	} else  		rc = -EIO; /* bad buffer passed in */ @@ -3286,7 +3981,7 @@ QInfRetry:  }  int -CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon, +CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  		 u16 netfid, FILE_ALL_INFO *pFindData)  {  	struct smb_t2_qfi_req *pSMB = NULL; @@ -3324,18 +4019,19 @@ QFileInfoRetry:  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);  	pSMB->Pad = 0;  	pSMB->Fid = netfid; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count); +	pSMB->t2.ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QPathInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QFileInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr);  		if (rc) /* BB add auto retry on EOPNOTSUPP? */  			rc = -EIO; -		else if (pSMBr->ByteCount < 40) +		else if (get_bcc(&pSMBr->hdr) < 40)  			rc = -EIO;	/* bad smb */  		else if (pFindData) {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -3353,13 +4049,12 @@ QFileInfoRetry:  }  int -CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, -		 const unsigned char *searchName, -		 FILE_ALL_INFO *pFindData, +CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, +		 const char *search_name, FILE_ALL_INFO *data,  		 int legacy /* old style infolevel */,  		 const struct nls_table *nls_codepage, int remap)  { -/* level 263 SMB_QUERY_FILE_ALL_INFO */ +	/* level 263 SMB_QUERY_FILE_ALL_INFO */  	TRANSACTION2_QPI_REQ *pSMB = NULL;  	TRANSACTION2_QPI_RSP *pSMBr = NULL;  	int rc = 0; @@ -3367,7 +4062,7 @@ CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,  	int name_len;  	__u16 params, byte_count; -/* cFYI(1, "In QPathInfo path %s", searchName); */ +	/* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */  QPathInfoRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -3376,14 +4071,14 @@ QPathInfoRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(searchName, PATH_MAX); +		name_len = strnlen(search_name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->FileName, searchName, name_len); +		strncpy(pSMB->FileName, search_name, name_len);  	}  	params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; @@ -3411,37 +4106,38 @@ QPathInfoRetry:  	else  		pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QPathInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr);  		if (rc) /* BB add auto retry on EOPNOTSUPP? */  			rc = -EIO; -		else if (!legacy && (pSMBr->ByteCount < 40)) +		else if (!legacy && get_bcc(&pSMBr->hdr) < 40)  			rc = -EIO;	/* bad smb */ -		else if (legacy && (pSMBr->ByteCount < 24)) +		else if (legacy && get_bcc(&pSMBr->hdr) < 24)  			rc = -EIO;  /* 24 or 26 expected but we do not read  					last field */ -		else if (pFindData) { +		else if (data) {  			int size;  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); -			/* On legacy responses we do not read the last field, -			EAsize, fortunately since it varies by subdialect and -			also note it differs on Set vs. Get, ie two bytes or 4 -			bytes depending but we don't care here */ +			/* +			 * 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 + +			memcpy((char *) data, (char *) &pSMBr->hdr.Protocol +  			       data_offset, size);  		} else  		    rc = -ENOMEM; @@ -3454,7 +4150,7 @@ QPathInfoRetry:  }  int -CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon, +CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  		 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)  {  	struct smb_t2_qfi_req *pSMB = NULL; @@ -3492,19 +4188,18 @@ UnixQFileInfoRetry:  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);  	pSMB->Pad = 0;  	pSMB->Fid = netfid; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count); +	pSMB->t2.ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QPathInfo = %d", rc); +		cifs_dbg(FYI, "Send error in UnixQFileInfo = %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."); +		if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { +			cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");  			rc = -EIO;	/* bad smb */  		} else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -3523,7 +4218,7 @@ UnixQFileInfoRetry:  }  int -CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, +CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,  		     const unsigned char *searchName,  		     FILE_UNIX_BASIC_INFO *pFindData,  		     const struct nls_table *nls_codepage, int remap) @@ -3536,7 +4231,7 @@ CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,  	int name_len;  	__u16 params, byte_count; -	cFYI(1, "In QPathInfo (Unix) the path %s", searchName); +	cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName);  UnixQPathInfoRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -3545,8 +4240,8 @@ UnixQPathInfoRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -				  PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -3577,20 +4272,18 @@ UnixQPathInfoRetry:  	pSMB->ParameterCount = pSMB->TotalParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QPathInfo = %d", rc); +		cifs_dbg(FYI, "Send error in UnixQPathInfo = %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."); +		if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { +			cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");  			rc = -EIO;	/* bad smb */  		} else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -3609,11 +4302,10 @@ UnixQPathInfoRetry:  /* 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) +CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, +	      const char *searchName, struct cifs_sb_info *cifs_sb, +	      __u16 *pnetfid, __u16 search_flags, +	      struct cifs_search_info *psrch_inf, bool msearch)  {  /* level 257 SMB_ */  	TRANSACTION2_FFIRST_REQ *pSMB = NULL; @@ -3621,10 +4313,11 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,  	T2_FFIRST_RSP_PARMS *parms;  	int rc = 0;  	int bytes_returned = 0; -	int name_len; +	int name_len, remap;  	__u16 params, byte_count; +	struct nls_table *nls_codepage; -	cFYI(1, "In FindFirst for %s", searchName); +	cifs_dbg(FYI, "In FindFirst for %s\n", searchName);  findFirstRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, @@ -3632,39 +4325,46 @@ findFirstRetry:  	if (rc)  		return rc; +	nls_codepage = cifs_sb->local_nls; +	remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; +  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -				 PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, +				       PATH_MAX, nls_codepage, remap);  		/* We can not add the asterik earlier in case  		it got remapped to 0xF03A as if it were part of the  		directory name instead of a wildcard */  		name_len *= 2; -		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; +		if (msearch) { +			pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb); +			pSMB->FileName[name_len+1] = 0; +			pSMB->FileName[name_len+2] = '*'; +			pSMB->FileName[name_len+3] = 0; +			name_len += 4; /* now the trailing null */ +			/* null terminate just in case */ +			pSMB->FileName[name_len] = 0; +			pSMB->FileName[name_len+1] = 0; +			name_len += 2; +		}  	} else {	/* BB add check for overrun of SMB buf BB */  		name_len = strnlen(searchName, PATH_MAX);  /* BB fix here and in unicode clause above ie  		if (name_len > buffersize-header)  			free buffer exit; BB */  		strncpy(pSMB->FileName, searchName, name_len); -		pSMB->FileName[name_len] = dirsep; -		pSMB->FileName[name_len+1] = '*'; -		pSMB->FileName[name_len+2] = 0; -		name_len += 3; +		if (msearch) { +			pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb); +			pSMB->FileName[name_len+1] = '*'; +			pSMB->FileName[name_len+2] = 0; +			name_len += 3; +		}  	}  	params = 12 + name_len /* includes null */ ;  	pSMB->TotalDataCount = 0;	/* no EAs */  	pSMB->MaxParameterCount = cpu_to_le16(10); -	pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf - -					  MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); +	pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);  	pSMB->MaxSetupCount = 0;  	pSMB->Reserved = 0;  	pSMB->Flags = 0; @@ -3685,23 +4385,22 @@ findFirstRetry:  	    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->SearchFlags = cpu_to_le16(search_flags);  	pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);  	/* BB what should we set StorageType to? Does it matter? BB */  	pSMB->SearchStorageType = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_ffirst); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_ffirst);  	if (rc) {/* BB add logic to retry regular search if Unix search  			rejected unexpectedly by server */  		/* BB Add code to handle unsupported level rc */ -		cFYI(1, "Error in FindFirst = %d", rc); +		cifs_dbg(FYI, "Error in FindFirst = %d\n", rc);  		cifs_buf_release(pSMB); @@ -3738,9 +4437,8 @@ findFirstRetry:  			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"); +			if (CIFSMaxBufSize < lnoff) { +				cifs_dbg(VFS, "ignoring corrupt resume name\n");  				psrch_inf->last_entry = NULL;  				return rc;  			} @@ -3748,7 +4446,8 @@ findFirstRetry:  			psrch_inf->last_entry = psrch_inf->srch_entries_start +  							lnoff; -			*pnetfid = parms->SearchHandle; +			if (pnetfid) +				*pnetfid = parms->SearchHandle;  		} else {  			cifs_buf_release(pSMB);  		} @@ -3757,18 +4456,20 @@ findFirstRetry:  	return rc;  } -int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, -		 __u16 searchHandle, struct cifs_search_info *psrch_inf) +int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, +		 __u16 searchHandle, __u16 search_flags, +		 struct cifs_search_info *psrch_inf)  {  	TRANSACTION2_FNEXT_REQ *pSMB = NULL;  	TRANSACTION2_FNEXT_RSP *pSMBr = NULL;  	T2_FNEXT_RSP_PARMS *parms;  	char *response_data;  	int rc = 0; -	int bytes_returned, name_len; +	int bytes_returned; +	unsigned int name_len;  	__u16 params, byte_count; -	cFYI(1, "In FindNext"); +	cifs_dbg(FYI, "In FindNext\n");  	if (psrch_inf->endOfSearch)  		return -ENOENT; @@ -3782,9 +4483,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,  	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->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);  	pSMB->MaxSetupCount = 0;  	pSMB->Reserved = 0;  	pSMB->Flags = 0; @@ -3802,8 +4501,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,  		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); +	pSMB->SearchFlags = cpu_to_le16(search_flags);  	name_len = psrch_inf->resume_name_len;  	params += name_len; @@ -3820,19 +4518,19 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,  	byte_count = params + 1 /* pad */ ;  	pSMB->TotalParameterCount = cpu_to_le16(params);  	pSMB->ParameterCount = pSMB->TotalParameterCount; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			(struct smb_hdr *) pSMBr, &bytes_returned, 0); -	cifs_stats_inc(&tcon->num_fnext); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext);  	if (rc) {  		if (rc == -EBADF) {  			psrch_inf->endOfSearch = true;  			cifs_buf_release(pSMB);  			rc = 0; /* search probably was closed at end of search*/  		} else -			cFYI(1, "FindNext returned = %d", rc); +			cifs_dbg(FYI, "FindNext returned = %d\n", rc);  	} else {                /* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -3866,17 +4564,16 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,  			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"); +			if (CIFSMaxBufSize < lnoff) { +				cifs_dbg(VFS, "ignoring corrupt resume name\n");  				psrch_inf->last_entry = NULL;  				return rc;  			} else  				psrch_inf->last_entry =  					psrch_inf->srch_entries_start + lnoff; -/*  cFYI(1, "fnxt2 entries in buf %d index_of_last %d", -	    psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */ +/*  cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n", +    psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */  			/* BB fixme add unlock here */  		} @@ -3895,13 +4592,13 @@ FNext2_err_exit:  }  int -CIFSFindClose(const int xid, struct cifsTconInfo *tcon, +CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,  	      const __u16 searchHandle)  {  	int rc = 0;  	FINDCLOSE_REQ *pSMB = NULL; -	cFYI(1, "In CIFSSMBFindClose"); +	cifs_dbg(FYI, "In CIFSSMBFindClose\n");  	rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);  	/* no sense returning error if session restarted @@ -3913,11 +4610,11 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon,  	pSMB->FileID = searchHandle;  	pSMB->ByteCount = 0; -	rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);  	if (rc) -		cERROR(1, "Send error in FindClose = %d", rc); +		cifs_dbg(VFS, "Send error in FindClose = %d\n", rc); -	cifs_stats_inc(&tcon->num_fclose); +	cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose);  	/* Since session is dead, search handle closed on server already */  	if (rc == -EAGAIN) @@ -3927,9 +4624,8 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon,  }  int -CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, -		      const unsigned char *searchName, -		      __u64 *inode_number, +CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, +		      const char *search_name, __u64 *inode_number,  		      const struct nls_table *nls_codepage, int remap)  {  	int rc = 0; @@ -3938,7 +4634,7 @@ CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,  	int name_len, bytes_returned;  	__u16 params, byte_count; -	cFYI(1, "In GetSrvInodeNum for %s", searchName); +	cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name);  	if (tcon == NULL)  		return -ENODEV; @@ -3950,14 +4646,15 @@ GetInodeNumberRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -			cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -					 PATH_MAX, nls_codepage, remap); +			cifsConvertToUTF16((__le16 *) pSMB->FileName, +					   search_name, PATH_MAX, nls_codepage, +					   remap);  		name_len++;     /* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(searchName, PATH_MAX); +		name_len = strnlen(search_name, PATH_MAX);  		name_len++;     /* trailing null */ -		strncpy(pSMB->FileName, searchName, name_len); +		strncpy(pSMB->FileName, search_name, name_len);  	}  	params = 2 /* level */  + 4 /* rsrvd */  + name_len /* incl null */ ; @@ -3982,18 +4679,18 @@ GetInodeNumberRetry:  	pSMB->ParameterCount = pSMB->TotalParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  		(struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "error %d in QueryInternalInfo", rc); +		cifs_dbg(FYI, "error %d in QueryInternalInfo\n", 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 || get_bcc(&pSMBr->hdr) < 2)  			/* If rc should we check for EOPNOSUPP and  			disable the srvino flag? or in caller? */  			rc = -EIO;      /* bad smb */ @@ -4003,7 +4700,7 @@ GetInodeNumberRetry:  			struct file_internal_info *pfinfo;  			/* BB Do we need a cast or hash here ? */  			if (count < 8) { -				cFYI(1, "Illegal size ret in QryIntrnlInf"); +				cifs_dbg(FYI, "Illegal size ret in QryIntrnlInf\n");  				rc = -EIO;  				goto GetInodeNumOut;  			} @@ -4044,16 +4741,16 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,  	*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); +		cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n", +			 *num_of_nodes);  		rc = -EINVAL;  		goto parse_DFS_referrals_exit;  	}  	ref = (struct dfs_referral_level_3 *) &(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)); +		cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", +			 le16_to_cpu(ref->VersionNumber));  		rc = -EINVAL;  		goto parse_DFS_referrals_exit;  	} @@ -4062,14 +4759,12 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,  	data_end = (char *)(&(pSMBr->PathConsumed)) +  				le16_to_cpu(pSMBr->t2.DataCount); -	cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n", -			*num_of_nodes, -			le32_to_cpu(pSMBr->DFSFlags)); +	cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", +		 *num_of_nodes, le32_to_cpu(pSMBr->DFSFlags)); -	*target_nodes = kzalloc(sizeof(struct dfs_info3_param) * -			*num_of_nodes, GFP_KERNEL); +	*target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param), +				GFP_KERNEL);  	if (*target_nodes == NULL) { -		cERROR(1, "Failed to allocate buffer for target_nodes\n");  		rc = -ENOMEM;  		goto parse_DFS_referrals_exit;  	} @@ -4088,9 +4783,9 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,  				rc = -ENOMEM;  				goto parse_DFS_referrals_exit;  			} -			cifsConvertToUCS((__le16 *) tmp, searchName, -					PATH_MAX, nls_codepage, remap); -			node->path_consumed = cifs_ucs2_bytes(tmp, +			cifsConvertToUTF16((__le16 *) tmp, searchName, +					   PATH_MAX, nls_codepage, remap); +			node->path_consumed = cifs_utf16_bytes(tmp,  					le16_to_cpu(pSMBr->PathConsumed),  					nls_codepage);  			kfree(tmp); @@ -4103,8 +4798,8 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,  		/* copy DfsPath */  		temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);  		max_len = data_end - temp; -		node->path_name = cifs_strndup_from_ucs(temp, max_len, -						      is_unicode, nls_codepage); +		node->path_name = cifs_strndup_from_utf16(temp, max_len, +						is_unicode, nls_codepage);  		if (!node->path_name) {  			rc = -ENOMEM;  			goto parse_DFS_referrals_exit; @@ -4113,10 +4808,14 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,  		/* copy link target UNC */  		temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);  		max_len = data_end - temp; -		node->node_name = cifs_strndup_from_ucs(temp, max_len, -						      is_unicode, nls_codepage); -		if (!node->node_name) +		node->node_name = cifs_strndup_from_utf16(temp, max_len, +						is_unicode, nls_codepage); +		if (!node->node_name) {  			rc = -ENOMEM; +			goto parse_DFS_referrals_exit; +		} + +		ref++;  	}  parse_DFS_referrals_exit: @@ -4129,9 +4828,8 @@ parse_DFS_referrals_exit:  }  int -CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, -		const unsigned char *searchName, -		struct dfs_info3_param **target_nodes, +CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, +		const char *search_name, struct dfs_info3_param **target_nodes,  		unsigned int *num_of_nodes,  		const struct nls_table *nls_codepage, int remap)  { @@ -4145,7 +4843,7 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,  	*num_of_nodes = 0;  	*target_nodes = NULL; -	cFYI(1, "In GetDFSRefer the path %s", searchName); +	cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name);  	if (ses == NULL)  		return -ENODEV;  getDFSRetry: @@ -4156,7 +4854,7 @@ getDFSRetry:  	/* server pointer checked in called function,  	but should never be null here anyway */ -	pSMB->hdr.Mid = GetNextMid(ses->server); +	pSMB->hdr.Mid = get_next_mid(ses->server);  	pSMB->hdr.Tid = ses->ipc_tid;  	pSMB->hdr.Uid = ses->Suid;  	if (ses->capabilities & CAP_STATUS32) @@ -4167,21 +4865,19 @@ getDFSRetry:  	if (ses->capabilities & CAP_UNICODE) {  		pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->RequestFileName, -				     searchName, PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->RequestFileName, +				       search_name, PATH_MAX, nls_codepage, +				       remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(searchName, PATH_MAX); +		name_len = strnlen(search_name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->RequestFileName, searchName, name_len); +		strncpy(pSMB->RequestFileName, search_name, name_len);  	} -	if (ses->server) { -		if (ses->server->secMode & -		   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) -			pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; -	} +	if (ses->server && ses->server->sign) +		pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;  	pSMB->hdr.Uid = ses->Suid; @@ -4206,31 +4902,30 @@ getDFSRetry:  	pSMB->ParameterCount = cpu_to_le16(params);  	pSMB->TotalParameterCount = pSMB->ParameterCount;  	pSMB->MaxReferralLevel = cpu_to_le16(3); -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in GetDFSRefer = %d", rc); +		cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc);  		goto GetDFSRefExit;  	}  	rc = validate_t2((struct smb_t2_rsp *)pSMBr);  	/* BB Also check if enough total bytes returned? */ -	if (rc || (pSMBr->ByteCount < 17)) { +	if (rc || get_bcc(&pSMBr->hdr) < 17) {  		rc = -EIO;      /* bad smb */  		goto GetDFSRefExit;  	} -	cFYI(1, "Decoding GetDFSRefer response BCC: %d  Offset %d", -				pSMBr->ByteCount, -				le16_to_cpu(pSMBr->t2.DataOffset)); +	cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d  Offset %d\n", +		 get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));  	/* parse returned result into more usable form */  	rc = parse_DFS_referrals(pSMBr, num_of_nodes,  				 target_nodes, nls_codepage, remap, -				 searchName); +				 search_name);  GetDFSRefExit:  	cifs_buf_release(pSMB); @@ -4243,7 +4938,8 @@ GetDFSRefExit:  /* 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) +SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, +	      struct kstatfs *FSData)  {  /* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */  	TRANSACTION2_QFSI_REQ *pSMB = NULL; @@ -4253,7 +4949,7 @@ SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)  	int bytes_returned = 0;  	__u16 params, byte_count; -	cFYI(1, "OldQFSInfo"); +	cifs_dbg(FYI, "OldQFSInfo\n");  oldQFSInfoRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		(void **) &pSMBr); @@ -4280,22 +4976,22 @@ oldQFSInfoRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  		(struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QFSInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);  	} else {                /* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < 18)) +		if (rc || get_bcc(&pSMBr->hdr) < 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); +			cifs_dbg(FYI, "qfsinf resp BCC: %d  Offset %d\n", +				 get_bcc(&pSMBr->hdr), data_offset);  			response_data = (FILE_SYSTEM_ALLOC_INFO *)  				(((char *) &pSMBr->hdr.Protocol) + data_offset); @@ -4307,10 +5003,10 @@ oldQFSInfoRetry:  			       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_dbg(FYI, "Blocks: %lld  Free: %lld Block size %ld\n", +				 (unsigned long long)FSData->f_blocks, +				 (unsigned long long)FSData->f_bfree, +				 FSData->f_bsize);  		}  	}  	cifs_buf_release(pSMB); @@ -4322,7 +5018,8 @@ oldQFSInfoRetry:  }  int -CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData) +CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, +	       struct kstatfs *FSData)  {  /* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */  	TRANSACTION2_QFSI_REQ *pSMB = NULL; @@ -4332,7 +5029,7 @@ CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)  	int bytes_returned = 0;  	__u16 params, byte_count; -	cFYI(1, "In QFSInfo"); +	cifs_dbg(FYI, "In QFSInfo\n");  QFSInfoRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -4359,17 +5056,17 @@ QFSInfoRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QFSInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < 24)) +		if (rc || get_bcc(&pSMBr->hdr) < 24)  			rc = -EIO;	/* bad smb */  		else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -4386,10 +5083,10 @@ QFSInfoRetry:  			    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_dbg(FYI, "Blocks: %lld  Free: %lld Block size %ld\n", +				 (unsigned long long)FSData->f_blocks, +				 (unsigned long long)FSData->f_bfree, +				 FSData->f_bsize);  		}  	}  	cifs_buf_release(pSMB); @@ -4401,7 +5098,7 @@ QFSInfoRetry:  }  int -CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon) +CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon)  {  /* level 0x105  SMB_QUERY_FILE_SYSTEM_INFO */  	TRANSACTION2_QFSI_REQ *pSMB = NULL; @@ -4411,7 +5108,7 @@ CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)  	int bytes_returned = 0;  	__u16 params, byte_count; -	cFYI(1, "In QFSAttributeInfo"); +	cifs_dbg(FYI, "In QFSAttributeInfo\n");  QFSAttributeRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -4439,17 +5136,17 @@ QFSAttributeRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cERROR(1, "Send error in QFSAttributeInfo = %d", rc); +		cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < 13)) { +		if (rc || get_bcc(&pSMBr->hdr) < 13) {  			/* BB also check if enough bytes returned */  			rc = -EIO;	/* bad smb */  		} else { @@ -4471,7 +5168,7 @@ QFSAttributeRetry:  }  int -CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon) +CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon)  {  /* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */  	TRANSACTION2_QFSI_REQ *pSMB = NULL; @@ -4481,7 +5178,7 @@ CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)  	int bytes_returned = 0;  	__u16 params, byte_count; -	cFYI(1, "In QFSDeviceInfo"); +	cifs_dbg(FYI, "In QFSDeviceInfo\n");  QFSDeviceRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -4510,17 +5207,18 @@ QFSDeviceRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QFSDeviceInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO))) +		if (rc || get_bcc(&pSMBr->hdr) < +			  sizeof(FILE_SYSTEM_DEVICE_INFO))  			rc = -EIO;	/* bad smb */  		else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -4541,7 +5239,7 @@ QFSDeviceRetry:  }  int -CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon) +CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon)  {  /* level 0x200  SMB_QUERY_CIFS_UNIX_INFO */  	TRANSACTION2_QFSI_REQ *pSMB = NULL; @@ -4551,7 +5249,7 @@ CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)  	int bytes_returned = 0;  	__u16 params, byte_count; -	cFYI(1, "In QFSUnixInfo"); +	cifs_dbg(FYI, "In QFSUnixInfo\n");  QFSUnixRetry:  	rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,  				   (void **) &pSMB, (void **) &pSMBr); @@ -4579,17 +5277,17 @@ QFSUnixRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cERROR(1, "Send error in QFSUnixInfo = %d", rc); +		cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < 13)) { +		if (rc || get_bcc(&pSMBr->hdr) < 13) {  			rc = -EIO;	/* bad smb */  		} else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -4611,7 +5309,7 @@ QFSUnixRetry:  }  int -CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) +CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, __u64 cap)  {  /* level 0x200  SMB_SET_CIFS_UNIX_INFO */  	TRANSACTION2_SETFSI_REQ *pSMB = NULL; @@ -4620,7 +5318,7 @@ CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)  	int bytes_returned = 0;  	__u16 params, param_offset, offset, byte_count; -	cFYI(1, "In SETFSUnixInfo"); +	cifs_dbg(FYI, "In SETFSUnixInfo\n");  SETFSUnixRetry:  	/* BB switch to small buf init to save memory */  	rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, @@ -4662,13 +5360,13 @@ SETFSUnixRetry:  	pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);  	pSMB->ClientUnixCap = cpu_to_le64(cap); -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cERROR(1, "Send error in SETFSUnixInfo = %d", rc); +		cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr);  		if (rc) @@ -4685,7 +5383,7 @@ SETFSUnixRetry:  int -CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon, +CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,  		   struct kstatfs *FSData)  {  /* level 0x201  SMB_QUERY_CIFS_POSIX_INFO */ @@ -4696,7 +5394,7 @@ CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,  	int bytes_returned = 0;  	__u16 params, byte_count; -	cFYI(1, "In QFSPosixInfo"); +	cifs_dbg(FYI, "In QFSPosixInfo\n");  QFSPosixRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -4724,17 +5422,17 @@ QFSPosixRetry:  	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; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QFSUnixInfo = %d", rc); +		cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); -		if (rc || (pSMBr->ByteCount < 13)) { +		if (rc || get_bcc(&pSMBr->hdr) < 13) {  			rc = -EIO;	/* bad smb */  		} else {  			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -4771,16 +5469,16 @@ QFSPosixRetry:  } -/* 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 */ - +/* + * We can not use write of zero bytes trick to set file size due to need for + * large file support. Also note that this SetPathInfo is preferred to + * SetFileInfo based method in next routine which is only needed to work around + * a sharing violation bugin Samba which this routine can run into. + */  int -CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName, -	      __u64 size, bool SetAllocation, -	      const struct nls_table *nls_codepage, int remap) +CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, +	      const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb, +	      bool set_allocation)  {  	struct smb_com_transaction2_spi_req *pSMB = NULL;  	struct smb_com_transaction2_spi_rsp *pSMBr = NULL; @@ -4788,9 +5486,11 @@ CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,  	int name_len;  	int rc = 0;  	int bytes_returned = 0; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; +  	__u16 params, byte_count, data_count, param_offset, offset; -	cFYI(1, "In SetEOF"); +	cifs_dbg(FYI, "In SetEOF\n");  SetEOFRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -4799,14 +5499,14 @@ SetEOFRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, +				       PATH_MAX, cifs_sb->local_nls, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(fileName, PATH_MAX); +		name_len = strnlen(file_name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->FileName, fileName, name_len); +		strncpy(pSMB->FileName, file_name, name_len);  	}  	params = 6 + name_len;  	data_count = sizeof(struct file_end_of_file_info); @@ -4820,7 +5520,7 @@ SetEOFRetry:  	param_offset = offsetof(struct smb_com_transaction2_spi_req,  				InformationLevel) - 4;  	offset = param_offset + params; -	if (SetAllocation) { +	if (set_allocation) {  		if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)  			pSMB->InformationLevel =  				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); @@ -4850,13 +5550,13 @@ SetEOFRetry:  	pSMB->ParameterCount = cpu_to_le16(params);  	pSMB->TotalParameterCount = pSMB->ParameterCount;  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	parm_data->FileSize = cpu_to_le64(size);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) -		cFYI(1, "SetPathInfo (file size) returned %d", rc); +		cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc);  	cifs_buf_release(pSMB); @@ -4867,24 +5567,23 @@ SetEOFRetry:  }  int -CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, -		   __u16 fid, __u32 pid_of_opener, bool SetAllocation) +CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifsFileInfo *cfile, __u64 size, bool set_allocation)  {  	struct smb_com_transaction2_sfi_req *pSMB  = NULL; -	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); +	cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n", +		 (long long)size);  	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);  	if (rc)  		return rc; -	pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); -	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); +	pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid); +	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16));  	params = 6;  	pSMB->MaxSetupCount = 0; @@ -4895,8 +5594,6 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,  	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 */ @@ -4915,8 +5612,8 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,  				+ offset);  	pSMB->DataOffset = cpu_to_le16(offset);  	parm_data->FileSize = cpu_to_le64(size); -	pSMB->Fid = fid; -	if (SetAllocation) { +	pSMB->Fid = cfile->fid.netfid; +	if (set_allocation) {  		if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)  			pSMB->InformationLevel =  				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); @@ -4932,11 +5629,12 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,  				cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);  	}  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count); -	rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);  	if (rc) { -		cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc); +		cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n", +			 rc);  	}  	/* Note: On -EAGAIN error only caller can retry on handle based calls @@ -4952,7 +5650,7 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,     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, +CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  		    const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)  {  	struct smb_com_transaction2_sfi_req *pSMB  = NULL; @@ -4960,7 +5658,7 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,  	int rc = 0;  	__u16 params, param_offset, offset, byte_count, count; -	cFYI(1, "Set Times (via SetFileInfo)"); +	cifs_dbg(FYI, "Set Times (via SetFileInfo)\n");  	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);  	if (rc) @@ -4978,7 +5676,8 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,  	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;  	offset = param_offset + params; -	data_offset = (char *) (&pSMB->hdr.Protocol) + offset; +	data_offset = (char *)pSMB + +			offsetof(struct smb_hdr, Protocol) + offset;  	count = sizeof(FILE_BASIC_INFO);  	pSMB->MaxParameterCount = cpu_to_le16(2); @@ -5000,12 +5699,13 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,  	else  		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); -	rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);  	if (rc) -		cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc); +		cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", +			 rc);  	/* Note: On -EAGAIN error only caller can retry on handle based calls  		since file handle passed in no longer valid */ @@ -5014,7 +5714,7 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,  }  int -CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon, +CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,  			  bool delete_file, __u16 fid, __u32 pid_of_opener)  {  	struct smb_com_transaction2_sfi_req *pSMB  = NULL; @@ -5022,7 +5722,7 @@ CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,  	int rc = 0;  	__u16 params, param_offset, offset, byte_count, count; -	cFYI(1, "Set File Disposition (via SetFileInfo)"); +	cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n");  	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);  	if (rc) @@ -5059,18 +5759,18 @@ CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,  	pSMB->Fid = fid;  	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, 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); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);  	if (rc) -		cFYI(1, "Send error in SetFileDisposition = %d", rc); +		cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc);  	return rc;  }  int -CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon, +CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,  		   const char *fileName, const FILE_BASIC_INFO *data,  		   const struct nls_table *nls_codepage, int remap)  { @@ -5082,7 +5782,7 @@ CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,  	char *data_offset;  	__u16 params, param_offset, offset, byte_count, count; -	cFYI(1, "In SetTimes"); +	cifs_dbg(FYI, "In SetTimes\n");  SetTimesRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, @@ -5092,8 +5792,8 @@ SetTimesRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -5132,13 +5832,13 @@ SetTimesRetry:  	else  		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) -		cFYI(1, "SetPathInfo (times) returned %d", rc); +		cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc);  	cifs_buf_release(pSMB); @@ -5154,7 +5854,7 @@ SetTimesRetry:  	  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, +CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon, char *fileName,  		__u16 dos_attrs, const struct nls_table *nls_codepage)  {  	SETATTR_REQ *pSMB = NULL; @@ -5163,7 +5863,7 @@ CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,  	int bytes_returned;  	int name_len; -	cFYI(1, "In SetAttrLegacy"); +	cifs_dbg(FYI, "In SetAttrLegacy\n");  SetAttrLgcyRetry:  	rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB, @@ -5173,8 +5873,8 @@ SetAttrLgcyRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -			ConvertToUCS((__le16 *) pSMB->fileName, fileName, -				PATH_MAX, nls_codepage); +			ConvertToUTF16((__le16 *) pSMB->fileName, fileName, +				       PATH_MAX, nls_codepage);  		name_len++;     /* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -5184,12 +5884,12 @@ SetAttrLgcyRetry:  	}  	pSMB->attr = cpu_to_le16(dos_attrs);  	pSMB->BufferFormat = 0x04; -	pSMB->hdr.smb_buf_length += name_len + 1; +	inc_rfc1001_len(pSMB, name_len + 1);  	pSMB->ByteCount = cpu_to_le16(name_len + 1);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) -		cFYI(1, "Error in LegacySetAttr = %d", rc); +		cifs_dbg(FYI, "Error in LegacySetAttr = %d\n", rc);  	cifs_buf_release(pSMB); @@ -5204,13 +5904,19 @@ static void  cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,  			const struct cifs_unix_set_info_args *args)  { +	u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64;  	u64 mode = args->mode; +	if (uid_valid(args->uid)) +		uid = from_kuid(&init_user_ns, args->uid); +	if (gid_valid(args->gid)) +		gid = from_kgid(&init_user_ns, args->gid); +  	/*  	 * Samba server ignores set of file size to zero due to bugs in some  	 * older clients, but we should be precise - we use SetFileSize to  	 * set file size and do not want to truncate file size to zero -	 * accidently as happened on one Samba server beta by putting +	 * accidentally as happened on one Samba server beta by putting  	 * zero instead of -1 here  	 */  	data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64); @@ -5218,8 +5924,8 @@ cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,  	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); +	data_offset->Uid = cpu_to_le64(uid); +	data_offset->Gid = cpu_to_le64(gid);  	/* better to leave device as zero when it is  */  	data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));  	data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); @@ -5242,16 +5948,16 @@ cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,  }  int -CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon, +CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  		       const struct cifs_unix_set_info_args *args,  		       u16 fid, u32 pid_of_opener)  {  	struct smb_com_transaction2_sfi_req *pSMB  = NULL; -	FILE_UNIX_BASIC_INFO *data_offset; +	char *data_offset;  	int rc = 0;  	u16 params, param_offset, offset, byte_count, count; -	cFYI(1, "Set Unix Info (via SetFileInfo)"); +	cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n");  	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);  	if (rc) @@ -5269,8 +5975,9 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,  	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;  	offset = param_offset + params; -	data_offset = (FILE_UNIX_BASIC_INFO *) -				((char *)(&pSMB->hdr.Protocol) + offset); +	data_offset = (char *)pSMB + +			offsetof(struct smb_hdr, Protocol) + offset; +  	count = sizeof(FILE_UNIX_BASIC_INFO);  	pSMB->MaxParameterCount = cpu_to_le16(2); @@ -5289,14 +5996,15 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,  	pSMB->Fid = fid;  	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count); -	cifs_fill_unix_set_info(data_offset, args); +	cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); -	rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);  	if (rc) -		cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc); +		cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", +			 rc);  	/* Note: On -EAGAIN error only caller can retry on handle based calls  		since file handle passed in no longer valid */ @@ -5305,7 +6013,8 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,  }  int -CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName, +CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, +		       const char *file_name,  		       const struct cifs_unix_set_info_args *args,  		       const struct nls_table *nls_codepage, int remap)  { @@ -5317,7 +6026,7 @@ CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,  	FILE_UNIX_BASIC_INFO *data_offset;  	__u16 params, param_offset, offset, count, byte_count; -	cFYI(1, "In SetUID/GID/Mode"); +	cifs_dbg(FYI, "In SetUID/GID/Mode\n");  setPermsRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -5326,14 +6035,14 @@ setPermsRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ -		name_len = strnlen(fileName, PATH_MAX); +		name_len = strnlen(file_name, PATH_MAX);  		name_len++;	/* trailing null */ -		strncpy(pSMB->FileName, fileName, name_len); +		strncpy(pSMB->FileName, file_name, name_len);  	}  	params = 6 + name_len; @@ -5365,7 +6074,7 @@ setPermsRetry:  	pSMB->TotalDataCount = pSMB->DataCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	cifs_fill_unix_set_info(data_offset, args); @@ -5373,7 +6082,7 @@ setPermsRetry:  	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_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc);  	cifs_buf_release(pSMB);  	if (rc == -EAGAIN) @@ -5381,79 +6090,6 @@ 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  /*   * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common @@ -5465,7 +6101,7 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,   * the data isn't copied to it, but the length is returned.   */  ssize_t -CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, +CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,  		const unsigned char *searchName, const unsigned char *ea_name,  		char *EAData, size_t buf_size,  		const struct nls_table *nls_codepage, int remap) @@ -5481,8 +6117,9 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,  	char *temp_ptr;  	char *end_of_smb;  	__u16 params, byte_count, data_offset; +	unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; -	cFYI(1, "In Query All EAs path %s", searchName); +	cifs_dbg(FYI, "In Query All EAs path %s\n", searchName);  QAllEAsRetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -5491,8 +6128,8 @@ QAllEAsRetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		list_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, +				       PATH_MAX, nls_codepage, remap);  		list_len++;	/* trailing null */  		list_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -5523,13 +6160,13 @@ QAllEAsRetry:  	pSMB->ParameterCount = pSMB->TotalParameterCount;  	pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cFYI(1, "Send error in QueryAllEAs = %d", rc); +		cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc);  		goto QAllEAsOut;  	} @@ -5539,7 +6176,7 @@ QAllEAsRetry:  	of these trans2 responses */  	rc = validate_t2((struct smb_t2_rsp *)pSMBr); -	if (rc || (pSMBr->ByteCount < 4)) { +	if (rc || get_bcc(&pSMBr->hdr) < 4) {  		rc = -EIO;	/* bad smb */  		goto QAllEAsOut;  	} @@ -5557,16 +6194,19 @@ QAllEAsRetry:  				(((char *) &pSMBr->hdr.Protocol) + data_offset);  	list_len = le32_to_cpu(ea_response_data->list_len); -	cFYI(1, "ea length %d", list_len); +	cifs_dbg(FYI, "ea length %d\n", list_len);  	if (list_len <= 8) { -		cFYI(1, "empty EA list returned from server"); +		cifs_dbg(FYI, "empty EA list returned from server\n"); +		/* didn't find the named attribute */ +		if (ea_name) +			rc = -ENODATA;  		goto QAllEAsOut;  	}  	/* make sure list_len doesn't go past end of SMB */ -	end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr); +	end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);  	if ((char *)ea_response_data + list_len > end_of_smb) { -		cFYI(1, "EA list appears to go beyond SMB"); +		cifs_dbg(FYI, "EA list appears to go beyond SMB\n");  		rc = -EIO;  		goto QAllEAsOut;  	} @@ -5583,7 +6223,7 @@ QAllEAsRetry:  		temp_ptr += 4;  		/* make sure we can read name_len and value_len */  		if (list_len < 0) { -			cFYI(1, "EA entry goes beyond length of list"); +			cifs_dbg(FYI, "EA entry goes beyond length of list\n");  			rc = -EIO;  			goto QAllEAsOut;  		} @@ -5592,13 +6232,14 @@ QAllEAsRetry:  		value_len = le16_to_cpu(temp_fea->value_len);  		list_len -= name_len + 1 + value_len;  		if (list_len < 0) { -			cFYI(1, "EA entry goes beyond length of list"); +			cifs_dbg(FYI, "EA entry goes beyond length of list\n");  			rc = -EIO;  			goto QAllEAsOut;  		}  		if (ea_name) { -			if (strncmp(ea_name, temp_ptr, name_len) == 0) { +			if (ea_name_len == name_len && +			    memcmp(ea_name, temp_ptr, name_len) == 0) {  				temp_ptr += name_len + 1;  				rc = value_len;  				if (buf_size == 0) @@ -5646,8 +6287,8 @@ QAllEAsOut:  }  int -CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName, -	     const char *ea_name, const void *ea_value, +CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, +	     const char *fileName, const char *ea_name, const void *ea_value,  	     const __u16 ea_value_len, const struct nls_table *nls_codepage,  	     int remap)  { @@ -5659,7 +6300,7 @@ CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,  	int bytes_returned = 0;  	__u16 params, param_offset, byte_count, offset, count; -	cFYI(1, "In SetEA"); +	cifs_dbg(FYI, "In SetEA\n");  SetEARetry:  	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,  		      (void **) &pSMBr); @@ -5668,8 +6309,8 @@ SetEARetry:  	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {  		name_len = -		    cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, -				     PATH_MAX, nls_codepage, remap); +		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, +				       PATH_MAX, nls_codepage, remap);  		name_len++;	/* trailing null */  		name_len *= 2;  	} else {	/* BB improve the check for buffer overruns BB */ @@ -5736,12 +6377,12 @@ SetEARetry:  	pSMB->ParameterCount = cpu_to_le16(params);  	pSMB->TotalParameterCount = pSMB->ParameterCount;  	pSMB->Reserved4 = 0; -	pSMB->hdr.smb_buf_length += byte_count; +	inc_rfc1001_len(pSMB, byte_count);  	pSMB->ByteCount = cpu_to_le16(byte_count);  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) -		cFYI(1, "SetPathInfo (EA) returned %d", rc); +		cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc);  	cifs_buf_release(pSMB); @@ -5750,5 +6391,94 @@ SetEARetry:  	return rc;  } -  #endif + +#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* BB unused temporarily */ +/* + *	Years ago the kernel added a "dnotify" function for Samba server, + *	to allow network clients (such as Windows) to display updated + *	lists of files in directory listings automatically when + *	files are added by one user when another user has the + *	same directory open on their desktop.  The Linux cifs kernel + *	client hooked into the kernel side of this interface for + *	the same reason, but ironically when the VFS moved from + *	"dnotify" to "inotify" it became harder to plug in Linux + *	network file system clients (the most obvious use case + *	for notify interfaces is when multiple users can update + *	the contents of the same directory - exactly what network + *	file systems can do) although the server (Samba) could + *	still use it.  For the short term we leave the worker + *	function ifdeffed out (below) until inotify is fixed + *	in the VFS to make it easier to plug in network file + *	system clients.  If inotify turns out to be permanently + *	incompatible for network fs clients, we could instead simply + *	expose this config flag by adding a future cifs (and smb2) notify ioctl. + */ +int CIFSSMBNotify(const unsigned int xid, struct cifs_tcon *tcon, +		  const int notify_subdirs, const __u16 netfid, +		  __u32 filter, struct file *pfile, int multishot, +		  const struct nls_table *nls_codepage) +{ +	int rc = 0; +	struct smb_com_transaction_change_notify_req *pSMB = NULL; +	struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL; +	struct dir_notify_req *dnotify_req; +	int bytes_returned; + +	cifs_dbg(FYI, "In CIFSSMBNotify for file handle %d\n", (int)netfid); +	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, +		      (void **) &pSMBr); +	if (rc) +		return rc; + +	pSMB->TotalParameterCount = 0 ; +	pSMB->TotalDataCount = 0; +	pSMB->MaxParameterCount = cpu_to_le32(2); +	pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); +	pSMB->MaxSetupCount = 4; +	pSMB->Reserved = 0; +	pSMB->ParameterOffset = 0; +	pSMB->DataCount = 0; +	pSMB->DataOffset = 0; +	pSMB->SetupCount = 4; /* single byte does not need le conversion */ +	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE); +	pSMB->ParameterCount = pSMB->TotalParameterCount; +	if (notify_subdirs) +		pSMB->WatchTree = 1; /* one byte - no le conversion needed */ +	pSMB->Reserved2 = 0; +	pSMB->CompletionFilter = cpu_to_le32(filter); +	pSMB->Fid = netfid; /* file handle always le */ +	pSMB->ByteCount = 0; + +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, +			 (struct smb_hdr *)pSMBr, &bytes_returned, +			 CIFS_ASYNC_OP); +	if (rc) { +		cifs_dbg(FYI, "Error in Notify = %d\n", rc); +	} else { +		/* Add file to outstanding requests */ +		/* BB change to kmem cache alloc */ +		dnotify_req = kmalloc( +						sizeof(struct dir_notify_req), +						 GFP_KERNEL); +		if (dnotify_req) { +			dnotify_req->Pid = pSMB->hdr.Pid; +			dnotify_req->PidHigh = pSMB->hdr.PidHigh; +			dnotify_req->Mid = pSMB->hdr.Mid; +			dnotify_req->Tid = pSMB->hdr.Tid; +			dnotify_req->Uid = pSMB->hdr.Uid; +			dnotify_req->netfid = netfid; +			dnotify_req->pfile = pfile; +			dnotify_req->filter = filter; +			dnotify_req->multishot = multishot; +			spin_lock(&GlobalMid_Lock); +			list_add_tail(&dnotify_req->lhead, +					&GlobalDnotifyReqList); +			spin_unlock(&GlobalMid_Lock); +		} else +			rc = -ENOMEM; +	} +	cifs_buf_release(pSMB); +	return rc; +} +#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 251a17c0354..20d75b8ddb2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1,7 +1,7 @@  /*   *   fs/cifs/connect.c   * - *   Copyright (C) International Business Machines  Corp., 2002,2009 + *   Copyright (C) International Business Machines  Corp., 2002,2011   *   Author(s): Steve French (sfrench@us.ibm.com)   *   *   This library is free software; you can redistribute it and/or modify @@ -37,7 +37,11 @@  #include <asm/uaccess.h>  #include <asm/processor.h>  #include <linux/inet.h> +#include <linux/module.h> +#include <keys/user-type.h>  #include <net/ipv6.h> +#include <linux/parser.h> +  #include "cifspdu.h"  #include "cifsglob.h"  #include "cifsproto.h" @@ -52,72 +56,235 @@  #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 *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 cred_uid; -	uid_t linux_uid; -	gid_t linux_gid; -	mode_t file_mode; -	mode_t dir_mode; -	unsigned secFlg; -	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; -	bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ -	bool fsc:1;	/* enable fscache */ -	bool mfsymlinks:1; /* use Minshall+French Symlinks */ -	bool multiuser:1; -	unsigned int rsize; -	unsigned int wsize; -	bool sockopt_tcp_nodelay:1; -	unsigned short int port; -	char *prepath; -	struct sockaddr_storage srcaddr; /* allow binding to a local IP */ -	struct nls_table *local_nls; -}; -  /* FIXME: should these be tunable? */  #define TLINK_ERROR_EXPIRE	(1 * HZ)  #define TLINK_IDLE_EXPIRE	(600 * HZ) -static int ipv4_connect(struct TCP_Server_Info *server); -static int ipv6_connect(struct TCP_Server_Info *server); +enum { + +	/* Mount options that take no arguments */ +	Opt_user_xattr, Opt_nouser_xattr, +	Opt_forceuid, Opt_noforceuid, +	Opt_forcegid, Opt_noforcegid, +	Opt_noblocksend, Opt_noautotune, +	Opt_hard, Opt_soft, Opt_perm, Opt_noperm, +	Opt_mapchars, Opt_nomapchars, Opt_sfu, +	Opt_nosfu, Opt_nodfs, Opt_posixpaths, +	Opt_noposixpaths, Opt_nounix, +	Opt_nocase, +	Opt_brl, Opt_nobrl, +	Opt_forcemandatorylock, Opt_setuids, +	Opt_nosetuids, Opt_dynperm, Opt_nodynperm, +	Opt_nohard, Opt_nosoft, +	Opt_nointr, Opt_intr, +	Opt_nostrictsync, Opt_strictsync, +	Opt_serverino, Opt_noserverino, +	Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl, +	Opt_acl, Opt_noacl, Opt_locallease, +	Opt_sign, Opt_seal, Opt_noac, +	Opt_fsc, Opt_mfsymlinks, +	Opt_multiuser, Opt_sloppy, Opt_nosharesock, + +	/* Mount options which take numeric value */ +	Opt_backupuid, Opt_backupgid, Opt_uid, +	Opt_cruid, Opt_gid, Opt_file_mode, +	Opt_dirmode, Opt_port, +	Opt_rsize, Opt_wsize, Opt_actimeo, + +	/* Mount options which take string value */ +	Opt_user, Opt_pass, Opt_ip, +	Opt_domain, Opt_srcaddr, Opt_iocharset, +	Opt_netbiosname, Opt_servern, +	Opt_ver, Opt_vers, Opt_sec, Opt_cache, + +	/* Mount options to be ignored */ +	Opt_ignore, + +	/* Options which could be blank */ +	Opt_blank_pass, +	Opt_blank_user, +	Opt_blank_ip, + +	Opt_err +}; + +static const match_table_t cifs_mount_option_tokens = { + +	{ Opt_user_xattr, "user_xattr" }, +	{ Opt_nouser_xattr, "nouser_xattr" }, +	{ Opt_forceuid, "forceuid" }, +	{ Opt_noforceuid, "noforceuid" }, +	{ Opt_forcegid, "forcegid" }, +	{ Opt_noforcegid, "noforcegid" }, +	{ Opt_noblocksend, "noblocksend" }, +	{ Opt_noautotune, "noautotune" }, +	{ Opt_hard, "hard" }, +	{ Opt_soft, "soft" }, +	{ Opt_perm, "perm" }, +	{ Opt_noperm, "noperm" }, +	{ Opt_mapchars, "mapchars" }, +	{ Opt_nomapchars, "nomapchars" }, +	{ Opt_sfu, "sfu" }, +	{ Opt_nosfu, "nosfu" }, +	{ Opt_nodfs, "nodfs" }, +	{ Opt_posixpaths, "posixpaths" }, +	{ Opt_noposixpaths, "noposixpaths" }, +	{ Opt_nounix, "nounix" }, +	{ Opt_nounix, "nolinux" }, +	{ Opt_nocase, "nocase" }, +	{ Opt_nocase, "ignorecase" }, +	{ Opt_brl, "brl" }, +	{ Opt_nobrl, "nobrl" }, +	{ Opt_nobrl, "nolock" }, +	{ Opt_forcemandatorylock, "forcemandatorylock" }, +	{ Opt_forcemandatorylock, "forcemand" }, +	{ Opt_setuids, "setuids" }, +	{ Opt_nosetuids, "nosetuids" }, +	{ Opt_dynperm, "dynperm" }, +	{ Opt_nodynperm, "nodynperm" }, +	{ Opt_nohard, "nohard" }, +	{ Opt_nosoft, "nosoft" }, +	{ Opt_nointr, "nointr" }, +	{ Opt_intr, "intr" }, +	{ Opt_nostrictsync, "nostrictsync" }, +	{ Opt_strictsync, "strictsync" }, +	{ Opt_serverino, "serverino" }, +	{ Opt_noserverino, "noserverino" }, +	{ Opt_rwpidforward, "rwpidforward" }, +	{ Opt_cifsacl, "cifsacl" }, +	{ Opt_nocifsacl, "nocifsacl" }, +	{ Opt_acl, "acl" }, +	{ Opt_noacl, "noacl" }, +	{ Opt_locallease, "locallease" }, +	{ Opt_sign, "sign" }, +	{ Opt_seal, "seal" }, +	{ Opt_noac, "noac" }, +	{ Opt_fsc, "fsc" }, +	{ Opt_mfsymlinks, "mfsymlinks" }, +	{ Opt_multiuser, "multiuser" }, +	{ Opt_sloppy, "sloppy" }, +	{ Opt_nosharesock, "nosharesock" }, + +	{ Opt_backupuid, "backupuid=%s" }, +	{ Opt_backupgid, "backupgid=%s" }, +	{ Opt_uid, "uid=%s" }, +	{ Opt_cruid, "cruid=%s" }, +	{ Opt_gid, "gid=%s" }, +	{ Opt_file_mode, "file_mode=%s" }, +	{ Opt_dirmode, "dirmode=%s" }, +	{ Opt_dirmode, "dir_mode=%s" }, +	{ Opt_port, "port=%s" }, +	{ Opt_rsize, "rsize=%s" }, +	{ Opt_wsize, "wsize=%s" }, +	{ Opt_actimeo, "actimeo=%s" }, + +	{ Opt_blank_user, "user=" }, +	{ Opt_blank_user, "username=" }, +	{ Opt_user, "user=%s" }, +	{ Opt_user, "username=%s" }, +	{ Opt_blank_pass, "pass=" }, +	{ Opt_blank_pass, "password=" }, +	{ Opt_pass, "pass=%s" }, +	{ Opt_pass, "password=%s" }, +	{ Opt_blank_ip, "ip=" }, +	{ Opt_blank_ip, "addr=" }, +	{ Opt_ip, "ip=%s" }, +	{ Opt_ip, "addr=%s" }, +	{ Opt_ignore, "unc=%s" }, +	{ Opt_ignore, "target=%s" }, +	{ Opt_ignore, "path=%s" }, +	{ Opt_domain, "dom=%s" }, +	{ Opt_domain, "domain=%s" }, +	{ Opt_domain, "workgroup=%s" }, +	{ Opt_srcaddr, "srcaddr=%s" }, +	{ Opt_ignore, "prefixpath=%s" }, +	{ Opt_iocharset, "iocharset=%s" }, +	{ Opt_netbiosname, "netbiosname=%s" }, +	{ Opt_servern, "servern=%s" }, +	{ Opt_ver, "ver=%s" }, +	{ Opt_vers, "vers=%s" }, +	{ Opt_sec, "sec=%s" }, +	{ Opt_cache, "cache=%s" }, + +	{ Opt_ignore, "cred" }, +	{ Opt_ignore, "credentials" }, +	{ Opt_ignore, "cred=%s" }, +	{ Opt_ignore, "credentials=%s" }, +	{ Opt_ignore, "guest" }, +	{ Opt_ignore, "rw" }, +	{ Opt_ignore, "ro" }, +	{ Opt_ignore, "suid" }, +	{ Opt_ignore, "nosuid" }, +	{ Opt_ignore, "exec" }, +	{ Opt_ignore, "noexec" }, +	{ Opt_ignore, "nodev" }, +	{ Opt_ignore, "noauto" }, +	{ Opt_ignore, "dev" }, +	{ Opt_ignore, "mand" }, +	{ Opt_ignore, "nomand" }, +	{ Opt_ignore, "_netdev" }, + +	{ Opt_err, NULL } +}; + +enum { +	Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p, +	Opt_sec_ntlmsspi, Opt_sec_ntlmssp, +	Opt_ntlm, Opt_sec_ntlmi, Opt_sec_ntlmv2, +	Opt_sec_ntlmv2i, Opt_sec_lanman, +	Opt_sec_none, + +	Opt_sec_err +}; + +static const match_table_t cifs_secflavor_tokens = { +	{ Opt_sec_krb5, "krb5" }, +	{ Opt_sec_krb5i, "krb5i" }, +	{ Opt_sec_krb5p, "krb5p" }, +	{ Opt_sec_ntlmsspi, "ntlmsspi" }, +	{ Opt_sec_ntlmssp, "ntlmssp" }, +	{ Opt_ntlm, "ntlm" }, +	{ Opt_sec_ntlmi, "ntlmi" }, +	{ Opt_sec_ntlmv2, "nontlm" }, +	{ Opt_sec_ntlmv2, "ntlmv2" }, +	{ Opt_sec_ntlmv2i, "ntlmv2i" }, +	{ Opt_sec_lanman, "lanman" }, +	{ Opt_sec_none, "none" }, + +	{ Opt_sec_err, NULL } +}; + +/* cache flavors */ +enum { +	Opt_cache_loose, +	Opt_cache_strict, +	Opt_cache_none, +	Opt_cache_err +}; + +static const match_table_t cifs_cacheflavor_tokens = { +	{ Opt_cache_loose, "loose" }, +	{ Opt_cache_strict, "strict" }, +	{ Opt_cache_none, "none" }, +	{ Opt_cache_err, NULL } +}; + +static const match_table_t cifs_smb_version_tokens = { +	{ Smb_1, SMB1_VERSION_STRING }, +	{ Smb_20, SMB20_VERSION_STRING}, +	{ Smb_21, SMB21_VERSION_STRING }, +	{ Smb_30, SMB30_VERSION_STRING }, +	{ Smb_302, SMB302_VERSION_STRING }, +}; + +static int ip_connect(struct TCP_Server_Info *server); +static int generic_ip_connect(struct TCP_Server_Info *server);  static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);  static void cifs_prune_tlinks(struct work_struct *work); +static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, +					const char *devname);  /*   * cifs tcp session reconnection @@ -127,14 +294,15 @@ static void cifs_prune_tlinks(struct work_struct *work);   * reconnect tcp session   * wake up waiters on reconnection? - (not needed currently)   */ -static int +int  cifs_reconnect(struct TCP_Server_Info *server)  {  	int rc = 0;  	struct list_head *tmp, *tmp2; -	struct cifsSesInfo *ses; -	struct cifsTconInfo *tcon; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon;  	struct mid_q_entry *mid_entry; +	struct list_head retry_list;  	spin_lock(&GlobalMid_Lock);  	if (server->tcpStatus == CifsExiting) { @@ -146,31 +314,37 @@ cifs_reconnect(struct TCP_Server_Info *server)  		server->tcpStatus = CifsNeedReconnect;  	spin_unlock(&GlobalMid_Lock);  	server->maxBuf = 0; +#ifdef CONFIG_CIFS_SMB2 +	server->max_read = 0; +#endif -	cFYI(1, "Reconnecting tcp session"); +	cifs_dbg(FYI, "Reconnecting tcp session\n");  	/* before reconnecting the tcp session, mark the smb session (uid)  		and the tid bad so they are not used until reconnected */ +	cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", +		 __func__);  	spin_lock(&cifs_tcp_ses_lock);  	list_for_each(tmp, &server->smb_ses_list) { -		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); +		ses = list_entry(tmp, struct cifs_ses, 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 = list_entry(tmp2, struct cifs_tcon, tcon_list);  			tcon->need_reconnect = true;  		}  	}  	spin_unlock(&cifs_tcp_ses_lock); +  	/* do not want to be sending data on a socket we are freeing */ +	cifs_dbg(FYI, "%s: tearing down socket\n", __func__);  	mutex_lock(&server->srv_mutex);  	if (server->ssocket) { -		cFYI(1, "State: 0x%x Flags: 0x%lx", server->ssocket->state, -			server->ssocket->flags); +		cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", +			 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); +		cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", +			 server->ssocket->state, server->ssocket->flags);  		sock_release(server->ssocket);  		server->ssocket = NULL;  	} @@ -179,466 +353,342 @@ cifs_reconnect(struct TCP_Server_Info *server)  	kfree(server->session_key.response);  	server->session_key.response = NULL;  	server->session_key.len = 0; +	server->lstrp = jiffies; +	mutex_unlock(&server->srv_mutex); +	/* mark submitted MIDs for retry and issue callback */ +	INIT_LIST_HEAD(&retry_list); +	cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);  	spin_lock(&GlobalMid_Lock); -	list_for_each(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; -		} +	list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { +		mid_entry = list_entry(tmp, struct mid_q_entry, qhead); +		if (mid_entry->mid_state == MID_REQUEST_SUBMITTED) +			mid_entry->mid_state = MID_RETRY_NEEDED; +		list_move(&mid_entry->qhead, &retry_list);  	}  	spin_unlock(&GlobalMid_Lock); -	mutex_unlock(&server->srv_mutex); -	while ((server->tcpStatus != CifsExiting) && -	       (server->tcpStatus != CifsGood)) { +	cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); +	list_for_each_safe(tmp, tmp2, &retry_list) { +		mid_entry = list_entry(tmp, struct mid_q_entry, qhead); +		list_del_init(&mid_entry->qhead); +		mid_entry->callback(mid_entry); +	} + +	do {  		try_to_freeze(); -		if (server->addr.sockAddr6.sin6_family == AF_INET6) -			rc = ipv6_connect(server); -		else -			rc = ipv4_connect(server); + +		/* we should try only the port we connected to before */ +		mutex_lock(&server->srv_mutex); +		rc = generic_ip_connect(server);  		if (rc) { -			cFYI(1, "reconnect error %d", rc); +			cifs_dbg(FYI, "reconnect error %d\n", rc);  			msleep(3000);  		} else {  			atomic_inc(&tcpSesReconnectCount);  			spin_lock(&GlobalMid_Lock);  			if (server->tcpStatus != CifsExiting) -				server->tcpStatus = CifsGood; +				server->tcpStatus = CifsNeedNegotiate;  			spin_unlock(&GlobalMid_Lock); -	/*		atomic_set(&server->inFlight,0);*/ -			wake_up(&server->response_q);  		} -	} +		mutex_unlock(&server->srv_mutex); +	} while (server->tcpStatus == CifsNeedReconnect); +  	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) +static void +cifs_echo_request(struct work_struct *work)  { -	struct smb_t2_rsp *pSMBt; -	int total_data_size; -	int data_in_this_rsp; -	int remaining; - -	if (pSMB->Command != SMB_COM_TRANSACTION2) -		return 0; +	int rc; +	struct TCP_Server_Info *server = container_of(work, +					struct TCP_Server_Info, echo.work); -	/* 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; -	} +	/* +	 * We cannot send an echo if it is disabled or until the +	 * NEGOTIATE_PROTOCOL request is done, which is indicated by +	 * server->ops->need_neg() == true. Also, no need to ping if +	 * we got a response recently. +	 */ +	if (!server->ops->need_neg || server->ops->need_neg(server) || +	    (server->ops->can_echo && !server->ops->can_echo(server)) || +	    time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ)) +		goto requeue_echo; -	pSMBt = (struct smb_t2_rsp *)pSMB; +	rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; +	if (rc) +		cifs_dbg(FYI, "Unable to send echo request to server: %s\n", +			 server->hostname); -	total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); -	data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount); +requeue_echo: +	queue_delayed_work(cifsiod_wq, &server->echo, SMB_ECHO_INTERVAL); +} -	remaining = total_data_size - data_in_this_rsp; +static bool +allocate_buffers(struct TCP_Server_Info *server) +{ +	if (!server->bigbuf) { +		server->bigbuf = (char *)cifs_buf_get(); +		if (!server->bigbuf) { +			cifs_dbg(VFS, "No memory for large SMB response\n"); +			msleep(3000); +			/* retry will check if exiting */ +			return false; +		} +	} else if (server->large_buf) { +		/* we are reusing a dirty large buf, clear its start */ +		memset(server->bigbuf, 0, HEADER_SIZE(server)); +	} -	if (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; +	if (!server->smallbuf) { +		server->smallbuf = (char *)cifs_small_buf_get(); +		if (!server->smallbuf) { +			cifs_dbg(VFS, "No memory for SMB response\n"); +			msleep(1000); +			/* retry will check if exiting */ +			return false;  		} -		return remaining; +		/* beginning of smb buffer is cleared in our buf_get */ +	} else { +		/* if existing small buf clear beginning */ +		memset(server->smallbuf, 0, HEADER_SIZE(server));  	} + +	return true;  } -static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) +static bool +server_unresponsive(struct TCP_Server_Info *server)  { -	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"); +	/* +	 * We need to wait 2 echo intervals to make sure we handle such +	 * situations right: +	 * 1s  client sends a normal SMB request +	 * 2s  client gets a response +	 * 30s echo workqueue job pops, and decides we got a response recently +	 *     and don't need to send another +	 * ... +	 * 65s kernel_recvmsg times out, and we see that we haven't gotten +	 *     a response in >60s. +	 */ +	if (server->tcpStatus == CifsGood && +	    time_after(jiffies, server->lstrp + 2 * SMB_ECHO_INTERVAL)) { +		cifs_dbg(VFS, "Server %s has not responded in %d seconds. Reconnecting...\n", +			 server->hostname, (2 * SMB_ECHO_INTERVAL) / HZ); +		cifs_reconnect(server); +		wake_up(&server->response_q); +		return true;  	} -	total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount); - -	remaining = total_data_size - total_in_buf; +	return false; +} -	if (remaining < 0) -		return -EINVAL; +/* + * kvec_array_init - clone a kvec array, and advance into it + * @new:	pointer to memory for cloned array + * @iov:	pointer to original array + * @nr_segs:	number of members in original array + * @bytes:	number of bytes to advance into the cloned array + * + * This function will copy the array provided in iov to a section of memory + * and advance the specified number of bytes into the new array. It returns + * the number of segments in the new array. "new" must be at least as big as + * the original iov array. + */ +static unsigned int +kvec_array_init(struct kvec *new, struct kvec *iov, unsigned int nr_segs, +		size_t bytes) +{ +	size_t base = 0; -	if (remaining == 0) /* nothing to do, ignore */ -		return 0; +	while (bytes || !iov->iov_len) { +		int copy = min(bytes, iov->iov_len); -	total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount); -	if (remaining < total_in_buf2) { -		cFYI(1, "transact2 2nd response contains too much data"); +		bytes -= copy; +		base += copy; +		if (iov->iov_len == base) { +			iov++; +			nr_segs--; +			base = 0; +		}  	} +	memcpy(new, iov, sizeof(*iov) * nr_segs); +	new->iov_base += base; +	new->iov_len -= base; +	return nr_segs; +} -	/* 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; +static struct kvec * +get_server_iovec(struct TCP_Server_Info *server, unsigned int nr_segs) +{ +	struct kvec *new_iov; -	if (remaining == total_in_buf2) { -		cFYI(1, "found the last secondary response"); -		return 0; /* we are done */ -	} else /* more responses to go */ -		return 1; +	if (server->iov && nr_segs <= server->nr_iov) +		return server->iov; +	/* not big enough -- allocate a new one and release the old */ +	new_iov = kmalloc(sizeof(*new_iov) * nr_segs, GFP_NOFS); +	if (new_iov) { +		kfree(server->iov); +		server->iov = new_iov; +		server->nr_iov = nr_segs; +	} +	return new_iov;  } -static int -cifs_demultiplex_thread(struct TCP_Server_Info *server) +int +cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig, +		       unsigned int nr_segs, unsigned int to_read)  { -	int length; -	unsigned int pdu_length, total_read; -	struct smb_hdr *smb_buffer = NULL; -	struct smb_hdr *bigbuf = NULL; -	struct smb_hdr *smallbuf = NULL; +	int length = 0; +	int total_read; +	unsigned int segs;  	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; +	struct kvec *iov; -	current->flags |= PF_MEMALLOC; -	cFYI(1, "Demultiplex PID: %d", task_pid_nr(current)); +	iov = get_server_iovec(server, nr_segs); +	if (!iov) +		return -ENOMEM; -	length = atomic_inc_return(&tcpSesAllocCount); -	if (length > 1) -		mempool_resize(cifs_req_poolp, length + cifs_min_rcv, -				GFP_KERNEL); +	smb_msg.msg_control = NULL; +	smb_msg.msg_controllen = 0; -	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)); +	for (total_read = 0; to_read; total_read += length, to_read -= length) { +		try_to_freeze(); + +		if (server_unresponsive(server)) { +			total_read = -EAGAIN; +			break;  		} -		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? */); +		segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + +		length = kernel_recvmsg(server->ssocket, &smb_msg, +					iov, segs, to_read, 0);  		if (server->tcpStatus == CifsExiting) { +			total_read = -ESHUTDOWN;  			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; +			total_read = -EAGAIN; +			break;  		} else if (length == -ERESTARTSYS ||  			   length == -EAGAIN ||  			   length == -EINTR) { -			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) { -			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); -			/* give server a second to clean up  */ -			msleep(1000); -			/* always try 445 first on reconnect since we get NACK -			 * on some if we ever connected to port 139 (the NACK -			 * is since we do not begin with RFC1001 session -			 * initialize frame) +			/* +			 * Minimum sleep to prevent looping, allowing socket +			 * to clear and app threads to set tcpStatus +			 * CifsNeedReconnect if server hung.  			 */ -			cifs_set_port((struct sockaddr *) -					&server->addr.sockAddr, 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; +			usleep_range(1000, 2000); +			length = 0;  			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); +		} else if (length <= 0) { +			cifs_dbg(FYI, "Received no data or error: expecting %d\n" +				 "got %d", to_read, length);  			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) { -				/* 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 || -				   length == -EINTR) { -				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) +			total_read = -EAGAIN;  			break; -		else if (reconnect == 1) -			continue; +		} +	} +	return total_read; +} -		length += 4; /* account for rfc1002 hdr */ +int +cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, +		      unsigned int to_read) +{ +	struct kvec iov; +	iov.iov_base = buf; +	iov.iov_len = to_read; -		dump_smb(smb_buffer, length); -		if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) { -			cifs_dump_mem("Bad SMB: ", smb_buffer, 48); -			continue; -		} +	return cifs_readv_from_socket(server, &iov, 1, to_read); +} +static bool +is_smb_response(struct TCP_Server_Info *server, unsigned char type) +{ +	/* +	 * The first byte big endian of the length field, +	 * is actually not part of the length but the type +	 * with the most common, zero, as regular data. +	 */ +	switch (type) { +	case RFC1002_SESSION_MESSAGE: +		/* Regular SMB response */ +		return true; +	case RFC1002_SESSION_KEEP_ALIVE: +		cifs_dbg(FYI, "RFC 1002 session keep alive\n"); +		break; +	case RFC1002_POSITIVE_SESSION_RESPONSE: +		cifs_dbg(FYI, "RFC 1002 positive session response\n"); +		break; +	case RFC1002_NEGATIVE_SESSION_RESPONSE: +		/* +		 * We get this from Windows 98 instead of an error on +		 * SMB negprot response. +		 */ +		cifs_dbg(FYI, "RFC 1002 negative session response\n"); +		/* give server a second to clean up */ +		msleep(1000); +		/* +		 * Always try 445 first on reconnect since we get NACK +		 * on some if we ever connected to port 139 (the NACK +		 * is since we do not begin with RFC1001 session +		 * initialize frame). +		 */ +		cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); +		cifs_reconnect(server); +		wake_up(&server->response_q); +		break; +	default: +		cifs_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); +		cifs_reconnect(server); +	} -		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); +	return false; +} -			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; +void +dequeue_mid(struct mid_q_entry *mid, bool malformed) +{  #ifdef CONFIG_CIFS_STATS2 -				mid_entry->when_received = jiffies; +	mid->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 */ +	spin_lock(&GlobalMid_Lock); +	if (!malformed) +		mid->mid_state = MID_RESPONSE_RECEIVED; +	else +		mid->mid_state = MID_RESPONSE_MALFORMED; +	list_del_init(&mid->qhead); +	spin_unlock(&GlobalMid_Lock); +} -		} -	} /* end while !EXITING */ +static void +handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, +	   char *buf, int malformed) +{ +	if (server->ops->check_trans2 && +	    server->ops->check_trans2(mid, server, buf, malformed)) +		return; +	mid->resp_buf = buf; +	mid->large_buf = server->large_buf; +	/* Was previous buf put in mpx struct for multi-rsp? */ +	if (!mid->multiRsp) { +		/* smb buffer will be freed by user thread */ +		if (server->large_buf) +			server->bigbuf = NULL; +		else +			server->smallbuf = NULL; +	} +	dequeue_mid(mid, malformed); +} + +static void clean_demultiplex_info(struct TCP_Server_Info *server) +{ +	int length;  	/* take it off the list, if it's not already */  	spin_lock(&cifs_tcp_ses_lock); @@ -651,107 +701,235 @@ multi_t2_fnd:  	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.  */ +	spin_lock(&server->req_lock); +	if (server->credits <= 0) +		server->credits = 1; +	spin_unlock(&server->req_lock); +	/* +	 * Although there should not be any requests blocked on this queue it +	 * can not hurt to be paranoid and try to wake up requests that may +	 * haven been blocked when more than 50 at time were on the wire to the +	 * same server - they now will see the session is in exit state and get +	 * out of SendReceive. +	 */  	wake_up_all(&server->request_q);  	/* give those requests time to exit */  	msleep(125);  	if (server->ssocket) { -		sock_release(csocket); +		sock_release(server->ssocket);  		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 -	 */ -	spin_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; -		} -		spin_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; -		} +	if (!list_empty(&server->pending_mid_q)) { +		struct list_head dispose_list; +		struct mid_q_entry *mid_entry; +		struct list_head *tmp, *tmp2; +		INIT_LIST_HEAD(&dispose_list);  		spin_lock(&GlobalMid_Lock); -		list_for_each(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); -			} +		list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { +			mid_entry = list_entry(tmp, struct mid_q_entry, qhead); +			cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid); +			mid_entry->mid_state = MID_SHUTDOWN; +			list_move(&mid_entry->qhead, &dispose_list);  		}  		spin_unlock(&GlobalMid_Lock); -		spin_unlock(&cifs_tcp_ses_lock); + +		/* now walk dispose list and issue callbacks */ +		list_for_each_safe(tmp, tmp2, &dispose_list) { +			mid_entry = list_entry(tmp, struct mid_q_entry, qhead); +			cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid); +			list_del_init(&mid_entry->qhead); +			mid_entry->callback(mid_entry); +		}  		/* 1/8th of sec is more than enough time for them to exit */  		msleep(125);  	}  	if (!list_empty(&server->pending_mid_q)) { -		/* mpx threads have not exited yet give them -		at least the smb send timeout time for long ops */ -		/* due to delays on oplock break requests, we need -		to wait at least 45 seconds before giving up -		on a request getting a response and going ahead -		and killing cifsd */ -		cFYI(1, "Wait for exit from demultiplex thread"); +		/* +		 * mpx threads have not exited yet give them at least the smb +		 * send timeout time for long ops. +		 * +		 * Due to delays on oplock break requests, we need to wait at +		 * least 45 seconds before giving up on a request getting a +		 * response and going ahead and killing cifsd. +		 */ +		cifs_dbg(FYI, "Wait for exit from demultiplex thread\n");  		msleep(46000); -		/* if threads still have not exited they are probably never -		coming home not much else we can do but free the memory */ -	} - -	/* 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 */ -	spin_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; +		/* +		 * If threads still have not exited they are probably never +		 * coming home not much else we can do but free the memory. +		 */  	} -	spin_unlock(&cifs_tcp_ses_lock);  	kfree(server->hostname); -	task_to_wake = xchg(&server->tsk, NULL); +	kfree(server->iov);  	kfree(server);  	length = atomic_dec_return(&tcpSesAllocCount); -	if (length  > 0) +	if (length > 0)  		mempool_resize(cifs_req_poolp, length + cifs_min_rcv,  				GFP_KERNEL); +} + +static int +standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ +	int length; +	char *buf = server->smallbuf; +	unsigned int pdu_length = get_rfc1002_length(buf); + +	/* make sure this will fit in a large buffer */ +	if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 4) { +		cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); +		cifs_reconnect(server); +		wake_up(&server->response_q); +		return -EAGAIN; +	} + +	/* switch to large buffer if too big for a small one */ +	if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { +		server->large_buf = true; +		memcpy(server->bigbuf, buf, server->total_read); +		buf = server->bigbuf; +	} + +	/* now read the rest */ +	length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, +				pdu_length - HEADER_SIZE(server) + 1 + 4); +	if (length < 0) +		return length; +	server->total_read += length; + +	dump_smb(buf, server->total_read); + +	/* +	 * We know that we received enough to get to the MID as we +	 * checked the pdu_length earlier. Now check to see +	 * if the rest of the header is OK. We borrow the length +	 * var for the rest of the loop to avoid a new stack var. +	 * +	 * 48 bytes is enough to display the header and a little bit +	 * into the payload for debugging purposes. +	 */ +	length = server->ops->check_message(buf, server->total_read); +	if (length != 0) +		cifs_dump_mem("Bad SMB: ", buf, +			min_t(unsigned int, server->total_read, 48)); + +	if (server->ops->is_status_pending && +	    server->ops->is_status_pending(buf, server, length)) +		return -1; + +	if (!mid) +		return length; + +	handle_mid(mid, server, buf, length); +	return 0; +} + +static int +cifs_demultiplex_thread(void *p) +{ +	int length; +	struct TCP_Server_Info *server = p; +	unsigned int pdu_length; +	char *buf = NULL; +	struct task_struct *task_to_wake = NULL; +	struct mid_q_entry *mid_entry; + +	current->flags |= PF_MEMALLOC; +	cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); + +	length = atomic_inc_return(&tcpSesAllocCount); +	if (length > 1) +		mempool_resize(cifs_req_poolp, length + cifs_min_rcv, +				GFP_KERNEL); + +	set_freezable(); +	while (server->tcpStatus != CifsExiting) { +		if (try_to_freeze()) +			continue; + +		if (!allocate_buffers(server)) +			continue; + +		server->large_buf = false; +		buf = server->smallbuf; +		pdu_length = 4; /* enough to get RFC1001 header */ + +		length = cifs_read_from_socket(server, buf, pdu_length); +		if (length < 0) +			continue; +		server->total_read = length; + +		/* +		 * The right amount was read from socket - 4 bytes, +		 * so we can now interpret the length field. +		 */ +		pdu_length = get_rfc1002_length(buf); + +		cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); +		if (!is_smb_response(server, buf[0])) +			continue; + +		/* make sure we have enough to get to the MID */ +		if (pdu_length < HEADER_SIZE(server) - 1 - 4) { +			cifs_dbg(VFS, "SMB response too short (%u bytes)\n", +				 pdu_length); +			cifs_reconnect(server); +			wake_up(&server->response_q); +			continue; +		} + +		/* read down to the MID */ +		length = cifs_read_from_socket(server, buf + 4, +					       HEADER_SIZE(server) - 1 - 4); +		if (length < 0) +			continue; +		server->total_read += length; + +		mid_entry = server->ops->find_mid(server, buf); + +		if (!mid_entry || !mid_entry->receive) +			length = standard_receive3(server, mid_entry); +		else +			length = mid_entry->receive(server, mid_entry); + +		if (length < 0) +			continue; + +		if (server->large_buf) +			buf = server->bigbuf; + +		server->lstrp = jiffies; +		if (mid_entry != NULL) { +			if (!mid_entry->multiRsp || mid_entry->multiEnd) +				mid_entry->callback(mid_entry); +		} else if (!server->ops->is_oplock_break || +			   !server->ops->is_oplock_break(buf, server)) { +			cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", +				 atomic_read(&midCount)); +			cifs_dump_mem("Received Data is: ", buf, +				      HEADER_SIZE(server)); +#ifdef CONFIG_CIFS_DEBUG2 +			if (server->ops->dump_detail) +				server->ops->dump_detail(buf); +			cifs_dump_mids(server); +#endif /* CIFS_DEBUG2 */ + +		} +	} /* end while !EXITING */ + +	/* buffer usually freed in free_mid - need to free it here on exit */ +	cifs_buf_release(server->bigbuf); +	if (server->smallbuf) /* no sense logging a debug message if NULL */ +		cifs_small_buf_release(server->smallbuf); + +	task_to_wake = xchg(&server->tsk, NULL); +	clean_demultiplex_info(server);  	/* if server->tsk was NULL then wait for a signal before exiting */  	if (!task_to_wake) { @@ -794,36 +972,258 @@ extract_hostname(const char *unc)  	return dst;  } +static int get_option_ul(substring_t args[], unsigned long *option) +{ +	int rc; +	char *string; + +	string = match_strdup(args); +	if (string == NULL) +		return -ENOMEM; +	rc = kstrtoul(string, 0, option); +	kfree(string); + +	return rc; +} + +static int get_option_uid(substring_t args[], kuid_t *result) +{ +	unsigned long value; +	kuid_t uid; +	int rc; + +	rc = get_option_ul(args, &value); +	if (rc) +		return rc; + +	uid = make_kuid(current_user_ns(), value); +	if (!uid_valid(uid)) +		return -EINVAL; + +	*result = uid; +	return 0; +} + +static int get_option_gid(substring_t args[], kgid_t *result) +{ +	unsigned long value; +	kgid_t gid; +	int rc; + +	rc = get_option_ul(args, &value); +	if (rc) +		return rc; + +	gid = make_kgid(current_user_ns(), value); +	if (!gid_valid(gid)) +		return -EINVAL; + +	*result = gid; +	return 0; +} + +static int cifs_parse_security_flavors(char *value, +				       struct smb_vol *vol) +{ + +	substring_t args[MAX_OPT_ARGS]; + +	/* +	 * With mount options, the last one should win. Reset any existing +	 * settings back to default. +	 */ +	vol->sectype = Unspecified; +	vol->sign = false; + +	switch (match_token(value, cifs_secflavor_tokens, args)) { +	case Opt_sec_krb5p: +		cifs_dbg(VFS, "sec=krb5p is not supported!\n"); +		return 1; +	case Opt_sec_krb5i: +		vol->sign = true; +		/* Fallthrough */ +	case Opt_sec_krb5: +		vol->sectype = Kerberos; +		break; +	case Opt_sec_ntlmsspi: +		vol->sign = true; +		/* Fallthrough */ +	case Opt_sec_ntlmssp: +		vol->sectype = RawNTLMSSP; +		break; +	case Opt_sec_ntlmi: +		vol->sign = true; +		/* Fallthrough */ +	case Opt_ntlm: +		vol->sectype = NTLM; +		break; +	case Opt_sec_ntlmv2i: +		vol->sign = true; +		/* Fallthrough */ +	case Opt_sec_ntlmv2: +		vol->sectype = NTLMv2; +		break; +#ifdef CONFIG_CIFS_WEAK_PW_HASH +	case Opt_sec_lanman: +		vol->sectype = LANMAN; +		break; +#endif +	case Opt_sec_none: +		vol->nullauth = 1; +		break; +	default: +		cifs_dbg(VFS, "bad security option: %s\n", value); +		return 1; +	} + +	return 0; +} + +static int +cifs_parse_cache_flavor(char *value, struct smb_vol *vol) +{ +	substring_t args[MAX_OPT_ARGS]; + +	switch (match_token(value, cifs_cacheflavor_tokens, args)) { +	case Opt_cache_loose: +		vol->direct_io = false; +		vol->strict_io = false; +		break; +	case Opt_cache_strict: +		vol->direct_io = false; +		vol->strict_io = true; +		break; +	case Opt_cache_none: +		vol->direct_io = true; +		vol->strict_io = false; +		break; +	default: +		cifs_dbg(VFS, "bad cache= option: %s\n", value); +		return 1; +	} +	return 0; +} + +static int +cifs_parse_smb_version(char *value, struct smb_vol *vol) +{ +	substring_t args[MAX_OPT_ARGS]; + +	switch (match_token(value, cifs_smb_version_tokens, args)) { +	case Smb_1: +		vol->ops = &smb1_operations; +		vol->vals = &smb1_values; +		break; +#ifdef CONFIG_CIFS_SMB2 +	case Smb_20: +		vol->ops = &smb20_operations; +		vol->vals = &smb20_values; +		break; +	case Smb_21: +		vol->ops = &smb21_operations; +		vol->vals = &smb21_values; +		break; +	case Smb_30: +		vol->ops = &smb30_operations; +		vol->vals = &smb30_values; +		break; +	case Smb_302: +		vol->ops = &smb30_operations; /* currently identical with 3.0 */ +		vol->vals = &smb302_values; +		break; +#endif +	default: +		cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value); +		return 1; +	} +	return 0; +} + +/* + * Parse a devname into substrings and populate the vol->UNC and vol->prepath + * fields with the result. Returns 0 on success and an error otherwise. + */  static int -cifs_parse_mount_options(char *options, const char *devname, +cifs_parse_devname(const char *devname, struct smb_vol *vol) +{ +	char *pos; +	const char *delims = "/\\"; +	size_t len; + +	/* make sure we have a valid UNC double delimiter prefix */ +	len = strspn(devname, delims); +	if (len != 2) +		return -EINVAL; + +	/* find delimiter between host and sharename */ +	pos = strpbrk(devname + 2, delims); +	if (!pos) +		return -EINVAL; + +	/* skip past delimiter */ +	++pos; + +	/* now go until next delimiter or end of string */ +	len = strcspn(pos, delims); + +	/* move "pos" up to delimiter or NULL */ +	pos += len; +	vol->UNC = kstrndup(devname, pos - devname, GFP_KERNEL); +	if (!vol->UNC) +		return -ENOMEM; + +	convert_delimiter(vol->UNC, '\\'); + +	/* If pos is NULL, or is a bogus trailing delimiter then no prepath */ +	if (!*pos++ || !*pos) +		return 0; + +	vol->prepath = kstrdup(pos, GFP_KERNEL); +	if (!vol->prepath) +		return -ENOMEM; + +	return 0; +} + +static int +cifs_parse_mount_options(const char *mountdata, const char *devname,  			 struct smb_vol *vol)  { -	char *value; -	char *data; +	char *data, *end; +	char *mountdata_copy = NULL, *options;  	unsigned int  temp_len, i, j;  	char separator[2];  	short int override_uid = -1;  	short int override_gid = -1;  	bool uid_specified = false;  	bool gid_specified = false; +	bool sloppy = false; +	char *invalid = NULL; +	char *nodename = utsname()->nodename; +	char *string = NULL; +	char *tmp_end, *value; +	char delim; +	bool got_ip = false; +	unsigned short port = 0; +	struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr;  	separator[0] = ',';  	separator[1] = 0; +	delim = separator[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; +	/* ensure we always start with zeroed-out smb_vol */ +	memset(vol, 0, sizeof(*vol)); + +	/* +	 * does not have to be perfect mapping since field is +	 * informational, only used for servers that do not support +	 * port 445 and it can be overridden at mount time +	 */ +	memset(vol->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); +	for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++) +		vol->source_rfc1001_name[i] = toupper(nodename[i]); + +	vol->source_rfc1001_name[RFC1001_NAME_LEN] = 0;  	/* null target name indicates to use *SMBSERVR default called name  	   if we end up sending RFC1001 session initialize */  	vol->target_rfc1001_name[0] = 0; @@ -840,563 +1240,635 @@ cifs_parse_mount_options(char *options, const char *devname,  	/* default to using server inode numbers where available */  	vol->server_ino = 1; -	if (!options) -		return 1; +	/* default is to use strict cifs caching semantics */ +	vol->strict_io = true; + +	vol->actimeo = CIFS_DEF_ACTIMEO; + +	/* FIXME: add autonegotiation -- for now, SMB1 is default */ +	vol->ops = &smb1_operations; +	vol->vals = &smb1_values; + +	if (!mountdata) +		goto cifs_parse_mount_err; + +	mountdata_copy = kstrndup(mountdata, PAGE_SIZE, GFP_KERNEL); +	if (!mountdata_copy) +		goto cifs_parse_mount_err; + +	options = mountdata_copy; +	end = options + strlen(options);  	if (strncmp(options, "sep=", 4) == 0) {  		if (options[4] != 0) {  			separator[0] = options[4];  			options += 5;  		} else { -			cFYI(1, "Null separator not allowed"); +			cifs_dbg(FYI, "Null separator not allowed\n");  		}  	} +	vol->backupuid_specified = false; /* no backup intent for a user */ +	vol->backupgid_specified = false; /* no backup intent for a group */ + +	switch (cifs_parse_devname(devname, vol)) { +	case 0: +		break; +	case -ENOMEM: +		cifs_dbg(VFS, "Unable to allocate memory for devname.\n"); +		goto cifs_parse_mount_err; +	case -EINVAL: +		cifs_dbg(VFS, "Malformed UNC in devname.\n"); +		goto cifs_parse_mount_err; +	default: +		cifs_dbg(VFS, "Unknown error parsing devname.\n"); +		goto cifs_parse_mount_err; +	}  	while ((data = strsep(&options, separator)) != NULL) { +		substring_t args[MAX_OPT_ARGS]; +		unsigned long option; +		int token; +  		if (!*data)  			continue; -		if ((value = strchr(data, '=')) != NULL) -			*value++ = '\0'; -		/* Have to parse this before we parse for "user" */ -		if (strnicmp(data, "user_xattr", 10) == 0) { +		token = match_token(data, cifs_mount_option_tokens, args); + +		switch (token) { + +		/* Ingnore the following */ +		case Opt_ignore: +			break; + +		/* Boolean values */ +		case Opt_user_xattr:  			vol->no_xattr = 0; -		} else if (strnicmp(data, "nouser_xattr", 12) == 0) { +			break; +		case Opt_nouser_xattr:  			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) || -			   !strnicmp(data, "addr", 4)) { -			if (!value || !*value) { -				vol->UNCip = NULL; -			} else if (strnlen(value, INET6_ADDRSTRLEN) < -							INET6_ADDRSTRLEN) { -				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; -#ifdef CONFIG_CIFS_EXPERIMENTAL -			} else if (strnicmp(value, "ntlmsspi", 8) == 0) { -				vol->secFlg |= CIFSSEC_MAY_NTLMSSP | -					CIFSSEC_MUST_SIGN; -			} else if (strnicmp(value, "ntlmssp", 7) == 0) { -				vol->secFlg |= CIFSSEC_MAY_NTLMSSP; -#endif -			} 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, "srcaddr", 7) == 0) { -			vol->srcaddr.ss_family = AF_UNSPEC; - -			if (!value || !*value) { -				printk(KERN_WARNING "CIFS: srcaddr value" -				       " not specified.\n"); -				return 1;	/* needs_arg; */ -			} -			i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, -						 value, strlen(value)); -			if (i == 0) { -				printk(KERN_WARNING "CIFS:  Could not parse" -				       " srcaddr: %s\n", -				       value); -				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) && value && *value) { -			vol->linux_uid = simple_strtoul(value, &value, 0); -			uid_specified = true; -		} else if (!strnicmp(data, "forceuid", 8)) { +			break; +		case Opt_forceuid:  			override_uid = 1; -		} else if (!strnicmp(data, "noforceuid", 10)) { +			break; +		case Opt_noforceuid:  			override_uid = 0; -		} else if (!strnicmp(data, "gid", 3) && value && *value) { -			vol->linux_gid = simple_strtoul(value, &value, 0); -			gid_specified = true; -		} else if (!strnicmp(data, "forcegid", 8)) { +			break; +		case Opt_forcegid:  			override_gid = 1; -		} else if (!strnicmp(data, "noforcegid", 10)) { +			break; +		case Opt_noforcegid:  			override_gid = 0; -		} 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) { -				cERROR(1, "no socket option specified"); -				continue; -			} else if (strnicmp(value, "TCP_NODELAY", 11) == 0) { -				vol->sockopt_tcp_nodelay = 1; -			} -		} 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) { -			/* ignore */ -		} else if (strnicmp(data, "ro", 2) == 0) { -			/* ignore */ -		} else if (strnicmp(data, "noblocksend", 11) == 0) { +			break; +		case Opt_noblocksend:  			vol->noblocksnd = 1; -		} else if (strnicmp(data, "noautotune", 10) == 0) { +			break; +		case Opt_noautotune:  			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, "hard", 4) == 0) { +			break; +		case Opt_hard:  			vol->retry = 1; -		} else if (strnicmp(data, "soft", 4) == 0) { +			break; +		case Opt_soft:  			vol->retry = 0; -		} else if (strnicmp(data, "perm", 4) == 0) { +			break; +		case Opt_perm:  			vol->noperm = 0; -		} else if (strnicmp(data, "noperm", 6) == 0) { +			break; +		case Opt_noperm:  			vol->noperm = 1; -		} else if (strnicmp(data, "mapchars", 8) == 0) { +			break; +		case Opt_mapchars:  			vol->remap = 1; -		} else if (strnicmp(data, "nomapchars", 10) == 0) { +			break; +		case Opt_nomapchars:  			vol->remap = 0; -		} else if (strnicmp(data, "sfu", 3) == 0) { +			break; +		case Opt_sfu:  			vol->sfu_emul = 1; -		} else if (strnicmp(data, "nosfu", 5) == 0) { +			break; +		case Opt_nosfu:  			vol->sfu_emul = 0; -		} else if (strnicmp(data, "nodfs", 5) == 0) { +			break; +		case Opt_nodfs:  			vol->nodfs = 1; -		} else if (strnicmp(data, "posixpaths", 10) == 0) { +			break; +		case Opt_posixpaths:  			vol->posix_paths = 1; -		} else if (strnicmp(data, "noposixpaths", 12) == 0) { +			break; +		case Opt_noposixpaths:  			vol->posix_paths = 0; -		} else if (strnicmp(data, "nounix", 6) == 0) { -			vol->no_linux_ext = 1; -		} else if (strnicmp(data, "nolinux", 7) == 0) { +			break; +		case Opt_nounix:  			vol->no_linux_ext = 1; -		} else if ((strnicmp(data, "nocase", 6) == 0) || -			   (strnicmp(data, "ignorecase", 10)  == 0)) { +			break; +		case Opt_nocase:  			vol->nocase = 1; -		} else if (strnicmp(data, "mand", 4) == 0) { -			/* ignore */ -		} else if (strnicmp(data, "nomand", 6) == 0) { -			/* ignore */ -		} else if (strnicmp(data, "_netdev", 7) == 0) { -			/* ignore */ -		} else if (strnicmp(data, "brl", 3) == 0) { +			break; +		case Opt_brl:  			vol->nobrl =  0; -		} else if ((strnicmp(data, "nobrl", 5) == 0) || -			   (strnicmp(data, "nolock", 6) == 0)) { +			break; +		case Opt_nobrl:  			vol->nobrl =  1; -			/* turn off mandatory locking in mode -			if remote locking is turned off since the -			local vfs will do advisory */ +			/* +			 * 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) */ +			break; +		case Opt_forcemandatorylock:  			vol->mand_lock = 1; -		} else if (strnicmp(data, "setuids", 7) == 0) { +			break; +		case Opt_setuids:  			vol->setuids = 1; -		} else if (strnicmp(data, "nosetuids", 9) == 0) { +			break; +		case Opt_nosetuids:  			vol->setuids = 0; -		} else if (strnicmp(data, "dynperm", 7) == 0) { +			break; +		case Opt_dynperm:  			vol->dynperm = true; -		} else if (strnicmp(data, "nodynperm", 9) == 0) { +			break; +		case Opt_nodynperm:  			vol->dynperm = false; -		} else if (strnicmp(data, "nohard", 6) == 0) { +			break; +		case Opt_nohard:  			vol->retry = 0; -		} else if (strnicmp(data, "nosoft", 6) == 0) { +			break; +		case Opt_nosoft:  			vol->retry = 1; -		} else if (strnicmp(data, "nointr", 6) == 0) { +			break; +		case Opt_nointr:  			vol->intr = 0; -		} else if (strnicmp(data, "intr", 4) == 0) { +			break; +		case Opt_intr:  			vol->intr = 1; -		} else if (strnicmp(data, "nostrictsync", 12) == 0) { +			break; +		case Opt_nostrictsync:  			vol->nostrictsync = 1; -		} else if (strnicmp(data, "strictsync", 10) == 0) { +			break; +		case Opt_strictsync:  			vol->nostrictsync = 0; -		} else if (strnicmp(data, "serverino", 7) == 0) { +			break; +		case Opt_serverino:  			vol->server_ino = 1; -		} else if (strnicmp(data, "noserverino", 9) == 0) { +			break; +		case Opt_noserverino:  			vol->server_ino = 0; -		} else if (strnicmp(data, "cifsacl", 7) == 0) { +			break; +		case Opt_rwpidforward: +			vol->rwpidforward = 1; +			break; +		case Opt_cifsacl:  			vol->cifs_acl = 1; -		} else if (strnicmp(data, "nocifsacl", 9) == 0) { +			break; +		case Opt_nocifsacl:  			vol->cifs_acl = 0; -		} else if (strnicmp(data, "acl", 3) == 0) { +			break; +		case Opt_acl:  			vol->no_psx_acl = 0; -		} else if (strnicmp(data, "noacl", 5) == 0) { +			break; +		case Opt_noacl:  			vol->no_psx_acl = 1; -#ifdef CONFIG_CIFS_EXPERIMENTAL -		} else if (strnicmp(data, "locallease", 6) == 0) { +			break; +		case Opt_locallease:  			vol->local_lease = 1; -#endif -		} else if (strnicmp(data, "sign", 4) == 0) { -			vol->secFlg |= CIFSSEC_MUST_SIGN; -		} else if (strnicmp(data, "seal", 4) == 0) { +			break; +		case Opt_sign: +			vol->sign = true; +			break; +		case Opt_seal:  			/* we do not do the following in secFlags because seal -			   is a per tree connection (mount) not a per socket -			   or per-smb connection option in the protocol */ -			/* vol->secFlg |= CIFSSEC_MUST_SEAL; */ +			 * 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, "noac", 4) == 0) { +			break; +		case Opt_noac:  			printk(KERN_WARNING "CIFS: Mount option noac not "  				"supported. Instead set "  				"/proc/fs/cifs/LookupCacheEnabled to 0\n"); -		} else if (strnicmp(data, "fsc", 3) == 0) { +			break; +		case Opt_fsc: +#ifndef CONFIG_CIFS_FSCACHE +			cifs_dbg(VFS, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n"); +			goto cifs_parse_mount_err; +#endif  			vol->fsc = true; -		} else if (strnicmp(data, "mfsymlinks", 10) == 0) { +			break; +		case Opt_mfsymlinks:  			vol->mfsymlinks = true; -		} else if (strnicmp(data, "multiuser", 8) == 0) { +			break; +		case Opt_multiuser:  			vol->multiuser = true; -		} 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; +			break; +		case Opt_sloppy: +			sloppy = true; +			break; +		case Opt_nosharesock: +			vol->nosharesock = true; +			break; + +		/* Numeric Values */ +		case Opt_backupuid: +			if (get_option_uid(args, &vol->backupuid)) { +				cifs_dbg(VFS, "%s: Invalid backupuid value\n", +					 __func__); +				goto cifs_parse_mount_err;  			} -			value = strpbrk(vol->UNC+2, "/\\"); -			if (value) -				*value = '\\'; -		} else { -			printk(KERN_WARNING "CIFS: UNC name too long\n"); -			return 1; +			vol->backupuid_specified = true; +			break; +		case Opt_backupgid: +			if (get_option_gid(args, &vol->backupgid)) { +				cifs_dbg(VFS, "%s: Invalid backupgid value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			vol->backupgid_specified = true; +			break; +		case Opt_uid: +			if (get_option_uid(args, &vol->linux_uid)) { +				cifs_dbg(VFS, "%s: Invalid uid value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			uid_specified = true; +			break; +		case Opt_cruid: +			if (get_option_uid(args, &vol->cred_uid)) { +				cifs_dbg(VFS, "%s: Invalid cruid value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			break; +		case Opt_gid: +			if (get_option_gid(args, &vol->linux_gid)) { +				cifs_dbg(VFS, "%s: Invalid gid value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			gid_specified = true; +			break; +		case Opt_file_mode: +			if (get_option_ul(args, &option)) { +				cifs_dbg(VFS, "%s: Invalid file_mode value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			vol->file_mode = option; +			break; +		case Opt_dirmode: +			if (get_option_ul(args, &option)) { +				cifs_dbg(VFS, "%s: Invalid dir_mode value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			vol->dir_mode = option; +			break; +		case Opt_port: +			if (get_option_ul(args, &option) || +			    option > USHRT_MAX) { +				cifs_dbg(VFS, "%s: Invalid port value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			port = (unsigned short)option; +			break; +		case Opt_rsize: +			if (get_option_ul(args, &option)) { +				cifs_dbg(VFS, "%s: Invalid rsize value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			vol->rsize = option; +			break; +		case Opt_wsize: +			if (get_option_ul(args, &option)) { +				cifs_dbg(VFS, "%s: Invalid wsize value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			vol->wsize = option; +			break; +		case Opt_actimeo: +			if (get_option_ul(args, &option)) { +				cifs_dbg(VFS, "%s: Invalid actimeo value\n", +					 __func__); +				goto cifs_parse_mount_err; +			} +			vol->actimeo = HZ * option; +			if (vol->actimeo > CIFS_MAX_ACTIMEO) { +				cifs_dbg(VFS, "attribute cache timeout too large\n"); +				goto cifs_parse_mount_err; +			} +			break; + +		/* String Arguments */ + +		case Opt_blank_user: +			/* null user, ie. anonymous authentication */ +			vol->nullauth = 1; +			vol->username = NULL; +			break; +		case Opt_user: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (strnlen(string, CIFS_MAX_USERNAME_LEN) > +							CIFS_MAX_USERNAME_LEN) { +				printk(KERN_WARNING "CIFS: username too long\n"); +				goto cifs_parse_mount_err; +			} +			vol->username = kstrdup(string, GFP_KERNEL); +			if (!vol->username) +				goto cifs_parse_mount_err; +			break; +		case Opt_blank_pass: +			/* passwords have to be handled differently +			 * to allow the character used for deliminator +			 * to be passed within them +			 */ + +			/* +			 * Check if this is a case where the  password +			 * starts with a delimiter +			 */ +			tmp_end = strchr(data, '='); +			tmp_end++; +			if (!(tmp_end < end && tmp_end[1] == delim)) { +				/* No it is not. Set the password to NULL */ +				vol->password = NULL; +				break; +			} +			/* Yes it is. Drop down to Opt_pass below.*/ +		case Opt_pass: +			/* Obtain the value string */ +			value = strchr(data, '='); +			value++; + +			/* Set tmp_end to end of the string */ +			tmp_end = (char *) value + strlen(value); + +			/* Check if following character is the deliminator +			 * If yes, we have encountered a double deliminator +			 * reset the NULL character to the deliminator +			 */ +			if (tmp_end < end && tmp_end[1] == delim) { +				tmp_end[0] = delim; + +				/* Keep iterating until we get to a single +				 * deliminator OR the end +				 */ +				while ((tmp_end = strchr(tmp_end, delim)) +					!= NULL && (tmp_end[1] == delim)) { +						tmp_end = (char *) &tmp_end[2]; +				} + +				/* Reset var options to point to next element */ +				if (tmp_end) { +					tmp_end[0] = '\0'; +					options = (char *) &tmp_end[1]; +				} else +					/* Reached the end of the mount option +					 * string */ +					options = end; +			} + +			/* Now build new password string */ +			temp_len = strlen(value); +			vol->password = kzalloc(temp_len+1, GFP_KERNEL); +			if (vol->password == NULL) { +				printk(KERN_WARNING "CIFS: no memory " +						    "for password\n"); +				goto cifs_parse_mount_err; +			} + +			for (i = 0, j = 0; i < temp_len; i++, j++) { +				vol->password[j] = value[i]; +				if ((value[i] == delim) && +				     value[i+1] == delim) +					/* skip the second deliminator */ +					i++; +			} +			vol->password[j] = '\0'; +			break; +		case Opt_blank_ip: +			/* FIXME: should this be an error instead? */ +			got_ip = false; +			break; +		case Opt_ip: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (!cifs_convert_address(dstaddr, string, +					strlen(string))) { +				printk(KERN_ERR "CIFS: bad ip= option (%s).\n", +					string); +				goto cifs_parse_mount_err; +			} +			got_ip = true; +			break; +		case Opt_domain: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (strnlen(string, CIFS_MAX_DOMAINNAME_LEN) +					== CIFS_MAX_DOMAINNAME_LEN) { +				printk(KERN_WARNING "CIFS: domain name too" +						    " long\n"); +				goto cifs_parse_mount_err; +			} + +			vol->domainname = kstrdup(string, GFP_KERNEL); +			if (!vol->domainname) { +				printk(KERN_WARNING "CIFS: no memory " +						    "for domainname\n"); +				goto cifs_parse_mount_err; +			} +			cifs_dbg(FYI, "Domain name set\n"); +			break; +		case Opt_srcaddr: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (!cifs_convert_address( +					(struct sockaddr *)&vol->srcaddr, +					string, strlen(string))) { +				printk(KERN_WARNING "CIFS:  Could not parse" +						    " srcaddr: %s\n", string); +				goto cifs_parse_mount_err; +			} +			break; +		case Opt_iocharset: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (strnlen(string, 1024) >= 65) { +				printk(KERN_WARNING "CIFS: iocharset name " +						    "too long.\n"); +				goto cifs_parse_mount_err; +			} + +			 if (strnicmp(string, "default", 7) != 0) { +				vol->iocharset = kstrdup(string, +							 GFP_KERNEL); +				if (!vol->iocharset) { +					printk(KERN_WARNING "CIFS: no memory" +							    "for charset\n"); +					goto cifs_parse_mount_err; +				} +			} +			/* if iocharset not set then load_nls_default +			 * is used by caller +			 */ +			 cifs_dbg(FYI, "iocharset set to %s\n", string); +			break; +		case Opt_netbiosname: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			memset(vol->source_rfc1001_name, 0x20, +				RFC1001_NAME_LEN); +			/* +			 * FIXME: are there cases in which a comma can +			 * be valid in workstation netbios name (and +			 * need special handling)? +			 */ +			for (i = 0; i < RFC1001_NAME_LEN; i++) { +				/* don't ucase netbiosname for user */ +				if (string[i] == 0) +					break; +				vol->source_rfc1001_name[i] = string[i]; +			} +			/* The string has 16th byte zero still from +			 * set at top of the function +			 */ +			if (i == RFC1001_NAME_LEN && string[i] != 0) +				printk(KERN_WARNING "CIFS: netbiosname" +				       " longer than 15 truncated.\n"); + +			break; +		case Opt_servern: +			/* servernetbiosname specified override *SMBSERVER */ +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			/* last byte, type, is 0x20 for servr type */ +			memset(vol->target_rfc1001_name, 0x20, +				RFC1001_NAME_LEN_WITH_NULL); + +			/* BB are there cases in which a comma can be +			   valid in this workstation netbios name +			   (and need special handling)? */ + +			/* user or mount helper must uppercase the +			   netbios name */ +			for (i = 0; i < 15; i++) { +				if (string[i] == 0) +					break; +				vol->target_rfc1001_name[i] = string[i]; +			} +			/* The string has 16th byte zero still from +			   set at top of the function  */ +			if (i == RFC1001_NAME_LEN && string[i] != 0) +				printk(KERN_WARNING "CIFS: server net" +				       "biosname longer than 15 truncated.\n"); +			break; +		case Opt_ver: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (strnicmp(string, "1", 1) == 0) { +				/* This is the default */ +				break; +			} +			/* For all other value, error */ +			printk(KERN_WARNING "CIFS: Invalid version" +					    " specified\n"); +			goto cifs_parse_mount_err; +		case Opt_vers: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (cifs_parse_smb_version(string, vol) != 0) +				goto cifs_parse_mount_err; +			break; +		case Opt_sec: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (cifs_parse_security_flavors(string, vol) != 0) +				goto cifs_parse_mount_err; +			break; +		case Opt_cache: +			string = match_strdup(args); +			if (string == NULL) +				goto out_nomem; + +			if (cifs_parse_cache_flavor(string, vol) != 0) +				goto cifs_parse_mount_err; +			break; +		default: +			/* +			 * An option we don't recognize. Save it off for later +			 * if we haven't already found one +			 */ +			if (!invalid) +				invalid = data; +			break;  		} +		/* Free up any allocated string */ +		kfree(string); +		string = NULL;  	} -	if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { -		cERROR(1, "Multiuser mounts currently require krb5 " -			  "authentication!"); -		return 1; +	if (!sloppy && invalid) { +		printk(KERN_ERR "CIFS: Unknown mount option \"%s\"\n", invalid); +		goto cifs_parse_mount_err; +	} + +#ifndef CONFIG_KEYS +	/* Muliuser mounts require CONFIG_KEYS support */ +	if (vol->multiuser) { +		cifs_dbg(VFS, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); +		goto cifs_parse_mount_err; +	} +#endif +	if (!vol->UNC) { +		cifs_dbg(VFS, "CIFS mount error: No usable UNC path provided in device string!\n"); +		goto cifs_parse_mount_err; +	} + +	/* make sure UNC has a share name */ +	if (!strchr(vol->UNC + 3, '\\')) { +		cifs_dbg(VFS, "Malformed UNC. Unable to find share name.\n"); +		goto cifs_parse_mount_err; +	} + +	if (!got_ip) { +		/* No ip= option specified? Try to get it from UNC */ +		if (!cifs_convert_address(dstaddr, &vol->UNC[2], +						strlen(&vol->UNC[2]))) { +			printk(KERN_ERR "Unable to determine destination " +					"address.\n"); +			goto cifs_parse_mount_err; +		}  	} -	if (vol->UNCip == NULL) -		vol->UNCip = &vol->UNC[2]; +	/* set the port that we got earlier */ +	cifs_set_port(dstaddr, port);  	if (uid_specified)  		vol->override_uid = override_uid; @@ -1410,7 +1882,15 @@ cifs_parse_mount_options(char *options, const char *devname,  		printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "  				   "specified with no gid= option.\n"); +	kfree(mountdata_copy);  	return 0; + +out_nomem: +	printk(KERN_WARNING "Could not allocate temporary buffer\n"); +cifs_parse_mount_err: +	kfree(string); +	kfree(mountdata_copy); +	return 1;  }  /** Returns true if srcaddr isn't specified and rhs isn't @@ -1430,7 +1910,7 @@ srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)  	}  	case AF_INET6: {  		struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; -		struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs; +		struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;  		return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr);  	}  	default: @@ -1439,35 +1919,71 @@ srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)  	}  } +/* + * If no port is specified in addr structure, we try to match with 445 port + * and if it fails - with 139 ports. It should be called only if address + * families of server and addr are equal. + */ +static bool +match_port(struct TCP_Server_Info *server, struct sockaddr *addr) +{ +	__be16 port, *sport; + +	switch (addr->sa_family) { +	case AF_INET: +		sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port; +		port = ((struct sockaddr_in *) addr)->sin_port; +		break; +	case AF_INET6: +		sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port; +		port = ((struct sockaddr_in6 *) addr)->sin6_port; +		break; +	default: +		WARN_ON(1); +		return false; +	} + +	if (!port) { +		port = htons(CIFS_PORT); +		if (port == *sport) +			return true; + +		port = htons(RFC1001_PORT); +	} + +	return port == *sport; +}  static bool  match_address(struct TCP_Server_Info *server, struct sockaddr *addr,  	      struct sockaddr *srcaddr)  { -	struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; -	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; -  	switch (addr->sa_family) { -	case AF_INET: -		if (addr4->sin_addr.s_addr != -		    server->addr.sockAddr.sin_addr.s_addr) -			return false; -		if (addr4->sin_port && -		    addr4->sin_port != server->addr.sockAddr.sin_port) +	case AF_INET: { +		struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; +		struct sockaddr_in *srv_addr4 = +					(struct sockaddr_in *)&server->dstaddr; + +		if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr)  			return false;  		break; -	case AF_INET6: +	} +	case AF_INET6: { +		struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +		struct sockaddr_in6 *srv_addr6 = +					(struct sockaddr_in6 *)&server->dstaddr; +  		if (!ipv6_addr_equal(&addr6->sin6_addr, -				     &server->addr.sockAddr6.sin6_addr)) -			return false; -		if (addr6->sin6_scope_id != -		    server->addr.sockAddr6.sin6_scope_id) +				     &srv_addr6->sin6_addr))  			return false; -		if (addr6->sin6_port && -		    addr6->sin6_port != server->addr.sockAddr6.sin6_port) +		if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id)  			return false;  		break;  	} +	default: +		WARN_ON(1); +		return false; /* don't expect to be here */ +	}  	if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))  		return false; @@ -1478,68 +1994,64 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr,  static bool  match_security(struct TCP_Server_Info *server, struct smb_vol *vol)  { -	unsigned int secFlags; - -	if (vol->secFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) -		secFlags = vol->secFlg; -	else -		secFlags = global_secflags | vol->secFlg; - -	switch (server->secType) { -	case LANMAN: -		if (!(secFlags & (CIFSSEC_MAY_LANMAN|CIFSSEC_MAY_PLNTXT))) -			return false; -		break; -	case NTLMv2: -		if (!(secFlags & CIFSSEC_MAY_NTLMV2)) -			return false; -		break; -	case NTLM: -		if (!(secFlags & CIFSSEC_MAY_NTLM)) -			return false; -		break; -	case Kerberos: -		if (!(secFlags & CIFSSEC_MAY_KRB5)) -			return false; -		break; -	case RawNTLMSSP: -		if (!(secFlags & CIFSSEC_MAY_NTLMSSP)) -			return false; -		break; -	default: -		/* shouldn't happen */ +	/* +	 * The select_sectype function should either return the vol->sectype +	 * that was specified, or "Unspecified" if that sectype was not +	 * compatible with the given NEGOTIATE request. +	 */ +	if (select_sectype(server, vol->sectype) == Unspecified)  		return false; -	} -	/* now check if signing mode is acceptible */ -	if ((secFlags & CIFSSEC_MAY_SIGN) == 0 && -	    (server->secMode & SECMODE_SIGN_REQUIRED)) -			return false; -	else if (((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) && -		 (server->secMode & -		  (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)) == 0) -			return false; +	/* +	 * Now check if signing mode is acceptable. No need to check +	 * global_secflags at this point since if MUST_SIGN is set then +	 * the server->sign had better be too. +	 */ +	if (vol->sign && !server->sign) +		return false;  	return true;  } +static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) +{ +	struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr; + +	if (vol->nosharesock) +		return 0; + +	if ((server->vals != vol->vals) || (server->ops != vol->ops)) +		return 0; + +	if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) +		return 0; + +	if (!match_address(server, addr, +			   (struct sockaddr *)&vol->srcaddr)) +		return 0; + +	if (!match_port(server, addr)) +		return 0; + +	if (!match_security(server, vol)) +		return 0; + +	return 1; +} +  static struct TCP_Server_Info * -cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) +cifs_find_tcp_session(struct smb_vol *vol)  {  	struct TCP_Server_Info *server;  	spin_lock(&cifs_tcp_ses_lock);  	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { -		if (!match_address(server, addr, -				   (struct sockaddr *)&vol->srcaddr)) -			continue; - -		if (!match_security(server, vol)) +		if (!match_server(server, vol))  			continue;  		++server->srv_count;  		spin_unlock(&cifs_tcp_ses_lock); -		cFYI(1, "Existing tcp session with server found"); +		cifs_dbg(FYI, "Existing tcp session with server found\n");  		return server;  	}  	spin_unlock(&cifs_tcp_ses_lock); @@ -1557,9 +2069,13 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)  		return;  	} +	put_net(cifs_net_ns(server)); +  	list_del_init(&server->tcp_ses_list);  	spin_unlock(&cifs_tcp_ses_lock); +	cancel_delayed_work_sync(&server->echo); +  	spin_lock(&GlobalMid_Lock);  	server->tcpStatus = CifsExiting;  	spin_unlock(&GlobalMid_Lock); @@ -1580,40 +2096,12 @@ static struct TCP_Server_Info *  cifs_get_tcp_session(struct smb_vol *volume_info)  {  	struct TCP_Server_Info *tcp_ses = NULL; -	struct sockaddr_storage 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_storage)); - -	cFYI(1, "UNC: %s ip: %s", volume_info->UNC, volume_info->UNCip); - -	if (volume_info->UNCip && volume_info->UNC) { -		rc = cifs_fill_sockaddr((struct sockaddr *)&addr, -					volume_info->UNCip, -					strlen(volume_info->UNCip), -					volume_info->port); -		if (!rc) { -			/* we failed translating address */ -			rc = -EINVAL; -			goto out_err; -		} -	} 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; -	} +	cifs_dbg(FYI, "UNC: %s\n", volume_info->UNC);  	/* see if we already have a matching tcp_ses */ -	tcp_ses = cifs_find_tcp_session((struct sockaddr *)&addr, volume_info); +	tcp_ses = cifs_find_tcp_session(volume_info);  	if (tcp_ses)  		return tcp_ses; @@ -1623,12 +2111,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  		goto out_err;  	} -	rc = cifs_crypto_shash_allocate(tcp_ses); -	if (rc) { -		cERROR(1, "could not setup hash structures rc %d", rc); -		goto out_err; -	} - +	tcp_ses->ops = volume_info->ops; +	tcp_ses->vals = volume_info->vals; +	cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));  	tcp_ses->hostname = extract_hostname(volume_info->UNC);  	if (IS_ERR(tcp_ses->hostname)) {  		rc = PTR_ERR(tcp_ses->hostname); @@ -1638,7 +2123,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	tcp_ses->noblocksnd = volume_info->noblocksnd;  	tcp_ses->noautotune = volume_info->noautotune;  	tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; -	atomic_set(&tcp_ses->inFlight, 0); +	tcp_ses->in_flight = 0; +	tcp_ses->credits = 1;  	init_waitqueue_head(&tcp_ses->response_q);  	init_waitqueue_head(&tcp_ses->request_q);  	INIT_LIST_HEAD(&tcp_ses->pending_mid_q); @@ -1649,33 +2135,29 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  		volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);  	tcp_ses->session_estab = false;  	tcp_ses->sequence_number = 0; +	tcp_ses->lstrp = jiffies; +	spin_lock_init(&tcp_ses->req_lock);  	INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);  	INIT_LIST_HEAD(&tcp_ses->smb_ses_list); - +	INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); +	memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, +	       sizeof(tcp_ses->srcaddr)); +	memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, +		sizeof(tcp_ses->dstaddr)); +#ifdef CONFIG_CIFS_SMB2 +	get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE); +#endif  	/*  	 * 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; -	memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, -	       sizeof(tcp_ses->srcaddr));  	++tcp_ses->srv_count; -	if (addr.ss_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)); -		rc = ipv6_connect(tcp_ses); -	} else { -		memcpy(&tcp_ses->addr.sockAddr, sin_server, -			sizeof(struct sockaddr_in)); -		rc = ipv4_connect(tcp_ses); -	} +	rc = ip_connect(tcp_ses);  	if (rc < 0) { -		cERROR(1, "Error connecting to socket. Aborting operation"); +		cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n");  		goto out_err_crypto_release;  	} @@ -1684,14 +2166,15 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	 * 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->tsk = kthread_run(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); +		cifs_dbg(VFS, "error %d create cifsd thread\n", rc);  		module_put(THIS_MODULE);  		goto out_err_crypto_release;  	} +	tcp_ses->tcpStatus = CifsNeedNegotiate;  	/* thread spawned, put it on the list */  	spin_lock(&cifs_tcp_ses_lock); @@ -1700,11 +2183,16 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	cifs_fscache_get_client_cookie(tcp_ses); +	/* queue echo request delayed work */ +	queue_delayed_work(cifsiod_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL); +  	return tcp_ses;  out_err_crypto_release:  	cifs_crypto_shash_release(tcp_ses); +	put_net(cifs_net_ns(tcp_ses)); +  out_err:  	if (tcp_ses) {  		if (!IS_ERR(tcp_ses->hostname)) @@ -1716,30 +2204,51 @@ out_err:  	return ERR_PTR(rc);  } -static struct cifsSesInfo * +static int match_session(struct cifs_ses *ses, struct smb_vol *vol) +{ +	if (vol->sectype != Unspecified && +	    vol->sectype != ses->sectype) +		return 0; + +	switch (ses->sectype) { +	case Kerberos: +		if (!uid_eq(vol->cred_uid, ses->cred_uid)) +			return 0; +		break; +	default: +		/* NULL username means anonymous session */ +		if (ses->user_name == NULL) { +			if (!vol->nullauth) +				return 0; +			break; +		} + +		/* anything else takes username/password */ +		if (strncmp(ses->user_name, +			    vol->username ? vol->username : "", +			    CIFS_MAX_USERNAME_LEN)) +			return 0; +		if ((vol->username && strlen(vol->username) != 0) && +		    ses->password != NULL && +		    strncmp(ses->password, +			    vol->password ? vol->password : "", +			    CIFS_MAX_PASSWORD_LEN)) +			return 0; +	} +	return 1; +} + +static struct cifs_ses *  cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)  { -	struct cifsSesInfo *ses; +	struct cifs_ses *ses;  	spin_lock(&cifs_tcp_ses_lock);  	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { -		switch (server->secType) { -		case Kerberos: -			if (vol->cred_uid != ses->cred_uid) -				continue; -			break; -		default: -			/* anything else takes username/password */ -			if (strncmp(ses->userName, vol->username, -				    MAX_USERNAME_SIZE)) -				continue; -			if (strlen(vol->username) != 0 && -			    ses->password != NULL && -			    strncmp(ses->password, -				    vol->password ? vol->password : "", -				    MAX_PASSWORD_SIZE)) -				continue; -		} +		if (ses->status == CifsExiting) +			continue; +		if (!match_session(ses, vol)) +			continue;  		++ses->ses_count;  		spin_unlock(&cifs_tcp_ses_lock);  		return ses; @@ -1749,41 +2258,188 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)  }  static void -cifs_put_smb_ses(struct cifsSesInfo *ses) +cifs_put_smb_ses(struct cifs_ses *ses)  { -	int xid; +	unsigned int rc, xid;  	struct TCP_Server_Info *server = ses->server; -	cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count); +	cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); +  	spin_lock(&cifs_tcp_ses_lock); +	if (ses->status == CifsExiting) { +		spin_unlock(&cifs_tcp_ses_lock); +		return; +	}  	if (--ses->ses_count > 0) {  		spin_unlock(&cifs_tcp_ses_lock);  		return;  	} +	if (ses->status == CifsGood) +		ses->status = CifsExiting; +	spin_unlock(&cifs_tcp_ses_lock); +	if (ses->status == CifsExiting && server->ops->logoff) { +		xid = get_xid(); +		rc = server->ops->logoff(xid, ses); +		if (rc) +			cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n", +				__func__, rc); +		_free_xid(xid); +	} + +	spin_lock(&cifs_tcp_ses_lock);  	list_del_init(&ses->smb_ses_list);  	spin_unlock(&cifs_tcp_ses_lock); -	if (ses->status == CifsGood) { -		xid = GetXid(); -		CIFSSMBLogoff(xid, ses); -		_FreeXid(xid); -	}  	sesInfoFree(ses);  	cifs_put_tcp_session(server);  } -static struct cifsSesInfo * +#ifdef CONFIG_KEYS + +/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */ +#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1) + +/* Populate username and pw fields from keyring if possible */ +static int +cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) +{ +	int rc = 0; +	char *desc, *delim, *payload; +	ssize_t len; +	struct key *key; +	struct TCP_Server_Info *server = ses->server; +	struct sockaddr_in *sa; +	struct sockaddr_in6 *sa6; +	struct user_key_payload *upayload; + +	desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); +	if (!desc) +		return -ENOMEM; + +	/* try to find an address key first */ +	switch (server->dstaddr.ss_family) { +	case AF_INET: +		sa = (struct sockaddr_in *)&server->dstaddr; +		sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); +		break; +	case AF_INET6: +		sa6 = (struct sockaddr_in6 *)&server->dstaddr; +		sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); +		break; +	default: +		cifs_dbg(FYI, "Bad ss_family (%hu)\n", +			 server->dstaddr.ss_family); +		rc = -EINVAL; +		goto out_err; +	} + +	cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); +	key = request_key(&key_type_logon, desc, ""); +	if (IS_ERR(key)) { +		if (!ses->domainName) { +			cifs_dbg(FYI, "domainName is NULL\n"); +			rc = PTR_ERR(key); +			goto out_err; +		} + +		/* didn't work, try to find a domain key */ +		sprintf(desc, "cifs:d:%s", ses->domainName); +		cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); +		key = request_key(&key_type_logon, desc, ""); +		if (IS_ERR(key)) { +			rc = PTR_ERR(key); +			goto out_err; +		} +	} + +	down_read(&key->sem); +	upayload = key->payload.data; +	if (IS_ERR_OR_NULL(upayload)) { +		rc = upayload ? PTR_ERR(upayload) : -EINVAL; +		goto out_key_put; +	} + +	/* find first : in payload */ +	payload = (char *)upayload->data; +	delim = strnchr(payload, upayload->datalen, ':'); +	cifs_dbg(FYI, "payload=%s\n", payload); +	if (!delim) { +		cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n", +			 upayload->datalen); +		rc = -EINVAL; +		goto out_key_put; +	} + +	len = delim - payload; +	if (len > CIFS_MAX_USERNAME_LEN || len <= 0) { +		cifs_dbg(FYI, "Bad value from username search (len=%zd)\n", +			 len); +		rc = -EINVAL; +		goto out_key_put; +	} + +	vol->username = kstrndup(payload, len, GFP_KERNEL); +	if (!vol->username) { +		cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n", +			 len); +		rc = -ENOMEM; +		goto out_key_put; +	} +	cifs_dbg(FYI, "%s: username=%s\n", __func__, vol->username); + +	len = key->datalen - (len + 1); +	if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) { +		cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); +		rc = -EINVAL; +		kfree(vol->username); +		vol->username = NULL; +		goto out_key_put; +	} + +	++delim; +	vol->password = kstrndup(delim, len, GFP_KERNEL); +	if (!vol->password) { +		cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n", +			 len); +		rc = -ENOMEM; +		kfree(vol->username); +		vol->username = NULL; +		goto out_key_put; +	} + +out_key_put: +	up_read(&key->sem); +	key_put(key); +out_err: +	kfree(desc); +	cifs_dbg(FYI, "%s: returning %d\n", __func__, rc); +	return rc; +} +#else /* ! CONFIG_KEYS */ +static inline int +cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)), +		   struct cifs_ses *ses __attribute__((unused))) +{ +	return -ENOSYS; +} +#endif /* CONFIG_KEYS */ + +static struct cifs_ses *  cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  { -	int rc = -ENOMEM, xid; -	struct cifsSesInfo *ses; +	int rc = -ENOMEM; +	unsigned int xid; +	struct cifs_ses *ses; +	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; +	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; -	xid = GetXid(); +	xid = get_xid();  	ses = cifs_find_smb_ses(server, volume_info);  	if (ses) { -		cFYI(1, "Existing smb sess found (status=%d)", ses->status); +		cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", +			 ses->status);  		mutex_lock(&ses->session_mutex);  		rc = cifs_negotiate_protocol(xid, ses); @@ -1791,18 +2447,18 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  			mutex_unlock(&ses->session_mutex);  			/* problem -- put our ses reference */  			cifs_put_smb_ses(ses); -			FreeXid(xid); +			free_xid(xid);  			return ERR_PTR(rc);  		}  		if (ses->need_reconnect) { -			cFYI(1, "Session needs reconnect"); +			cifs_dbg(FYI, "Session needs reconnect\n");  			rc = cifs_setup_session(xid, ses,  						volume_info->local_nls);  			if (rc) {  				mutex_unlock(&ses->session_mutex);  				/* problem -- put our reference */  				cifs_put_smb_ses(ses); -				FreeXid(xid); +				free_xid(xid);  				return ERR_PTR(rc);  			}  		} @@ -1810,27 +2466,27 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  		/* existing SMB ses has a server reference already */  		cifs_put_tcp_session(server); -		FreeXid(xid); +		free_xid(xid);  		return ses;  	} -	cFYI(1, "Existing smb sess not found"); +	cifs_dbg(FYI, "Existing smb sess not found\n");  	ses = sesInfoAlloc();  	if (ses == NULL)  		goto get_ses_fail;  	/* new SMB session uses our server ref */  	ses->server = server; -	if (server->addr.sockAddr6.sin6_family == AF_INET6) -		sprintf(ses->serverName, "%pI6", -			&server->addr.sockAddr6.sin6_addr); +	if (server->dstaddr.ss_family == AF_INET6) +		sprintf(ses->serverName, "%pI6", &addr6->sin6_addr);  	else -		sprintf(ses->serverName, "%pI4", -			&server->addr.sockAddr.sin_addr.s_addr); +		sprintf(ses->serverName, "%pI4", &addr->sin_addr); -	if (volume_info->username) -		strncpy(ses->userName, volume_info->username, -			MAX_USERNAME_SIZE); +	if (volume_info->username) { +		ses->user_name = kstrdup(volume_info->username, GFP_KERNEL); +		if (!ses->user_name) +			goto get_ses_fail; +	}  	/* volume_info->password freed at unmount */  	if (volume_info->password) { @@ -1845,7 +2501,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  	}  	ses->cred_uid = volume_info->cred_uid;  	ses->linux_uid = volume_info->linux_uid; -	ses->overrideSecFlg = volume_info->secFlg; + +	ses->sectype = volume_info->sectype; +	ses->sign = volume_info->sign;  	mutex_lock(&ses->session_mutex);  	rc = cifs_negotiate_protocol(xid, ses); @@ -1860,29 +2518,35 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  	list_add(&ses->smb_ses_list, &server->smb_ses_list);  	spin_unlock(&cifs_tcp_ses_lock); -	FreeXid(xid); +	free_xid(xid);  	return ses;  get_ses_fail:  	sesInfoFree(ses); -	FreeXid(xid); +	free_xid(xid);  	return ERR_PTR(rc);  } -static struct cifsTconInfo * -cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) +static int match_tcon(struct cifs_tcon *tcon, const char *unc) +{ +	if (tcon->tidStatus == CifsExiting) +		return 0; +	if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE)) +		return 0; +	return 1; +} + +static struct cifs_tcon * +cifs_find_tcon(struct cifs_ses *ses, const char *unc)  {  	struct list_head *tmp; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	spin_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)) +		tcon = list_entry(tmp, struct cifs_tcon, tcon_list); +		if (!match_tcon(tcon, unc))  			continue; -  		++tcon->tc_count;  		spin_unlock(&cifs_tcp_ses_lock);  		return tcon; @@ -1892,12 +2556,12 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)  }  static void -cifs_put_tcon(struct cifsTconInfo *tcon) +cifs_put_tcon(struct cifs_tcon *tcon)  { -	int xid; -	struct cifsSesInfo *ses = tcon->ses; +	unsigned int xid; +	struct cifs_ses *ses = tcon->ses; -	cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count); +	cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);  	spin_lock(&cifs_tcp_ses_lock);  	if (--tcon->tc_count > 0) {  		spin_unlock(&cifs_tcp_ses_lock); @@ -1907,32 +2571,37 @@ cifs_put_tcon(struct cifsTconInfo *tcon)  	list_del_init(&tcon->tcon_list);  	spin_unlock(&cifs_tcp_ses_lock); -	xid = GetXid(); -	CIFSSMBTDis(xid, tcon); -	_FreeXid(xid); +	xid = get_xid(); +	if (ses->server->ops->tree_disconnect) +		ses->server->ops->tree_disconnect(xid, tcon); +	_free_xid(xid);  	cifs_fscache_release_super_cookie(tcon);  	tconInfoFree(tcon);  	cifs_put_smb_ses(ses);  } -static struct cifsTconInfo * -cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) +static struct cifs_tcon * +cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)  {  	int rc, xid; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	tcon = cifs_find_tcon(ses, volume_info->UNC);  	if (tcon) { -		cFYI(1, "Found match on UNC path"); +		cifs_dbg(FYI, "Found match on UNC path\n");  		/* existing tcon already has a reference */  		cifs_put_smb_ses(ses);  		if (tcon->seal != volume_info->seal) -			cERROR(1, "transport encryption setting " -				   "conflicts with existing tid"); +			cifs_dbg(VFS, "transport encryption setting conflicts with existing tid\n");  		return tcon;  	} +	if (!ses->server->ops->tree_connect) { +		rc = -ENOSYS; +		goto out_fail; +	} +  	tcon = tconInfoAlloc();  	if (tcon == NULL) {  		rc = -ENOMEM; @@ -1948,35 +2617,32 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info)  		}  	} -	if (strchr(volume_info->UNC + 3, '\\') == NULL -	    && strchr(volume_info->UNC + 3, '/') == NULL) { -		cERROR(1, "Missing share name"); -		rc = -ENODEV; -		goto out_fail; -	} - -	/* BB Do we need to wrap session_mutex around -	 * this TCon call and Unix SetFS as -	 * we do on SessSetup and reconnect? */ -	xid = GetXid(); -	rc = CIFSTCon(xid, ses, volume_info->UNC, tcon, volume_info->local_nls); -	FreeXid(xid); -	cFYI(1, "CIFS Tcon rc = %d", rc); +	/* +	 * BB Do we need to wrap session_mutex around this TCon call and Unix +	 * SetFS as we do on SessSetup and reconnect? +	 */ +	xid = get_xid(); +	rc = ses->server->ops->tree_connect(xid, ses, volume_info->UNC, tcon, +					    volume_info->local_nls); +	free_xid(xid); +	cifs_dbg(FYI, "Tcon rc = %d\n", rc);  	if (rc)  		goto out_fail;  	if (volume_info->nodfs) {  		tcon->Flags &= ~SMB_SHARE_IS_IN_DFS; -		cFYI(1, "DFS disabled (%d)", tcon->Flags); +		cifs_dbg(FYI, "DFS disabled (%d)\n", tcon->Flags);  	}  	tcon->seal = volume_info->seal; -	/* 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 */ +	/* +	 * 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; +	INIT_LIST_HEAD(&tcon->pending_opens);  	spin_lock(&cifs_tcp_ses_lock);  	list_add(&tcon->tcon_list, &ses->tcon_list); @@ -2009,38 +2675,127 @@ cifs_put_tlink(struct tcon_link *tlink)  	return;  } +static inline struct tcon_link * +cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) +{ +	return cifs_sb->master_tlink; +} + +static int +compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) +{ +	struct cifs_sb_info *old = CIFS_SB(sb); +	struct cifs_sb_info *new = mnt_data->cifs_sb; + +	if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK)) +		return 0; + +	if ((old->mnt_cifs_flags & CIFS_MOUNT_MASK) != +	    (new->mnt_cifs_flags & CIFS_MOUNT_MASK)) +		return 0; + +	/* +	 * We want to share sb only if we don't specify an r/wsize or +	 * specified r/wsize is greater than or equal to existing one. +	 */ +	if (new->wsize && new->wsize < old->wsize) +		return 0; + +	if (new->rsize && new->rsize < old->rsize) +		return 0; + +	if (!uid_eq(old->mnt_uid, new->mnt_uid) || !gid_eq(old->mnt_gid, new->mnt_gid)) +		return 0; + +	if (old->mnt_file_mode != new->mnt_file_mode || +	    old->mnt_dir_mode != new->mnt_dir_mode) +		return 0; + +	if (strcmp(old->local_nls->charset, new->local_nls->charset)) +		return 0; + +	if (old->actimeo != new->actimeo) +		return 0; + +	return 1; +} + +int +cifs_match_super(struct super_block *sb, void *data) +{ +	struct cifs_mnt_data *mnt_data = (struct cifs_mnt_data *)data; +	struct smb_vol *volume_info; +	struct cifs_sb_info *cifs_sb; +	struct TCP_Server_Info *tcp_srv; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon; +	struct tcon_link *tlink; +	int rc = 0; + +	spin_lock(&cifs_tcp_ses_lock); +	cifs_sb = CIFS_SB(sb); +	tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); +	if (IS_ERR(tlink)) { +		spin_unlock(&cifs_tcp_ses_lock); +		return rc; +	} +	tcon = tlink_tcon(tlink); +	ses = tcon->ses; +	tcp_srv = ses->server; + +	volume_info = mnt_data->vol; + +	if (!match_server(tcp_srv, volume_info) || +	    !match_session(ses, volume_info) || +	    !match_tcon(tcon, volume_info->UNC)) { +		rc = 0; +		goto out; +	} + +	rc = compare_mount_options(sb, mnt_data); +out: +	spin_unlock(&cifs_tcp_ses_lock); +	cifs_put_tlink(tlink); +	return rc; +} +  int -get_dfs_path(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) +get_dfs_path(const unsigned int xid, struct cifs_ses *ses, const char *old_path, +	     const struct nls_table *nls_codepage, unsigned int *num_referrals, +	     struct dfs_info3_param **referrals, int remap)  {  	char *temp_unc;  	int rc = 0; -	*pnum_referrals = 0; -	*preferrals = NULL; +	if (!ses->server->ops->tree_connect || !ses->server->ops->get_dfs_refer) +		return -ENOSYS; + +	*num_referrals = 0; +	*referrals = NULL; -	if (pSesInfo->ipc_tid == 0) { +	if (ses->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); +			strnlen(ses->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); +		strcpy(temp_unc + 2, ses->serverName); +		strcpy(temp_unc + 2 + strlen(ses->serverName), "\\IPC$"); +		rc = ses->server->ops->tree_connect(xid, ses, temp_unc, NULL, +						    nls_codepage); +		cifs_dbg(FYI, "Tcon rc = %d ipc_tid = %d\n", rc, ses->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 */ +		rc = ses->server->ops->get_dfs_refer(xid, ses, old_path, +						     referrals, num_referrals, +						     nls_codepage, remap); +	/* +	 * BB - map targetUNCs to dfs_info3 structures, here or in +	 * ses->server->ops->get_dfs_refer. +	 */  	return rc;  } @@ -2108,96 +2863,138 @@ bind_socket(struct TCP_Server_Info *server)  			saddr4 = (struct sockaddr_in *)&server->srcaddr;  			saddr6 = (struct sockaddr_in6 *)&server->srcaddr;  			if (saddr6->sin6_family == AF_INET6) -				cERROR(1, "cifs: " -				       "Failed to bind to: %pI6c, error: %d\n", -				       &saddr6->sin6_addr, rc); +				cifs_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", +					 &saddr6->sin6_addr, rc);  			else -				cERROR(1, "cifs: " -				       "Failed to bind to: %pI4, error: %d\n", -				       &saddr4->sin_addr.s_addr, rc); +				cifs_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", +					 &saddr4->sin_addr.s_addr, rc);  		}  	}  	return rc;  }  static int -ipv4_connect(struct TCP_Server_Info *server) +ip_rfc1001_connect(struct TCP_Server_Info *server)  {  	int rc = 0; -	int val; -	bool connected = false; -	__be16 orig_port = 0; +	/* +	 * some servers require RFC1001 sessinit before sending +	 * negprot - BB check reconnection in case where second +	 * sessinit is sent but no second negprot +	 */ +	struct rfc1002_session_packet *ses_init_buf; +	struct smb_hdr *smb_buf; +	ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), +			       GFP_KERNEL); +	if (ses_init_buf) { +		ses_init_buf->trailer.session_req.called_len = 32; + +		if (server->server_RFC1001_name && +		    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 = cpu_to_be32(0x81000044); +		rc = smb_send(server, smb_buf, 0x44); +		kfree(ses_init_buf); +		/* +		 * RFC1001 layer in at least one server +		 * requires very short break before negprot +		 * presumably because not expecting negprot +		 * to follow so fast.  This is a simple +		 * solution that works without +		 * complicating the code and causes no +		 * significant slowing down on mount +		 * for everyone else +		 */ +		usleep_range(1000, 2000); +	} +	/* +	 * else the negprot may still work without this +	 * even though malloc failed +	 */ + +	return rc; +} + +static int +generic_ip_connect(struct TCP_Server_Info *server) +{ +	int rc = 0; +	__be16 sport; +	int slen, sfamily;  	struct socket *socket = server->ssocket; +	struct sockaddr *saddr; + +	saddr = (struct sockaddr *) &server->dstaddr; + +	if (server->dstaddr.ss_family == AF_INET6) { +		sport = ((struct sockaddr_in6 *) saddr)->sin6_port; +		slen = sizeof(struct sockaddr_in6); +		sfamily = AF_INET6; +	} else { +		sport = ((struct sockaddr_in *) saddr)->sin_port; +		slen = sizeof(struct sockaddr_in); +		sfamily = AF_INET; +	}  	if (socket == NULL) { -		rc = sock_create_kern(PF_INET, SOCK_STREAM, -				      IPPROTO_TCP, &socket); +		rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, +				   IPPROTO_TCP, &socket, 1);  		if (rc < 0) { -			cERROR(1, "Error %d creating socket", rc); +			cifs_dbg(VFS, "Error %d creating socket\n", rc); +			server->ssocket = NULL;  			return rc;  		}  		/* BB other socket options to set KEEPALIVE, NODELAY? */ -		cFYI(1, "Socket created"); +		cifs_dbg(FYI, "Socket created\n");  		server->ssocket = socket;  		socket->sk->sk_allocation = GFP_NOFS; -		cifs_reclassify_socket4(socket); +		if (sfamily == AF_INET6) +			cifs_reclassify_socket6(socket); +		else +			cifs_reclassify_socket4(socket);  	}  	rc = bind_socket(server);  	if (rc < 0)  		return rc; -	/* 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 +	 * the default. sock_setsockopt not used because it expects +	 * user space buffer  	 */  	socket->sk->sk_rcvtimeo = 7 * HZ;  	socket->sk->sk_sndtimeo = 5 * HZ; @@ -2211,176 +3008,63 @@ ipv4_connect(struct TCP_Server_Info *server)  	}  	if (server->tcp_nodelay) { -		val = 1; +		int val = 1;  		rc = kernel_setsockopt(socket, SOL_TCP, TCP_NODELAY,  				(char *)&val, sizeof(val));  		if (rc) -			cFYI(1, "set TCP_NODELAY socket option error %d", rc); +			cifs_dbg(FYI, "set TCP_NODELAY socket option error %d\n", +				 rc);  	} -	 cFYI(1, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx", +	cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n",  		 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(server, smb_buf, 0x44); -			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 */ - +	rc = socket->ops->connect(socket, saddr, slen, 0); +	if (rc < 0) { +		cifs_dbg(FYI, "Error %d connecting to server\n", rc); +		sock_release(socket); +		server->ssocket = NULL; +		return rc;  	} +	if (sport == htons(RFC1001_PORT)) +		rc = ip_rfc1001_connect(server); +  	return rc;  }  static int -ipv6_connect(struct TCP_Server_Info *server) +ip_connect(struct TCP_Server_Info *server)  { -	int rc = 0; -	int val; -	bool connected = false; -	__be16 orig_port = 0; -	struct socket *socket = server->ssocket; +	__be16 *sport; +	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; +	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; -	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; -		} +	if (server->dstaddr.ss_family == AF_INET6) +		sport = &addr6->sin6_port; +	else +		sport = &addr->sin_port; -		/* 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); -	} +	if (*sport == 0) { +		int rc; -	rc = bind_socket(server); -	if (rc < 0) -		return rc; +		/* try with 445 port at first */ +		*sport = htons(CIFS_PORT); -	/* 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); +		rc = generic_ip_connect(server);  		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 = 5 * HZ; +			return rc; -	if (server->tcp_nodelay) { -		val = 1; -		rc = kernel_setsockopt(socket, SOL_TCP, TCP_NODELAY, -				(char *)&val, sizeof(val)); -		if (rc) -			cFYI(1, "set TCP_NODELAY socket option error %d", rc); +		/* if it failed, try with 139 port */ +		*sport = htons(RFC1001_PORT);  	} -	server->ssocket = socket; - -	return rc; +	return generic_ip_connect(server);  } -void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, -			  struct super_block *sb, struct smb_vol *vol_info) +void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, +			  struct cifs_sb_info *cifs_sb, struct smb_vol *vol_info)  {  	/* if we are reconnecting then should we check to see if  	 * any requested capabilities changed locally e.g. via @@ -2396,19 +3080,19 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,  	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"); +		cifs_dbg(FYI, "Linux protocol extensions disabled\n");  		return;  	} else if (vol_info)  		tcon->unix_ext = 1; /* Unix Extensions supported */  	if (tcon->unix_ext == 0) { -		cFYI(1, "Unix extensions disabled so not set on reconnect"); +		cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n");  		return;  	}  	if (!CIFSSMBQFSUnixInfo(xid, tcon)) {  		__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - +		cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);  		/* check for reconnect case in which we do not  		   want to change the mount behavior if we can avoid it */  		if (vol_info == NULL) { @@ -2418,153 +3102,91 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,  				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"); +					cifs_dbg(VFS, "POSIXPATH support change\n");  				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"); +				cifs_dbg(VFS, "possible reconnect error\n"); +				cifs_dbg(VFS, "server disabled POSIX path support\n");  			}  		} +		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) +			cifs_dbg(VFS, "per-share encryption not supported yet\n"); +  		cap &= CIFS_UNIX_CAP_MASK;  		if (vol_info && vol_info->no_psx_acl)  			cap &= ~CIFS_UNIX_POSIX_ACL_CAP;  		else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { -			cFYI(1, "negotiated posix acl support"); -			if (sb) -				sb->s_flags |= MS_POSIXACL; +			cifs_dbg(FYI, "negotiated posix acl support\n"); +			if (cifs_sb) +				cifs_sb->mnt_cifs_flags |= +					CIFS_MOUNT_POSIXACL;  		}  		if (vol_info && vol_info->posix_paths == 0)  			cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;  		else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { -			cFYI(1, "negotiate posix pathnames"); -			if (sb) -				CIFS_SB(sb)->mnt_cifs_flags |= +			cifs_dbg(FYI, "negotiate posix pathnames\n"); +			if (cifs_sb) +				cifs_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); +		cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap);  #ifdef CONFIG_CIFS_DEBUG2  		if (cap & CIFS_UNIX_FCNTL_CAP) -			cFYI(1, "FCNTL cap"); +			cifs_dbg(FYI, "FCNTL cap\n");  		if (cap & CIFS_UNIX_EXTATTR_CAP) -			cFYI(1, "EXTATTR cap"); +			cifs_dbg(FYI, "EXTATTR cap\n");  		if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) -			cFYI(1, "POSIX path cap"); +			cifs_dbg(FYI, "POSIX path cap\n");  		if (cap & CIFS_UNIX_XATTR_CAP) -			cFYI(1, "XATTR cap"); +			cifs_dbg(FYI, "XATTR cap\n");  		if (cap & CIFS_UNIX_POSIX_ACL_CAP) -			cFYI(1, "POSIX ACL cap"); +			cifs_dbg(FYI, "POSIX ACL cap\n");  		if (cap & CIFS_UNIX_LARGE_READ_CAP) -			cFYI(1, "very large read cap"); +			cifs_dbg(FYI, "very large read cap\n");  		if (cap & CIFS_UNIX_LARGE_WRITE_CAP) -			cFYI(1, "very large write cap"); +			cifs_dbg(FYI, "very large write cap\n"); +		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) +			cifs_dbg(FYI, "transport encryption cap\n"); +		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) +			cifs_dbg(FYI, "mandatory transport encryption cap\n");  #endif /* CIFS_DEBUG2 */  		if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {  			if (vol_info == NULL) { -				cFYI(1, "resetting capabilities failed"); +				cifs_dbg(FYI, "resetting capabilities failed\n");  			} 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."); +				cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n");  		}  	}  } -static void -convert_delimiter(char *path, char delim) +void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, +			struct cifs_sb_info *cifs_sb)  { -	int i; -	char old_delim; - -	if (path == NULL) -		return; +	INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); -	if (delim == '/') -		old_delim = '\\'; -	else -		old_delim = '/'; +	spin_lock_init(&cifs_sb->tlink_tree_lock); +	cifs_sb->tlink_tree = RB_ROOT; -	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) -{ -	INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); +	/* +	 * Temporarily set r/wsize for matching superblock. If we end up using +	 * new sb then client will later negotiate it downward if needed. +	 */ +	cifs_sb->rsize = pvolume_info->rsize; +	cifs_sb->wsize = pvolume_info->wsize; -	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); +	cifs_dbg(FYI, "file mode: 0x%hx  dir mode: 0x%hx\n", +		 cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode); + +	cifs_sb->actimeo = pvolume_info->actimeo; +	cifs_sb->local_nls = pvolume_info->local_nls;  	if (pvolume_info->noperm)  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; @@ -2584,8 +3206,18 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC;  	if (pvolume_info->mand_lock)  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; +	if (pvolume_info->rwpidforward) +		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;  	if (pvolume_info->cifs_acl)  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; +	if (pvolume_info->backupuid_specified) { +		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; +		cifs_sb->mnt_backupuid = pvolume_info->backupuid; +	} +	if (pvolume_info->backupgid_specified) { +		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; +		cifs_sb->mnt_backupgid = pvolume_info->backupgid; +	}  	if (pvolume_info->override_uid)  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;  	if (pvolume_info->override_gid) @@ -2597,143 +3229,155 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,  	if (pvolume_info->multiuser)  		cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER |  					    CIFS_MOUNT_NO_PERM); +	if (pvolume_info->strict_io) +		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO;  	if (pvolume_info->direct_io) { -		cFYI(1, "mounting share using direct i/o"); +		cifs_dbg(FYI, "mounting share using direct i/o\n");  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;  	}  	if (pvolume_info->mfsymlinks) {  		if (pvolume_info->sfu_emul) { -			cERROR(1,  "mount option mfsymlinks ignored if sfu " -				   "mount option is used"); +			cifs_dbg(VFS, "mount option mfsymlinks ignored if sfu mount option is used\n");  		} else {  			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;  		}  	}  	if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) -		cERROR(1, "mount option dynperm ignored if cifsacl " -			   "mount option supported"); -} - -static int -is_path_accessible(int xid, struct cifsTconInfo *tcon, -		   struct cifs_sb_info *cifs_sb, const char *full_path) -{ -	int rc; -	FILE_ALL_INFO *pfile_info; - -	pfile_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); -	if (pfile_info == NULL) -		return -ENOMEM; - -	rc = CIFSSMBQPathInfo(xid, tcon, full_path, pfile_info, -			      0 /* not legacy */, cifs_sb->local_nls, -			      cifs_sb->mnt_cifs_flags & -				CIFS_MOUNT_MAP_SPECIAL_CHR); -	kfree(pfile_info); -	return rc; +		cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n");  }  static void -cleanup_volume_info(struct smb_vol **pvolume_info) +cleanup_volume_info_contents(struct smb_vol *volume_info)  { -	struct smb_vol *volume_info; - -	if (!pvolume_info || !*pvolume_info) -		return; - -	volume_info = *pvolume_info; +	kfree(volume_info->username);  	kzfree(volume_info->password);  	kfree(volume_info->UNC); +	kfree(volume_info->domainname); +	kfree(volume_info->iocharset);  	kfree(volume_info->prepath); +} + +void +cifs_cleanup_volume_info(struct smb_vol *volume_info) +{ +	if (!volume_info) +		return; +	cleanup_volume_info_contents(volume_info);  	kfree(volume_info); -	*pvolume_info = NULL; -	return;  } +  #ifdef CONFIG_CIFS_DFS_UPCALL -/* build_path_to_root returns full path to root when - * we do not have an exiting connection (tcon) */ +/* + * cifs_build_path_to_root returns full path to root when we do not have an + * exiting connection (tcon) + */  static char * -build_unc_path_to_root(const struct smb_vol *volume_info, +build_unc_path_to_root(const struct smb_vol *vol,  		const struct cifs_sb_info *cifs_sb)  { -	char *full_path; +	char *full_path, *pos; +	unsigned int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0; +	unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1); -	int unc_len = strnlen(volume_info->UNC, MAX_TREE_SIZE + 1); -	full_path = kmalloc(unc_len + cifs_sb->prepathlen + 1, GFP_KERNEL); +	full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);  	if (full_path == NULL)  		return ERR_PTR(-ENOMEM); -	strncpy(full_path, volume_info->UNC, unc_len); -	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { -		int i; -		for (i = 0; i < unc_len; i++) { -			if (full_path[i] == '\\') -				full_path[i] = '/'; -		} -	} +	strncpy(full_path, vol->UNC, unc_len); +	pos = full_path + unc_len; -	if (cifs_sb->prepathlen) -		strncpy(full_path + unc_len, cifs_sb->prepath, -				cifs_sb->prepathlen); +	if (pplen) { +		*pos = CIFS_DIR_SEP(cifs_sb); +		strncpy(pos + 1, vol->prepath, pplen); +		pos += pplen; +	} -	full_path[unc_len + cifs_sb->prepathlen] = 0; /* add trailing null */ +	*pos = '\0'; /* add trailing null */ +	convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); +	cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);  	return full_path;  } -#endif -int -cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, -		char *mount_data_global, const char *devname) +/* + * Perform a dfs referral query for a share and (optionally) prefix + * + * If a referral is found, cifs_sb->mountdata will be (re-)allocated + * to a string containing updated options for the submount.  Otherwise it + * will be left untouched. + * + * Returns the rc from get_dfs_path to the caller, which can be used to + * determine whether there were referrals. + */ +static int +expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, +		    struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb, +		    int check_prefix)  {  	int rc; -	int xid; -	struct smb_vol *volume_info; -	struct cifsSesInfo *pSesInfo; -	struct cifsTconInfo *tcon; -	struct TCP_Server_Info *srvTcp; -	char   *full_path; -	char *mount_data = mount_data_global; -	struct tcon_link *tlink; -#ifdef CONFIG_CIFS_DFS_UPCALL -	struct dfs_info3_param *referrals = NULL;  	unsigned int num_referrals = 0; -	int referral_walks_count = 0; -try_mount_again: -#endif -	rc = 0; -	tcon = NULL; -	pSesInfo = NULL; -	srvTcp = NULL; -	full_path = NULL; -	tlink = NULL; +	struct dfs_info3_param *referrals = NULL; +	char *full_path = NULL, *ref_path = NULL, *mdata = NULL; -	xid = GetXid(); +	full_path = build_unc_path_to_root(volume_info, cifs_sb); +	if (IS_ERR(full_path)) +		return PTR_ERR(full_path); -	volume_info = kzalloc(sizeof(struct smb_vol), GFP_KERNEL); -	if (!volume_info) { -		rc = -ENOMEM; -		goto out; -	} +	/* For DFS paths, skip the first '\' of the UNC */ +	ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1; -	if (cifs_parse_mount_options(mount_data, devname, volume_info)) { -		rc = -EINVAL; -		goto out; +	rc = get_dfs_path(xid, ses, ref_path, cifs_sb->local_nls, +			  &num_referrals, &referrals, +			  cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + +	if (!rc && num_referrals > 0) { +		char *fake_devname = NULL; + +		mdata = cifs_compose_mount_options(cifs_sb->mountdata, +						   full_path + 1, referrals, +						   &fake_devname); + +		free_dfs_info_array(referrals, num_referrals); + +		if (IS_ERR(mdata)) { +			rc = PTR_ERR(mdata); +			mdata = NULL; +		} else { +			cleanup_volume_info_contents(volume_info); +			rc = cifs_setup_volume_info(volume_info, mdata, +							fake_devname); +		} +		kfree(fake_devname); +		kfree(cifs_sb->mountdata); +		cifs_sb->mountdata = mdata;  	} +	kfree(full_path); +	return rc; +} +#endif + +static int +cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, +			const char *devname) +{ +	int rc = 0; + +	if (cifs_parse_mount_options(mount_data, devname, volume_info)) +		return -EINVAL;  	if (volume_info->nullauth) { -		cFYI(1, "null user"); -		volume_info->username = ""; +		cifs_dbg(FYI, "Anonymous login\n"); +		kfree(volume_info->username); +		volume_info->username = NULL;  	} else if (volume_info->username) {  		/* BB fixme parse for domain name here */ -		cFYI(1, "Username: %s", volume_info->username); +		cifs_dbg(FYI, "Username: %s\n", volume_info->username);  	} else { -		cifserror("No username specified"); +		cifs_dbg(VFS, "No username specified\n");  	/* In userspace mount helper we can get user name from alternate  	   locations such as env variables and files on disk */ -		rc = -EINVAL; -		goto out; +		return -EINVAL;  	}  	/* this is needed for ASCII cp to Unicode converts */ @@ -2743,86 +3387,157 @@ try_mount_again:  	} else {  		volume_info->local_nls = load_nls(volume_info->iocharset);  		if (volume_info->local_nls == NULL) { -			cERROR(1, "CIFS mount error: iocharset %s not found", +			cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n",  				 volume_info->iocharset); -			rc = -ELIBACC; -			goto out; +			return -ELIBACC;  		}  	} -	cifs_sb->local_nls = volume_info->local_nls; + +	return rc; +} + +struct smb_vol * +cifs_get_volume_info(char *mount_data, const char *devname) +{ +	int rc; +	struct smb_vol *volume_info; + +	volume_info = kmalloc(sizeof(struct smb_vol), GFP_KERNEL); +	if (!volume_info) +		return ERR_PTR(-ENOMEM); + +	rc = cifs_setup_volume_info(volume_info, mount_data, devname); +	if (rc) { +		cifs_cleanup_volume_info(volume_info); +		volume_info = ERR_PTR(rc); +	} + +	return volume_info; +} + +int +cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) +{ +	int rc; +	unsigned int xid; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	char   *full_path; +	struct tcon_link *tlink; +#ifdef CONFIG_CIFS_DFS_UPCALL +	int referral_walks_count = 0; +#endif + +	rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY); +	if (rc) +		return rc; + +#ifdef CONFIG_CIFS_DFS_UPCALL +try_mount_again: +	/* cleanup activities if we're chasing a referral */ +	if (referral_walks_count) { +		if (tcon) +			cifs_put_tcon(tcon); +		else if (ses) +			cifs_put_smb_ses(ses); + +		free_xid(xid); +	} +#endif +	rc = 0; +	tcon = NULL; +	ses = NULL; +	server = NULL; +	full_path = NULL; +	tlink = NULL; + +	xid = get_xid();  	/* get a reference to a tcp session */ -	srvTcp = cifs_get_tcp_session(volume_info); -	if (IS_ERR(srvTcp)) { -		rc = PTR_ERR(srvTcp); +	server = cifs_get_tcp_session(volume_info); +	if (IS_ERR(server)) { +		rc = PTR_ERR(server); +		bdi_destroy(&cifs_sb->bdi);  		goto out;  	}  	/* get a reference to a SMB session */ -	pSesInfo = cifs_get_smb_ses(srvTcp, volume_info); -	if (IS_ERR(pSesInfo)) { -		rc = PTR_ERR(pSesInfo); -		pSesInfo = NULL; +	ses = cifs_get_smb_ses(server, volume_info); +	if (IS_ERR(ses)) { +		rc = PTR_ERR(ses); +		ses = NULL;  		goto mount_fail_check;  	} -	setup_cifs_sb(volume_info, cifs_sb); -	if (pSesInfo->capabilities & CAP_LARGE_FILES) -		sb->s_maxbytes = MAX_LFS_FILESIZE; -	else -		sb->s_maxbytes = MAX_NON_LFS; - -	/* BB FIXME fix time_gran to be larger for LANMAN sessions */ -	sb->s_time_gran = 100; -  	/* search for existing tcon to this server share */ -	tcon = cifs_get_tcon(pSesInfo, volume_info); +	tcon = cifs_get_tcon(ses, volume_info);  	if (IS_ERR(tcon)) {  		rc = PTR_ERR(tcon);  		tcon = NULL;  		goto remote_path_check;  	} -	/* 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) +	if (cap_unix(tcon->ses)) {  		/* reset of caps checks mount to see if unix extensions  		   disabled for just this mount */ -		reset_cifs_unix_caps(xid, tcon, sb, volume_info); -	else +		reset_cifs_unix_caps(xid, tcon, cifs_sb, volume_info); +		if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && +		    (le64_to_cpu(tcon->fsUnixInfo.Capability) & +		     CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { +			rc = -EACCES; +			goto mount_fail_check; +		} +	} else  		tcon->unix_ext = 0; /* server does not support them */ -	/* 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)); +	/* do not care if a following call succeed - informational */ +	if (!tcon->ipc && server->ops->qfs_tcon) +		server->ops->qfs_tcon(xid, tcon); -	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)); +	cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info); +	cifs_sb->rsize = server->ops->negotiate_rsize(tcon, volume_info); + +	/* tune readahead according to rsize */ +	cifs_sb->bdi.ra_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;  remote_path_check: -	/* check if a whole path (including prepath) is not remote */ -	if (!rc && cifs_sb->prepathlen && tcon) { -		/* build_path_to_root works only when we have a valid tcon */ -		full_path = cifs_build_path_to_root(cifs_sb); +#ifdef CONFIG_CIFS_DFS_UPCALL +	/* +	 * Perform an unconditional check for whether there are DFS +	 * referrals for this path without prefix, to provide support +	 * for DFS referrals from w2k8 servers which don't seem to respond +	 * with PATH_NOT_COVERED to requests that include the prefix. +	 * Chase the referral if found, otherwise continue normally. +	 */ +	if (referral_walks_count == 0) { +		int refrc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, +						false); +		if (!refrc) { +			referral_walks_count++; +			goto try_mount_again; +		} +	} +#endif + +	/* check if a whole path is not remote */ +	if (!rc && tcon) { +		if (!server->ops->is_path_accessible) { +			rc = -ENOSYS; +			goto mount_fail_check; +		} +		/* +		 * cifs_build_path_to_root works only when we have a valid tcon +		 */ +		full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon);  		if (full_path == NULL) {  			rc = -ENOMEM;  			goto mount_fail_check;  		} -		rc = is_path_accessible(xid, tcon, cifs_sb, full_path); -		if (rc != -EREMOTE) { +		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, +						     full_path); +		if (rc != 0 && rc != -EREMOTE) {  			kfree(full_path);  			goto mount_fail_check;  		} @@ -2842,50 +3557,14 @@ remote_path_check:  			rc = -ELOOP;  			goto mount_fail_check;  		} -		/* 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)); -		full_path = build_unc_path_to_root(volume_info, cifs_sb); -		if (IS_ERR(full_path)) { -			rc = PTR_ERR(full_path); -			goto mount_fail_check; -		} - -		cFYI(1, "Getting referral for: %s", full_path); -		rc = get_dfs_path(xid, pSesInfo , full_path + 1, -			cifs_sb->local_nls, &num_referrals, &referrals, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); -		if (!rc && num_referrals > 0) { -			char *fake_devname = NULL; - -			if (mount_data != mount_data_global) -				kfree(mount_data); - -			mount_data = cifs_compose_mount_options( -					cifs_sb->mountdata, full_path + 1, -					referrals, &fake_devname); - -			free_dfs_info_array(referrals, num_referrals); -			kfree(fake_devname); -			kfree(full_path); -			if (IS_ERR(mount_data)) { -				rc = PTR_ERR(mount_data); -				mount_data = NULL; -				goto mount_fail_check; -			} - -			if (tcon) -				cifs_put_tcon(tcon); -			else if (pSesInfo) -				cifs_put_smb_ses(pSesInfo); +		rc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, true); -			cleanup_volume_info(&volume_info); +		if (!rc) {  			referral_walks_count++; -			FreeXid(xid);  			goto try_mount_again;  		} +		goto mount_fail_check;  #else /* No DFS support, return error on mount */  		rc = -EOPNOTSUPP;  #endif @@ -2901,7 +3580,7 @@ remote_path_check:  		goto mount_fail_check;  	} -	tlink->tl_uid = pSesInfo->linux_uid; +	tlink->tl_uid = ses->linux_uid;  	tlink->tl_tcon = tcon;  	tlink->tl_time = jiffies;  	set_bit(TCON_LINK_MASTER, &tlink->tl_flags); @@ -2912,39 +3591,35 @@ remote_path_check:  	tlink_rb_insert(&cifs_sb->tlink_tree, tlink);  	spin_unlock(&cifs_sb->tlink_tree_lock); -	queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, +	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,  				TLINK_IDLE_EXPIRE);  mount_fail_check:  	/* on error free sesinfo and tcon struct if needed */  	if (rc) { -		if (mount_data != mount_data_global) -			kfree(mount_data);  		/* If find_unc succeeded then rc == 0 so we can not end */ -		/* up accidently freeing someone elses tcon struct */ +		/* up accidentally freeing someone elses tcon struct */  		if (tcon)  			cifs_put_tcon(tcon); -		else if (pSesInfo) -			cifs_put_smb_ses(pSesInfo); +		else if (ses) +			cifs_put_smb_ses(ses);  		else -			cifs_put_tcp_session(srvTcp); -		goto out; +			cifs_put_tcp_session(server); +		bdi_destroy(&cifs_sb->bdi);  	} -	/* 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 */ -	cleanup_volume_info(&volume_info); -	FreeXid(xid); +	free_xid(xid);  	return rc;  } +/* + * Issue a TREE_CONNECT request. Note that for IPC$ shares, that the tcon + * pointer may be NULL. + */  int -CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, -	 const char *tree, struct cifsTconInfo *tcon, +CIFSTCon(const unsigned int xid, struct cifs_ses *ses, +	 const char *tree, struct cifs_tcon *tcon,  	 const struct nls_table *nls_codepage)  {  	struct smb_hdr *smb_buffer; @@ -2953,8 +3628,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  	TCONX_RSP *pSMBr;  	unsigned char *bcc_ptr;  	int rc = 0; -	int length, bytes_left; -	__u16 count; +	int length; +	__u16 bytes_left, count;  	if (ses == NULL)  		return -EIO; @@ -2968,7 +3643,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  	header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,  			NULL /*no tid */ , 4 /*wct */ ); -	smb_buffer->Mid = GetNextMid(ses->server); +	smb_buffer->Mid = get_next_mid(ses->server);  	smb_buffer->Uid = ses->Suid;  	pSMB = (TCONX_REQ *) smb_buffer;  	pSMBr = (TCONX_RSP *) smb_buffer_response; @@ -2976,13 +3651,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  	pSMB->AndXCommand = 0xFF;  	pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);  	bcc_ptr = &pSMB->Password[0]; -	if ((ses->server->secMode) & SECMODE_USER) { +	if (!tcon || (ses->server->sec_mode & SECMODE_USER)) {  		pSMB->PasswordLength = cpu_to_le16(1);	/* minimum */  		*bcc_ptr = 0; /* password is null byte */  		bcc_ptr++;              /* skip password */  		/* already aligned so no need to do it below */  	} else { -		pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); +		pSMB->PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);  		/* BB FIXME add code to fail this if NTLMv2 or Kerberos  		   specified as required (when that support is added to  		   the vfs in the future) as only NTLM or the much @@ -2991,16 +3666,17 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  		   NTLMv2 password here) */  #ifdef CONFIG_CIFS_WEAK_PW_HASH  		if ((global_secflags & CIFSSEC_MAY_LANMAN) && -		    (ses->server->secType == LANMAN)) +		    (ses->sectype == LANMAN))  			calc_lanman_hash(tcon->password, ses->server->cryptkey, -					 ses->server->secMode & +					 ses->server->sec_mode &  					    SECMODE_PW_ENCRYPT ? true : false,  					 bcc_ptr);  		else  #endif /* CIFS_WEAK_PW_HASH */ -		SMBNTencrypt(tcon->password, ses->server->cryptkey, bcc_ptr); +		rc = SMBNTencrypt(tcon->password, ses->server->cryptkey, +					bcc_ptr, nls_codepage); -		bcc_ptr += CIFS_SESS_KEY_SIZE; +		bcc_ptr += CIFS_AUTH_RESP_SIZE;  		if (ses->capabilities & CAP_UNICODE) {  			/* must align unicode strings */  			*bcc_ptr = 0; /* null byte password */ @@ -3008,8 +3684,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  		}  	} -	if (ses->server->secMode & -			(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) +	if (ses->server->sign)  		smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;  	if (ses->capabilities & CAP_STATUS32) { @@ -3021,7 +3696,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  	if (ses->capabilities & CAP_UNICODE) {  		smb_buffer->Flags2 |= SMBFLG2_UNICODE;  		length = -		    cifs_strtoUCS((__le16 *) bcc_ptr, tree, +		    cifs_strtoUTF16((__le16 *) bcc_ptr, tree,  			6 /* max utf8 char length in bytes */ *  			(/* server len*/ + 256 /* share len */), nls_codepage);  		bcc_ptr += 2 * length;	/* convert num 16 bit words to bytes */ @@ -3034,11 +3709,12 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  	bcc_ptr += strlen("?????");  	bcc_ptr += 1;  	count = bcc_ptr - &pSMB->Password[0]; -	pSMB->hdr.smb_buf_length += count; +	pSMB->hdr.smb_buf_length = cpu_to_be32(be32_to_cpu( +					pSMB->hdr.smb_buf_length) + count);  	pSMB->ByteCount = cpu_to_le16(count);  	rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, -			 CIFS_STD_OP); +			 0);  	/* above now done in SendReceive */  	if ((rc == 0) && (tcon != NULL)) { @@ -3048,7 +3724,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  		tcon->need_reconnect = false;  		tcon->tid = smb_buffer_response->Tid;  		bcc_ptr = pByteArea(smb_buffer_response); -		bytes_left = BCC(smb_buffer_response); +		bytes_left = get_bcc(smb_buffer_response);  		length = strnlen(bcc_ptr, bytes_left - 2);  		if (smb_buffer->Flags2 & SMBFLG2_UNICODE)  			is_unicode = true; @@ -3060,26 +3736,26 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  		if (length == 3) {  			if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&  			    (bcc_ptr[2] == 'C')) { -				cFYI(1, "IPC connection"); +				cifs_dbg(FYI, "IPC connection\n");  				tcon->ipc = 1;  			}  		} else if (length == 2) {  			if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {  				/* the most common case */ -				cFYI(1, "disk share connection"); +				cifs_dbg(FYI, "disk share connection\n");  			}  		}  		bcc_ptr += length + 1;  		bytes_left -= (length + 1); -		strncpy(tcon->treeName, tree, MAX_TREE_SIZE); +		strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));  		/* mostly informational -- no need to fail on error here */  		kfree(tcon->nativeFileSystem); -		tcon->nativeFileSystem = cifs_strndup_from_ucs(bcc_ptr, +		tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr,  						      bytes_left, is_unicode,  						      nls_codepage); -		cFYI(1, "nativeFileSystem=%s", tcon->nativeFileSystem); +		cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem);  		if ((smb_buffer_response->WordCount == 3) ||  			 (smb_buffer_response->WordCount == 7)) @@ -3087,7 +3763,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  			tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);  		else  			tcon->Flags = 0; -		cFYI(1, "Tcon flags: 0x%x ", tcon->Flags); +		cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags);  	} else if ((rc == 0) && tcon == NULL) {  		/* all we need to save for IPC$ connection */  		ses->ipc_tid = smb_buffer_response->Tid; @@ -3097,13 +3773,19 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,  	return rc;  } -int -cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) +static void delayed_free(struct rcu_head *p) +{ +	struct cifs_sb_info *sbi = container_of(p, struct cifs_sb_info, rcu); +	unload_nls(sbi->local_nls); +	kfree(sbi); +} + +void +cifs_umount(struct cifs_sb_info *cifs_sb)  {  	struct rb_root *root = &cifs_sb->tlink_tree;  	struct rb_node *node;  	struct tcon_link *tlink; -	char *tmp;  	cancel_delayed_work_sync(&cifs_sb->prune_tlinks); @@ -3120,105 +3802,87 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)  	}  	spin_unlock(&cifs_sb->tlink_tree_lock); -	tmp = cifs_sb->prepath; -	cifs_sb->prepathlen = 0; -	cifs_sb->prepath = NULL; -	kfree(tmp); - -	return 0; +	bdi_destroy(&cifs_sb->bdi); +	kfree(cifs_sb->mountdata); +	call_rcu(&cifs_sb->rcu, delayed_free);  } -int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) +int +cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)  {  	int rc = 0;  	struct TCP_Server_Info *server = ses->server; +	if (!server->ops->need_neg || !server->ops->negotiate) +		return -ENOSYS; +  	/* only send once per connect */ -	if (server->maxBuf != 0) +	if (!server->ops->need_neg(server))  		return 0; -	rc = CIFSSMBNegotiate(xid, ses); -	if (rc == -EAGAIN) { -		/* retry only once on 1st time connection */ -		rc = CIFSSMBNegotiate(xid, ses); -		if (rc == -EAGAIN) -			rc = -EHOSTDOWN; -	} +	set_credits(server, 1); + +	rc = server->ops->negotiate(xid, ses);  	if (rc == 0) {  		spin_lock(&GlobalMid_Lock); -		if (server->tcpStatus != CifsExiting) +		if (server->tcpStatus == CifsNeedNegotiate)  			server->tcpStatus = CifsGood;  		else  			rc = -EHOSTDOWN;  		spin_unlock(&GlobalMid_Lock); -  	}  	return rc;  } - -int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, -			struct nls_table *nls_info) +int +cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, +		   struct nls_table *nls_info)  { -	int rc = 0; +	int rc = -ENOSYS;  	struct TCP_Server_Info *server = ses->server; -	ses->flags = 0;  	ses->capabilities = server->capabilities;  	if (linuxExtEnabled == 0) -		ses->capabilities &= (~CAP_UNIX); +		ses->capabilities &= (~server->vals->cap_unix); -	cFYI(1, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", -		 server->secMode, server->capabilities, server->timeAdj); - -	rc = CIFS_SessSetup(xid, ses, nls_info); -	if (rc) { -		cERROR(1, "Send error in SessSetup = %d", rc); -	} else { -		mutex_lock(&ses->server->srv_mutex); -		if (!server->session_estab) { -			server->session_key.response = ses->auth_key.response; -			server->session_key.len = ses->auth_key.len; -			server->sequence_number = 0x2; -			server->session_estab = true; -			ses->auth_key.response = NULL; -		} -		mutex_unlock(&server->srv_mutex); +	cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", +		 server->sec_mode, server->capabilities, server->timeAdj); -		cFYI(1, "CIFS Session Established successfully"); -		spin_lock(&GlobalMid_Lock); -		ses->status = CifsGood; -		ses->need_reconnect = false; -		spin_unlock(&GlobalMid_Lock); -	} +	if (server->ops->sess_setup) +		rc = server->ops->sess_setup(xid, ses, nls_info); -	kfree(ses->auth_key.response); -	ses->auth_key.response = NULL; -	ses->auth_key.len = 0; -	kfree(ses->ntlmssp); -	ses->ntlmssp = NULL; +	if (rc) +		cifs_dbg(VFS, "Send error in SessSetup = %d\n", rc);  	return rc;  } -static struct cifsTconInfo * -cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) +static int +cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) +{ +	vol->sectype = ses->sectype; + +	/* krb5 is special, since we don't need username or pw */ +	if (vol->sectype == Kerberos) +		return 0; + +	return cifs_set_cifscreds(vol, ses); +} + +static struct cifs_tcon * +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)  { -	struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); -	struct cifsSesInfo *ses; -	struct cifsTconInfo *tcon = NULL; +	int rc; +	struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); +	struct cifs_ses *ses; +	struct cifs_tcon *tcon = NULL;  	struct smb_vol *vol_info; -	char username[MAX_USERNAME_SIZE + 1];  	vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); -	if (vol_info == NULL) { -		tcon = ERR_PTR(-ENOMEM); -		goto out; -	} +	if (vol_info == NULL) +		return ERR_PTR(-ENOMEM); -	snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); -	vol_info->username = username;  	vol_info->local_nls = cifs_sb->local_nls;  	vol_info->linux_uid = fsuid;  	vol_info->cred_uid = fsuid; @@ -3227,9 +3891,14 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)  	vol_info->nocase = master_tcon->nocase;  	vol_info->local_lease = master_tcon->local_lease;  	vol_info->no_linux_ext = !master_tcon->unix_ext; +	vol_info->sectype = master_tcon->ses->sectype; +	vol_info->sign = master_tcon->ses->sign; -	/* FIXME: allow for other secFlg settings */ -	vol_info->secFlg = CIFSSEC_MUST_KRB5; +	rc = cifs_set_vol_auth(vol_info, master_tcon->ses); +	if (rc) { +		tcon = ERR_PTR(rc); +		goto out; +	}  	/* get a reference for the same TCP session */  	spin_lock(&cifs_tcp_ses_lock); @@ -3238,7 +3907,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)  	ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);  	if (IS_ERR(ses)) { -		tcon = (struct cifsTconInfo *)ses; +		tcon = (struct cifs_tcon *)ses;  		cifs_put_tcp_session(master_tcon->ses->server);  		goto out;  	} @@ -3249,21 +3918,17 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)  		goto out;  	} -	if (ses->capabilities & CAP_UNIX) +	if (cap_unix(ses))  		reset_cifs_unix_caps(0, tcon, NULL, vol_info);  out: +	kfree(vol_info->username); +	kfree(vol_info->password);  	kfree(vol_info);  	return tcon;  } -static inline struct tcon_link * -cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) -{ -	return cifs_sb->master_tlink; -} - -struct cifsTconInfo * +struct cifs_tcon *  cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)  {  	return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); @@ -3278,7 +3943,7 @@ cifs_sb_tcon_pending_wait(void *unused)  /* find and return a tlink with given uid */  static struct tcon_link * -tlink_rb_search(struct rb_root *root, uid_t uid) +tlink_rb_search(struct rb_root *root, kuid_t uid)  {  	struct rb_node *node = root->rb_node;  	struct tcon_link *tlink; @@ -3286,9 +3951,9 @@ tlink_rb_search(struct rb_root *root, uid_t uid)  	while (node) {  		tlink = rb_entry(node, struct tcon_link, tl_rbnode); -		if (tlink->tl_uid > uid) +		if (uid_gt(tlink->tl_uid, uid))  			node = node->rb_left; -		else if (tlink->tl_uid < uid) +		else if (uid_lt(tlink->tl_uid, uid))  			node = node->rb_right;  		else  			return tlink; @@ -3307,7 +3972,7 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)  		tlink = rb_entry(*new, struct tcon_link, tl_rbnode);  		parent = *new; -		if (tlink->tl_uid > new_tlink->tl_uid) +		if (uid_gt(tlink->tl_uid, new_tlink->tl_uid))  			new = &((*new)->rb_left);  		else  			new = &((*new)->rb_right); @@ -3337,7 +4002,7 @@ struct tcon_link *  cifs_sb_tlink(struct cifs_sb_info *cifs_sb)  {  	int ret; -	uid_t fsuid = current_fsuid(); +	kuid_t fsuid = current_fsuid();  	struct tcon_link *tlink, *newtlink;  	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) @@ -3450,6 +4115,6 @@ cifs_prune_tlinks(struct work_struct *work)  	}  	spin_unlock(&cifs_sb->tlink_tree_lock); -	queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, +	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,  				TLINK_IDLE_EXPIRE);  } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 3840eddbfb7..3db0c5fd9a1 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -32,6 +32,7 @@  #include "cifsproto.h"  #include "cifs_debug.h"  #include "cifs_fs_sb.h" +#include "cifs_unicode.h"  static void  renew_parental_timestamps(struct dentry *direntry) @@ -44,65 +45,101 @@ renew_parental_timestamps(struct dentry *direntry)  	} while (!IS_ROOT(direntry));  } +char * +cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, +			struct cifs_tcon *tcon) +{ +	int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0; +	int dfsplen; +	char *full_path = NULL; + +	/* if no prefix path, simply set path to the root of share to "" */ +	if (pplen == 0) { +		full_path = kzalloc(1, GFP_KERNEL); +		return full_path; +	} + +	if (tcon->Flags & SMB_SHARE_IS_IN_DFS) +		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); +	else +		dfsplen = 0; + +	full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); +	if (full_path == NULL) +		return full_path; + +	if (dfsplen) +		strncpy(full_path, tcon->treeName, dfsplen); +	full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb); +	strncpy(full_path + dfsplen + 1, vol->prepath, pplen); +	convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); +	full_path[dfsplen + pplen] = 0; /* add trailing null */ +	return full_path; +} +  /* Note: caller must free return buffer */  char *  build_path_from_dentry(struct dentry *direntry)  {  	struct dentry *temp;  	int namelen; -	int pplen;  	int dfsplen;  	char *full_path;  	char dirsep;  	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(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 */ +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); +	unsigned seq;  	dirsep = CIFS_DIR_SEP(cifs_sb); -	pplen = cifs_sb->prepathlen;  	if (tcon->Flags & SMB_SHARE_IS_IN_DFS)  		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);  	else  		dfsplen = 0;  cifs_bp_rename_retry: -	namelen = pplen + dfsplen; +	namelen = dfsplen; +	seq = read_seqbegin(&rename_lock); +	rcu_read_lock();  	for (temp = direntry; !IS_ROOT(temp);) {  		namelen += (1 + temp->d_name.len);  		temp = temp->d_parent;  		if (temp == NULL) { -			cERROR(1, "corrupt dentry"); +			cifs_dbg(VFS, "corrupt dentry\n"); +			rcu_read_unlock();  			return NULL;  		}  	} +	rcu_read_unlock();  	full_path = kmalloc(namelen+1, GFP_KERNEL);  	if (full_path == NULL)  		return full_path;  	full_path[namelen] = 0;	/* trailing null */ +	rcu_read_lock();  	for (temp = direntry; !IS_ROOT(temp);) { +		spin_lock(&temp->d_lock);  		namelen -= 1 + temp->d_name.len;  		if (namelen < 0) { +			spin_unlock(&temp->d_lock);  			break;  		} else {  			full_path[namelen] = dirsep;  			strncpy(full_path + namelen + 1, temp->d_name.name,  				temp->d_name.len); -			cFYI(0, "name: %s", full_path + namelen); +			cifs_dbg(FYI, "name: %s\n", full_path + namelen);  		} +		spin_unlock(&temp->d_lock);  		temp = temp->d_parent;  		if (temp == NULL) { -			cERROR(1, "corrupt dentry"); +			cifs_dbg(VFS, "corrupt dentry\n"); +			rcu_read_unlock();  			kfree(full_path);  			return NULL;  		}  	} -	if (namelen != pplen + dfsplen) { -		cERROR(1, "did not end path lookup where expected namelen is %d", -			namelen); +	rcu_read_unlock(); +	if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) { +		cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n", +			 namelen, dfsplen);  		/* presumably this is only possible if racing with a rename  		of one of the parent directories  (we can not lock the dentries  		above us to prevent this, but retrying should be harmless) */ @@ -126,123 +163,152 @@ cifs_bp_rename_retry:  			}  		}  	} -	strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen);  	return full_path;  } -static void setup_cifs_dentry(struct cifsTconInfo *tcon, -			      struct dentry *direntry, -			      struct inode *newinode) +/* + * Don't allow the separator character in a path component. + * The VFS will not allow "/", but "\" is allowed by posix. + */ +static int +check_name(struct dentry *direntry)  { -	if (tcon->nocase) -		direntry->d_op = &cifs_ci_dentry_ops; -	else -		direntry->d_op = &cifs_dentry_ops; -	d_instantiate(direntry, newinode); +	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); +	int i; + +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { +		for (i = 0; i < direntry->d_name.len; i++) { +			if (direntry->d_name.name[i] == '\\') { +				cifs_dbg(FYI, "Invalid file name\n"); +				return -EINVAL; +			} +		} +	} +	return 0;  } +  /* Inode operations in similar order to how they appear in Linux file fs.h */ -int -cifs_create(struct inode *inode, struct dentry *direntry, int mode, -		struct nameidata *nd) +static int +cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, +	       struct tcon_link *tlink, unsigned oflags, umode_t mode, +	       __u32 *oplock, struct cifs_fid *fid)  {  	int rc = -ENOENT; -	int xid;  	int create_options = CREATE_NOT_DIR; -	__u32 oplock = 0; -	int oflags; -	/* -	 * BB below access is probably too much for mknod to request -	 *    but we have to do query and setpathinfo so requesting -	 *    less could fail (unless we want to request getatr and setatr -	 *    permissions (only).  At least for POSIX we do not have to -	 *    request so much. -	 */ -	int desiredAccess = GENERIC_READ | GENERIC_WRITE; -	__u16 fileHandle; -	struct cifs_sb_info *cifs_sb; -	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	int desired_access; +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct cifs_tcon *tcon = tlink_tcon(tlink);  	char *full_path = NULL;  	FILE_ALL_INFO *buf = NULL;  	struct inode *newinode = NULL; -	int disposition = FILE_OVERWRITE_IF; - -	xid = GetXid(); - -	cifs_sb = CIFS_SB(inode->i_sb); -	tlink = cifs_sb_tlink(cifs_sb); -	if (IS_ERR(tlink)) { -		FreeXid(xid); -		return PTR_ERR(tlink); -	} -	tcon = tlink_tcon(tlink); +	int disposition; +	struct TCP_Server_Info *server = tcon->ses->server; +	struct cifs_open_parms oparms; -	if (oplockEnabled) -		oplock = REQ_OPLOCK; - -	if (nd && (nd->flags & LOOKUP_OPEN)) -		oflags = nd->intent.open.file->f_flags; -	else -		oflags = O_RDONLY | O_CREAT; +	*oplock = 0; +	if (tcon->ses->server->oplocks) +		*oplock = REQ_OPLOCK;  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) {  		rc = -ENOMEM; -		goto cifs_create_out; +		goto out;  	} -	if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && +	if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&  	    (CIFS_UNIX_POSIX_PATH_OPS_CAP &  			le64_to_cpu(tcon->fsUnixInfo.Capability))) { -		rc = cifs_posix_open(full_path, &newinode, -			inode->i_sb, mode, oflags, &oplock, &fileHandle, xid); -		/* EIO could indicate that (posix open) operation is not -		   supported, despite what server claimed in capability -		   negotation.  EREMOTE indicates DFS junction, which is not -		   handled in posix open */ - -		if (rc == 0) { -			if (newinode == NULL) /* query inode info */ +		rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, +				     oflags, oplock, &fid->netfid, xid); +		switch (rc) { +		case 0: +			if (newinode == NULL) { +				/* query inode info */  				goto cifs_create_get_file_info; -			else /* success, no need to query */ -				goto cifs_create_set_dentry; -		} else if ((rc != -EIO) && (rc != -EREMOTE) && -			 (rc != -EOPNOTSUPP) && (rc != -EINVAL)) -			goto cifs_create_out; -		/* else fallthrough to retry, using older open call, this is -		   case where server does not support this SMB level, and -		   falsely claims capability (also get here for DFS case -		   which should be rare for path not covered on files) */ -	} +			} + +			if (!S_ISREG(newinode->i_mode)) { +				/* +				 * The server may allow us to open things like +				 * FIFOs, but the client isn't set up to deal +				 * with that. If it's not a regular file, just +				 * close it and proceed as if it were a normal +				 * lookup. +				 */ +				CIFSSMBClose(xid, tcon, fid->netfid); +				goto cifs_create_get_file_info; +			} +			/* success, no need to query */ +			goto cifs_create_set_dentry; -	if (nd && (nd->flags & LOOKUP_OPEN)) { -		/* if the file is going to stay open, then we -		   need to set the desired access properly */ -		desiredAccess = 0; -		if (OPEN_FMODE(oflags) & FMODE_READ) -			desiredAccess |= GENERIC_READ; /* is this too little? */ -		if (OPEN_FMODE(oflags) & FMODE_WRITE) -			desiredAccess |= GENERIC_WRITE; - -		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"); +		case -ENOENT: +			goto cifs_create_get_file_info; + +		case -EIO: +		case -EINVAL: +			/* +			 * EIO could indicate that (posix open) operation is not +			 * supported, despite what server claimed in capability +			 * negotiation. +			 * +			 * POSIX open in samba versions 3.3.1 and earlier could +			 * incorrectly fail with invalid parameter. +			 */ +			tcon->broken_posix_open = true; +			break; + +		case -EREMOTE: +		case -EOPNOTSUPP: +			/* +			 * EREMOTE indicates DFS junction, which is not handled +			 * in posix open.  If either that or op not supported +			 * returned, follow the normal lookup. +			 */ +			break; + +		default: +			goto out; +		} +		/* +		 * fallthrough to retry, using older open call, this is case +		 * where server does not support this SMB level, and falsely +		 * claims capability (also get here for DFS case which should be +		 * rare for path not covered on files) +		 */  	} -	/* BB add processing to set equivalent of mode - e.g. via CreateX with -	   ACLs */ +	desired_access = 0; +	if (OPEN_FMODE(oflags) & FMODE_READ) +		desired_access |= GENERIC_READ; /* is this too little? */ +	if (OPEN_FMODE(oflags) & FMODE_WRITE) +		desired_access |= GENERIC_WRITE; + +	disposition = FILE_OVERWRITE_IF; +	if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) +		disposition = FILE_CREATE; +	else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) +		disposition = FILE_OVERWRITE_IF; +	else if ((oflags & O_CREAT) == O_CREAT) +		disposition = FILE_OPEN_IF; +	else +		cifs_dbg(FYI, "Create flag not set in create function\n"); + +	/* +	 * BB add processing to set equivalent of mode - e.g. via CreateX with +	 * ACLs +	 */ + +	if (!server->ops->open) { +		rc = -ENOSYS; +		goto out; +	}  	buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);  	if (buf == NULL) {  		rc = -ENOMEM; -		goto cifs_create_out; +		goto out;  	}  	/* @@ -252,29 +318,29 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,  	if (!tcon->unix_ext && (mode & S_IWUGO) == 0)  		create_options |= CREATE_OPTION_READONLY; -	if (tcon->ses->capabilities & CAP_NT_SMBS) -		rc = CIFSSMBOpen(xid, tcon, 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, tcon, full_path, disposition, -			desiredAccess, create_options, -			&fileHandle, &oplock, buf, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); -	} +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = desired_access; +	oparms.create_options = create_options; +	oparms.disposition = disposition; +	oparms.path = full_path; +	oparms.fid = fid; +	oparms.reconnect = false; + +	rc = server->ops->open(xid, &oparms, oplock, buf);  	if (rc) { -		cFYI(1, "cifs_create returned 0x%x", rc); -		goto cifs_create_out; +		cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); +		goto out;  	} -	/* If Open reported that we actually created a file -	   then we now have to set the mode if possible */ -	if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) { +	/* +	 * If Open reported that we actually created a file then we now have to +	 * set the mode if possible. +	 */ +	if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {  		struct cifs_unix_set_info_args args = {  				.mode	= mode,  				.ctime	= NO_CHANGE_64, @@ -284,22 +350,22 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,  		};  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { -			args.uid = (__u64) current_fsuid(); +			args.uid = current_fsuid();  			if (inode->i_mode & S_ISGID) -				args.gid = (__u64) inode->i_gid; +				args.gid = inode->i_gid;  			else -				args.gid = (__u64) current_fsgid(); +				args.gid = current_fsgid();  		} else { -			args.uid = NO_CHANGE_64; -			args.gid = NO_CHANGE_64; +			args.uid = INVALID_UID; /* no change */ +			args.gid = INVALID_GID; /* no change */  		} -		CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, -					cifs_sb->local_nls, -					cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); +		CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, +				       current->tgid);  	} else { -		/* BB implement mode setting via Windows security -		   descriptors e.g. */ +		/* +		 * BB implement mode setting via Windows security +		 * descriptors e.g. +		 */  		/* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/  		/* Could set r/o dos attribute if mode & 0222 == 0 */ @@ -308,15 +374,17 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,  cifs_create_get_file_info:  	/* server might mask mode so we have to query for it */  	if (tcon->unix_ext) -		rc = cifs_get_inode_info_unix(&newinode, full_path, -					      inode->i_sb, xid); +		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); +		rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, +					 xid, fid);  		if (newinode) { +			if (server->ops->set_lease_key) +				server->ops->set_lease_key(newinode, fid);  			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)  				newinode->i_mode = mode; -			if ((oplock & CIFS_CREATE_ACTION) && +			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) @@ -328,52 +396,182 @@ cifs_create_get_file_info:  	}  cifs_create_set_dentry: -	if (rc == 0) -		setup_cifs_dentry(tcon, direntry, newinode); -	else -		cFYI(1, "Create worked, get_inode_info failed rc = %d", rc); +	if (rc != 0) { +		cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", +			 rc); +		if (server->ops->close) +			server->ops->close(xid, tcon, fid); +		goto out; +	} +	d_drop(direntry); +	d_add(direntry, newinode); -	if (newinode && nd && (nd->flags & LOOKUP_OPEN)) { -		struct cifsFileInfo *pfile_info; -		struct file *filp; +out: +	kfree(buf); +	kfree(full_path); +	return rc; +} -		filp = lookup_instantiate_filp(nd, direntry, generic_file_open); -		if (IS_ERR(filp)) { -			rc = PTR_ERR(filp); -			CIFSSMBClose(xid, tcon, fileHandle); -			goto cifs_create_out; -		} +int +cifs_atomic_open(struct inode *inode, struct dentry *direntry, +		 struct file *file, unsigned oflags, umode_t mode, +		 int *opened) +{ +	int rc; +	unsigned int xid; +	struct tcon_link *tlink; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	struct cifs_fid fid; +	struct cifs_pending_open open; +	__u32 oplock; +	struct cifsFileInfo *file_info; -		pfile_info = cifs_new_fileinfo(fileHandle, filp, tlink, oplock); -		if (pfile_info == NULL) { -			fput(filp); -			CIFSSMBClose(xid, tcon, fileHandle); -			rc = -ENOMEM; -		} -	} else { -		CIFSSMBClose(xid, tcon, fileHandle); +	/* +	 * Posix open is only called (at lookup time) for file create now. For +	 * opens (rather than creates), because we do not know if it is a file +	 * or directory yet, and current Samba no longer allows us to do posix +	 * open on dirs, we could end up wasting an open call on what turns out +	 * to be a dir. For file opens, we wait to call posix open till +	 * cifs_open.  It could be added to atomic_open in the future but the +	 * performance tradeoff of the extra network request when EISDIR or +	 * EACCES is returned would have to be weighed against the 50% reduction +	 * in network traffic in the other paths. +	 */ +	if (!(oflags & O_CREAT)) { +		struct dentry *res; + +		/* +		 * Check for hashed negative dentry. We have already revalidated +		 * the dentry and it is fine. No need to perform another lookup. +		 */ +		if (!d_unhashed(direntry)) +			return -ENOENT; + +		res = cifs_lookup(inode, direntry, 0); +		if (IS_ERR(res)) +			return PTR_ERR(res); + +		return finish_no_open(file, res);  	} -cifs_create_out: -	kfree(buf); -	kfree(full_path); +	rc = check_name(direntry); +	if (rc) +		return rc; + +	xid = get_xid(); + +	cifs_dbg(FYI, "parent inode = 0x%p name is: %s and dentry = 0x%p\n", +		 inode, direntry->d_name.name, direntry); + +	tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); +	if (IS_ERR(tlink)) { +		rc = PTR_ERR(tlink); +		goto out_free_xid; +	} + +	tcon = tlink_tcon(tlink); +	server = tcon->ses->server; + +	if (server->ops->new_lease_key) +		server->ops->new_lease_key(&fid); + +	cifs_add_pending_open(&fid, tlink, &open); + +	rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, +			    &oplock, &fid); + +	if (rc) { +		cifs_del_pending_open(&open); +		goto out; +	} + +	if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) +		*opened |= FILE_CREATED; + +	rc = finish_open(file, direntry, generic_file_open, opened); +	if (rc) { +		if (server->ops->close) +			server->ops->close(xid, tcon, &fid); +		cifs_del_pending_open(&open); +		goto out; +	} + +	file_info = cifs_new_fileinfo(&fid, file, tlink, oplock); +	if (file_info == NULL) { +		if (server->ops->close) +			server->ops->close(xid, tcon, &fid); +		cifs_del_pending_open(&open); +		fput(file); +		rc = -ENOMEM; +	} + +out: +	cifs_put_tlink(tlink); +out_free_xid: +	free_xid(xid); +	return rc; +} + +int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode, +		bool excl) +{ +	int rc; +	unsigned int xid = get_xid(); +	/* +	 * BB below access is probably too much for mknod to request +	 *    but we have to do query and setpathinfo so requesting +	 *    less could fail (unless we want to request getatr and setatr +	 *    permissions (only).  At least for POSIX we do not have to +	 *    request so much. +	 */ +	unsigned oflags = O_EXCL | O_CREAT | O_RDWR; +	struct tcon_link *tlink; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	struct cifs_fid fid; +	__u32 oplock; + +	cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %s and dentry = 0x%p\n", +		 inode, direntry->d_name.name, direntry); + +	tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); +	rc = PTR_ERR(tlink); +	if (IS_ERR(tlink)) +		goto out_free_xid; + +	tcon = tlink_tcon(tlink); +	server = tcon->ses->server; + +	if (server->ops->new_lease_key) +		server->ops->new_lease_key(&fid); + +	rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, +			    &oplock, &fid); +	if (!rc && server->ops->close) +		server->ops->close(xid, tcon, &fid); +  	cifs_put_tlink(tlink); -	FreeXid(xid); +out_free_xid: +	free_xid(xid);  	return rc;  } -int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, +int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,  		dev_t device_number)  {  	int rc = -EPERM; -	int xid; +	unsigned int xid; +	int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *tcon; +	struct cifs_io_parms io_parms;  	char *full_path = NULL;  	struct inode *newinode = NULL;  	int oplock = 0; -	u16 fileHandle; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	FILE_ALL_INFO *buf = NULL;  	unsigned int bytes_written;  	struct win_dev *pdev; @@ -386,9 +584,9 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -396,7 +594,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,  		goto mknod_out;  	} -	if (pTcon->unix_ext) { +	if (tcon->unix_ext) {  		struct cifs_unix_set_info_args args = {  			.mode	= mode & ~current_umask(),  			.ctime	= NO_CHANGE_64, @@ -405,13 +603,13 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,  			.device	= device_number,  		};  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { -			args.uid = (__u64) current_fsuid(); -			args.gid = (__u64) current_fsgid(); +			args.uid = current_fsuid(); +			args.gid = current_fsgid();  		} else { -			args.uid = NO_CHANGE_64; -			args.gid = NO_CHANGE_64; +			args.uid = INVALID_UID; /* no change */ +			args.gid = INVALID_GID; /* no change */  		} -		rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, +		rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,  					    cifs_sb->local_nls,  					    cifs_sb->mnt_cifs_flags &  						CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -420,10 +618,6 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,  		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); @@ -434,52 +628,57 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,  		goto mknod_out; -	cFYI(1, "sfu compat create special file"); +	cifs_dbg(FYI, "sfu compat create special file\n");  	buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);  	if (buf == NULL) {  		kfree(full_path);  		rc = -ENOMEM; -		FreeXid(xid); +		free_xid(xid);  		return rc;  	} -	/* FIXME: would WRITE_OWNER | WRITE_DAC be better? */ -	rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, -			 GENERIC_WRITE, CREATE_NOT_DIR | CREATE_OPTION_SPECIAL, -			 &fileHandle, &oplock, buf, cifs_sb->local_nls, -			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_WRITE; +	oparms.create_options = create_options; +	oparms.disposition = FILE_CREATE; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, buf);  	if (rc)  		goto mknod_out; -	/* BB Do not bother to decode buf since no local inode yet to put -	 * timestamps in, but we can reuse it safely */ +	/* +	 * BB Do not bother to decode buf since no local inode yet to put +	 * timestamps in, but we can reuse it safely. +	 */  	pdev = (struct win_dev *)buf; +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = sizeof(struct win_dev);  	if (S_ISCHR(mode)) {  		memcpy(pdev->type, "IntxCHR", 8); -		pdev->major = -		      cpu_to_le64(MAJOR(device_number)); -		pdev->minor = -		      cpu_to_le64(MINOR(device_number)); -		rc = CIFSSMBWrite(xid, pTcon, -			fileHandle, -			sizeof(struct win_dev), -			0, &bytes_written, (char *)pdev, -			NULL, 0); +		pdev->major = cpu_to_le64(MAJOR(device_number)); +		pdev->minor = cpu_to_le64(MINOR(device_number)); +		rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev, +				  NULL, 0);  	} else if (S_ISBLK(mode)) {  		memcpy(pdev->type, "IntxBLK", 8); -		pdev->major = -		      cpu_to_le64(MAJOR(device_number)); -		pdev->minor = -		      cpu_to_le64(MINOR(device_number)); -		rc = CIFSSMBWrite(xid, pTcon, -			fileHandle, -			sizeof(struct win_dev), -			0, &bytes_written, (char *)pdev, -			NULL, 0); +		pdev->major = cpu_to_le64(MAJOR(device_number)); +		pdev->minor = cpu_to_le64(MINOR(device_number)); +		rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev, +				  NULL, 0);  	} /* else if (S_ISFIFO) */ -	CIFSSMBClose(xid, pTcon, fileHandle); +	CIFSSMBClose(xid, tcon, fid.netfid);  	d_drop(direntry);  	/* FIXME: add code here to set EAs */ @@ -487,66 +686,41 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,  mknod_out:  	kfree(full_path);  	kfree(buf); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  	return rc;  }  struct dentry *  cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, -	    struct nameidata *nd) +	    unsigned int flags)  { -	int xid; +	unsigned int xid;  	int rc = 0; /* to get around spurious gcc warning, set to zero here */ -	__u32 oplock = 0; -	__u16 fileHandle = 0; -	bool posix_open = false;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; -	struct cifsFileInfo *cfile; +	struct cifs_tcon *pTcon;  	struct inode *newInode = NULL;  	char *full_path = NULL; -	struct file *filp; -	xid = GetXid(); +	xid = get_xid(); -	cFYI(1, "parent inode = 0x%p name is: %s and dentry = 0x%p", -	      parent_dir_inode, direntry->d_name.name, direntry); +	cifs_dbg(FYI, "parent inode = 0x%p name is: %s and dentry = 0x%p\n", +		 parent_dir_inode, direntry->d_name.name, direntry);  	/* check whether path exists */  	cifs_sb = CIFS_SB(parent_dir_inode->i_sb);  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) { -		FreeXid(xid); +		free_xid(xid);  		return (struct dentry *)tlink;  	}  	pTcon = tlink_tcon(tlink); -	/* -	 * 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"); -				rc = -EINVAL; -				goto lookup_out; -			} -	} - -	/* -	 * O_EXCL: optimize away the lookup, but don't hash the dentry. Let -	 * the VFS handle the create. -	 */ -	if (nd && (nd->flags & LOOKUP_EXCL)) { -		d_instantiate(direntry, NULL); -		rc = 0; +	rc = check_name(direntry); +	if (rc)  		goto lookup_out; -	}  	/* can not grab the rename sem here since it would  	deadlock in the cases (beginning of sys_rename itself) @@ -558,74 +732,23 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,  	}  	if (direntry->d_inode != NULL) { -		cFYI(1, "non-NULL inode in lookup"); +		cifs_dbg(FYI, "non-NULL inode in lookup\n");  	} else { -		cFYI(1, "NULL inode in lookup"); +		cifs_dbg(FYI, "NULL inode in lookup\n");  	} -	cFYI(1, "Full path: %s inode = 0x%p", full_path, direntry->d_inode); - -	/* Posix open is only called (at lookup time) for file create now. -	 * For opens (rather than creates), because we do not know if it -	 * is a file or directory yet, and current Samba no longer allows -	 * us to do posix open on dirs, we could end up wasting an open call -	 * on what turns out to be a dir. For file opens, we wait to call posix -	 * open till cifs_open.  It could be added here (lookup) in the future -	 * but the performance tradeoff of the extra network request when EISDIR -	 * or EACCES is returned would have to be weighed against the 50% -	 * reduction in network traffic in the other paths. -	 */ +	cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", +		 full_path, direntry->d_inode); +  	if (pTcon->unix_ext) { -		if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && -		     (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && -		     (nd->intent.open.file->f_flags & O_CREAT)) { -			rc = cifs_posix_open(full_path, &newInode, -					parent_dir_inode->i_sb, -					nd->intent.open.create_mode, -					nd->intent.open.file->f_flags, &oplock, -					&fileHandle, xid); -			/* -			 * The check below works around a bug in POSIX -			 * open in samba versions 3.3.1 and earlier where -			 * open could incorrectly fail with invalid parameter. -			 * If either that or op not supported returned, follow -			 * the normal lookup. -			 */ -			if ((rc == 0) || (rc == -ENOENT)) -				posix_open = true; -			else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP)) -				pTcon->broken_posix_open = true; -		} -		if (!posix_open) -			rc = cifs_get_inode_info_unix(&newInode, full_path, -						parent_dir_inode->i_sb, xid); -	} else +		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); -		if (posix_open) { -			filp = lookup_instantiate_filp(nd, direntry, -						       generic_file_open); -			if (IS_ERR(filp)) { -				rc = PTR_ERR(filp); -				CIFSSMBClose(xid, pTcon, fileHandle); -				goto lookup_out; -			} - -			cfile = cifs_new_fileinfo(fileHandle, filp, tlink, -						  oplock); -			if (cfile == NULL) { -				fput(filp); -				CIFSSMBClose(xid, pTcon, fileHandle); -				rc = -ENOMEM; -				goto lookup_out; -			} -		}  		/* since paths are not looked up by component - the parent  		   directories are presumed to be good here */  		renew_parental_timestamps(direntry); @@ -633,15 +756,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *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); +		cifs_dbg(FYI, "Unexpected lookup error %d\n", rc);  		/* We special case check for Access Denied - since that  		is a common return code */  	} @@ -649,80 +768,144 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,  lookup_out:  	kfree(full_path);  	cifs_put_tlink(tlink); -	FreeXid(xid); +	free_xid(xid);  	return ERR_PTR(rc);  }  static int -cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) +cifs_d_revalidate(struct dentry *direntry, unsigned int flags)  { -	int isValid = 1; +	if (flags & LOOKUP_RCU) +		return -ECHILD;  	if (direntry->d_inode) {  		if (cifs_revalidate_dentry(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; +		else { +			/* +			 * If the inode wasn't known to be a dfs entry when +			 * the dentry was instantiated, such as when created +			 * via ->readdir(), it needs to be set now since the +			 * attributes will have been updated by +			 * cifs_revalidate_dentry(). +			 */ +			if (IS_AUTOMOUNT(direntry->d_inode) && +			   !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { +				spin_lock(&direntry->d_lock); +				direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; +				spin_unlock(&direntry->d_lock); +			} + +			return 1;  		}  	} -	return isValid; +	/* +	 * This may be nfsd (or something), anyway, we can't see the +	 * intent of this. So, since this can be for creation, drop it. +	 */ +	if (!flags) +		return 0; + +	/* +	 * Drop the negative dentry, in order to make sure to use the +	 * case sensitive name which is specified by user if this is +	 * for creation. +	 */ +	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) +		return 0; + +	if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled) +		return 0; + +	return 1;  }  /* static int cifs_d_delete(struct dentry *direntry)  {  	int rc = 0; -	cFYI(1, "In cifs d_delete, name = %s", direntry->d_name.name); +	cifs_dbg(FYI, "In cifs d_delete, name = %s\n", direntry->d_name.name);  	return rc;  }     */  const struct dentry_operations cifs_dentry_ops = {  	.d_revalidate = cifs_d_revalidate, +	.d_automount = cifs_dfs_d_automount,  /* d_delete:       cifs_d_delete,      */ /* not needed except for debugging */  }; -static int cifs_ci_hash(struct dentry *dentry, struct qstr *q) +static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q)  { -	struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; +	struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;  	unsigned long hash; -	int i; +	wchar_t c; +	int i, charlen;  	hash = init_name_hash(); -	for (i = 0; i < q->len; i++) -		hash = partial_name_hash(nls_tolower(codepage, q->name[i]), -					 hash); +	for (i = 0; i < q->len; i += charlen) { +		charlen = codepage->char2uni(&q->name[i], q->len - i, &c); +		/* error out if we can't convert the character */ +		if (unlikely(charlen < 0)) +			return charlen; +		hash = partial_name_hash(cifs_toupper(c), hash); +	}  	q->hash = end_name_hash(hash);  	return 0;  } -static int cifs_ci_compare(struct dentry *dentry, struct qstr *a, -			   struct qstr *b) +static int cifs_ci_compare(const struct dentry *parent, const struct dentry *dentry, +		unsigned int len, const char *str, const struct qstr *name)  { -	struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls; +	struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls; +	wchar_t c1, c2; +	int i, l1, l2; + +	/* +	 * We make the assumption here that uppercase characters in the local +	 * codepage are always the same length as their lowercase counterparts. +	 * +	 * If that's ever not the case, then this will fail to match it. +	 */ +	if (name->len != len) +		return 1; + +	for (i = 0; i < len; i += l1) { +		/* Convert characters in both strings to UTF-16. */ +		l1 = codepage->char2uni(&str[i], len - i, &c1); +		l2 = codepage->char2uni(&name->name[i], name->len - i, &c2); -	if ((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 +		 * If we can't convert either character, just declare it to +		 * be 1 byte long and compare the original byte.  		 */ -		memcpy((void *)a->name, b->name, a->len); -		return 0; +		if (unlikely(l1 < 0 && l2 < 0)) { +			if (str[i] != name->name[i]) +				return 1; +			l1 = 1; +			continue; +		} + +		/* +		 * Here, we again ass|u|me that upper/lowercase versions of +		 * a character are the same length in the local NLS. +		 */ +		if (l1 != l2) +			return 1; + +		/* Now compare uppercase versions of these characters */ +		if (cifs_toupper(c1) != cifs_toupper(c2)) +			return 1;  	} -	return 1; + +	return 0;  }  const struct dentry_operations cifs_ci_dentry_ops = {  	.d_revalidate = cifs_d_revalidate,  	.d_hash = cifs_ci_hash,  	.d_compare = cifs_ci_compare, +	.d_automount = cifs_dfs_d_automount,  }; diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 0eb87026cad..7ede7306599 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -34,7 +34,7 @@  /**   * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. - * @unc: UNC path specifying the server + * @unc: UNC path specifying the server (with '/' as delimiter)   * @ip_addr: Where to return the IP address.   *   * The IP address will be returned in string form, and the caller is @@ -55,7 +55,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)  	len = strlen(unc);  	if (len < 3) { -		cFYI(1, "%s: unc is too short: %s", __func__, unc); +		cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc);  		return -EINVAL;  	} @@ -64,12 +64,12 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)  	hostname = unc + 2;  	/* Search for server name delimiter */ -	sep = memchr(hostname, '\\', len); +	sep = memchr(hostname, '/', len);  	if (sep) -		len = sep - unc; +		len = sep - hostname;  	else -		cFYI(1, "%s: probably server name is whole unc: %s", -		     __func__, unc); +		cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n", +			 __func__, unc);  	/* Try to interpret hostname as an IPv4 or IPv6 address */  	rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len); @@ -79,11 +79,11 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)  	/* Perform the upcall */  	rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL);  	if (rc < 0) -		cERROR(1, "%s: unable to resolve: %*.*s", -		       __func__, len, len, hostname); +		cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", +			 __func__, len, len, hostname);  	else -		cFYI(1, "%s: resolved: %*.*s to %s", -		     __func__, len, len, hostname, *ip_addr); +		cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n", +			 __func__, len, len, hostname, *ip_addr);  	return rc;  name_is_IP_address: @@ -92,7 +92,8 @@ name_is_IP_address:  		return -ENOMEM;  	memcpy(name, hostname, len);  	name[len] = 0; -	cFYI(1, "%s: unc is IP, skipping dns upcall: %s", __func__, name); +	cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n", +		 __func__, name);  	*ip_addr = name;  	return 0;  } diff --git a/fs/cifs/export.c b/fs/cifs/export.c index 993f82045bf..ce8b7f677c5 100644 --- a/fs/cifs/export.c +++ b/fs/cifs/export.c @@ -45,11 +45,11 @@  #include "cifs_debug.h"  #include "cifsfs.h" -#ifdef CONFIG_CIFS_EXPERIMENTAL +#ifdef CONFIG_CIFS_NFSD_EXPORT  static struct dentry *cifs_get_parent(struct dentry *dentry)  {  	/* BB need to add code here eventually to enable export via NFSD */ -	cFYI(1, "get parent for %p", dentry); +	cifs_dbg(FYI, "get parent for %p\n", dentry);  	return ERR_PTR(-EACCES);  } @@ -63,5 +63,5 @@ const struct export_operations cifs_export_ops = {  	.encode_fs =  */  }; -#endif /* EXPERIMENTAL */ +#endif /* CONFIG_CIFS_NFSD_EXPORT */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 06c3e83fa38..e90a1e9aa62 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -32,6 +32,7 @@  #include <linux/delay.h>  #include <linux/mount.h>  #include <linux/slab.h> +#include <linux/swap.h>  #include <asm/div64.h>  #include "cifsfs.h"  #include "cifspdu.h" @@ -42,6 +43,7 @@  #include "cifs_fs_sb.h"  #include "fscache.h" +  static inline int cifs_convert_flags(unsigned int flags)  {  	if ((flags & O_ACCMODE) == O_RDONLY) @@ -71,10 +73,14 @@ static u32 cifs_posix_convert_flags(unsigned int flags)  	else if ((flags & O_ACCMODE) == O_RDWR)  		posix_flags = SMB_O_RDWR; -	if (flags & O_CREAT) +	if (flags & O_CREAT) {  		posix_flags |= SMB_O_CREAT; -	if (flags & O_EXCL) -		posix_flags |= SMB_O_EXCL; +		if (flags & O_EXCL) +			posix_flags |= SMB_O_EXCL; +	} else if (flags & O_EXCL) +		cifs_dbg(FYI, "Application %s pid %d has incorrectly set O_EXCL flag but not O_CREAT on file open. Ignoring O_EXCL\n", +			 current->comm, current->tgid); +  	if (flags & O_TRUNC)  		posix_flags |= SMB_O_TRUNC;  	/* be safe and imply O_SYNC for O_DSYNC */ @@ -104,56 +110,9 @@ static inline int cifs_get_disposition(unsigned int flags)  		return FILE_OPEN;  } -static inline int cifs_open_inode_helper(struct inode *inode, -	struct cifsTconInfo *pTcon, __u32 oplock, FILE_ALL_INFO *buf, -	char *full_path, int xid) -{ -	struct cifsInodeInfo *pCifsInode = CIFS_I(inode); -	struct timespec temp; -	int rc; - -	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(buf->LastWriteTime); -	if (timespec_equal(&inode->i_mtime, &temp) && -			   (inode->i_size == -			    (loff_t)le64_to_cpu(buf->EndOfFile))) { -		cFYI(1, "inode unchanged on server"); -	} else { -		if (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(inode->i_mapping); -			mapping_set_error(inode->i_mapping, rc); -		} -		cFYI(1, "invalidating remote inode since open detected it " -			 "changed"); -		invalidate_remote_inode(inode); -	} - -client_can_cache: -	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, buf, inode->i_sb, -					 xid, NULL); - -	cifs_set_oplock_level(pCifsInode, oplock); - -	return rc; -} -  int cifs_posix_open(char *full_path, struct inode **pinode,  			struct super_block *sb, int mode, unsigned int f_flags, -			__u32 *poplock, __u16 *pnetfid, int xid) +			__u32 *poplock, __u16 *pnetfid, unsigned int xid)  {  	int rc;  	FILE_UNIX_BASIC_INFO *presp_data; @@ -161,9 +120,9 @@ int cifs_posix_open(char *full_path, struct inode **pinode,  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);  	struct cifs_fattr fattr;  	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon; -	cFYI(1, "posix open %s", full_path); +	cifs_dbg(FYI, "posix open %s\n", full_path);  	presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);  	if (presp_data == NULL) @@ -213,45 +172,182 @@ posix_open_ret:  	return rc;  } +static int +cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, +	     struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, +	     struct cifs_fid *fid, unsigned int xid) +{ +	int rc; +	int desired_access; +	int disposition; +	int create_options = CREATE_NOT_DIR; +	FILE_ALL_INFO *buf; +	struct TCP_Server_Info *server = tcon->ses->server; +	struct cifs_open_parms oparms; + +	if (!server->ops->open) +		return -ENOSYS; + +	desired_access = cifs_convert_flags(f_flags); + +/********************************************************************* + *  open flag mapping table: + * + *	POSIX Flag            CIFS Disposition + *	----------            ---------------- + *	O_CREAT               FILE_OPEN_IF + *	O_CREAT | O_EXCL      FILE_CREATE + *	O_CREAT | O_TRUNC     FILE_OVERWRITE_IF + *	O_TRUNC               FILE_OVERWRITE + *	none of the above     FILE_OPEN + * + *	Note that there is not a direct match between disposition + *	FILE_SUPERSEDE (ie create whether or not file exists although + *	O_CREAT | O_TRUNC is similar but truncates the existing + *	file rather than creating a new file as FILE_SUPERSEDE does + *	(which uses the attributes / metadata passed in on open call) + *? + *?  O_SYNC is a reasonable match to CIFS writethrough flag + *?  and the read write flags match reasonably.  O_LARGEFILE + *?  is irrelevant because largefile support is always used + *?  by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY, + *	 O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation + *********************************************************************/ + +	disposition = cifs_get_disposition(f_flags); + +	/* BB pass O_SYNC flag through on file attributes .. BB */ + +	buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = desired_access; +	oparms.create_options = create_options; +	oparms.disposition = disposition; +	oparms.path = full_path; +	oparms.fid = fid; +	oparms.reconnect = false; + +	rc = server->ops->open(xid, &oparms, oplock, buf); + +	if (rc) +		goto out; + +	if (tcon->unix_ext) +		rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, +					      xid); +	else +		rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, +					 xid, fid); + +out: +	kfree(buf); +	return rc; +} + +static bool +cifs_has_mand_locks(struct cifsInodeInfo *cinode) +{ +	struct cifs_fid_locks *cur; +	bool has_locks = false; + +	down_read(&cinode->lock_sem); +	list_for_each_entry(cur, &cinode->llist, llist) { +		if (!list_empty(&cur->locks)) { +			has_locks = true; +			break; +		} +	} +	up_read(&cinode->lock_sem); +	return has_locks; +} +  struct cifsFileInfo * -cifs_new_fileinfo(__u16 fileHandle, struct file *file, +cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,  		  struct tcon_link *tlink, __u32 oplock)  {  	struct dentry *dentry = file->f_path.dentry;  	struct inode *inode = dentry->d_inode; -	struct cifsInodeInfo *pCifsInode = CIFS_I(inode); -	struct cifsFileInfo *pCifsFile; - -	pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); -	if (pCifsFile == NULL) -		return pCifsFile; - -	pCifsFile->count = 1; -	pCifsFile->netfid = fileHandle; -	pCifsFile->pid = current->tgid; -	pCifsFile->uid = current_fsuid(); -	pCifsFile->dentry = dget(dentry); -	pCifsFile->f_flags = file->f_flags; -	pCifsFile->invalidHandle = false; -	pCifsFile->tlink = cifs_get_tlink(tlink); -	mutex_init(&pCifsFile->fh_mutex); -	mutex_init(&pCifsFile->lock_mutex); -	INIT_LIST_HEAD(&pCifsFile->llist); -	INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); +	struct cifsInodeInfo *cinode = CIFS_I(inode); +	struct cifsFileInfo *cfile; +	struct cifs_fid_locks *fdlocks; +	struct cifs_tcon *tcon = tlink_tcon(tlink); +	struct TCP_Server_Info *server = tcon->ses->server; + +	cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); +	if (cfile == NULL) +		return cfile; + +	fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL); +	if (!fdlocks) { +		kfree(cfile); +		return NULL; +	} + +	INIT_LIST_HEAD(&fdlocks->locks); +	fdlocks->cfile = cfile; +	cfile->llist = fdlocks; +	down_write(&cinode->lock_sem); +	list_add(&fdlocks->llist, &cinode->llist); +	up_write(&cinode->lock_sem); + +	cfile->count = 1; +	cfile->pid = current->tgid; +	cfile->uid = current_fsuid(); +	cfile->dentry = dget(dentry); +	cfile->f_flags = file->f_flags; +	cfile->invalidHandle = false; +	cfile->tlink = cifs_get_tlink(tlink); +	INIT_WORK(&cfile->oplock_break, cifs_oplock_break); +	mutex_init(&cfile->fh_mutex); + +	cifs_sb_active(inode->i_sb); + +	/* +	 * If the server returned a read oplock and we have mandatory brlocks, +	 * set oplock level to None. +	 */ +	if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { +		cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); +		oplock = 0; +	}  	spin_lock(&cifs_file_list_lock); -	list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); +	if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) +		oplock = fid->pending_open->oplock; +	list_del(&fid->pending_open->olist); + +	fid->purge_cache = false; +	server->ops->set_fid(cfile, fid, oplock); + +	list_add(&cfile->tlist, &tcon->openFileList);  	/* if readable file instance put first in list*/  	if (file->f_mode & FMODE_READ) -		list_add(&pCifsFile->flist, &pCifsInode->openFileList); +		list_add(&cfile->flist, &cinode->openFileList);  	else -		list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); +		list_add_tail(&cfile->flist, &cinode->openFileList);  	spin_unlock(&cifs_file_list_lock); -	cifs_set_oplock_level(pCifsInode, oplock); +	if (fid->purge_cache) +		cifs_zap_mapping(inode); -	file->private_data = pCifsFile; -	return pCifsFile; +	file->private_data = cfile; +	return cfile; +} + +struct cifsFileInfo * +cifsFileInfo_get(struct cifsFileInfo *cifs_file) +{ +	spin_lock(&cifs_file_list_lock); +	cifsFileInfo_get_locked(cifs_file); +	spin_unlock(&cifs_file_list_lock); +	return cifs_file;  }  /* @@ -262,9 +358,14 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file,  void cifsFileInfo_put(struct cifsFileInfo *cifs_file)  {  	struct inode *inode = cifs_file->dentry->d_inode; -	struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink); +	struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); +	struct TCP_Server_Info *server = tcon->ses->server;  	struct cifsInodeInfo *cifsi = CIFS_I(inode); +	struct super_block *sb = inode->i_sb; +	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);  	struct cifsLockInfo *li, *tmp; +	struct cifs_fid fid; +	struct cifs_pending_open open;  	spin_lock(&cifs_file_list_lock);  	if (--cifs_file->count > 0) { @@ -272,67 +373,90 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)  		return;  	} +	if (server->ops->get_lease_key) +		server->ops->get_lease_key(inode, &fid); + +	/* store open in pending opens to make sure we don't miss lease break */ +	cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open); +  	/* remove it from the lists */  	list_del(&cifs_file->flist);  	list_del(&cifs_file->tlist);  	if (list_empty(&cifsi->openFileList)) { -		cFYI(1, "closing last open instance for inode %p", -			cifs_file->dentry->d_inode); +		cifs_dbg(FYI, "closing last open instance for inode %p\n", +			 cifs_file->dentry->d_inode); +		/* +		 * In strict cache mode we need invalidate mapping on the last +		 * close  because it may cause a error when we open this file +		 * again and get at least level II oplock. +		 */ +		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) +			set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);  		cifs_set_oplock_level(cifsi, 0);  	}  	spin_unlock(&cifs_file_list_lock); +	cancel_work_sync(&cifs_file->oplock_break); +  	if (!tcon->need_reconnect && !cifs_file->invalidHandle) { -		int xid, rc; +		struct TCP_Server_Info *server = tcon->ses->server; +		unsigned int xid; -		xid = GetXid(); -		rc = CIFSSMBClose(xid, tcon, cifs_file->netfid); -		FreeXid(xid); +		xid = get_xid(); +		if (server->ops->close) +			server->ops->close(xid, tcon, &cifs_file->fid); +		_free_xid(xid);  	} -	/* Delete any outstanding lock records. We'll lose them when the file +	cifs_del_pending_open(&open); + +	/* +	 * Delete any outstanding lock records. We'll lose them when the file  	 * is closed anyway.  	 */ -	mutex_lock(&cifs_file->lock_mutex); -	list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) { +	down_write(&cifsi->lock_sem); +	list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {  		list_del(&li->llist); +		cifs_del_lock_waiters(li);  		kfree(li);  	} -	mutex_unlock(&cifs_file->lock_mutex); +	list_del(&cifs_file->llist->llist); +	kfree(cifs_file->llist); +	up_write(&cifsi->lock_sem);  	cifs_put_tlink(cifs_file->tlink);  	dput(cifs_file->dentry); +	cifs_sb_deactive(sb);  	kfree(cifs_file);  }  int cifs_open(struct inode *inode, struct file *file) +  {  	int rc = -EACCES; -	int xid; +	unsigned int xid;  	__u32 oplock;  	struct cifs_sb_info *cifs_sb; -	struct cifsTconInfo *tcon; +	struct TCP_Server_Info *server; +	struct cifs_tcon *tcon;  	struct tcon_link *tlink; -	struct cifsFileInfo *pCifsFile = NULL; -	struct cifsInodeInfo *pCifsInode; +	struct cifsFileInfo *cfile = NULL;  	char *full_path = NULL; -	int desiredAccess; -	int disposition; -	__u16 netfid; -	FILE_ALL_INFO *buf = NULL; +	bool posix_open_ok = false; +	struct cifs_fid fid; +	struct cifs_pending_open open; -	xid = GetXid(); +	xid = get_xid();  	cifs_sb = CIFS_SB(inode->i_sb);  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) { -		FreeXid(xid); +		free_xid(xid);  		return PTR_ERR(tlink);  	}  	tcon = tlink_tcon(tlink); - -	pCifsInode = CIFS_I(file->f_path.dentry->d_inode); +	server = tcon->ses->server;  	full_path = build_path_from_dentry(file->f_path.dentry);  	if (full_path == NULL) { @@ -340,291 +464,279 @@ int cifs_open(struct inode *inode, struct file *file)  		goto out;  	} -	cFYI(1, "inode = 0x%p file flags are 0x%x for %s", +	cifs_dbg(FYI, "inode = 0x%p file flags are 0x%x for %s\n",  		 inode, file->f_flags, full_path); -	if (oplockEnabled) +	if (server->oplocks)  		oplock = REQ_OPLOCK;  	else  		oplock = 0;  	if (!tcon->broken_posix_open && tcon->unix_ext && -	    (tcon->ses->capabilities & CAP_UNIX) && -	    (CIFS_UNIX_POSIX_PATH_OPS_CAP & -			le64_to_cpu(tcon->fsUnixInfo.Capability))) { +	    cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & +				le64_to_cpu(tcon->fsUnixInfo.Capability))) {  		/* can not refresh inode info since size could be stale */  		rc = cifs_posix_open(full_path, &inode, inode->i_sb,  				cifs_sb->mnt_file_mode /* ignored */, -				file->f_flags, &oplock, &netfid, xid); +				file->f_flags, &oplock, &fid.netfid, xid);  		if (rc == 0) { -			cFYI(1, "posix open succeeded"); - -			pCifsFile = cifs_new_fileinfo(netfid, file, tlink, -						      oplock); -			if (pCifsFile == NULL) { -				CIFSSMBClose(xid, tcon, netfid); -				rc = -ENOMEM; -			} - -			cifs_fscache_set_inode_cookie(inode, file); - -			goto out; +			cifs_dbg(FYI, "posix open succeeded\n"); +			posix_open_ok = true;  		} else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {  			if (tcon->ses->serverNOS) -				cERROR(1, "server %s of type %s returned" -					   " unexpected error on SMB posix open" -					   ", disabling posix open support." -					   " Check if server update available.", -					   tcon->ses->serverName, -					   tcon->ses->serverNOS); +				cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n", +					 tcon->ses->serverName, +					 tcon->ses->serverNOS);  			tcon->broken_posix_open = true;  		} else if ((rc != -EIO) && (rc != -EREMOTE) &&  			 (rc != -EOPNOTSUPP)) /* path not found or net err */  			goto out; -		/* else fallthrough to retry open the old way on network i/o -		   or DFS errors */ +		/* +		 * Else fallthrough to retry open the old way on network i/o +		 * or DFS errors. +		 */  	} -	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 (server->ops->get_lease_key) +		server->ops->get_lease_key(inode, &fid); -	/* BB pass O_SYNC flag through on file attributes .. BB */ +	cifs_add_pending_open(&fid, tlink, &open); -	/* 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) */ +	if (!posix_open_ok) { +		if (server->ops->get_lease_key) +			server->ops->get_lease_key(inode, &fid); -	/* 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 (tcon->ses->capabilities & CAP_NT_SMBS) -		rc = CIFSSMBOpen(xid, tcon, 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, tcon, 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; +		rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, +				  file->f_flags, &oplock, &fid, xid); +		if (rc) { +			cifs_del_pending_open(&open); +			goto out; +		}  	} -	rc = cifs_open_inode_helper(inode, tcon, oplock, buf, full_path, xid); -	if (rc != 0) -		goto out; - -	pCifsFile = cifs_new_fileinfo(netfid, file, tlink, oplock); -	if (pCifsFile == NULL) { +	cfile = cifs_new_fileinfo(&fid, file, tlink, oplock); +	if (cfile == NULL) { +		if (server->ops->close) +			server->ops->close(xid, tcon, &fid); +		cifs_del_pending_open(&open);  		rc = -ENOMEM;  		goto out;  	}  	cifs_fscache_set_inode_cookie(inode, file); -	if (oplock & CIFS_CREATE_ACTION) { -		/* time to set mode which we can not set earlier due to -		   problems creating new read-only files */ -		if (tcon->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, -			}; -			CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, -					       cifs_sb->local_nls, -					       cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); -		} +	if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) { +		/* +		 * Time to set mode which we can not set earlier due to +		 * problems creating new read-only files. +		 */ +		struct cifs_unix_set_info_args args = { +			.mode	= inode->i_mode, +			.uid	= INVALID_UID, /* no change */ +			.gid	= INVALID_GID, /* no change */ +			.ctime	= NO_CHANGE_64, +			.atime	= NO_CHANGE_64, +			.mtime	= NO_CHANGE_64, +			.device	= 0, +		}; +		CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid, +				       cfile->pid);  	}  out: -	kfree(buf);  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  	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) +static int cifs_push_posix_locks(struct cifsFileInfo *cfile); + +/* + * Try to reacquire byte range locks that were released when session + * to server was lost. + */ +static int +cifs_relock_file(struct cifsFileInfo *cfile)  { +	struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);  	int rc = 0; -/* BB list all locks open on this file and relock */ +	down_read(&cinode->lock_sem); +	if (cinode->can_cache_brlcks) { +		/* can cache locks - no need to relock */ +		up_read(&cinode->lock_sem); +		return rc; +	} + +	if (cap_unix(tcon->ses) && +	    (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && +	    ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) +		rc = cifs_push_posix_locks(cfile); +	else +		rc = tcon->ses->server->ops->push_mand_locks(cfile); +	up_read(&cinode->lock_sem);  	return rc;  } -static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) +static int +cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)  {  	int rc = -EACCES; -	int xid; +	unsigned int xid;  	__u32 oplock;  	struct cifs_sb_info *cifs_sb; -	struct cifsTconInfo *tcon; -	struct cifsInodeInfo *pCifsInode; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	struct cifsInodeInfo *cinode;  	struct inode *inode;  	char *full_path = NULL; -	int desiredAccess; +	int desired_access;  	int disposition = FILE_OPEN; -	__u16 netfid; +	int create_options = CREATE_NOT_DIR; +	struct cifs_open_parms oparms; -	xid = GetXid(); -	mutex_lock(&pCifsFile->fh_mutex); -	if (!pCifsFile->invalidHandle) { -		mutex_unlock(&pCifsFile->fh_mutex); +	xid = get_xid(); +	mutex_lock(&cfile->fh_mutex); +	if (!cfile->invalidHandle) { +		mutex_unlock(&cfile->fh_mutex);  		rc = 0; -		FreeXid(xid); +		free_xid(xid);  		return rc;  	} -	inode = pCifsFile->dentry->d_inode; +	inode = cfile->dentry->d_inode;  	cifs_sb = CIFS_SB(inode->i_sb); -	tcon = tlink_tcon(pCifsFile->tlink); +	tcon = tlink_tcon(cfile->tlink); +	server = tcon->ses->server; -/* can not grab rename sem here because various ops, including -   those that already have the rename sem can end up causing writepage -   to get called and if the server was down that means we end up here, -   and we can never tell if the caller already has the rename_sem */ -	full_path = build_path_from_dentry(pCifsFile->dentry); +	/* +	 * Can not grab rename sem here because various ops, including those +	 * that already have the rename sem can end up causing writepage to get +	 * called and if the server was down that means we end up here, and we +	 * can never tell if the caller already has the rename_sem. +	 */ +	full_path = build_path_from_dentry(cfile->dentry);  	if (full_path == NULL) {  		rc = -ENOMEM; -		mutex_unlock(&pCifsFile->fh_mutex); -		FreeXid(xid); +		mutex_unlock(&cfile->fh_mutex); +		free_xid(xid);  		return rc;  	} -	cFYI(1, "inode = 0x%p file flags 0x%x for %s", -		 inode, pCifsFile->f_flags, full_path); +	cifs_dbg(FYI, "inode = 0x%p file flags 0x%x for %s\n", +		 inode, cfile->f_flags, full_path); -	if (oplockEnabled) +	if (tcon->ses->server->oplocks)  		oplock = REQ_OPLOCK;  	else  		oplock = 0; -	if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && +	if (tcon->unix_ext && cap_unix(tcon->ses) &&  	    (CIFS_UNIX_POSIX_PATH_OPS_CAP & -			le64_to_cpu(tcon->fsUnixInfo.Capability))) { - +				le64_to_cpu(tcon->fsUnixInfo.Capability))) {  		/*  		 * O_CREAT, O_EXCL and O_TRUNC already had their effect on the  		 * original open. Must mask them off for a reopen.  		 */ -		unsigned int oflags = pCifsFile->f_flags & +		unsigned int oflags = cfile->f_flags &  						~(O_CREAT | O_EXCL | O_TRUNC);  		rc = cifs_posix_open(full_path, NULL, inode->i_sb, -				cifs_sb->mnt_file_mode /* ignored */, -				oflags, &oplock, &netfid, xid); +				     cifs_sb->mnt_file_mode /* ignored */, +				     oflags, &oplock, &cfile->fid.netfid, xid);  		if (rc == 0) { -			cFYI(1, "posix reopen succeeded"); +			cifs_dbg(FYI, "posix reopen succeeded\n"); +			oparms.reconnect = true;  			goto reopen_success;  		} -		/* fallthrough to retry open the old way on errors, especially -		   in the reconnect path it is important to retry hard */ +		/* +		 * fallthrough to retry open the old way on errors, especially +		 * in the reconnect path it is important to retry hard +		 */  	} -	desiredAccess = cifs_convert_flags(pCifsFile->f_flags); +	desired_access = cifs_convert_flags(cfile->f_flags); + +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	if (server->ops->get_lease_key) +		server->ops->get_lease_key(inode, &cfile->fid); -	/* 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 */ +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = desired_access; +	oparms.create_options = create_options; +	oparms.disposition = disposition; +	oparms.path = full_path; +	oparms.fid = &cfile->fid; +	oparms.reconnect = true; + +	/* +	 * Can not refresh inode by passing in file_info buf to be returned by +	 * ops->open and then calling get_inode_info with returned buf since +	 * file might have write behind data that needs to be flushed and server +	 * version of file size can be stale. If we knew for sure that inode was +	 * not dirty locally we could do this. +	 */ +	rc = server->ops->open(xid, &oparms, &oplock, NULL); +	if (rc == -ENOENT && oparms.reconnect == false) { +		/* durable handle timeout is expired - open the file again */ +		rc = server->ops->open(xid, &oparms, &oplock, NULL); +		/* indicate that we need to relock the file */ +		oparms.reconnect = true; +	} -	rc = CIFSSMBOpen(xid, tcon, 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) { -		mutex_unlock(&pCifsFile->fh_mutex); -		cFYI(1, "cifs_open returned 0x%x", rc); -		cFYI(1, "oplock: %d", oplock); +		mutex_unlock(&cfile->fh_mutex); +		cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc); +		cifs_dbg(FYI, "oplock: %d\n", oplock);  		goto reopen_error_exit;  	}  reopen_success: -	pCifsFile->netfid = netfid; -	pCifsFile->invalidHandle = false; -	mutex_unlock(&pCifsFile->fh_mutex); -	pCifsInode = CIFS_I(inode); +	cfile->invalidHandle = false; +	mutex_unlock(&cfile->fh_mutex); +	cinode = CIFS_I(inode);  	if (can_flush) {  		rc = filemap_write_and_wait(inode->i_mapping);  		mapping_set_error(inode->i_mapping, rc);  		if (tcon->unix_ext) -			rc = cifs_get_inode_info_unix(&inode, -				full_path, inode->i_sb, xid); +			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 */ - -	cifs_set_oplock_level(pCifsInode, oplock); +			rc = cifs_get_inode_info(&inode, full_path, NULL, +						 inode->i_sb, xid, NULL); +	} +	/* +	 * Else we are writing out data to server already and could deadlock if +	 * we tried to flush data, and since we do not know if we have data that +	 * would invalidate the current end of file on the server we can not go +	 * to the server to get the new inode info. +	 */ -	cifs_relock_file(pCifsFile); +	server->ops->set_fid(cfile, &cfile->fid, oplock); +	if (oparms.reconnect) +		cifs_relock_file(cfile);  reopen_error_exit:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	return rc;  }  int cifs_close(struct inode *inode, struct file *file)  { -	cifsFileInfo_put(file->private_data); -	file->private_data = NULL; +	if (file->private_data != NULL) { +		cifsFileInfo_put(file->private_data); +		file->private_data = NULL; +	}  	/* return code from the ->release op is always ignored */  	return 0; @@ -633,418 +745,921 @@ int cifs_close(struct inode *inode, struct file *file)  int cifs_closedir(struct inode *inode, struct file *file)  {  	int rc = 0; -	int xid; -	struct cifsFileInfo *pCFileStruct = file->private_data; -	char *ptmp; +	unsigned int xid; +	struct cifsFileInfo *cfile = file->private_data; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	char *buf; -	cFYI(1, "Closedir inode = 0x%p", inode); +	cifs_dbg(FYI, "Closedir inode = 0x%p\n", inode); -	xid = GetXid(); +	if (cfile == NULL) +		return rc; -	if (pCFileStruct) { -		struct cifsTconInfo *pTcon = tlink_tcon(pCFileStruct->tlink); +	xid = get_xid(); +	tcon = tlink_tcon(cfile->tlink); +	server = tcon->ses->server; -		cFYI(1, "Freeing private data in close dir"); -		spin_lock(&cifs_file_list_lock); -		if (!pCFileStruct->srch_inf.endOfSearch && -		    !pCFileStruct->invalidHandle) { -			pCFileStruct->invalidHandle = true; -			spin_unlock(&cifs_file_list_lock); -			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 -			spin_unlock(&cifs_file_list_lock); -		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); -		} -		cifs_put_tlink(pCFileStruct->tlink); -		kfree(file->private_data); -		file->private_data = NULL; +	cifs_dbg(FYI, "Freeing private data in close dir\n"); +	spin_lock(&cifs_file_list_lock); +	if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { +		cfile->invalidHandle = true; +		spin_unlock(&cifs_file_list_lock); +		if (server->ops->close_dir) +			rc = server->ops->close_dir(xid, tcon, &cfile->fid); +		else +			rc = -ENOSYS; +		cifs_dbg(FYI, "Closing uncompleted readdir with rc %d\n", rc); +		/* not much we can do if it fails anyway, ignore rc */ +		rc = 0; +	} else +		spin_unlock(&cifs_file_list_lock); + +	buf = cfile->srch_inf.ntwrk_buf_start; +	if (buf) { +		cifs_dbg(FYI, "closedir free smb buf in srch struct\n"); +		cfile->srch_inf.ntwrk_buf_start = NULL; +		if (cfile->srch_inf.smallBuf) +			cifs_small_buf_release(buf); +		else +			cifs_buf_release(buf);  	} + +	cifs_put_tlink(cfile->tlink); +	kfree(file->private_data); +	file->private_data = NULL;  	/* BB can we lock the filestruct while this is going on? */ -	FreeXid(xid); +	free_xid(xid);  	return rc;  } -static int store_file_lock(struct cifsFileInfo *fid, __u64 len, -				__u64 offset, __u8 lockType) +static struct cifsLockInfo * +cifs_lock_init(__u64 offset, __u64 length, __u8 type)  { -	struct cifsLockInfo *li = +	struct cifsLockInfo *lock =  		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; +	if (!lock) +		return lock; +	lock->offset = offset; +	lock->length = length; +	lock->type = type; +	lock->pid = current->tgid; +	INIT_LIST_HEAD(&lock->blist); +	init_waitqueue_head(&lock->block_q); +	return lock;  } -int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) +void +cifs_del_lock_waiters(struct cifsLockInfo *lock)  { -	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; +	struct cifsLockInfo *li, *tmp; +	list_for_each_entry_safe(li, tmp, &lock->blist, blist) { +		list_del_init(&li->blist); +		wake_up(&li->block_q); +	} +} -	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"); +#define CIFS_LOCK_OP	0 +#define CIFS_READ_OP	1 +#define CIFS_WRITE_OP	2 -	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); -	tcon = tlink_tcon(((struct cifsFileInfo *)file->private_data)->tlink); -	netfid = ((struct cifsFileInfo *)file->private_data)->netfid; +/* @rw_check : 0 - no op, 1 - read, 2 - write */ +static bool +cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, +			    __u64 length, __u8 type, struct cifsFileInfo *cfile, +			    struct cifsLockInfo **conf_lock, int rw_check) +{ +	struct cifsLockInfo *li; +	struct cifsFileInfo *cur_cfile = fdlocks->cfile; +	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; -	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; +	list_for_each_entry(li, &fdlocks->locks, llist) { +		if (offset + length <= li->offset || +		    offset >= li->offset + li->length) +			continue; +		if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid && +		    server->ops->compare_fids(cfile, cur_cfile)) { +			/* shared lock prevents write op through the same fid */ +			if (!(li->type & server->vals->shared_lock_type) || +			    rw_check != CIFS_WRITE_OP) +				continue;  		} +		if ((type & server->vals->shared_lock_type) && +		    ((server->ops->compare_fids(cfile, cur_cfile) && +		     current->tgid == li->pid) || type == li->type)) +			continue; +		if (conf_lock) +			*conf_lock = li; +		return true; +	} +	return false; +} -		/* 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; +bool +cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, +			__u8 type, struct cifsLockInfo **conf_lock, +			int rw_check) +{ +	bool rc = false; +	struct cifs_fid_locks *cur; +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); + +	list_for_each_entry(cur, &cinode->llist, llist) { +		rc = cifs_find_fid_lock_conflict(cur, offset, length, type, +						 cfile, conf_lock, rw_check); +		if (rc) +			break; +	} -		} else { -			/* if rc == ERR_SHARING_VIOLATION ? */ -			rc = 0; +	return rc; +} -			if (lockType & LOCKING_ANDX_SHARED_LOCK) { -				pfLock->fl_type = F_WRLCK; -			} else { -				rc = CIFSSMBLock(xid, tcon, netfid, length, -					pfLock->fl_start, 0, 1, -					lockType | LOCKING_ANDX_SHARED_LOCK, -					0 /* wait flag */); -				if (rc == 0) { -					rc = CIFSSMBLock(xid, tcon, netfid, -						length, pfLock->fl_start, 1, 0, -						lockType | -						LOCKING_ANDX_SHARED_LOCK, -						0 /* wait flag */); -					pfLock->fl_type = F_RDLCK; -					if (rc != 0) -						cERROR(1, "Error unlocking " -						"previously locked range %d " -						"during test of lock", rc); -					rc = 0; -				} else { -					pfLock->fl_type = F_WRLCK; -					rc = 0; -				} -			} -		} +/* + * Check if there is another lock that prevents us to set the lock (mandatory + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ +static int +cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, +	       __u8 type, struct file_lock *flock) +{ +	int rc = 0; +	struct cifsLockInfo *conf_lock; +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; +	bool exist; + +	down_read(&cinode->lock_sem); + +	exist = cifs_find_lock_conflict(cfile, offset, length, type, +					&conf_lock, CIFS_LOCK_OP); +	if (exist) { +		flock->fl_start = conf_lock->offset; +		flock->fl_end = conf_lock->offset + conf_lock->length - 1; +		flock->fl_pid = conf_lock->pid; +		if (conf_lock->type & server->vals->shared_lock_type) +			flock->fl_type = F_RDLCK; +		else +			flock->fl_type = F_WRLCK; +	} else if (!cinode->can_cache_brlcks) +		rc = 1; +	else +		flock->fl_type = F_UNLCK; + +	up_read(&cinode->lock_sem); +	return rc; +} + +static void +cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) +{ +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	down_write(&cinode->lock_sem); +	list_add_tail(&lock->llist, &cfile->llist->locks); +	up_write(&cinode->lock_sem); +} -		FreeXid(xid); +/* + * Set the byte-range lock (mandatory style). Returns: + * 1) 0, if we set the lock and don't need to request to the server; + * 2) 1, if no locks prevent us but we need to request to the server; + * 3) -EACCESS, if there is a lock that prevents us and wait is false. + */ +static int +cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, +		 bool wait) +{ +	struct cifsLockInfo *conf_lock; +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	bool exist; +	int rc = 0; + +try_again: +	exist = false; +	down_write(&cinode->lock_sem); + +	exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, +					lock->type, &conf_lock, CIFS_LOCK_OP); +	if (!exist && cinode->can_cache_brlcks) { +		list_add_tail(&lock->llist, &cfile->llist->locks); +		up_write(&cinode->lock_sem);  		return rc;  	} -	if (!numLock && !numUnlock) { -		/* if no lock or unlock then nothing -		to do since we do not know what it is */ -		FreeXid(xid); -		return -EOPNOTSUPP; +	if (!exist) +		rc = 1; +	else if (!wait) +		rc = -EACCES; +	else { +		list_add_tail(&lock->blist, &conf_lock->blist); +		up_write(&cinode->lock_sem); +		rc = wait_event_interruptible(lock->block_q, +					(lock->blist.prev == &lock->blist) && +					(lock->blist.next == &lock->blist)); +		if (!rc) +			goto try_again; +		down_write(&cinode->lock_sem); +		list_del_init(&lock->blist);  	} -	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; +	up_write(&cinode->lock_sem); +	return rc; +} -		rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */, -				      length, pfLock, -				      posix_lock_type, wait_flag); -	} else { -		struct cifsFileInfo *fid = file->private_data; +/* + * Check if there is another lock that prevents us to set the lock (posix + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ +static int +cifs_posix_lock_test(struct file *file, struct file_lock *flock) +{ +	int rc = 0; +	struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); +	unsigned char saved_type = flock->fl_type; -		if (numLock) { -			rc = CIFSSMBLock(xid, tcon, netfid, length, -					pfLock->fl_start, -					0, numLock, lockType, wait_flag); +	if ((flock->fl_flags & FL_POSIX) == 0) +		return 1; -			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; +	down_read(&cinode->lock_sem); +	posix_test_lock(file, flock); -			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; -					else { -						list_del(&li->llist); -						kfree(li); -					} -				} -			} -			mutex_unlock(&fid->lock_mutex); -		} +	if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) { +		flock->fl_type = saved_type; +		rc = 1;  	} -	if (pfLock->fl_flags & FL_POSIX) -		posix_lock_file_wait(file, pfLock); -	FreeXid(xid); +	up_read(&cinode->lock_sem);  	return rc;  }  /* - * Set the timeout on write requests past EOF. For some servers (Windows) - * these calls can be very long. - * - * If we're writing >10M past the EOF we give a 180s timeout. Anything less - * than that gets a 45s timeout. Writes not past EOF get 15s timeouts. - * The 10M cutoff is totally arbitrary. A better scheme for this would be - * welcome if someone wants to suggest one. - * - * We may be able to do a better job with this if there were some way to - * declare that a file should be sparse. + * Set the byte-range lock (posix style). Returns: + * 1) 0, if we set the lock and don't need to request to the server; + * 2) 1, if we need to request to the server; + * 3) <0, if the error occurs while setting the lock.   */  static int -cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) +cifs_posix_lock_set(struct file *file, struct file_lock *flock)  { -	if (offset <= cifsi->server_eof) -		return CIFS_STD_OP; -	else if (offset > (cifsi->server_eof + (10 * 1024 * 1024))) -		return CIFS_VLONG_OP; -	else -		return CIFS_LONG_OP; +	struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); +	int rc = 1; + +	if ((flock->fl_flags & FL_POSIX) == 0) +		return rc; + +try_again: +	down_write(&cinode->lock_sem); +	if (!cinode->can_cache_brlcks) { +		up_write(&cinode->lock_sem); +		return rc; +	} + +	rc = posix_lock_file(file, flock, NULL); +	up_write(&cinode->lock_sem); +	if (rc == FILE_LOCK_DEFERRED) { +		rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next); +		if (!rc) +			goto try_again; +		posix_unblock_lock(flock); +	} +	return rc;  } -/* update the file size (if needed) after a write */ -static void -cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, -		      unsigned int bytes_written) +int +cifs_push_mandatory_locks(struct cifsFileInfo *cfile)  { -	loff_t end_of_write = offset + bytes_written; +	unsigned int xid; +	int rc = 0, stored_rc; +	struct cifsLockInfo *li, *tmp; +	struct cifs_tcon *tcon; +	unsigned int num, max_num, max_buf; +	LOCKING_ANDX_RANGE *buf, *cur; +	int types[] = {LOCKING_ANDX_LARGE_FILES, +		       LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES}; +	int i; -	if (end_of_write > cifsi->server_eof) -		cifsi->server_eof = end_of_write; +	xid = get_xid(); +	tcon = tlink_tcon(cfile->tlink); + +	/* +	 * Accessing maxBuf is racy with cifs_reconnect - need to store value +	 * and check it for zero before using. +	 */ +	max_buf = tcon->ses->server->maxBuf; +	if (!max_buf) { +		free_xid(xid); +		return -EINVAL; +	} + +	max_num = (max_buf - sizeof(struct smb_hdr)) / +						sizeof(LOCKING_ANDX_RANGE); +	buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); +	if (!buf) { +		free_xid(xid); +		return -ENOMEM; +	} + +	for (i = 0; i < 2; i++) { +		cur = buf; +		num = 0; +		list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { +			if (li->type != types[i]) +				continue; +			cur->Pid = cpu_to_le16(li->pid); +			cur->LengthLow = cpu_to_le32((u32)li->length); +			cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); +			cur->OffsetLow = cpu_to_le32((u32)li->offset); +			cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); +			if (++num == max_num) { +				stored_rc = cifs_lockv(xid, tcon, +						       cfile->fid.netfid, +						       (__u8)li->type, 0, num, +						       buf); +				if (stored_rc) +					rc = stored_rc; +				cur = buf; +				num = 0; +			} else +				cur++; +		} + +		if (num) { +			stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, +					       (__u8)types[i], 0, num, buf); +			if (stored_rc) +				rc = stored_rc; +		} +	} + +	kfree(buf); +	free_xid(xid); +	return rc; +} + +/* copied from fs/locks.c with a name change */ +#define cifs_for_each_lock(inode, lockp) \ +	for (lockp = &inode->i_flock; *lockp != NULL; \ +	     lockp = &(*lockp)->fl_next) + +struct lock_to_push { +	struct list_head llist; +	__u64 offset; +	__u64 length; +	__u32 pid; +	__u16 netfid; +	__u8 type; +}; + +static int +cifs_push_posix_locks(struct cifsFileInfo *cfile) +{ +	struct inode *inode = cfile->dentry->d_inode; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct file_lock *flock, **before; +	unsigned int count = 0, i = 0; +	int rc = 0, xid, type; +	struct list_head locks_to_send, *el; +	struct lock_to_push *lck, *tmp; +	__u64 length; + +	xid = get_xid(); + +	spin_lock(&inode->i_lock); +	cifs_for_each_lock(inode, before) { +		if ((*before)->fl_flags & FL_POSIX) +			count++; +	} +	spin_unlock(&inode->i_lock); + +	INIT_LIST_HEAD(&locks_to_send); + +	/* +	 * Allocating count locks is enough because no FL_POSIX locks can be +	 * added to the list while we are holding cinode->lock_sem that +	 * protects locking operations of this inode. +	 */ +	for (; i < count; i++) { +		lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); +		if (!lck) { +			rc = -ENOMEM; +			goto err_out; +		} +		list_add_tail(&lck->llist, &locks_to_send); +	} + +	el = locks_to_send.next; +	spin_lock(&inode->i_lock); +	cifs_for_each_lock(inode, before) { +		flock = *before; +		if ((flock->fl_flags & FL_POSIX) == 0) +			continue; +		if (el == &locks_to_send) { +			/* +			 * The list ended. We don't have enough allocated +			 * structures - something is really wrong. +			 */ +			cifs_dbg(VFS, "Can't push all brlocks!\n"); +			break; +		} +		length = 1 + flock->fl_end - flock->fl_start; +		if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK) +			type = CIFS_RDLCK; +		else +			type = CIFS_WRLCK; +		lck = list_entry(el, struct lock_to_push, llist); +		lck->pid = flock->fl_pid; +		lck->netfid = cfile->fid.netfid; +		lck->length = length; +		lck->type = type; +		lck->offset = flock->fl_start; +		el = el->next; +	} +	spin_unlock(&inode->i_lock); + +	list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { +		int stored_rc; + +		stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid, +					     lck->offset, lck->length, NULL, +					     lck->type, 0); +		if (stored_rc) +			rc = stored_rc; +		list_del(&lck->llist); +		kfree(lck); +	} + +out: +	free_xid(xid); +	return rc; +err_out: +	list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { +		list_del(&lck->llist); +		kfree(lck); +	} +	goto out;  } -ssize_t cifs_user_write(struct file *file, const char __user *write_data, -	size_t write_size, loff_t *poffset) +static int +cifs_push_locks(struct cifsFileInfo *cfile)  { -	struct inode *inode = file->f_path.dentry->d_inode; +	struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);  	int rc = 0; -	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; -	struct cifsInodeInfo *cifsi = CIFS_I(inode); -	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	/* we are going to update can_cache_brlcks here - need a write access */ +	down_write(&cinode->lock_sem); +	if (!cinode->can_cache_brlcks) { +		up_write(&cinode->lock_sem); +		return rc; +	} + +	if (cap_unix(tcon->ses) && +	    (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && +	    ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) +		rc = cifs_push_posix_locks(cfile); +	else +		rc = tcon->ses->server->ops->push_mand_locks(cfile); -	/* cFYI(1, " write %d bytes to offset %lld of %s", write_size, -	   *poffset, file->f_path.dentry->d_name.name); */ +	cinode->can_cache_brlcks = false; +	up_write(&cinode->lock_sem); +	return rc; +} -	if (file->private_data == NULL) -		return -EBADF; +static void +cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, +		bool *wait_flag, struct TCP_Server_Info *server) +{ +	if (flock->fl_flags & FL_POSIX) +		cifs_dbg(FYI, "Posix\n"); +	if (flock->fl_flags & FL_FLOCK) +		cifs_dbg(FYI, "Flock\n"); +	if (flock->fl_flags & FL_SLEEP) { +		cifs_dbg(FYI, "Blocking lock\n"); +		*wait_flag = true; +	} +	if (flock->fl_flags & FL_ACCESS) +		cifs_dbg(FYI, "Process suspended by mandatory locking - not implemented yet\n"); +	if (flock->fl_flags & FL_LEASE) +		cifs_dbg(FYI, "Lease on file - not implemented yet\n"); +	if (flock->fl_flags & +	    (~(FL_POSIX | FL_FLOCK | FL_SLEEP | +	       FL_ACCESS | FL_LEASE | FL_CLOSE))) +		cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags); + +	*type = server->vals->large_lock_type; +	if (flock->fl_type == F_WRLCK) { +		cifs_dbg(FYI, "F_WRLCK\n"); +		*type |= server->vals->exclusive_lock_type; +		*lock = 1; +	} else if (flock->fl_type == F_UNLCK) { +		cifs_dbg(FYI, "F_UNLCK\n"); +		*type |= server->vals->unlock_lock_type; +		*unlock = 1; +		/* Check if unlock includes more than one lock range */ +	} else if (flock->fl_type == F_RDLCK) { +		cifs_dbg(FYI, "F_RDLCK\n"); +		*type |= server->vals->shared_lock_type; +		*lock = 1; +	} else if (flock->fl_type == F_EXLCK) { +		cifs_dbg(FYI, "F_EXLCK\n"); +		*type |= server->vals->exclusive_lock_type; +		*lock = 1; +	} else if (flock->fl_type == F_SHLCK) { +		cifs_dbg(FYI, "F_SHLCK\n"); +		*type |= server->vals->shared_lock_type; +		*lock = 1; +	} else +		cifs_dbg(FYI, "Unknown type of lock\n"); +} -	open_file = file->private_data; -	pTcon = tlink_tcon(open_file->tlink); +static int +cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, +	   bool wait_flag, bool posix_lck, unsigned int xid) +{ +	int rc = 0; +	__u64 length = 1 + flock->fl_end - flock->fl_start; +	struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server; +	__u16 netfid = cfile->fid.netfid; -	rc = generic_write_checks(file, poffset, &write_size, 0); -	if (rc) +	if (posix_lck) { +		int posix_lock_type; + +		rc = cifs_posix_lock_test(file, flock); +		if (!rc) +			return rc; + +		if (type & server->vals->shared_lock_type) +			posix_lock_type = CIFS_RDLCK; +		else +			posix_lock_type = CIFS_WRLCK; +		rc = CIFSSMBPosixLock(xid, tcon, netfid, current->tgid, +				      flock->fl_start, length, flock, +				      posix_lock_type, wait_flag);  		return rc; +	} -	xid = GetXid(); +	rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock); +	if (!rc) +		return rc; -	long_op = cifs_write_timeout(cifsi, *poffset); -	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->invalidHandle) { -				/* we could deadlock if we called -				   filemap_fdatawait from here so tell -				   reopen_file not to flush data to server -				   now */ -				rc = cifs_reopen_file(open_file, false); -				if (rc != 0) -					break; -			} +	/* BB we could chain these into one lock request BB */ +	rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type, +				    1, 0, false); +	if (rc == 0) { +		rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, +					    type, 0, 1, false); +		flock->fl_type = F_UNLCK; +		if (rc != 0) +			cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", +				 rc); +		return 0; +	} -			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; +	if (type & server->vals->shared_lock_type) { +		flock->fl_type = F_WRLCK; +		return 0; +	} + +	type &= ~server->vals->exclusive_lock_type; + +	rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, +				    type | server->vals->shared_lock_type, +				    1, 0, false); +	if (rc == 0) { +		rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, +			type | server->vals->shared_lock_type, 0, 1, false); +		flock->fl_type = F_RDLCK; +		if (rc != 0) +			cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", +				 rc); +	} else +		flock->fl_type = F_WRLCK; + +	return 0; +} + +void +cifs_move_llist(struct list_head *source, struct list_head *dest) +{ +	struct list_head *li, *tmp; +	list_for_each_safe(li, tmp, source) +		list_move(li, dest); +} + +void +cifs_free_llist(struct list_head *llist) +{ +	struct cifsLockInfo *li, *tmp; +	list_for_each_entry_safe(li, tmp, llist, llist) { +		cifs_del_lock_waiters(li); +		list_del(&li->llist); +		kfree(li); +	} +} + +int +cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, +		  unsigned int xid) +{ +	int rc = 0, stored_rc; +	int types[] = {LOCKING_ANDX_LARGE_FILES, +		       LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES}; +	unsigned int i; +	unsigned int max_num, num, max_buf; +	LOCKING_ANDX_RANGE *buf, *cur; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct cifsLockInfo *li, *tmp; +	__u64 length = 1 + flock->fl_end - flock->fl_start; +	struct list_head tmp_llist; + +	INIT_LIST_HEAD(&tmp_llist); + +	/* +	 * Accessing maxBuf is racy with cifs_reconnect - need to store value +	 * and check it for zero before using. +	 */ +	max_buf = tcon->ses->server->maxBuf; +	if (!max_buf) +		return -EINVAL; + +	max_num = (max_buf - sizeof(struct smb_hdr)) / +						sizeof(LOCKING_ANDX_RANGE); +	buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	down_write(&cinode->lock_sem); +	for (i = 0; i < 2; i++) { +		cur = buf; +		num = 0; +		list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { +			if (flock->fl_start > li->offset || +			    (flock->fl_start + length) < +			    (li->offset + li->length)) +				continue; +			if (current->tgid != li->pid) +				continue; +			if (types[i] != li->type) +				continue; +			if (cinode->can_cache_brlcks) { +				/* +				 * We can cache brlock requests - simply remove +				 * a lock from the file's list. +				 */ +				list_del(&li->llist); +				cifs_del_lock_waiters(li); +				kfree(li); +				continue;  			} -		} else { -			cifs_update_eof(cifsi, *poffset, bytes_written); -			*poffset += bytes_written; +			cur->Pid = cpu_to_le16(li->pid); +			cur->LengthLow = cpu_to_le32((u32)li->length); +			cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); +			cur->OffsetLow = cpu_to_le32((u32)li->offset); +			cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); +			/* +			 * We need to save a lock here to let us add it again to +			 * the file's list if the unlock range request fails on +			 * the server. +			 */ +			list_move(&li->llist, &tmp_llist); +			if (++num == max_num) { +				stored_rc = cifs_lockv(xid, tcon, +						       cfile->fid.netfid, +						       li->type, num, 0, buf); +				if (stored_rc) { +					/* +					 * We failed on the unlock range +					 * request - add all locks from the tmp +					 * list to the head of the file's list. +					 */ +					cifs_move_llist(&tmp_llist, +							&cfile->llist->locks); +					rc = stored_rc; +				} else +					/* +					 * The unlock range request succeed - +					 * free the tmp list. +					 */ +					cifs_free_llist(&tmp_llist); +				cur = buf; +				num = 0; +			} else +				cur++; +		} +		if (num) { +			stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, +					       types[i], num, 0, buf); +			if (stored_rc) { +				cifs_move_llist(&tmp_llist, +						&cfile->llist->locks); +				rc = stored_rc; +			} else +				cifs_free_llist(&tmp_llist);  		} -		long_op = CIFS_STD_OP; /* subsequent writes fast - -				    15 seconds is plenty */  	} -	cifs_stats_bytes_written(pTcon, total_written); +	up_write(&cinode->lock_sem); +	kfree(buf); +	return rc; +} -/* 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 > inode->i_size) -			i_size_write(inode, *poffset); -		spin_unlock(&inode->i_lock); +static int +cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, +	   bool wait_flag, bool posix_lck, int lock, int unlock, +	   unsigned int xid) +{ +	int rc = 0; +	__u64 length = 1 + flock->fl_end - flock->fl_start; +	struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server; +	struct inode *inode = cfile->dentry->d_inode; + +	if (posix_lck) { +		int posix_lock_type; + +		rc = cifs_posix_lock_set(file, flock); +		if (!rc || rc < 0) +			return rc; + +		if (type & server->vals->shared_lock_type) +			posix_lock_type = CIFS_RDLCK; +		else +			posix_lock_type = CIFS_WRLCK; + +		if (unlock == 1) +			posix_lock_type = CIFS_UNLCK; + +		rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid, +				      current->tgid, flock->fl_start, length, +				      NULL, posix_lock_type, wait_flag); +		goto out;  	} -	mark_inode_dirty_sync(inode); -	FreeXid(xid); -	return total_written; +	if (lock) { +		struct cifsLockInfo *lock; + +		lock = cifs_lock_init(flock->fl_start, length, type); +		if (!lock) +			return -ENOMEM; + +		rc = cifs_lock_add_if(cfile, lock, wait_flag); +		if (rc < 0) { +			kfree(lock); +			return rc; +		} +		if (!rc) +			goto out; + +		/* +		 * Windows 7 server can delay breaking lease from read to None +		 * if we set a byte-range lock on a file - break it explicitly +		 * before sending the lock to the server to be sure the next +		 * read won't conflict with non-overlapted locks due to +		 * pagereading. +		 */ +		if (!CIFS_CACHE_WRITE(CIFS_I(inode)) && +					CIFS_CACHE_READ(CIFS_I(inode))) { +			cifs_zap_mapping(inode); +			cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n", +				 inode); +			CIFS_I(inode)->oplock = 0; +		} + +		rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, +					    type, 1, 0, wait_flag); +		if (rc) { +			kfree(lock); +			return rc; +		} + +		cifs_lock_add(cfile, lock); +	} else if (unlock) +		rc = server->ops->mand_unlock_range(cfile, flock, xid); + +out: +	if (flock->fl_flags & FL_POSIX) +		posix_lock_file_wait(file, flock); +	return rc;  } -static ssize_t cifs_write(struct cifsFileInfo *open_file, -			  const char *write_data, size_t write_size, -			  loff_t *poffset) +int cifs_lock(struct file *file, int cmd, struct file_lock *flock) +{ +	int rc, xid; +	int lock = 0, unlock = 0; +	bool wait_flag = false; +	bool posix_lck = false; +	struct cifs_sb_info *cifs_sb; +	struct cifs_tcon *tcon; +	struct cifsInodeInfo *cinode; +	struct cifsFileInfo *cfile; +	__u16 netfid; +	__u32 type; + +	rc = -EACCES; +	xid = get_xid(); + +	cifs_dbg(FYI, "Lock parm: 0x%x flockflags: 0x%x flocktype: 0x%x start: %lld end: %lld\n", +		 cmd, flock->fl_flags, flock->fl_type, +		 flock->fl_start, flock->fl_end); + +	cfile = (struct cifsFileInfo *)file->private_data; +	tcon = tlink_tcon(cfile->tlink); + +	cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, +			tcon->ses->server); + +	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	netfid = cfile->fid.netfid; +	cinode = CIFS_I(file_inode(file)); + +	if (cap_unix(tcon->ses) && +	    (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && +	    ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) +		posix_lck = true; +	/* +	 * BB add code here to normalize offset and length to account for +	 * negative length which we can not accept over the wire. +	 */ +	if (IS_GETLK(cmd)) { +		rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid); +		free_xid(xid); +		return rc; +	} + +	if (!lock && !unlock) { +		/* +		 * if no lock or unlock then nothing to do since we do not +		 * know what it is +		 */ +		free_xid(xid); +		return -EOPNOTSUPP; +	} + +	rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock, +			xid); +	free_xid(xid); +	return rc; +} + +/* + * update the file size (if needed) after a write. Should be called with + * the inode->i_lock held + */ +void +cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, +		      unsigned int bytes_written) +{ +	loff_t end_of_write = offset + bytes_written; + +	if (end_of_write > cifsi->server_eof) +		cifsi->server_eof = end_of_write; +} + +static ssize_t +cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, +	   size_t write_size, loff_t *offset)  {  	int rc = 0;  	unsigned int bytes_written = 0;  	unsigned int total_written;  	struct cifs_sb_info *cifs_sb; -	struct cifsTconInfo *pTcon; -	int xid, long_op; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	unsigned int xid;  	struct dentry *dentry = open_file->dentry;  	struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode); +	struct cifs_io_parms io_parms;  	cifs_sb = CIFS_SB(dentry->d_sb); -	cFYI(1, "write %zd bytes to offset %lld of %s", write_size, -	   *poffset, dentry->d_name.name); +	cifs_dbg(FYI, "write %zd bytes to offset %lld of %s\n", +		 write_size, *offset, dentry->d_name.name); + +	tcon = tlink_tcon(open_file->tlink); +	server = tcon->ses->server; -	pTcon = tlink_tcon(open_file->tlink); +	if (!server->ops->sync_write) +		return -ENOSYS; -	xid = GetXid(); +	xid = get_xid(); -	long_op = cifs_write_timeout(cifsi, *poffset);  	for (total_written = 0; write_size > total_written;  	     total_written += bytes_written) {  		rc = -EAGAIN;  		while (rc == -EAGAIN) { +			struct kvec iov[2]; +			unsigned int len; +  			if (open_file->invalidHandle) {  				/* we could deadlock if we called  				   filemap_fdatawait from here so tell @@ -1054,61 +1669,47 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file,  				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); + +			len = min((size_t)cifs_sb->wsize, +				  write_size - total_written); +			/* iov[0] is reserved for smb header */ +			iov[1].iov_base = (char *)write_data + total_written; +			iov[1].iov_len = len; +			io_parms.pid = pid; +			io_parms.tcon = tcon; +			io_parms.offset = *offset; +			io_parms.length = len; +			rc = server->ops->sync_write(xid, open_file, &io_parms, +						     &bytes_written, iov, 1);  		}  		if (rc || (bytes_written == 0)) {  			if (total_written)  				break;  			else { -				FreeXid(xid); +				free_xid(xid);  				return rc;  			}  		} else { -			cifs_update_eof(cifsi, *poffset, bytes_written); -			*poffset += bytes_written; +			spin_lock(&dentry->d_inode->i_lock); +			cifs_update_eof(cifsi, *offset, bytes_written); +			spin_unlock(&dentry->d_inode->i_lock); +			*offset += bytes_written;  		} -		long_op = CIFS_STD_OP; /* subsequent writes fast - -				    15 seconds is plenty */  	} -	cifs_stats_bytes_written(pTcon, total_written); +	cifs_stats_bytes_written(tcon, total_written);  	if (total_written > 0) {  		spin_lock(&dentry->d_inode->i_lock); -		if (*poffset > dentry->d_inode->i_size) -			i_size_write(dentry->d_inode, *poffset); +		if (*offset > dentry->d_inode->i_size) +			i_size_write(dentry->d_inode, *offset);  		spin_unlock(&dentry->d_inode->i_lock);  	}  	mark_inode_dirty_sync(dentry->d_inode); -	FreeXid(xid); +	free_xid(xid);  	return total_written;  } -#ifdef CONFIG_CIFS_EXPERIMENTAL  struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,  					bool fsuid_only)  { @@ -1124,13 +1725,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,  	   are always at the end of the list but since the first entry might  	   have a close pending, we go through the whole list */  	list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { -		if (fsuid_only && open_file->uid != current_fsuid()) +		if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))  			continue;  		if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {  			if (!open_file->invalidHandle) {  				/* found a good file */  				/* lock it so it will not be closed on us */ -				cifsFileInfo_get(open_file); +				cifsFileInfo_get_locked(open_file);  				spin_unlock(&cifs_file_list_lock);  				return open_file;  			} /* else might as well continue, and look for @@ -1142,22 +1743,22 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,  	spin_unlock(&cifs_file_list_lock);  	return NULL;  } -#endif  struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,  					bool fsuid_only)  { -	struct cifsFileInfo *open_file; +	struct cifsFileInfo *open_file, *inv_file = NULL;  	struct cifs_sb_info *cifs_sb;  	bool any_available = false;  	int rc; +	unsigned int refind = 0;  	/* Having a null inode here (because mapping->host was set to zero by  	the VFS or MM) should not happen but we had reports of on oops (due to  	it being zero) during stress testcases so we need to check for it */  	if (cifs_inode == NULL) { -		cERROR(1, "Null inode passed to cifs_writeable_file"); +		cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n");  		dump_stack();  		return NULL;  	} @@ -1170,40 +1771,25 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,  	spin_lock(&cifs_file_list_lock);  refind_writable: +	if (refind > MAX_REOPEN_ATT) { +		spin_unlock(&cifs_file_list_lock); +		return NULL; +	}  	list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {  		if (!any_available && open_file->pid != current->tgid)  			continue; -		if (fsuid_only && open_file->uid != current_fsuid()) +		if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))  			continue;  		if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { -			cifsFileInfo_get(open_file); -  			if (!open_file->invalidHandle) {  				/* found a good writable file */ +				cifsFileInfo_get_locked(open_file);  				spin_unlock(&cifs_file_list_lock);  				return open_file; +			} else { +				if (!inv_file) +					inv_file = open_file;  			} - -			spin_unlock(&cifs_file_list_lock); - -			/* Had to unlock since following call can block */ -			rc = cifs_reopen_file(open_file, false); -			if (!rc) -				return open_file; - -			/* if it fails, try another handle if possible */ -			cFYI(1, "wp failed on reopen file"); -			cifsFileInfo_put(open_file); - -			spin_lock(&cifs_file_list_lock); - -			/* 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 */ @@ -1211,7 +1797,30 @@ refind_writable:  		any_available = true;  		goto refind_writable;  	} + +	if (inv_file) { +		any_available = false; +		cifsFileInfo_get_locked(inv_file); +	} +  	spin_unlock(&cifs_file_list_lock); + +	if (inv_file) { +		rc = cifs_reopen_file(inv_file, false); +		if (!rc) +			return inv_file; +		else { +			spin_lock(&cifs_file_list_lock); +			list_move_tail(&inv_file->flist, +					&cifs_inode->openFileList); +			spin_unlock(&cifs_file_list_lock); +			cifsFileInfo_put(inv_file); +			spin_lock(&cifs_file_list_lock); +			++refind; +			goto refind_writable; +		} +	} +  	return NULL;  } @@ -1222,7 +1831,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)  	char *write_data;  	int rc = -EFAULT;  	int bytes_written = 0; -	struct cifs_sb_info *cifs_sb;  	struct inode *inode;  	struct cifsFileInfo *open_file; @@ -1230,7 +1838,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)  		return -EFAULT;  	inode = page->mapping->host; -	cifs_sb = CIFS_SB(inode->i_sb);  	offset += (loff_t)from;  	write_data = kmap(page); @@ -1253,8 +1860,8 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)  	open_file = find_writable_file(CIFS_I(mapping->host), false);  	if (open_file) { -		bytes_written = cifs_write(open_file, write_data, -					   to - from, &offset); +		bytes_written = cifs_write(open_file, open_file->pid, +					   write_data, to - from, &offset);  		cifsFileInfo_put(open_file);  		/* Does mm or vfs already set times? */  		inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); @@ -1263,7 +1870,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)  		else if (bytes_written < 0)  			rc = bytes_written;  	} else { -		cFYI(1, "No writeable filehandles for inode"); +		cifs_dbg(FYI, "No writeable filehandles for inode\n");  		rc = -EIO;  	} @@ -1274,64 +1881,21 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)  static int cifs_writepages(struct address_space *mapping,  			   struct writeback_control *wbc)  { -	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 cifsTconInfo *tcon; -	struct cifsInodeInfo *cifsi = CIFS_I(mapping->host); +	struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb); +	bool done = false, scanned = false, range_whole = false; +	pgoff_t end, index; +	struct cifs_writedata *wdata; +	struct TCP_Server_Info *server;  	struct page *page; -	struct pagevec pvec;  	int rc = 0; -	int scanned = 0; -	int xid, long_op; - -	cifs_sb = CIFS_SB(mapping->host->i_sb);  	/* -	 * If wsize is smaller that the page cache size, default to writing +	 * If wsize is smaller than the page cache size, default to writing  	 * one page at a time via cifs_writepage  	 */  	if (cifs_sb->wsize < PAGE_CACHE_SIZE)  		return generic_writepages(mapping, wbc); -	iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL); -	if (iov == NULL) -		return generic_writepages(mapping, wbc); - -	/* -	 * if there's no open file, then this is likely to fail too, -	 * but it'll at least handle the return. Maybe it should be -	 * a BUG() instead? -	 */ -	open_file = find_writable_file(CIFS_I(mapping->host), false); -	if (!open_file) { -		kfree(iov); -		return generic_writepages(mapping, wbc); -	} - -	tcon = tlink_tcon(open_file->tlink); -	if (!experimEnabled && tcon->ses->server->secMode & -			(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { -		cifsFileInfo_put(open_file); -		kfree(iov); -		return generic_writepages(mapping, wbc); -	} -	cifsFileInfo_put(open_file); - -	xid = GetXid(); - -	pagevec_init(&pvec, 0);  	if (wbc->range_cyclic) {  		index = mapping->writeback_index; /* Start from prev offset */  		end = -1; @@ -1339,24 +1903,50 @@ static int cifs_writepages(struct address_space *mapping,  		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; +			range_whole = true; +		scanned = true;  	}  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; +	while (!done && index <= end) { +		unsigned int i, nr_pages, found_pages; +		pgoff_t next = 0, tofind; +		struct page **pages; -		first = -1; -		next = 0; -		n_iov = 0; -		bytes_to_write = 0; +		tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1, +				end - index) + 1; -		for (i = 0; i < nr_pages; i++) { -			page = pvec.pages[i]; +		wdata = cifs_writedata_alloc((unsigned int)tofind, +					     cifs_writev_complete); +		if (!wdata) { +			rc = -ENOMEM; +			break; +		} + +		/* +		 * find_get_pages_tag seems to return a max of 256 on each +		 * iteration, so we must call it several times in order to +		 * fill the array or the wsize is effectively limited to +		 * 256 * PAGE_CACHE_SIZE. +		 */ +		found_pages = 0; +		pages = wdata->pages; +		do { +			nr_pages = find_get_pages_tag(mapping, &index, +							PAGECACHE_TAG_DIRTY, +							tofind, pages); +			found_pages += nr_pages; +			tofind -= nr_pages; +			pages += nr_pages; +		} while (nr_pages && tofind && index <= end); + +		if (found_pages == 0) { +			kref_put(&wdata->refcount, cifs_writedata_release); +			break; +		} + +		nr_pages = 0; +		for (i = 0; i < found_pages; i++) { +			page = wdata->pages[i];  			/*  			 * At this point we hold neither mapping->tree_lock nor  			 * lock on the page itself: the page may be truncated or @@ -1365,7 +1955,7 @@ retry:  			 * mapping  			 */ -			if (first < 0) +			if (nr_pages == 0)  				lock_page(page);  			else if (!trylock_page(page))  				break; @@ -1376,7 +1966,7 @@ retry:  			}  			if (!wbc->range_cyclic && page->index > end) { -				done = 1; +				done = true;  				unlock_page(page);  				break;  			} @@ -1402,109 +1992,114 @@ retry:  			 */  			set_page_writeback(page); -			if (page_offset(page) >= mapping->host->i_size) { -				done = 1; +			if (page_offset(page) >= i_size_read(mapping->host)) { +				done = true;  				unlock_page(page);  				end_page_writeback(page);  				break;  			} -			/* -			 * BB can we get rid of this?  pages are held by pvec -			 */ -			page_cache_get(page); +			wdata->pages[i] = page; +			next = page->index + 1; +			++nr_pages; +		} -			len = min(mapping->host->i_size - page_offset(page), -				  (loff_t)PAGE_CACHE_SIZE); +		/* reset index to refind any pages skipped */ +		if (nr_pages == 0) +			index = wdata->pages[0]->index + 1; -			/* 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; +		/* put any pages we aren't going to use */ +		for (i = nr_pages; i < found_pages; i++) { +			page_cache_release(wdata->pages[i]); +			wdata->pages[i] = NULL; +		} -			if (first < 0) { -				first = i; -				offset = page_offset(page); -			} -			next = page->index + 1; -			if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize) -				break; +		/* nothing to write? */ +		if (nr_pages == 0) { +			kref_put(&wdata->refcount, cifs_writedata_release); +			continue;  		} -		if (n_iov) { -			open_file = find_writable_file(CIFS_I(mapping->host), -							false); -			if (!open_file) { -				cERROR(1, "No writable handles for inode"); + +		wdata->sync_mode = wbc->sync_mode; +		wdata->nr_pages = nr_pages; +		wdata->offset = page_offset(wdata->pages[0]); +		wdata->pagesz = PAGE_CACHE_SIZE; +		wdata->tailsz = +			min(i_size_read(mapping->host) - +			    page_offset(wdata->pages[nr_pages - 1]), +			    (loff_t)PAGE_CACHE_SIZE); +		wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) + +					wdata->tailsz; + +		do { +			if (wdata->cfile != NULL) +				cifsFileInfo_put(wdata->cfile); +			wdata->cfile = find_writable_file(CIFS_I(mapping->host), +							  false); +			if (!wdata->cfile) { +				cifs_dbg(VFS, "No writable handles for inode\n");  				rc = -EBADF; -			} else { -				long_op = cifs_write_timeout(cifsi, offset); -				rc = CIFSSMBWrite2(xid, tcon, open_file->netfid, -						   bytes_to_write, offset, -						   &bytes_written, iov, n_iov, -						   long_op); -				cifsFileInfo_put(open_file); -				cifs_update_eof(cifsi, offset, bytes_written); +				break;  			} - -			if (rc || bytes_written < bytes_to_write) { -				cERROR(1, "Write2 ret %d, wrote %d", -					  rc, bytes_written); -				mapping_set_error(mapping, rc); -			} else { -				cifs_stats_bytes_written(tcon, bytes_written); +			wdata->pid = wdata->cfile->pid; +			server = tlink_tcon(wdata->cfile->tlink)->ses->server; +			rc = server->ops->async_writev(wdata, +							cifs_writedata_release); +		} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN); + +		for (i = 0; i < nr_pages; ++i) +			unlock_page(wdata->pages[i]); + +		/* send failure -- clean up the mess */ +		if (rc != 0) { +			for (i = 0; i < nr_pages; ++i) { +				if (rc == -EAGAIN) +					redirty_page_for_writepage(wbc, +							   wdata->pages[i]); +				else +					SetPageError(wdata->pages[i]); +				end_page_writeback(wdata->pages[i]); +				page_cache_release(wdata->pages[i]);  			} +			if (rc != -EAGAIN) +				mapping_set_error(mapping, rc); +		} +		kref_put(&wdata->refcount, cifs_writedata_release); -			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; +		wbc->nr_to_write -= nr_pages; +		if (wbc->nr_to_write <= 0) +			done = true; -		pagevec_release(&pvec); +		index = next;  	} +  	if (!scanned && !done) {  		/*  		 * We hit the last page and there is more work to be done: wrap  		 * back to the start of the file  		 */ -		scanned = 1; +		scanned = true;  		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) +static int +cifs_writepage_locked(struct page *page, struct writeback_control *wbc)  { -	int rc = -EFAULT; -	int xid; +	int rc; +	unsigned int xid; -	xid = GetXid(); +	xid = get_xid();  /* BB add check for wbc flags */  	page_cache_get(page);  	if (!PageUptodate(page)) -		cFYI(1, "ppw - page not up to date"); +		cifs_dbg(FYI, "ppw - page not up to date\n");  	/*  	 * Set the "writeback" flag, and clear "dirty" in the radix tree. @@ -1517,12 +2112,26 @@ static int cifs_writepage(struct page *page, struct writeback_control *wbc)  	 * to fail to update with the state of the page correctly.  	 */  	set_page_writeback(page); +retry_write:  	rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE); -	SetPageUptodate(page); /* BB add check for error and Clearuptodate? */ -	unlock_page(page); +	if (rc == -EAGAIN && wbc->sync_mode == WB_SYNC_ALL) +		goto retry_write; +	else if (rc == -EAGAIN) +		redirty_page_for_writepage(wbc, page); +	else if (rc != 0) +		SetPageError(page); +	else +		SetPageUptodate(page);  	end_page_writeback(page);  	page_cache_release(page); -	FreeXid(xid); +	free_xid(xid); +	return rc; +} + +static int cifs_writepage(struct page *page, struct writeback_control *wbc) +{ +	int rc = cifs_writepage_locked(page, wbc); +	unlock_page(page);  	return rc;  } @@ -1532,8 +2141,16 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,  {  	int rc;  	struct inode *inode = mapping->host; +	struct cifsFileInfo *cfile = file->private_data; +	struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +	__u32 pid; + +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) +		pid = cfile->pid; +	else +		pid = current->tgid; -	cFYI(1, "write_end for page %p from pos %lld with %d bytes", +	cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n",  		 page, pos, copied);  	if (PageChecked(page)) { @@ -1546,21 +2163,20 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,  	if (!PageUptodate(page)) {  		char *page_data;  		unsigned offset = pos & (PAGE_CACHE_SIZE - 1); -		int xid; +		unsigned int xid; -		xid = GetXid(); +		xid = get_xid();  		/* this is probably better than directly calling  		   partialpage_write since in this function the file handle is  		   known which we might as well	leverage */  		/* BB check if anything else missing out of ppw  		   such as updating last write time */  		page_data = kmap(page); -		rc = cifs_write(file->private_data, page_data + offset, -				copied, &pos); +		rc = cifs_write(cfile, pid, page_data + offset, copied, &pos);  		/* if (rc < 0) should we set writebehind rc? */  		kunmap(page); -		FreeXid(xid); +		free_xid(xid);  	} else {  		rc = copied;  		pos += copied; @@ -1580,59 +2196,82 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,  	return rc;  } -int cifs_fsync(struct file *file, int datasync) +int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, +		      int datasync)  { -	int xid; +	unsigned int xid;  	int rc = 0; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server;  	struct cifsFileInfo *smbfile = file->private_data; -	struct inode *inode = file->f_path.dentry->d_inode; +	struct inode *inode = file_inode(file); +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); -	xid = GetXid(); +	rc = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (rc) +		return rc; +	mutex_lock(&inode->i_mutex); -	cFYI(1, "Sync file - name: %s datasync: 0x%x", -		file->f_path.dentry->d_name.name, datasync); +	xid = get_xid(); -	rc = filemap_write_and_wait(inode->i_mapping); -	if (rc == 0) { -		struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	cifs_dbg(FYI, "Sync file - name: %s datasync: 0x%x\n", +		 file->f_path.dentry->d_name.name, datasync); -		tcon = tlink_tcon(smbfile->tlink); -		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) -			rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); +	if (!CIFS_CACHE_READ(CIFS_I(inode))) { +		rc = cifs_zap_mapping(inode); +		if (rc) { +			cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc); +			rc = 0; /* don't care about it in fsync */ +		}  	} -	FreeXid(xid); +	tcon = tlink_tcon(smbfile->tlink); +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { +		server = tcon->ses->server; +		if (server->ops->flush) +			rc = server->ops->flush(xid, tcon, &smbfile->fid); +		else +			rc = -ENOSYS; +	} + +	free_xid(xid); +	mutex_unlock(&inode->i_mutex);  	return rc;  } -/* static void cifs_sync_page(struct page *page) +int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  { -	struct address_space *mapping; -	struct inode *inode; -	unsigned long index = page->index; -	unsigned int rpages = 0; +	unsigned int xid;  	int rc = 0; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	struct cifsFileInfo *smbfile = file->private_data; +	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	struct inode *inode = file->f_mapping->host; -	cFYI(1, "sync page %p", page); -	mapping = page->mapping; -	if (!mapping) -		return 0; -	inode = mapping->host; -	if (!inode) -		return; */ +	rc = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (rc) +		return rc; +	mutex_lock(&inode->i_mutex); -/*	fill in rpages then -	result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ +	xid = get_xid(); -/*	cFYI(1, "rpages is %d for sync page of Index %ld", rpages, index); +	cifs_dbg(FYI, "Sync file - name: %s datasync: 0x%x\n", +		 file->f_path.dentry->d_name.name, datasync); -#if 0 -	if (rc < 0) -		return rc; -	return 0; -#endif -} */ +	tcon = tlink_tcon(smbfile->tlink); +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { +		server = tcon->ses->server; +		if (server->ops->flush) +			rc = server->ops->flush(xid, tcon, &smbfile->fid); +		else +			rc = -ENOSYS; +	} + +	free_xid(xid); +	mutex_unlock(&inode->i_mutex); +	return rc; +}  /*   * As file closes, flush all cached write data for this inode checking @@ -1640,137 +2279,766 @@ int cifs_fsync(struct file *file, int datasync)   */  int cifs_flush(struct file *file, fl_owner_t id)  { -	struct inode *inode = file->f_path.dentry->d_inode; +	struct inode *inode = file_inode(file);  	int rc = 0;  	if (file->f_mode & FMODE_WRITE)  		rc = filemap_write_and_wait(inode->i_mapping); -	cFYI(1, "Flush inode %p file %p rc %d", inode, file, rc); +	cifs_dbg(FYI, "Flush inode %p file %p rc %d\n", inode, file, rc);  	return rc;  } -ssize_t cifs_user_read(struct file *file, char __user *read_data, -	size_t read_size, loff_t *poffset) +static int +cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)  { -	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; +	int rc = 0; +	unsigned long i; + +	for (i = 0; i < num_pages; i++) { +		pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); +		if (!pages[i]) { +			/* +			 * save number of pages we have already allocated and +			 * return with ENOMEM error +			 */ +			num_pages = i; +			rc = -ENOMEM; +			break; +		} +	} + +	if (rc) { +		for (i = 0; i < num_pages; i++) +			put_page(pages[i]); +	} +	return rc; +} + +static inline +size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) +{ +	size_t num_pages; +	size_t clen; + +	clen = min_t(const size_t, len, wsize); +	num_pages = DIV_ROUND_UP(clen, PAGE_SIZE); + +	if (cur_len) +		*cur_len = clen; + +	return num_pages; +} + +static void +cifs_uncached_writedata_release(struct kref *refcount) +{ +	int i; +	struct cifs_writedata *wdata = container_of(refcount, +					struct cifs_writedata, refcount); + +	for (i = 0; i < wdata->nr_pages; i++) +		put_page(wdata->pages[i]); +	cifs_writedata_release(refcount); +} + +static void +cifs_uncached_writev_complete(struct work_struct *work) +{ +	struct cifs_writedata *wdata = container_of(work, +					struct cifs_writedata, work); +	struct inode *inode = wdata->cfile->dentry->d_inode; +	struct cifsInodeInfo *cifsi = CIFS_I(inode); + +	spin_lock(&inode->i_lock); +	cifs_update_eof(cifsi, wdata->offset, wdata->bytes); +	if (cifsi->server_eof > inode->i_size) +		i_size_write(inode, cifsi->server_eof); +	spin_unlock(&inode->i_lock); + +	complete(&wdata->done); + +	kref_put(&wdata->refcount, cifs_uncached_writedata_release); +} + +/* attempt to send write to server, retry on any -EAGAIN errors */ +static int +cifs_uncached_retry_writev(struct cifs_writedata *wdata) +{ +	int rc; +	struct TCP_Server_Info *server; + +	server = tlink_tcon(wdata->cfile->tlink)->ses->server; + +	do { +		if (wdata->cfile->invalidHandle) { +			rc = cifs_reopen_file(wdata->cfile, false); +			if (rc != 0) +				continue; +		} +		rc = server->ops->async_writev(wdata, +					       cifs_uncached_writedata_release); +	} while (rc == -EAGAIN); + +	return rc; +} + +static ssize_t +cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset) +{ +	unsigned long nr_pages, i; +	size_t bytes, copied, len, cur_len; +	ssize_t total_written = 0; +	loff_t offset;  	struct cifsFileInfo *open_file; -	char *smb_read_data; -	char __user *current_offset; -	struct smb_com_read_rsp *pSMBr; +	struct cifs_tcon *tcon; +	struct cifs_sb_info *cifs_sb; +	struct cifs_writedata *wdata, *tmp; +	struct list_head wdata_list; +	int rc; +	pid_t pid; + +	len = iov_iter_count(from); +	rc = generic_write_checks(file, poffset, &len, 0); +	if (rc) +		return rc; -	xid = GetXid(); +	if (!len) +		return 0; + +	iov_iter_truncate(from, len); + +	INIT_LIST_HEAD(&wdata_list);  	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	open_file = file->private_data; +	tcon = tlink_tcon(open_file->tlink); -	if (file->private_data == NULL) { -		rc = -EBADF; -		FreeXid(xid); -		return rc; +	if (!tcon->ses->server->ops->async_writev) +		return -ENOSYS; + +	offset = *poffset; + +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) +		pid = open_file->pid; +	else +		pid = current->tgid; + +	do { +		size_t save_len; + +		nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); +		wdata = cifs_writedata_alloc(nr_pages, +					     cifs_uncached_writev_complete); +		if (!wdata) { +			rc = -ENOMEM; +			break; +		} + +		rc = cifs_write_allocate_pages(wdata->pages, nr_pages); +		if (rc) { +			kfree(wdata); +			break; +		} + +		save_len = cur_len; +		for (i = 0; i < nr_pages; i++) { +			bytes = min_t(size_t, cur_len, PAGE_SIZE); +			copied = copy_page_from_iter(wdata->pages[i], 0, bytes, +						     from); +			cur_len -= copied; +			/* +			 * If we didn't copy as much as we expected, then that +			 * may mean we trod into an unmapped area. Stop copying +			 * at that point. On the next pass through the big +			 * loop, we'll likely end up getting a zero-length +			 * write and bailing out of it. +			 */ +			if (copied < bytes) +				break; +		} +		cur_len = save_len - cur_len; + +		/* +		 * If we have no data to send, then that probably means that +		 * the copy above failed altogether. That's most likely because +		 * the address in the iovec was bogus. Set the rc to -EFAULT, +		 * free anything we allocated and bail out. +		 */ +		if (!cur_len) { +			for (i = 0; i < nr_pages; i++) +				put_page(wdata->pages[i]); +			kfree(wdata); +			rc = -EFAULT; +			break; +		} + +		/* +		 * i + 1 now represents the number of pages we actually used in +		 * the copy phase above. Bring nr_pages down to that, and free +		 * any pages that we didn't use. +		 */ +		for ( ; nr_pages > i + 1; nr_pages--) +			put_page(wdata->pages[nr_pages - 1]); + +		wdata->sync_mode = WB_SYNC_ALL; +		wdata->nr_pages = nr_pages; +		wdata->offset = (__u64)offset; +		wdata->cfile = cifsFileInfo_get(open_file); +		wdata->pid = pid; +		wdata->bytes = cur_len; +		wdata->pagesz = PAGE_SIZE; +		wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); +		rc = cifs_uncached_retry_writev(wdata); +		if (rc) { +			kref_put(&wdata->refcount, +				 cifs_uncached_writedata_release); +			break; +		} + +		list_add_tail(&wdata->list, &wdata_list); +		offset += cur_len; +		len -= cur_len; +	} while (len > 0); + +	/* +	 * If at least one write was successfully sent, then discard any rc +	 * value from the later writes. If the other write succeeds, then +	 * we'll end up returning whatever was written. If it fails, then +	 * we'll get a new rc value from that. +	 */ +	if (!list_empty(&wdata_list)) +		rc = 0; + +	/* +	 * Wait for and collect replies for any successful sends in order of +	 * increasing offset. Once an error is hit or we get a fatal signal +	 * while waiting, then return without waiting for any more replies. +	 */ +restart_loop: +	list_for_each_entry_safe(wdata, tmp, &wdata_list, list) { +		if (!rc) { +			/* FIXME: freezable too? */ +			rc = wait_for_completion_killable(&wdata->done); +			if (rc) +				rc = -EINTR; +			else if (wdata->result) +				rc = wdata->result; +			else +				total_written += wdata->bytes; + +			/* resend call if it's a retryable error */ +			if (rc == -EAGAIN) { +				rc = cifs_uncached_retry_writev(wdata); +				goto restart_loop; +			} +		} +		list_del_init(&wdata->list); +		kref_put(&wdata->refcount, cifs_uncached_writedata_release); +	} + +	if (total_written > 0) +		*poffset += total_written; + +	cifs_stats_bytes_written(tcon, total_written); +	return total_written ? total_written : (ssize_t)rc; +} + +ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) +{ +	ssize_t written; +	struct inode *inode; +	loff_t pos = iocb->ki_pos; + +	inode = file_inode(iocb->ki_filp); + +	/* +	 * BB - optimize the way when signing is disabled. We can drop this +	 * extra memory-to-memory copying and use iovec buffers for constructing +	 * write request. +	 */ + +	written = cifs_iovec_write(iocb->ki_filp, from, &pos); +	if (written > 0) { +		set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); +		iocb->ki_pos = pos; +	} + +	return written; +} + +static ssize_t +cifs_writev(struct kiocb *iocb, struct iov_iter *from) +{ +	struct file *file = iocb->ki_filp; +	struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; +	struct inode *inode = file->f_mapping->host; +	struct cifsInodeInfo *cinode = CIFS_I(inode); +	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; +	ssize_t rc = -EACCES; +	loff_t lock_pos = iocb->ki_pos; + +	/* +	 * We need to hold the sem to be sure nobody modifies lock list +	 * with a brlock that prevents writing. +	 */ +	down_read(&cinode->lock_sem); +	mutex_lock(&inode->i_mutex); +	if (file->f_flags & O_APPEND) +		lock_pos = i_size_read(inode); +	if (!cifs_find_lock_conflict(cfile, lock_pos, iov_iter_count(from), +				     server->vals->exclusive_lock_type, NULL, +				     CIFS_WRITE_OP)) { +		rc = __generic_file_write_iter(iocb, from); +		mutex_unlock(&inode->i_mutex); + +		if (rc > 0) { +			ssize_t err; + +			err = generic_write_sync(file, iocb->ki_pos - rc, rc); +			if (err < 0) +				rc = err; +		} +	} else { +		mutex_unlock(&inode->i_mutex); +	} +	up_read(&cinode->lock_sem); +	return rc; +} + +ssize_t +cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) +{ +	struct inode *inode = file_inode(iocb->ki_filp); +	struct cifsInodeInfo *cinode = CIFS_I(inode); +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct cifsFileInfo *cfile = (struct cifsFileInfo *) +						iocb->ki_filp->private_data; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	ssize_t written; + +	written = cifs_get_writer(cinode); +	if (written) +		return written; + +	if (CIFS_CACHE_WRITE(cinode)) { +		if (cap_unix(tcon->ses) && +		(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) +		  && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) { +			written = generic_file_write_iter(iocb, from); +			goto out; +		} +		written = cifs_writev(iocb, from); +		goto out; +	} +	/* +	 * For non-oplocked files in strict cache mode we need to write the data +	 * to the server exactly from the pos to pos+len-1 rather than flush all +	 * affected pages because it may cause a error with mandatory locks on +	 * these pages but not on the region from pos to ppos+len-1. +	 */ +	written = cifs_user_writev(iocb, from); +	if (written > 0 && CIFS_CACHE_READ(cinode)) { +		/* +		 * Windows 7 server can delay breaking level2 oplock if a write +		 * request comes - break it on the client to prevent reading +		 * an old data. +		 */ +		cifs_zap_mapping(inode); +		cifs_dbg(FYI, "Set no oplock for inode=%p after a write operation\n", +			 inode); +		cinode->oplock = 0; +	} +out: +	cifs_put_writer(cinode); +	return written; +} + +static struct cifs_readdata * +cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete) +{ +	struct cifs_readdata *rdata; + +	rdata = kzalloc(sizeof(*rdata) + (sizeof(struct page *) * nr_pages), +			GFP_KERNEL); +	if (rdata != NULL) { +		kref_init(&rdata->refcount); +		INIT_LIST_HEAD(&rdata->list); +		init_completion(&rdata->done); +		INIT_WORK(&rdata->work, complete); +	} + +	return rdata; +} + +void +cifs_readdata_release(struct kref *refcount) +{ +	struct cifs_readdata *rdata = container_of(refcount, +					struct cifs_readdata, refcount); + +	if (rdata->cfile) +		cifsFileInfo_put(rdata->cfile); + +	kfree(rdata); +} + +static int +cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages) +{ +	int rc = 0; +	struct page *page; +	unsigned int i; + +	for (i = 0; i < nr_pages; i++) { +		page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); +		if (!page) { +			rc = -ENOMEM; +			break; +		} +		rdata->pages[i] = page;  	} + +	if (rc) { +		for (i = 0; i < nr_pages; i++) { +			put_page(rdata->pages[i]); +			rdata->pages[i] = NULL; +		} +	} +	return rc; +} + +static void +cifs_uncached_readdata_release(struct kref *refcount) +{ +	struct cifs_readdata *rdata = container_of(refcount, +					struct cifs_readdata, refcount); +	unsigned int i; + +	for (i = 0; i < rdata->nr_pages; i++) { +		put_page(rdata->pages[i]); +		rdata->pages[i] = NULL; +	} +	cifs_readdata_release(refcount); +} + +static int +cifs_retry_async_readv(struct cifs_readdata *rdata) +{ +	int rc; +	struct TCP_Server_Info *server; + +	server = tlink_tcon(rdata->cfile->tlink)->ses->server; + +	do { +		if (rdata->cfile->invalidHandle) { +			rc = cifs_reopen_file(rdata->cfile, true); +			if (rc != 0) +				continue; +		} +		rc = server->ops->async_readv(rdata); +	} while (rc == -EAGAIN); + +	return rc; +} + +/** + * cifs_readdata_to_iov - copy data from pages in response to an iovec + * @rdata:	the readdata response with list of pages holding data + * @iter:	destination for our data + * + * This function copies data from a list of pages in a readdata response into + * an array of iovecs. It will first calculate where the data should go + * based on the info in the readdata and then copy the data into that spot. + */ +static int +cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter) +{ +	size_t remaining = rdata->bytes; +	unsigned int i; + +	for (i = 0; i < rdata->nr_pages; i++) { +		struct page *page = rdata->pages[i]; +		size_t copy = min_t(size_t, remaining, PAGE_SIZE); +		size_t written = copy_page_to_iter(page, 0, copy, iter); +		remaining -= written; +		if (written < copy && iov_iter_count(iter) > 0) +			break; +	} +	return remaining ? -EFAULT : 0; +} + +static void +cifs_uncached_readv_complete(struct work_struct *work) +{ +	struct cifs_readdata *rdata = container_of(work, +						struct cifs_readdata, work); + +	complete(&rdata->done); +	kref_put(&rdata->refcount, cifs_uncached_readdata_release); +} + +static int +cifs_uncached_read_into_pages(struct TCP_Server_Info *server, +			struct cifs_readdata *rdata, unsigned int len) +{ +	int total_read = 0, result = 0; +	unsigned int i; +	unsigned int nr_pages = rdata->nr_pages; +	struct kvec iov; + +	rdata->tailsz = PAGE_SIZE; +	for (i = 0; i < nr_pages; i++) { +		struct page *page = rdata->pages[i]; + +		if (len >= PAGE_SIZE) { +			/* enough data to fill the page */ +			iov.iov_base = kmap(page); +			iov.iov_len = PAGE_SIZE; +			cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n", +				 i, iov.iov_base, iov.iov_len); +			len -= PAGE_SIZE; +		} else if (len > 0) { +			/* enough for partial page, fill and zero the rest */ +			iov.iov_base = kmap(page); +			iov.iov_len = len; +			cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n", +				 i, iov.iov_base, iov.iov_len); +			memset(iov.iov_base + len, '\0', PAGE_SIZE - len); +			rdata->tailsz = len; +			len = 0; +		} else { +			/* no need to hold page hostage */ +			rdata->pages[i] = NULL; +			rdata->nr_pages--; +			put_page(page); +			continue; +		} + +		result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len); +		kunmap(page); +		if (result < 0) +			break; + +		total_read += result; +	} + +	return total_read > 0 ? total_read : result; +} + +ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to) +{ +	struct file *file = iocb->ki_filp; +	ssize_t rc; +	size_t len, cur_len; +	ssize_t total_read = 0; +	loff_t offset = iocb->ki_pos; +	unsigned int npages; +	struct cifs_sb_info *cifs_sb; +	struct cifs_tcon *tcon; +	struct cifsFileInfo *open_file; +	struct cifs_readdata *rdata, *tmp; +	struct list_head rdata_list; +	pid_t pid; + +	len = iov_iter_count(to); +	if (!len) +		return 0; + +	INIT_LIST_HEAD(&rdata_list); +	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);  	open_file = file->private_data; -	pTcon = tlink_tcon(open_file->tlink); +	tcon = tlink_tcon(open_file->tlink); + +	if (!tcon->ses->server->ops->async_readv) +		return -ENOSYS; + +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) +		pid = open_file->pid; +	else +		pid = current->tgid;  	if ((file->f_flags & O_ACCMODE) == O_WRONLY) -		cFYI(1, "attempting read on write only file instance"); +		cifs_dbg(FYI, "attempting read on write only file instance\n"); -	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) { -				rc = cifs_reopen_file(open_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; -			} +	do { +		cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize); +		npages = DIV_ROUND_UP(cur_len, PAGE_SIZE); + +		/* allocate a readdata struct */ +		rdata = cifs_readdata_alloc(npages, +					    cifs_uncached_readv_complete); +		if (!rdata) { +			rc = -ENOMEM; +			break;  		} -		if (rc || (bytes_read == 0)) { -			if (total_read) { -				break; + +		rc = cifs_read_allocate_pages(rdata, npages); +		if (rc) +			goto error; + +		rdata->cfile = cifsFileInfo_get(open_file); +		rdata->nr_pages = npages; +		rdata->offset = offset; +		rdata->bytes = cur_len; +		rdata->pid = pid; +		rdata->pagesz = PAGE_SIZE; +		rdata->read_into_pages = cifs_uncached_read_into_pages; + +		rc = cifs_retry_async_readv(rdata); +error: +		if (rc) { +			kref_put(&rdata->refcount, +				 cifs_uncached_readdata_release); +			break; +		} + +		list_add_tail(&rdata->list, &rdata_list); +		offset += cur_len; +		len -= cur_len; +	} while (len > 0); + +	/* if at least one read request send succeeded, then reset rc */ +	if (!list_empty(&rdata_list)) +		rc = 0; + +	len = iov_iter_count(to); +	/* the loop below should proceed in the order of increasing offsets */ +	list_for_each_entry_safe(rdata, tmp, &rdata_list, list) { +	again: +		if (!rc) { +			/* FIXME: freezable sleep too? */ +			rc = wait_for_completion_killable(&rdata->done); +			if (rc) +				rc = -EINTR; +			else if (rdata->result) { +				rc = rdata->result; +				/* resend call if it's a retryable error */ +				if (rc == -EAGAIN) { +					rc = cifs_retry_async_readv(rdata); +					goto again; +				}  			} else { -				FreeXid(xid); -				return rc; +				rc = cifs_readdata_to_iov(rdata, to);  			} -		} else { -			cifs_stats_bytes_read(pTcon, bytes_read); -			*poffset += bytes_read; +  		} +		list_del_init(&rdata->list); +		kref_put(&rdata->refcount, cifs_uncached_readdata_release);  	} -	FreeXid(xid); -	return total_read; + +	total_read = len - iov_iter_count(to); + +	cifs_stats_bytes_read(tcon, total_read); + +	/* mask nodata case */ +	if (rc == -ENODATA) +		rc = 0; + +	if (total_read) { +		iocb->ki_pos += total_read; +		return total_read; +	} +	return rc;  } +ssize_t +cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to) +{ +	struct inode *inode = file_inode(iocb->ki_filp); +	struct cifsInodeInfo *cinode = CIFS_I(inode); +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct cifsFileInfo *cfile = (struct cifsFileInfo *) +						iocb->ki_filp->private_data; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	int rc = -EACCES; -static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, -	loff_t *poffset) +	/* +	 * In strict cache mode we need to read from the server all the time +	 * if we don't have level II oplock because the server can delay mtime +	 * change - so we can't make a decision about inode invalidating. +	 * And we can also fail with pagereading if there are mandatory locks +	 * on pages affected by this read but not on the region from pos to +	 * pos+len-1. +	 */ +	if (!CIFS_CACHE_READ(cinode)) +		return cifs_user_readv(iocb, to); + +	if (cap_unix(tcon->ses) && +	    (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && +	    ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) +		return generic_file_read_iter(iocb, to); + +	/* +	 * We need to hold the sem to be sure nobody modifies lock list +	 * with a brlock that prevents reading. +	 */ +	down_read(&cinode->lock_sem); +	if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to), +				     tcon->ses->server->vals->shared_lock_type, +				     NULL, CIFS_READ_OP)) +		rc = generic_file_read_iter(iocb, to); +	up_read(&cinode->lock_sem); +	return rc; +} + +static ssize_t +cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)  {  	int rc = -EACCES;  	unsigned int bytes_read = 0;  	unsigned int total_read;  	unsigned int current_read_size; +	unsigned int rsize;  	struct cifs_sb_info *cifs_sb; -	struct cifsTconInfo *pTcon; -	int xid; -	char *current_offset; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	unsigned int xid; +	char *cur_offset;  	struct cifsFileInfo *open_file; +	struct cifs_io_parms io_parms;  	int buf_type = CIFS_NO_BUFFER; +	__u32 pid; -	xid = GetXid(); +	xid = get_xid();  	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	/* FIXME: set up handlers for larger reads and/or convert to async */ +	rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize); +  	if (file->private_data == NULL) {  		rc = -EBADF; -		FreeXid(xid); +		free_xid(xid);  		return rc;  	}  	open_file = file->private_data; -	pTcon = tlink_tcon(open_file->tlink); +	tcon = tlink_tcon(open_file->tlink); +	server = tcon->ses->server; + +	if (!server->ops->sync_read) { +		free_xid(xid); +		return -ENOSYS; +	} + +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) +		pid = open_file->pid; +	else +		pid = current->tgid;  	if ((file->f_flags & O_ACCMODE) == O_WRONLY) -		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); +		cifs_dbg(FYI, "attempting read on write only file instance\n"); + +	for (total_read = 0, cur_offset = read_data; read_size > total_read; +	     total_read += bytes_read, cur_offset += bytes_read) { +		current_read_size = min_t(uint, read_size - total_read, rsize); +		/* +		 * For windows me and 9x we do not want to request more than it +		 * negotiated since it will refuse the read then. +		 */ +		if ((tcon->ses) && !(tcon->ses->capabilities & +				tcon->ses->server->vals->cap_large_files)) { +			current_read_size = min_t(uint, current_read_size, +					CIFSMaxBufSize);  		}  		rc = -EAGAIN;  		while (rc == -EAGAIN) { @@ -1779,242 +3047,351 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,  				if (rc != 0)  					break;  			} -			rc = CIFSSMBRead(xid, pTcon, -					 open_file->netfid, -					 current_read_size, *poffset, -					 &bytes_read, ¤t_offset, -					 &buf_type); +			io_parms.pid = pid; +			io_parms.tcon = tcon; +			io_parms.offset = *offset; +			io_parms.length = current_read_size; +			rc = server->ops->sync_read(xid, open_file, &io_parms, +						    &bytes_read, &cur_offset, +						    &buf_type);  		}  		if (rc || (bytes_read == 0)) {  			if (total_read) {  				break;  			} else { -				FreeXid(xid); +				free_xid(xid);  				return rc;  			}  		} else { -			cifs_stats_bytes_read(pTcon, total_read); -			*poffset += bytes_read; +			cifs_stats_bytes_read(tcon, total_read); +			*offset += bytes_read;  		}  	} -	FreeXid(xid); +	free_xid(xid);  	return total_read;  } +/* + * If the page is mmap'ed into a process' page tables, then we need to make + * sure that it doesn't change while being written back. + */ +static int +cifs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) +{ +	struct page *page = vmf->page; + +	lock_page(page); +	return VM_FAULT_LOCKED; +} + +static struct vm_operations_struct cifs_file_vm_ops = { +	.fault = filemap_fault, +	.map_pages = filemap_map_pages, +	.page_mkwrite = cifs_page_mkwrite, +	.remap_pages = generic_file_remap_pages, +}; + +int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) +{ +	int rc, xid; +	struct inode *inode = file_inode(file); + +	xid = get_xid(); + +	if (!CIFS_CACHE_READ(CIFS_I(inode))) { +		rc = cifs_zap_mapping(inode); +		if (rc) +			return rc; +	} + +	rc = generic_file_mmap(file, vma); +	if (rc == 0) +		vma->vm_ops = &cifs_file_vm_ops; +	free_xid(xid); +	return rc; +} +  int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)  {  	int rc, xid; -	xid = GetXid(); +	xid = get_xid();  	rc = cifs_revalidate_file(file);  	if (rc) { -		cFYI(1, "Validation prior to mmap failed, error=%d", rc); -		FreeXid(xid); +		cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n", +			 rc); +		free_xid(xid);  		return rc;  	}  	rc = generic_file_mmap(file, vma); -	FreeXid(xid); +	if (rc == 0) +		vma->vm_ops = &cifs_file_vm_ops; +	free_xid(xid);  	return rc;  } - -static void cifs_copy_cache_pages(struct address_space *mapping, -	struct list_head *pages, int bytes_read, char *data) +static void +cifs_readv_complete(struct work_struct *work)  { -	struct page *page; -	char *target; +	unsigned int i; +	struct cifs_readdata *rdata = container_of(work, +						struct cifs_readdata, work); -	while (bytes_read > 0) { -		if (list_empty(pages)) -			break; +	for (i = 0; i < rdata->nr_pages; i++) { +		struct page *page = rdata->pages[i]; -		page = list_entry(pages->prev, struct page, lru); -		list_del(&page->lru); +		lru_cache_add_file(page); -		if (add_to_page_cache_lru(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; +		if (rdata->result == 0) { +			flush_dcache_page(page); +			SetPageUptodate(page);  		} -		page_cache_release(page); -		target = kmap_atomic(page, KM_USER0); +		unlock_page(page); + +		if (rdata->result == 0) +			cifs_readpage_to_fscache(rdata->mapping->host, page); + +		page_cache_release(page); +		rdata->pages[i] = NULL; +	} +	kref_put(&rdata->refcount, cifs_readdata_release); +} -		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; +static int +cifs_readpages_read_into_pages(struct TCP_Server_Info *server, +			struct cifs_readdata *rdata, unsigned int len) +{ +	int total_read = 0, result = 0; +	unsigned int i; +	u64 eof; +	pgoff_t eof_index; +	unsigned int nr_pages = rdata->nr_pages; +	struct kvec iov; + +	/* determine the eof that the server (probably) has */ +	eof = CIFS_I(rdata->mapping->host)->server_eof; +	eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0; +	cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index); + +	rdata->tailsz = PAGE_CACHE_SIZE; +	for (i = 0; i < nr_pages; i++) { +		struct page *page = rdata->pages[i]; + +		if (len >= PAGE_CACHE_SIZE) { +			/* enough data to fill the page */ +			iov.iov_base = kmap(page); +			iov.iov_len = PAGE_CACHE_SIZE; +			cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n", +				 i, page->index, iov.iov_base, iov.iov_len); +			len -= PAGE_CACHE_SIZE; +		} else if (len > 0) { +			/* enough for partial page, fill and zero the rest */ +			iov.iov_base = kmap(page); +			iov.iov_len = len; +			cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n", +				 i, page->index, iov.iov_base, iov.iov_len); +			memset(iov.iov_base + len, +				'\0', PAGE_CACHE_SIZE - len); +			rdata->tailsz = len; +			len = 0; +		} else if (page->index > eof_index) { +			/* +			 * The VFS will not try to do readahead past the +			 * i_size, but it's possible that we have outstanding +			 * writes with gaps in the middle and the i_size hasn't +			 * caught up yet. Populate those with zeroed out pages +			 * to prevent the VFS from repeatedly attempting to +			 * fill them until the writes are flushed. +			 */ +			zero_user(page, 0, PAGE_CACHE_SIZE); +			lru_cache_add_file(page); +			flush_dcache_page(page); +			SetPageUptodate(page); +			unlock_page(page); +			page_cache_release(page); +			rdata->pages[i] = NULL; +			rdata->nr_pages--; +			continue;  		} else { -			memcpy(target, data, PAGE_CACHE_SIZE); -			bytes_read -= PAGE_CACHE_SIZE; +			/* no need to hold page hostage */ +			lru_cache_add_file(page); +			unlock_page(page); +			page_cache_release(page); +			rdata->pages[i] = NULL; +			rdata->nr_pages--; +			continue;  		} -		kunmap_atomic(target, KM_USER0); -		flush_dcache_page(page); -		SetPageUptodate(page); -		unlock_page(page); -		data += PAGE_CACHE_SIZE; +		result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len); +		kunmap(page); +		if (result < 0) +			break; -		/* add page to FS-Cache */ -		cifs_readpage_to_fscache(mapping->host, page); +		total_read += result;  	} -	return; + +	return total_read > 0 ? total_read : result;  }  static int cifs_readpages(struct file *file, struct address_space *mapping,  	struct list_head *page_list, unsigned num_pages)  { -	int rc = -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 cifsFileInfo *open_file; -	int buf_type = CIFS_NO_BUFFER; +	int rc; +	struct list_head tmplist; +	struct cifsFileInfo *open_file = file->private_data; +	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	unsigned int rsize = cifs_sb->rsize; +	pid_t pid; -	xid = GetXid(); -	if (file->private_data == NULL) { -		rc = -EBADF; -		FreeXid(xid); -		return rc; -	} -	open_file = file->private_data; -	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); -	pTcon = tlink_tcon(open_file->tlink); +	/* +	 * Give up immediately if rsize is too small to read an entire page. +	 * The VFS will fall back to readpage. We should never reach this +	 * point however since we set ra_pages to 0 when the rsize is smaller +	 * than a cache page. +	 */ +	if (unlikely(rsize < PAGE_CACHE_SIZE)) +		return 0;  	/*  	 * Reads as many pages as possible from fscache. Returns -ENOBUFS  	 * immediately if the cookie is negative +	 * +	 * After this point, every page in the list might have PG_fscache set, +	 * so we will need to clean that up off of every page we don't use.  	 */  	rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list,  					 &num_pages);  	if (rc == 0) -		goto read_complete; +		return rc; + +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) +		pid = open_file->pid; +	else +		pid = current->tgid; -	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; +	rc = 0; +	INIT_LIST_HEAD(&tmplist); -		if (list_empty(page_list)) -			break; +	cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n", +		 __func__, file, mapping, num_pages); + +	/* +	 * Start with the page at end of list and move it to private +	 * list. Do the same with any following pages until we hit +	 * the rsize limit, hit an index discontinuity, or run out of +	 * pages. Issue the async read and then start the loop again +	 * until the list is empty. +	 * +	 * Note that list order is important. The page_list is in +	 * the order of declining indexes. When we put the pages in +	 * the rdata->pages, then we want them in increasing order. +	 */ +	while (!list_empty(page_list)) { +		unsigned int i; +		unsigned int bytes = PAGE_CACHE_SIZE; +		unsigned int expected_index; +		unsigned int nr_pages = 1; +		loff_t offset; +		struct page *page, *tpage; +		struct cifs_readdata *rdata;  		page = list_entry(page_list->prev, struct page, lru); + +		/* +		 * Lock the page and put it in the cache. Since no one else +		 * should have access to this page, we're safe to simply set +		 * PG_locked without checking it first. +		 */ +		__set_page_locked(page); +		rc = add_to_page_cache_locked(page, mapping, +					      page->index, GFP_KERNEL); + +		/* give up if we can't stick it in the cache */ +		if (rc) { +			__clear_page_locked(page); +			break; +		} + +		/* move first page to the tmplist */  		offset = (loff_t)page->index << PAGE_CACHE_SHIFT; +		list_move_tail(&page->lru, &tmplist); -		/* 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 +		/* now try and add more pages onto the request */ +		expected_index = page->index + 1; +		list_for_each_entry_safe_reverse(page, tpage, page_list, lru) { +			/* discontinuity ? */ +			if (page->index != expected_index)  				break; -		} -		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) { -				rc = cifs_reopen_file(open_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; -				} +			/* would this page push the read over the rsize? */ +			if (bytes + PAGE_CACHE_SIZE > rsize) +				break; + +			__set_page_locked(page); +			if (add_to_page_cache_locked(page, mapping, +						page->index, GFP_KERNEL)) { +				__clear_page_locked(page); +				break;  			} +			list_move_tail(&page->lru, &tmplist); +			bytes += PAGE_CACHE_SIZE; +			expected_index++; +			nr_pages++;  		} -		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)); - -			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; */ + +		rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete); +		if (!rdata) { +			/* best to give up if we're out of mem */ +			list_for_each_entry_safe(page, tpage, &tmplist, lru) { +				list_del(&page->lru); +				lru_cache_add_file(page); +				unlock_page(page); +				page_cache_release(page);  			} -		} 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? */ +			rc = -ENOMEM;  			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; + +		rdata->cfile = cifsFileInfo_get(open_file); +		rdata->mapping = mapping; +		rdata->offset = offset; +		rdata->bytes = bytes; +		rdata->pid = pid; +		rdata->pagesz = PAGE_CACHE_SIZE; +		rdata->read_into_pages = cifs_readpages_read_into_pages; + +		list_for_each_entry_safe(page, tpage, &tmplist, lru) { +			list_del(&page->lru); +			rdata->pages[rdata->nr_pages++] = page; +		} + +		rc = cifs_retry_async_readv(rdata); +		if (rc != 0) { +			for (i = 0; i < rdata->nr_pages; i++) { +				page = rdata->pages[i]; +				lru_cache_add_file(page); +				unlock_page(page); +				page_cache_release(page); +			} +			kref_put(&rdata->refcount, cifs_readdata_release); +			break;  		} -		bytes_read = 0; -	} -/* 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; +		kref_put(&rdata->refcount, cifs_readdata_release);  	} -read_complete: -	FreeXid(xid); +	/* Any pages that have been shown to fscache but didn't get added to +	 * the pagecache must be uncached before they get returned to the +	 * allocator. +	 */ +	cifs_fscache_readpages_cancel(mapping->host, page_list);  	return rc;  } +/* + * cifs_readpage_worker must be called with the page pinned + */  static int cifs_readpage_worker(struct file *file, struct page *page,  	loff_t *poffset)  { @@ -2022,11 +3399,10 @@ static int cifs_readpage_worker(struct file *file, struct page *page,  	int rc;  	/* Is the page cached? */ -	rc = cifs_readpage_from_fscache(file->f_path.dentry->d_inode, page); +	rc = cifs_readpage_from_fscache(file_inode(file), page);  	if (rc == 0)  		goto read_complete; -	page_cache_get(page);  	read_data = kmap(page);  	/* for reads over a certain size could initiate async read ahead */ @@ -2035,10 +3411,10 @@ static int cifs_readpage_worker(struct file *file, struct page *page,  	if (rc < 0)  		goto io_error;  	else -		cFYI(1, "Bytes read %d", rc); +		cifs_dbg(FYI, "Bytes read %d\n", rc); -	file->f_path.dentry->d_inode->i_atime = -		current_fs_time(file->f_path.dentry->d_inode->i_sb); +	file_inode(file)->i_atime = +		current_fs_time(file_inode(file)->i_sb);  	if (PAGE_CACHE_SIZE > rc)  		memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc); @@ -2047,13 +3423,13 @@ static int cifs_readpage_worker(struct file *file, struct page *page,  	SetPageUptodate(page);  	/* send this page to the cache */ -	cifs_readpage_to_fscache(file->f_path.dentry->d_inode, page); +	cifs_readpage_to_fscache(file_inode(file), page);  	rc = 0;  io_error:  	kunmap(page); -	page_cache_release(page); +	unlock_page(page);  read_complete:  	return rc; @@ -2063,24 +3439,22 @@ 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; +	unsigned int xid; -	xid = GetXid(); +	xid = get_xid();  	if (file->private_data == NULL) {  		rc = -EBADF; -		FreeXid(xid); +		free_xid(xid);  		return rc;  	} -	cFYI(1, "readpage %p at offset %d 0x%x\n", +	cifs_dbg(FYI, "readpage %p at offset %d 0x%x\n",  		 page, (int)offset, (int)offset);  	rc = cifs_readpage_worker(file, page, &offset); -	unlock_page(page); - -	FreeXid(xid); +	free_xid(xid);  	return rc;  } @@ -2133,6 +3507,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,  			loff_t pos, unsigned len, unsigned flags,  			struct page **pagep, void **fsdata)  { +	int oncethru = 0;  	pgoff_t index = pos >> PAGE_CACHE_SHIFT;  	loff_t offset = pos & (PAGE_CACHE_SIZE - 1);  	loff_t page_start = pos & PAGE_MASK; @@ -2140,8 +3515,9 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,  	struct page *page;  	int rc = 0; -	cFYI(1, "write_begin from %lld len %d", (long long)pos, len); +	cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len); +start:  	page = grab_cache_page_write_begin(mapping, index, flags);  	if (!page) {  		rc = -ENOMEM; @@ -2165,7 +3541,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,  	 * 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) { +	if (CIFS_CACHE_READ(CIFS_I(mapping->host))) {  		i_size = i_size_read(mapping->host);  		if (page_start >= i_size ||  		    (offset == 0 && (pos + len) >= i_size)) { @@ -2183,13 +3559,16 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,  		}  	} -	if ((file->f_flags & O_ACCMODE) != O_WRONLY) { +	if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) {  		/*  		 * might as well read a page, it is fast enough. If we get  		 * an error, we don't need to return it. cifs_write_end will  		 * do a sync write instead since PG_uptodate isn't set.  		 */  		cifs_readpage_worker(file, page, &page_start); +		page_cache_release(page); +		oncethru = 1; +		goto start;  	} else {  		/* we could try using another file handle if there is one -  		   but how would we lock it to prevent close of that handle @@ -2209,36 +3588,84 @@ static int cifs_release_page(struct page *page, gfp_t gfp)  	return cifs_fscache_release_page(page, gfp);  } -static void cifs_invalidate_page(struct page *page, unsigned long offset) +static void cifs_invalidate_page(struct page *page, unsigned int offset, +				 unsigned int length)  {  	struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host); -	if (offset == 0) +	if (offset == 0 && length == PAGE_CACHE_SIZE)  		cifs_fscache_invalidate_page(page, &cifsi->vfs_inode);  } +static int cifs_launder_page(struct page *page) +{ +	int rc = 0; +	loff_t range_start = page_offset(page); +	loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); +	struct writeback_control wbc = { +		.sync_mode = WB_SYNC_ALL, +		.nr_to_write = 0, +		.range_start = range_start, +		.range_end = range_end, +	}; + +	cifs_dbg(FYI, "Launder page: %p\n", page); + +	if (clear_page_dirty_for_io(page)) +		rc = cifs_writepage_locked(page, &wbc); + +	cifs_fscache_invalidate_page(page, page->mapping->host); +	return rc; +} + +static int +cifs_pending_writers_wait(void *unused) +{ +	schedule(); +	return 0; +} +  void cifs_oplock_break(struct work_struct *work)  {  	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,  						  oplock_break);  	struct inode *inode = cfile->dentry->d_inode;  	struct cifsInodeInfo *cinode = CIFS_I(inode); +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server;  	int rc = 0; +	wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, +			cifs_pending_writers_wait, TASK_UNINTERRUPTIBLE); + +	server->ops->downgrade_oplock(server, cinode, +		test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags)); + +	if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && +						cifs_has_mand_locks(cinode)) { +		cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", +			 inode); +		cinode->oplock = 0; +	} +  	if (inode && S_ISREG(inode->i_mode)) { -		if (cinode->clientCanCacheRead) +		if (CIFS_CACHE_READ(cinode))  			break_lease(inode, O_RDONLY);  		else  			break_lease(inode, O_WRONLY);  		rc = filemap_fdatawrite(inode->i_mapping); -		if (cinode->clientCanCacheRead == 0) { +		if (!CIFS_CACHE_READ(cinode)) {  			rc = filemap_fdatawait(inode->i_mapping);  			mapping_set_error(inode->i_mapping, rc); -			invalidate_remote_inode(inode); +			cifs_zap_mapping(inode);  		} -		cFYI(1, "Oplock flush inode %p rc %d", inode, rc); +		cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);  	} +	rc = cifs_push_locks(cfile); +	if (rc) +		cifs_dbg(VFS, "Push locks rc = %d\n", rc); +  	/*  	 * releasing stale oplock after recent reconnect of smb session using  	 * a now incorrect file handle is not a data integrity issue but do @@ -2246,34 +3673,33 @@ void cifs_oplock_break(struct work_struct *work)  	 * disconnected since oplock already released by the server  	 */  	if (!cfile->oplock_break_cancelled) { -		rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0, -				 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false); -		cFYI(1, "Oplock release rc = %d", rc); +		rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid, +							     cinode); +		cifs_dbg(FYI, "Oplock release rc = %d\n", rc);  	} - -	/* -	 * We might have kicked in before is_valid_oplock_break() -	 * finished grabbing reference for us.  Make sure it's done by -	 * waiting for cifs_file_list_lock. -	 */ -	spin_lock(&cifs_file_list_lock); -	spin_unlock(&cifs_file_list_lock); - -	cifs_oplock_break_put(cfile); +	cifs_done_oplock_break(cinode);  } -/* must be called while holding cifs_file_list_lock */ -void cifs_oplock_break_get(struct cifsFileInfo *cfile) +/* + * The presence of cifs_direct_io() in the address space ops vector + * allowes open() O_DIRECT flags which would have failed otherwise. + * + * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests + * so this method should never be called. + * + * Direct IO is not yet supported in the cached mode.  + */ +static ssize_t +cifs_direct_io(int rw, struct kiocb *iocb, struct iov_iter *iter, +               loff_t pos)  { -	cifs_sb_active(cfile->dentry->d_sb); -	cifsFileInfo_get(cfile); +        /* +         * FIXME +         * Eventually need to support direct IO for non forcedirectio mounts +         */ +        return -EINVAL;  } -void cifs_oplock_break_put(struct cifsFileInfo *cfile) -{ -	cifsFileInfo_put(cfile); -	cifs_sb_deactive(cfile->dentry->d_sb); -}  const struct address_space_operations cifs_addr_ops = {  	.readpage = cifs_readpage, @@ -2284,9 +3710,9 @@ const struct address_space_operations cifs_addr_ops = {  	.write_end = cifs_write_end,  	.set_page_dirty = __set_page_dirty_nobuffers,  	.releasepage = cifs_release_page, +	.direct_IO = cifs_direct_io,  	.invalidatepage = cifs_invalidate_page, -	/* .sync_page = cifs_sync_page, */ -	/* .direct_IO = */ +	.launder_page = cifs_launder_page,  };  /* @@ -2303,6 +3729,5 @@ const struct address_space_operations cifs_addr_ops_smallbuf = {  	.set_page_dirty = __set_page_dirty_nobuffers,  	.releasepage = cifs_release_page,  	.invalidatepage = cifs_invalidate_page, -	/* .sync_page = cifs_sync_page, */ -	/* .direct_IO = */ +	.launder_page = cifs_launder_page,  }; diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index a2ad94efcfe..8d4b7bc8ae9 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -2,7 +2,7 @@   *   fs/cifs/fscache.c - CIFS filesystem cache interface   *   *   Copyright (c) 2010 Novell, Inc. - *   Author(s): Suresh Jayaraman (sjayaraman@suse.de> + *   Author(s): Suresh Jayaraman <sjayaraman@suse.de>   *   *   This library is free software; you can redistribute it and/or modify   *   it under the terms of the GNU Lesser General Public License as published @@ -27,33 +27,33 @@ void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server)  {  	server->fscache =  		fscache_acquire_cookie(cifs_fscache_netfs.primary_index, -				&cifs_fscache_server_index_def, server); -	cFYI(1, "CIFS: get client cookie (0x%p/0x%p)", server, -				server->fscache); +				&cifs_fscache_server_index_def, server, true); +	cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", +		 __func__, server, server->fscache);  }  void cifs_fscache_release_client_cookie(struct TCP_Server_Info *server)  { -	cFYI(1, "CIFS: release client cookie (0x%p/0x%p)", server, -				server->fscache); +	cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", +		 __func__, server, server->fscache);  	fscache_relinquish_cookie(server->fscache, 0);  	server->fscache = NULL;  } -void cifs_fscache_get_super_cookie(struct cifsTconInfo *tcon) +void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)  {  	struct TCP_Server_Info *server = tcon->ses->server;  	tcon->fscache =  		fscache_acquire_cookie(server->fscache, -				&cifs_fscache_super_index_def, tcon); -	cFYI(1, "CIFS: get superblock cookie (0x%p/0x%p)", -				server->fscache, tcon->fscache); +				&cifs_fscache_super_index_def, tcon, true); +	cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", +		 __func__, server->fscache, tcon->fscache);  } -void cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) +void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)  { -	cFYI(1, "CIFS: releasing superblock cookie (0x%p)", tcon->fscache); +	cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache);  	fscache_relinquish_cookie(tcon->fscache, 0);  	tcon->fscache = NULL;  } @@ -62,15 +62,17 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode)  {  	struct cifsInodeInfo *cifsi = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);  	if (cifsi->fscache)  		return; -	cifsi->fscache = fscache_acquire_cookie(tcon->fscache, -				&cifs_fscache_inode_object_def, cifsi); -	cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", tcon->fscache, -				cifsi->fscache); +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) { +		cifsi->fscache = fscache_acquire_cookie(tcon->fscache, +				&cifs_fscache_inode_object_def, cifsi, true); +		cifs_dbg(FYI, "%s: got FH cookie (0x%p/0x%p)\n", +			 __func__, tcon->fscache, cifsi->fscache); +	}  }  void cifs_fscache_release_inode_cookie(struct inode *inode) @@ -78,8 +80,7 @@ void cifs_fscache_release_inode_cookie(struct inode *inode)  	struct cifsInodeInfo *cifsi = CIFS_I(inode);  	if (cifsi->fscache) { -		cFYI(1, "CIFS releasing inode cookie (0x%p)", -				cifsi->fscache); +		cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);  		fscache_relinquish_cookie(cifsi->fscache, 0);  		cifsi->fscache = NULL;  	} @@ -90,8 +91,8 @@ static void cifs_fscache_disable_inode_cookie(struct inode *inode)  	struct cifsInodeInfo *cifsi = CIFS_I(inode);  	if (cifsi->fscache) { -		cFYI(1, "CIFS disabling inode cookie (0x%p)", -				cifsi->fscache); +		cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache); +		fscache_uncache_all_inode_pages(cifsi->fscache, inode);  		fscache_relinquish_cookie(cifsi->fscache, 1);  		cifsi->fscache = NULL;  	} @@ -101,10 +102,8 @@ void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp)  {  	if ((filp->f_flags & O_ACCMODE) != O_RDONLY)  		cifs_fscache_disable_inode_cookie(inode); -	else { +	else  		cifs_fscache_enable_inode_cookie(inode); -		cFYI(1, "CIFS: fscache inode cookie set"); -	}  }  void cifs_fscache_reset_inode_cookie(struct inode *inode) @@ -120,9 +119,9 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode)  		cifsi->fscache = fscache_acquire_cookie(  					cifs_sb_master_tcon(cifs_sb)->fscache,  					&cifs_fscache_inode_object_def, -					cifsi); -		cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p", -				cifsi->fscache, old); +					cifsi, true); +		cifs_dbg(FYI, "%s: new cookie 0x%p oldcookie 0x%p\n", +			 __func__, cifsi->fscache, old);  	}  } @@ -132,8 +131,8 @@ int cifs_fscache_release_page(struct page *page, gfp_t gfp)  		struct inode *inode = page->mapping->host;  		struct cifsInodeInfo *cifsi = CIFS_I(inode); -		cFYI(1, "CIFS: fscache release page (0x%p/0x%p)", -				page, cifsi->fscache); +		cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", +			 __func__, page, cifsi->fscache);  		if (!fscache_maybe_release_page(cifsi->fscache, page, gfp))  			return 0;  	} @@ -144,8 +143,7 @@ int cifs_fscache_release_page(struct page *page, gfp_t gfp)  static void cifs_readpage_from_fscache_complete(struct page *page, void *ctx,  						int error)  { -	cFYI(1, "CFS: readpage_from_fscache_complete (0x%p/%d)", -			page, error); +	cifs_dbg(FYI, "%s: (0x%p/%d)\n", __func__, page, error);  	if (!error)  		SetPageUptodate(page);  	unlock_page(page); @@ -158,8 +156,8 @@ int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)  {  	int ret; -	cFYI(1, "CIFS: readpage_from_fscache(fsc:%p, p:%p, i:0x%p", -			CIFS_I(inode)->fscache, page, inode); +	cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", +		 __func__, CIFS_I(inode)->fscache, page, inode);  	ret = fscache_read_or_alloc_page(CIFS_I(inode)->fscache, page,  					 cifs_readpage_from_fscache_complete,  					 NULL, @@ -167,15 +165,15 @@ int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)  	switch (ret) {  	case 0: /* page found in fscache, read submitted */ -		cFYI(1, "CIFS: readpage_from_fscache: submitted"); +		cifs_dbg(FYI, "%s: submitted\n", __func__);  		return ret;  	case -ENOBUFS:	/* page won't be cached */  	case -ENODATA:	/* page not in cache */ -		cFYI(1, "CIFS: readpage_from_fscache %d", ret); +		cifs_dbg(FYI, "%s: %d\n", __func__, ret);  		return 1;  	default: -		cERROR(1, "unknown error ret = %d", ret); +		cifs_dbg(VFS, "unknown error ret = %d\n", ret);  	}  	return ret;  } @@ -190,8 +188,8 @@ int __cifs_readpages_from_fscache(struct inode *inode,  {  	int ret; -	cFYI(1, "CIFS: __cifs_readpages_from_fscache (0x%p/%u/0x%p)", -			CIFS_I(inode)->fscache, *nr_pages, inode); +	cifs_dbg(FYI, "%s: (0x%p/%u/0x%p)\n", +		 __func__, CIFS_I(inode)->fscache, *nr_pages, inode);  	ret = fscache_read_or_alloc_pages(CIFS_I(inode)->fscache, mapping,  					  pages, nr_pages,  					  cifs_readpage_from_fscache_complete, @@ -199,16 +197,16 @@ int __cifs_readpages_from_fscache(struct inode *inode,  					  mapping_gfp_mask(mapping));  	switch (ret) {  	case 0:	/* read submitted to the cache for all pages */ -		cFYI(1, "CIFS: readpages_from_fscache: submitted"); +		cifs_dbg(FYI, "%s: submitted\n", __func__);  		return ret;  	case -ENOBUFS:	/* some pages are not cached and can't be */  	case -ENODATA:	/* some pages are not cached */ -		cFYI(1, "CIFS: readpages_from_fscache: no page"); +		cifs_dbg(FYI, "%s: no page\n", __func__);  		return 1;  	default: -		cFYI(1, "unknown error ret = %d", ret); +		cifs_dbg(FYI, "unknown error ret = %d\n", ret);  	}  	return ret; @@ -218,19 +216,26 @@ void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)  {  	int ret; -	cFYI(1, "CIFS: readpage_to_fscache(fsc: %p, p: %p, i: %p", -			CIFS_I(inode)->fscache, page, inode); +	cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", +		 __func__, CIFS_I(inode)->fscache, page, inode);  	ret = fscache_write_page(CIFS_I(inode)->fscache, page, GFP_KERNEL);  	if (ret != 0)  		fscache_uncache_page(CIFS_I(inode)->fscache, page);  } +void __cifs_fscache_readpages_cancel(struct inode *inode, struct list_head *pages) +{ +	cifs_dbg(FYI, "%s: (fsc: %p, i: %p)\n", +		 __func__, CIFS_I(inode)->fscache, inode); +	fscache_readpages_cancel(CIFS_I(inode)->fscache, pages); +} +  void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode)  {  	struct cifsInodeInfo *cifsi = CIFS_I(inode);  	struct fscache_cookie *cookie = cifsi->fscache; -	cFYI(1, "CIFS: fscache invalidatepage (0x%p/0x%p)", page, cookie); +	cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie);  	fscache_wait_on_page_write(cookie, page);  	fscache_uncache_page(cookie, page);  } diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 31b88ec2341..24794b6cd8e 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -40,8 +40,8 @@ extern void cifs_fscache_unregister(void);   */  extern void cifs_fscache_get_client_cookie(struct TCP_Server_Info *);  extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *); -extern void cifs_fscache_get_super_cookie(struct cifsTconInfo *); -extern void cifs_fscache_release_super_cookie(struct cifsTconInfo *); +extern void cifs_fscache_get_super_cookie(struct cifs_tcon *); +extern void cifs_fscache_release_super_cookie(struct cifs_tcon *);  extern void cifs_fscache_release_inode_cookie(struct inode *);  extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *); @@ -54,6 +54,7 @@ extern int __cifs_readpages_from_fscache(struct inode *,  					 struct address_space *,  					 struct list_head *,  					 unsigned *); +extern void __cifs_fscache_readpages_cancel(struct inode *, struct list_head *);  extern void __cifs_readpage_to_fscache(struct inode *, struct page *); @@ -91,6 +92,13 @@ static inline void cifs_readpage_to_fscache(struct inode *inode,  		__cifs_readpage_to_fscache(inode, page);  } +static inline void cifs_fscache_readpages_cancel(struct inode *inode, +						 struct list_head *pages) +{ +	if (CIFS_I(inode)->fscache) +		return __cifs_fscache_readpages_cancel(inode, pages); +} +  #else /* CONFIG_CIFS_FSCACHE */  static inline int cifs_fscache_register(void) { return 0; }  static inline void cifs_fscache_unregister(void) {} @@ -99,9 +107,9 @@ static inline void  cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {}  static inline void  cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) {} -static inline void cifs_fscache_get_super_cookie(struct cifsTconInfo *tcon) {} +static inline void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) {}  static inline void -cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) {} +cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {}  static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {}  static inline void cifs_fscache_set_inode_cookie(struct inode *inode, @@ -131,6 +139,11 @@ static inline int cifs_readpages_from_fscache(struct inode *inode,  static inline void cifs_readpage_to_fscache(struct inode *inode,  			struct page *page) {} +static inline void cifs_fscache_readpages_cancel(struct inode *inode, +						 struct list_head *pages) +{ +} +  #endif /* CONFIG_CIFS_FSCACHE */  #endif /* _CIFS_FSCACHE_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ef3a55bf86b..a174605f6af 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -22,6 +22,7 @@  #include <linux/stat.h>  #include <linux/slab.h>  #include <linux/pagemap.h> +#include <linux/freezer.h>  #include <asm/div64.h>  #include "cifsfs.h"  #include "cifspdu.h" @@ -32,7 +33,7 @@  #include "fscache.h" -static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) +static void cifs_set_ops(struct inode *inode)  {  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); @@ -44,13 +45,17 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)  				inode->i_fop = &cifs_file_direct_nobrl_ops;  			else  				inode->i_fop = &cifs_file_direct_ops; +		} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { +			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) +				inode->i_fop = &cifs_file_strict_nobrl_ops; +			else +				inode->i_fop = &cifs_file_strict_ops;  		} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)  			inode->i_fop = &cifs_file_nobrl_ops;  		else { /* not direct, send byte range locks */  			inode->i_fop = &cifs_file_ops;  		} -  		/* check if server can support readpages */  		if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <  				PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) @@ -60,7 +65,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)  		break;  	case S_IFDIR:  #ifdef CONFIG_CIFS_DFS_UPCALL -		if (is_dfs_referral) { +		if (IS_AUTOMOUNT(inode)) {  			inode->i_op = &cifs_dfs_referral_inode_operations;  		} else {  #else /* NO DFS support, treat as a directory */ @@ -87,31 +92,60 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)  {  	struct cifsInodeInfo *cifs_i = CIFS_I(inode); -	cFYI(1, "%s: revalidating inode %llu", __func__, cifs_i->uniqueid); +	cifs_dbg(FYI, "%s: revalidating inode %llu\n", +		 __func__, cifs_i->uniqueid);  	if (inode->i_state & I_NEW) { -		cFYI(1, "%s: inode %llu is new", __func__, cifs_i->uniqueid); +		cifs_dbg(FYI, "%s: inode %llu is new\n", +			 __func__, cifs_i->uniqueid);  		return;  	}  	/* don't bother with revalidation if we have an oplock */ -	if (cifs_i->clientCanCacheRead) { -		cFYI(1, "%s: inode %llu is oplocked", __func__, -			 cifs_i->uniqueid); +	if (CIFS_CACHE_READ(cifs_i)) { +		cifs_dbg(FYI, "%s: inode %llu is oplocked\n", +			 __func__, cifs_i->uniqueid);  		return;  	}  	 /* revalidate if mtime or size have changed */  	if (timespec_equal(&inode->i_mtime, &fattr->cf_mtime) &&  	    cifs_i->server_eof == fattr->cf_eof) { -		cFYI(1, "%s: inode %llu is unchanged", __func__, -			 cifs_i->uniqueid); +		cifs_dbg(FYI, "%s: inode %llu is unchanged\n", +			 __func__, cifs_i->uniqueid);  		return;  	} -	cFYI(1, "%s: invalidating inode %llu mapping", __func__, -		 cifs_i->uniqueid); -	cifs_i->invalid_mapping = true; +	cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n", +		 __func__, cifs_i->uniqueid); +	set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); +} + +/* + * copy nlink to the inode, unless it wasn't provided.  Provide + * sane values if we don't have an existing one and none was provided + */ +static void +cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) +{ +	/* +	 * if we're in a situation where we can't trust what we +	 * got from the server (readdir, some non-unix cases) +	 * fake reasonable values +	 */ +	if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { +		/* only provide fake values on a new inode */ +		if (inode->i_state & I_NEW) { +			if (fattr->cf_cifsattrs & ATTR_DIRECTORY) +				set_nlink(inode, 2); +			else +				set_nlink(inode, 1); +		} +		return; +	} + +	/* we trust the server, so update it */ +	set_nlink(inode, fattr->cf_nlink);  }  /* populate an inode with info from a cifs_fattr struct */ @@ -120,15 +154,15 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)  {  	struct cifsInodeInfo *cifs_i = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); -	unsigned long oldtime = cifs_i->time;  	cifs_revalidate_cache(inode, fattr); +	spin_lock(&inode->i_lock);  	inode->i_atime = fattr->cf_atime;  	inode->i_mtime = fattr->cf_mtime;  	inode->i_ctime = fattr->cf_ctime;  	inode->i_rdev = fattr->cf_rdev; -	inode->i_nlink = fattr->cf_nlink; +	cifs_nlink_fattr_to_inode(inode, fattr);  	inode->i_uid = fattr->cf_uid;  	inode->i_gid = fattr->cf_gid; @@ -144,17 +178,16 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)  	else  		cifs_i->time = jiffies; -	cFYI(1, "inode 0x%p old_time=%ld new_time=%ld", inode, -		 oldtime, cifs_i->time); - -	cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING; +	if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) +		set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); +	else +		clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);  	cifs_i->server_eof = fattr->cf_eof;  	/*  	 * Can't safely change the file size here if the client is writing to  	 * it due to potential races.  	 */ -	spin_lock(&inode->i_lock);  	if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) {  		i_size_write(inode, fattr->cf_eof); @@ -167,7 +200,10 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)  	}  	spin_unlock(&inode->i_lock); -	cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL); +	if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) +		inode->i_flags |= S_AUTOMOUNT; +	if (inode->i_state & I_NEW) +		cifs_set_ops(inode);  }  void @@ -238,19 +274,29 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,  		/* safest to call it a file if we do not know */  		fattr->cf_mode |= S_IFREG;  		fattr->cf_dtype = DT_REG; -		cFYI(1, "unknown type %d", le32_to_cpu(info->Type)); +		cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type));  		break;  	} -	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) -		fattr->cf_uid = cifs_sb->mnt_uid; -	else -		fattr->cf_uid = le64_to_cpu(info->Uid); - -	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) -		fattr->cf_gid = cifs_sb->mnt_gid; -	else -		fattr->cf_gid = le64_to_cpu(info->Gid); +	fattr->cf_uid = cifs_sb->mnt_uid; +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { +		u64 id = le64_to_cpu(info->Uid); +		if (id < ((uid_t)-1)) { +			kuid_t uid = make_kuid(&init_user_ns, id); +			if (uid_valid(uid)) +				fattr->cf_uid = uid; +		} +	} +	 +	fattr->cf_gid = cifs_sb->mnt_gid; +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { +		u64 id = le64_to_cpu(info->Gid); +		if (id < ((gid_t)-1)) { +			kgid_t gid = make_kgid(&init_user_ns, id); +			if (gid_valid(gid)) +				fattr->cf_gid = gid; +		} +	}  	fattr->cf_nlink = le64_to_cpu(info->Nlinks);  } @@ -267,7 +313,7 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)  {  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); -	cFYI(1, "creating fake fattr for DFS referral"); +	cifs_dbg(FYI, "creating fake fattr for DFS referral\n");  	memset(fattr, 0, sizeof(*fattr));  	fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; @@ -280,19 +326,20 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)  	fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;  } -int cifs_get_file_info_unix(struct file *filp) +static int +cifs_get_file_info_unix(struct file *filp)  {  	int rc; -	int xid; +	unsigned int xid;  	FILE_UNIX_BASIC_INFO find_data;  	struct cifs_fattr fattr; -	struct inode *inode = filp->f_path.dentry->d_inode; +	struct inode *inode = file_inode(filp);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct cifsFileInfo *cfile = filp->private_data; -	struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink); +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); -	xid = GetXid(); -	rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); +	xid = get_xid(); +	rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);  	if (!rc) {  		cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);  	} else if (rc == -EREMOTE) { @@ -301,22 +348,22 @@ int cifs_get_file_info_unix(struct file *filp)  	}  	cifs_fattr_to_inode(inode, &fattr); -	FreeXid(xid); +	free_xid(xid);  	return rc;  }  int cifs_get_inode_info_unix(struct inode **pinode,  			     const unsigned char *full_path, -			     struct super_block *sb, int xid) +			     struct super_block *sb, unsigned int xid)  {  	int rc;  	FILE_UNIX_BASIC_INFO find_data;  	struct cifs_fattr fattr; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	struct tcon_link *tlink;  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); -	cFYI(1, "Getting info on %s", full_path); +	cifs_dbg(FYI, "Getting info on %s\n", full_path);  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) @@ -340,9 +387,10 @@ int cifs_get_inode_info_unix(struct inode **pinode,  	/* check for Minshall+French symlinks */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { -		int tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); +		int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, +					     full_path);  		if (tmprc) -			cFYI(1, "CIFSCheckMFSymlink: %d", tmprc); +			cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);  	}  	if (*pinode == NULL) { @@ -360,17 +408,20 @@ int cifs_get_inode_info_unix(struct inode **pinode,  }  static int -cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, -	      struct cifs_sb_info *cifs_sb, int xid) +cifs_sfu_type(struct cifs_fattr *fattr, const char *path, +	      struct cifs_sb_info *cifs_sb, unsigned int xid)  {  	int rc;  	int oplock = 0; -	__u16 netfid;  	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; +	struct cifs_io_parms io_parms;  	char buf[24];  	unsigned int bytes_read;  	char *pbuf; +	int buf_type = CIFS_NO_BUFFER;  	pbuf = buf; @@ -391,58 +442,69 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); -	rc = CIFSSMBOpen(xid, tcon, 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, tcon, 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"); -				fattr->cf_mode |= S_IFBLK; -				fattr->cf_dtype = DT_BLK; -				if (bytes_read == 24) { -					/* we have enough to decode dev num */ -					__u64 mjr; /* major */ -					__u64 mnr; /* minor */ -					mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); -					mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); -					fattr->cf_rdev = MKDEV(mjr, mnr); -				} -			} else if (memcmp("IntxCHR", pbuf, 8) == 0) { -				cFYI(1, "Char device"); -				fattr->cf_mode |= S_IFCHR; -				fattr->cf_dtype = DT_CHR; -				if (bytes_read == 24) { -					/* we have enough to decode dev num */ -					__u64 mjr; /* major */ -					__u64 mnr; /* minor */ -					mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); -					mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); -					fattr->cf_rdev = MKDEV(mjr, mnr); -				} -			} else if (memcmp("IntxLNK", pbuf, 7) == 0) { -				cFYI(1, "Symlink"); -				fattr->cf_mode |= S_IFLNK; -				fattr->cf_dtype = DT_LNK; -			} else { -				fattr->cf_mode |= S_IFREG; /* file? */ -				fattr->cf_dtype = DT_REG; -				rc = -EOPNOTSUPP; +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_READ; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc) { +		cifs_put_tlink(tlink); +		return rc; +	} + +	/* Read header */ +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = 24; + +	rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); +	if ((rc == 0) && (bytes_read >= 8)) { +		if (memcmp("IntxBLK", pbuf, 8) == 0) { +			cifs_dbg(FYI, "Block device\n"); +			fattr->cf_mode |= S_IFBLK; +			fattr->cf_dtype = DT_BLK; +			if (bytes_read == 24) { +				/* we have enough to decode dev num */ +				__u64 mjr; /* major */ +				__u64 mnr; /* minor */ +				mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); +				mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); +				fattr->cf_rdev = MKDEV(mjr, mnr);  			} +		} else if (memcmp("IntxCHR", pbuf, 8) == 0) { +			cifs_dbg(FYI, "Char device\n"); +			fattr->cf_mode |= S_IFCHR; +			fattr->cf_dtype = DT_CHR; +			if (bytes_read == 24) { +				/* we have enough to decode dev num */ +				__u64 mjr; /* major */ +				__u64 mnr; /* minor */ +				mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); +				mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); +				fattr->cf_rdev = MKDEV(mjr, mnr); +			} +		} else if (memcmp("IntxLNK", pbuf, 7) == 0) { +			cifs_dbg(FYI, "Symlink\n"); +			fattr->cf_mode |= S_IFLNK; +			fattr->cf_dtype = DT_LNK;  		} else { -			fattr->cf_mode |= S_IFREG; /* then it is a file */ +			fattr->cf_mode |= S_IFREG; /* file? */  			fattr->cf_dtype = DT_REG; -			rc = -EOPNOTSUPP; /* or some unknown SFU type */ +			rc = -EOPNOTSUPP;  		} -		CIFSSMBClose(xid, tcon, netfid); +	} else { +		fattr->cf_mode |= S_IFREG; /* then it is a file */ +		fattr->cf_dtype = DT_REG; +		rc = -EOPNOTSUPP; /* or some unknown SFU type */  	} +	CIFSSMBClose(xid, tcon, fid.netfid);  	cifs_put_tlink(tlink);  	return rc;  } @@ -455,34 +517,39 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,   * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ?   */  static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, -			 struct cifs_sb_info *cifs_sb, int xid) +			 struct cifs_sb_info *cifs_sb, unsigned int xid)  {  #ifdef CONFIG_CIFS_XATTR  	ssize_t rc;  	char ea_value[4];  	__u32 mode;  	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink))  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); -	rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS", -			    ea_value, 4 /* size of buf */, cifs_sb->local_nls, -			    cifs_sb->mnt_cifs_flags & -				CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (tcon->ses->server->ops->query_all_EAs == NULL) { +		cifs_put_tlink(tlink); +		return -EOPNOTSUPP; +	} + +	rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path, +			"SETFILEBITS", ea_value, 4 /* size of buf */, +			cifs_sb->local_nls, +			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);  	cifs_put_tlink(tlink);  	if (rc < 0)  		return (int)rc;  	else if (rc > 3) {  		mode = le32_to_cpu(*((__le32 *)ea_value));  		fattr->cf_mode &= ~SFBITS_MASK; -		cFYI(1, "special bits 0%o org mode 0%o", mode, -			 fattr->cf_mode); +		cifs_dbg(FYI, "special bits 0%o org mode 0%o\n", +			 mode, fattr->cf_mode);  		fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; -		cFYI(1, "special mode bits 0%o", mode); +		cifs_dbg(FYI, "special mode bits 0%o\n", mode);  	}  	return 0; @@ -494,9 +561,10 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,  /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */  static void  cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, -		       struct cifs_sb_info *cifs_sb, bool adjust_tz) +		       struct cifs_sb_info *cifs_sb, bool adjust_tz, +		       bool symlink)  { -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);  	memset(fattr, 0, sizeof(*fattr));  	fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); @@ -518,10 +586,22 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,  	fattr->cf_eof = le64_to_cpu(info->EndOfFile);  	fattr->cf_bytes = le64_to_cpu(info->AllocationSize); +	fattr->cf_createtime = le64_to_cpu(info->CreationTime); -	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { +	fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + +	if (symlink) { +		fattr->cf_mode = S_IFLNK; +		fattr->cf_dtype = DT_LNK; +	} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {  		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;  		fattr->cf_dtype = DT_DIR; +		/* +		 * Server can return wrong NumberOfLinks value for directories +		 * when Unix extensions are disabled - fake it. +		 */ +		if (!tcon->unix_ext) +			fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;  	} else {  		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;  		fattr->cf_dtype = DT_REG; @@ -529,28 +609,52 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,  		/* clear write bits if ATTR_READONLY is set */  		if (fattr->cf_cifsattrs & ATTR_READONLY)  			fattr->cf_mode &= ~(S_IWUGO); -	} -	fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); +		/* +		 * Don't accept zero nlink from non-unix servers unless +		 * delete is pending.  Instead mark it as unknown. +		 */ +		if ((fattr->cf_nlink < 1) && !tcon->unix_ext && +		    !info->DeletePending) { +			cifs_dbg(1, "bogus file nlink value %u\n", +				fattr->cf_nlink); +			fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; +		} +	}  	fattr->cf_uid = cifs_sb->mnt_uid;  	fattr->cf_gid = cifs_sb->mnt_gid;  } -int cifs_get_file_info(struct file *filp) +static int +cifs_get_file_info(struct file *filp)  {  	int rc; -	int xid; +	unsigned int xid;  	FILE_ALL_INFO find_data;  	struct cifs_fattr fattr; -	struct inode *inode = filp->f_path.dentry->d_inode; +	struct inode *inode = file_inode(filp);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct cifsFileInfo *cfile = filp->private_data; -	struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink); - -	xid = GetXid(); -	rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); -	if (rc == -EOPNOTSUPP || rc == -EINVAL) { +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server; + +	if (!server->ops->query_file_info) +		return -ENOSYS; + +	xid = get_xid(); +	rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); +	switch (rc) { +	case 0: +		cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false, +				       false); +		break; +	case -EREMOTE: +		cifs_create_dfs_fattr(&fattr, inode->i_sb); +		rc = 0; +		break; +	case -EOPNOTSUPP: +	case -EINVAL:  		/*  		 * FIXME: legacy server -- fall back to path-based call?  		 * for now, just skip revalidating and mark inode for @@ -558,87 +662,109 @@ int cifs_get_file_info(struct file *filp)  		 */  		rc = 0;  		CIFS_I(inode)->time = 0; +	default:  		goto cgfi_exit; -	} else if (rc == -EREMOTE) { -		cifs_create_dfs_fattr(&fattr, inode->i_sb); -		rc = 0; -	} else if (rc) -		goto cgfi_exit; +	}  	/*  	 * don't bother with SFU junk here -- just mark inode as needing  	 * revalidation.  	 */ -	cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);  	fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;  	fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;  	cifs_fattr_to_inode(inode, &fattr);  cgfi_exit: -	FreeXid(xid); +	free_xid(xid);  	return rc;  } -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 +cifs_get_inode_info(struct inode **inode, const char *full_path, +		    FILE_ALL_INFO *data, struct super_block *sb, int xid, +		    const struct cifs_fid *fid)  { -	int rc = 0, tmprc; -	struct cifsTconInfo *pTcon; +	bool validinum = false; +	__u16 srchflgs; +	int rc = 0, tmprc = ENOSYS; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server;  	struct tcon_link *tlink;  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);  	char *buf = NULL; -	bool adjustTZ = false; +	bool adjust_tz = false;  	struct cifs_fattr fattr; +	struct cifs_search_info *srchinf = NULL; +	bool symlink = false;  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink); +	server = tcon->ses->server; -	cFYI(1, "Getting info on %s", full_path); +	cifs_dbg(FYI, "Getting info on %s\n", full_path); -	if ((pfindData == NULL) && (*pinode != NULL)) { -		if (CIFS_I(*pinode)->clientCanCacheRead) { -			cFYI(1, "No need to revalidate cached inode sizes"); +	if ((data == NULL) && (*inode != NULL)) { +		if (CIFS_CACHE_READ(CIFS_I(*inode))) { +			cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");  			goto cgii_exit;  		}  	} -	/* if file info not passed in then get it from server */ -	if (pfindData == NULL) { +	/* if inode info is not passed, get it from server */ +	if (data == NULL) { +		if (!server->ops->query_path_info) { +			rc = -ENOSYS; +			goto cgii_exit; +		}  		buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);  		if (buf == NULL) {  			rc = -ENOMEM;  			goto cgii_exit;  		} -		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; -		} +		data = (FILE_ALL_INFO *)buf; +		rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, +						  data, &adjust_tz, &symlink);  	}  	if (!rc) { -		cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *) pfindData, -				       cifs_sb, adjustTZ); +		cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz, +				       symlink);  	} else if (rc == -EREMOTE) {  		cifs_create_dfs_fattr(&fattr, sb);  		rc = 0; -	} else { +	} else if (rc == -EACCES && backup_cred(cifs_sb)) { +			srchinf = kzalloc(sizeof(struct cifs_search_info), +						GFP_KERNEL); +			if (srchinf == NULL) { +				rc = -ENOMEM; +				goto cgii_exit; +			} + +			srchinf->endOfSearch = false; +			srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + +			srchflgs = CIFS_SEARCH_CLOSE_ALWAYS | +					CIFS_SEARCH_CLOSE_AT_END | +					CIFS_SEARCH_BACKUP_SEARCH; + +			rc = CIFSFindFirst(xid, tcon, full_path, +				cifs_sb, NULL, srchflgs, srchinf, false); +			if (!rc) { +				data = +				(FILE_ALL_INFO *)srchinf->srch_entries_start; + +				cifs_dir_info_to_fattr(&fattr, +				(FILE_DIRECTORY_INFO *)data, cifs_sb); +				fattr.cf_uniqueid = le64_to_cpu( +				((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId); +				validinum = true; + +				cifs_buf_release(srchinf->ntwrk_buf_start); +			} +			kfree(srchinf); +	} else  		goto cgii_exit; -	}  	/*  	 * If an inode wasn't passed in, then get the inode number @@ -646,53 +772,45 @@ int cifs_get_inode_info(struct inode **pinode,  	 * 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. The SNIA -	 * CIFS spec claims that this value is unique within the scope of a -	 * share, and the windows docs hint that it's actually unique -	 * per-machine. -	 * -	 * There may be higher info levels that work but are there Windows -	 * server or network appliances for which IndexNumber field is not -	 * guaranteed unique?  	 */ -	if (*pinode == NULL) { +	if (*inode == NULL) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { -			int rc1 = 0; - -			rc1 = CIFSGetSrvInodeNumber(xid, pTcon, -					full_path, &fattr.cf_uniqueid, -					cifs_sb->local_nls, -					cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); -			if (rc1 || !fattr.cf_uniqueid) { -				cFYI(1, "GetSrvInodeNum rc %d", rc1); -				fattr.cf_uniqueid = iunique(sb, ROOT_I); -				cifs_autodisable_serverino(cifs_sb); +			if (validinum == false) { +				if (server->ops->get_srv_inum) +					tmprc = server->ops->get_srv_inum(xid, +						tcon, cifs_sb, full_path, +						&fattr.cf_uniqueid, data); +				if (tmprc) { +					cifs_dbg(FYI, "GetSrvInodeNum rc %d\n", +						 tmprc); +					fattr.cf_uniqueid = iunique(sb, ROOT_I); +					cifs_autodisable_serverino(cifs_sb); +				}  			} -		} else { +		} else  			fattr.cf_uniqueid = iunique(sb, ROOT_I); -		} -	} else { -		fattr.cf_uniqueid = CIFS_I(*pinode)->uniqueid; -	} +	} else +		fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;  	/* query for SFU type info if supported and needed */  	if (fattr.cf_cifsattrs & ATTR_SYSTEM &&  	    cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {  		tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid);  		if (tmprc) -			cFYI(1, "cifs_sfu_type failed: %d", tmprc); +			cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc);  	} -#ifdef CONFIG_CIFS_EXPERIMENTAL +#ifdef CONFIG_CIFS_ACL  	/* fill in 0777 bits from ACL */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { -		cFYI(1, "Getting mode bits from ACL"); -		cifs_acl_to_fattr(cifs_sb, &fattr, *pinode, full_path, pfid); +		rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, full_path, fid); +		if (rc) { +			cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", +				 __func__, rc); +			goto cgii_exit; +		}  	} -#endif +#endif /* CONFIG_CIFS_ACL */  	/* fill in remaining high mode bits e.g. SUID, VTX */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) @@ -700,17 +818,18 @@ int cifs_get_inode_info(struct inode **pinode,  	/* check for Minshall+French symlinks */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { -		tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); +		tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, +					 full_path);  		if (tmprc) -			cFYI(1, "CIFSCheckMFSymlink: %d", tmprc); +			cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);  	} -	if (!*pinode) { -		*pinode = cifs_iget(sb, &fattr); -		if (!*pinode) +	if (!*inode) { +		*inode = cifs_iget(sb, &fattr); +		if (!*inode)  			rc = -ENOMEM;  	} else { -		cifs_fattr_to_inode(*pinode, &fattr); +		cifs_fattr_to_inode(*inode, &fattr);  	}  cgii_exit: @@ -723,48 +842,6 @@ static const struct inode_operations cifs_ipc_inode_ops = {  	.lookup = cifs_lookup,  }; -char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) -{ -	int pplen = cifs_sb->prepathlen; -	int dfsplen; -	char *full_path = NULL; -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); - -	/* 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 (tcon->Flags & SMB_SHARE_IS_IN_DFS) -		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); -	else -		dfsplen = 0; - -	full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); -	if (full_path == NULL) -		return full_path; - -	if (dfsplen) { -		strncpy(full_path, tcon->treeName, dfsplen); -		/* 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; -} -  static int  cifs_find_inode(struct inode *inode, void *opaque)  { @@ -774,12 +851,16 @@ cifs_find_inode(struct inode *inode, void *opaque)  	if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)  		return 0; +	/* use createtime like an i_generation field */ +	if (CIFS_I(inode)->createtime != fattr->cf_createtime) +		return 0; +  	/* don't match inode of different type */  	if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))  		return 0;  	/* if it's not a directory or has no dentries, then flag it */ -	if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) +	if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry))  		fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;  	return 1; @@ -791,6 +872,7 @@ cifs_init_inode(struct inode *inode, void *opaque)  	struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;  	CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; +	CIFS_I(inode)->createtime = fattr->cf_createtime;  	return 0;  } @@ -804,14 +886,14 @@ inode_has_hashed_dentries(struct inode *inode)  {  	struct dentry *dentry; -	spin_lock(&dcache_lock); -	list_for_each_entry(dentry, &inode->i_dentry, d_alias) { +	spin_lock(&inode->i_lock); +	hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {  		if (!d_unhashed(dentry) || IS_ROOT(dentry)) { -			spin_unlock(&dcache_lock); +			spin_unlock(&inode->i_lock);  			return true;  		}  	} -	spin_unlock(&dcache_lock); +	spin_unlock(&inode->i_lock);  	return false;  } @@ -823,7 +905,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)  	struct inode *inode;  retry_iget5_locked: -	cFYI(1, "looking for uniqueid=%llu", fattr->cf_uniqueid); +	cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid);  	/* hash down to 32-bits on 32-bit arch */  	hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); @@ -861,28 +943,24 @@ retry_iget5_locked:  }  /* gets root inode */ -struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) +struct inode *cifs_root_iget(struct super_block *sb)  { -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);  	struct inode *inode = NULL;  	long rc; -	char *full_path; -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); - -	full_path = cifs_build_path_to_root(cifs_sb); -	if (full_path == NULL) -		return ERR_PTR(-ENOMEM); +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); -	xid = GetXid(); +	xid = get_xid();  	if (tcon->unix_ext) -		rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); +		rc = cifs_get_inode_info_unix(&inode, "", sb, xid);  	else -		rc = cifs_get_inode_info(&inode, full_path, NULL, sb, -						xid, NULL); +		rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL); -	if (!inode) -		return ERR_PTR(rc); +	if (!inode) { +		inode = ERR_PTR(rc); +		goto out; +	}  #ifdef CONFIG_CIFS_FSCACHE  	/* populate tcon->resource_id */ @@ -890,48 +968,44 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)  #endif  	if (rc && tcon->ipc) { -		cFYI(1, "ipc connection - fake read inode"); +		cifs_dbg(FYI, "ipc connection - fake read inode\n"); +		spin_lock(&inode->i_lock);  		inode->i_mode |= S_IFDIR; -		inode->i_nlink = 2; +		set_nlink(inode, 2);  		inode->i_op = &cifs_ipc_inode_ops;  		inode->i_fop = &simple_dir_operations;  		inode->i_uid = cifs_sb->mnt_uid;  		inode->i_gid = cifs_sb->mnt_gid; +		spin_unlock(&inode->i_lock);  	} else if (rc) { -		kfree(full_path); -		_FreeXid(xid);  		iget_failed(inode); -		return ERR_PTR(rc); +		inode = ERR_PTR(rc);  	} - -	kfree(full_path); -	/* can not call macro FreeXid here since in a void func +out: +	/* can not call macro free_xid here since in a void func  	 * TODO: This is no longer true  	 */ -	_FreeXid(xid); +	_free_xid(xid);  	return inode;  } -static int -cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, -		    char *full_path, __u32 dosattr) +int +cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned 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 tcon_link *tlink = NULL; -	struct cifsTconInfo *pTcon; +	struct TCP_Server_Info *server;  	FILE_BASIC_INFO	info_buf;  	if (attrs == NULL)  		return -EINVAL; +	server = cifs_sb_master_tcon(cifs_sb)->ses->server; +	if (!server->ops->set_file_info) +		return -ENOSYS; +  	if (attrs->ia_valid & ATTR_ATIME) {  		set_time = true;  		info_buf.LastAccessTime = @@ -953,7 +1027,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,  	 * server times.  	 */  	if (set_time && (attrs->ia_valid & ATTR_CTIME)) { -		cFYI(1, "CIFS - CTIME changed"); +		cifs_dbg(FYI, "CIFS - CTIME changed\n");  		info_buf.ChangeTime =  		    cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));  	} else @@ -962,89 +1036,27 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,  	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, true); -	if (open_file) { -		netfid = open_file->netfid; -		netpid = open_file->pid; -		pTcon = tlink_tcon(open_file->tlink); -		goto set_via_filehandle; -	} - -	tlink = cifs_sb_tlink(cifs_sb); -	if (IS_ERR(tlink)) { -		rc = PTR_ERR(tlink); -		tlink = NULL; -		goto out; -	} -	pTcon = tlink_tcon(tlink); - -	/* -	 * 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 -		cifsFileInfo_put(open_file); -out: -	if (tlink != NULL) -		cifs_put_tlink(tlink); -	return rc; +	return server->ops->set_file_info(inode, full_path, &info_buf, xid);  }  /* - * open the given file (if it isn't already), set the DELETE_ON_CLOSE bit + * 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 +cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, +			   const unsigned int xid)  {  	int oplock = 0;  	int rc; -	__u16 netfid; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	struct inode *inode = dentry->d_inode;  	struct cifsInodeInfo *cifsInode = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	__u32 dosattr, origattr;  	FILE_BASIC_INFO *info_buf = NULL; @@ -1053,10 +1065,25 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); -	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); +	/* +	 * We cannot rename the file if the server doesn't support +	 * CAP_INFOLEVEL_PASSTHRU +	 */ +	if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) { +		rc = -EBUSY; +		goto out; +	} + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = DELETE | FILE_WRITE_ATTRIBUTES; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc != 0)  		goto out; @@ -1077,28 +1104,29 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)  			goto out_close;  		}  		info_buf->Attributes = cpu_to_le32(dosattr); -		rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, +		rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,  					current->tgid);  		/* although we would like to mark the file hidden   		   if that fails we will still try to rename it */ -		if (rc != 0) +		if (!rc)  			cifsInode->cifsAttrs = dosattr;  		else  			dosattr = origattr; /* since not able to change them */  	}  	/* rename the file */ -	rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls, +	rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, +				   cifs_sb->local_nls,  				   cifs_sb->mnt_cifs_flags &  					    CIFS_MOUNT_MAP_SPECIAL_CHR);  	if (rc != 0) { -		rc = -ETXTBSY; +		rc = -EBUSY;  		goto undo_setattr;  	}  	/* try to set DELETE_ON_CLOSE */ -	if (!cifsInode->delete_pending) { -		rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, +	if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { +		rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid,  					       current->tgid);  		/*  		 * some samba versions return -ENOENT when we try to set the @@ -1111,14 +1139,14 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)  		if (rc == -ENOENT)  			rc = 0;  		else if (rc != 0) { -			rc = -ETXTBSY; +			rc = -EBUSY;  			goto undo_rename;  		} -		cifsInode->delete_pending = true; +		set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);  	}  out_close: -	CIFSSMBClose(xid, tcon, netfid); +	CIFSSMBClose(xid, tcon, fid.netfid);  out:  	kfree(info_buf);  	cifs_put_tlink(tlink); @@ -1130,13 +1158,13 @@ out:  	 * them anyway.  	 */  undo_rename: -	CIFSSMBRenameOpenFile(xid, tcon, netfid, dentry->d_name.name, +	CIFSSMBRenameOpenFile(xid, tcon, fid.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, +		if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,  					current->tgid))  			cifsInode->cifsAttrs = origattr;  	} @@ -1144,6 +1172,15 @@ undo_setattr:  	goto out_close;  } +/* copied from fs/nfs/dir.c with small changes */ +static void +cifs_drop_nlink(struct inode *inode) +{ +	spin_lock(&inode->i_lock); +	if (inode->i_nlink > 0) +		drop_nlink(inode); +	spin_unlock(&inode->i_lock); +}  /*   * If dentry->d_inode is null (usually meaning the cached dentry @@ -1155,25 +1192,27 @@ undo_setattr:  int cifs_unlink(struct inode *dir, struct dentry *dentry)  {  	int rc = 0; -	int xid; +	unsigned int xid;  	char *full_path = NULL;  	struct inode *inode = dentry->d_inode;  	struct cifsInodeInfo *cifs_inode;  	struct super_block *sb = dir->i_sb;  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);  	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server;  	struct iattr *attrs = NULL;  	__u32 dosattr = 0, origattr = 0; -	cFYI(1, "cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry); +	cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink))  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); +	server = tcon->ses->server; -	xid = GetXid(); +	xid = get_xid();  	/* Unlink can be called from rename so we can not take the  	 * sb->s_vfs_rename_mutex here */ @@ -1183,31 +1222,37 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)  		goto unlink_out;  	} -	if ((tcon->ses->capabilities & CAP_UNIX) && -		(CIFS_UNIX_POSIX_PATH_OPS_CAP & -			le64_to_cpu(tcon->fsUnixInfo.Capability))) { +	if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & +				le64_to_cpu(tcon->fsUnixInfo.Capability))) {  		rc = CIFSPOSIXDelFile(xid, tcon, full_path,  			SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,  			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); -		cFYI(1, "posix del rc %d", rc); +		cifs_dbg(FYI, "posix del rc %d\n", 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); +	if (!server->ops->unlink) { +		rc = -ENOSYS; +		goto psx_del_no_retry; +	} + +	rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);  psx_del_no_retry:  	if (!rc) {  		if (inode) -			drop_nlink(inode); +			cifs_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 == -EBUSY) { +		if (server->ops->rename_pending_delete) { +			rc = server->ops->rename_pending_delete(full_path, +								dentry, xid); +			if (rc == 0) +				cifs_drop_nlink(inode); +		}  	} else if ((rc == -EACCES) && (dosattr == 0) && inode) {  		attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);  		if (attrs == NULL) { @@ -1249,31 +1294,168 @@ out_reval:  unlink_out:  	kfree(full_path);  	kfree(attrs); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  	return rc;  } -int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) +static int +cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, +		 const char *full_path, struct cifs_sb_info *cifs_sb, +		 struct cifs_tcon *tcon, const unsigned int xid)  { -	int rc = 0, tmprc; -	int xid; -	struct cifs_sb_info *cifs_sb; -	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; -	char *full_path = NULL; +	int rc = 0; +	struct inode *inode = NULL; + +	if (tcon->unix_ext) +		rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, +					      xid); +	else +		rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, +					 xid, NULL); + +	if (rc) +		return rc; + +	/* +	 * setting nlink not necessary except in cases where we failed to get it +	 * from the server or was set bogus. Also, since this is a brand new +	 * inode, no need to grab the i_lock before setting the i_nlink. +	 */ +	if (inode->i_nlink < 2) +		set_nlink(inode, 2); +	mode &= ~current_umask(); +	/* must turn on setgid bit if parent dir has it */ +	if (parent->i_mode & S_ISGID) +		mode |= S_ISGID; + +	if (tcon->unix_ext) { +		struct cifs_unix_set_info_args args = { +			.mode	= mode, +			.ctime	= NO_CHANGE_64, +			.atime	= NO_CHANGE_64, +			.mtime	= NO_CHANGE_64, +			.device	= 0, +		}; +		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { +			args.uid = current_fsuid(); +			if (parent->i_mode & S_ISGID) +				args.gid = parent->i_gid; +			else +				args.gid = current_fsgid(); +		} else { +			args.uid = INVALID_UID; /* no change */ +			args.gid = INVALID_GID; /* no change */ +		} +		CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, +				       cifs_sb->local_nls, +				       cifs_sb->mnt_cifs_flags & +				       CIFS_MOUNT_MAP_SPECIAL_CHR); +	} else { +		struct TCP_Server_Info *server = tcon->ses->server; +		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && +		    (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo) +			server->ops->mkdir_setinfo(inode, full_path, cifs_sb, +						   tcon, xid); +		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) +			inode->i_mode = (mode | S_IFDIR); + +		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { +			inode->i_uid = current_fsuid(); +			if (inode->i_mode & S_ISGID) +				inode->i_gid = parent->i_gid; +			else +				inode->i_gid = current_fsgid(); +		} +	} +	d_instantiate(dentry, inode); +	return rc; +} + +static int +cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode, +		 const char *full_path, struct cifs_sb_info *cifs_sb, +		 struct cifs_tcon *tcon, const unsigned int xid) +{ +	int rc = 0; +	u32 oplock = 0; +	FILE_UNIX_BASIC_INFO *info = NULL;  	struct inode *newinode = NULL;  	struct cifs_fattr fattr; -	cFYI(1, "In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode); +	info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); +	if (info == NULL) { +		rc = -ENOMEM; +		goto posix_mkdir_out; +	} + +	mode &= ~current_umask(); +	rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, +			     NULL /* netfid */, info, &oplock, full_path, +			     cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +			     CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (rc == -EOPNOTSUPP) +		goto posix_mkdir_out; +	else if (rc) { +		cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc); +		d_drop(dentry); +		goto posix_mkdir_out; +	} + +	if (info->Type == cpu_to_le32(-1)) +		/* no return info, go query for it */ +		goto posix_mkdir_get_info; +	/* +	 * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if +	 * need to set uid/gid. +	 */ + +	cifs_unix_basic_to_fattr(&fattr, info, cifs_sb); +	cifs_fill_uniqueid(inode->i_sb, &fattr); +	newinode = cifs_iget(inode->i_sb, &fattr); +	if (!newinode) +		goto posix_mkdir_get_info; + +	d_instantiate(dentry, newinode); + +#ifdef CONFIG_CIFS_DEBUG2 +	cifs_dbg(FYI, "instantiated dentry %p %s to inode %p\n", +		 dentry, dentry->d_name.name, newinode); + +	if (newinode->i_nlink != 2) +		cifs_dbg(FYI, "unexpected number of links %d\n", +			 newinode->i_nlink); +#endif + +posix_mkdir_out: +	kfree(info); +	return rc; +posix_mkdir_get_info: +	rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon, +			      xid); +	goto posix_mkdir_out; +} + +int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) +{ +	int rc = 0; +	unsigned int xid; +	struct cifs_sb_info *cifs_sb; +	struct tcon_link *tlink; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	char *full_path; + +	cifs_dbg(FYI, "In cifs_mkdir, mode = 0x%hx inode = 0x%p\n", +		 mode, inode);  	cifs_sb = CIFS_SB(inode->i_sb);  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -1281,161 +1463,39 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)  		goto mkdir_out;  	} -	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; +	if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & +				le64_to_cpu(tcon->fsUnixInfo.Capability))) { +		rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, +				      tcon, xid); +		if (rc != -EOPNOTSUPP)  			goto mkdir_out; -		} - -		mode &= ~current_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; - -			cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb); -			cifs_fill_uniqueid(inode->i_sb, &fattr); -			newinode = cifs_iget(inode->i_sb, &fattr); -			if (!newinode) { -				kfree(pInfo); -				goto mkdir_get_info; -			} +	} -			d_instantiate(direntry, newinode); +	server = tcon->ses->server; -#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); +	if (!server->ops->mkdir) { +		rc = -ENOSYS;  		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); +	rc = server->ops->mkdir(xid, tcon, full_path, cifs_sb);  	if (rc) { -		cFYI(1, "cifs_mkdir returned 0x%x", rc); +		cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", 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_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; -			} -			CIFSSMBUnixSetPathInfo(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(); -				} -			} -		} +		goto mkdir_out;  	} + +	rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, +			      xid);  mkdir_out: +	/* +	 * Force revalidate to get parent dir info when needed since cached +	 * attributes are invalid now. +	 */ +	CIFS_I(inode)->time = 0;  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  	return rc;  } @@ -1443,16 +1503,17 @@ mkdir_out:  int cifs_rmdir(struct inode *inode, struct dentry *direntry)  {  	int rc = 0; -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server;  	char *full_path = NULL;  	struct cifsInodeInfo *cifsInode; -	cFYI(1, "cifs_rmdir, inode = 0x%p", inode); +	cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -1466,14 +1527,19 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)  		rc = PTR_ERR(tlink);  		goto rmdir_exit;  	} -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink); +	server = tcon->ses->server; + +	if (!server->ops->rmdir) { +		rc = -ENOSYS; +		cifs_put_tlink(tlink); +		goto rmdir_exit; +	} -	rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls, -			  cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);  	cifs_put_tlink(tlink);  	if (!rc) { -		drop_nlink(inode);  		spin_lock(&direntry->d_inode->i_lock);  		i_size_write(direntry->d_inode, 0);  		clear_nlink(direntry->d_inode); @@ -1481,84 +1547,98 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)  	}  	cifsInode = CIFS_I(direntry->d_inode); -	cifsInode->time = 0;	/* force revalidate to go get info when -				   needed */ +	/* force revalidate to go get info when needed */ +	cifsInode->time = 0;  	cifsInode = CIFS_I(inode); -	cifsInode->time = 0;	/* force revalidate to get parent dir info -				   since cached search results now invalid */ +	/* +	 * Force revalidate to get parent dir info when needed since cached +	 * attributes are invalid now. +	 */ +	cifsInode->time = 0;  	direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =  		current_fs_time(inode->i_sb);  rmdir_exit:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	return rc;  }  static int -cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, -		struct dentry *to_dentry, const char *toPath) +cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, +	       const char *from_path, struct dentry *to_dentry, +	       const char *to_path)  {  	struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; -	__u16 srcfid; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	int oplock, rc;  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink); +	server = tcon->ses->server; + +	if (!server->ops->rename) +		return -ENOSYS;  	/* 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); +	rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb);  	/* -	 * don't bother with rename by filehandle unless file is busy and -	 * source Note that cross directory moves do not work with +	 * 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) +	if (rc == 0 || rc != -EBUSY)  		goto do_rename_exit;  	/* open-file renames don't work across directories */  	if (to_dentry->d_parent != from_dentry->d_parent)  		goto do_rename_exit; +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb;  	/* open the file to be renamed -- we need DELETE perms */ -	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); - +	oparms.desired_access = DELETE; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = from_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc == 0) { -		rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid, +		rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid,  				(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); +		CIFSSMBClose(xid, tcon, fid.netfid);  	}  do_rename_exit:  	cifs_put_tlink(tlink);  	return rc;  } -int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, -	struct inode *target_dir, struct dentry *target_dentry) +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; +	char *from_name = NULL; +	char *to_name = NULL;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	FILE_UNIX_BASIC_INFO *info_buf_source = NULL;  	FILE_UNIX_BASIC_INFO *info_buf_target; -	int xid, rc, tmprc; +	unsigned int xid; +	int rc, tmprc;  	cifs_sb = CIFS_SB(source_dir->i_sb);  	tlink = cifs_sb_tlink(cifs_sb); @@ -1566,31 +1646,31 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	/*  	 * 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) { +	from_name = build_path_from_dentry(source_dentry); +	if (from_name == NULL) {  		rc = -ENOMEM;  		goto cifs_rename_exit;  	} -	toName = build_path_from_dentry(target_dentry); -	if (toName == NULL) { +	to_name = build_path_from_dentry(target_dentry); +	if (to_name == NULL) {  		rc = -ENOMEM;  		goto cifs_rename_exit;  	} -	rc = cifs_do_rename(xid, source_dentry, fromName, -			    target_dentry, toName); +	rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, +			    to_name);  	if (rc == -EEXIST && tcon->unix_ext) {  		/* -		 * Are src and dst hardlinks of same inode? We can -		 * only tell with unix extensions enabled +		 * 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), @@ -1601,19 +1681,19 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,  		}  		info_buf_target = info_buf_source + 1; -		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName, -					info_buf_source, -					cifs_sb->local_nls, -					cifs_sb->mnt_cifs_flags & -					CIFS_MOUNT_MAP_SPECIAL_CHR); +		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name, +					     info_buf_source, +					     cifs_sb->local_nls, +					     cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR);  		if (tmprc != 0)  			goto unlink_target; -		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, toName, -					info_buf_target, -					cifs_sb->local_nls, -					cifs_sb->mnt_cifs_flags & -					CIFS_MOUNT_MAP_SPECIAL_CHR); +		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name, +					     info_buf_target, +					     cifs_sb->local_nls, +					     cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR);  		if (tmprc == 0 && (info_buf_source->UniqueId ==  				   info_buf_target->UniqueId)) { @@ -1621,8 +1701,11 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,  			rc = 0;  			goto cifs_rename_exit;  		} -	} /* else ... BB we could add the same check for Windows by -		     checking the UniqueId via FILE_INTERNAL_INFO */ +	} +	/* +	 * else ... BB we could add the same check for Windows by +	 * checking the UniqueId via FILE_INTERNAL_INFO +	 */  unlink_target:  	/* Try unlinking the target dentry if it's not negative */ @@ -1630,16 +1713,15 @@ unlink_target:  		tmprc = cifs_unlink(target_dir, target_dentry);  		if (tmprc)  			goto cifs_rename_exit; - -		rc = cifs_do_rename(xid, source_dentry, fromName, -				    target_dentry, toName); +		rc = cifs_do_rename(xid, source_dentry, from_name, +				    target_dentry, to_name);  	}  cifs_rename_exit:  	kfree(info_buf_source); -	kfree(fromName); -	kfree(toName); -	FreeXid(xid); +	kfree(from_name); +	kfree(to_name); +	free_xid(xid);  	cifs_put_tlink(tlink);  	return rc;  } @@ -1648,8 +1730,9 @@ static bool  cifs_inode_needs_reval(struct inode *inode)  {  	struct cifsInodeInfo *cifs_i = CIFS_I(inode); +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); -	if (cifs_i->clientCanCacheRead) +	if (CIFS_CACHE_READ(cifs_i))  		return false;  	if (!lookupCacheEnabled) @@ -1658,84 +1741,127 @@ cifs_inode_needs_reval(struct inode *inode)  	if (cifs_i->time == 0)  		return true; -	/* FIXME: the actimeo should be tunable */ -	if (time_after_eq(jiffies, cifs_i->time + HZ)) +	if (!cifs_sb->actimeo) +		return true; + +	if (!time_in_range(jiffies, cifs_i->time, +				cifs_i->time + cifs_sb->actimeo))  		return true;  	/* hardlinked files w/ noserverino get "special" treatment */ -	if (!(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&  	    S_ISREG(inode->i_mode) && inode->i_nlink != 1)  		return true;  	return false;  } -/* check invalid_mapping flag and zap the cache if it's set */ -static void +/* + * Zap the cache. Called when invalid_mapping flag is set. + */ +int  cifs_invalidate_mapping(struct inode *inode)  { -	int rc; -	struct cifsInodeInfo *cifs_i = CIFS_I(inode); - -	cifs_i->invalid_mapping = false; +	int rc = 0; -	/* write back any cached data */  	if (inode->i_mapping && inode->i_mapping->nrpages != 0) { -		rc = filemap_write_and_wait(inode->i_mapping); -		mapping_set_error(inode->i_mapping, rc); +		rc = invalidate_inode_pages2(inode->i_mapping); +		if (rc) +			cifs_dbg(VFS, "%s: could not invalidate inode %p\n", +				 __func__, inode);  	} -	invalidate_remote_inode(inode); +  	cifs_fscache_reset_inode_cookie(inode); +	return rc;  } -int cifs_revalidate_file(struct file *filp) +/** + * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks + * @word: long word containing the bit lock + */ +static int +cifs_wait_bit_killable(void *word) +{ +	if (fatal_signal_pending(current)) +		return -ERESTARTSYS; +	freezable_schedule_unsafe(); +	return 0; +} + +int +cifs_revalidate_mapping(struct inode *inode) +{ +	int rc; +	unsigned long *flags = &CIFS_I(inode)->flags; + +	rc = wait_on_bit_lock(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, +				TASK_KILLABLE); +	if (rc) +		return rc; + +	if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { +		rc = cifs_invalidate_mapping(inode); +		if (rc) +			set_bit(CIFS_INO_INVALID_MAPPING, flags); +	} + +	clear_bit_unlock(CIFS_INO_LOCK, flags); +	smp_mb__after_atomic(); +	wake_up_bit(flags, CIFS_INO_LOCK); + +	return rc; +} + +int +cifs_zap_mapping(struct inode *inode) +{ +	set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); +	return cifs_revalidate_mapping(inode); +} + +int cifs_revalidate_file_attr(struct file *filp)  {  	int rc = 0; -	struct inode *inode = filp->f_path.dentry->d_inode; +	struct inode *inode = file_inode(filp);  	struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data;  	if (!cifs_inode_needs_reval(inode)) -		goto check_inval; +		return rc;  	if (tlink_tcon(cfile->tlink)->unix_ext)  		rc = cifs_get_file_info_unix(filp);  	else  		rc = cifs_get_file_info(filp); -check_inval: -	if (CIFS_I(inode)->invalid_mapping) -		cifs_invalidate_mapping(inode); -  	return rc;  } -/* revalidate a dentry's inode attributes */ -int cifs_revalidate_dentry(struct dentry *dentry) +int cifs_revalidate_dentry_attr(struct dentry *dentry)  { -	int xid; +	unsigned int xid;  	int rc = 0; -	char *full_path = NULL;  	struct inode *inode = dentry->d_inode;  	struct super_block *sb = dentry->d_sb; +	char *full_path = NULL;  	if (inode == NULL)  		return -ENOENT; -	xid = GetXid(); -  	if (!cifs_inode_needs_reval(inode)) -		goto check_inval; +		return rc; + +	xid = get_xid();  	/* can not safely grab the rename sem here if rename calls revalidate  	   since that would deadlock */  	full_path = build_path_from_dentry(dentry);  	if (full_path == NULL) {  		rc = -ENOMEM; -		goto check_inval; +		goto out;  	} -	cFYI(1, "Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " -		 "jiffies %ld", full_path, inode, inode->i_count.counter, +	cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n", +		 full_path, inode, inode->i_count.counter,  		 dentry, dentry->d_time, jiffies);  	if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) @@ -1744,41 +1870,80 @@ int cifs_revalidate_dentry(struct dentry *dentry)  		rc = cifs_get_inode_info(&inode, full_path, NULL, sb,  					 xid, NULL); -check_inval: -	if (CIFS_I(inode)->invalid_mapping) -		cifs_invalidate_mapping(inode); - +out:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	return rc;  } +int cifs_revalidate_file(struct file *filp) +{ +	int rc; +	struct inode *inode = file_inode(filp); + +	rc = cifs_revalidate_file_attr(filp); +	if (rc) +		return rc; + +	return cifs_revalidate_mapping(inode); +} + +/* revalidate a dentry's inode attributes */ +int cifs_revalidate_dentry(struct dentry *dentry) +{ +	int rc; +	struct inode *inode = dentry->d_inode; + +	rc = cifs_revalidate_dentry_attr(dentry); +	if (rc) +		return rc; + +	return cifs_revalidate_mapping(inode); +} +  int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,  		 struct kstat *stat)  {  	struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); -	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); -	int err = cifs_revalidate_dentry(dentry); - -	if (!err) { -		generic_fillattr(dentry->d_inode, stat); -		stat->blksize = CIFS_MAX_MSGSIZE; -		stat->ino = CIFS_I(dentry->d_inode)->uniqueid; +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); +	struct inode *inode = dentry->d_inode; +	int rc; -		/* -		 * If on a multiuser mount without unix extensions, and the -		 * admin hasn't overridden them, set the ownership to the -		 * fsuid/fsgid of the current process. -		 */ -		if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && -		    !tcon->unix_ext) { -			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) -				stat->uid = current_fsuid(); -			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) -				stat->gid = current_fsgid(); +	/* +	 * We need to be sure that all dirty pages are written and the server +	 * has actual ctime, mtime and file length. +	 */ +	if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && +	    inode->i_mapping->nrpages != 0) { +		rc = filemap_fdatawait(inode->i_mapping); +		if (rc) { +			mapping_set_error(inode->i_mapping, rc); +			return rc;  		}  	} -	return err; + +	rc = cifs_revalidate_dentry_attr(dentry); +	if (rc) +		return rc; + +	generic_fillattr(inode, stat); +	stat->blksize = CIFS_MAX_MSGSIZE; +	stat->ino = CIFS_I(inode)->uniqueid; + +	/* +	 * If on a multiuser mount without unix extensions or cifsacl being +	 * enabled, and the admin hasn't overridden them, set the ownership +	 * to the fsuid/fsgid of the current process. +	 */ +	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && +	    !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && +	    !tcon->unix_ext) { +		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) +			stat->uid = current_fsuid(); +		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) +			stat->gid = current_fsgid(); +	} +	return rc;  }  static int cifs_truncate_page(struct address_space *mapping, loff_t from) @@ -1800,26 +1965,25 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from)  static void cifs_setsize(struct inode *inode, loff_t offset)  { -	loff_t oldsize; -  	spin_lock(&inode->i_lock); -	oldsize = inode->i_size;  	i_size_write(inode, offset);  	spin_unlock(&inode->i_lock); -	truncate_pagecache(inode, oldsize, offset); +	truncate_pagecache(inode, offset);  }  static int  cifs_set_file_size(struct inode *inode, struct iattr *attrs, -		   int xid, char *full_path) +		   unsigned int xid, char *full_path)  {  	int rc;  	struct cifsFileInfo *open_file;  	struct cifsInodeInfo *cifsInode = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink = NULL; -	struct cifsTconInfo *pTcon = NULL; +	struct cifs_tcon *tcon = NULL; +	struct TCP_Server_Info *server; +	struct cifs_io_parms io_parms;  	/*  	 * To avoid spurious oplock breaks from server, in the case of @@ -1832,63 +1996,79 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,  	 */  	open_file = find_writable_file(cifsInode, true);  	if (open_file) { -		__u16 nfid = open_file->netfid; -		__u32 npid = open_file->pid; -		pTcon = tlink_tcon(open_file->tlink); -		rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, -					npid, false); +		tcon = tlink_tcon(open_file->tlink); +		server = tcon->ses->server; +		if (server->ops->set_file_size) +			rc = server->ops->set_file_size(xid, tcon, open_file, +							attrs->ia_size, false); +		else +			rc = -ENOSYS;  		cifsFileInfo_put(open_file); -		cFYI(1, "SetFSize for attrs rc = %d", rc); +		cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", 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); + +			io_parms.netfid = open_file->fid.netfid; +			io_parms.pid = open_file->pid; +			io_parms.tcon = tcon; +			io_parms.offset = 0; +			io_parms.length = attrs->ia_size; +			rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, +					  NULL, NULL, 1); +			cifs_dbg(FYI, "Wrt seteof rc %d\n", rc);  		}  	} else  		rc = -EINVAL; -	if (rc != 0) { -		if (pTcon == NULL) { -			tlink = cifs_sb_tlink(cifs_sb); -			if (IS_ERR(tlink)) -				return PTR_ERR(tlink); -			pTcon = tlink_tcon(tlink); -		} +	if (!rc) +		goto set_size_out; + +	if (tcon == NULL) { +		tlink = cifs_sb_tlink(cifs_sb); +		if (IS_ERR(tlink)) +			return PTR_ERR(tlink); +		tcon = tlink_tcon(tlink); +		server = tcon->ses->server; +	} -		/* Set file size by pathname rather than by handle -		   either because no valid, writeable file handle for -		   it was found or because there was an error setting -		   it by handle */ -		rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, -				   false, cifs_sb->local_nls, +	/* +	 * Set file size by pathname rather than by handle either because no +	 * valid, writeable file handle for it was found or because there was +	 * an error setting it by handle. +	 */ +	if (server->ops->set_path_size) +		rc = server->ops->set_path_size(xid, tcon, full_path, +						attrs->ia_size, cifs_sb, false); +	else +		rc = -ENOSYS; +	cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); +	if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { +		__u16 netfid; +		int oplock = 0; + +		rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN, +				   GENERIC_WRITE, CREATE_NOT_DIR, &netfid, +				   &oplock, NULL, cifs_sb->local_nls,  				   cifs_sb->mnt_cifs_flags & -					CIFS_MOUNT_MAP_SPECIAL_CHR); -		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); -			} +						CIFS_MOUNT_MAP_SPECIAL_CHR); +		if (rc == 0) { +			unsigned int bytes_written; + +			io_parms.netfid = netfid; +			io_parms.pid = current->tgid; +			io_parms.tcon = tcon; +			io_parms.offset = 0; +			io_parms.length = attrs->ia_size; +			rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, NULL, +					  NULL,  1); +			cifs_dbg(FYI, "wrt seteof rc %d\n", rc); +			CIFSSMBClose(xid, tcon, netfid);  		} -		if (tlink) -			cifs_put_tlink(tlink);  	} +	if (tlink) +		cifs_put_tlink(tlink); +set_size_out:  	if (rc == 0) {  		cifsInode->server_eof = attrs->ia_size;  		cifs_setsize(inode, attrs->ia_size); @@ -1902,20 +2082,20 @@ static int  cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)  {  	int rc; -	int xid; +	unsigned int xid;  	char *full_path = NULL;  	struct inode *inode = direntry->d_inode;  	struct cifsInodeInfo *cifsInode = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *pTcon;  	struct cifs_unix_set_info_args *args = NULL;  	struct cifsFileInfo *open_file; -	cFYI(1, "setattr_unix on file %s attrs->ia_valid=0x%x", +	cifs_dbg(FYI, "setattr_unix on file %s attrs->ia_valid=0x%x\n",  		 direntry->d_name.name, attrs->ia_valid); -	xid = GetXid(); +	xid = get_xid();  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)  		attrs->ia_valid |= ATTR_FORCE; @@ -1970,12 +2150,12 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)  	if (attrs->ia_valid & ATTR_UID)  		args->uid = attrs->ia_uid;  	else -		args->uid = NO_CHANGE_64; +		args->uid = INVALID_UID; /* no change */  	if (attrs->ia_valid & ATTR_GID)  		args->gid = attrs->ia_gid;  	else -		args->gid = NO_CHANGE_64; +		args->gid = INVALID_GID; /* no change */  	if (attrs->ia_valid & ATTR_ATIME)  		args->atime = cifs_UnixTimeToNT(attrs->ia_atime); @@ -1995,7 +2175,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)  	args->device = 0;  	open_file = find_writable_file(cifsInode, true);  	if (open_file) { -		u16 nfid = open_file->netfid; +		u16 nfid = open_file->fid.netfid;  		u32 npid = open_file->pid;  		pTcon = tlink_tcon(open_file->tlink);  		rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); @@ -2035,14 +2215,16 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)  out:  	kfree(args);  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	return rc;  }  static int  cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)  { -	int xid; +	unsigned int xid; +	kuid_t uid = INVALID_UID; +	kgid_t gid = INVALID_GID;  	struct inode *inode = direntry->d_inode;  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct cifsInodeInfo *cifsInode = CIFS_I(inode); @@ -2051,9 +2233,9 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)  	__u32 dosattr = 0;  	__u64 mode = NO_CHANGE_64; -	xid = GetXid(); +	xid = get_xid(); -	cFYI(1, "setattr on file %s attrs->iavalid 0x%x", +	cifs_dbg(FYI, "setattr on file %s attrs->iavalid 0x%x\n",  		 direntry->d_name.name, attrs->ia_valid);  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) @@ -2061,14 +2243,14 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)  	rc = inode_change_ok(inode, attrs);  	if (rc < 0) { -		FreeXid(xid); +		free_xid(xid);  		return rc;  	}  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) {  		rc = -ENOMEM; -		FreeXid(xid); +		free_xid(xid);  		return rc;  	} @@ -2093,13 +2275,25 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)  			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 (attrs->ia_valid & ATTR_UID) +		uid = attrs->ia_uid; + +	if (attrs->ia_valid & ATTR_GID) +		gid = attrs->ia_gid; + +#ifdef CONFIG_CIFS_ACL +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { +		if (uid_valid(uid) || gid_valid(gid)) { +			rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64, +							uid, gid); +			if (rc) { +				cifs_dbg(FYI, "%s: Setting id failed with error: %d\n", +					 __func__, rc); +				goto cifs_setattr_exit; +			} +		} +	} else +#endif /* CONFIG_CIFS_ACL */  	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID))  		attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); @@ -2108,17 +2302,19 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)  		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 +#ifdef CONFIG_CIFS_ACL +		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { +			rc = id_mode_to_cifs_acl(inode, full_path, mode, +						INVALID_UID, INVALID_GID); +			if (rc) { +				cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n", +					 __func__, rc); +				goto cifs_setattr_exit; +			} +		} else +#endif /* CONFIG_CIFS_ACL */  		if (((mode & S_IWUGO) == 0) &&  		    (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { @@ -2180,7 +2376,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)  cifs_setattr_exit:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	return rc;  } @@ -2189,7 +2385,7 @@ 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_master_tcon(cifs_sb); +	struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);  	if (pTcon->unix_ext)  		return cifs_setattr_unix(direntry, attrs); @@ -2202,7 +2398,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs)  #if 0  void cifs_delete_inode(struct inode *inode)  { -	cFYI(1, "In cifs_delete_inode, inode = 0x%p", inode); +	cifs_dbg(FYI, "In cifs_delete_inode, inode = 0x%p\n", inode);  	/* may have to add back in if and when safe distributed caching of  	   directories added e.g. via FindNotify */  } diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 0c98672d012..45cb59bcc79 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -3,7 +3,7 @@   *   *   vfs operations that deal with io control   * - *   Copyright (C) International Business Machines  Corp., 2005,2007 + *   Copyright (C) International Business Machines  Corp., 2005,2013   *   Author(s): Steve French (sfrench@us.ibm.com)   *   *   This library is free software; you can redistribute it and/or modify @@ -22,81 +22,207 @@   */  #include <linux/fs.h> +#include <linux/file.h> +#include <linux/mount.h> +#include <linux/mm.h> +#include <linux/pagemap.h>  #include "cifspdu.h"  #include "cifsglob.h"  #include "cifsproto.h"  #include "cifs_debug.h"  #include "cifsfs.h" -#define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2) +#define CIFS_IOCTL_MAGIC	0xCF +#define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int) + +static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, +			unsigned long srcfd, u64 off, u64 len, u64 destoff) +{ +	int rc; +	struct cifsFileInfo *smb_file_target = dst_file->private_data; +	struct inode *target_inode = file_inode(dst_file); +	struct cifs_tcon *target_tcon; +	struct fd src_file; +	struct cifsFileInfo *smb_file_src; +	struct inode *src_inode; +	struct cifs_tcon *src_tcon; + +	cifs_dbg(FYI, "ioctl clone range\n"); +	/* the destination must be opened for writing */ +	if (!(dst_file->f_mode & FMODE_WRITE)) { +		cifs_dbg(FYI, "file target not open for write\n"); +		return -EINVAL; +	} + +	/* check if target volume is readonly and take reference */ +	rc = mnt_want_write_file(dst_file); +	if (rc) { +		cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); +		return rc; +	} + +	src_file = fdget(srcfd); +	if (!src_file.file) { +		rc = -EBADF; +		goto out_drop_write; +	} + +	if ((!src_file.file->private_data) || (!dst_file->private_data)) { +		rc = -EBADF; +		cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); +		goto out_fput; +	} + +	rc = -EXDEV; +	smb_file_target = dst_file->private_data; +	smb_file_src = src_file.file->private_data; +	src_tcon = tlink_tcon(smb_file_src->tlink); +	target_tcon = tlink_tcon(smb_file_target->tlink); + +	/* check if source and target are on same tree connection */ +	if (src_tcon != target_tcon) { +		cifs_dbg(VFS, "file copy src and target on different volume\n"); +		goto out_fput; +	} + +	src_inode = file_inode(src_file.file); + +	/* +	 * Note: cifs case is easier than btrfs since server responsible for +	 * checks for proper open modes and file type and if it wants +	 * server could even support copy of range where source = target +	 */ + +	/* so we do not deadlock racing two ioctls on same files */ +	if (target_inode < src_inode) { +		mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT); +		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD); +	} else { +		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT); +		mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD); +	} + +	/* determine range to clone */ +	rc = -EINVAL; +	if (off + len > src_inode->i_size || off + len < off) +		goto out_unlock; +	if (len == 0) +		len = src_inode->i_size - off; + +	cifs_dbg(FYI, "about to flush pages\n"); +	/* should we flush first and last page first */ +	truncate_inode_pages_range(&target_inode->i_data, destoff, +				   PAGE_CACHE_ALIGN(destoff + len)-1); + +	if (target_tcon->ses->server->ops->clone_range) +		rc = target_tcon->ses->server->ops->clone_range(xid, +			smb_file_src, smb_file_target, off, len, destoff); + +	/* force revalidate of size and timestamps of target file now +	   that target is updated on the server */ +	CIFS_I(target_inode)->time = 0; +out_unlock: +	/* although unlocking in the reverse order from locking is not +	   strictly necessary here it is a little cleaner to be consistent */ +	if (target_inode < src_inode) { +		mutex_unlock(&src_inode->i_mutex); +		mutex_unlock(&target_inode->i_mutex); +	} else { +		mutex_unlock(&target_inode->i_mutex); +		mutex_unlock(&src_inode->i_mutex); +	} +out_fput: +	fdput(src_file); +out_drop_write: +	mnt_drop_write_file(dst_file); +	return rc; +}  long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)  { -	struct inode *inode = filep->f_dentry->d_inode; +	struct inode *inode = file_inode(filep);  	int rc = -ENOTTY; /* strange error - but the precedent */ -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb; -#ifdef CONFIG_CIFS_POSIX  	struct cifsFileInfo *pSMBFile = filep->private_data; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon;  	__u64	ExtAttrBits = 0; -	__u64	ExtAttrMask = 0;  	__u64   caps; -#endif /* CONFIG_CIFS_POSIX */ -	xid = GetXid(); +	xid = get_xid(); -	cFYI(1, "ioctl file %p  cmd %u  arg %lu", filep, command, arg); +	cifs_dbg(FYI, "ioctl file %p  cmd %u  arg %lu\n", filep, command, arg);  	cifs_sb = CIFS_SB(inode->i_sb);  	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 (pSMBFile == NULL)  				break;  			tcon = tlink_tcon(pSMBFile->tlink);  			caps = le64_to_cpu(tcon->fsUnixInfo.Capability); +#ifdef CONFIG_CIFS_POSIX  			if (CIFS_UNIX_EXTATTR_CAP & caps) { -				rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid, -					&ExtAttrBits, &ExtAttrMask); +				__u64	ExtAttrMask = 0; +				rc = CIFSGetExtAttr(xid, tcon, +						    pSMBFile->fid.netfid, +						    &ExtAttrBits, &ExtAttrMask);  				if (rc == 0)  					rc = put_user(ExtAttrBits &  						FS_FL_USER_VISIBLE,  						(int __user *)arg); +				if (rc != EOPNOTSUPP) +					break; +			} +#endif /* CONFIG_CIFS_POSIX */ +			rc = 0; +			if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { +				/* add in the compressed bit */ +				ExtAttrBits = FS_COMPR_FL; +				rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, +					      (int __user *)arg);  			}  			break; -  		case FS_IOC_SETFLAGS:  			if (pSMBFile == NULL)  				break;  			tcon = tlink_tcon(pSMBFile->tlink);  			caps = le64_to_cpu(tcon->fsUnixInfo.Capability); -			if (CIFS_UNIX_EXTATTR_CAP & caps) { -				if (get_user(ExtAttrBits, (int __user *)arg)) { -					rc = -EFAULT; -					break; -				} -				/* rc= CIFSGetExtAttr(xid,tcon,pSMBFile->netfid, -					extAttrBits, &ExtAttrMask);*/ + +			if (get_user(ExtAttrBits, (int __user *)arg)) { +				rc = -EFAULT; +				break; +			} + +			/* +			 * if (CIFS_UNIX_EXTATTR_CAP & caps) +			 *	rc = CIFSSetExtAttr(xid, tcon, +			 *		       pSMBFile->fid.netfid, +			 *		       extAttrBits, +			 *		       &ExtAttrMask); +			 * if (rc != EOPNOTSUPP) +			 *	break; +			 */ + +			/* Currently only flag we can set is compressed flag */ +			if ((ExtAttrBits & FS_COMPR_FL) == 0) +				break; + +			/* Try to set compress flag */ +			if (tcon->ses->server->ops->set_compression) { +				rc = tcon->ses->server->ops->set_compression( +							xid, tcon, pSMBFile); +				cifs_dbg(FYI, "set compress flag rc %d\n", rc);  			} -			cFYI(1, "set flags not implemented yet");  			break; -#endif /* CONFIG_CIFS_POSIX */ +		case CIFS_IOC_COPYCHUNK_FILE: +			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); +			break;  		default: -			cFYI(1, "unsupported ioctl"); +			cifs_dbg(FYI, "unsupported ioctl\n");  			break;  	} -	FreeXid(xid); +	free_xid(xid);  	return rc;  } diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 85cdbf831e7..68559fd557f 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -28,7 +28,10 @@  #include "cifsproto.h"  #include "cifs_debug.h"  #include "cifs_fs_sb.h" -#include "md5.h" + +/* + * M-F Symlink Functions - Begin + */  #define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)  #define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) @@ -47,16 +50,58 @@  	md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15]  static int -CIFSParseMFSymlink(const u8 *buf, -		   unsigned int buf_len, -		   unsigned int *_link_len, -		   char **_link_str) +symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) +{ +	int rc; +	unsigned int size; +	struct crypto_shash *md5; +	struct sdesc *sdescmd5; + +	md5 = crypto_alloc_shash("md5", 0, 0); +	if (IS_ERR(md5)) { +		rc = PTR_ERR(md5); +		cifs_dbg(VFS, "%s: Crypto md5 allocation error %d\n", +			 __func__, rc); +		return rc; +	} +	size = sizeof(struct shash_desc) + crypto_shash_descsize(md5); +	sdescmd5 = kmalloc(size, GFP_KERNEL); +	if (!sdescmd5) { +		rc = -ENOMEM; +		goto symlink_hash_err; +	} +	sdescmd5->shash.tfm = md5; +	sdescmd5->shash.flags = 0x0; + +	rc = crypto_shash_init(&sdescmd5->shash); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__); +		goto symlink_hash_err; +	} +	rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); +		goto symlink_hash_err; +	} +	rc = crypto_shash_final(&sdescmd5->shash, md5_hash); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + +symlink_hash_err: +	crypto_free_shash(md5); +	kfree(sdescmd5); + +	return rc; +} + +static int +parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, +		 char **_link_str)  {  	int rc;  	unsigned int link_len;  	const char *md5_str1;  	const char *link_str; -	struct MD5Context md5_ctx;  	u8 md5_hash[16];  	char md5_str2[34]; @@ -70,9 +115,11 @@ CIFSParseMFSymlink(const u8 *buf,  	if (rc != 1)  		return -EINVAL; -	cifs_MD5_init(&md5_ctx); -	cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); -	cifs_MD5_final(md5_hash, &md5_ctx); +	rc = symlink_hash(link_len, link_str, md5_hash); +	if (rc) { +		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); +		return rc; +	}  	snprintf(md5_str2, sizeof(md5_str2),  		 CIFS_MF_SYMLINK_MD5_FORMAT, @@ -92,11 +139,11 @@ CIFSParseMFSymlink(const u8 *buf,  }  static int -CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) +format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)  { +	int rc;  	unsigned int link_len;  	unsigned int ofs; -	struct MD5Context md5_ctx;  	u8 md5_hash[16];  	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) @@ -107,9 +154,11 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)  	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)  		return -ENAMETOOLONG; -	cifs_MD5_init(&md5_ctx); -	cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); -	cifs_MD5_final(md5_hash, &md5_ctx); +	rc = symlink_hash(link_len, link_str, md5_hash); +	if (rc) { +		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); +		return rc; +	}  	snprintf(buf, buf_len,  		 CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, @@ -133,14 +182,26 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)  	return 0;  } +bool +couldbe_mf_symlink(const struct cifs_fattr *fattr) +{ +	if (!S_ISREG(fattr->cf_mode)) +		/* it's not a symlink */ +		return false; + +	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) +		/* it's not a symlink */ +		return false; + +	return true; +} +  static int -CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon, -		    const char *fromName, const char *toName, -		    const struct nls_table *nls_codepage, int remap) +create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, +		  struct cifs_sb_info *cifs_sb, const char *fromName, +		  const char *toName)  {  	int rc; -	int oplock = 0; -	__u16 netfid = 0;  	u8 *buf;  	unsigned int bytes_written = 0; @@ -148,158 +209,87 @@ CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon,  	if (!buf)  		return -ENOMEM; -	rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); -	if (rc != 0) { -		kfree(buf); -		return rc; -	} - -	rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, -			 CREATE_NOT_DIR, &netfid, &oplock, NULL, -			 nls_codepage, remap); -	if (rc != 0) { -		kfree(buf); -		return rc; -	} +	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); +	if (rc) +		goto out; -	rc = CIFSSMBWrite(xid, tcon, netfid, -			  CIFS_MF_SYMLINK_FILE_SIZE /* length */, -			  0 /* offset */, -			  &bytes_written, buf, NULL, 0); -	CIFSSMBClose(xid, tcon, netfid); -	kfree(buf); -	if (rc != 0) -		return rc; +	rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, cifs_sb, +					fromName, buf, &bytes_written); +	if (rc) +		goto out;  	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) -		return -EIO; - -	return 0; +		rc = -EIO; +out: +	kfree(buf); +	return rc;  }  static int -CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, -		   const unsigned char *searchName, char **symlinkinfo, -		   const struct nls_table *nls_codepage, int remap) +query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, +		 struct cifs_sb_info *cifs_sb, const unsigned char *path, +		 char **symlinkinfo)  {  	int rc; -	int oplock = 0; -	__u16 netfid = 0; -	u8 *buf; -	char *pbuf; -	unsigned int bytes_read = 0; -	int buf_type = CIFS_NO_BUFFER; +	u8 *buf = NULL;  	unsigned int link_len = 0; -	FILE_ALL_INFO file_info; - -	rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, -			 CREATE_NOT_DIR, &netfid, &oplock, &file_info, -			 nls_codepage, remap); -	if (rc != 0) -		return rc; - -	if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { -		CIFSSMBClose(xid, tcon, netfid); -		/* it's not a symlink */ -		return -EINVAL; -	} +	unsigned int bytes_read = 0;  	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);  	if (!buf)  		return -ENOMEM; -	pbuf = buf; -	rc = CIFSSMBRead(xid, tcon, netfid, -			 CIFS_MF_SYMLINK_FILE_SIZE /* length */, -			 0 /* offset */, -			 &bytes_read, &pbuf, &buf_type); -	CIFSSMBClose(xid, tcon, netfid); -	if (rc != 0) { -		kfree(buf); -		return rc; -	} +	if (tcon->ses->server->ops->query_mf_symlink) +		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, +					      cifs_sb, path, buf, &bytes_read); +	else +		rc = -ENOSYS; -	rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo); -	kfree(buf); -	if (rc != 0) -		return rc; - -	return 0; -} - -bool -CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) -{ -	if (!(fattr->cf_mode & S_IFREG)) -		/* it's not a symlink */ -		return false; +	if (rc) +		goto out; -	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) -		/* it's not a symlink */ -		return false; +	if (bytes_read == 0) { /* not a symlink */ +		rc = -EINVAL; +		goto out; +	} -	return true; +	rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo); +out: +	kfree(buf); +	return rc;  }  int -CIFSCheckMFSymlink(struct cifs_fattr *fattr, -		   const unsigned char *path, -		   struct cifs_sb_info *cifs_sb, int xid) +check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, +		 const unsigned char *path)  {  	int rc; -	int oplock = 0; -	__u16 netfid = 0; -	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; -	u8 *buf; -	char *pbuf; -	unsigned int bytes_read = 0; -	int buf_type = CIFS_NO_BUFFER; +	u8 *buf = NULL;  	unsigned int link_len = 0; -	FILE_ALL_INFO file_info; +	unsigned int bytes_read = 0; -	if (!CIFSCouldBeMFSymlink(fattr)) +	if (!couldbe_mf_symlink(fattr))  		/* it's not a symlink */  		return 0; -	tlink = cifs_sb_tlink(cifs_sb); -	if (IS_ERR(tlink)) -		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); - -	rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, -			 CREATE_NOT_DIR, &netfid, &oplock, &file_info, -			 cifs_sb->local_nls, -			 cifs_sb->mnt_cifs_flags & -				CIFS_MOUNT_MAP_SPECIAL_CHR); -	if (rc != 0) -		goto out; +	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; -	if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { -		CIFSSMBClose(xid, pTcon, netfid); -		/* it's not a symlink */ -		goto out; -	} +	if (tcon->ses->server->ops->query_mf_symlink) +		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, +					      cifs_sb, path, buf, &bytes_read); +	else +		rc = -ENOSYS; -	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); -	if (!buf) { -		rc = -ENOMEM; +	if (rc)  		goto out; -	} -	pbuf = buf; -	rc = CIFSSMBRead(xid, pTcon, netfid, -			 CIFS_MF_SYMLINK_FILE_SIZE /* length */, -			 0 /* offset */, -			 &bytes_read, &pbuf, &buf_type); -	CIFSSMBClose(xid, pTcon, netfid); -	if (rc != 0) { -		kfree(buf); +	if (bytes_read == 0) /* not a symlink */  		goto out; -	} -	rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); -	kfree(buf); +	rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL);  	if (rc == -EINVAL) {  		/* it's not a symlink */  		rc = 0; @@ -315,81 +305,188 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr,  	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;  	fattr->cf_dtype = DT_LNK;  out: -	cifs_put_tlink(tlink); +	kfree(buf);  	return rc;  } +/* + * SMB 1.0 Protocol specific functions + */ + +int +cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +		      struct cifs_sb_info *cifs_sb, const unsigned char *path, +		      char *pbuf, unsigned int *pbytes_read) +{ +	int rc; +	int oplock = 0; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; +	struct cifs_io_parms io_parms; +	int buf_type = CIFS_NO_BUFFER; +	FILE_ALL_INFO file_info; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_READ; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, &file_info); +	if (rc) +		return rc; + +	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) +		/* it's not a symlink */ +		goto out; + +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + +	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); +out: +	CIFSSMBClose(xid, tcon, fid.netfid); +	return rc; +} + +int +cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +		       struct cifs_sb_info *cifs_sb, const unsigned char *path, +		       char *pbuf, unsigned int *pbytes_written) +{ +	int rc; +	int oplock = 0; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; +	struct cifs_io_parms io_parms; +	int create_options = CREATE_NOT_DIR; + +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_WRITE; +	oparms.create_options = create_options; +	oparms.disposition = FILE_CREATE; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc) +		return rc; + +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + +	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf, NULL, 0); +	CIFSSMBClose(xid, tcon, fid.netfid); +	return rc; +} + +/* + * M-F Symlink Functions - End + */ +  int  cifs_hardlink(struct dentry *old_file, struct inode *inode,  	      struct dentry *direntry)  {  	int rc = -EACCES; -	int xid; -	char *fromName = NULL; -	char *toName = NULL; +	unsigned int xid; +	char *from_name = NULL; +	char *to_name = NULL;  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server;  	struct cifsInodeInfo *cifsInode;  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid(); -	fromName = build_path_from_dentry(old_file); -	toName = build_path_from_dentry(direntry); -	if ((fromName == NULL) || (toName == NULL)) { +	from_name = build_path_from_dentry(old_file); +	to_name = build_path_from_dentry(direntry); +	if ((from_name == NULL) || (to_name == NULL)) {  		rc = -ENOMEM;  		goto cifs_hl_exit;  	} -	if (pTcon->unix_ext) -		rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, +	if (tcon->unix_ext) +		rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,  					    cifs_sb->local_nls,  					    cifs_sb->mnt_cifs_flags &  						CIFS_MOUNT_MAP_SPECIAL_CHR);  	else { -		rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, -					cifs_sb->local_nls, -					cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); +		server = tcon->ses->server; +		if (!server->ops->create_hardlink) { +			rc = -ENOSYS; +			goto cifs_hl_exit; +		} +		rc = server->ops->create_hardlink(xid, tcon, from_name, to_name, +						  cifs_sb);  		if ((rc == -EIO) || (rc == -EINVAL))  			rc = -EOPNOTSUPP;  	}  	d_drop(direntry);	/* force new lookup from server of target */ -	/* if source file is cached (oplocked) revalidate will not go to server -	   until the file is closed or oplock broken so update nlinks locally */ +	/* +	 * if 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? */ +			spin_lock(&old_file->d_inode->i_lock); +			inc_nlink(old_file->d_inode); +			spin_unlock(&old_file->d_inode->i_lock); +			/* +			 * BB should we make this contingent on superblock flag +			 * NOATIME? +			 */ +			/* old_file->d_inode->i_ctime = CURRENT_TIME; */ +			/* +			 * parent dir timestamps will update from srv within a +			 * second, would it really be worth it to set the parent +			 * dir cifs inode time to zero to force revalidate +			 * (faster) for it too? +			 */  		} -		/* if not oplocked will force revalidate to get info -		   on source file from srv */ +		/* +		 * 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;  */ +		/* +		 * 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); +	kfree(from_name); +	kfree(to_name); +	free_xid(xid);  	cifs_put_tlink(tlink);  	return rc;  } @@ -399,14 +496,15 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)  {  	struct inode *inode = direntry->d_inode;  	int rc = -ENOMEM; -	int xid; +	unsigned int xid;  	char *full_path = NULL;  	char *target_path = NULL;  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink = NULL; -	struct cifsTconInfo *tcon; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; -	xid = GetXid(); +	xid = get_xid();  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) { @@ -415,31 +513,13 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)  		goto out;  	}  	tcon = tlink_tcon(tlink); - -	/* -	 * For now, we just handle symlinks with unix extensions enabled. -	 * Eventually we should handle NTFS reparse points, and MacOS -	 * symlink support. For instance... -	 * -	 * rc = CIFSSMBQueryReparseLinkInfo(...) -	 * -	 * For now, just return -EACCES when the server doesn't support posix -	 * extensions. Note that we still allow querying symlinks when posix -	 * extensions are manually disabled. We could disable these as well -	 * but there doesn't seem to be any harm in allowing the client to -	 * read them. -	 */ -	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) -	    && !(tcon->ses->capabilities & CAP_UNIX)) { -		rc = -EACCES; -		goto out; -	} +	server = tcon->ses->server;  	full_path = build_path_from_dentry(direntry);  	if (!full_path)  		goto out; -	cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); +	cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);  	rc = -EACCES;  	/* @@ -447,14 +527,12 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)  	 * and fallback to UNIX Extensions Symlinks.  	 */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) -		rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, -					cifs_sb->local_nls, -					cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); +		rc = query_mf_symlink(xid, tcon, cifs_sb, full_path, +				      &target_path); -	if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) -		rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, -					     cifs_sb->local_nls); +	if (rc != 0 && server->ops->query_symlink) +		rc = server->ops->query_symlink(xid, tcon, full_path, +						&target_path, cifs_sb);  	kfree(full_path);  out: @@ -463,7 +541,7 @@ out:  		target_path = ERR_PTR(rc);  	} -	FreeXid(xid); +	free_xid(xid);  	if (tlink)  		cifs_put_tlink(tlink);  	nd_set_link(nd, target_path); @@ -474,14 +552,14 @@ int  cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)  {  	int rc = -EOPNOTSUPP; -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *pTcon;  	char *full_path = NULL;  	struct inode *newinode = NULL; -	xid = GetXid(); +	xid = get_xid();  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) { @@ -496,15 +574,12 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)  		goto symlink_exit;  	} -	cFYI(1, "Full path: %s", full_path); -	cFYI(1, "symname is %s", symname); +	cifs_dbg(FYI, "Full path: %s\n", full_path); +	cifs_dbg(FYI, "symname is %s\n", symname);  	/* BB what if DFS and this volume is on different share? BB */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) -		rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, -					 cifs_sb->local_nls, -					 cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); +		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);  	else if (pTcon->unix_ext)  		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,  					   cifs_sb->local_nls); @@ -521,26 +596,15 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)  						 inode->i_sb, xid, NULL);  		if (rc != 0) { -			cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d", -			      rc); +			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n", +				 rc);  		} else { -			if (pTcon->nocase) -				direntry->d_op = &cifs_ci_dentry_ops; -			else -				direntry->d_op = &cifs_dentry_ops;  			d_instantiate(direntry, newinode);  		}  	}  symlink_exit:  	kfree(full_path);  	cifs_put_tlink(tlink); -	FreeXid(xid); +	free_xid(xid);  	return rc;  } - -void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie) -{ -	char *p = nd_get_link(nd); -	if (!IS_ERR(p)) -		kfree(p); -} diff --git a/fs/cifs/md4.c b/fs/cifs/md4.c deleted file mode 100644 index a725c2609d6..00000000000 --- a/fs/cifs/md4.c +++ /dev/null @@ -1,205 +0,0 @@ -/* -   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 <linux/module.h> -#include <linux/fs.h> -#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; -} diff --git a/fs/cifs/md5.c b/fs/cifs/md5.c deleted file mode 100644 index 98b66a54c31..00000000000 --- a/fs/cifs/md5.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * 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 cifs_MD5_init, call cifs_MD5_update as - * needed on buffers full of bytes, and then call cifs_MD5_final, 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 <linux/string.h> -#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 -cifs_MD5_init(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 -cifs_MD5_update(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 -cifs_MD5_final(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<<s | 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.  cifs_MD5_update 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; - -		cifs_MD5_init(&tctx); -		cifs_MD5_update(&tctx, key, key_len); -		cifs_MD5_final(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; -	} - -	cifs_MD5_init(&ctx->ctx); -	cifs_MD5_update(&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; -	} - -	cifs_MD5_init(&ctx->ctx); -	cifs_MD5_update(&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) -{ -	cifs_MD5_update(&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; - -	cifs_MD5_final(digest, &ctx->ctx); - -	cifs_MD5_init(&ctx_o); -	cifs_MD5_update(&ctx_o, ctx->k_opad, 64); -	cifs_MD5_update(&ctx_o, digest, 16); -	cifs_MD5_final(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 diff --git a/fs/cifs/md5.h b/fs/cifs/md5.h deleted file mode 100644 index 6fba8cb402f..00000000000 --- a/fs/cifs/md5.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 cifs_MD5_init(struct MD5Context *context); -void cifs_MD5_update(struct MD5Context *context, unsigned char const *buf, -			unsigned len); -void cifs_MD5_final(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);*/ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 43f10281bc1..3b0c62e622d 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -29,6 +29,9 @@  #include "smberr.h"  #include "nterr.h"  #include "cifs_unicode.h" +#ifdef CONFIG_CIFS_SMB2 +#include "smb2pdu.h" +#endif  extern mempool_t *cifs_sm_req_poolp;  extern mempool_t *cifs_req_poolp; @@ -40,7 +43,7 @@ extern mempool_t *cifs_req_poolp;     since the cifs fs was mounted */  unsigned int -_GetXid(void) +_get_xid(void)  {  	unsigned int xid; @@ -51,14 +54,14 @@ _GetXid(void)  	if (GlobalTotalActiveXid > GlobalMaxActiveXid)  		GlobalMaxActiveXid = GlobalTotalActiveXid;  	if (GlobalTotalActiveXid > 65000) -		cFYI(1, "warning: more than 65000 requests active"); +		cifs_dbg(FYI, "warning: more than 65000 requests active\n");  	xid = GlobalCurrentXid++;  	spin_unlock(&GlobalMid_Lock);  	return xid;  }  void -_FreeXid(unsigned int xid) +_free_xid(unsigned int xid)  {  	spin_lock(&GlobalMid_Lock);  	/* if (GlobalTotalActiveXid == 0) @@ -67,12 +70,12 @@ _FreeXid(unsigned int xid)  	spin_unlock(&GlobalMid_Lock);  } -struct cifsSesInfo * +struct cifs_ses *  sesInfoAlloc(void)  { -	struct cifsSesInfo *ret_buf; +	struct cifs_ses *ret_buf; -	ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); +	ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL);  	if (ret_buf) {  		atomic_inc(&sesInfoAllocCount);  		ret_buf->status = CifsNew; @@ -85,10 +88,10 @@ sesInfoAlloc(void)  }  void -sesInfoFree(struct cifsSesInfo *buf_to_free) +sesInfoFree(struct cifs_ses *buf_to_free)  {  	if (buf_to_free == NULL) { -		cFYI(1, "Null buffer passed to sesInfoFree"); +		cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");  		return;  	} @@ -100,15 +103,17 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)  		memset(buf_to_free->password, 0, strlen(buf_to_free->password));  		kfree(buf_to_free->password);  	} +	kfree(buf_to_free->user_name);  	kfree(buf_to_free->domainName); +	kfree(buf_to_free->auth_key.response);  	kfree(buf_to_free);  } -struct cifsTconInfo * +struct cifs_tcon *  tconInfoAlloc(void)  { -	struct cifsTconInfo *ret_buf; -	ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); +	struct cifs_tcon *ret_buf; +	ret_buf = kzalloc(sizeof(struct cifs_tcon), GFP_KERNEL);  	if (ret_buf) {  		atomic_inc(&tconInfoAllocCount);  		ret_buf->tidStatus = CifsNew; @@ -123,10 +128,10 @@ tconInfoAlloc(void)  }  void -tconInfoFree(struct cifsTconInfo *buf_to_free) +tconInfoFree(struct cifs_tcon *buf_to_free)  {  	if (buf_to_free == NULL) { -		cFYI(1, "Null buffer passed to tconInfoFree"); +		cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");  		return;  	}  	atomic_dec(&tconInfoAllocCount); @@ -142,17 +147,27 @@ 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 */ +	size_t buf_size = sizeof(struct smb_hdr); + +#ifdef CONFIG_CIFS_SMB2 +	/* +	 * SMB2 header is bigger than CIFS one - no problems to clean some +	 * more bytes for CIFS. +	 */ +	buf_size = sizeof(struct smb2_hdr); +#endif +	/* +	 * We could use negotiated size instead of max_msgsize - +	 * but it may be more efficient to always alloc same size +	 * albeit slightly larger than necessary and maxbuffersize +	 * defaults to this and can not be bigger. +	 */  	ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);  	/* clear the first few header bytes */  	/* for most paths, more is cleared in header_assemble */  	if (ret_buf) { -		memset(ret_buf, 0, sizeof(struct smb_hdr) + 3); +		memset(ret_buf, 0, buf_size + 3);  		atomic_inc(&bufAllocCount);  #ifdef CONFIG_CIFS_STATS2  		atomic_inc(&totBufAllocCount); @@ -166,7 +181,7 @@ void  cifs_buf_release(void *buf_to_free)  {  	if (buf_to_free == NULL) { -		/* cFYI(1, "Null buffer passed to cifs_buf_release");*/ +		/* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/  		return;  	}  	mempool_free(buf_to_free, cifs_req_poolp); @@ -202,7 +217,7 @@ cifs_small_buf_release(void *buf_to_free)  {  	if (buf_to_free == NULL) { -		cFYI(1, "Null buffer passed to cifs_small_buf_release"); +		cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n");  		return;  	}  	mempool_free(buf_to_free, cifs_sm_req_poolp); @@ -211,93 +226,21 @@ cifs_small_buf_release(void *buf_to_free)  	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 +		const struct cifs_tcon *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 = +	buffer->smb_buf_length = cpu_to_be32(  	    (2 * word_count) + sizeof(struct smb_hdr) -  	    4 /*  RFC 1001 length field does not count */  + -	    2 /* for bcc field itself */ ; -	/* 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 */ +	    2 /* for bcc field itself */) ;  	buffer->Protocol[0] = 0xFF;  	buffer->Protocol[1] = 'S'; @@ -318,60 +261,14 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,  			/* 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"); -					spin_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"); -							} -						} -					} -					spin_unlock(&cifs_tcp_ses_lock); -				} -			} +			buffer->Mid = get_next_mid(treeCon->ses->server);  		}  		if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)  			buffer->Flags2 |= SMBFLG2_DFS;  		if (treeCon->nocase)  			buffer->Flags  |= SMBFLG_CASELESS;  		if ((treeCon->ses) && (treeCon->ses->server)) -			if (treeCon->ses->server->secMode & -			  (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) +			if (treeCon->ses->server->sign)  				buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;  	} @@ -381,46 +278,46 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,  }  static int -checkSMBhdr(struct smb_hdr *smb, __u16 mid) +check_smb_hdr(struct smb_hdr *smb)  { -	/* 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"); +	/* does it have the right SMB "signature" ? */ +	if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { +		cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", +			 *(unsigned int *)smb->Protocol); +		return 1;  	} -	cERROR(1, "bad smb detected. The Mid=%d", smb->Mid); + +	/* if it's a response then accept */ +	if (smb->Flags & SMBFLG_RESPONSE) +		return 0; + +	/* only one valid case where server sends us request */ +	if (smb->Command == SMB_COM_LOCKING_ANDX) +		return 0; + +	cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", +		 get_mid(smb));  	return 1;  }  int -checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) +checkSMB(char *buf, unsigned int total_read)  { -	__u32 len = smb->smb_buf_length; +	struct smb_hdr *smb = (struct smb_hdr *)buf; +	__u32 rfclen = be32_to_cpu(smb->smb_buf_length);  	__u32 clc_len;  /* calculated length */ -	cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len); +	cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", +		 total_read, rfclen); -	if (length < 2 + sizeof(struct smb_hdr)) { -		if ((length >= sizeof(struct smb_hdr) - 1) +	/* is this frame too small to even get to a BCC? */ +	if (total_read < 2 + sizeof(struct smb_hdr)) { +		if ((total_read >= sizeof(struct smb_hdr) - 1)  			    && (smb->Status.CifsError != 0)) { +			/* it's an error return */  			smb->WordCount = 0;  			/* some error cases do not return wct and bcc */  			return 0; -		} else if ((length == sizeof(struct smb_hdr) + 1) && +		} else if ((total_read == sizeof(struct smb_hdr) + 1) &&  				(smb->WordCount == 0)) {  			char *tmp = (char *)smb;  			/* Need to work around a bug in two servers here */ @@ -436,90 +333,89 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)  				tmp[sizeof(struct smb_hdr)+1] = 0;  				return 0;  			} -			cERROR(1, "rcvd invalid byte count (bcc)"); +			cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n");  		} else { -			cERROR(1, "Length less than smb header size"); +			cifs_dbg(VFS, "Length less than smb header size\n");  		} -		return 1; -	} -	if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { -		cERROR(1, "smb length greater than MaxBufSize, mid=%d", -				   smb->Mid); -		return 1; +		return -EIO;  	} -	if (checkSMBhdr(smb, mid)) -		return 1; -	clc_len = smbCalcSize_LE(smb); +	/* otherwise, there is enough to get to the BCC */ +	if (check_smb_hdr(smb)) +		return -EIO; +	clc_len = smbCalcSize(smb); -	if (4 + len != length) { -		cERROR(1, "Length read does not match RFC1001 length %d", -			   len); -		return 1; +	if (4 + rfclen != total_read) { +		cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", +			 rfclen); +		return -EIO;  	} -	if (4 + len != clc_len) { +	if (4 + rfclen != clc_len) { +		__u16 mid = get_mid(smb);  		/* check if bcc wrapped around for large read responses */ -		if ((len > 64 * 1024) && (len > clc_len)) { +		if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {  			/* check if lengths match mod 64K */ -			if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF)) +			if (((4 + rfclen) & 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; +		cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", +			 clc_len, 4 + rfclen, mid); + +		if (4 + rfclen < clc_len) { +			cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", +				 rfclen, mid); +			return -EIO; +		} else if (rfclen > clc_len + 512) { +			/* +			 * Some servers (Windows XP in particular) send more +			 * data than the lengths in the SMB packet would +			 * indicate on certain calls (byte range locks and +			 * trans2 find first calls in particular). While the +			 * client can handle such a frame by ignoring the +			 * trailing data, we choose limit the amount of extra +			 * data to 512 bytes. +			 */ +			cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", +				 rfclen, mid); +			return -EIO;  		}  	}  	return 0;  }  bool -is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) +is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)  { +	struct smb_hdr *buf = (struct smb_hdr *)buffer;  	struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;  	struct list_head *tmp, *tmp1, *tmp2; -	struct cifsSesInfo *ses; -	struct cifsTconInfo *tcon; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon;  	struct cifsInodeInfo *pCifsInode;  	struct cifsFileInfo *netfile; -	cFYI(1, "Checking for oplock break or dnotify response"); +	cifs_dbg(FYI, "Checking for oplock break or dnotify response\n");  	if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&  	   (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {  		struct smb_com_transaction_change_notify_rsp *pSMBr =  			(struct smb_com_transaction_change_notify_rsp *)buf;  		struct file_notify_information *pnotify;  		__u32 data_offset = 0; -		if (pSMBr->ByteCount > sizeof(struct file_notify_information)) { +		if (get_bcc(buf) > sizeof(struct file_notify_information)) {  			data_offset = le32_to_cpu(pSMBr->DataOffset);  			pnotify = (struct file_notify_information *)  				((char *)&pSMBr->hdr.Protocol + data_offset); -			cFYI(1, "dnotify on %s Action: 0x%x", +			cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n",  				 pnotify->FileName, pnotify->Action);  			/*   cifs_dump_mem("Rcvd notify Data: ",buf,  				sizeof(struct smb_hdr)+60); */  			return true;  		}  		if (pSMBr->hdr.Status.CifsError) { -			cFYI(1, "notify err 0x%d", -				pSMBr->hdr.Status.CifsError); +			cifs_dbg(FYI, "notify err 0x%d\n", +				 pSMBr->hdr.Status.CifsError);  			return true;  		}  		return false; @@ -533,7 +429,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)  		   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"); +			cifs_dbg(FYI, "invalid handle on oplock break\n");  			return true;  		} else if (ERRbadfid ==  		   le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { @@ -545,7 +441,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)  	if (pSMB->hdr.WordCount != 8)  		return false; -	cFYI(1, "oplock type 0x%d level 0x%d", +	cifs_dbg(FYI, "oplock type 0x%d level 0x%d\n",  		 pSMB->LockType, pSMB->OplockLevel);  	if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))  		return false; @@ -553,34 +449,41 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)  	/* look up tcon based on tid & uid */  	spin_lock(&cifs_tcp_ses_lock);  	list_for_each(tmp, &srv->smb_ses_list) { -		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); +		ses = list_entry(tmp, struct cifs_ses, smb_ses_list);  		list_for_each(tmp1, &ses->tcon_list) { -			tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list); +			tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);  			if (tcon->tid != buf->Tid)  				continue; -			cifs_stats_inc(&tcon->num_oplock_brks); +			cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);  			spin_lock(&cifs_file_list_lock);  			list_for_each(tmp2, &tcon->openFileList) {  				netfile = list_entry(tmp2, struct cifsFileInfo,  						     tlist); -				if (pSMB->Fid != netfile->netfid) +				if (pSMB->Fid != netfile->fid.netfid)  					continue; -				cFYI(1, "file id match, oplock break"); +				cifs_dbg(FYI, "file id match, oplock break\n");  				pCifsInode = CIFS_I(netfile->dentry->d_inode); -				cifs_set_oplock_level(pCifsInode, -						      pSMB->OplockLevel); +				set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, +					&pCifsInode->flags); +  				/* -				 * cifs_oplock_break_put() can't be called -				 * from here.  Get reference after queueing -				 * succeeded.  cifs_oplock_break() will -				 * synchronize using cifs_file_list_lock. +				 * Set flag if the server downgrades the oplock +				 * to L2 else clear.  				 */ -				if (queue_work(system_nrt_wq, -					       &netfile->oplock_break)) -					cifs_oplock_break_get(netfile); +				if (pSMB->OplockLevel) +					set_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &pCifsInode->flags); +				else +					clear_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &pCifsInode->flags); + +				queue_work(cifsiod_wq, +					   &netfile->oplock_break);  				netfile->oplock_break_cancelled = false;  				spin_unlock(&cifs_file_list_lock); @@ -589,26 +492,25 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)  			}  			spin_unlock(&cifs_file_list_lock);  			spin_unlock(&cifs_tcp_ses_lock); -			cFYI(1, "No matching file for oplock break"); +			cifs_dbg(FYI, "No matching file for oplock break\n");  			return true;  		}  	}  	spin_unlock(&cifs_tcp_ses_lock); -	cFYI(1, "Can not process oplock break for non-existent connection"); +	cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");  	return true;  }  void -dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) +dump_smb(void *buf, int smb_buf_length)  {  	int i, j;  	char debug_line[17]; -	unsigned char *buffer; +	unsigned char *buffer = buf;  	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 */ @@ -637,88 +539,13 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)  	return;  } -/* 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; -} -  void  cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)  {  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {  		cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; -		cERROR(1, "Autodisabling the use of server inode numbers on " -			   "%s. This server doesn't seem to support them " -			   "properly. Hardlinks will not be recognized on this " -			   "mount. Consider mounting with the \"noserverino\" " -			   "option to silence this message.", -			   cifs_sb_master_tcon(cifs_sb)->treeName); +		cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s. This server doesn't seem to support them properly. Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n", +			 cifs_sb_master_tcon(cifs_sb)->treeName);  	}  } @@ -727,17 +554,114 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)  	oplock &= 0xF;  	if (oplock == OPLOCK_EXCLUSIVE) { -		cinode->clientCanCacheAll = true; -		cinode->clientCanCacheRead = true; -		cFYI(1, "Exclusive Oplock granted on inode %p", -		     &cinode->vfs_inode); +		cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG; +		cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", +			 &cinode->vfs_inode);  	} else if (oplock == OPLOCK_READ) { -		cinode->clientCanCacheAll = false; -		cinode->clientCanCacheRead = true; -		cFYI(1, "Level II Oplock granted on inode %p", -		    &cinode->vfs_inode); -	} else { -		cinode->clientCanCacheAll = false; -		cinode->clientCanCacheRead = false; +		cinode->oplock = CIFS_CACHE_READ_FLG; +		cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", +			 &cinode->vfs_inode); +	} else +		cinode->oplock = 0; +} + +static int +cifs_oplock_break_wait(void *unused) +{ +	schedule(); +	return signal_pending(current) ? -ERESTARTSYS : 0; +} + +/* + * We wait for oplock breaks to be processed before we attempt to perform + * writes. + */ +int cifs_get_writer(struct cifsInodeInfo *cinode) +{ +	int rc; + +start: +	rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, +				   cifs_oplock_break_wait, TASK_KILLABLE); +	if (rc) +		return rc; + +	spin_lock(&cinode->writers_lock); +	if (!cinode->writers) +		set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +	cinode->writers++; +	/* Check to see if we have started servicing an oplock break */ +	if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { +		cinode->writers--; +		if (cinode->writers == 0) { +			clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +			wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); +		} +		spin_unlock(&cinode->writers_lock); +		goto start;  	} +	spin_unlock(&cinode->writers_lock); +	return 0; +} + +void cifs_put_writer(struct cifsInodeInfo *cinode) +{ +	spin_lock(&cinode->writers_lock); +	cinode->writers--; +	if (cinode->writers == 0) { +		clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +		wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); +	} +	spin_unlock(&cinode->writers_lock); +} + +void cifs_done_oplock_break(struct cifsInodeInfo *cinode) +{ +	clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); +	wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); +} + +bool +backup_cred(struct cifs_sb_info *cifs_sb) +{ +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { +		if (uid_eq(cifs_sb->mnt_backupuid, current_fsuid())) +			return true; +	} +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { +		if (in_group_p(cifs_sb->mnt_backupgid)) +			return true; +	} + +	return false; +} + +void +cifs_del_pending_open(struct cifs_pending_open *open) +{ +	spin_lock(&cifs_file_list_lock); +	list_del(&open->olist); +	spin_unlock(&cifs_file_list_lock); +} + +void +cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, +			     struct cifs_pending_open *open) +{ +#ifdef CONFIG_CIFS_SMB2 +	memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); +#endif +	open->oplock = CIFS_OPLOCK_NO_CHANGE; +	open->tlink = tlink; +	fid->pending_open = open; +	list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); +} + +void +cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, +		      struct cifs_pending_open *open) +{ +	spin_lock(&cifs_file_list_lock); +	cifs_add_pending_open_locked(fid, tlink, open); +	spin_unlock(&cifs_file_list_lock);  } diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 9aad47a2d62..6834b9c3bec 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -51,7 +51,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {  	{ERRnoaccess, -EACCES},  	{ERRbadfid, -EBADF},  	{ERRbadmcb, -EIO}, -	{ERRnomem, -ENOMEM}, +	{ERRnomem, -EREMOTEIO},  	{ERRbadmem, -EFAULT},  	{ERRbadenv, -EFAULT},  	{ERRbadformat, -EINVAL}, @@ -62,7 +62,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {  	{ERRdiffdevice, -EXDEV},  	{ERRnofiles, -ENOENT},  	{ERRwriteprot, -EROFS}, -	{ERRbadshare, -ETXTBSY}, +	{ERRbadshare, -EBUSY},  	{ERRlock, -EACCES},  	{ERRunsup, -EINVAL},  	{ERRnosuchshare, -ENXIO}, @@ -110,7 +110,7 @@ static const struct smb_to_posix_error mapping_table_ERRSRV[] = {  	{ERRnoroom, -ENOSPC},  	{ERRrmuns, -EUSERS},  	{ERRtimeout, -ETIME}, -	{ERRnoresource, -ENOBUFS}, +	{ERRnoresource, -EREMOTEIO},  	{ERRtoomanyuids, -EUSERS},  	{ERRbaduid, -EACCES},  	{ERRusempx, -EIO}, @@ -150,8 +150,8 @@ cifs_inet_pton(const int address_family, const char *cp, int len, void *dst)  	else if (address_family == AF_INET6)  		ret = in6_pton(cp, len, dst , '\\', NULL); -	cFYI(DBG2, "address conversion returned %d for %*.*s", -	     ret, len, len, cp); +	cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n", +		 ret, len, len, cp);  	if (ret > 0)  		ret = 1;  	return ret; @@ -170,7 +170,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len)  {  	int rc, alen, slen;  	const char *pct; -	char *endp, scope_id[13]; +	char scope_id[13];  	struct sockaddr_in *s4 = (struct sockaddr_in *) dst;  	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; @@ -197,15 +197,14 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len)  		memcpy(scope_id, pct + 1, slen);  		scope_id[slen] = '\0'; -		s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); -		if (endp != scope_id + slen) -			return 0; +		rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); +		rc = (rc == 0) ? 1 : 0;  	}  	return rc;  } -int +void  cifs_set_port(struct sockaddr *addr, const unsigned short int port)  {  	switch (addr->sa_family) { @@ -215,19 +214,7 @@ cifs_set_port(struct sockaddr *addr, const unsigned short int port)  	case AF_INET6:  		((struct sockaddr_in6 *)addr)->sin6_port = htons(port);  		break; -	default: -		return 0;  	} -	return 1; -} - -int -cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, -		   const unsigned short int port) -{ -	if (!cifs_convert_address(dst, src, len)) -		return 0; -	return cifs_set_port(dst, port);  }  /***************************************************************************** @@ -413,7 +400,7 @@ static const struct {  	 from NT_STATUS_INSUFFICIENT_RESOURCES to  	 NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */  	{ -	ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, { +	ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, {  	ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {  	ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {  	ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { @@ -683,7 +670,7 @@ static const struct {  	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}, { +	ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, {  	ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {  	ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {  	ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { @@ -793,7 +780,9 @@ static const struct {  	ERRDOS, ERRnoaccess, 0xc0000290}, {  	ERRDOS, ERRbadfunc, 0xc000029c}, {  	ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { -	ERRDOS, ERRinvlevel, 0x007c0001}, }; +	ERRDOS, ERRinvlevel, 0x007c0001}, { +	0, 0, 0 } +};  /*****************************************************************************   Print an error message from the status code @@ -806,8 +795,8 @@ cifs_print_status(__u32 status_code)  	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); +			pr_notice("Status code returned 0x%08x %s\n", +				  status_code, nt_errs[idx].nt_errstr);  		}  		idx++;  	} @@ -836,8 +825,9 @@ ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)  }  int -map_smb_to_linux_error(struct smb_hdr *smb, int logErr) +map_smb_to_linux_error(char *buf, bool logErr)  { +	struct smb_hdr *smb = (struct smb_hdr *)buf;  	unsigned int i;  	int rc = -EIO;	/* if transport error smb error may not be set */  	__u8 smberrclass; @@ -899,8 +889,8 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr)  	}  	/* else ERRHRD class errors or junk  - return EIO */ -	cFYI(1, "Mapping smb error code %d to POSIX err %d", -		 smberrcode, rc); +	cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", +		 le32_to_cpu(smb->Status.CifsError), rc);  	/* generic corrective action e.g. reconnect SMB session on  	 * ERRbaduid could be added */ @@ -913,17 +903,11 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr)   * portion, the number of word parameters and the data portion of the message   */  unsigned int -smbCalcSize(struct smb_hdr *ptr) +smbCalcSize(void *buf)  { +	struct smb_hdr *ptr = (struct smb_hdr *)buf;  	return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + -		2 /* size of the bcc field */ + 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))); +		2 /* size of the bcc field */ + get_bcc(ptr));  }  /* The following are taken from fs/ntfs/util.c */ @@ -957,8 +941,9 @@ cifs_UnixTimeToNT(struct timespec t)  	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}; +static const int total_days_of_prev_months[] = { +	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +};  struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)  { @@ -969,20 +954,20 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)  	SMB_TIME *st = (SMB_TIME *)&time;  	SMB_DATE *sd = (SMB_DATE *)&date; -	cFYI(1, "date %d time %d", date, time); +	cifs_dbg(FYI, "date %d time %d\n", date, time);  	sec = 2 * st->TwoSeconds;  	min = st->Minutes;  	if ((sec > 59) || (min > 59)) -		cERROR(1, "illegal time min %d sec %d", min, sec); +		cifs_dbg(VFS, "illegal time min %d sec %d\n", min, sec);  	sec += (min * 60);  	sec += 60 * 60 * st->Hours;  	if (st->Hours > 24) -		cERROR(1, "illegal hours %d", st->Hours); +		cifs_dbg(VFS, "illegal hours %d\n", st->Hours);  	days = sd->Day;  	month = sd->Month;  	if ((days > 31) || (month > 12)) { -		cERROR(1, "illegal date, month %d day: %d", month, days); +		cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);  		if (month > 12)  			month = 12;  	} @@ -1008,7 +993,7 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)  	ts.tv_sec = sec + offset; -	/* cFYI(1, "sec after cnvrt dos to unix time %d",sec); */ +	/* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */  	ts.tv_nsec = 0;  	return ts; diff --git a/fs/cifs/nterr.c b/fs/cifs/nterr.c index 819fd994b12..b6023c64612 100644 --- a/fs/cifs/nterr.c +++ b/fs/cifs/nterr.c @@ -31,7 +31,7 @@ const struct nt_err_code_struct nt_errs[] = {  	{"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_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW},  	{"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR},  	{"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA},  	{"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, @@ -681,7 +681,7 @@ const struct nt_err_code_struct nt_errs[] = {  	 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}, +	{"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, +	{"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED},  	{NULL, 0}  }; diff --git a/fs/cifs/nterr.h b/fs/cifs/nterr.h index 257267367d4..7a0eae5ae7c 100644 --- a/fs/cifs/nterr.h +++ b/fs/cifs/nterr.h @@ -35,18 +35,20 @@ struct nt_err_code_struct {  extern const struct nt_err_code_struct nt_errs[];  /* Win32 Status codes. */ -#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 +#define NT_STATUS_MORE_ENTRIES         0x0105 +#define NT_ERROR_INVALID_PARAMETER     0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER   0x007a +#define NT_STATUS_1804                 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR      0x010c -/* Win32 Error codes extracted using a loop in smbclient then printing a -   netmon sniff to a file. */ +/* + * 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_OK                   0x0000 +#define NT_STATUS_SOME_UNMAPPED        0x0107 +#define NT_STATUS_BUFFER_OVERFLOW  0x80000005  #define NT_STATUS_NO_MORE_ENTRIES  0x8000001a  #define NT_STATUS_MEDIA_CHANGED    0x8000001c  #define NT_STATUS_END_OF_MEDIA     0x8000001e diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 5d52e4a3b1e..848249fa120 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h @@ -126,3 +126,13 @@ typedef struct _AUTHENTICATE_MESSAGE {  	   do not set the version is present flag */  	char UserString[0];  } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; + +/* + * Size of the session key (crypto key encrypted with the password + */ + +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); +void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); +int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, +			struct cifs_ses *ses, +			const struct nls_table *nls_cp); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index ef7bb7b50f5..b15862e0f68 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -4,6 +4,7 @@   *   Directory search handling   *   *   Copyright (C) International Business Machines  Corp., 2004, 2008 + *   Copyright (C) Red Hat, Inc., 2011   *   Author(s): Steve French (sfrench@us.ibm.com)   *   *   This library is free software; you can redistribute it and/or modify @@ -47,15 +48,15 @@ static void dump_cifs_file_struct(struct file *file, char *label)  	if (file) {  		cf = file->private_data;  		if (cf == NULL) { -			cFYI(1, "empty cifs private file data"); +			cifs_dbg(FYI, "empty cifs private file data\n");  			return;  		}  		if (cf->invalidHandle) -			cFYI(1, "invalid handle"); +			cifs_dbg(FYI, "invalid handle\n");  		if (cf->srch_inf.endOfSearch) -			cFYI(1, "end of search"); +			cifs_dbg(FYI, "end of search\n");  		if (cf->srch_inf.emptyDir) -			cFYI(1, "empty dir"); +			cifs_dbg(FYI, "empty dir\n");  	}  }  #else @@ -65,57 +66,72 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)  #endif /* DEBUG2 */  /* + * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT + *   * Find the dentry that matches "name". If there isn't one, create one. If it's   * a negative dentry or the uniqueid changed, then drop it and recreate it.   */ -static struct dentry * -cifs_readdir_lookup(struct dentry *parent, struct qstr *name, +static void +cifs_prime_dcache(struct dentry *parent, struct qstr *name,  		    struct cifs_fattr *fattr)  {  	struct dentry *dentry, *alias;  	struct inode *inode;  	struct super_block *sb = parent->d_inode->i_sb; +	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); -	cFYI(1, "For %s", name->name); +	cifs_dbg(FYI, "%s: for %s\n", __func__, name->name); -	if (parent->d_op && parent->d_op->d_hash) -		parent->d_op->d_hash(parent, name); -	else -		name->hash = full_name_hash(name->name, name->len); +	dentry = d_hash_and_lookup(parent, name); +	if (unlikely(IS_ERR(dentry))) +		return; -	dentry = d_lookup(parent, name);  	if (dentry) { -		/* FIXME: check for inode number changes? */ -		if (dentry->d_inode != NULL) -			return dentry; -		d_drop(dentry); +		int err; + +		inode = dentry->d_inode; +		if (inode) { +			/* +			 * If we're generating inode numbers, then we don't +			 * want to clobber the existing one with the one that +			 * the readdir code created. +			 */ +			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) +				fattr->cf_uniqueid = CIFS_I(inode)->uniqueid; + +			/* update inode in place if i_ino didn't change */ +			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) { +				cifs_fattr_to_inode(inode, fattr); +				goto out; +			} +		} +		err = d_invalidate(dentry);  		dput(dentry); +		if (err) +			return;  	} +	/* +	 * If we know that the inode will need to be revalidated immediately, +	 * then don't create a new dentry for it. We'll end up doing an on +	 * the wire call either way and this spares us an invalidation. +	 */ +	if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) +		return; +  	dentry = d_alloc(parent, name); -	if (dentry == NULL) -		return NULL; +	if (!dentry) +		return;  	inode = cifs_iget(sb, fattr); -	if (!inode) { -		dput(dentry); -		return NULL; -	} - -	if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase) -		dentry->d_op = &cifs_ci_dentry_ops; -	else -		dentry->d_op = &cifs_dentry_ops; +	if (!inode) +		goto out;  	alias = d_materialise_unique(dentry, inode); -	if (alias != NULL) { -		dput(dentry); -		if (IS_ERR(alias)) -			return NULL; -		dentry = alias; -	} - -	return dentry; +	if (alias && !IS_ERR(alias)) +		dput(alias); +out: +	dput(dentry);  }  static void @@ -132,9 +148,30 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)  		fattr->cf_dtype = DT_REG;  	} +	/* +	 * We need to revalidate it further to make a decision about whether it +	 * is a symbolic link, DFS referral or a reparse point with a direct +	 * access like junctions, deduplicated files, NFS symlinks. +	 */ +	if (fattr->cf_cifsattrs & ATTR_REPARSE) +		fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + +	/* non-unix readdir doesn't provide nlink */ +	fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; +  	if (fattr->cf_cifsattrs & ATTR_READONLY)  		fattr->cf_mode &= ~S_IWUGO; +	/* +	 * We of course don't get ACL info in FIND_FIRST/NEXT results, so +	 * mark it for revalidation so that "ls -l" will look right. It might +	 * be super-slow, but if we don't do this then the ownership of files +	 * may look wrong since the inodes may not have timed out by the time +	 * "ls" does a stat() call on them. +	 */ +	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) +		fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; +  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&  	    fattr->cf_cifsattrs & ATTR_SYSTEM) {  		if (fattr->cf_eof == 0)  { @@ -152,7 +189,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)  	}  } -static void +void  cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,  		       struct cifs_sb_info *cifs_sb)  { @@ -160,6 +197,7 @@ cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,  	fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes);  	fattr->cf_eof = le64_to_cpu(info->EndOfFile);  	fattr->cf_bytes = le64_to_cpu(info->AllocationSize); +	fattr->cf_createtime = le64_to_cpu(info->CreationTime);  	fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);  	fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);  	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); @@ -193,13 +231,13 @@ cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,        we try to do FindFirst on (NTFS) directory symlinks */  /*  int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, -			     int xid) +			     unsigned int xid)  {  	__u16 fid;  	int len;  	int oplock = 0;  	int rc; -	struct cifsTconInfo *ptcon = cifs_sb_tcon(cifs_sb); +	struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb);  	char *tmpbuffer;  	rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, @@ -214,38 +252,51 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,  				fid,  				cifs_sb->local_nls);  		if (CIFSSMBClose(xid, ptcon, fid)) { -			cFYI(1, "Error closing temporary reparsepoint open"); +			cifs_dbg(FYI, "Error closing temporary reparsepoint open\n");  		}  	}  }   */ -static int initiate_cifs_search(const int xid, struct file *file) +static int +initiate_cifs_search(const unsigned int xid, struct file *file)  { +	__u16 search_flags;  	int rc = 0;  	char *full_path = NULL;  	struct cifsFileInfo *cifsFile;  	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); -	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct tcon_link *tlink = NULL; +	struct cifs_tcon *tcon; +	struct TCP_Server_Info *server; -	tlink = cifs_sb_tlink(cifs_sb); -	if (IS_ERR(tlink)) -		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); - -	if (file->private_data == NULL) -		file->private_data = -			kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);  	if (file->private_data == NULL) { -		rc = -ENOMEM; +		tlink = cifs_sb_tlink(cifs_sb); +		if (IS_ERR(tlink)) +			return PTR_ERR(tlink); + +		cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); +		if (cifsFile == NULL) { +			rc = -ENOMEM; +			goto error_exit; +		} +		file->private_data = cifsFile; +		cifsFile->tlink = cifs_get_tlink(tlink); +		tcon = tlink_tcon(tlink); +	} else { +		cifsFile = file->private_data; +		tcon = tlink_tcon(cifsFile->tlink); +	} + +	server = tcon->ses->server; + +	if (!server->ops->query_dir_first) { +		rc = -ENOSYS;  		goto error_exit;  	} -	cifsFile = file->private_data;  	cifsFile->invalidHandle = true;  	cifsFile->srch_inf.endOfSearch = false; -	cifsFile->tlink = cifs_get_tlink(tlink);  	full_path = build_path_from_dentry(file->f_path.dentry);  	if (full_path == NULL) { @@ -253,16 +304,16 @@ static int initiate_cifs_search(const int xid, struct file *file)  		goto error_exit;  	} -	cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos); +	cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);  ffirst_retry:  	/* test for Unix extensions */  	/* but now check for them on the share/mount not on the SMB session */ -/*	if (pTcon->ses->capabilities & CAP_UNIX) { */ -	if (pTcon->unix_ext) +	/* if (cap_unix(tcon->ses) { */ +	if (tcon->unix_ext)  		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; -	else if ((pTcon->ses->capabilities & -			(CAP_NT_SMBS | CAP_NT_FIND)) == 0) { +	else if ((tcon->ses->capabilities & +		  tcon->ses->server->vals->cap_nt_find) == 0) {  		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;  	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {  		cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; @@ -270,10 +321,14 @@ ffirst_retry:  		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)); +	search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; +	if (backup_cred(cifs_sb)) +		search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + +	rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, +					  &cifsFile->fid, search_flags, +					  &cifsFile->srch_inf); +  	if (rc == 0)  		cifsFile->invalidHandle = false;  	/* BB add following call to handle readdir on new NTFS symlink errors @@ -291,16 +346,16 @@ error_exit:  }  /* return length of unicode string in bytes */ -static int cifs_unicode_bytelen(char *str) +static int cifs_unicode_bytelen(const char *str)  {  	int len; -	__le16 *ustr = (__le16 *)str; +	const __le16 *ustr = (const __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"); +	cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n");  	return len << 1;  } @@ -317,96 +372,146 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)  				pfData->FileNameLength;  	} else  		new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset); -	cFYI(1, "new entry %p old entry %p", new_entry, old_entry); +	cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry);  	/* validate that new_entry is not past end of SMB */  	if (new_entry >= end_of_smb) { -		cERROR(1, "search entry %p began after end of SMB %p old entry %p", -			new_entry, end_of_smb, old_entry); +		cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n", +			 new_entry, end_of_smb, old_entry);  		return NULL;  	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&  		    (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))  		  || ((level != SMB_FIND_FILE_INFO_STANDARD) &&  		   (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb)))  { -		cERROR(1, "search entry %p extends after end of SMB %p", -			new_entry, end_of_smb); +		cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n", +			 new_entry, end_of_smb);  		return NULL;  	} else  		return new_entry;  } +struct cifs_dirent { +	const char	*name; +	size_t		namelen; +	u32		resume_key; +	u64		ino; +}; + +static void cifs_fill_dirent_unix(struct cifs_dirent *de, +		const FILE_UNIX_INFO *info, bool is_unicode) +{ +	de->name = &info->FileName[0]; +	if (is_unicode) +		de->namelen = cifs_unicode_bytelen(de->name); +	else +		de->namelen = strnlen(de->name, PATH_MAX); +	de->resume_key = info->ResumeKey; +	de->ino = le64_to_cpu(info->basic.UniqueId); +} + +static void cifs_fill_dirent_dir(struct cifs_dirent *de, +		const FILE_DIRECTORY_INFO *info) +{ +	de->name = &info->FileName[0]; +	de->namelen = le32_to_cpu(info->FileNameLength); +	de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_full(struct cifs_dirent *de, +		const FILE_FULL_DIRECTORY_INFO *info) +{ +	de->name = &info->FileName[0]; +	de->namelen = le32_to_cpu(info->FileNameLength); +	de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_search(struct cifs_dirent *de, +		const SEARCH_ID_FULL_DIR_INFO *info) +{ +	de->name = &info->FileName[0]; +	de->namelen = le32_to_cpu(info->FileNameLength); +	de->resume_key = info->FileIndex; +	de->ino = le64_to_cpu(info->UniqueId); +} + +static void cifs_fill_dirent_both(struct cifs_dirent *de, +		const FILE_BOTH_DIRECTORY_INFO *info) +{ +	de->name = &info->FileName[0]; +	de->namelen = le32_to_cpu(info->FileNameLength); +	de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_std(struct cifs_dirent *de, +		const FIND_FILE_STANDARD_INFO *info) +{ +	de->name = &info->FileName[0]; +	/* one byte length, no endianess conversion */ +	de->namelen = info->FileNameLength; +	de->resume_key = info->ResumeKey; +} + +static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, +		u16 level, bool is_unicode) +{ +	memset(de, 0, sizeof(*de)); + +	switch (level) { +	case SMB_FIND_FILE_UNIX: +		cifs_fill_dirent_unix(de, info, is_unicode); +		break; +	case SMB_FIND_FILE_DIRECTORY_INFO: +		cifs_fill_dirent_dir(de, info); +		break; +	case SMB_FIND_FILE_FULL_DIRECTORY_INFO: +		cifs_fill_dirent_full(de, info); +		break; +	case SMB_FIND_FILE_ID_FULL_DIR_INFO: +		cifs_fill_dirent_search(de, info); +		break; +	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: +		cifs_fill_dirent_both(de, info); +		break; +	case SMB_FIND_FILE_INFO_STANDARD: +		cifs_fill_dirent_std(de, info); +		break; +	default: +		cifs_dbg(FYI, "Unknown findfirst level %d\n", level); +		return -EINVAL; +	} + +	return 0; +} +  #define UNICODE_DOT cpu_to_le16(0x2e)  /* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ -static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) +static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)  {  	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; -			} +	if (!de->name) +		return 0; + +	if (is_unicode) { +		__le16 *ufilename = (__le16 *)de->name; +		if (de->namelen == 2) { +			/* check for . */ +			if (ufilename[0] == UNICODE_DOT) +				rc = 1; +		} else if (de->namelen == 4) { +			/* check for .. */ +			if (ufilename[0] == UNICODE_DOT && +			    ufilename[1] == UNICODE_DOT) +				rc = 2; +		} +	} else /* ASCII */ { +		if (de->namelen == 1) { +			if (de->name[0] == '.') +				rc = 1; +		} else if (de->namelen == 2) { +			if (de->name[0] == '.' && de->name[1] == '.') +				rc = 2;  		}  	} @@ -417,7 +522,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)     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 inode *inode = file_inode(file);  	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);  	if (cifsInfo->time == 0) @@ -428,321 +533,223 @@ static int is_dir_changed(struct file *file)  }  static int cifs_save_resume_key(const char *current_entry, -	struct cifsFileInfo *cifsFile) +	struct cifsFileInfo *file_info)  { -	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; +	struct cifs_dirent de; +	int rc; -		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; +	rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level, +			      file_info->srch_inf.unicode); +	if (!rc) { +		file_info->srch_inf.presume_name = de.name; +		file_info->srch_inf.resume_name_len = de.namelen; +		file_info->srch_inf.resume_key = de.resume_key;  	} -	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) +/* + * Find the corresponding entry in the search. Note that the SMB server returns + * search entries for . and .. which complicates logic here if we choose to + * parse for them and we do not assume that they are located in the findfirst + * return buffer. We start counting in the buffer with entry 2 and increment for + * every entry (do not increment for . or .. entry). + */ +static int +find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, +		struct file *file, char **current_entry, int *num_to_ret)  { +	__u16 search_flags;  	int rc = 0;  	int pos_in_buf = 0;  	loff_t first_entry_in_buffer; -	loff_t index_to_find = file->f_pos; -	struct cifsFileInfo *cifsFile = file->private_data; +	loff_t index_to_find = pos; +	struct cifsFileInfo *cfile = file->private_data; +	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	struct TCP_Server_Info *server = tcon->ses->server;  	/* check if index in the buffer */ -	if ((cifsFile == NULL) || (ppCurrentEntry == NULL) || -	   (num_to_ret == NULL)) +	if (!server->ops->query_dir_first || !server->ops->query_dir_next) +		return -ENOSYS; + +	if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))  		return -ENOENT; -	*ppCurrentEntry = NULL; -	first_entry_in_buffer = -		cifsFile->srch_inf.index_of_last_entry - -			cifsFile->srch_inf.entries_in_buffer; +	*current_entry = NULL; +	first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - +					cfile->srch_inf.entries_in_buffer; -	/* if first entry in buf is zero then is first buffer -	in search response data which means it is likely . and .. -	will be in this buffer, although some servers do not return -	. and .. for the root of a drive and for those we need -	to start two entries earlier */ +	/* +	 * 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)) { +	if (((index_to_find < cfile->srch_inf.index_of_last_entry) && +	     is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {  		/* close and restart search */ -		cFYI(1, "search backing up - close and restart search"); +		cifs_dbg(FYI, "search backing up - close and restart search\n");  		spin_lock(&cifs_file_list_lock); -		if (!cifsFile->srch_inf.endOfSearch && -		    !cifsFile->invalidHandle) { -			cifsFile->invalidHandle = true; +		if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) { +			cfile->invalidHandle = true;  			spin_unlock(&cifs_file_list_lock); -			CIFSFindClose(xid, pTcon, cifsFile->netfid); +			if (server->ops->close) +				server->ops->close(xid, tcon, &cfile->fid);  		} else  			spin_unlock(&cifs_file_list_lock); -		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. +		if (cfile->srch_inf.ntwrk_buf_start) { +			cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); +			if (cfile->srch_inf.smallBuf) +				cifs_small_buf_release(cfile->srch_inf.  						ntwrk_buf_start);  			else -				cifs_buf_release(cifsFile->srch_inf. +				cifs_buf_release(cfile->srch_inf.  						ntwrk_buf_start); -			cifsFile->srch_inf.ntwrk_buf_start = NULL; +			cfile->srch_inf.ntwrk_buf_start = NULL;  		}  		rc = initiate_cifs_search(xid, file);  		if (rc) { -			cFYI(1, "error %d reinitiating a search on rewind", +			cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",  				 rc);  			return rc;  		} -		cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile); +		/* FindFirst/Next set last_entry to NULL on malformed reply */ +		if (cfile->srch_inf.last_entry) +			cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);  	} -	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); +	search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; +	if (backup_cred(cifs_sb)) +		search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + +	while ((index_to_find >= cfile->srch_inf.index_of_last_entry) && +	       (rc == 0) && !cfile->srch_inf.endOfSearch) { +		cifs_dbg(FYI, "calling findnext2\n"); +		rc = server->ops->query_dir_next(xid, tcon, &cfile->fid, +						 search_flags, +						 &cfile->srch_inf); +		/* FindFirst/Next set last_entry to NULL on malformed reply */ +		if (cfile->srch_inf.last_entry) +			cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);  		if (rc)  			return -ENOENT;  	} -	if (index_to_find < cifsFile->srch_inf.index_of_last_entry) { +	if (index_to_find < cfile->srch_inf.index_of_last_entry) {  		/* we found the buffer that contains the entry */  		/* scan and find it */  		int i; -		char *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; +		char *cur_ent; +		char *end_of_smb = cfile->srch_inf.ntwrk_buf_start + +			server->ops->calc_smb_size( +					cfile->srch_inf.ntwrk_buf_start); + +		cur_ent = cfile->srch_inf.srch_entries_start; +		first_entry_in_buffer = cfile->srch_inf.index_of_last_entry +					- cfile->srch_inf.entries_in_buffer;  		pos_in_buf = index_to_find - first_entry_in_buffer; -		cFYI(1, "found entry - pos_in_buf %d", pos_in_buf); +		cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf); -		for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) { +		for (i = 0; (i < (pos_in_buf)) && (cur_ent != 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); +			cur_ent = nxt_dir_entry(cur_ent, end_of_smb, +						cfile->srch_inf.info_level);  		} -		if ((current_entry == NULL) && (i < pos_in_buf)) { +		if ((cur_ent == 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); +			cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n", +				 pos_in_buf, index_to_find, rc);  		}  		rc = 0; -		*ppCurrentEntry = current_entry; +		*current_entry = cur_ent;  	} else { -		cFYI(1, "index not in buffer - could not findnext into it"); +		cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");  		return 0;  	} -	if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { -		cFYI(1, "can not return entries pos_in_buf beyond last"); +	if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) { +		cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n");  		*num_to_ret = 0;  	} else -		*num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; +		*num_to_ret = cfile->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, unsigned int max_len, __u64 *pinum) +static int cifs_filldir(char *find_entry, struct file *file, +		struct dir_context *ctx, +		char *scratch_buf, unsigned int max_len)  { +	struct cifsFileInfo *file_info = file->private_data; +	struct super_block *sb = file->f_path.dentry->d_sb; +	struct cifs_sb_info *cifs_sb = CIFS_SB(sb); +	struct cifs_dirent de = { NULL, }; +	struct cifs_fattr fattr; +	struct qstr name;  	int rc = 0; -	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; +	ino_t ino; -		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); -		} - -		*pinum = le64_to_cpu(pFindData->basic.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 = le64_to_cpu(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; -	} +	rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level, +			      file_info->srch_inf.unicode); +	if (rc) +		return rc; -	if (len > max_len) { -		cERROR(1, "bad search response length %d past smb end", len); +	if (de.namelen > max_len) { +		cifs_dbg(VFS, "bad search response length %zd past smb end\n", +			 de.namelen);  		return -EINVAL;  	} -	if (unicode) { -		pqst->len = cifs_from_ucs2((char *) pqst->name, -					   (__le16 *) filename, -					   UNICODE_NAME_MAX, -					   min(len, max_len), nlt, -					   cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); -		pqst->len -= nls_nullsize(nlt); -	} else { -		pqst->name = filename; -		pqst->len = len; -	} -	return rc; -} - -static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir, -			void *direntry, char *scratch_buf, unsigned int max_len) -{ -	int rc = 0; -	struct qstr qstring; -	struct cifsFileInfo *pCifsF; -	u64    inum; -	ino_t  ino; -	struct super_block *sb; -	struct cifs_sb_info *cifs_sb; -	struct dentry *tmp_dentry; -	struct cifs_fattr fattr; - -	/* 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) +	if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))  		return 0; -	sb = file->f_path.dentry->d_sb; -	cifs_sb = CIFS_SB(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 (file_info->srch_inf.unicode) { +		struct nls_table *nlt = cifs_sb->local_nls; -	if (rc) -		return rc; +		name.name = scratch_buf; +		name.len = +			cifs_from_utf16((char *)name.name, (__le16 *)de.name, +					UNICODE_NAME_MAX, +					min_t(size_t, de.namelen, +					      (size_t)max_len), nlt, +					cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR); +		name.len -= nls_nullsize(nlt); +	} else { +		name.name = de.name; +		name.len = de.namelen; +	} -	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) +	switch (file_info->srch_inf.info_level) { +	case SMB_FIND_FILE_UNIX:  		cifs_unix_basic_to_fattr(&fattr, -				 &((FILE_UNIX_INFO *) pfindEntry)->basic, -				 cifs_sb); -	else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) -		cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *) -					pfindEntry, cifs_sb); -	else -		cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *) -					pfindEntry, cifs_sb); +					 &((FILE_UNIX_INFO *)find_entry)->basic, +					 cifs_sb); +		break; +	case SMB_FIND_FILE_INFO_STANDARD: +		cifs_std_info_to_fattr(&fattr, +				       (FIND_FILE_STANDARD_INFO *)find_entry, +				       cifs_sb); +		break; +	default: +		cifs_dir_info_to_fattr(&fattr, +				       (FILE_DIRECTORY_INFO *)find_entry, +				       cifs_sb); +		break; +	} -	if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { -		fattr.cf_uniqueid = inum; +	if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { +		fattr.cf_uniqueid = de.ino;  	} else {  		fattr.cf_uniqueid = iunique(sb, ROOT_I);  		cifs_autodisable_serverino(cifs_sb);  	}  	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && -	    CIFSCouldBeMFSymlink(&fattr)) +	    couldbe_mf_symlink(&fattr))  		/*  		 * trying to get the type and mode can be slow,  		 * so just call those regular files for now, and mark @@ -750,35 +757,19 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,  		 */  		fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; -	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); -	tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr); - -	rc = filldir(direntry, qstring.name, qstring.len, file->f_pos, -		     ino, fattr.cf_dtype); +	cifs_prime_dcache(file->f_dentry, &name, &fattr); -	/* -	 * 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 -	 * -	 * FIXME: This looks bogus. filldir returns -EOVERFLOW in the above -	 * case already. Why should we be clobbering other errors from it? -	 */ -	if (rc) { -		cFYI(1, "filldir rc = %d", rc); -		rc = -EOVERFLOW; -	} -	dput(tmp_dentry); -	return rc; +	ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); +	return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype);  } -int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) +int cifs_readdir(struct file *file, struct dir_context *ctx)  {  	int rc = 0; -	int xid, i; -	struct cifs_sb_info *cifs_sb; -	struct cifsTconInfo *pTcon; +	unsigned int xid; +	int i; +	struct cifs_tcon *tcon;  	struct cifsFileInfo *cifsFile = NULL;  	char *current_entry;  	int num_to_fill = 0; @@ -786,9 +777,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)  	char *end_of_smb;  	unsigned int max_len; -	xid = GetXid(); - -	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	xid = get_xid();  	/*  	 * Ensure FindFirst doesn't fail before doing filldir() for '.' and @@ -796,108 +785,93 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)  	 */  	if (file->private_data == NULL) {  		rc = initiate_cifs_search(xid, file); -		cFYI(1, "initiate cifs search rc %d", rc); +		cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);  		if (rc)  			goto rddir2_exit;  	} -	switch ((int) file->f_pos) { -	case 0: -		if (filldir(direntry, ".", 1, file->f_pos, -		     file->f_path.dentry->d_inode->i_ino, DT_DIR) < 0) { -			cERROR(1, "Filldir for current dir failed"); -			rc = -ENOMEM; -			break; +	if (!dir_emit_dots(file, ctx)) +		goto rddir2_exit; + +	/* 1) If search is active, +		is in current search buffer? +		if it before then restart search +		if after then keep searching till find it */ + +	if (file->private_data == NULL) { +		rc = -EINVAL; +		goto rddir2_exit; +	} +	cifsFile = file->private_data; +	if (cifsFile->srch_inf.endOfSearch) { +		if (cifsFile->srch_inf.emptyDir) { +			cifs_dbg(FYI, "End of search, empty dir\n"); +			rc = 0; +			goto rddir2_exit;  		} -		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; +	} /* else { +		cifsFile->invalidHandle = true; +		tcon->ses->server->close(xid, tcon, &cifsFile->fid); +	} */ + +	tcon = tlink_tcon(cifsFile->tlink); +	rc = find_cifs_entry(xid, tcon, ctx->pos, file, ¤t_entry, +			     &num_to_fill); +	if (rc) { +		cifs_dbg(FYI, "fce error %d\n", rc); +		goto rddir2_exit; +	} else if (current_entry != NULL) { +		cifs_dbg(FYI, "entry %lld found\n", ctx->pos); +	} else { +		cifs_dbg(FYI, "could not find entry\n"); +		goto rddir2_exit; +	} +	cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", +		 num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); +	max_len = tcon->ses->server->ops->calc_smb_size( +			cifsFile->srch_inf.ntwrk_buf_start); +	end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; + +	tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); +	if (tmp_buf == NULL) { +		rc = -ENOMEM; +		goto rddir2_exit; +	} + +	for (i = 0; i < num_to_fill; i++) { +		if (current_entry == NULL) { +			/* evaluate whether this case is an error */ +			cifs_dbg(VFS, "past SMB end,  num to fill %d i %d\n", +				 num_to_fill, i);  			break;  		} -		file->f_pos++; -	default: -		/* 1) If search is active, -			is in current search buffer? -			if it before then restart search -			if after then keep searching till find it */ - -		if (file->private_data == NULL) { -			rc = -EINVAL; -			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); -		} */ - -		pTcon = tlink_tcon(cifsFile->tlink); -		rc = find_cifs_entry(xid, pTcon, file, -				¤t_entry, &num_to_fill); +		/* +		 * if buggy server returns . and .. late do we want to +		 * check for that here? +		 */ +		rc = cifs_filldir(current_entry, file, ctx, +				  tmp_buf, max_len);  		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; - -		tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); -		if (tmp_buf == NULL) { -			rc = -ENOMEM; +			if (rc > 0) +				rc = 0;  			break;  		} -		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 */ +		ctx->pos++; +		if (ctx->pos == +			cifsFile->srch_inf.index_of_last_entry) { +			cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", +				 ctx->pos, tmp_buf); +			cifs_save_resume_key(current_entry, cifsFile); +			break; +		} else +			current_entry = +				nxt_dir_entry(current_entry, end_of_smb, +					cifsFile->srch_inf.info_level); +	} +	kfree(tmp_buf);  rddir2_exit: -	FreeXid(xid); +	free_xid(xid);  	return rc;  } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 7b01d3f6eed..e87387dbf39 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -32,89 +32,7 @@  #include <linux/slab.h>  #include "cifs_spnego.h" -/* - * Checks if this is the first smb session to be reconnected after - * the socket has been reestablished (so we know whether to use vc 0). - * Called while holding the cifs_tcp_ses_lock, so do not block - */ -static bool is_first_ses_reconnect(struct cifsSesInfo *ses) -{ -	struct list_head *tmp; -	struct cifsSesInfo *tmp_ses; - -	list_for_each(tmp, &ses->server->smb_ses_list) { -		tmp_ses = list_entry(tmp, struct cifsSesInfo, -				     smb_ses_list); -		if (tmp_ses->need_reconnect == false) -			return false; -	} -	/* could not find a session that was already connected, -	   this must be the first one we are reconnecting */ -	return true; -} - -/* - *	vc number 0 is treated specially by some servers, and should be the - *      first one we request.  After that we can use vcnumbers up to maxvcs, - *	one for each smb session (some Windows versions set maxvcs incorrectly - *	so maxvc=1 can be ignored).  If we have too many vcs, we can reuse - *	any vc but zero (some servers reset the connection on vcnum zero) - * - */ -static __le16 get_next_vcnum(struct cifsSesInfo *ses) -{ -	__u16 vcnum = 0; -	struct list_head *tmp; -	struct cifsSesInfo *tmp_ses; -	__u16 max_vcs = ses->server->max_vcs; -	__u16 i; -	int free_vc_found = 0; - -	/* Quoting the MS-SMB specification: "Windows-based SMB servers set this -	field to one but do not enforce this limit, which allows an SMB client -	to establish more virtual circuits than allowed by this value ... but -	other server implementations can enforce this limit." */ -	if (max_vcs < 2) -		max_vcs = 0xFFFF; - -	spin_lock(&cifs_tcp_ses_lock); -	if ((ses->need_reconnect) && is_first_ses_reconnect(ses)) -			goto get_vc_num_exit;  /* vcnum will be zero */ -	for (i = ses->server->srv_count - 1; i < max_vcs; i++) { -		if (i == 0) /* this is the only connection, use vc 0 */ -			break; - -		free_vc_found = 1; - -		list_for_each(tmp, &ses->server->smb_ses_list) { -			tmp_ses = list_entry(tmp, struct cifsSesInfo, -					     smb_ses_list); -			if (tmp_ses->vcnum == i) { -				free_vc_found = 0; -				break; /* found duplicate, try next vcnum */ -			} -		} -		if (free_vc_found) -			break; /* we found a vcnumber that will work - use it */ -	} - -	if (i == 0) -		vcnum = 0; /* for most common case, ie if one smb session, use -			      vc zero.  Also for case when no free vcnum, zero -			      is safest to send (some clients only send zero) */ -	else if (free_vc_found == 0) -		vcnum = 1;  /* we can not reuse vc=0 safely, since some servers -				reset all uids on that, but 1 is ok. */ -	else -		vcnum = i; -	ses->vcnum = vcnum; -get_vc_num_exit: -	spin_unlock(&cifs_tcp_ses_lock); - -	return cpu_to_le16(vcnum); -} - -static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) +static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)  {  	__u32 capabilities = 0; @@ -124,9 +42,11 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)  	/*	that we use in next few lines                               */  	/* Note that header is initialized to zero in header_assemble */  	pSMB->req.AndXCommand = 0xFF; -	pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); +	pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, +					CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, +					USHRT_MAX));  	pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); -	pSMB->req.VcNumber = get_next_vcnum(ses); +	pSMB->req.VcNumber = __constant_cpu_to_le16(1);  	/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ @@ -136,8 +56,7 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)  	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)) +	if (ses->server->sign)  		pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;  	if (ses->capabilities & CAP_UNICODE) { @@ -165,23 +84,23 @@ unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)  	int bytes_ret = 0;  	/* Copy OS version */ -	bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32, -				  nls_cp); +	bytes_ret = cifs_strtoUTF16((__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); +	bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, +				    32, nls_cp);  	bcc_ptr += 2 * bytes_ret;  	bcc_ptr += 2; /* trailing null */ -	bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, -				  32, nls_cp); +	bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, +				    32, nls_cp);  	bcc_ptr += 2 * bytes_ret;  	bcc_ptr += 2; /* trailing null */  	*pbcc_area = bcc_ptr;  } -static void unicode_domain_string(char **pbcc_area, struct cifsSesInfo *ses, +static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,  				   const struct nls_table *nls_cp)  {  	char *bcc_ptr = *pbcc_area; @@ -195,8 +114,8 @@ static void unicode_domain_string(char **pbcc_area, struct cifsSesInfo *ses,  		*(bcc_ptr+1) = 0;  		bytes_ret = 0;  	} else -		bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, -					  256, nls_cp); +		bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, +					    CIFS_MAX_DOMAINNAME_LEN, nls_cp);  	bcc_ptr += 2 * bytes_ret;  	bcc_ptr += 2;  /* account for null terminator */ @@ -204,7 +123,7 @@ static void unicode_domain_string(char **pbcc_area, struct cifsSesInfo *ses,  } -static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, +static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,  				   const struct nls_table *nls_cp)  {  	char *bcc_ptr = *pbcc_area; @@ -219,13 +138,13 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,  		bcc_ptr++;  	} */  	/* copy user */ -	if (ses->userName == NULL) { +	if (ses->user_name == NULL) {  		/* null user mount */  		*bcc_ptr = 0;  		*(bcc_ptr+1) = 0;  	} else { -		bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName, -					  MAX_USERNAME_SIZE, nls_cp); +		bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, +					    CIFS_MAX_USERNAME_LEN, nls_cp);  	}  	bcc_ptr += 2 * bytes_ret;  	bcc_ptr += 2; /* account for null termination */ @@ -236,7 +155,7 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,  	*pbcc_area = bcc_ptr;  } -static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, +static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,  				 const struct nls_table *nls_cp)  {  	char *bcc_ptr = *pbcc_area; @@ -244,20 +163,18 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,  	/* 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 { -		strncpy(bcc_ptr, ses->userName, MAX_USERNAME_SIZE); +	if (ses->user_name != NULL) { +		strncpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); +		bcc_ptr += strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);  	} -	bcc_ptr += strnlen(ses->userName, MAX_USERNAME_SIZE); +	/* else null user mount */  	*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); +		strncpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); +		bcc_ptr += strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);  	} /* else we will send a null domain name  	     so the server will default to its own domain */  	*bcc_ptr = 0; @@ -277,30 +194,17 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,  }  static void -decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, +decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses,  		      const struct nls_table *nls_cp)  {  	int len;  	char *data = *pbcc_area; -	cFYI(1, "bleft %d", bleft); - -	/* -	 * Windows servers do not always double null terminate their final -	 * Unicode string. Check to see if there are an uneven number of bytes -	 * left. If so, then add an extra NULL pad byte to the end of the -	 * response. -	 * -	 * See section 2.7.2 in "Implementing CIFS" for details -	 */ -	if (bleft % 2) { -		data[bleft] = 0; -		++bleft; -	} +	cifs_dbg(FYI, "bleft %d\n", bleft);  	kfree(ses->serverOS); -	ses->serverOS = cifs_strndup_from_ucs(data, bleft, true, nls_cp); -	cFYI(1, "serverOS=%s", ses->serverOS); +	ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); +	cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS);  	len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;  	data += len;  	bleft -= len; @@ -308,8 +212,8 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses,  		return;  	kfree(ses->serverNOS); -	ses->serverNOS = cifs_strndup_from_ucs(data, bleft, true, nls_cp); -	cFYI(1, "serverNOS=%s", ses->serverNOS); +	ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); +	cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS);  	len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2;  	data += len;  	bleft -= len; @@ -317,42 +221,39 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses,  		return;  	kfree(ses->serverDomain); -	ses->serverDomain = cifs_strndup_from_ucs(data, bleft, true, nls_cp); -	cFYI(1, "serverDomain=%s", ses->serverDomain); +	ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); +	cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain);  	return;  } -static int decode_ascii_ssetup(char **pbcc_area, int bleft, -			       struct cifsSesInfo *ses, -			       const struct nls_table *nls_cp) +static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, +				struct cifs_ses *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); +	cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft);  	len = strnlen(bcc_ptr, bleft);  	if (len >= bleft) -		return rc; +		return;  	kfree(ses->serverOS);  	ses->serverOS = kzalloc(len + 1, GFP_KERNEL);  	if (ses->serverOS)  		strncpy(ses->serverOS, bcc_ptr, len); -	if (strncmp(ses->serverOS, "OS/2", 4) == 0) { -			cFYI(1, "OS/2 server"); -			ses->flags |= CIFS_SES_OS2; -	} +	if (strncmp(ses->serverOS, "OS/2", 4) == 0) +		cifs_dbg(FYI, "OS/2 server\n");  	bcc_ptr += len + 1;  	bleft -= len + 1;  	len = strnlen(bcc_ptr, bleft);  	if (len >= bleft) -		return rc; +		return;  	kfree(ses->serverNOS); @@ -365,20 +266,18 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft,  	len = strnlen(bcc_ptr, bleft);  	if (len > bleft) -		return rc; +		return;  	/* No domain field in LANMAN case. Domain is  	   returned by old servers in the SMB negprot response */  	/* BB For newer servers which do not support Unicode,  	   but thus do return domain here we could add parsing  	   for it later, but it is not very important */ -	cFYI(1, "ascii: bytes left %d", bleft); - -	return rc; +	cifs_dbg(FYI, "ascii: bytes left %d\n", bleft);  } -static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, -				    struct cifsSesInfo *ses) +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, +				    struct cifs_ses *ses)  {  	unsigned int tioffset; /* challenge message target info area */  	unsigned int tilen; /* challenge message target info area length  */ @@ -386,16 +285,18 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,  	CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr;  	if (blob_len < sizeof(CHALLENGE_MESSAGE)) { -		cERROR(1, "challenge blob len %d too small", blob_len); +		cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len);  		return -EINVAL;  	}  	if (memcmp(pblob->Signature, "NTLMSSP", 8)) { -		cERROR(1, "blob signature incorrect %s", pblob->Signature); +		cifs_dbg(VFS, "blob signature incorrect %s\n", +			 pblob->Signature);  		return -EINVAL;  	}  	if (pblob->MessageType != NtLmChallenge) { -		cERROR(1, "Incorrect message type %d", pblob->MessageType); +		cifs_dbg(VFS, "Incorrect message type %d\n", +			 pblob->MessageType);  		return -EINVAL;  	} @@ -405,48 +306,52 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,  	/* BB spec says that if AvId field of MsvAvTimestamp is populated then  		we must set the MIC field of the AUTHENTICATE_MESSAGE */  	ses->ntlmssp->server_flags = le32_to_cpu(pblob->NegotiateFlags); -	tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset); -	tilen = cpu_to_le16(pblob->TargetInfoArray.Length); +	tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); +	tilen = le16_to_cpu(pblob->TargetInfoArray.Length); +	if (tioffset > blob_len || tioffset + tilen > blob_len) { +		cifs_dbg(VFS, "tioffset + tilen too high %u + %u", +			tioffset, tilen); +		return -EINVAL; +	}  	if (tilen) { -		ses->auth_key.response = kmalloc(tilen, GFP_KERNEL); +		ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen, +						 GFP_KERNEL);  		if (!ses->auth_key.response) { -			cERROR(1, "Challenge target info allocation failure"); +			cifs_dbg(VFS, "Challenge target info alloc failure");  			return -ENOMEM;  		} -		memcpy(ses->auth_key.response, bcc_ptr + tioffset, tilen);  		ses->auth_key.len = tilen;  	}  	return 0;  } -#ifdef CONFIG_CIFS_EXPERIMENTAL  /* BB Move to ntlmssp.c eventually */  /* We do not malloc the blob, it is passed in pbuffer, because     it is fixed size, and small, making this approach cleaner */ -static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, -					 struct cifsSesInfo *ses) +void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, +					 struct cifs_ses *ses)  {  	NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;  	__u32 flags; +	memset(pbuffer, 0, sizeof(NEGOTIATE_MESSAGE));  	memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);  	sec_blob->MessageType = NtLmNegotiate;  	/* BB is NTLMV2 session security format easier to use here? */  	flags = NTLMSSP_NEGOTIATE_56 |	NTLMSSP_REQUEST_TARGET |  		NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | -		NTLMSSP_NEGOTIATE_NTLM; -	if (ses->server->secMode & -			(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { +		NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; +	if (ses->server->sign) {  		flags |= NTLMSSP_NEGOTIATE_SIGN; -		if (!ses->server->session_estab) -			flags |= NTLMSSP_NEGOTIATE_KEY_XCH | -				NTLMSSP_NEGOTIATE_EXTENDED_SEC; +		if (!ses->server->session_estab || +				ses->ntlmssp->sesskey_per_smbsess) +			flags |= NTLMSSP_NEGOTIATE_KEY_XCH;  	} -	sec_blob->NegotiateFlags |= cpu_to_le32(flags); +	sec_blob->NegotiateFlags = cpu_to_le32(flags);  	sec_blob->WorkstationName.BufferOffset = 0;  	sec_blob->WorkstationName.Length = 0; @@ -461,9 +366,9 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,  /* We do not malloc the blob, it is passed in pbuffer, because its     maximum possible size is fixed and small, making this approach cleaner.     This function returns the length of the data in the blob */ -static int build_ntlmssp_auth_blob(unsigned char *pbuffer, +int build_ntlmssp_auth_blob(unsigned char *pbuffer,  					u16 *buflen, -				   struct cifsSesInfo *ses, +				   struct cifs_ses *ses,  				   const struct nls_table *nls_cp)  {  	int rc; @@ -477,15 +382,16 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,  	flags = NTLMSSP_NEGOTIATE_56 |  		NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |  		NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | -		NTLMSSP_NEGOTIATE_NTLM; -	if (ses->server->secMode & -	   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) +		NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; +	if (ses->server->sign) {  		flags |= NTLMSSP_NEGOTIATE_SIGN; -	if (ses->server->secMode & SECMODE_SIGN_REQUIRED) -		flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; +		if (!ses->server->session_estab || +				ses->ntlmssp->sesskey_per_smbsess) +			flags |= NTLMSSP_NEGOTIATE_KEY_XCH; +	}  	tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE); -	sec_blob->NegotiateFlags |= cpu_to_le32(flags); +	sec_blob->NegotiateFlags = cpu_to_le32(flags);  	sec_blob->LmChallengeResponse.BufferOffset =  				cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE)); @@ -495,7 +401,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,  	sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer);  	rc = setup_ntlmv2_rsp(ses, nls_cp);  	if (rc) { -		cERROR(1, "Error %d during NTLMSSP authentication", rc); +		cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);  		goto setup_ntlmv2_ret;  	}  	memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, @@ -514,8 +420,8 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,  		tmp += 2;  	} else {  		int len; -		len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, -				    MAX_USERNAME_SIZE, nls_cp); +		len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, +				      CIFS_MAX_USERNAME_LEN, nls_cp);  		len *= 2; /* unicode is 2 bytes each */  		sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);  		sec_blob->DomainName.Length = cpu_to_le16(len); @@ -523,15 +429,15 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,  		tmp += len;  	} -	if (ses->userName == NULL) { +	if (ses->user_name == NULL) {  		sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);  		sec_blob->UserName.Length = 0;  		sec_blob->UserName.MaximumLength = 0;  		tmp += 2;  	} else {  		int len; -		len = cifs_strtoUCS((__le16 *)tmp, ses->userName, -				    MAX_USERNAME_SIZE, nls_cp); +		len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, +				      CIFS_MAX_USERNAME_LEN, nls_cp);  		len *= 2; /* unicode is 2 bytes each */  		sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);  		sec_blob->UserName.Length = cpu_to_le16(len); @@ -544,8 +450,9 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,  	sec_blob->WorkstationName.MaximumLength = 0;  	tmp += 2; -	if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && -			!calc_seckey(ses)) { +	if (((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) || +		(ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) +			&& !calc_seckey(ses)) {  		memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);  		sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);  		sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); @@ -563,19 +470,58 @@ setup_ntlmv2_ret:  	return rc;  } - -static void setup_ntlmssp_neg_req(SESSION_SETUP_ANDX *pSMB, -				 struct cifsSesInfo *ses) +enum securityEnum +select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)  { -	build_ntlmssp_negotiate_blob(&pSMB->req.SecurityBlob[0], ses); -	pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE)); - -	return; +	switch (server->negflavor) { +	case CIFS_NEGFLAVOR_EXTENDED: +		switch (requested) { +		case Kerberos: +		case RawNTLMSSP: +			return requested; +		case Unspecified: +			if (server->sec_ntlmssp && +			    (global_secflags & CIFSSEC_MAY_NTLMSSP)) +				return RawNTLMSSP; +			if ((server->sec_kerberos || server->sec_mskerberos) && +			    (global_secflags & CIFSSEC_MAY_KRB5)) +				return Kerberos; +			/* Fallthrough */ +		default: +			return Unspecified; +		} +	case CIFS_NEGFLAVOR_UNENCAP: +		switch (requested) { +		case NTLM: +		case NTLMv2: +			return requested; +		case Unspecified: +			if (global_secflags & CIFSSEC_MAY_NTLMV2) +				return NTLMv2; +			if (global_secflags & CIFSSEC_MAY_NTLM) +				return NTLM; +		default: +			/* Fallthrough to attempt LANMAN authentication next */ +			break; +		} +	case CIFS_NEGFLAVOR_LANMAN: +		switch (requested) { +		case LANMAN: +			return requested; +		case Unspecified: +			if (global_secflags & CIFSSEC_MAY_LANMAN) +				return LANMAN; +			/* Fallthrough */ +		default: +			return Unspecified; +		} +	default: +		return Unspecified; +	}  } -#endif  int -CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, +CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,  	       const struct nls_table *nls_cp)  {  	int rc = 0; @@ -585,22 +531,29 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,  	char *str_area;  	SESSION_SETUP_ANDX *pSMB;  	__u32 capabilities; -	int count; +	__u16 count;  	int resp_buf_type;  	struct kvec iov[3];  	enum securityEnum type; -	__u16 action; -	int bytes_remaining; +	__u16 action, bytes_remaining;  	struct key *spnego_key = NULL;  	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */  	u16 blob_len;  	char *ntlmsspblob = NULL; -	if (ses == NULL) +	if (ses == NULL) { +		WARN(1, "%s: ses == NULL!", __func__);  		return -EINVAL; +	} + +	type = select_sectype(ses->server, ses->sectype); +	cifs_dbg(FYI, "sess setup type %d\n", type); +	if (type == Unspecified) { +		cifs_dbg(VFS, +			"Unable to select appropriate authentication method!"); +		return -EINVAL; +	} -	type = ses->server->secType; -	cFYI(1, "sess setup type %d", type);  	if (type == RawNTLMSSP) {  		/* if memory allocation is successful, caller of this function  		 * frees it. @@ -608,6 +561,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,  		ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);  		if (!ses->ntlmssp)  			return -ENOMEM; +		ses->ntlmssp->sesskey_per_smbsess = false; +  	}  ssetup_ntlmssp_authenticate: @@ -646,7 +601,7 @@ ssetup_ntlmssp_authenticate:  	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; +	iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;  	/* setting this here allows the code at the end of the function  	   to free the request buffer if there's an error */ @@ -660,20 +615,18 @@ ssetup_ntlmssp_authenticate:  	}  	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]; +		char lnm_session_key[CIFS_AUTH_RESP_SIZE];  		pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;  		/* no capabilities flags in old lanman negotiation */ -		pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); +		pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);  		/* Calculate hash with password and copy into bcc_ptr.  		 * Encryption Key (stored as in cryptkey) gets used if the @@ -681,20 +634,19 @@ ssetup_ntlmssp_authenticate:  		 * to use challenge/response method (i.e. Password bit is 1).  		 */ -		calc_lanman_hash(ses->password, ses->server->cryptkey, -				 ses->server->secMode & SECMODE_PW_ENCRYPT ? +		rc = calc_lanman_hash(ses->password, ses->server->cryptkey, +				 ses->server->sec_mode & 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; +		memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); +		bcc_ptr += CIFS_AUTH_RESP_SIZE;  		/* can not sign if LANMAN negotiated so no need  		to calculate signing key? but what if server  		changed to do higher than lanman dialect and  		we reconnected would we ever calc signing_key? */ -		cFYI(1, "Negotiating LANMAN setting up strings"); +		cifs_dbg(FYI, "Negotiating LANMAN setting up strings\n");  		/* Unicode not allowed for LANMAN dialects */  		ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);  #endif @@ -706,9 +658,10 @@ ssetup_ntlmssp_authenticate:  			cpu_to_le16(CIFS_AUTH_RESP_SIZE);  		/* calculate ntlm response and session key */ -		rc = setup_ntlm_response(ses); +		rc = setup_ntlm_response(ses, nls_cp);  		if (rc) { -			cERROR(1, "Error %d during NTLM authentication", rc); +			cifs_dbg(VFS, "Error %d during NTLM authentication\n", +				 rc);  			goto ssetup_exit;  		} @@ -738,7 +691,8 @@ ssetup_ntlmssp_authenticate:  		/* calculate nlmv2 response and session key */  		rc = setup_ntlmv2_rsp(ses, nls_cp);  		if (rc) { -			cERROR(1, "Error %d during NTLMv2 authentication", rc); +			cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", +				 rc);  			goto ssetup_exit;  		}  		memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, @@ -774,21 +728,22 @@ ssetup_ntlmssp_authenticate:  		/* 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_dbg(VFS, "incorrect version of cifs.upcall " +				   "expected %d but got %d)",  				   CIFS_SPNEGO_UPCALL_VERSION, msg->version);  			rc = -EKEYREJECTED;  			goto ssetup_exit;  		} -		ses->auth_key.response = kmalloc(msg->sesskey_len, GFP_KERNEL); +		ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, +						 GFP_KERNEL);  		if (!ses->auth_key.response) { -			cERROR(1, "Kerberos can't allocate (%u bytes) memory", -					msg->sesskey_len); +			cifs_dbg(VFS, +				"Kerberos can't allocate (%u bytes) memory", +				msg->sesskey_len);  			rc = -ENOMEM;  			goto ssetup_exit;  		} -		memcpy(ses->auth_key.response, msg->data, msg->sesskey_len);  		ses->auth_key.len = msg->sesskey_len;  		pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; @@ -810,96 +765,96 @@ ssetup_ntlmssp_authenticate:  		/* BB: is this right? */  			ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);  #else /* ! CONFIG_CIFS_UPCALL */ -		cERROR(1, "Kerberos negotiated but upcall support disabled!"); +		cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");  		rc = -ENOSYS;  		goto ssetup_exit;  #endif /* CONFIG_CIFS_UPCALL */ -	} else { -#ifdef CONFIG_CIFS_EXPERIMENTAL -		if (type == RawNTLMSSP) { -			if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { -				cERROR(1, "NTLMSSP requires Unicode support"); -				rc = -ENOSYS; +	} else if (type == RawNTLMSSP) { +		if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { +			cifs_dbg(VFS, "NTLMSSP requires Unicode support\n"); +			rc = -ENOSYS; +			goto ssetup_exit; +		} + +		cifs_dbg(FYI, "ntlmssp session setup phase %d\n", phase); +		pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; +		capabilities |= CAP_EXTENDED_SECURITY; +		pSMB->req.Capabilities |= cpu_to_le32(capabilities); +		switch(phase) { +		case NtLmNegotiate: +			build_ntlmssp_negotiate_blob( +				pSMB->req.SecurityBlob, ses); +			iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); +			iov[1].iov_base = pSMB->req.SecurityBlob; +			pSMB->req.SecurityBlobLength = +				cpu_to_le16(sizeof(NEGOTIATE_MESSAGE)); +			break; +		case NtLmAuthenticate: +			/* +			 * 5 is an empirical value, large enough to hold +			 * authenticate message plus max 10 of av paris, +			 * domain, user, workstation names, flags, etc. +			 */ +			ntlmsspblob = kzalloc( +				5*sizeof(struct _AUTHENTICATE_MESSAGE), +				GFP_KERNEL); +			if (!ntlmsspblob) { +				rc = -ENOMEM;  				goto ssetup_exit;  			} -			cFYI(1, "ntlmssp session setup phase %d", phase); -			pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; -			capabilities |= CAP_EXTENDED_SECURITY; -			pSMB->req.Capabilities |= cpu_to_le32(capabilities); -			if (phase == NtLmNegotiate) { -				setup_ntlmssp_neg_req(pSMB, ses); -				iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); -				iov[1].iov_base = &pSMB->req.SecurityBlob[0]; -			} else if (phase == NtLmAuthenticate) { -				/* 5 is an empirical value, large enought to -				 * hold authenticate message, max 10 of -				 * av paris, doamin,user,workstation mames, -				 * flags etc.. -				 */ -				ntlmsspblob = kmalloc( -					5*sizeof(struct _AUTHENTICATE_MESSAGE), -					GFP_KERNEL); -				if (!ntlmsspblob) { -					cERROR(1, "Can't allocate NTLMSSP"); -					rc = -ENOMEM; -					goto ssetup_exit; -				} - -				rc = build_ntlmssp_auth_blob(ntlmsspblob, -							&blob_len, ses, nls_cp); -				if (rc) -					goto ssetup_exit; -				iov[1].iov_len = blob_len; -				iov[1].iov_base = ntlmsspblob; -				pSMB->req.SecurityBlobLength = -					cpu_to_le16(blob_len); -				/* Make sure that we tell the server that we -				   are using the uid that it just gave us back -				   on the response (challenge) */ -				smb_buf->Uid = ses->Suid; -			} else { -				cERROR(1, "invalid phase %d", phase); -				rc = -ENOSYS; +			rc = build_ntlmssp_auth_blob(ntlmsspblob, +						&blob_len, ses, nls_cp); +			if (rc)  				goto ssetup_exit; -			} -			/* unicode strings must be word aligned */ -			if ((iov[0].iov_len + iov[1].iov_len) % 2) { -				*bcc_ptr = 0; -				bcc_ptr++; -			} -			unicode_oslm_strings(&bcc_ptr, nls_cp); -		} else { -			cERROR(1, "secType %d not supported!", type); +			iov[1].iov_len = blob_len; +			iov[1].iov_base = ntlmsspblob; +			pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); +			/* +			 * Make sure that we tell the server that we are using +			 * the uid that it just gave us back on the response +			 * (challenge) +			 */ +			smb_buf->Uid = ses->Suid; +			break; +		default: +			cifs_dbg(VFS, "invalid phase %d\n", phase);  			rc = -ENOSYS;  			goto ssetup_exit;  		} -#else -		cERROR(1, "secType %d not supported!", type); +		/* unicode strings must be word aligned */ +		if ((iov[0].iov_len + iov[1].iov_len) % 2) { +			*bcc_ptr = 0; +			bcc_ptr++; +		} +		unicode_oslm_strings(&bcc_ptr, nls_cp); +	} else { +		cifs_dbg(VFS, "secType %d not supported!\n", type);  		rc = -ENOSYS;  		goto ssetup_exit; -#endif  	}  	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; +	smb_buf->smb_buf_length = +		cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count); -	BCC_LE(smb_buf) = cpu_to_le16(count); +	put_bcc(count, smb_buf);  	rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, -			  CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); +			  CIFS_LOG_ERROR);  	/* SMB request buf freed in SendReceive2 */  	pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;  	smb_buf = (struct smb_hdr *)iov[0].iov_base; -	if ((type == RawNTLMSSP) && (smb_buf->Status.CifsError == +	if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) && +	    (smb_buf->Status.CifsError ==  			cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {  		if (phase != NtLmNegotiate) { -			cERROR(1, "Unexpected more processing error"); +			cifs_dbg(VFS, "Unexpected more processing error\n");  			goto ssetup_exit;  		}  		/* NTLMSSP Negotiate sent now processing challenge (response) */ @@ -911,23 +866,24 @@ ssetup_ntlmssp_authenticate:  	if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {  		rc = -EIO; -		cERROR(1, "bad word count %d", smb_buf->WordCount); +		cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);  		goto ssetup_exit;  	}  	action = le16_to_cpu(pSMB->resp.Action);  	if (action & GUEST_LOGIN) -		cFYI(1, "Guest login"); /* BB mark SesInfo struct? */ +		cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */  	ses->Suid = smb_buf->Uid;   /* UID left in wire format (le) */ -	cFYI(1, "UID = %d ", ses->Suid); +	cifs_dbg(FYI, "UID = %llu\n", ses->Suid);  	/* response can have either 3 or 4 word count - Samba sends 3 */  	/* and lanman response is 3 */ -	bytes_remaining = BCC(smb_buf); +	bytes_remaining = get_bcc(smb_buf);  	bcc_ptr = pByteArea(smb_buf);  	if (smb_buf->WordCount == 4) {  		blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);  		if (blob_len > bytes_remaining) { -			cERROR(1, "bad security blob length %d", blob_len); +			cifs_dbg(VFS, "bad security blob length %d\n", +				 blob_len);  			rc = -EINVAL;  			goto ssetup_exit;  		} @@ -942,7 +898,9 @@ ssetup_ntlmssp_authenticate:  	}  	/* BB check if Unicode and decode strings */ -	if (smb_buf->Flags2 & SMBFLG2_UNICODE) { +	if (bytes_remaining == 0) { +		/* no string area to decode, do nothing */ +	} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {  		/* unicode string area must be word-aligned */  		if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {  			++bcc_ptr; @@ -950,20 +908,19 @@ ssetup_ntlmssp_authenticate:  		}  		decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);  	} else { -		rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, -					 ses, nls_cp); +		decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp);  	}  ssetup_exit:  	if (spnego_key) { -		key_revoke(spnego_key); +		key_invalidate(spnego_key);  		key_put(spnego_key);  	}  	kfree(str_area);  	kfree(ntlmsspblob);  	ntlmsspblob = NULL;  	if (resp_buf_type == CIFS_SMALL_BUFFER) { -		cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base); +		cifs_dbg(FYI, "ssetup freeing small buf %p\n", iov[0].iov_base);  		cifs_small_buf_release(iov[0].iov_base);  	} else if (resp_buf_type == CIFS_LARGE_BUFFER)  		cifs_buf_release(iov[0].iov_base); @@ -972,5 +929,37 @@ ssetup_exit:  	if ((phase == NtLmChallenge) && (rc == 0))  		goto ssetup_ntlmssp_authenticate; +	if (!rc) { +		mutex_lock(&ses->server->srv_mutex); +		if (!ses->server->session_estab) { +			if (ses->server->sign) { +				ses->server->session_key.response = +					kmemdup(ses->auth_key.response, +					ses->auth_key.len, GFP_KERNEL); +				if (!ses->server->session_key.response) { +					rc = -ENOMEM; +					mutex_unlock(&ses->server->srv_mutex); +					goto keycp_exit; +				} +				ses->server->session_key.len = +							ses->auth_key.len; +			} +			ses->server->sequence_number = 0x2; +			ses->server->session_estab = true; +		} +		mutex_unlock(&ses->server->srv_mutex); + +		cifs_dbg(FYI, "CIFS session established successfully\n"); +		spin_lock(&GlobalMid_Lock); +		ses->status = CifsGood; +		ses->need_reconnect = false; +		spin_unlock(&GlobalMid_Lock); +	} + +keycp_exit: +	kfree(ses->auth_key.response); +	ses->auth_key.response = NULL; +	kfree(ses->ntlmssp); +  	return rc;  } diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c new file mode 100644 index 00000000000..d1fdfa84870 --- /dev/null +++ b/fs/cifs/smb1ops.c @@ -0,0 +1,1107 @@ +/* + *  SMB1 (CIFS) version specific operations + * + *  Copyright (c) 2012, Jeff Layton <jlayton@redhat.com> + * + *  This library is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License v2 as published + *  by the Free Software Foundation. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *  the GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with this library; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/pagemap.h> +#include <linux/vfs.h> +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifspdu.h" + +/* + * An NT cancel request header looks just like the original request except: + * + * The Command is SMB_COM_NT_CANCEL + * The WordCount is zeroed out + * The ByteCount is zeroed out + * + * This function mangles an existing request buffer into a + * SMB_COM_NT_CANCEL request and then sends it. + */ +static int +send_nt_cancel(struct TCP_Server_Info *server, void *buf, +	       struct mid_q_entry *mid) +{ +	int rc = 0; +	struct smb_hdr *in_buf = (struct smb_hdr *)buf; + +	/* -4 for RFC1001 length and +2 for BCC field */ +	in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4  + 2); +	in_buf->Command = SMB_COM_NT_CANCEL; +	in_buf->WordCount = 0; +	put_bcc(0, in_buf); + +	mutex_lock(&server->srv_mutex); +	rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); +	if (rc) { +		mutex_unlock(&server->srv_mutex); +		return rc; +	} + +	/* +	 * The response to this call was already factored into the sequence +	 * number when the call went out, so we must adjust it back downward +	 * after signing here. +	 */ +	--server->sequence_number; +	rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); +	if (rc < 0) +		server->sequence_number--; + +	mutex_unlock(&server->srv_mutex); + +	cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", +		 get_mid(in_buf), rc); + +	return rc; +} + +static bool +cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ +	return ob1->fid.netfid == ob2->fid.netfid; +} + +static unsigned int +cifs_read_data_offset(char *buf) +{ +	READ_RSP *rsp = (READ_RSP *)buf; +	return le16_to_cpu(rsp->DataOffset); +} + +static unsigned int +cifs_read_data_length(char *buf) +{ +	READ_RSP *rsp = (READ_RSP *)buf; +	return (le16_to_cpu(rsp->DataLengthHigh) << 16) + +	       le16_to_cpu(rsp->DataLength); +} + +static struct mid_q_entry * +cifs_find_mid(struct TCP_Server_Info *server, char *buffer) +{ +	struct smb_hdr *buf = (struct smb_hdr *)buffer; +	struct mid_q_entry *mid; + +	spin_lock(&GlobalMid_Lock); +	list_for_each_entry(mid, &server->pending_mid_q, qhead) { +		if (compare_mid(mid->mid, buf) && +		    mid->mid_state == MID_REQUEST_SUBMITTED && +		    le16_to_cpu(mid->command) == buf->Command) { +			spin_unlock(&GlobalMid_Lock); +			return mid; +		} +	} +	spin_unlock(&GlobalMid_Lock); +	return NULL; +} + +static void +cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add, +		 const int optype) +{ +	spin_lock(&server->req_lock); +	server->credits += add; +	server->in_flight--; +	spin_unlock(&server->req_lock); +	wake_up(&server->request_q); +} + +static void +cifs_set_credits(struct TCP_Server_Info *server, const int val) +{ +	spin_lock(&server->req_lock); +	server->credits = val; +	server->oplocks = val > 1 ? enable_oplocks : false; +	spin_unlock(&server->req_lock); +} + +static int * +cifs_get_credits_field(struct TCP_Server_Info *server, const int optype) +{ +	return &server->credits; +} + +static unsigned int +cifs_get_credits(struct mid_q_entry *mid) +{ +	return 1; +} + +/* + * Find a free multiplex id (SMB mid). Otherwise there could be + * mid collisions which might cause problems, demultiplexing the + * wrong response to this request. Multiplex ids could collide if + * one of a series requests takes much longer than the others, or + * if a very large number of long lived requests (byte range + * locks or FindNotify requests) are pending. No more than + * 64K-1 requests can be outstanding at one time. If no + * mids are available, return zero. A future optimization + * could make the combination of mids and uid the key we use + * to demultiplex on (rather than mid alone). + * In addition to the above check, the cifs demultiplex + * code already used the command code as a secondary + * check of the frame and if signing is negotiated the + * response would be discarded if the mid were the same + * but the signature was wrong. Since the mid is not put in the + * pending queue until later (when it is about to be dispatched) + * we do have to limit the number of outstanding requests + * to somewhat less than 64K-1 although it is hard to imagine + * so many threads being in the vfs at one time. + */ +static __u64 +cifs_get_next_mid(struct TCP_Server_Info *server) +{ +	__u64 mid = 0; +	__u16 last_mid, cur_mid; +	bool collision; + +	spin_lock(&GlobalMid_Lock); + +	/* mid is 16 bit only for CIFS/SMB */ +	cur_mid = (__u16)((server->CurrentMid) & 0xffff); +	/* we do not want to loop forever */ +	last_mid = cur_mid; +	cur_mid++; + +	/* +	 * This nested loop looks more expensive than it is. +	 * In practice the list of pending requests is short, +	 * fewer than 50, and the mids are likely to be unique +	 * on the first pass through the loop unless some request +	 * takes longer than the 64 thousand requests before it +	 * (and it would also have to have been a request that +	 * did not time out). +	 */ +	while (cur_mid != last_mid) { +		struct mid_q_entry *mid_entry; +		unsigned int num_mids; + +		collision = false; +		if (cur_mid == 0) +			cur_mid++; + +		num_mids = 0; +		list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { +			++num_mids; +			if (mid_entry->mid == cur_mid && +			    mid_entry->mid_state == MID_REQUEST_SUBMITTED) { +				/* This mid is in use, try a different one */ +				collision = true; +				break; +			} +		} + +		/* +		 * if we have more than 32k mids in the list, then something +		 * is very wrong. Possibly a local user is trying to DoS the +		 * box by issuing long-running calls and SIGKILL'ing them. If +		 * we get to 2^16 mids then we're in big trouble as this +		 * function could loop forever. +		 * +		 * Go ahead and assign out the mid in this situation, but force +		 * an eventual reconnect to clean out the pending_mid_q. +		 */ +		if (num_mids > 32768) +			server->tcpStatus = CifsNeedReconnect; + +		if (!collision) { +			mid = (__u64)cur_mid; +			server->CurrentMid = mid; +			break; +		} +		cur_mid++; +	} +	spin_unlock(&GlobalMid_Lock); +	return mid; +} + +/* +	return codes: +		0	not a transact2, or all data present +		>0	transact2 with that much data missing +		-EINVAL	invalid transact2 + */ +static int +check2ndT2(char *buf) +{ +	struct smb_hdr *pSMB = (struct smb_hdr *)buf; +	struct smb_t2_rsp *pSMBt; +	int remaining; +	__u16 total_data_size, data_in_this_rsp; + +	if (pSMB->Command != SMB_COM_TRANSACTION2) +		return 0; + +	/* check for plausible wct, bcc and t2 data and parm sizes */ +	/* check for parm and data offset going beyond end of smb */ +	if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ +		cifs_dbg(FYI, "invalid transact2 word count\n"); +		return -EINVAL; +	} + +	pSMBt = (struct smb_t2_rsp *)pSMB; + +	total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); +	data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + +	if (total_data_size == data_in_this_rsp) +		return 0; +	else if (total_data_size < data_in_this_rsp) { +		cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", +			 total_data_size, data_in_this_rsp); +		return -EINVAL; +	} + +	remaining = total_data_size - data_in_this_rsp; + +	cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", +		 remaining); +	if (total_data_size > CIFSMaxBufSize) { +		cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", +			 total_data_size, CIFSMaxBufSize); +		return -EINVAL; +	} +	return remaining; +} + +static int +coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) +{ +	struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; +	struct smb_t2_rsp *pSMBt  = (struct smb_t2_rsp *)target_hdr; +	char *data_area_of_tgt; +	char *data_area_of_src; +	int remaining; +	unsigned int byte_count, total_in_tgt; +	__u16 tgt_total_cnt, src_total_cnt, total_in_src; + +	src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); +	tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + +	if (tgt_total_cnt != src_total_cnt) +		cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", +			 src_total_cnt, tgt_total_cnt); + +	total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + +	remaining = tgt_total_cnt - total_in_tgt; + +	if (remaining < 0) { +		cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%hu\n", +			 tgt_total_cnt, total_in_tgt); +		return -EPROTO; +	} + +	if (remaining == 0) { +		/* nothing to do, ignore */ +		cifs_dbg(FYI, "no more data remains\n"); +		return 0; +	} + +	total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); +	if (remaining < total_in_src) +		cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); + +	/* find end of first SMB data area */ +	data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + +				get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); + +	/* validate target area */ +	data_area_of_src = (char *)&pSMBs->hdr.Protocol + +				get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); + +	data_area_of_tgt += total_in_tgt; + +	total_in_tgt += total_in_src; +	/* is the result too big for the field? */ +	if (total_in_tgt > USHRT_MAX) { +		cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", +			 total_in_tgt); +		return -EPROTO; +	} +	put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); + +	/* fix up the BCC */ +	byte_count = get_bcc(target_hdr); +	byte_count += total_in_src; +	/* is the result too big for the field? */ +	if (byte_count > USHRT_MAX) { +		cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); +		return -EPROTO; +	} +	put_bcc(byte_count, target_hdr); + +	byte_count = be32_to_cpu(target_hdr->smb_buf_length); +	byte_count += total_in_src; +	/* don't allow buffer to overflow */ +	if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { +		cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", +			 byte_count); +		return -ENOBUFS; +	} +	target_hdr->smb_buf_length = cpu_to_be32(byte_count); + +	/* copy second buffer into end of first buffer */ +	memcpy(data_area_of_tgt, data_area_of_src, total_in_src); + +	if (remaining != total_in_src) { +		/* more responses to go */ +		cifs_dbg(FYI, "waiting for more secondary responses\n"); +		return 1; +	} + +	/* we are done */ +	cifs_dbg(FYI, "found the last secondary response\n"); +	return 0; +} + +static void +cifs_downgrade_oplock(struct TCP_Server_Info *server, +			struct cifsInodeInfo *cinode, bool set_level2) +{ +	if (set_level2) +		cifs_set_oplock_level(cinode, OPLOCK_READ); +	else +		cifs_set_oplock_level(cinode, 0); +} + +static bool +cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, +		  char *buf, int malformed) +{ +	if (malformed) +		return false; +	if (check2ndT2(buf) <= 0) +		return false; +	mid->multiRsp = true; +	if (mid->resp_buf) { +		/* merge response - fix up 1st*/ +		malformed = coalesce_t2(buf, mid->resp_buf); +		if (malformed > 0) +			return true; +		/* All parts received or packet is malformed. */ +		mid->multiEnd = true; +		dequeue_mid(mid, malformed); +		return true; +	} +	if (!server->large_buf) { +		/*FIXME: switch to already allocated largebuf?*/ +		cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); +	} else { +		/* Have first buffer */ +		mid->resp_buf = buf; +		mid->large_buf = true; +		server->bigbuf = NULL; +	} +	return true; +} + +static bool +cifs_need_neg(struct TCP_Server_Info *server) +{ +	return server->maxBuf == 0; +} + +static int +cifs_negotiate(const unsigned int xid, struct cifs_ses *ses) +{ +	int rc; +	rc = CIFSSMBNegotiate(xid, ses); +	if (rc == -EAGAIN) { +		/* retry only once on 1st time connection */ +		set_credits(ses->server, 1); +		rc = CIFSSMBNegotiate(xid, ses); +		if (rc == -EAGAIN) +			rc = -EHOSTDOWN; +	} +	return rc; +} + +static unsigned int +cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +{ +	__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); +	struct TCP_Server_Info *server = tcon->ses->server; +	unsigned int wsize; + +	/* start with specified wsize, or default */ +	if (volume_info->wsize) +		wsize = volume_info->wsize; +	else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) +		wsize = CIFS_DEFAULT_IOSIZE; +	else +		wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; + +	/* can server support 24-bit write sizes? (via UNIX extensions) */ +	if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) +		wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE); + +	/* +	 * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? +	 * Limit it to max buffer offered by the server, minus the size of the +	 * WRITEX header, not including the 4 byte RFC1001 length. +	 */ +	if (!(server->capabilities & CAP_LARGE_WRITE_X) || +	    (!(server->capabilities & CAP_UNIX) && server->sign)) +		wsize = min_t(unsigned int, wsize, +				server->maxBuf - sizeof(WRITE_REQ) + 4); + +	/* hard limit of CIFS_MAX_WSIZE */ +	wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE); + +	return wsize; +} + +static unsigned int +cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +{ +	__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); +	struct TCP_Server_Info *server = tcon->ses->server; +	unsigned int rsize, defsize; + +	/* +	 * Set default value... +	 * +	 * HACK alert! Ancient servers have very small buffers. Even though +	 * MS-CIFS indicates that servers are only limited by the client's +	 * bufsize for reads, testing against win98se shows that it throws +	 * INVALID_PARAMETER errors if you try to request too large a read. +	 * OS/2 just sends back short reads. +	 * +	 * If the server doesn't advertise CAP_LARGE_READ_X, then assume that +	 * it can't handle a read request larger than its MaxBufferSize either. +	 */ +	if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP)) +		defsize = CIFS_DEFAULT_IOSIZE; +	else if (server->capabilities & CAP_LARGE_READ_X) +		defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; +	else +		defsize = server->maxBuf - sizeof(READ_RSP); + +	rsize = volume_info->rsize ? volume_info->rsize : defsize; + +	/* +	 * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to +	 * the client's MaxBufferSize. +	 */ +	if (!(server->capabilities & CAP_LARGE_READ_X)) +		rsize = min_t(unsigned int, CIFSMaxBufSize, rsize); + +	/* hard limit of CIFS_MAX_RSIZE */ +	rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE); + +	return rsize; +} + +static void +cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ +	CIFSSMBQFSDeviceInfo(xid, tcon); +	CIFSSMBQFSAttributeInfo(xid, tcon); +} + +static int +cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, +			struct cifs_sb_info *cifs_sb, const char *full_path) +{ +	int rc; +	FILE_ALL_INFO *file_info; + +	file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); +	if (file_info == NULL) +		return -ENOMEM; + +	rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, +			      0 /* not legacy */, cifs_sb->local_nls, +			      cifs_sb->mnt_cifs_flags & +				CIFS_MOUNT_MAP_SPECIAL_CHR); + +	if (rc == -EOPNOTSUPP || rc == -EINVAL) +		rc = SMBQueryInformation(xid, tcon, full_path, file_info, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +				  CIFS_MOUNT_MAP_SPECIAL_CHR); +	kfree(file_info); +	return rc; +} + +static int +cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, +		     struct cifs_sb_info *cifs_sb, const char *full_path, +		     FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink) +{ +	int rc; + +	*symlink = false; + +	/* could do find first instead but this returns more info */ +	rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, +			      cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR); +	/* +	 * BB optimize code so we do not make the above call when server claims +	 * no NT SMB support and the above call failed at least once - set flag +	 * in tcon or mount. +	 */ +	if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { +		rc = SMBQueryInformation(xid, tcon, full_path, data, +					 cifs_sb->local_nls, +					 cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR); +		*adjustTZ = true; +	} + +	if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) { +		int tmprc; +		int oplock = 0; +		struct cifs_fid fid; +		struct cifs_open_parms oparms; + +		oparms.tcon = tcon; +		oparms.cifs_sb = cifs_sb; +		oparms.desired_access = FILE_READ_ATTRIBUTES; +		oparms.create_options = 0; +		oparms.disposition = FILE_OPEN; +		oparms.path = full_path; +		oparms.fid = &fid; +		oparms.reconnect = false; + +		/* Need to check if this is a symbolic link or not */ +		tmprc = CIFS_open(xid, &oparms, &oplock, NULL); +		if (tmprc == -EOPNOTSUPP) +			*symlink = true; +		else +			CIFSSMBClose(xid, tcon, fid.netfid); +	} + +	return rc; +} + +static int +cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, +		  struct cifs_sb_info *cifs_sb, const char *full_path, +		  u64 *uniqueid, FILE_ALL_INFO *data) +{ +	/* +	 * We can not use the IndexNumber field by default from Windows or +	 * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA +	 * CIFS spec claims that this value is unique within the scope of a +	 * share, and the windows docs hint that it's actually unique +	 * per-machine. +	 * +	 * There may be higher info levels that work but are there Windows +	 * server or network appliances for which IndexNumber field is not +	 * guaranteed unique? +	 */ +	return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid, +				     cifs_sb->local_nls, +				     cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR); +} + +static int +cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, +		     struct cifs_fid *fid, FILE_ALL_INFO *data) +{ +	return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data); +} + +static void +cifs_clear_stats(struct cifs_tcon *tcon) +{ +#ifdef CONFIG_CIFS_STATS +	atomic_set(&tcon->stats.cifs_stats.num_writes, 0); +	atomic_set(&tcon->stats.cifs_stats.num_reads, 0); +	atomic_set(&tcon->stats.cifs_stats.num_flushes, 0); +	atomic_set(&tcon->stats.cifs_stats.num_oplock_brks, 0); +	atomic_set(&tcon->stats.cifs_stats.num_opens, 0); +	atomic_set(&tcon->stats.cifs_stats.num_posixopens, 0); +	atomic_set(&tcon->stats.cifs_stats.num_posixmkdirs, 0); +	atomic_set(&tcon->stats.cifs_stats.num_closes, 0); +	atomic_set(&tcon->stats.cifs_stats.num_deletes, 0); +	atomic_set(&tcon->stats.cifs_stats.num_mkdirs, 0); +	atomic_set(&tcon->stats.cifs_stats.num_rmdirs, 0); +	atomic_set(&tcon->stats.cifs_stats.num_renames, 0); +	atomic_set(&tcon->stats.cifs_stats.num_t2renames, 0); +	atomic_set(&tcon->stats.cifs_stats.num_ffirst, 0); +	atomic_set(&tcon->stats.cifs_stats.num_fnext, 0); +	atomic_set(&tcon->stats.cifs_stats.num_fclose, 0); +	atomic_set(&tcon->stats.cifs_stats.num_hardlinks, 0); +	atomic_set(&tcon->stats.cifs_stats.num_symlinks, 0); +	atomic_set(&tcon->stats.cifs_stats.num_locks, 0); +	atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0); +	atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); +#endif +} + +static void +cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ +#ifdef CONFIG_CIFS_STATS +	seq_printf(m, " Oplocks breaks: %d", +		   atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); +	seq_printf(m, "\nReads:  %d Bytes: %llu", +		   atomic_read(&tcon->stats.cifs_stats.num_reads), +		   (long long)(tcon->bytes_read)); +	seq_printf(m, "\nWrites: %d Bytes: %llu", +		   atomic_read(&tcon->stats.cifs_stats.num_writes), +		   (long long)(tcon->bytes_written)); +	seq_printf(m, "\nFlushes: %d", +		   atomic_read(&tcon->stats.cifs_stats.num_flushes)); +	seq_printf(m, "\nLocks: %d HardLinks: %d Symlinks: %d", +		   atomic_read(&tcon->stats.cifs_stats.num_locks), +		   atomic_read(&tcon->stats.cifs_stats.num_hardlinks), +		   atomic_read(&tcon->stats.cifs_stats.num_symlinks)); +	seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", +		   atomic_read(&tcon->stats.cifs_stats.num_opens), +		   atomic_read(&tcon->stats.cifs_stats.num_closes), +		   atomic_read(&tcon->stats.cifs_stats.num_deletes)); +	seq_printf(m, "\nPosix Opens: %d Posix Mkdirs: %d", +		   atomic_read(&tcon->stats.cifs_stats.num_posixopens), +		   atomic_read(&tcon->stats.cifs_stats.num_posixmkdirs)); +	seq_printf(m, "\nMkdirs: %d Rmdirs: %d", +		   atomic_read(&tcon->stats.cifs_stats.num_mkdirs), +		   atomic_read(&tcon->stats.cifs_stats.num_rmdirs)); +	seq_printf(m, "\nRenames: %d T2 Renames %d", +		   atomic_read(&tcon->stats.cifs_stats.num_renames), +		   atomic_read(&tcon->stats.cifs_stats.num_t2renames)); +	seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", +		   atomic_read(&tcon->stats.cifs_stats.num_ffirst), +		   atomic_read(&tcon->stats.cifs_stats.num_fnext), +		   atomic_read(&tcon->stats.cifs_stats.num_fclose)); +#endif +} + +static void +cifs_mkdir_setinfo(struct inode *inode, const char *full_path, +		   struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, +		   const unsigned int xid) +{ +	FILE_BASIC_INFO info; +	struct cifsInodeInfo *cifsInode; +	u32 dosattrs; +	int rc; + +	memset(&info, 0, sizeof(info)); +	cifsInode = CIFS_I(inode); +	dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; +	info.Attributes = cpu_to_le32(dosattrs); +	rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, +				cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (rc == 0) +		cifsInode->cifsAttrs = dosattrs; +} + +static int +cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, +	       __u32 *oplock, FILE_ALL_INFO *buf) +{ +	if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) +		return SMBLegacyOpen(xid, oparms->tcon, oparms->path, +				     oparms->disposition, +				     oparms->desired_access, +				     oparms->create_options, +				     &oparms->fid->netfid, oplock, buf, +				     oparms->cifs_sb->local_nls, +				     oparms->cifs_sb->mnt_cifs_flags +						& CIFS_MOUNT_MAP_SPECIAL_CHR); +	return CIFS_open(xid, oparms, oplock, buf); +} + +static void +cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) +{ +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	cfile->fid.netfid = fid->netfid; +	cifs_set_oplock_level(cinode, oplock); +	cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); +} + +static void +cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon, +		struct cifs_fid *fid) +{ +	CIFSSMBClose(xid, tcon, fid->netfid); +} + +static int +cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon, +		struct cifs_fid *fid) +{ +	return CIFSSMBFlush(xid, tcon, fid->netfid); +} + +static int +cifs_sync_read(const unsigned int xid, struct cifsFileInfo *cfile, +	       struct cifs_io_parms *parms, unsigned int *bytes_read, +	       char **buf, int *buf_type) +{ +	parms->netfid = cfile->fid.netfid; +	return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type); +} + +static int +cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile, +		struct cifs_io_parms *parms, unsigned int *written, +		struct kvec *iov, unsigned long nr_segs) +{ + +	parms->netfid = cfile->fid.netfid; +	return CIFSSMBWrite2(xid, parms, written, iov, nr_segs); +} + +static int +smb_set_file_info(struct inode *inode, const char *full_path, +		  FILE_BASIC_INFO *buf, const unsigned int xid) +{ +	int oplock = 0; +	int rc; +	__u32 netpid; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; +	struct cifsFileInfo *open_file; +	struct cifsInodeInfo *cinode = CIFS_I(inode); +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct tcon_link *tlink = NULL; +	struct cifs_tcon *tcon; + +	/* if the file is already open for write, just use that fileid */ +	open_file = find_writable_file(cinode, true); +	if (open_file) { +		fid.netfid = open_file->fid.netfid; +		netpid = open_file->pid; +		tcon = tlink_tcon(open_file->tlink); +		goto set_via_filehandle; +	} + +	tlink = cifs_sb_tlink(cifs_sb); +	if (IS_ERR(tlink)) { +		rc = PTR_ERR(tlink); +		tlink = NULL; +		goto out; +	} +	tcon = tlink_tcon(tlink); + +	rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, +					cifs_sb->mnt_cifs_flags & +						CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (rc == 0) { +		cinode->cifsAttrs = le32_to_cpu(buf->Attributes); +		goto out; +	} else if (rc != -EOPNOTSUPP && rc != -EINVAL) { +		goto out; +	} + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc != 0) { +		if (rc == -EIO) +			rc = -EINVAL; +		goto out; +	} + +	netpid = current->tgid; + +set_via_filehandle: +	rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid); +	if (!rc) +		cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + +	if (open_file == NULL) +		CIFSSMBClose(xid, tcon, fid.netfid); +	else +		cifsFileInfo_put(open_file); +out: +	if (tlink != NULL) +		cifs_put_tlink(tlink); +	return rc; +} + +static int +cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifsFileInfo *cfile) +{ +	return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); +} + +static int +cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, +		     const char *path, struct cifs_sb_info *cifs_sb, +		     struct cifs_fid *fid, __u16 search_flags, +		     struct cifs_search_info *srch_inf) +{ +	return CIFSFindFirst(xid, tcon, path, cifs_sb, +			     &fid->netfid, search_flags, srch_inf, true); +} + +static int +cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, +		    struct cifs_fid *fid, __u16 search_flags, +		    struct cifs_search_info *srch_inf) +{ +	return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf); +} + +static int +cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon, +	       struct cifs_fid *fid) +{ +	return CIFSFindClose(xid, tcon, fid->netfid); +} + +static int +cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, +		     struct cifsInodeInfo *cinode) +{ +	return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0, +			   LOCKING_ANDX_OPLOCK_RELEASE, false, +			   CIFS_CACHE_READ(cinode) ? 1 : 0); +} + +static int +cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon, +	     struct kstatfs *buf) +{ +	int rc = -EOPNOTSUPP; + +	buf->f_type = CIFS_MAGIC_NUMBER; + +	/* +	 * We could add a second check for a QFS Unix capability bit +	 */ +	if ((tcon->ses->capabilities & CAP_UNIX) && +	    (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability))) +		rc = CIFSSMBQFSPosixInfo(xid, tcon, buf); + +	/* +	 * Only need to call the old QFSInfo if failed on newer one, +	 * e.g. by OS/2. +	 **/ +	if (rc && (tcon->ses->capabilities & CAP_NT_SMBS)) +		rc = CIFSSMBQFSInfo(xid, tcon, buf); + +	/* +	 * Some old Windows servers also do not support level 103, retry with +	 * older level one if old server failed the previous call or we +	 * bypassed it because we detected that this was an older LANMAN sess +	 */ +	if (rc) +		rc = SMBOldQFSInfo(xid, tcon, buf); +	return rc; +} + +static int +cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, +	       __u64 length, __u32 type, int lock, int unlock, bool wait) +{ +	return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid, +			   current->tgid, length, offset, unlock, lock, +			   (__u8)type, wait, 0); +} + +static int +cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, +		       const unsigned char *searchName, char **symlinkinfo, +		       const struct nls_table *nls_codepage) +{ +#ifdef CONFIG_CIFS_DFS_UPCALL +	int rc; +	unsigned int num_referrals = 0; +	struct dfs_info3_param *referrals = NULL; + +	rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, +			  &num_referrals, &referrals, 0); + +	if (!rc && num_referrals > 0) { +		*symlinkinfo = kstrndup(referrals->node_name, +					strlen(referrals->node_name), +					GFP_KERNEL); +		if (!*symlinkinfo) +			rc = -ENOMEM; +		free_dfs_info_array(referrals, num_referrals); +	} +	return rc; +#else /* No DFS support */ +	return -EREMOTE; +#endif +} + +static int +cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, +		   const char *full_path, char **target_path, +		   struct cifs_sb_info *cifs_sb) +{ +	int rc; +	int oplock = 0; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; + +	cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + +	/* Check for unix extensions */ +	if (cap_unix(tcon->ses)) { +		rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, +					     cifs_sb->local_nls); +		if (rc == -EREMOTE) +			rc = cifs_unix_dfs_readlink(xid, tcon, full_path, +						    target_path, +						    cifs_sb->local_nls); + +		goto out; +	} + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.create_options = OPEN_REPARSE_POINT; +	oparms.disposition = FILE_OPEN; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc) +		goto out; + +	rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path, +				 cifs_sb->local_nls); +	if (rc) +		goto out_close; + +	convert_delimiter(*target_path, '/'); +out_close: +	CIFSSMBClose(xid, tcon, fid.netfid); +out: +	if (!rc) +		cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); +	return rc; +} + +static bool +cifs_is_read_op(__u32 oplock) +{ +	return oplock == OPLOCK_READ; +} + +struct smb_version_operations smb1_operations = { +	.send_cancel = send_nt_cancel, +	.compare_fids = cifs_compare_fids, +	.setup_request = cifs_setup_request, +	.setup_async_request = cifs_setup_async_request, +	.check_receive = cifs_check_receive, +	.add_credits = cifs_add_credits, +	.set_credits = cifs_set_credits, +	.get_credits_field = cifs_get_credits_field, +	.get_credits = cifs_get_credits, +	.get_next_mid = cifs_get_next_mid, +	.read_data_offset = cifs_read_data_offset, +	.read_data_length = cifs_read_data_length, +	.map_error = map_smb_to_linux_error, +	.find_mid = cifs_find_mid, +	.check_message = checkSMB, +	.dump_detail = cifs_dump_detail, +	.clear_stats = cifs_clear_stats, +	.print_stats = cifs_print_stats, +	.is_oplock_break = is_valid_oplock_break, +	.downgrade_oplock = cifs_downgrade_oplock, +	.check_trans2 = cifs_check_trans2, +	.need_neg = cifs_need_neg, +	.negotiate = cifs_negotiate, +	.negotiate_wsize = cifs_negotiate_wsize, +	.negotiate_rsize = cifs_negotiate_rsize, +	.sess_setup = CIFS_SessSetup, +	.logoff = CIFSSMBLogoff, +	.tree_connect = CIFSTCon, +	.tree_disconnect = CIFSSMBTDis, +	.get_dfs_refer = CIFSGetDFSRefer, +	.qfs_tcon = cifs_qfs_tcon, +	.is_path_accessible = cifs_is_path_accessible, +	.query_path_info = cifs_query_path_info, +	.query_file_info = cifs_query_file_info, +	.get_srv_inum = cifs_get_srv_inum, +	.set_path_size = CIFSSMBSetEOF, +	.set_file_size = CIFSSMBSetFileSize, +	.set_file_info = smb_set_file_info, +	.set_compression = cifs_set_compression, +	.echo = CIFSSMBEcho, +	.mkdir = CIFSSMBMkDir, +	.mkdir_setinfo = cifs_mkdir_setinfo, +	.rmdir = CIFSSMBRmDir, +	.unlink = CIFSSMBDelFile, +	.rename_pending_delete = cifs_rename_pending_delete, +	.rename = CIFSSMBRename, +	.create_hardlink = CIFSCreateHardLink, +	.query_symlink = cifs_query_symlink, +	.open = cifs_open_file, +	.set_fid = cifs_set_fid, +	.close = cifs_close_file, +	.flush = cifs_flush_file, +	.async_readv = cifs_async_readv, +	.async_writev = cifs_async_writev, +	.sync_read = cifs_sync_read, +	.sync_write = cifs_sync_write, +	.query_dir_first = cifs_query_dir_first, +	.query_dir_next = cifs_query_dir_next, +	.close_dir = cifs_close_dir, +	.calc_smb_size = smbCalcSize, +	.oplock_response = cifs_oplock_response, +	.queryfs = cifs_queryfs, +	.mand_lock = cifs_mand_lock, +	.mand_unlock_range = cifs_unlock_range, +	.push_mand_locks = cifs_push_mandatory_locks, +	.query_mf_symlink = cifs_query_mf_symlink, +	.create_mf_symlink = cifs_create_mf_symlink, +	.is_read_op = cifs_is_read_op, +#ifdef CONFIG_CIFS_XATTR +	.query_all_EAs = CIFSSMBQAllEAs, +	.set_EA = CIFSSMBSetEA, +#endif /* CIFS_XATTR */ +#ifdef CONFIG_CIFS_ACL +	.get_acl = get_cifs_acl, +	.get_acl_by_fid = get_cifs_acl_by_fid, +	.set_acl = set_cifs_acl, +#endif /* CIFS_ACL */ +}; + +struct smb_version_values smb1_values = { +	.version_string = SMB1_VERSION_STRING, +	.large_lock_type = LOCKING_ANDX_LARGE_FILES, +	.exclusive_lock_type = 0, +	.shared_lock_type = LOCKING_ANDX_SHARED_LOCK, +	.unlock_lock_type = 0, +	.header_size = sizeof(struct smb_hdr), +	.max_header_size = MAX_CIFS_HDR_SIZE, +	.read_rsp_size = sizeof(READ_RSP), +	.lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), +	.cap_unix = CAP_UNIX, +	.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, +	.cap_large_files = CAP_LARGE_FILES, +	.signing_enabled = SECMODE_SIGN_ENABLED, +	.signing_required = SECMODE_SIGN_REQUIRED, +}; diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c new file mode 100644 index 00000000000..3f17b455083 --- /dev/null +++ b/fs/cifs/smb2file.c @@ -0,0 +1,265 @@ +/* + *   fs/cifs/smb2file.c + * + *   Copyright (C) International Business Machines  Corp., 2002, 2011 + *   Author(s): Steve French (sfrench@us.ibm.com), + *              Pavel Shilovsky ((pshilovsky@samba.org) 2012 + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <asm/div64.h> +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2proto.h" + +int +smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, +	       __u32 *oplock, FILE_ALL_INFO *buf) +{ +	int rc; +	__le16 *smb2_path; +	struct smb2_file_all_info *smb2_data = NULL; +	__u8 smb2_oplock[17]; +	struct cifs_fid *fid = oparms->fid; + +	smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); +	if (smb2_path == NULL) { +		rc = -ENOMEM; +		goto out; +	} + +	smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, +			    GFP_KERNEL); +	if (smb2_data == NULL) { +		rc = -ENOMEM; +		goto out; +	} + +	oparms->desired_access |= FILE_READ_ATTRIBUTES; +	*smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; + +	if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) +		memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); + +	rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL); +	if (rc) +		goto out; + +	if (buf) { +		/* open response does not have IndexNumber field - get it */ +		rc = SMB2_get_srv_num(xid, oparms->tcon, fid->persistent_fid, +				      fid->volatile_fid, +				      &smb2_data->IndexNumber); +		if (rc) { +			/* let get_inode_info disable server inode numbers */ +			smb2_data->IndexNumber = 0; +			rc = 0; +		} +		move_smb2_info_to_cifs(buf, smb2_data); +	} + +	*oplock = *smb2_oplock; +out: +	kfree(smb2_data); +	kfree(smb2_path); +	return rc; +} + +int +smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, +		  const unsigned int xid) +{ +	int rc = 0, stored_rc; +	unsigned int max_num, num = 0, max_buf; +	struct smb2_lock_element *buf, *cur; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct cifsLockInfo *li, *tmp; +	__u64 length = 1 + flock->fl_end - flock->fl_start; +	struct list_head tmp_llist; + +	INIT_LIST_HEAD(&tmp_llist); + +	/* +	 * Accessing maxBuf is racy with cifs_reconnect - need to store value +	 * and check it for zero before using. +	 */ +	max_buf = tcon->ses->server->maxBuf; +	if (!max_buf) +		return -EINVAL; + +	max_num = max_buf / sizeof(struct smb2_lock_element); +	buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	cur = buf; + +	down_write(&cinode->lock_sem); +	list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { +		if (flock->fl_start > li->offset || +		    (flock->fl_start + length) < +		    (li->offset + li->length)) +			continue; +		if (current->tgid != li->pid) +			continue; +		if (cinode->can_cache_brlcks) { +			/* +			 * We can cache brlock requests - simply remove a lock +			 * from the file's list. +			 */ +			list_del(&li->llist); +			cifs_del_lock_waiters(li); +			kfree(li); +			continue; +		} +		cur->Length = cpu_to_le64(li->length); +		cur->Offset = cpu_to_le64(li->offset); +		cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); +		/* +		 * We need to save a lock here to let us add it again to the +		 * file's list if the unlock range request fails on the server. +		 */ +		list_move(&li->llist, &tmp_llist); +		if (++num == max_num) { +			stored_rc = smb2_lockv(xid, tcon, +					       cfile->fid.persistent_fid, +					       cfile->fid.volatile_fid, +					       current->tgid, num, buf); +			if (stored_rc) { +				/* +				 * We failed on the unlock range request - add +				 * all locks from the tmp list to the head of +				 * the file's list. +				 */ +				cifs_move_llist(&tmp_llist, +						&cfile->llist->locks); +				rc = stored_rc; +			} else +				/* +				 * The unlock range request succeed - free the +				 * tmp list. +				 */ +				cifs_free_llist(&tmp_llist); +			cur = buf; +			num = 0; +		} else +			cur++; +	} +	if (num) { +		stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, +				       cfile->fid.volatile_fid, current->tgid, +				       num, buf); +		if (stored_rc) { +			cifs_move_llist(&tmp_llist, &cfile->llist->locks); +			rc = stored_rc; +		} else +			cifs_free_llist(&tmp_llist); +	} +	up_write(&cinode->lock_sem); + +	kfree(buf); +	return rc; +} + +static int +smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid, +		       struct smb2_lock_element *buf, unsigned int max_num) +{ +	int rc = 0, stored_rc; +	struct cifsFileInfo *cfile = fdlocks->cfile; +	struct cifsLockInfo *li; +	unsigned int num = 0; +	struct smb2_lock_element *cur = buf; +	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + +	list_for_each_entry(li, &fdlocks->locks, llist) { +		cur->Length = cpu_to_le64(li->length); +		cur->Offset = cpu_to_le64(li->offset); +		cur->Flags = cpu_to_le32(li->type | +						SMB2_LOCKFLAG_FAIL_IMMEDIATELY); +		if (++num == max_num) { +			stored_rc = smb2_lockv(xid, tcon, +					       cfile->fid.persistent_fid, +					       cfile->fid.volatile_fid, +					       current->tgid, num, buf); +			if (stored_rc) +				rc = stored_rc; +			cur = buf; +			num = 0; +		} else +			cur++; +	} +	if (num) { +		stored_rc = smb2_lockv(xid, tcon, +				       cfile->fid.persistent_fid, +				       cfile->fid.volatile_fid, +				       current->tgid, num, buf); +		if (stored_rc) +			rc = stored_rc; +	} + +	return rc; +} + +int +smb2_push_mandatory_locks(struct cifsFileInfo *cfile) +{ +	int rc = 0, stored_rc; +	unsigned int xid; +	unsigned int max_num, max_buf; +	struct smb2_lock_element *buf; +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct cifs_fid_locks *fdlocks; + +	xid = get_xid(); + +	/* +	 * Accessing maxBuf is racy with cifs_reconnect - need to store value +	 * and check it for zero before using. +	 */ +	max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; +	if (!max_buf) { +		free_xid(xid); +		return -EINVAL; +	} + +	max_num = max_buf / sizeof(struct smb2_lock_element); +	buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL); +	if (!buf) { +		free_xid(xid); +		return -ENOMEM; +	} + +	list_for_each_entry(fdlocks, &cinode->llist, llist) { +		stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num); +		if (stored_rc) +			rc = stored_rc; +	} + +	kfree(buf); +	free_xid(xid); +	return rc; +} diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h new file mode 100644 index 00000000000..bc0bb9c34f7 --- /dev/null +++ b/fs/cifs/smb2glob.h @@ -0,0 +1,63 @@ +/* + *   fs/cifs/smb2glob.h + * + *   Definitions for various global variables and structures + * + *   Copyright (C) International Business Machines  Corp., 2002, 2011 + *                 Etersoft, 2012 + *   Author(s): Steve French (sfrench@us.ibm.com) + *              Jeremy Allison (jra@samba.org) + *              Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + */ +#ifndef _SMB2_GLOB_H +#define _SMB2_GLOB_H + +#define SMB2_MAGIC_NUMBER 0xFE534D42 + +/* + ***************************************************************** + * Constants go here + ***************************************************************** + */ + +/* + * Identifiers for functions that use the open, operation, close pattern + * in smb2inode.c:smb2_open_op_close() + */ +#define SMB2_OP_SET_DELETE 1 +#define SMB2_OP_SET_INFO 2 +#define SMB2_OP_QUERY_INFO 3 +#define SMB2_OP_QUERY_DIR 4 +#define SMB2_OP_MKDIR 5 +#define SMB2_OP_RENAME 6 +#define SMB2_OP_DELETE 7 +#define SMB2_OP_HARDLINK 8 +#define SMB2_OP_SET_EOF 9 + +/* Used when constructing chained read requests. */ +#define CHAINED_REQUEST 1 +#define START_OF_CHAIN 2 +#define END_OF_CHAIN 4 +#define RELATED_REQUEST 8 + +#define SMB2_SIGNATURE_SIZE (16) +#define SMB2_NTLMV2_SESSKEY_SIZE (16) +#define SMB2_HMACSHA256_SIZE (32) +#define SMB2_CMACAES_SIZE (16) +#define SMB3_SIGNKEY_SIZE (16) + +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 + +#endif	/* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c new file mode 100644 index 00000000000..84c012a6aba --- /dev/null +++ b/fs/cifs/smb2inode.c @@ -0,0 +1,273 @@ +/* + *   fs/cifs/smb2inode.c + * + *   Copyright (C) International Business Machines  Corp., 2002, 2011 + *                 Etersoft, 2012 + *   Author(s): Pavel Shilovsky (pshilovsky@samba.org), + *              Steve French (sfrench@us.ibm.com) + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <asm/div64.h> +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2glob.h" +#include "smb2pdu.h" +#include "smb2proto.h" + +static int +smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifs_sb_info *cifs_sb, const char *full_path, +		   __u32 desired_access, __u32 create_disposition, +		   __u32 create_options, void *data, int command) +{ +	int rc, tmprc = 0; +	__le16 *utf16_path; +	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); +	if (!utf16_path) +		return -ENOMEM; + +	oparms.tcon = tcon; +	oparms.desired_access = desired_access; +	oparms.disposition = create_disposition; +	oparms.create_options = create_options; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); +	if (rc) { +		kfree(utf16_path); +		return rc; +	} + +	switch (command) { +	case SMB2_OP_DELETE: +		break; +	case SMB2_OP_QUERY_INFO: +		tmprc = SMB2_query_info(xid, tcon, fid.persistent_fid, +					fid.volatile_fid, +					(struct smb2_file_all_info *)data); +		break; +	case SMB2_OP_MKDIR: +		/* +		 * Directories are created through parameters in the +		 * SMB2_open() call. +		 */ +		break; +	case SMB2_OP_RENAME: +		tmprc = SMB2_rename(xid, tcon, fid.persistent_fid, +				    fid.volatile_fid, (__le16 *)data); +		break; +	case SMB2_OP_HARDLINK: +		tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid, +					  fid.volatile_fid, (__le16 *)data); +		break; +	case SMB2_OP_SET_EOF: +		tmprc = SMB2_set_eof(xid, tcon, fid.persistent_fid, +				     fid.volatile_fid, current->tgid, +				     (__le64 *)data); +		break; +	case SMB2_OP_SET_INFO: +		tmprc = SMB2_set_info(xid, tcon, fid.persistent_fid, +				      fid.volatile_fid, +				      (FILE_BASIC_INFO *)data); +		break; +	default: +		cifs_dbg(VFS, "Invalid command\n"); +		break; +	} + +	rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	if (tmprc) +		rc = tmprc; +	kfree(utf16_path); +	return rc; +} + +void +move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) +{ +	memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src); +	dst->CurrentByteOffset = src->CurrentByteOffset; +	dst->Mode = src->Mode; +	dst->AlignmentRequirement = src->AlignmentRequirement; +	dst->IndexNumber1 = 0; /* we don't use it */ +} + +int +smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, +		     struct cifs_sb_info *cifs_sb, const char *full_path, +		     FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink) +{ +	int rc; +	struct smb2_file_all_info *smb2_data; + +	*adjust_tz = false; +	*symlink = false; + +	smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, +			    GFP_KERNEL); +	if (smb2_data == NULL) +		return -ENOMEM; + +	rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, +				FILE_READ_ATTRIBUTES, FILE_OPEN, 0, +				smb2_data, SMB2_OP_QUERY_INFO); +	if (rc == -EOPNOTSUPP) { +		*symlink = true; +		/* Failed on a symbolic link - query a reparse point info */ +		rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, +					FILE_READ_ATTRIBUTES, FILE_OPEN, +					OPEN_REPARSE_POINT, smb2_data, +					SMB2_OP_QUERY_INFO); +	} +	if (rc) +		goto out; + +	move_smb2_info_to_cifs(data, smb2_data); +out: +	kfree(smb2_data); +	return rc; +} + +int +smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +	   struct cifs_sb_info *cifs_sb) +{ +	return smb2_open_op_close(xid, tcon, cifs_sb, name, +				  FILE_WRITE_ATTRIBUTES, FILE_CREATE, +				  CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); +} + +void +smb2_mkdir_setinfo(struct inode *inode, const char *name, +		   struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, +		   const unsigned int xid) +{ +	FILE_BASIC_INFO data; +	struct cifsInodeInfo *cifs_i; +	u32 dosattrs; +	int tmprc; + +	memset(&data, 0, sizeof(data)); +	cifs_i = CIFS_I(inode); +	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; +	data.Attributes = cpu_to_le32(dosattrs); +	tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name, +				   FILE_WRITE_ATTRIBUTES, FILE_CREATE, +				   CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); +	if (tmprc == 0) +		cifs_i->cifsAttrs = dosattrs; +} + +int +smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +	   struct cifs_sb_info *cifs_sb) +{ +	return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, +				  CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE, +				  NULL, SMB2_OP_DELETE); +} + +int +smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, +	    struct cifs_sb_info *cifs_sb) +{ +	return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, +				  CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, +				  NULL, SMB2_OP_DELETE); +} + +static int +smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, +		   const char *from_name, const char *to_name, +		   struct cifs_sb_info *cifs_sb, __u32 access, int command) +{ +	__le16 *smb2_to_name = NULL; +	int rc; + +	smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); +	if (smb2_to_name == NULL) { +		rc = -ENOMEM; +		goto smb2_rename_path; +	} + +	rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access, +				FILE_OPEN, 0, smb2_to_name, command); +smb2_rename_path: +	kfree(smb2_to_name); +	return rc; +} + +int +smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, +		 const char *from_name, const char *to_name, +		 struct cifs_sb_info *cifs_sb) +{ +	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, +				  DELETE, SMB2_OP_RENAME); +} + +int +smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, +		     const char *from_name, const char *to_name, +		     struct cifs_sb_info *cifs_sb) +{ +	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, +				  FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK); +} + +int +smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, +		   const char *full_path, __u64 size, +		   struct cifs_sb_info *cifs_sb, bool set_alloc) +{ +	__le64 eof = cpu_to_le64(size); +	return smb2_open_op_close(xid, tcon, cifs_sb, full_path, +				  FILE_WRITE_DATA, FILE_OPEN, 0, &eof, +				  SMB2_OP_SET_EOF); +} + +int +smb2_set_file_info(struct inode *inode, const char *full_path, +		   FILE_BASIC_INFO *buf, const unsigned int xid) +{ +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct tcon_link *tlink; +	int rc; + +	tlink = cifs_sb_tlink(cifs_sb); +	if (IS_ERR(tlink)) +		return PTR_ERR(tlink); +	rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path, +				FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, +				SMB2_OP_SET_INFO); +	cifs_put_tlink(tlink); +	return rc; +} diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c new file mode 100644 index 00000000000..94bd4fbb13d --- /dev/null +++ b/fs/cifs/smb2maperror.c @@ -0,0 +1,2479 @@ +/* + *   fs/smb2/smb2maperror.c + * + *   Functions which do error mapping of SMB2 status codes to POSIX errors + * + *   Copyright (C) International Business Machines  Corp., 2009 + *   Author(s): Steve French (sfrench@us.ibm.com) + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/errno.h> +#include "cifsglob.h" +#include "cifs_debug.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "smb2status.h" + +struct status_to_posix_error { +	__le32 smb2_status; +	int posix_error; +	char *status_string; +}; + +static const struct status_to_posix_error smb2_error_map_table[] = { +	{STATUS_SUCCESS, 0, "STATUS_SUCCESS"}, +	{STATUS_WAIT_0,  0, "STATUS_WAIT_0"}, +	{STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"}, +	{STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"}, +	{STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"}, +	{STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"}, +	{STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"}, +	{STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"}, +	{STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"}, +	{STATUS_USER_APC, -EIO, "STATUS_USER_APC"}, +	{STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"}, +	{STATUS_ALERTED, -EIO, "STATUS_ALERTED"}, +	{STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"}, +	{STATUS_PENDING, -EIO, "STATUS_PENDING"}, +	{STATUS_REPARSE, -EIO, "STATUS_REPARSE"}, +	{STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"}, +	{STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"}, +	{STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"}, +	{STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO, +	"STATUS_OPLOCK_BREAK_IN_PROGRESS"}, +	{STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"}, +	{STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"}, +	{STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"}, +	{STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"}, +	{STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"}, +	{STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO, +	"STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"}, +	{STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"}, +	{STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"}, +	{STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO, +	"STATUS_PAGE_FAULT_COPY_ON_WRITE"}, +	{STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"}, +	{STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"}, +	{STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"}, +	{STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"}, +	{STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"}, +	{STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"}, +	{STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO, +	"STATUS_RESOURCE_REQUIREMENTS_CHANGED"}, +	{STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"}, +	{STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO, +	"STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"}, +	{STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"}, +	{STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"}, +	{STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"}, +	{STATUS_VOLSNAP_HIBERNATE_READY, -EIO, +	"STATUS_VOLSNAP_HIBERNATE_READY"}, +	{STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO, +	"STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"}, +	{STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO, +	"STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"}, +	{STATUS_INTERRUPT_STILL_CONNECTED, -EIO, +	"STATUS_INTERRUPT_STILL_CONNECTED"}, +	{STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"}, +	{STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO, +	"STATUS_FILE_LOCKED_WITH_ONLY_READERS"}, +	{STATUS_FILE_LOCKED_WITH_WRITERS, -EIO, +	"STATUS_FILE_LOCKED_WITH_WRITERS"}, +	{STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS, +	"STATUS_RESOURCEMANAGER_READ_ONLY"}, +	{STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"}, +	{DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"}, +	{DBG_CONTINUE, -EIO, "DBG_CONTINUE"}, +	{STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"}, +	{STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"}, +	{STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"}, +	{STATUS_WORKING_SET_LIMIT_RANGE, -EIO, +	"STATUS_WORKING_SET_LIMIT_RANGE"}, +	{STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"}, +	{STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"}, +	{STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"}, +	{STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"}, +	{STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"}, +	{STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"}, +	{STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"}, +	{STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO, +	"STATUS_FT_READ_RECOVERY_FROM_BACKUP"}, +	{STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"}, +	{STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT, +	"STATUS_SERIAL_COUNTER_TIMEOUT"}, +	{STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"}, +	{STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO, +	"STATUS_IMAGE_MACHINE_TYPE_MISMATCH"}, +	{STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"}, +	{STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"}, +	{STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO, +	"STATUS_RECEIVE_PARTIAL_EXPEDITED"}, +	{STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"}, +	{STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"}, +	{STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"}, +	{STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"}, +	{STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"}, +	{STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"}, +	{STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"}, +	{STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"}, +	{STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"}, +	{STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"}, +	{STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"}, +	{STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"}, +	{STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"}, +	{STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"}, +	{STATUS_WX86_EXCEPTION_CONTINUE, -EIO, +	"STATUS_WX86_EXCEPTION_CONTINUE"}, +	{STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO, +	"STATUS_WX86_EXCEPTION_LASTCHANCE"}, +	{STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"}, +	{STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO, +	"STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"}, +	{STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"}, +	{STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"}, +	{STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"}, +	{STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"}, +	{STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"}, +	{STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"}, +	{STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"}, +	{STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"}, +	{STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"}, +	{STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO, +	"STATUS_DRIVERS_LEAKING_LOCKED_PAGES"}, +	{STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"}, +	{STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO, +	"STATUS_SYSTEM_POWERSTATE_TRANSITION"}, +	{STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO, +	"STATUS_ALPC_CHECK_COMPLETION_LIST"}, +	{STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO, +	"STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"}, +	{STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"}, +	{STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"}, +	{STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"}, +	{STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"}, +	{STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"}, +	{DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"}, +	{DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"}, +	{DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"}, +	{DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"}, +	{DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"}, +	{DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"}, +	{DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"}, +	{DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"}, +	{DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"}, +	{RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"}, +	{RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"}, +	{STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"}, +	{STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"}, +	{STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO, +	"STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"}, +	{STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"}, +	{STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"}, +	{STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"}, +	{STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO, +	"STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"}, +	{STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO, +	"STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"}, +	{STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO, +	"STATUS_GRAPHICS_DRIVER_MISMATCH"}, +	{STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO, +	"STATUS_GRAPHICS_MODE_NOT_PINNED"}, +	{STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO, +	"STATUS_GRAPHICS_NO_PREFERRED_MODE"}, +	{STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO, +	"STATUS_GRAPHICS_DATASET_IS_EMPTY"}, +	{STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO, +	"STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"}, +	{STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO, +	"STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"}, +	{STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO, +	"STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"}, +	{STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO, +	"STATUS_GRAPHICS_LEADLINK_START_DEFERRED"}, +	{STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO, +	"STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"}, +	{STATUS_GRAPHICS_START_DEFERRED, -EIO, +	"STATUS_GRAPHICS_START_DEFERRED"}, +	{STATUS_NDIS_INDICATION_REQUIRED, -EIO, +	"STATUS_NDIS_INDICATION_REQUIRED"}, +	{STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"}, +	{STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, +	{STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, +	{STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, +	{STATUS_BUFFER_OVERFLOW, -EIO, "STATUS_BUFFER_OVERFLOW"}, +	{STATUS_NO_MORE_FILES, -EIO, "STATUS_NO_MORE_FILES"}, +	{STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, +	{STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, +	{STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, +	{STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"}, +	{STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"}, +	{STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"}, +	{STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"}, +	{STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"}, +	{STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"}, +	{STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"}, +	{STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"}, +	{STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"}, +	{STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"}, +	{STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"}, +	{STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"}, +	{STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"}, +	{STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"}, +	{STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"}, +	{STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"}, +	{STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"}, +	{STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"}, +	{STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"}, +	{STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"}, +	{STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"}, +	{STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"}, +	{STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO, +	"STATUS_REDIRECTOR_HAS_OPEN_HANDLES"}, +	{STATUS_SERVER_HAS_OPEN_HANDLES, -EIO, +	"STATUS_SERVER_HAS_OPEN_HANDLES"}, +	{STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"}, +	{STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"}, +	{STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO, +	"STATUS_CLEANER_CARTRIDGE_INSTALLED"}, +	{STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"}, +	{STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"}, +	{STATUS_REGISTRY_HIVE_RECOVERED, -EIO, +	"STATUS_REGISTRY_HIVE_RECOVERED"}, +	{STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"}, +	{STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO, +	"STATUS_DLL_MIGHT_BE_INCOMPATIBLE"}, +	{STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"}, +	{STATUS_DEVICE_REQUIRES_CLEANING, -EIO, +	"STATUS_DEVICE_REQUIRES_CLEANING"}, +	{STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"}, +	{STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"}, +	{DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"}, +	{STATUS_CLUSTER_NODE_ALREADY_UP, -EIO, +	"STATUS_CLUSTER_NODE_ALREADY_UP"}, +	{STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO, +	"STATUS_CLUSTER_NODE_ALREADY_DOWN"}, +	{STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO, +	"STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"}, +	{STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO, +	"STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"}, +	{STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO, +	"STATUS_CLUSTER_NODE_ALREADY_MEMBER"}, +	{STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"}, +	{STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"}, +	{STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO, +	"STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"}, +	{STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO, +	"STATUS_TXF_METADATA_ALREADY_PRESENT"}, +	{STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO, +	"STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"}, +	{STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO, +	"STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"}, +	{STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, +	{STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, +	{STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, +	{STATUS_NOT_IMPLEMENTED, -ENOSYS, "STATUS_NOT_IMPLEMENTED"}, +	{STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, +	{STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, +	{STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, +	{STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"}, +	{STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"}, +	{STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"}, +	{STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"}, +	{STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"}, +	{STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"}, +	{STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"}, +	{STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, +	{STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, +	{STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, +	{STATUS_INVALID_DEVICE_REQUEST, -EIO, "STATUS_INVALID_DEVICE_REQUEST"}, +	{STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, +	{STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, +	{STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, +	{STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"}, +	{STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, +	{STATUS_MORE_PROCESSING_REQUIRED, -EIO, +	"STATUS_MORE_PROCESSING_REQUIRED"}, +	{STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, +	{STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, +	"STATUS_CONFLICTING_ADDRESSES"}, +	{STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, +	{STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"}, +	{STATUS_UNABLE_TO_DELETE_SECTION, -EIO, +	"STATUS_UNABLE_TO_DELETE_SECTION"}, +	{STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"}, +	{STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"}, +	{STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"}, +	{STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"}, +	{STATUS_INVALID_FILE_FOR_SECTION, -EIO, +	"STATUS_INVALID_FILE_FOR_SECTION"}, +	{STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"}, +	{STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"}, +	{STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"}, +	{STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"}, +	{STATUS_NONCONTINUABLE_EXCEPTION, -EIO, +	"STATUS_NONCONTINUABLE_EXCEPTION"}, +	{STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"}, +	{STATUS_UNWIND, -EIO, "STATUS_UNWIND"}, +	{STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"}, +	{STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"}, +	{STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"}, +	{STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"}, +	{STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"}, +	{STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"}, +	{STATUS_INVALID_PORT_ATTRIBUTES, -EIO, +	"STATUS_INVALID_PORT_ATTRIBUTES"}, +	{STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"}, +	{STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"}, +	{STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"}, +	{STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"}, +	{STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"}, +	{STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"}, +	{STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"}, +	{STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"}, +	{STATUS_DEVICE_ALREADY_ATTACHED, -EIO, +	"STATUS_DEVICE_ALREADY_ATTACHED"}, +	{STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"}, +	{STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"}, +	{STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"}, +	{STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"}, +	{STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"}, +	{STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"}, +	{STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"}, +	{STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"}, +	{STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, +	"STATUS_PORT_CONNECTION_REFUSED"}, +	{STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, +	{STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, +	{STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, +	{STATUS_INVALID_PAGE_PROTECTION, -EIO, +	"STATUS_INVALID_PAGE_PROTECTION"}, +	{STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"}, +	{STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO, +	"STATUS_SEMAPHORE_LIMIT_EXCEEDED"}, +	{STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"}, +	{STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"}, +	{STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"}, +	{STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"}, +	{STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"}, +	{STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"}, +	{STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"}, +	{STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"}, +	{STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"}, +	{STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, +	{STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, +	{STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, +	{STATUS_FILE_LOCK_CONFLICT, -EIO, "STATUS_FILE_LOCK_CONFLICT"}, +	{STATUS_LOCK_NOT_GRANTED, -EIO, "STATUS_LOCK_NOT_GRANTED"}, +	{STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, +	{STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, +	"STATUS_CTL_FILE_NOT_SUPPORTED"}, +	{STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"}, +	{STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"}, +	{STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"}, +	{STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"}, +	{STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"}, +	{STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"}, +	{STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"}, +	{STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"}, +	{STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"}, +	{STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"}, +	{STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"}, +	{STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"}, +	{STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"}, +	{STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"}, +	{STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"}, +	{STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"}, +	{STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"}, +	{STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"}, +	{STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"}, +	{STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"}, +	{STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"}, +	{STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"}, +	{STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"}, +	{STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"}, +	{STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"}, +	{STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"}, +	{STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"}, +	{STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"}, +	{STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO, +	"STATUS_TOO_MANY_LUIDS_REQUESTED"}, +	{STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"}, +	{STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"}, +	{STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"}, +	{STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"}, +	{STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"}, +	{STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"}, +	{STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"}, +	{STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"}, +	{STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"}, +	{STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"}, +	{STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"}, +	{STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"}, +	{STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"}, +	{STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO, +	"STATUS_TOO_MANY_GUIDS_REQUESTED"}, +	{STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"}, +	{STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"}, +	{STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"}, +	{STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"}, +	{STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"}, +	{STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"}, +	{STATUS_RESOURCE_DATA_NOT_FOUND, -EIO, +	"STATUS_RESOURCE_DATA_NOT_FOUND"}, +	{STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO, +	"STATUS_RESOURCE_TYPE_NOT_FOUND"}, +	{STATUS_RESOURCE_NAME_NOT_FOUND, -EIO, +	"STATUS_RESOURCE_NAME_NOT_FOUND"}, +	{STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"}, +	{STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"}, +	{STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"}, +	{STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"}, +	{STATUS_FLOAT_INVALID_OPERATION, -EIO, +	"STATUS_FLOAT_INVALID_OPERATION"}, +	{STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"}, +	{STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"}, +	{STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"}, +	{STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"}, +	{STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"}, +	{STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"}, +	{STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"}, +	{STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"}, +	{STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO, +	"STATUS_ALLOTTED_SPACE_EXCEEDED"}, +	{STATUS_INSUFFICIENT_RESOURCES, -EREMOTEIO, +				"STATUS_INSUFFICIENT_RESOURCES"}, +	{STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"}, +	{STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"}, +	{STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"}, +	{STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"}, +	{STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"}, +	{STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"}, +	{STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"}, +	{STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"}, +	{STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"}, +	{STATUS_INVALID_GROUP_ATTRIBUTES, -EIO, +	"STATUS_INVALID_GROUP_ATTRIBUTES"}, +	{STATUS_BAD_IMPERSONATION_LEVEL, -EIO, +	"STATUS_BAD_IMPERSONATION_LEVEL"}, +	{STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"}, +	{STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"}, +	{STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"}, +	{STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"}, +	{STATUS_INSTRUCTION_MISALIGNMENT, -EIO, +	"STATUS_INSTRUCTION_MISALIGNMENT"}, +	{STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"}, +	{STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"}, +	{STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"}, +	{STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"}, +	{STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"}, +	{STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"}, +	{STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"}, +	{STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, +	{STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, +	{STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, +	{STATUS_IO_TIMEOUT, -ETIMEDOUT, "STATUS_IO_TIMEOUT"}, +	{STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, +	{STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, +	{STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, +	{STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"}, +	{STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"}, +	{STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"}, +	{STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN, +	"STATUS_REMOTE_NOT_LISTENING"}, +	{STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"}, +	{STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"}, +	{STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"}, +	{STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"}, +	{STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"}, +	{STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"}, +	{STATUS_INVALID_NETWORK_RESPONSE, -EIO, +	"STATUS_INVALID_NETWORK_RESPONSE"}, +	{STATUS_UNEXPECTED_NETWORK_ERROR, -EIO, +	"STATUS_UNEXPECTED_NETWORK_ERROR"}, +	{STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"}, +	{STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, +	{STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, +	{STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, +	{STATUS_NETWORK_NAME_DELETED, -EIO, "STATUS_NETWORK_NAME_DELETED"}, +	{STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, +	{STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, +	{STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, +	{STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"}, +	{STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"}, +	{STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"}, +	{STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"}, +	{STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"}, +	{STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"}, +	{STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"}, +	{STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"}, +	{STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"}, +	{STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"}, +	{STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"}, +	{STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"}, +	{STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"}, +	{STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO, +	"STATUS_CANT_ACCESS_DOMAIN_INFO"}, +	{STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"}, +	{STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"}, +	{STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"}, +	{STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"}, +	{STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"}, +	{STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"}, +	{STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"}, +	{STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"}, +	{STATUS_INVALID_OPLOCK_PROTOCOL, -EIO, +	"STATUS_INVALID_OPLOCK_PROTOCOL"}, +	{STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"}, +	{STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"}, +	{STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"}, +	{STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"}, +	{STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"}, +	{STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"}, +	{STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO, +	"STATUS_UNEXPECTED_MM_CREATE_ERR"}, +	{STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO, +	"STATUS_UNEXPECTED_MM_MAP_ERROR"}, +	{STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO, +	"STATUS_UNEXPECTED_MM_EXTEND_ERR"}, +	{STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"}, +	{STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"}, +	{STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"}, +	{STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"}, +	{STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"}, +	{STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"}, +	{STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"}, +	{STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"}, +	{STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"}, +	{STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"}, +	{STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"}, +	{STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"}, +	{STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"}, +	{STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"}, +	{STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"}, +	{STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"}, +	{STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"}, +	{STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"}, +	{STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"}, +	{STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"}, +	{STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"}, +	{STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"}, +	{STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"}, +	{STATUS_BAD_LOGON_SESSION_STATE, -EIO, +	"STATUS_BAD_LOGON_SESSION_STATE"}, +	{STATUS_LOGON_SESSION_COLLISION, -EIO, +	"STATUS_LOGON_SESSION_COLLISION"}, +	{STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"}, +	{STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"}, +	{STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"}, +	{STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"}, +	{STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"}, +	{STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"}, +	{STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"}, +	{STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"}, +	{STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"}, +	{STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"}, +	{STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"}, +	{STATUS_ABIOS_LID_ALREADY_OWNED, -EIO, +	"STATUS_ABIOS_LID_ALREADY_OWNED"}, +	{STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"}, +	{STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"}, +	{STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"}, +	{STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO, +	"STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"}, +	{STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"}, +	{STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"}, +	{STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"}, +	{STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"}, +	{STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"}, +	{STATUS_INVALID_IMAGE_NE_FORMAT, -EIO, +	"STATUS_INVALID_IMAGE_NE_FORMAT"}, +	{STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"}, +	{STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"}, +	{STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"}, +	{STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"}, +	{STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"}, +	{STATUS_CANNOT_DELETE, -EIO, "STATUS_CANNOT_DELETE"}, +	{STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"}, +	{STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"}, +	{STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"}, +	{STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"}, +	{STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"}, +	{STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"}, +	{STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"}, +	{STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"}, +	{STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"}, +	{STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"}, +	{STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT, +	"STATUS_PAGEFILE_QUOTA_EXCEEDED"}, +	{STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"}, +	{STATUS_INVALID_IMAGE_LE_FORMAT, -EIO, +	"STATUS_INVALID_IMAGE_LE_FORMAT"}, +	{STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"}, +	{STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"}, +	{STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"}, +	{STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"}, +	{STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"}, +	{STATUS_SYNCHRONIZATION_REQUIRED, -EIO, +	"STATUS_SYNCHRONIZATION_REQUIRED"}, +	{STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"}, +	{STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"}, +	{STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"}, +	{STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"}, +	{STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"}, +	{STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"}, +	{STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"}, +	{STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"}, +	{STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"}, +	{STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"}, +	{STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"}, +	{STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"}, +	{STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"}, +	{STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"}, +	{STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"}, +	{STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"}, +	{STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"}, +	{STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"}, +	{STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"}, +	{STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"}, +	{STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"}, +	{STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"}, +	{STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"}, +	{STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"}, +	{STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"}, +	{STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"}, +	{STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"}, +	{STATUS_SERIAL_NO_DEVICE_INITED, -EIO, +	"STATUS_SERIAL_NO_DEVICE_INITED"}, +	{STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"}, +	{STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"}, +	{STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"}, +	{STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"}, +	{STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"}, +	{STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"}, +	{STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"}, +	{STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"}, +	{STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"}, +	{STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"}, +	{STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"}, +	{STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"}, +	{STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO, +	"STATUS_NT_CROSS_ENCRYPTION_REQUIRED"}, +	{STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO, +	"STATUS_DOMAIN_CTRLR_CONFIG_ERROR"}, +	{STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"}, +	{STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO, +	"STATUS_ILL_FORMED_SERVICE_ENTRY"}, +	{STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"}, +	{STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"}, +	{STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"}, +	{STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"}, +	{STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO, +	"STATUS_FLOPPY_ID_MARK_NOT_FOUND"}, +	{STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"}, +	{STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"}, +	{STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"}, +	{STATUS_DISK_RECALIBRATE_FAILED, -EIO, +	"STATUS_DISK_RECALIBRATE_FAILED"}, +	{STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"}, +	{STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"}, +	{STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"}, +	{STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"}, +	{STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO, +	"STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"}, +	{STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"}, +	{STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"}, +	{STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"}, +	{STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"}, +	{STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"}, +	{STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"}, +	{STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"}, +	{STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"}, +	{STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"}, +	{STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"}, +	{STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"}, +	{STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"}, +	{STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO, +	"STATUS_LM_CROSS_ENCRYPTION_REQUIRED"}, +	{STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"}, +	{STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"}, +	{STATUS_DEVICE_CONFIGURATION_ERROR, -EIO, +	"STATUS_DEVICE_CONFIGURATION_ERROR"}, +	{STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"}, +	{STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"}, +	{STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"}, +	{STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"}, +	{STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"}, +	{STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"}, +	{STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"}, +	{STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"}, +	{STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"}, +	{STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"}, +	{STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO, +	"STATUS_TRUSTED_RELATIONSHIP_FAILURE"}, +	{STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"}, +	{STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"}, +	{STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"}, +	{STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"}, +	{STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"}, +	{STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"}, +	{STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"}, +	{STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO, +	"STATUS_NETWORK_CREDENTIAL_CONFLICT"}, +	{STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"}, +	{STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"}, +	{STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO, +	"STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"}, +	{STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO, +	"STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"}, +	{STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO, +	"STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"}, +	{STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO, +	"STATUS_DOMAIN_TRUST_INCONSISTENT"}, +	{STATUS_FS_DRIVER_REQUIRED, -EIO, "STATUS_FS_DRIVER_REQUIRED"}, +	{STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO, +	"STATUS_IMAGE_ALREADY_LOADED_AS_DLL"}, +	{STATUS_NETWORK_OPEN_RESTRICTION, -EIO, +	"STATUS_NETWORK_OPEN_RESTRICTION"}, +	{STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"}, +	{STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"}, +	{STATUS_RESOURCE_LANG_NOT_FOUND, -EIO, +	"STATUS_RESOURCE_LANG_NOT_FOUND"}, +	{STATUS_INSUFF_SERVER_RESOURCES, -EIO, +	"STATUS_INSUFF_SERVER_RESOURCES"}, +	{STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"}, +	{STATUS_INVALID_ADDRESS_COMPONENT, -EIO, +	"STATUS_INVALID_ADDRESS_COMPONENT"}, +	{STATUS_INVALID_ADDRESS_WILDCARD, -EIO, +	"STATUS_INVALID_ADDRESS_WILDCARD"}, +	{STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"}, +	{STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE, +	"STATUS_ADDRESS_ALREADY_EXISTS"}, +	{STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"}, +	{STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED, +	"STATUS_CONNECTION_DISCONNECTED"}, +	{STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"}, +	{STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"}, +	{STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"}, +	{STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"}, +	{STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"}, +	{STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"}, +	{STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"}, +	{STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"}, +	{STATUS_TRANSACTION_INVALID_TYPE, -EIO, +	"STATUS_TRANSACTION_INVALID_TYPE"}, +	{STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"}, +	{STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"}, +	{STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO, +	"STATUS_CANNOT_LOAD_REGISTRY_FILE"}, +	{STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"}, +	{STATUS_SYSTEM_PROCESS_TERMINATED, -EIO, +	"STATUS_SYSTEM_PROCESS_TERMINATED"}, +	{STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"}, +	{STATUS_NO_BROWSER_SERVERS_FOUND, -EIO, +	"STATUS_NO_BROWSER_SERVERS_FOUND"}, +	{STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"}, +	{STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"}, +	{STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"}, +	{STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"}, +	{STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO, +	"STATUS_IMAGE_CHECKSUM_MISMATCH"}, +	{STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"}, +	{STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO, +	"STATUS_CLIENT_SERVER_PARAMETERS_INVALID"}, +	{STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"}, +	{STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"}, +	{STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"}, +	{STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"}, +	{STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"}, +	{STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"}, +	{STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"}, +	{STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"}, +	{STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"}, +	{STATUS_RETRY, -EAGAIN, "STATUS_RETRY"}, +	{STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"}, +	{STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"}, +	{STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"}, +	{STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"}, +	{STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"}, +	{STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO, +	"STATUS_DOMAIN_CONTROLLER_NOT_FOUND"}, +	{STATUS_ACCOUNT_LOCKED_OUT, -EIO, "STATUS_ACCOUNT_LOCKED_OUT"}, +	{STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"}, +	{STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"}, +	{STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"}, +	{STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO, +	"STATUS_ADDRESS_ALREADY_ASSOCIATED"}, +	{STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"}, +	{STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"}, +	{STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"}, +	{STATUS_NETWORK_UNREACHABLE, -ENETUNREACH, +	"STATUS_NETWORK_UNREACHABLE"}, +	{STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"}, +	{STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH, +	"STATUS_PROTOCOL_UNREACHABLE"}, +	{STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"}, +	{STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"}, +	{STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"}, +	{STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"}, +	{STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"}, +	{STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"}, +	{STATUS_TIMER_RESOLUTION_NOT_SET, -EIO, +	"STATUS_TIMER_RESOLUTION_NOT_SET"}, +	{STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"}, +	{STATUS_LOGIN_TIME_RESTRICTION, -EACCES, +	"STATUS_LOGIN_TIME_RESTRICTION"}, +	{STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES, +	"STATUS_LOGIN_WKSTA_RESTRICTION"}, +	{STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"}, +	{STATUS_INSUFFICIENT_LOGON_INFO, -EIO, +	"STATUS_INSUFFICIENT_LOGON_INFO"}, +	{STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"}, +	{STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"}, +	{STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"}, +	{STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"}, +	{STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"}, +	{STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"}, +	{STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"}, +	{STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"}, +	{STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES, +	"STATUS_LICENSE_QUOTA_EXCEEDED"}, +	{STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"}, +	{STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"}, +	{STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"}, +	{STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"}, +	{STATUS_UNSUPPORTED_COMPRESSION, -EIO, +	"STATUS_UNSUPPORTED_COMPRESSION"}, +	{STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"}, +	{STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO, +	"STATUS_INVALID_PLUGPLAY_DEVICE_PATH"}, +	{STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO, +	"STATUS_DRIVER_ORDINAL_NOT_FOUND"}, +	{STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO, +	"STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"}, +	{STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"}, +	{STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"}, +	{STATUS_QUOTA_LIST_INCONSISTENT, -EIO, +	"STATUS_QUOTA_LIST_INCONSISTENT"}, +	{STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"}, +	{STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"}, +	{STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"}, +	{STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"}, +	{STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"}, +	{STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"}, +	{STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"}, +	{STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"}, +	{STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"}, +	{STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"}, +	{STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"}, +	{STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"}, +	{STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"}, +	{STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"}, +	{STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"}, +	{STATUS_IO_REPARSE_TAG_MISMATCH, -EIO, +	"STATUS_IO_REPARSE_TAG_MISMATCH"}, +	{STATUS_IO_REPARSE_DATA_INVALID, -EIO, +	"STATUS_IO_REPARSE_DATA_INVALID"}, +	{STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO, +	"STATUS_IO_REPARSE_TAG_NOT_HANDLED"}, +	{STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, +	"STATUS_REPARSE_POINT_NOT_RESOLVED"}, +	{STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, +	"STATUS_DIRECTORY_IS_A_REPARSE_POINT"}, +	{STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"}, +	{STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"}, +	{STATUS_DESTINATION_ELEMENT_FULL, -EIO, +	"STATUS_DESTINATION_ELEMENT_FULL"}, +	{STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO, +	"STATUS_ILLEGAL_ELEMENT_ADDRESS"}, +	{STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"}, +	{STATUS_REINITIALIZATION_NEEDED, -EIO, +	"STATUS_REINITIALIZATION_NEEDED"}, +	{STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"}, +	{STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"}, +	{STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"}, +	{STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"}, +	{STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"}, +	{STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"}, +	{STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"}, +	{STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"}, +	{STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"}, +	{STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"}, +	{STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"}, +	{STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"}, +	{STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"}, +	{STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"}, +	{STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"}, +	{STATUS_POLICY_OBJECT_NOT_FOUND, -EIO, +	"STATUS_POLICY_OBJECT_NOT_FOUND"}, +	{STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"}, +	{STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"}, +	{STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO, +	"STATUS_REMOTE_STORAGE_NOT_ACTIVE"}, +	{STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO, +	"STATUS_REMOTE_STORAGE_MEDIA_ERROR"}, +	{STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"}, +	{STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"}, +	{STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO, +	"STATUS_DS_NO_ATTRIBUTE_OR_VALUE"}, +	{STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO, +	"STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"}, +	{STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO, +	"STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"}, +	{STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO, +	"STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"}, +	{STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"}, +	{STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"}, +	{STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"}, +	{STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"}, +	{STATUS_DS_INCORRECT_ROLE_OWNER, -EIO, +	"STATUS_DS_INCORRECT_ROLE_OWNER"}, +	{STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"}, +	{STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"}, +	{STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"}, +	{STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"}, +	{STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"}, +	{STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO, +	"STATUS_DS_CROSS_DOM_MOVE_FAILED"}, +	{STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"}, +	{STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO, +	"STATUS_DIRECTORY_SERVICE_REQUIRED"}, +	{STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO, +	"STATUS_REPARSE_ATTRIBUTE_CONFLICT"}, +	{STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"}, +	{STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"}, +	{STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"}, +	{STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"}, +	{STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO, +	"STATUS_JOURNAL_DELETE_IN_PROGRESS"}, +	{STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"}, +	{STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"}, +	{STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO, +	"STATUS_DS_ADMIN_LIMIT_EXCEEDED"}, +	{STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"}, +	{STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO, +	"STATUS_MUTUAL_AUTHENTICATION_FAILED"}, +	{STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"}, +	{STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO, +	"STATUS_DATATYPE_MISALIGNMENT_ERROR"}, +	{STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"}, +	{STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"}, +	{STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"}, +	{STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"}, +	{STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"}, +	{STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"}, +	{STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"}, +	{STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO, +	"STATUS_DS_SENSITIVE_GROUP_VIOLATION"}, +	{STATUS_PNP_RESTART_ENUMERATION, -EIO, +	"STATUS_PNP_RESTART_ENUMERATION"}, +	{STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"}, +	{STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO, +	"STATUS_DS_CANT_MOD_PRIMARYGROUPID"}, +	{STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO, +	"STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"}, +	{STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"}, +	{STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"}, +	{STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"}, +	{STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO, +	"STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"}, +	{STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO, +	"STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"}, +	{STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO, +	"STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"}, +	{STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, +	"STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"}, +	{STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO, +	"STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"}, +	{STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO, +	"STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"}, +	{STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO, +	"STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"}, +	{STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO, +	"STATUS_DS_HAVE_PRIMARY_MEMBERS"}, +	{STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"}, +	{STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"}, +	{STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO, +	"STATUS_SAM_NEED_BOOTKEY_PASSWORD"}, +	{STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO, +	"STATUS_SAM_NEED_BOOTKEY_FLOPPY"}, +	{STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"}, +	{STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"}, +	{STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"}, +	{STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"}, +	{STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO, +	"STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"}, +	{STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO, +	"STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"}, +	{STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT, +	"STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"}, +	{STATUS_MULTIPLE_FAULT_VIOLATION, -EIO, +	"STATUS_MULTIPLE_FAULT_VIOLATION"}, +	{STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO, +	"STATUS_CURRENT_DOMAIN_NOT_ALLOWED"}, +	{STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"}, +	{STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"}, +	{STATUS_DS_INIT_FAILURE_CONSOLE, -EIO, +	"STATUS_DS_INIT_FAILURE_CONSOLE"}, +	{STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO, +	"STATUS_DS_SAM_INIT_FAILURE_CONSOLE"}, +	{STATUS_UNFINISHED_CONTEXT_DELETED, -EIO, +	"STATUS_UNFINISHED_CONTEXT_DELETED"}, +	{STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"}, +	{STATUS_OBJECTID_NOT_FOUND, -EIO, "STATUS_OBJECTID_NOT_FOUND"}, +	{STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"}, +	{STATUS_WRONG_CREDENTIAL_HANDLE, -EIO, +	"STATUS_WRONG_CREDENTIAL_HANDLE"}, +	{STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"}, +	{STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"}, +	{STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"}, +	{STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO, +	"STATUS_STRONG_CRYPTO_NOT_SUPPORTED"}, +	{STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"}, +	{STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"}, +	{STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"}, +	{STATUS_SMARTCARD_LOGON_REQUIRED, -EIO, +	"STATUS_SMARTCARD_LOGON_REQUIRED"}, +	{STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"}, +	{STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"}, +	{STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"}, +	{STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"}, +	{STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO, +	"STATUS_SERVER_SHUTDOWN_IN_PROGRESS"}, +	{STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP, +	"STATUS_NOT_SUPPORTED_ON_SBS"}, +	{STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"}, +	{STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"}, +	{STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"}, +	{STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"}, +	{STATUS_COPY_PROTECTION_FAILURE, -EIO, +	"STATUS_COPY_PROTECTION_FAILURE"}, +	{STATUS_CSS_AUTHENTICATION_FAILURE, -EIO, +	"STATUS_CSS_AUTHENTICATION_FAILURE"}, +	{STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"}, +	{STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO, +	"STATUS_CSS_KEY_NOT_ESTABLISHED"}, +	{STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"}, +	{STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"}, +	{STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"}, +	{STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"}, +	{STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO, +	"STATUS_SMARTCARD_SUBSYSTEM_FAILURE"}, +	{STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"}, +	{STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"}, +	{STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"}, +	{STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"}, +	{STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"}, +	{STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"}, +	{STATUS_DS_VERSION_CHECK_FAILURE, -EIO, +	"STATUS_DS_VERSION_CHECK_FAILURE"}, +	{STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"}, +	{STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"}, +	{STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, +	"STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"}, +	{STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"}, +	{STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"}, +	{STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"}, +	{STATUS_NETWORK_SESSION_EXPIRED, -EIO, +	"STATUS_NETWORK_SESSION_EXPIRED"}, +	{STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"}, +	{STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"}, +	{STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"}, +	{STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES, +	"STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"}, +	{STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES, +	"STATUS_ACCESS_DISABLED_BY_POLICY_PATH"}, +	{STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES, +	"STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"}, +	{STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES, +	"STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"}, +	{STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"}, +	{STATUS_DEVICE_ENUMERATION_ERROR, -EIO, +	"STATUS_DEVICE_ENUMERATION_ERROR"}, +	{STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO, +	"STATUS_MOUNT_POINT_NOT_RESOLVED"}, +	{STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO, +	"STATUS_INVALID_DEVICE_OBJECT_PARAMETER"}, +	{STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"}, +	{STATUS_DRIVER_BLOCKED_CRITICAL, -EIO, +	"STATUS_DRIVER_BLOCKED_CRITICAL"}, +	{STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"}, +	{STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"}, +	{STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"}, +	{STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO, +	"STATUS_INVALID_IMPORT_OF_NON_DLL"}, +	{STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"}, +	{STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES, +	"STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"}, +	{STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"}, +	{STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"}, +	{STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"}, +	{STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"}, +	{STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO, +	"STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"}, +	{STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"}, +	{STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO, +	"STATUS_SMARTCARD_NO_KEY_CONTAINER"}, +	{STATUS_SMARTCARD_NO_CERTIFICATE, -EIO, +	"STATUS_SMARTCARD_NO_CERTIFICATE"}, +	{STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"}, +	{STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"}, +	{STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"}, +	{STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"}, +	{STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"}, +	{STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"}, +	{STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"}, +	{STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"}, +	{STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO, +	"STATUS_DRIVER_FAILED_PRIOR_UNLOAD"}, +	{STATUS_SMARTCARD_SILENT_CONTEXT, -EIO, +	"STATUS_SMARTCARD_SILENT_CONTEXT"}, +	{STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, +	"STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"}, +	{STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, +	"STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"}, +	{STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT, +	"STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"}, +	{STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"}, +	{STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"}, +	{STATUS_DS_GROUP_CONVERSION_ERROR, -EIO, +	"STATUS_DS_GROUP_CONVERSION_ERROR"}, +	{STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO, +	"STATUS_VOLSNAP_PREPARE_HIBERNATE"}, +	{STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"}, +	{STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"}, +	{STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"}, +	{STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO, +	"STATUS_CROSSREALM_DELEGATION_FAILURE"}, +	{STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"}, +	{STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO, +	"STATUS_ISSUING_CA_UNTRUSTED_KDC"}, +	{STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"}, +	{STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"}, +	{STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT, +	"STATUS_PARAMETER_QUOTA_EXCEEDED"}, +	{STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"}, +	{STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"}, +	{STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO, +	"STATUS_AUTHENTICATION_FIREWALL_FAILED"}, +	{STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"}, +	{STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO, +	"STATUS_HUNG_DISPLAY_DRIVER_THREAD"}, +	{STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO, +	"STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"}, +	{STATUS_INVALID_CRUNTIME_PARAMETER, -EIO, +	"STATUS_INVALID_CRUNTIME_PARAMETER"}, +	{STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"}, +	{STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"}, +	{STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"}, +	{STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"}, +	{STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO, +	"STATUS_INCOMPATIBLE_DRIVER_BLOCKED"}, +	{STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"}, +	{STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"}, +	{STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"}, +	{STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"}, +	{STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"}, +	{STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO, +	"STATUS_REQUEST_OUT_OF_SEQUENCE"}, +	{STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"}, +	{STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"}, +	{STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"}, +	{STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO, +	"STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"}, +	{STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"}, +	{STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"}, +	{STATUS_CRED_REQUIRES_CONFIRMATION, -EIO, +	"STATUS_CRED_REQUIRES_CONFIRMATION"}, +	{STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO, +	"STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"}, +	{STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO, +	"STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"}, +	{STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO, +	"STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"}, +	{STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO, +	"STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"}, +	{STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO, +	"STATUS_CS_ENCRYPTION_FILE_NOT_CSE"}, +	{STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"}, +	{STATUS_DRIVER_PROCESS_TERMINATED, -EIO, +	"STATUS_DRIVER_PROCESS_TERMINATED"}, +	{STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO, +	"STATUS_AMBIGUOUS_SYSTEM_DEVICE"}, +	{STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO, +	"STATUS_SYSTEM_DEVICE_NOT_FOUND"}, +	{STATUS_RESTART_BOOT_APPLICATION, -EIO, +	"STATUS_RESTART_BOOT_APPLICATION"}, +	{STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"}, +	{STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, +	{STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, +	{STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, +	{STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, +	{STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, +	{STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, +	{STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"}, +	{STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"}, +	{STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO, +	"STATUS_LPC_RECEIVE_BUFFER_EXPECTED"}, +	{STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO, +	"STATUS_LPC_INVALID_CONNECTION_USAGE"}, +	{STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO, +	"STATUS_LPC_REQUESTS_NOT_ALLOWED"}, +	{STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"}, +	{STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"}, +	{STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO, +	"STATUS_THREADPOOL_HANDLE_EXCEPTION"}, +	{STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO, +	"STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"}, +	{STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO, +	"STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"}, +	{STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO, +	"STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"}, +	{STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO, +	"STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"}, +	{STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO, +	"STATUS_THREADPOOL_RELEASED_DURING_OPERATION"}, +	{STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO, +	"STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"}, +	{STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO, +	"STATUS_APC_RETURNED_WHILE_IMPERSONATING"}, +	{STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"}, +	{STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"}, +	{STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO, +	"STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"}, +	{STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"}, +	{STATUS_INVALID_IDN_NORMALIZATION, -EIO, +	"STATUS_INVALID_IDN_NORMALIZATION"}, +	{STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"}, +	{STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"}, +	{STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"}, +	{STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO, +	"STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"}, +	{STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO, +	"STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"}, +	{STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"}, +	{STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO, +	"STATUS_CALLBACK_RETURNED_TRANSACTION"}, +	{STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO, +	"STATUS_CALLBACK_RETURNED_LDR_LOCK"}, +	{STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"}, +	{STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO, +	"STATUS_CALLBACK_RETURNED_PRI_BACK"}, +	{STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO, +	"STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"}, +	{STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"}, +	{STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO, +	"STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"}, +	{STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"}, +	{STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"}, +	{STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"}, +	{STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"}, +	{STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"}, +	{STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"}, +	{STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"}, +	{STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"}, +	{STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"}, +	{STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"}, +	{STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"}, +	{STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"}, +	{STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"}, +	{STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"}, +	{STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"}, +	{STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"}, +	{STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"}, +	{STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"}, +	{STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP, +	"STATUS_PROTOCOL_NOT_SUPPORTED"}, +	{STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO, +	"STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"}, +	{STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO, +	"STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"}, +	{STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO, +	"STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"}, +	{STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"}, +	{STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"}, +	{STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"}, +	{STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"}, +	{DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"}, +	{DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"}, +	{RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"}, +	{RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"}, +	{RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"}, +	{RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP, +	"RPC_NT_PROTSEQ_NOT_SUPPORTED"}, +	{RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"}, +	{RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"}, +	{RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO, +	"RPC_NT_INVALID_ENDPOINT_FORMAT"}, +	{RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"}, +	{RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"}, +	{RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"}, +	{RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"}, +	{RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"}, +	{RPC_NT_TYPE_ALREADY_REGISTERED, -EIO, +	"RPC_NT_TYPE_ALREADY_REGISTERED"}, +	{RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"}, +	{RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"}, +	{RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"}, +	{RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"}, +	{RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"}, +	{RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"}, +	{RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"}, +	{RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"}, +	{RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"}, +	{RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"}, +	{RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"}, +	{RPC_NT_INVALID_NETWORK_OPTIONS, -EIO, +	"RPC_NT_INVALID_NETWORK_OPTIONS"}, +	{RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"}, +	{RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"}, +	{RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"}, +	{RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"}, +	{RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"}, +	{RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"}, +	{RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"}, +	{RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"}, +	{RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"}, +	{RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"}, +	{RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO, +	"RPC_NT_UNSUPPORTED_NAME_SYNTAX"}, +	{RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"}, +	{RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"}, +	{RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"}, +	{RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"}, +	{RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"}, +	{RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"}, +	{RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"}, +	{RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"}, +	{RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"}, +	{RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"}, +	{RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"}, +	{RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"}, +	{EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"}, +	{EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"}, +	{EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"}, +	{RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"}, +	{RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"}, +	{RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"}, +	{RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"}, +	{RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO, +	"RPC_NT_NOT_ALL_OBJS_UNEXPORTED"}, +	{RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"}, +	{RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"}, +	{RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"}, +	{RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO, +	"RPC_NT_NAME_SERVICE_UNAVAILABLE"}, +	{RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"}, +	{RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"}, +	{RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"}, +	{RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"}, +	{RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"}, +	{RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"}, +	{RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"}, +	{RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"}, +	{RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"}, +	{RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"}, +	{RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"}, +	{RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"}, +	{EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"}, +	{RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"}, +	{RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"}, +	{RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"}, +	{RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"}, +	{RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"}, +	{RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO, +	"RPC_NT_UNSUPPORTED_AUTHN_LEVEL"}, +	{RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"}, +	{RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"}, +	{RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"}, +	{RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"}, +	{RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"}, +	{RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"}, +	{RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"}, +	{RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"}, +	{RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO, +	"RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"}, +	{RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO, +	"RPC_NT_SS_CHAR_TRANS_SHORT_FILE"}, +	{RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"}, +	{RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"}, +	{RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"}, +	{RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"}, +	{RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO, +	"RPC_NT_SS_CANNOT_GET_CALL_HANDLE"}, +	{RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"}, +	{RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO, +	"RPC_NT_ENUM_VALUE_OUT_OF_RANGE"}, +	{RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"}, +	{RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"}, +	{RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"}, +	{RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"}, +	{RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"}, +	{RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"}, +	{RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"}, +	{RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"}, +	{RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"}, +	{RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"}, +	{RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"}, +	{STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"}, +	{STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"}, +	{STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO, +	"STATUS_PNP_IRQ_TRANSLATION_FAILED"}, +	{STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"}, +	{STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"}, +	{STATUS_CTX_WINSTATION_NAME_INVALID, -EIO, +	"STATUS_CTX_WINSTATION_NAME_INVALID"}, +	{STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"}, +	{STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"}, +	{STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"}, +	{STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"}, +	{STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO, +	"STATUS_CTX_MODEM_INF_NOT_FOUND"}, +	{STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"}, +	{STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"}, +	{STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT, +	"STATUS_CTX_MODEM_RESPONSE_TIMEOUT"}, +	{STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO, +	"STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"}, +	{STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO, +	"STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"}, +	{STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY, +	"STATUS_CTX_MODEM_RESPONSE_BUSY"}, +	{STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO, +	"STATUS_CTX_MODEM_RESPONSE_VOICE"}, +	{STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"}, +	{STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO, +	"STATUS_CTX_LICENSE_CLIENT_INVALID"}, +	{STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO, +	"STATUS_CTX_LICENSE_NOT_AVAILABLE"}, +	{STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"}, +	{STATUS_CTX_WINSTATION_NOT_FOUND, -EIO, +	"STATUS_CTX_WINSTATION_NOT_FOUND"}, +	{STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO, +	"STATUS_CTX_WINSTATION_NAME_COLLISION"}, +	{STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"}, +	{STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"}, +	{STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"}, +	{STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"}, +	{STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO, +	"STATUS_CTX_CLIENT_QUERY_TIMEOUT"}, +	{STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"}, +	{STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"}, +	{STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"}, +	{STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES, +	"STATUS_CTX_WINSTATION_ACCESS_DENIED"}, +	{STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"}, +	{STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"}, +	{STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"}, +	{STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"}, +	{STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"}, +	{STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO, +	"STATUS_CTX_CLIENT_LICENSE_NOT_SET"}, +	{STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO, +	"STATUS_CTX_CLIENT_LICENSE_IN_USE"}, +	{STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO, +	"STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"}, +	{STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"}, +	{STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"}, +	{STATUS_CTX_SECURITY_LAYER_ERROR, -EIO, +	"STATUS_CTX_SECURITY_LAYER_ERROR"}, +	{STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO, +	"STATUS_TS_INCOMPATIBLE_SESSIONS"}, +	{STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"}, +	{STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"}, +	{STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"}, +	{STATUS_MUI_INVALID_LOCALE_NAME, -EIO, +	"STATUS_MUI_INVALID_LOCALE_NAME"}, +	{STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO, +	"STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"}, +	{STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"}, +	{STATUS_RESOURCE_ENUM_USER_STOP, -EIO, +	"STATUS_RESOURCE_ENUM_USER_STOP"}, +	{STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"}, +	{STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"}, +	{STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO, +	"STATUS_CLUSTER_JOIN_IN_PROGRESS"}, +	{STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"}, +	{STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO, +	"STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"}, +	{STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"}, +	{STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO, +	"STATUS_CLUSTER_NETWORK_NOT_FOUND"}, +	{STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO, +	"STATUS_CLUSTER_NETINTERFACE_EXISTS"}, +	{STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO, +	"STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"}, +	{STATUS_CLUSTER_INVALID_REQUEST, -EIO, +	"STATUS_CLUSTER_INVALID_REQUEST"}, +	{STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO, +	"STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"}, +	{STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"}, +	{STATUS_CLUSTER_NODE_UNREACHABLE, -EIO, +	"STATUS_CLUSTER_NODE_UNREACHABLE"}, +	{STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO, +	"STATUS_CLUSTER_NODE_NOT_MEMBER"}, +	{STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO, +	"STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"}, +	{STATUS_CLUSTER_INVALID_NETWORK, -EIO, +	"STATUS_CLUSTER_INVALID_NETWORK"}, +	{STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO, +	"STATUS_CLUSTER_NO_NET_ADAPTERS"}, +	{STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"}, +	{STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"}, +	{STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO, +	"STATUS_CLUSTER_NODE_NOT_PAUSED"}, +	{STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO, +	"STATUS_CLUSTER_NO_SECURITY_CONTEXT"}, +	{STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO, +	"STATUS_CLUSTER_NETWORK_NOT_INTERNAL"}, +	{STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"}, +	{STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"}, +	{STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"}, +	{STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"}, +	{STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"}, +	{STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"}, +	{STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"}, +	{STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"}, +	{STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"}, +	{STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"}, +	{STATUS_ACPI_INVALID_TARGETTYPE, -EIO, +	"STATUS_ACPI_INVALID_TARGETTYPE"}, +	{STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO, +	"STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"}, +	{STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO, +	"STATUS_ACPI_ADDRESS_NOT_MAPPED"}, +	{STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"}, +	{STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"}, +	{STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"}, +	{STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"}, +	{STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO, +	"STATUS_ACPI_INVALID_ACCESS_SIZE"}, +	{STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO, +	"STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"}, +	{STATUS_ACPI_ALREADY_INITIALIZED, -EIO, +	"STATUS_ACPI_ALREADY_INITIALIZED"}, +	{STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"}, +	{STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO, +	"STATUS_ACPI_INVALID_MUTEX_LEVEL"}, +	{STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"}, +	{STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"}, +	{STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"}, +	{STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"}, +	{STATUS_ACPI_REG_HANDLER_FAILED, -EIO, +	"STATUS_ACPI_REG_HANDLER_FAILED"}, +	{STATUS_ACPI_POWER_REQUEST_FAILED, -EIO, +	"STATUS_ACPI_POWER_REQUEST_FAILED"}, +	{STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"}, +	{STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"}, +	{STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO, +	"STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"}, +	{STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"}, +	{STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO, +	"STATUS_SXS_MANIFEST_FORMAT_ERROR"}, +	{STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO, +	"STATUS_SXS_MANIFEST_PARSE_ERROR"}, +	{STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO, +	"STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"}, +	{STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"}, +	{STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"}, +	{STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"}, +	{STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO, +	"STATUS_SXS_THREAD_QUERIES_DISABLED"}, +	{STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"}, +	{STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO, +	"STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"}, +	{STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"}, +	{STATUS_SXS_INVALID_DEACTIVATION, -EIO, +	"STATUS_SXS_INVALID_DEACTIVATION"}, +	{STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO, +	"STATUS_SXS_MULTIPLE_DEACTIVATION"}, +	{STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO, +	"STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"}, +	{STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO, +	"STATUS_SXS_PROCESS_TERMINATION_REQUESTED"}, +	{STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO, +	"STATUS_SXS_CORRUPT_ACTIVATION_STACK"}, +	{STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"}, +	{STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO, +	"STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"}, +	{STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO, +	"STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"}, +	{STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO, +	"STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"}, +	{STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO, +	"STATUS_SXS_IDENTITY_PARSE_ERROR"}, +	{STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO, +	"STATUS_SXS_COMPONENT_STORE_CORRUPT"}, +	{STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"}, +	{STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO, +	"STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"}, +	{STATUS_SXS_IDENTITIES_DIFFERENT, -EIO, +	"STATUS_SXS_IDENTITIES_DIFFERENT"}, +	{STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO, +	"STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"}, +	{STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO, +	"STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"}, +	{STATUS_ADVANCED_INSTALLER_FAILED, -EIO, +	"STATUS_ADVANCED_INSTALLER_FAILED"}, +	{STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"}, +	{STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"}, +	{STATUS_SXS_SETTING_NOT_REGISTERED, -EIO, +	"STATUS_SXS_SETTING_NOT_REGISTERED"}, +	{STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO, +	"STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"}, +	{STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO, +	"STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"}, +	{STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"}, +	{STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"}, +	{STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"}, +	{STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"}, +	{STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"}, +	{STATUS_TM_INITIALIZATION_FAILED, -EIO, +	"STATUS_TM_INITIALIZATION_FAILED"}, +	{STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"}, +	{STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"}, +	{STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"}, +	{STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"}, +	{STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO, +	"STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"}, +	{STATUS_LOG_RESIZE_INVALID_SIZE, -EIO, +	"STATUS_LOG_RESIZE_INVALID_SIZE"}, +	{STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO, +	"STATUS_REMOTE_FILE_VERSION_MISMATCH"}, +	{STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO, +	"STATUS_CRM_PROTOCOL_ALREADY_EXISTS"}, +	{STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO, +	"STATUS_TRANSACTION_PROPAGATION_FAILED"}, +	{STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"}, +	{STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO, +	"STATUS_TRANSACTION_SUPERIOR_EXISTS"}, +	{STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO, +	"STATUS_TRANSACTION_REQUEST_NOT_VALID"}, +	{STATUS_TRANSACTION_NOT_REQUESTED, -EIO, +	"STATUS_TRANSACTION_NOT_REQUESTED"}, +	{STATUS_TRANSACTION_ALREADY_ABORTED, -EIO, +	"STATUS_TRANSACTION_ALREADY_ABORTED"}, +	{STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO, +	"STATUS_TRANSACTION_ALREADY_COMMITTED"}, +	{STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO, +	"STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"}, +	{STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO, +	"STATUS_CURRENT_TRANSACTION_NOT_VALID"}, +	{STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"}, +	{STATUS_OBJECT_NO_LONGER_EXISTS, -EIO, +	"STATUS_OBJECT_NO_LONGER_EXISTS"}, +	{STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO, +	"STATUS_STREAM_MINIVERSION_NOT_FOUND"}, +	{STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO, +	"STATUS_STREAM_MINIVERSION_NOT_VALID"}, +	{STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO, +	"STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"}, +	{STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO, +	"STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"}, +	{STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO, +	"STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"}, +	{STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"}, +	{STATUS_LOG_CORRUPTION_DETECTED, -EIO, +	"STATUS_LOG_CORRUPTION_DETECTED"}, +	{STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"}, +	{STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO, +	"STATUS_ENLISTMENT_NOT_SUPERIOR"}, +	{STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO, +	"STATUS_FILE_IDENTITY_NOT_PERSISTENT"}, +	{STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO, +	"STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"}, +	{STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"}, +	{STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"}, +	{STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO, +	"STATUS_INDOUBT_TRANSACTIONS_EXIST"}, +	{STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"}, +	{STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"}, +	{STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"}, +	{STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO, +	"STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"}, +	{STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO, +	"STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"}, +	{STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO, +	"STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"}, +	{STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO, +	"STATUS_TRANSACTION_REQUIRED_PROMOTION"}, +	{STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO, +	"STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"}, +	{STATUS_TRANSACTIONS_NOT_FROZEN, -EIO, +	"STATUS_TRANSACTIONS_NOT_FROZEN"}, +	{STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO, +	"STATUS_TRANSACTION_FREEZE_IN_PROGRESS"}, +	{STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"}, +	{STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO, +	"STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"}, +	{STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO, +	"STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"}, +	{STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"}, +	{STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"}, +	{STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO, +	"STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"}, +	{STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO, +	"STATUS_CANNOT_ABORT_TRANSACTIONS"}, +	{STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"}, +	{STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO, +	"STATUS_RESOURCEMANAGER_NOT_FOUND"}, +	{STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"}, +	{STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO, +	"STATUS_TRANSACTIONMANAGER_NOT_FOUND"}, +	{STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO, +	"STATUS_TRANSACTIONMANAGER_NOT_ONLINE"}, +	{STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO, +	"STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"}, +	{STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"}, +	{STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO, +	"STATUS_TRANSACTION_OBJECT_EXPIRED"}, +	{STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO, +	"STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"}, +	{STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO, +	"STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"}, +	{STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO, +	"STATUS_TRANSACTION_RECORD_TOO_LONG"}, +	{STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO, +	"STATUS_NO_LINK_TRACKING_IN_TRANSACTION"}, +	{STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP, +	"STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"}, +	{STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO, +	"STATUS_TRANSACTION_INTEGRITY_VIOLATED"}, +	{STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"}, +	{STATUS_LOG_SECTOR_PARITY_INVALID, -EIO, +	"STATUS_LOG_SECTOR_PARITY_INVALID"}, +	{STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"}, +	{STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"}, +	{STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"}, +	{STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"}, +	{STATUS_LOG_READ_CONTEXT_INVALID, -EIO, +	"STATUS_LOG_READ_CONTEXT_INVALID"}, +	{STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"}, +	{STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"}, +	{STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"}, +	{STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"}, +	{STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"}, +	{STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"}, +	{STATUS_LOG_METADATA_INCONSISTENT, -EIO, +	"STATUS_LOG_METADATA_INCONSISTENT"}, +	{STATUS_LOG_RESERVATION_INVALID, -EIO, +	"STATUS_LOG_RESERVATION_INVALID"}, +	{STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"}, +	{STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO, +	"STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"}, +	{STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"}, +	{STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO, +	"STATUS_LOG_POLICY_ALREADY_INSTALLED"}, +	{STATUS_LOG_POLICY_NOT_INSTALLED, -EIO, +	"STATUS_LOG_POLICY_NOT_INSTALLED"}, +	{STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"}, +	{STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"}, +	{STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO, +	"STATUS_LOG_PINNED_ARCHIVE_TAIL"}, +	{STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"}, +	{STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO, +	"STATUS_LOG_RECORDS_RESERVED_INVALID"}, +	{STATUS_LOG_SPACE_RESERVED_INVALID, -EIO, +	"STATUS_LOG_SPACE_RESERVED_INVALID"}, +	{STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"}, +	{STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"}, +	{STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"}, +	{STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"}, +	{STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO, +	"STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"}, +	{STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO, +	"STATUS_LOG_ARCHIVE_IN_PROGRESS"}, +	{STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"}, +	{STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO, +	"STATUS_LOG_NOT_ENOUGH_CONTAINERS"}, +	{STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO, +	"STATUS_LOG_CLIENT_ALREADY_REGISTERED"}, +	{STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO, +	"STATUS_LOG_CLIENT_NOT_REGISTERED"}, +	{STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO, +	"STATUS_LOG_FULL_HANDLER_IN_PROGRESS"}, +	{STATUS_LOG_CONTAINER_READ_FAILED, -EIO, +	"STATUS_LOG_CONTAINER_READ_FAILED"}, +	{STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO, +	"STATUS_LOG_CONTAINER_WRITE_FAILED"}, +	{STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO, +	"STATUS_LOG_CONTAINER_OPEN_FAILED"}, +	{STATUS_LOG_CONTAINER_STATE_INVALID, -EIO, +	"STATUS_LOG_CONTAINER_STATE_INVALID"}, +	{STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"}, +	{STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"}, +	{STATUS_LOG_METADATA_FLUSH_FAILED, -EIO, +	"STATUS_LOG_METADATA_FLUSH_FAILED"}, +	{STATUS_LOG_INCONSISTENT_SECURITY, -EIO, +	"STATUS_LOG_INCONSISTENT_SECURITY"}, +	{STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO, +	"STATUS_LOG_APPENDED_FLUSH_FAILED"}, +	{STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"}, +	{STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO, +	"STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"}, +	{STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"}, +	{STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO, +	"STATUS_FLT_CONTEXT_ALREADY_DEFINED"}, +	{STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO, +	"STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"}, +	{STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"}, +	{STATUS_FLT_INVALID_NAME_REQUEST, -EIO, +	"STATUS_FLT_INVALID_NAME_REQUEST"}, +	{STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO, +	"STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"}, +	{STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"}, +	{STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"}, +	{STATUS_FLT_POST_OPERATION_CLEANUP, -EIO, +	"STATUS_FLT_POST_OPERATION_CLEANUP"}, +	{STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"}, +	{STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"}, +	{STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO, +	"STATUS_FLT_MUST_BE_NONPAGED_POOL"}, +	{STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"}, +	{STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"}, +	{STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"}, +	{STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"}, +	{STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO, +	"STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"}, +	{STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO, +	"STATUS_FLT_INSTANCE_NAME_COLLISION"}, +	{STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"}, +	{STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"}, +	{STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"}, +	{STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO, +	"STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"}, +	{STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO, +	"STATUS_FLT_INVALID_CONTEXT_REGISTRATION"}, +	{STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"}, +	{STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"}, +	{STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO, +	"STATUS_FLT_VOLUME_ALREADY_MOUNTED"}, +	{STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"}, +	{STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO, +	"STATUS_FLT_CONTEXT_ALREADY_LINKED"}, +	{STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO, +	"STATUS_FLT_NO_WAITER_FOR_REPLY"}, +	{STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"}, +	{STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO, +	"STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"}, +	{STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO, +	"STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"}, +	{STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO, +	"STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"}, +	{STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO, +	"STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"}, +	{STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO, +	"STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"}, +	{STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO, +	"STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"}, +	{STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO, +	"STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"}, +	{STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO, +	"STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"}, +	{STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO, +	"STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"}, +	{STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO, +	"STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"}, +	{STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO, +	"STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"}, +	{STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO, +	"STATUS_GRAPHICS_ADAPTER_WAS_RESET"}, +	{STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO, +	"STATUS_GRAPHICS_INVALID_DRIVER_MODEL"}, +	{STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO, +	"STATUS_GRAPHICS_PRESENT_MODE_CHANGED"}, +	{STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO, +	"STATUS_GRAPHICS_PRESENT_OCCLUDED"}, +	{STATUS_GRAPHICS_PRESENT_DENIED, -EIO, +	"STATUS_GRAPHICS_PRESENT_DENIED"}, +	{STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO, +	"STATUS_GRAPHICS_CANNOTCOLORCONVERT"}, +	{STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO, +	"STATUS_GRAPHICS_NO_VIDEO_MEMORY"}, +	{STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO, +	"STATUS_GRAPHICS_CANT_LOCK_MEMORY"}, +	{STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY, +	"STATUS_GRAPHICS_ALLOCATION_BUSY"}, +	{STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO, +	"STATUS_GRAPHICS_TOO_MANY_REFERENCES"}, +	{STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO, +	"STATUS_GRAPHICS_TRY_AGAIN_LATER"}, +	{STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"}, +	{STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO, +	"STATUS_GRAPHICS_ALLOCATION_INVALID"}, +	{STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO, +	"STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"}, +	{STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO, +	"STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"}, +	{STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO, +	"STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"}, +	{STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO, +	"STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"}, +	{STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO, +	"STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"}, +	{STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO, +	"STATUS_GRAPHICS_ALLOCATION_CLOSED"}, +	{STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO, +	"STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"}, +	{STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO, +	"STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"}, +	{STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO, +	"STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"}, +	{STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO, +	"STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"}, +	{STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO, +	"STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"}, +	{STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"}, +	{STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"}, +	{STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"}, +	{STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"}, +	{STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"}, +	{STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"}, +	{STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO, +	"STATUS_GRAPHICS_INVALID_FREQUENCY"}, +	{STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO, +	"STATUS_GRAPHICS_INVALID_ACTIVE_REGION"}, +	{STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO, +	"STATUS_GRAPHICS_INVALID_TOTAL_REGION"}, +	{STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"}, +	{STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"}, +	{STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO, +	"STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"}, +	{STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"}, +	{STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO, +	"STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"}, +	{STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"}, +	{STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"}, +	{STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO, +	"STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"}, +	{STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO, +	"STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"}, +	{STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"}, +	{STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"}, +	{STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"}, +	{STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"}, +	{STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO, +	"STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"}, +	{STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO, +	"STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"}, +	{STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"}, +	{STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"}, +	{STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"}, +	{STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO, +	"STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"}, +	{STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO, +	"STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"}, +	{STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO, +	"STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"}, +	{STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO, +	"STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"}, +	{STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"}, +	{STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO, +	"STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"}, +	{STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO, +	"STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"}, +	{STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"}, +	{STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"}, +	{STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO, +	"STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"}, +	{STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO, +	"STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"}, +	{STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO, +	"STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"}, +	{STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"}, +	{STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO, +	"STATUS_GRAPHICS_RESOURCES_NOT_RELATED"}, +	{STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO, +	"STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"}, +	{STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO, +	"STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"}, +	{STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO, +	"STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"}, +	{STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO, +	"STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"}, +	{STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"}, +	{STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO, +	"STATUS_GRAPHICS_NO_ACTIVE_VIDPN"}, +	{STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"}, +	{STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO, +	"STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"}, +	{STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"}, +	{STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO, +	"STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"}, +	{STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO, +	"STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"}, +	{STATUS_GRAPHICS_INVALID_STRIDE, -EIO, +	"STATUS_GRAPHICS_INVALID_STRIDE"}, +	{STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO, +	"STATUS_GRAPHICS_INVALID_PIXELFORMAT"}, +	{STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO, +	"STATUS_GRAPHICS_INVALID_COLORBASIS"}, +	{STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO, +	"STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"}, +	{STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO, +	"STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"}, +	{STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO, +	"STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"}, +	{STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO, +	"STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"}, +	{STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO, +	"STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"}, +	{STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO, +	"STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"}, +	{STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO, +	"STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"}, +	{STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED, +	-EIO, +	"STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO, +	"STATUS_GRAPHICS_INVALID_GAMMA_RAMP"}, +	{STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO, +	"STATUS_GRAPHICS_MODE_NOT_IN_MODESET"}, +	{STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO, +	"STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"}, +	{STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO, +	"STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"}, +	{STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO, +	"STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"}, +	{STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO, +	"STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"}, +	{STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO, +	"STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"}, +	{STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO, +	"STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"}, +	{STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO, +	"STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"}, +	{STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO, +	"STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"}, +	{STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO, +	"STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"}, +	{STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"}, +	{STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO, +	"STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"}, +	{STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO, +	"STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"}, +	{STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO, +	"STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"}, +	{STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO, +	"STATUS_GRAPHICS_INVALID_CLIENT_TYPE"}, +	{STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO, +	"STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"}, +	{STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO, +	"STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"}, +	{STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO, +	"STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"}, +	{STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO, +	"STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"}, +	{STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO, +	"STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"}, +	{STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO, +	"STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"}, +	{STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO, +	"STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"}, +	{STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO, +	"STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"}, +	{STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO, +	"STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"}, +	{STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO, +	"STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"}, +	{STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO, +	"STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"}, +	{STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS, +	-EIO, +	"STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"}, +	{STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO, +	"STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"}, +	{STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO, +	"STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"}, +	{STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO, +	"STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"}, +	{STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO, +	"STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"}, +	{STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_OPM_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_COPP_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_UAB_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO, +	"STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"}, +	{STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO, +	"STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"}, +	{STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO, +	"STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"}, +	{STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, +	"STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, +	{STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, +	"STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, +	{STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO, +	"STATUS_GRAPHICS_OPM_INVALID_POINTER"}, +	{STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO, +	"STATUS_GRAPHICS_OPM_INTERNAL_ERROR"}, +	{STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO, +	"STATUS_GRAPHICS_OPM_INVALID_HANDLE"}, +	{STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, +	"STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, +	{STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO, +	"STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"}, +	{STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO, +	"STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"}, +	{STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO, +	"STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"}, +	{STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO, +	"STATUS_GRAPHICS_PVP_HFS_FAILED"}, +	{STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO, +	"STATUS_GRAPHICS_OPM_INVALID_SRM"}, +	{STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO, +	"STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"}, +	{STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO, +	"STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"}, +	{STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO, +	"STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"}, +	{STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO, +	"STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"}, +	{STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO, +	"STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"}, +	{STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO, +	"STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"}, +	{STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO, +	"STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"}, +	{STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, +	"STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"}, +	{STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_I2C_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO, +	"STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"}, +	{STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO, +	"STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"}, +	{STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO, +	"STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"}, +	{STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO, +	"STATUS_GRAPHICS_DDCCI_INVALID_DATA"}, +	{STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE, +	-EIO, +	"STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"}, +	{STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO, +	"STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"}, +	{STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO, +	"STATUS_GRAPHICS_MCA_INTERNAL_ERROR"}, +	{STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO, +	"STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"}, +	{STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO, +	"STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"}, +	{STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO, +	"STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"}, +	{STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO, +	"STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"}, +	{STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO, +	"STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"}, +	{STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"}, +	{STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, +	"STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, +	{STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, +	"STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, +	{STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, +	"STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"}, +	{STATUS_GRAPHICS_INVALID_POINTER, -EIO, +	"STATUS_GRAPHICS_INVALID_POINTER"}, +	{STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, +	"STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, +	{STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO, +	"STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"}, +	{STATUS_GRAPHICS_INTERNAL_ERROR, -EIO, +	"STATUS_GRAPHICS_INTERNAL_ERROR"}, +	{STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, +	"STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"}, +	{STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"}, +	{STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"}, +	{STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"}, +	{STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"}, +	{STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"}, +	{STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"}, +	{STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"}, +	{STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"}, +	{STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"}, +	{STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"}, +	{STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"}, +	{STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"}, +	{STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"}, +	{STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"}, +	{STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"}, +	{STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"}, +	{STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"}, +	{STATUS_FVE_FAILED_AUTHENTICATION, -EIO, +	"STATUS_FVE_FAILED_AUTHENTICATION"}, +	{STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"}, +	{STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"}, +	{STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"}, +	{STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"}, +	{STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"}, +	{STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO, +	"STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"}, +	{STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"}, +	{STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"}, +	{STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"}, +	{STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO, +	"STATUS_FVE_AUTH_INVALID_APPLICATION"}, +	{STATUS_FVE_AUTH_INVALID_CONFIG, -EIO, +	"STATUS_FVE_AUTH_INVALID_CONFIG"}, +	{STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"}, +	{STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"}, +	{STATUS_FVE_BAD_METADATA_POINTER, -EIO, +	"STATUS_FVE_BAD_METADATA_POINTER"}, +	{STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"}, +	{STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"}, +	{STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"}, +	{STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"}, +	{STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"}, +	{STATUS_FWP_CONDITION_NOT_FOUND, -EIO, +	"STATUS_FWP_CONDITION_NOT_FOUND"}, +	{STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"}, +	{STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"}, +	{STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"}, +	{STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO, +	"STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"}, +	{STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"}, +	{STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"}, +	{STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"}, +	{STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"}, +	{STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO, +	"STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"}, +	{STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"}, +	{STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"}, +	{STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"}, +	{STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"}, +	{STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"}, +	{STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"}, +	{STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"}, +	{STATUS_FWP_NET_EVENTS_DISABLED, -EIO, +	"STATUS_FWP_NET_EVENTS_DISABLED"}, +	{STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"}, +	{STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"}, +	{STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"}, +	{STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"}, +	{STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO, +	"STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"}, +	{STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"}, +	{STATUS_FWP_NOTIFICATION_DROPPED, -EIO, +	"STATUS_FWP_NOTIFICATION_DROPPED"}, +	{STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"}, +	{STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO, +	"STATUS_FWP_INCOMPATIBLE_SA_STATE"}, +	{STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"}, +	{STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"}, +	{STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"}, +	{STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"}, +	{STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"}, +	{STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"}, +	{STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"}, +	{STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"}, +	{STATUS_FWP_INVALID_ACTION_TYPE, -EIO, +	"STATUS_FWP_INVALID_ACTION_TYPE"}, +	{STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"}, +	{STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO, +	"STATUS_FWP_MATCH_TYPE_MISMATCH"}, +	{STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"}, +	{STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"}, +	{STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"}, +	{STATUS_FWP_DUPLICATE_CONDITION, -EIO, +	"STATUS_FWP_DUPLICATE_CONDITION"}, +	{STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"}, +	{STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO, +	"STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"}, +	{STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO, +	"STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"}, +	{STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO, +	"STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"}, +	{STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO, +	"STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"}, +	{STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO, +	"STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"}, +	{STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO, +	"STATUS_FWP_INCOMPATIBLE_DH_GROUP"}, +	{STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP, +	"STATUS_FWP_EM_NOT_SUPPORTED"}, +	{STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"}, +	{STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO, +	"STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"}, +	{STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"}, +	{STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"}, +	{STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO, +	"STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"}, +	{STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO, +	"STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"}, +	{STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO, +	"STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"}, +	{STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"}, +	{STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO, +	"STATUS_FWP_INJECT_HANDLE_CLOSING"}, +	{STATUS_FWP_INJECT_HANDLE_STALE, -EIO, +	"STATUS_FWP_INJECT_HANDLE_STALE"}, +	{STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"}, +	{STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"}, +	{STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"}, +	{STATUS_NDIS_BAD_CHARACTERISTICS, -EIO, +	"STATUS_NDIS_BAD_CHARACTERISTICS"}, +	{STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"}, +	{STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"}, +	{STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"}, +	{STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"}, +	{STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"}, +	{STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO, +	"STATUS_NDIS_MULTICAST_NOT_FOUND"}, +	{STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"}, +	{STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"}, +	{STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"}, +	{STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO, +	"STATUS_NDIS_INVALID_DEVICE_REQUEST"}, +	{STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"}, +	{STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"}, +	{STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"}, +	{STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS, +	"STATUS_NDIS_BUFFER_TOO_SHORT"}, +	{STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"}, +	{STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"}, +	{STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"}, +	{STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO, +	"STATUS_NDIS_GROUP_ADDRESS_IN_USE"}, +	{STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"}, +	{STATUS_NDIS_ERROR_READING_FILE, -EIO, +	"STATUS_NDIS_ERROR_READING_FILE"}, +	{STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"}, +	{STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"}, +	{STATUS_NDIS_MEDIA_DISCONNECTED, -EIO, +	"STATUS_NDIS_MEDIA_DISCONNECTED"}, +	{STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"}, +	{STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"}, +	{STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO, +	"STATUS_NDIS_INTERFACE_NOT_FOUND"}, +	{STATUS_NDIS_UNSUPPORTED_REVISION, -EIO, +	"STATUS_NDIS_UNSUPPORTED_REVISION"}, +	{STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"}, +	{STATUS_NDIS_INVALID_PORT_STATE, -EIO, +	"STATUS_NDIS_INVALID_PORT_STATE"}, +	{STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"}, +	{STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"}, +	{STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO, +	"STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"}, +	{STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO, +	"STATUS_NDIS_DOT11_MEDIA_IN_USE"}, +	{STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO, +	"STATUS_NDIS_DOT11_POWER_STATE_INVALID"}, +	{STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"}, +	{STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO, +	"STATUS_IPSEC_SA_LIFETIME_EXPIRED"}, +	{STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"}, +	{STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO, +	"STATUS_IPSEC_REPLAY_CHECK_FAILED"}, +	{STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"}, +	{STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO, +	"STATUS_IPSEC_INTEGRITY_CHECK_FAILED"}, +	{STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"}, +	{0, 0, NULL} +}; + +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +smb2_print_status(__le32 status) +{ +	int idx = 0; + +	while (smb2_error_map_table[idx].status_string != NULL) { +		if ((smb2_error_map_table[idx].smb2_status) == status) { +			pr_notice("Status code returned 0x%08x %s\n", status, +				  smb2_error_map_table[idx].status_string); +		} +		idx++; +	} +	return; +} + +int +map_smb2_to_linux_error(char *buf, bool log_err) +{ +	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; +	unsigned int i; +	int rc = -EIO; +	__le32 smb2err = hdr->Status; + +	if (smb2err == 0) +		return 0; + +	/* mask facility */ +	if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) && +	    (smb2err != STATUS_END_OF_FILE)) +		smb2_print_status(smb2err); +	else if (cifsFYI & CIFS_RC) +		smb2_print_status(smb2err); + +	for (i = 0; i < sizeof(smb2_error_map_table) / +			sizeof(struct status_to_posix_error); i++) { +		if (smb2_error_map_table[i].smb2_status == smb2err) { +			rc = smb2_error_map_table[i].posix_error; +			break; +		} +	} + +	/* on error mapping not found  - return EIO */ + +	cifs_dbg(FYI, "Mapping SMB2 status code %d to POSIX err %d\n", +		 smb2err, rc); + +	return rc; +} diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c new file mode 100644 index 00000000000..b8021fde987 --- /dev/null +++ b/fs/cifs/smb2misc.c @@ -0,0 +1,609 @@ +/* + *   fs/cifs/smb2misc.c + * + *   Copyright (C) International Business Machines  Corp., 2002,2011 + *                 Etersoft, 2012 + *   Author(s): Steve French (sfrench@us.ibm.com) + *              Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/ctype.h> +#include "smb2pdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2status.h" + +static int +check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid) +{ +	/* +	 * Make sure that this really is an SMB, that it is a response, +	 * and that the message ids match. +	 */ +	if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) && +	    (mid == hdr->MessageId)) { +		if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) +			return 0; +		else { +			/* only one valid case where server sends us request */ +			if (hdr->Command == SMB2_OPLOCK_BREAK) +				return 0; +			else +				cifs_dbg(VFS, "Received Request not response\n"); +		} +	} else { /* bad signature or mid */ +		if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER) +			cifs_dbg(VFS, "Bad protocol string signature header %x\n", +				 *(unsigned int *) hdr->ProtocolId); +		if (mid != hdr->MessageId) +			cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", +				 mid, hdr->MessageId); +	} +	cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", hdr->MessageId); +	return 1; +} + +/* + *  The following table defines the expected "StructureSize" of SMB2 responses + *  in order by SMB2 command.  This is similar to "wct" in SMB/CIFS responses. + * + *  Note that commands are defined in smb2pdu.h in le16 but the array below is + *  indexed by command in host byte order + */ +static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { +	/* SMB2_NEGOTIATE */ __constant_cpu_to_le16(65), +	/* SMB2_SESSION_SETUP */ __constant_cpu_to_le16(9), +	/* SMB2_LOGOFF */ __constant_cpu_to_le16(4), +	/* SMB2_TREE_CONNECT */ __constant_cpu_to_le16(16), +	/* SMB2_TREE_DISCONNECT */ __constant_cpu_to_le16(4), +	/* SMB2_CREATE */ __constant_cpu_to_le16(89), +	/* SMB2_CLOSE */ __constant_cpu_to_le16(60), +	/* SMB2_FLUSH */ __constant_cpu_to_le16(4), +	/* SMB2_READ */ __constant_cpu_to_le16(17), +	/* SMB2_WRITE */ __constant_cpu_to_le16(17), +	/* SMB2_LOCK */ __constant_cpu_to_le16(4), +	/* SMB2_IOCTL */ __constant_cpu_to_le16(49), +	/* BB CHECK this ... not listed in documentation */ +	/* SMB2_CANCEL */ __constant_cpu_to_le16(0), +	/* SMB2_ECHO */ __constant_cpu_to_le16(4), +	/* SMB2_QUERY_DIRECTORY */ __constant_cpu_to_le16(9), +	/* SMB2_CHANGE_NOTIFY */ __constant_cpu_to_le16(9), +	/* SMB2_QUERY_INFO */ __constant_cpu_to_le16(9), +	/* SMB2_SET_INFO */ __constant_cpu_to_le16(2), +	/* BB FIXME can also be 44 for lease break */ +	/* SMB2_OPLOCK_BREAK */ __constant_cpu_to_le16(24) +}; + +int +smb2_check_message(char *buf, unsigned int length) +{ +	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; +	struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; +	__u64 mid = hdr->MessageId; +	__u32 len = get_rfc1002_length(buf); +	__u32 clc_len;  /* calculated length */ +	int command; + +	/* BB disable following printk later */ +	cifs_dbg(FYI, "%s length: 0x%x, smb_buf_length: 0x%x\n", +		 __func__, length, len); + +	/* +	 * Add function to do table lookup of StructureSize by command +	 * ie Validate the wct via smb2_struct_sizes table above +	 */ + +	if (length < sizeof(struct smb2_pdu)) { +		if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) { +			pdu->StructureSize2 = 0; +			/* +			 * As with SMB/CIFS, on some error cases servers may +			 * not return wct properly +			 */ +			return 0; +		} else { +			cifs_dbg(VFS, "Length less than SMB header size\n"); +		} +		return 1; +	} +	if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE - 4) { +		cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n", +			 mid); +		return 1; +	} + +	if (check_smb2_hdr(hdr, mid)) +		return 1; + +	if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { +		cifs_dbg(VFS, "Illegal structure size %u\n", +			 le16_to_cpu(hdr->StructureSize)); +		return 1; +	} + +	command = le16_to_cpu(hdr->Command); +	if (command >= NUMBER_OF_SMB2_COMMANDS) { +		cifs_dbg(VFS, "Illegal SMB2 command %d\n", command); +		return 1; +	} + +	if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { +		if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 || +		    pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) { +			/* error packets have 9 byte structure size */ +			cifs_dbg(VFS, "Illegal response size %u for command %d\n", +				 le16_to_cpu(pdu->StructureSize2), command); +			return 1; +		} else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0) +			   && (le16_to_cpu(pdu->StructureSize2) != 44) +			   && (le16_to_cpu(pdu->StructureSize2) != 36)) { +			/* special case for SMB2.1 lease break message */ +			cifs_dbg(VFS, "Illegal response size %d for oplock break\n", +				 le16_to_cpu(pdu->StructureSize2)); +			return 1; +		} +	} + +	if (4 + len != length) { +		cifs_dbg(VFS, "Total length %u RFC1002 length %u mismatch mid %llu\n", +			 length, 4 + len, mid); +		return 1; +	} + +	clc_len = smb2_calc_size(hdr); + +	if (4 + len != clc_len) { +		cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n", +			 clc_len, 4 + len, mid); +		/* create failed on symlink */ +		if (command == SMB2_CREATE_HE && +		    hdr->Status == STATUS_STOPPED_ON_SYMLINK) +			return 0; +		/* Windows 7 server returns 24 bytes more */ +		if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE) +			return 0; +		/* server can return one byte more */ +		if (clc_len == 4 + len + 1) +			return 0; +		return 1; +	} +	return 0; +} + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 responses. SMB2 responses + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { +	/* SMB2_NEGOTIATE */ true, +	/* SMB2_SESSION_SETUP */ true, +	/* SMB2_LOGOFF */ false, +	/* SMB2_TREE_CONNECT */	false, +	/* SMB2_TREE_DISCONNECT */ false, +	/* SMB2_CREATE */ true, +	/* SMB2_CLOSE */ false, +	/* SMB2_FLUSH */ false, +	/* SMB2_READ */	true, +	/* SMB2_WRITE */ false, +	/* SMB2_LOCK */	false, +	/* SMB2_IOCTL */ true, +	/* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ +	/* SMB2_ECHO */ false, +	/* SMB2_QUERY_DIRECTORY */ true, +	/* SMB2_CHANGE_NOTIFY */ true, +	/* SMB2_QUERY_INFO */ true, +	/* SMB2_SET_INFO */ false, +	/* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Returns the pointer to the beginning of the data area. Length of the data + * area and the offset to it (from the beginning of the smb are also returned. + */ +char * +smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) +{ +	*off = 0; +	*len = 0; + +	/* error responses do not have data area */ +	if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && +	    (((struct smb2_err_rsp *)hdr)->StructureSize) == +						SMB2_ERROR_STRUCTURE_SIZE2) +		return NULL; + +	/* +	 * Following commands have data areas so we have to get the location +	 * of the data buffer offset and data buffer length for the particular +	 * command. +	 */ +	switch (hdr->Command) { +	case SMB2_NEGOTIATE: +		*off = le16_to_cpu( +		    ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferOffset); +		*len = le16_to_cpu( +		    ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength); +		break; +	case SMB2_SESSION_SETUP: +		*off = le16_to_cpu( +		    ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferOffset); +		*len = le16_to_cpu( +		    ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); +		break; +	case SMB2_CREATE: +		*off = le32_to_cpu( +		    ((struct smb2_create_rsp *)hdr)->CreateContextsOffset); +		*len = le32_to_cpu( +		    ((struct smb2_create_rsp *)hdr)->CreateContextsLength); +		break; +	case SMB2_QUERY_INFO: +		*off = le16_to_cpu( +		    ((struct smb2_query_info_rsp *)hdr)->OutputBufferOffset); +		*len = le32_to_cpu( +		    ((struct smb2_query_info_rsp *)hdr)->OutputBufferLength); +		break; +	case SMB2_READ: +		*off = ((struct smb2_read_rsp *)hdr)->DataOffset; +		*len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength); +		break; +	case SMB2_QUERY_DIRECTORY: +		*off = le16_to_cpu( +		  ((struct smb2_query_directory_rsp *)hdr)->OutputBufferOffset); +		*len = le32_to_cpu( +		  ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength); +		break; +	case SMB2_IOCTL: +		*off = le32_to_cpu( +		  ((struct smb2_ioctl_rsp *)hdr)->OutputOffset); +		*len = le32_to_cpu(((struct smb2_ioctl_rsp *)hdr)->OutputCount); +		break; +	case SMB2_CHANGE_NOTIFY: +	default: +		/* BB FIXME for unimplemented cases above */ +		cifs_dbg(VFS, "no length check for command\n"); +		break; +	} + +	/* +	 * Invalid length or offset probably means data area is invalid, but +	 * we have little choice but to ignore the data area in this case. +	 */ +	if (*off > 4096) { +		cifs_dbg(VFS, "offset %d too large, data area ignored\n", *off); +		*len = 0; +		*off = 0; +	} else if (*off < 0) { +		cifs_dbg(VFS, "negative offset %d to data invalid ignore data area\n", +			 *off); +		*off = 0; +		*len = 0; +	} else if (*len < 0) { +		cifs_dbg(VFS, "negative data length %d invalid, data area ignored\n", +			 *len); +		*len = 0; +	} else if (*len > 128 * 1024) { +		cifs_dbg(VFS, "data area larger than 128K: %d\n", *len); +		*len = 0; +	} + +	/* return pointer to beginning of data area, ie offset from SMB start */ +	if ((*off != 0) && (*len != 0)) +		return hdr->ProtocolId + *off; +	else +		return NULL; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +unsigned int +smb2_calc_size(void *buf) +{ +	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; +	struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; +	int offset; /* the offset from the beginning of SMB to data area */ +	int data_length; /* the length of the variable length data area */ +	/* Structure Size has already been checked to make sure it is 64 */ +	int len = 4 + le16_to_cpu(pdu->hdr.StructureSize); + +	/* +	 * StructureSize2, ie length of fixed parameter area has already +	 * been checked to make sure it is the correct length. +	 */ +	len += le16_to_cpu(pdu->StructureSize2); + +	if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) +		goto calc_size_exit; + +	smb2_get_data_area_len(&offset, &data_length, hdr); +	cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset); + +	if (data_length > 0) { +		/* +		 * Check to make sure that data area begins after fixed area, +		 * Note that last byte of the fixed area is part of data area +		 * for some commands, typically those with odd StructureSize, +		 * so we must add one to the calculation (and 4 to account for +		 * the size of the RFC1001 hdr. +		 */ +		if (offset + 4 + 1 < len) { +			cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", +				 offset + 4 + 1, len); +			data_length = 0; +		} else { +			len = 4 + offset + data_length; +		} +	} +calc_size_exit: +	cifs_dbg(FYI, "SMB2 len %d\n", len); +	return len; +} + +/* Note: caller must free return buffer */ +__le16 * +cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) +{ +	int len; +	const char *start_of_path; +	__le16 *to; + +	/* Windows doesn't allow paths beginning with \ */ +	if (from[0] == '\\') +		start_of_path = from + 1; +	else +		start_of_path = from; +	to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, +				   cifs_sb->local_nls, +				   cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR); +	return to; +} + +__le32 +smb2_get_lease_state(struct cifsInodeInfo *cinode) +{ +	__le32 lease = 0; + +	if (CIFS_CACHE_WRITE(cinode)) +		lease |= SMB2_LEASE_WRITE_CACHING; +	if (CIFS_CACHE_HANDLE(cinode)) +		lease |= SMB2_LEASE_HANDLE_CACHING; +	if (CIFS_CACHE_READ(cinode)) +		lease |= SMB2_LEASE_READ_CACHING; +	return lease; +} + +struct smb2_lease_break_work { +	struct work_struct lease_break; +	struct tcon_link *tlink; +	__u8 lease_key[16]; +	__le32 lease_state; +}; + +static void +cifs_ses_oplock_break(struct work_struct *work) +{ +	struct smb2_lease_break_work *lw = container_of(work, +				struct smb2_lease_break_work, lease_break); +	int rc; + +	rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, +			      lw->lease_state); +	cifs_dbg(FYI, "Lease release rc %d\n", rc); +	cifs_put_tlink(lw->tlink); +	kfree(lw); +} + +static bool +smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, +		    struct smb2_lease_break_work *lw) +{ +	bool found; +	__u8 lease_state; +	struct list_head *tmp; +	struct cifsFileInfo *cfile; +	struct TCP_Server_Info *server = tcon->ses->server; +	struct cifs_pending_open *open; +	struct cifsInodeInfo *cinode; +	int ack_req = le32_to_cpu(rsp->Flags & +				  SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + +	lease_state = le32_to_cpu(rsp->NewLeaseState); + +	list_for_each(tmp, &tcon->openFileList) { +		cfile = list_entry(tmp, struct cifsFileInfo, tlist); +		cinode = CIFS_I(cfile->dentry->d_inode); + +		if (memcmp(cinode->lease_key, rsp->LeaseKey, +							SMB2_LEASE_KEY_SIZE)) +			continue; + +		cifs_dbg(FYI, "found in the open list\n"); +		cifs_dbg(FYI, "lease key match, lease break 0x%d\n", +			 le32_to_cpu(rsp->NewLeaseState)); + +		server->ops->set_oplock_level(cinode, lease_state, 0, NULL); + +		if (ack_req) +			cfile->oplock_break_cancelled = false; +		else +			cfile->oplock_break_cancelled = true; + +		queue_work(cifsiod_wq, &cfile->oplock_break); +		kfree(lw); +		return true; +	} + +	found = false; +	list_for_each_entry(open, &tcon->pending_opens, olist) { +		if (memcmp(open->lease_key, rsp->LeaseKey, +			   SMB2_LEASE_KEY_SIZE)) +			continue; + +		if (!found && ack_req) { +			found = true; +			memcpy(lw->lease_key, open->lease_key, +			       SMB2_LEASE_KEY_SIZE); +			lw->tlink = cifs_get_tlink(open->tlink); +			queue_work(cifsiod_wq, &lw->lease_break); +		} + +		cifs_dbg(FYI, "found in the pending open list\n"); +		cifs_dbg(FYI, "lease key match, lease break 0x%d\n", +			 le32_to_cpu(rsp->NewLeaseState)); + +		open->oplock = lease_state; +	} +	return found; +} + +static bool +smb2_is_valid_lease_break(char *buffer) +{ +	struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; +	struct list_head *tmp, *tmp1, *tmp2; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon; +	struct smb2_lease_break_work *lw; + +	lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); +	if (!lw) +		return false; + +	INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); +	lw->lease_state = rsp->NewLeaseState; + +	cifs_dbg(FYI, "Checking for lease break\n"); + +	/* look up tcon based on tid & uid */ +	spin_lock(&cifs_tcp_ses_lock); +	list_for_each(tmp, &cifs_tcp_ses_list) { +		server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list); + +		list_for_each(tmp1, &server->smb_ses_list) { +			ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); + +			spin_lock(&cifs_file_list_lock); +			list_for_each(tmp2, &ses->tcon_list) { +				tcon = list_entry(tmp2, struct cifs_tcon, +						  tcon_list); +				cifs_stats_inc( +				    &tcon->stats.cifs_stats.num_oplock_brks); +				if (smb2_tcon_has_lease(tcon, rsp, lw)) { +					spin_unlock(&cifs_file_list_lock); +					spin_unlock(&cifs_tcp_ses_lock); +					return true; +				} +			} +			spin_unlock(&cifs_file_list_lock); +		} +	} +	spin_unlock(&cifs_tcp_ses_lock); +	kfree(lw); +	cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); +	return false; +} + +bool +smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) +{ +	struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; +	struct list_head *tmp, *tmp1, *tmp2; +	struct cifs_ses *ses; +	struct cifs_tcon *tcon; +	struct cifsInodeInfo *cinode; +	struct cifsFileInfo *cfile; + +	cifs_dbg(FYI, "Checking for oplock break\n"); + +	if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) +		return false; + +	if (rsp->StructureSize != +				smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { +		if (le16_to_cpu(rsp->StructureSize) == 44) +			return smb2_is_valid_lease_break(buffer); +		else +			return false; +	} + +	cifs_dbg(FYI, "oplock level 0x%d\n", rsp->OplockLevel); + +	/* look up tcon based on tid & uid */ +	spin_lock(&cifs_tcp_ses_lock); +	list_for_each(tmp, &server->smb_ses_list) { +		ses = list_entry(tmp, struct cifs_ses, smb_ses_list); +		list_for_each(tmp1, &ses->tcon_list) { +			tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); + +			cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); +			spin_lock(&cifs_file_list_lock); +			list_for_each(tmp2, &tcon->openFileList) { +				cfile = list_entry(tmp2, struct cifsFileInfo, +						     tlist); +				if (rsp->PersistentFid != +				    cfile->fid.persistent_fid || +				    rsp->VolatileFid != +				    cfile->fid.volatile_fid) +					continue; + +				cifs_dbg(FYI, "file id match, oplock break\n"); +				cinode = CIFS_I(cfile->dentry->d_inode); + +				if (!CIFS_CACHE_WRITE(cinode) && +				    rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) +					cfile->oplock_break_cancelled = true; +				else +					cfile->oplock_break_cancelled = false; + +				set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, +					&cinode->flags); + +				/* +				 * Set flag if the server downgrades the oplock +				 * to L2 else clear. +				 */ +				if (rsp->OplockLevel) +					set_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &cinode->flags); +				else +					clear_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &cinode->flags); + +				queue_work(cifsiod_wq, &cfile->oplock_break); + +				spin_unlock(&cifs_file_list_lock); +				spin_unlock(&cifs_tcp_ses_lock); +				return true; +			} +			spin_unlock(&cifs_file_list_lock); +			spin_unlock(&cifs_tcp_ses_lock); +			cifs_dbg(FYI, "No matching file for oplock break\n"); +			return true; +		} +	} +	spin_unlock(&cifs_tcp_ses_lock); +	cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); +	return false; +} diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c new file mode 100644 index 00000000000..787844bde38 --- /dev/null +++ b/fs/cifs/smb2ops.c @@ -0,0 +1,1413 @@ +/* + *  SMB2 version specific operations + * + *  Copyright (c) 2012, Jeff Layton <jlayton@redhat.com> + * + *  This library is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License v2 as published + *  by the Free Software Foundation. + * + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *  the GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public License + *  along with this library; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/pagemap.h> +#include <linux/vfs.h> +#include "cifsglob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2status.h" +#include "smb2glob.h" + +static int +change_conf(struct TCP_Server_Info *server) +{ +	server->credits += server->echo_credits + server->oplock_credits; +	server->oplock_credits = server->echo_credits = 0; +	switch (server->credits) { +	case 0: +		return -1; +	case 1: +		server->echoes = false; +		server->oplocks = false; +		cifs_dbg(VFS, "disabling echoes and oplocks\n"); +		break; +	case 2: +		server->echoes = true; +		server->oplocks = false; +		server->echo_credits = 1; +		cifs_dbg(FYI, "disabling oplocks\n"); +		break; +	default: +		server->echoes = true; +		server->oplocks = true; +		server->echo_credits = 1; +		server->oplock_credits = 1; +	} +	server->credits -= server->echo_credits + server->oplock_credits; +	return 0; +} + +static void +smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, +		 const int optype) +{ +	int *val, rc = 0; +	spin_lock(&server->req_lock); +	val = server->ops->get_credits_field(server, optype); +	*val += add; +	server->in_flight--; +	if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP) +		rc = change_conf(server); +	/* +	 * Sometimes server returns 0 credits on oplock break ack - we need to +	 * rebalance credits in this case. +	 */ +	else if (server->in_flight > 0 && server->oplock_credits == 0 && +		 server->oplocks) { +		if (server->credits > 1) { +			server->credits--; +			server->oplock_credits++; +		} +	} +	spin_unlock(&server->req_lock); +	wake_up(&server->request_q); +	if (rc) +		cifs_reconnect(server); +} + +static void +smb2_set_credits(struct TCP_Server_Info *server, const int val) +{ +	spin_lock(&server->req_lock); +	server->credits = val; +	spin_unlock(&server->req_lock); +} + +static int * +smb2_get_credits_field(struct TCP_Server_Info *server, const int optype) +{ +	switch (optype) { +	case CIFS_ECHO_OP: +		return &server->echo_credits; +	case CIFS_OBREAK_OP: +		return &server->oplock_credits; +	default: +		return &server->credits; +	} +} + +static unsigned int +smb2_get_credits(struct mid_q_entry *mid) +{ +	return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest); +} + +static __u64 +smb2_get_next_mid(struct TCP_Server_Info *server) +{ +	__u64 mid; +	/* for SMB2 we need the current value */ +	spin_lock(&GlobalMid_Lock); +	mid = server->CurrentMid++; +	spin_unlock(&GlobalMid_Lock); +	return mid; +} + +static struct mid_q_entry * +smb2_find_mid(struct TCP_Server_Info *server, char *buf) +{ +	struct mid_q_entry *mid; +	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + +	spin_lock(&GlobalMid_Lock); +	list_for_each_entry(mid, &server->pending_mid_q, qhead) { +		if ((mid->mid == hdr->MessageId) && +		    (mid->mid_state == MID_REQUEST_SUBMITTED) && +		    (mid->command == hdr->Command)) { +			spin_unlock(&GlobalMid_Lock); +			return mid; +		} +	} +	spin_unlock(&GlobalMid_Lock); +	return NULL; +} + +static void +smb2_dump_detail(void *buf) +{ +#ifdef CONFIG_CIFS_DEBUG2 +	struct smb2_hdr *smb = (struct smb2_hdr *)buf; + +	cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", +		 smb->Command, smb->Status, smb->Flags, smb->MessageId, +		 smb->ProcessId); +	cifs_dbg(VFS, "smb buf %p len %u\n", smb, smb2_calc_size(smb)); +#endif +} + +static bool +smb2_need_neg(struct TCP_Server_Info *server) +{ +	return server->max_read == 0; +} + +static int +smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) +{ +	int rc; +	ses->server->CurrentMid = 0; +	rc = SMB2_negotiate(xid, ses); +	/* BB we probably don't need to retry with modern servers */ +	if (rc == -EAGAIN) +		rc = -EHOSTDOWN; +	return rc; +} + +static unsigned int +smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +{ +	struct TCP_Server_Info *server = tcon->ses->server; +	unsigned int wsize; + +	/* start with specified wsize, or default */ +	wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE; +	wsize = min_t(unsigned int, wsize, server->max_write); +	/* set it to the maximum buffer size value we can send with 1 credit */ +	wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); + +	return wsize; +} + +static unsigned int +smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +{ +	struct TCP_Server_Info *server = tcon->ses->server; +	unsigned int rsize; + +	/* start with specified rsize, or default */ +	rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE; +	rsize = min_t(unsigned int, rsize, server->max_read); +	/* set it to the maximum buffer size value we can send with 1 credit */ +	rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); + +	return rsize; +} + +#ifdef CONFIG_CIFS_STATS2 +static int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc; +	unsigned int ret_data_len = 0; +	struct network_interface_info_ioctl_rsp *out_buf; + +	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, +			FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, +			NULL /* no data input */, 0 /* no data input */, +			(char **)&out_buf, &ret_data_len); + +	if ((rc == 0)  && (ret_data_len > 0)) { +		/* Dump info on first interface */ +		cifs_dbg(FYI, "Adapter Capability 0x%x\t", +			le32_to_cpu(out_buf->Capability)); +		cifs_dbg(FYI, "Link Speed %lld\n", +			le64_to_cpu(out_buf->LinkSpeed)); +	} else +		cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); + +	return rc; +} +#endif /* STATS2 */ + +static void +smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc; +	__le16 srch_path = 0; /* Null - open root of share */ +	u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); +	if (rc) +		return; + +#ifdef CONFIG_CIFS_STATS2 +	SMB3_request_interfaces(xid, tcon); +#endif /* STATS2 */ + +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_ATTRIBUTE_INFORMATION); +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_DEVICE_INFORMATION); +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ +	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	return; +} + +static void +smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc; +	__le16 srch_path = 0; /* Null - open root of share */ +	u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); +	if (rc) +		return; + +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_ATTRIBUTE_INFORMATION); +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_DEVICE_INFORMATION); +	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	return; +} + +static int +smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, +			struct cifs_sb_info *cifs_sb, const char *full_path) +{ +	int rc; +	__le16 *utf16_path; +	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); +	if (!utf16_path) +		return -ENOMEM; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); +	if (rc) { +		kfree(utf16_path); +		return rc; +	} + +	rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	kfree(utf16_path); +	return rc; +} + +static int +smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, +		  struct cifs_sb_info *cifs_sb, const char *full_path, +		  u64 *uniqueid, FILE_ALL_INFO *data) +{ +	*uniqueid = le64_to_cpu(data->IndexNumber); +	return 0; +} + +static int +smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, +		     struct cifs_fid *fid, FILE_ALL_INFO *data) +{ +	int rc; +	struct smb2_file_all_info *smb2_data; + +	smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, +			    GFP_KERNEL); +	if (smb2_data == NULL) +		return -ENOMEM; + +	rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, +			     smb2_data); +	if (!rc) +		move_smb2_info_to_cifs(data, smb2_data); +	kfree(smb2_data); +	return rc; +} + +static bool +smb2_can_echo(struct TCP_Server_Info *server) +{ +	return server->echoes; +} + +static void +smb2_clear_stats(struct cifs_tcon *tcon) +{ +#ifdef CONFIG_CIFS_STATS +	int i; +	for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { +		atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); +		atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); +	} +#endif +} + +static void +smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) +{ +	seq_puts(m, "\n\tShare Capabilities:"); +	if (tcon->capabilities & SMB2_SHARE_CAP_DFS) +		seq_puts(m, " DFS,"); +	if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) +		seq_puts(m, " CONTINUOUS AVAILABILITY,"); +	if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) +		seq_puts(m, " SCALEOUT,"); +	if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) +		seq_puts(m, " CLUSTER,"); +	if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) +		seq_puts(m, " ASYMMETRIC,"); +	if (tcon->capabilities == 0) +		seq_puts(m, " None"); +	if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) +		seq_puts(m, " Aligned,"); +	if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) +		seq_puts(m, " Partition Aligned,"); +	if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) +		seq_puts(m, " SSD,"); +	if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) +		seq_puts(m, " TRIM-support,"); + +	seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); +	if (tcon->perf_sector_size) +		seq_printf(m, "\tOptimal sector size: 0x%x", +			   tcon->perf_sector_size); +} + +static void +smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ +#ifdef CONFIG_CIFS_STATS +	atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; +	atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; +	seq_printf(m, "\nNegotiates: %d sent %d failed", +		   atomic_read(&sent[SMB2_NEGOTIATE_HE]), +		   atomic_read(&failed[SMB2_NEGOTIATE_HE])); +	seq_printf(m, "\nSessionSetups: %d sent %d failed", +		   atomic_read(&sent[SMB2_SESSION_SETUP_HE]), +		   atomic_read(&failed[SMB2_SESSION_SETUP_HE])); +	seq_printf(m, "\nLogoffs: %d sent %d failed", +		   atomic_read(&sent[SMB2_LOGOFF_HE]), +		   atomic_read(&failed[SMB2_LOGOFF_HE])); +	seq_printf(m, "\nTreeConnects: %d sent %d failed", +		   atomic_read(&sent[SMB2_TREE_CONNECT_HE]), +		   atomic_read(&failed[SMB2_TREE_CONNECT_HE])); +	seq_printf(m, "\nTreeDisconnects: %d sent %d failed", +		   atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), +		   atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); +	seq_printf(m, "\nCreates: %d sent %d failed", +		   atomic_read(&sent[SMB2_CREATE_HE]), +		   atomic_read(&failed[SMB2_CREATE_HE])); +	seq_printf(m, "\nCloses: %d sent %d failed", +		   atomic_read(&sent[SMB2_CLOSE_HE]), +		   atomic_read(&failed[SMB2_CLOSE_HE])); +	seq_printf(m, "\nFlushes: %d sent %d failed", +		   atomic_read(&sent[SMB2_FLUSH_HE]), +		   atomic_read(&failed[SMB2_FLUSH_HE])); +	seq_printf(m, "\nReads: %d sent %d failed", +		   atomic_read(&sent[SMB2_READ_HE]), +		   atomic_read(&failed[SMB2_READ_HE])); +	seq_printf(m, "\nWrites: %d sent %d failed", +		   atomic_read(&sent[SMB2_WRITE_HE]), +		   atomic_read(&failed[SMB2_WRITE_HE])); +	seq_printf(m, "\nLocks: %d sent %d failed", +		   atomic_read(&sent[SMB2_LOCK_HE]), +		   atomic_read(&failed[SMB2_LOCK_HE])); +	seq_printf(m, "\nIOCTLs: %d sent %d failed", +		   atomic_read(&sent[SMB2_IOCTL_HE]), +		   atomic_read(&failed[SMB2_IOCTL_HE])); +	seq_printf(m, "\nCancels: %d sent %d failed", +		   atomic_read(&sent[SMB2_CANCEL_HE]), +		   atomic_read(&failed[SMB2_CANCEL_HE])); +	seq_printf(m, "\nEchos: %d sent %d failed", +		   atomic_read(&sent[SMB2_ECHO_HE]), +		   atomic_read(&failed[SMB2_ECHO_HE])); +	seq_printf(m, "\nQueryDirectories: %d sent %d failed", +		   atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), +		   atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); +	seq_printf(m, "\nChangeNotifies: %d sent %d failed", +		   atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), +		   atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); +	seq_printf(m, "\nQueryInfos: %d sent %d failed", +		   atomic_read(&sent[SMB2_QUERY_INFO_HE]), +		   atomic_read(&failed[SMB2_QUERY_INFO_HE])); +	seq_printf(m, "\nSetInfos: %d sent %d failed", +		   atomic_read(&sent[SMB2_SET_INFO_HE]), +		   atomic_read(&failed[SMB2_SET_INFO_HE])); +	seq_printf(m, "\nOplockBreaks: %d sent %d failed", +		   atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), +		   atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); +#endif +} + +static void +smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) +{ +	struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); +	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + +	cfile->fid.persistent_fid = fid->persistent_fid; +	cfile->fid.volatile_fid = fid->volatile_fid; +	server->ops->set_oplock_level(cinode, oplock, fid->epoch, +				      &fid->purge_cache); +	cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); +} + +static void +smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, +		struct cifs_fid *fid) +{ +	SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +static int +SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, +		     u64 persistent_fid, u64 volatile_fid, +		     struct copychunk_ioctl *pcchunk) +{ +	int rc; +	unsigned int ret_data_len; +	struct resume_key_req *res_key; + +	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, +			FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */, +			NULL, 0 /* no input */, +			(char **)&res_key, &ret_data_len); + +	if (rc) { +		cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); +		goto req_res_key_exit; +	} +	if (ret_data_len < sizeof(struct resume_key_req)) { +		cifs_dbg(VFS, "Invalid refcopy resume key length\n"); +		rc = -EINVAL; +		goto req_res_key_exit; +	} +	memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); + +req_res_key_exit: +	kfree(res_key); +	return rc; +} + +static int +smb2_clone_range(const unsigned int xid, +			struct cifsFileInfo *srcfile, +			struct cifsFileInfo *trgtfile, u64 src_off, +			u64 len, u64 dest_off) +{ +	int rc; +	unsigned int ret_data_len; +	struct copychunk_ioctl *pcchunk; +	struct copychunk_ioctl_rsp *retbuf = NULL; +	struct cifs_tcon *tcon; +	int chunks_copied = 0; +	bool chunk_sizes_updated = false; + +	pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); + +	if (pcchunk == NULL) +		return -ENOMEM; + +	cifs_dbg(FYI, "in smb2_clone_range - about to call request res key\n"); +	/* Request a key from the server to identify the source of the copy */ +	rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), +				srcfile->fid.persistent_fid, +				srcfile->fid.volatile_fid, pcchunk); + +	/* Note: request_res_key sets res_key null only if rc !=0 */ +	if (rc) +		goto cchunk_out; + +	/* For now array only one chunk long, will make more flexible later */ +	pcchunk->ChunkCount = __constant_cpu_to_le32(1); +	pcchunk->Reserved = 0; +	pcchunk->Reserved2 = 0; + +	tcon = tlink_tcon(trgtfile->tlink); + +	while (len > 0) { +		pcchunk->SourceOffset = cpu_to_le64(src_off); +		pcchunk->TargetOffset = cpu_to_le64(dest_off); +		pcchunk->Length = +			cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); + +		/* Request server copy to target from src identified by key */ +		rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, +			trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, +			true /* is_fsctl */, (char *)pcchunk, +			sizeof(struct copychunk_ioctl),	(char **)&retbuf, +			&ret_data_len); +		if (rc == 0) { +			if (ret_data_len != +					sizeof(struct copychunk_ioctl_rsp)) { +				cifs_dbg(VFS, "invalid cchunk response size\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			if (retbuf->TotalBytesWritten == 0) { +				cifs_dbg(FYI, "no bytes copied\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			/* +			 * Check if server claimed to write more than we asked +			 */ +			if (le32_to_cpu(retbuf->TotalBytesWritten) > +			    le32_to_cpu(pcchunk->Length)) { +				cifs_dbg(VFS, "invalid copy chunk response\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			if (le32_to_cpu(retbuf->ChunksWritten) != 1) { +				cifs_dbg(VFS, "invalid num chunks written\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			chunks_copied++; + +			src_off += le32_to_cpu(retbuf->TotalBytesWritten); +			dest_off += le32_to_cpu(retbuf->TotalBytesWritten); +			len -= le32_to_cpu(retbuf->TotalBytesWritten); + +			cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", +				le32_to_cpu(retbuf->ChunksWritten), +				le32_to_cpu(retbuf->ChunkBytesWritten), +				le32_to_cpu(retbuf->TotalBytesWritten)); +		} else if (rc == -EINVAL) { +			if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) +				goto cchunk_out; + +			cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", +				le32_to_cpu(retbuf->ChunksWritten), +				le32_to_cpu(retbuf->ChunkBytesWritten), +				le32_to_cpu(retbuf->TotalBytesWritten)); + +			/* +			 * Check if this is the first request using these sizes, +			 * (ie check if copy succeed once with original sizes +			 * and check if the server gave us different sizes after +			 * we already updated max sizes on previous request). +			 * if not then why is the server returning an error now +			 */ +			if ((chunks_copied != 0) || chunk_sizes_updated) +				goto cchunk_out; + +			/* Check that server is not asking us to grow size */ +			if (le32_to_cpu(retbuf->ChunkBytesWritten) < +					tcon->max_bytes_chunk) +				tcon->max_bytes_chunk = +					le32_to_cpu(retbuf->ChunkBytesWritten); +			else +				goto cchunk_out; /* server gave us bogus size */ + +			/* No need to change MaxChunks since already set to 1 */ +			chunk_sizes_updated = true; +		} +	} + +cchunk_out: +	kfree(pcchunk); +	return rc; +} + +static int +smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, +		struct cifs_fid *fid) +{ +	return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +static unsigned int +smb2_read_data_offset(char *buf) +{ +	struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; +	return rsp->DataOffset; +} + +static unsigned int +smb2_read_data_length(char *buf) +{ +	struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; +	return le32_to_cpu(rsp->DataLength); +} + + +static int +smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile, +	       struct cifs_io_parms *parms, unsigned int *bytes_read, +	       char **buf, int *buf_type) +{ +	parms->persistent_fid = cfile->fid.persistent_fid; +	parms->volatile_fid = cfile->fid.volatile_fid; +	return SMB2_read(xid, parms, bytes_read, buf, buf_type); +} + +static int +smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile, +		struct cifs_io_parms *parms, unsigned int *written, +		struct kvec *iov, unsigned long nr_segs) +{ + +	parms->persistent_fid = cfile->fid.persistent_fid; +	parms->volatile_fid = cfile->fid.volatile_fid; +	return SMB2_write(xid, parms, written, iov, nr_segs); +} + +static int +smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifsFileInfo *cfile, __u64 size, bool set_alloc) +{ +	__le64 eof = cpu_to_le64(size); +	return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, +			    cfile->fid.volatile_fid, cfile->pid, &eof); +} + +static int +smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifsFileInfo *cfile) +{ +	return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, +			    cfile->fid.volatile_fid); +} + +static int +smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, +		     const char *path, struct cifs_sb_info *cifs_sb, +		     struct cifs_fid *fid, __u16 search_flags, +		     struct cifs_search_info *srch_inf) +{ +	__le16 *utf16_path; +	int rc; +	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; + +	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); +	if (!utf16_path) +		return -ENOMEM; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); +	kfree(utf16_path); +	if (rc) { +		cifs_dbg(VFS, "open dir failed\n"); +		return rc; +	} + +	srch_inf->entries_in_buffer = 0; +	srch_inf->index_of_last_entry = 0; + +	rc = SMB2_query_directory(xid, tcon, fid->persistent_fid, +				  fid->volatile_fid, 0, srch_inf); +	if (rc) { +		cifs_dbg(VFS, "query directory failed\n"); +		SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +	} +	return rc; +} + +static int +smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, +		    struct cifs_fid *fid, __u16 search_flags, +		    struct cifs_search_info *srch_inf) +{ +	return SMB2_query_directory(xid, tcon, fid->persistent_fid, +				    fid->volatile_fid, 0, srch_inf); +} + +static int +smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, +	       struct cifs_fid *fid) +{ +	return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +/* +* If we negotiate SMB2 protocol and get STATUS_PENDING - update +* the number of credits and return true. Otherwise - return false. +*/ +static bool +smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length) +{ +	struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + +	if (hdr->Status != STATUS_PENDING) +		return false; + +	if (!length) { +		spin_lock(&server->req_lock); +		server->credits += le16_to_cpu(hdr->CreditRequest); +		spin_unlock(&server->req_lock); +		wake_up(&server->request_q); +	} + +	return true; +} + +static int +smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, +		     struct cifsInodeInfo *cinode) +{ +	if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) +		return SMB2_lease_break(0, tcon, cinode->lease_key, +					smb2_get_lease_state(cinode)); + +	return SMB2_oplock_break(0, tcon, fid->persistent_fid, +				 fid->volatile_fid, +				 CIFS_CACHE_READ(cinode) ? 1 : 0); +} + +static int +smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, +	     struct kstatfs *buf) +{ +	int rc; +	__le16 srch_path = 0; /* Null - open root of share */ +	u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); +	if (rc) +		return rc; +	buf->f_type = SMB2_MAGIC_NUMBER; +	rc = SMB2_QFS_info(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			   buf); +	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	return rc; +} + +static bool +smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ +	return ob1->fid.persistent_fid == ob2->fid.persistent_fid && +	       ob1->fid.volatile_fid == ob2->fid.volatile_fid; +} + +static int +smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, +	       __u64 length, __u32 type, int lock, int unlock, bool wait) +{ +	if (unlock && !lock) +		type = SMB2_LOCKFLAG_UNLOCK; +	return SMB2_lock(xid, tlink_tcon(cfile->tlink), +			 cfile->fid.persistent_fid, cfile->fid.volatile_fid, +			 current->tgid, length, offset, type, wait); +} + +static void +smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) +{ +	memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static void +smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) +{ +	memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static void +smb2_new_lease_key(struct cifs_fid *fid) +{ +	get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static int +smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, +		   const char *full_path, char **target_path, +		   struct cifs_sb_info *cifs_sb) +{ +	int rc; +	__le16 *utf16_path; +	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; +	struct smb2_err_rsp *err_buf = NULL; +	struct smb2_symlink_err_rsp *symlink; +	unsigned int sub_len, sub_offset; + +	cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + +	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); +	if (!utf16_path) +		return -ENOMEM; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_buf); + +	if (!rc || !err_buf) { +		kfree(utf16_path); +		return -ENOENT; +	} +	/* open must fail on symlink - reset rc */ +	rc = 0; +	symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData; +	sub_len = le16_to_cpu(symlink->SubstituteNameLength); +	sub_offset = le16_to_cpu(symlink->SubstituteNameOffset); +	*target_path = cifs_strndup_from_utf16( +				(char *)symlink->PathBuffer + sub_offset, +				sub_len, true, cifs_sb->local_nls); +	if (!(*target_path)) { +		kfree(utf16_path); +		return -ENOMEM; +	} +	convert_delimiter(*target_path, '/'); +	cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); +	kfree(utf16_path); +	return rc; +} + +static void +smb2_downgrade_oplock(struct TCP_Server_Info *server, +			struct cifsInodeInfo *cinode, bool set_level2) +{ +	if (set_level2) +		server->ops->set_oplock_level(cinode, SMB2_OPLOCK_LEVEL_II, +						0, NULL); +	else +		server->ops->set_oplock_level(cinode, 0, 0, NULL); +} + +static void +smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, +		      unsigned int epoch, bool *purge_cache) +{ +	oplock &= 0xFF; +	if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) +		return; +	if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { +		cinode->oplock = CIFS_CACHE_RHW_FLG; +		cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", +			 &cinode->vfs_inode); +	} else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { +		cinode->oplock = CIFS_CACHE_RW_FLG; +		cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", +			 &cinode->vfs_inode); +	} else if (oplock == SMB2_OPLOCK_LEVEL_II) { +		cinode->oplock = CIFS_CACHE_READ_FLG; +		cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", +			 &cinode->vfs_inode); +	} else +		cinode->oplock = 0; +} + +static void +smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, +		       unsigned int epoch, bool *purge_cache) +{ +	char message[5] = {0}; + +	oplock &= 0xFF; +	if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) +		return; + +	cinode->oplock = 0; +	if (oplock & SMB2_LEASE_READ_CACHING_HE) { +		cinode->oplock |= CIFS_CACHE_READ_FLG; +		strcat(message, "R"); +	} +	if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { +		cinode->oplock |= CIFS_CACHE_HANDLE_FLG; +		strcat(message, "H"); +	} +	if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { +		cinode->oplock |= CIFS_CACHE_WRITE_FLG; +		strcat(message, "W"); +	} +	if (!cinode->oplock) +		strcat(message, "None"); +	cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, +		 &cinode->vfs_inode); +} + +static void +smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, +		      unsigned int epoch, bool *purge_cache) +{ +	unsigned int old_oplock = cinode->oplock; + +	smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); + +	if (purge_cache) { +		*purge_cache = false; +		if (old_oplock == CIFS_CACHE_READ_FLG) { +			if (cinode->oplock == CIFS_CACHE_READ_FLG && +			    (epoch - cinode->epoch > 0)) +				*purge_cache = true; +			else if (cinode->oplock == CIFS_CACHE_RH_FLG && +				 (epoch - cinode->epoch > 1)) +				*purge_cache = true; +			else if (cinode->oplock == CIFS_CACHE_RHW_FLG && +				 (epoch - cinode->epoch > 1)) +				*purge_cache = true; +			else if (cinode->oplock == 0 && +				 (epoch - cinode->epoch > 0)) +				*purge_cache = true; +		} else if (old_oplock == CIFS_CACHE_RH_FLG) { +			if (cinode->oplock == CIFS_CACHE_RH_FLG && +			    (epoch - cinode->epoch > 0)) +				*purge_cache = true; +			else if (cinode->oplock == CIFS_CACHE_RHW_FLG && +				 (epoch - cinode->epoch > 1)) +				*purge_cache = true; +		} +		cinode->epoch = epoch; +	} +} + +static bool +smb2_is_read_op(__u32 oplock) +{ +	return oplock == SMB2_OPLOCK_LEVEL_II; +} + +static bool +smb21_is_read_op(__u32 oplock) +{ +	return (oplock & SMB2_LEASE_READ_CACHING_HE) && +	       !(oplock & SMB2_LEASE_WRITE_CACHING_HE); +} + +static __le32 +map_oplock_to_lease(u8 oplock) +{ +	if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) +		return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING; +	else if (oplock == SMB2_OPLOCK_LEVEL_II) +		return SMB2_LEASE_READ_CACHING; +	else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) +		return SMB2_LEASE_HANDLE_CACHING | SMB2_LEASE_READ_CACHING | +		       SMB2_LEASE_WRITE_CACHING; +	return 0; +} + +static char * +smb2_create_lease_buf(u8 *lease_key, u8 oplock) +{ +	struct create_lease *buf; + +	buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); +	if (!buf) +		return NULL; + +	buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key)); +	buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8))); +	buf->lcontext.LeaseState = map_oplock_to_lease(oplock); + +	buf->ccontext.DataOffset = cpu_to_le16(offsetof +					(struct create_lease, lcontext)); +	buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); +	buf->ccontext.NameOffset = cpu_to_le16(offsetof +				(struct create_lease, Name)); +	buf->ccontext.NameLength = cpu_to_le16(4); +	/* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ +	buf->Name[0] = 'R'; +	buf->Name[1] = 'q'; +	buf->Name[2] = 'L'; +	buf->Name[3] = 's'; +	return (char *)buf; +} + +static char * +smb3_create_lease_buf(u8 *lease_key, u8 oplock) +{ +	struct create_lease_v2 *buf; + +	buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); +	if (!buf) +		return NULL; + +	buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key)); +	buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8))); +	buf->lcontext.LeaseState = map_oplock_to_lease(oplock); + +	buf->ccontext.DataOffset = cpu_to_le16(offsetof +					(struct create_lease_v2, lcontext)); +	buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); +	buf->ccontext.NameOffset = cpu_to_le16(offsetof +				(struct create_lease_v2, Name)); +	buf->ccontext.NameLength = cpu_to_le16(4); +	/* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ +	buf->Name[0] = 'R'; +	buf->Name[1] = 'q'; +	buf->Name[2] = 'L'; +	buf->Name[3] = 's'; +	return (char *)buf; +} + +static __u8 +smb2_parse_lease_buf(void *buf, unsigned int *epoch) +{ +	struct create_lease *lc = (struct create_lease *)buf; + +	*epoch = 0; /* not used */ +	if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) +		return SMB2_OPLOCK_LEVEL_NOCHANGE; +	return le32_to_cpu(lc->lcontext.LeaseState); +} + +static __u8 +smb3_parse_lease_buf(void *buf, unsigned int *epoch) +{ +	struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; + +	*epoch = le16_to_cpu(lc->lcontext.Epoch); +	if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) +		return SMB2_OPLOCK_LEVEL_NOCHANGE; +	return le32_to_cpu(lc->lcontext.LeaseState); +} + +struct smb_version_operations smb20_operations = { +	.compare_fids = smb2_compare_fids, +	.setup_request = smb2_setup_request, +	.setup_async_request = smb2_setup_async_request, +	.check_receive = smb2_check_receive, +	.add_credits = smb2_add_credits, +	.set_credits = smb2_set_credits, +	.get_credits_field = smb2_get_credits_field, +	.get_credits = smb2_get_credits, +	.get_next_mid = smb2_get_next_mid, +	.read_data_offset = smb2_read_data_offset, +	.read_data_length = smb2_read_data_length, +	.map_error = map_smb2_to_linux_error, +	.find_mid = smb2_find_mid, +	.check_message = smb2_check_message, +	.dump_detail = smb2_dump_detail, +	.clear_stats = smb2_clear_stats, +	.print_stats = smb2_print_stats, +	.is_oplock_break = smb2_is_valid_oplock_break, +	.downgrade_oplock = smb2_downgrade_oplock, +	.need_neg = smb2_need_neg, +	.negotiate = smb2_negotiate, +	.negotiate_wsize = smb2_negotiate_wsize, +	.negotiate_rsize = smb2_negotiate_rsize, +	.sess_setup = SMB2_sess_setup, +	.logoff = SMB2_logoff, +	.tree_connect = SMB2_tcon, +	.tree_disconnect = SMB2_tdis, +	.qfs_tcon = smb2_qfs_tcon, +	.is_path_accessible = smb2_is_path_accessible, +	.can_echo = smb2_can_echo, +	.echo = SMB2_echo, +	.query_path_info = smb2_query_path_info, +	.get_srv_inum = smb2_get_srv_inum, +	.query_file_info = smb2_query_file_info, +	.set_path_size = smb2_set_path_size, +	.set_file_size = smb2_set_file_size, +	.set_file_info = smb2_set_file_info, +	.set_compression = smb2_set_compression, +	.mkdir = smb2_mkdir, +	.mkdir_setinfo = smb2_mkdir_setinfo, +	.rmdir = smb2_rmdir, +	.unlink = smb2_unlink, +	.rename = smb2_rename_path, +	.create_hardlink = smb2_create_hardlink, +	.query_symlink = smb2_query_symlink, +	.open = smb2_open_file, +	.set_fid = smb2_set_fid, +	.close = smb2_close_file, +	.flush = smb2_flush_file, +	.async_readv = smb2_async_readv, +	.async_writev = smb2_async_writev, +	.sync_read = smb2_sync_read, +	.sync_write = smb2_sync_write, +	.query_dir_first = smb2_query_dir_first, +	.query_dir_next = smb2_query_dir_next, +	.close_dir = smb2_close_dir, +	.calc_smb_size = smb2_calc_size, +	.is_status_pending = smb2_is_status_pending, +	.oplock_response = smb2_oplock_response, +	.queryfs = smb2_queryfs, +	.mand_lock = smb2_mand_lock, +	.mand_unlock_range = smb2_unlock_range, +	.push_mand_locks = smb2_push_mandatory_locks, +	.get_lease_key = smb2_get_lease_key, +	.set_lease_key = smb2_set_lease_key, +	.new_lease_key = smb2_new_lease_key, +	.calc_signature = smb2_calc_signature, +	.is_read_op = smb2_is_read_op, +	.set_oplock_level = smb2_set_oplock_level, +	.create_lease_buf = smb2_create_lease_buf, +	.parse_lease_buf = smb2_parse_lease_buf, +	.clone_range = smb2_clone_range, +}; + +struct smb_version_operations smb21_operations = { +	.compare_fids = smb2_compare_fids, +	.setup_request = smb2_setup_request, +	.setup_async_request = smb2_setup_async_request, +	.check_receive = smb2_check_receive, +	.add_credits = smb2_add_credits, +	.set_credits = smb2_set_credits, +	.get_credits_field = smb2_get_credits_field, +	.get_credits = smb2_get_credits, +	.get_next_mid = smb2_get_next_mid, +	.read_data_offset = smb2_read_data_offset, +	.read_data_length = smb2_read_data_length, +	.map_error = map_smb2_to_linux_error, +	.find_mid = smb2_find_mid, +	.check_message = smb2_check_message, +	.dump_detail = smb2_dump_detail, +	.clear_stats = smb2_clear_stats, +	.print_stats = smb2_print_stats, +	.is_oplock_break = smb2_is_valid_oplock_break, +	.downgrade_oplock = smb2_downgrade_oplock, +	.need_neg = smb2_need_neg, +	.negotiate = smb2_negotiate, +	.negotiate_wsize = smb2_negotiate_wsize, +	.negotiate_rsize = smb2_negotiate_rsize, +	.sess_setup = SMB2_sess_setup, +	.logoff = SMB2_logoff, +	.tree_connect = SMB2_tcon, +	.tree_disconnect = SMB2_tdis, +	.qfs_tcon = smb2_qfs_tcon, +	.is_path_accessible = smb2_is_path_accessible, +	.can_echo = smb2_can_echo, +	.echo = SMB2_echo, +	.query_path_info = smb2_query_path_info, +	.get_srv_inum = smb2_get_srv_inum, +	.query_file_info = smb2_query_file_info, +	.set_path_size = smb2_set_path_size, +	.set_file_size = smb2_set_file_size, +	.set_file_info = smb2_set_file_info, +	.set_compression = smb2_set_compression, +	.mkdir = smb2_mkdir, +	.mkdir_setinfo = smb2_mkdir_setinfo, +	.rmdir = smb2_rmdir, +	.unlink = smb2_unlink, +	.rename = smb2_rename_path, +	.create_hardlink = smb2_create_hardlink, +	.query_symlink = smb2_query_symlink, +	.open = smb2_open_file, +	.set_fid = smb2_set_fid, +	.close = smb2_close_file, +	.flush = smb2_flush_file, +	.async_readv = smb2_async_readv, +	.async_writev = smb2_async_writev, +	.sync_read = smb2_sync_read, +	.sync_write = smb2_sync_write, +	.query_dir_first = smb2_query_dir_first, +	.query_dir_next = smb2_query_dir_next, +	.close_dir = smb2_close_dir, +	.calc_smb_size = smb2_calc_size, +	.is_status_pending = smb2_is_status_pending, +	.oplock_response = smb2_oplock_response, +	.queryfs = smb2_queryfs, +	.mand_lock = smb2_mand_lock, +	.mand_unlock_range = smb2_unlock_range, +	.push_mand_locks = smb2_push_mandatory_locks, +	.get_lease_key = smb2_get_lease_key, +	.set_lease_key = smb2_set_lease_key, +	.new_lease_key = smb2_new_lease_key, +	.calc_signature = smb2_calc_signature, +	.is_read_op = smb21_is_read_op, +	.set_oplock_level = smb21_set_oplock_level, +	.create_lease_buf = smb2_create_lease_buf, +	.parse_lease_buf = smb2_parse_lease_buf, +	.clone_range = smb2_clone_range, +}; + +struct smb_version_operations smb30_operations = { +	.compare_fids = smb2_compare_fids, +	.setup_request = smb2_setup_request, +	.setup_async_request = smb2_setup_async_request, +	.check_receive = smb2_check_receive, +	.add_credits = smb2_add_credits, +	.set_credits = smb2_set_credits, +	.get_credits_field = smb2_get_credits_field, +	.get_credits = smb2_get_credits, +	.get_next_mid = smb2_get_next_mid, +	.read_data_offset = smb2_read_data_offset, +	.read_data_length = smb2_read_data_length, +	.map_error = map_smb2_to_linux_error, +	.find_mid = smb2_find_mid, +	.check_message = smb2_check_message, +	.dump_detail = smb2_dump_detail, +	.clear_stats = smb2_clear_stats, +	.print_stats = smb2_print_stats, +	.dump_share_caps = smb2_dump_share_caps, +	.is_oplock_break = smb2_is_valid_oplock_break, +	.downgrade_oplock = smb2_downgrade_oplock, +	.need_neg = smb2_need_neg, +	.negotiate = smb2_negotiate, +	.negotiate_wsize = smb2_negotiate_wsize, +	.negotiate_rsize = smb2_negotiate_rsize, +	.sess_setup = SMB2_sess_setup, +	.logoff = SMB2_logoff, +	.tree_connect = SMB2_tcon, +	.tree_disconnect = SMB2_tdis, +	.qfs_tcon = smb3_qfs_tcon, +	.is_path_accessible = smb2_is_path_accessible, +	.can_echo = smb2_can_echo, +	.echo = SMB2_echo, +	.query_path_info = smb2_query_path_info, +	.get_srv_inum = smb2_get_srv_inum, +	.query_file_info = smb2_query_file_info, +	.set_path_size = smb2_set_path_size, +	.set_file_size = smb2_set_file_size, +	.set_file_info = smb2_set_file_info, +	.set_compression = smb2_set_compression, +	.mkdir = smb2_mkdir, +	.mkdir_setinfo = smb2_mkdir_setinfo, +	.rmdir = smb2_rmdir, +	.unlink = smb2_unlink, +	.rename = smb2_rename_path, +	.create_hardlink = smb2_create_hardlink, +	.query_symlink = smb2_query_symlink, +	.open = smb2_open_file, +	.set_fid = smb2_set_fid, +	.close = smb2_close_file, +	.flush = smb2_flush_file, +	.async_readv = smb2_async_readv, +	.async_writev = smb2_async_writev, +	.sync_read = smb2_sync_read, +	.sync_write = smb2_sync_write, +	.query_dir_first = smb2_query_dir_first, +	.query_dir_next = smb2_query_dir_next, +	.close_dir = smb2_close_dir, +	.calc_smb_size = smb2_calc_size, +	.is_status_pending = smb2_is_status_pending, +	.oplock_response = smb2_oplock_response, +	.queryfs = smb2_queryfs, +	.mand_lock = smb2_mand_lock, +	.mand_unlock_range = smb2_unlock_range, +	.push_mand_locks = smb2_push_mandatory_locks, +	.get_lease_key = smb2_get_lease_key, +	.set_lease_key = smb2_set_lease_key, +	.new_lease_key = smb2_new_lease_key, +	.generate_signingkey = generate_smb3signingkey, +	.calc_signature = smb3_calc_signature, +	.is_read_op = smb21_is_read_op, +	.set_oplock_level = smb3_set_oplock_level, +	.create_lease_buf = smb3_create_lease_buf, +	.parse_lease_buf = smb3_parse_lease_buf, +	.clone_range = smb2_clone_range, +	.validate_negotiate = smb3_validate_negotiate, +}; + +struct smb_version_values smb20_values = { +	.version_string = SMB20_VERSION_STRING, +	.protocol_id = SMB20_PROT_ID, +	.req_capabilities = 0, /* MBZ */ +	.large_lock_type = 0, +	.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, +	.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, +	.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +	.header_size = sizeof(struct smb2_hdr), +	.max_header_size = MAX_SMB2_HDR_SIZE, +	.read_rsp_size = sizeof(struct smb2_read_rsp) - 1, +	.lock_cmd = SMB2_LOCK, +	.cap_unix = 0, +	.cap_nt_find = SMB2_NT_FIND, +	.cap_large_files = SMB2_LARGE_FILES, +	.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.create_lease_size = sizeof(struct create_lease), +}; + +struct smb_version_values smb21_values = { +	.version_string = SMB21_VERSION_STRING, +	.protocol_id = SMB21_PROT_ID, +	.req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ +	.large_lock_type = 0, +	.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, +	.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, +	.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +	.header_size = sizeof(struct smb2_hdr), +	.max_header_size = MAX_SMB2_HDR_SIZE, +	.read_rsp_size = sizeof(struct smb2_read_rsp) - 1, +	.lock_cmd = SMB2_LOCK, +	.cap_unix = 0, +	.cap_nt_find = SMB2_NT_FIND, +	.cap_large_files = SMB2_LARGE_FILES, +	.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.create_lease_size = sizeof(struct create_lease), +}; + +struct smb_version_values smb30_values = { +	.version_string = SMB30_VERSION_STRING, +	.protocol_id = SMB30_PROT_ID, +	.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU, +	.large_lock_type = 0, +	.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, +	.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, +	.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +	.header_size = sizeof(struct smb2_hdr), +	.max_header_size = MAX_SMB2_HDR_SIZE, +	.read_rsp_size = sizeof(struct smb2_read_rsp) - 1, +	.lock_cmd = SMB2_LOCK, +	.cap_unix = 0, +	.cap_nt_find = SMB2_NT_FIND, +	.cap_large_files = SMB2_LARGE_FILES, +	.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb302_values = { +	.version_string = SMB302_VERSION_STRING, +	.protocol_id = SMB302_PROT_ID, +	.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU, +	.large_lock_type = 0, +	.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, +	.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, +	.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +	.header_size = sizeof(struct smb2_hdr), +	.max_header_size = MAX_SMB2_HDR_SIZE, +	.read_rsp_size = sizeof(struct smb2_read_rsp) - 1, +	.lock_cmd = SMB2_LOCK, +	.cap_unix = 0, +	.cap_nt_find = SMB2_NT_FIND, +	.cap_large_files = SMB2_LARGE_FILES, +	.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, +	.create_lease_size = sizeof(struct create_lease_v2), +}; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c new file mode 100644 index 00000000000..b0b260dbb19 --- /dev/null +++ b/fs/cifs/smb2pdu.c @@ -0,0 +1,2616 @@ +/* + *   fs/cifs/smb2pdu.c + * + *   Copyright (C) International Business Machines  Corp., 2009, 2013 + *                 Etersoft, 2012 + *   Author(s): Steve French (sfrench@us.ibm.com) + *              Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + *   Contains the routines for constructing the SMB2 PDUs themselves + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ + /* Note that there are handle based routines which must be		      */ + /* treated slightly differently for reconnection purposes since we never     */ + /* want to reuse a stale file handle and only the caller knows the file info */ + +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/vfs.h> +#include <linux/task_io_accounting_ops.h> +#include <linux/uaccess.h> +#include <linux/pagemap.h> +#include <linux/xattr.h> +#include "smb2pdu.h" +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "ntlmssp.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "cifspdu.h" + +/* + *  The following table defines the expected "StructureSize" of SMB2 requests + *  in order by SMB2 command.  This is similar to "wct" in SMB/CIFS requests. + * + *  Note that commands are defined in smb2pdu.h in le16 but the array below is + *  indexed by command in host byte order. + */ +static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { +	/* SMB2_NEGOTIATE */ 36, +	/* SMB2_SESSION_SETUP */ 25, +	/* SMB2_LOGOFF */ 4, +	/* SMB2_TREE_CONNECT */	9, +	/* SMB2_TREE_DISCONNECT */ 4, +	/* SMB2_CREATE */ 57, +	/* SMB2_CLOSE */ 24, +	/* SMB2_FLUSH */ 24, +	/* SMB2_READ */	49, +	/* SMB2_WRITE */ 49, +	/* SMB2_LOCK */	48, +	/* SMB2_IOCTL */ 57, +	/* SMB2_CANCEL */ 4, +	/* SMB2_ECHO */ 4, +	/* SMB2_QUERY_DIRECTORY */ 33, +	/* SMB2_CHANGE_NOTIFY */ 32, +	/* SMB2_QUERY_INFO */ 41, +	/* SMB2_SET_INFO */ 33, +	/* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ +}; + + +static void +smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , +		  const struct cifs_tcon *tcon) +{ +	struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; +	char *temp = (char *)hdr; +	/* lookup word count ie StructureSize from table */ +	__u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_cmd)]; + +	/* +	 * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of +	 * largest operations (Create) +	 */ +	memset(temp, 0, 256); + +	/* Note this is only network field converted to big endian */ +	hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr) +			- 4 /*  RFC 1001 length field itself not counted */); + +	hdr->ProtocolId[0] = 0xFE; +	hdr->ProtocolId[1] = 'S'; +	hdr->ProtocolId[2] = 'M'; +	hdr->ProtocolId[3] = 'B'; +	hdr->StructureSize = cpu_to_le16(64); +	hdr->Command = smb2_cmd; +	hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */ +	hdr->ProcessId = cpu_to_le32((__u16)current->tgid); + +	if (!tcon) +		goto out; + +	/* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */ +	/* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ +	/* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ +	if ((tcon->ses) && +	    (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) +		hdr->CreditCharge = cpu_to_le16(1); +	/* else CreditCharge MBZ */ + +	hdr->TreeId = tcon->tid; +	/* Uid is not converted */ +	if (tcon->ses) +		hdr->SessionId = tcon->ses->Suid; + +	/* +	 * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have +	 * to pass the path on the Open SMB prefixed by \\server\share. +	 * Not sure when we would need to do the augmented path (if ever) and +	 * setting this flag breaks the SMB2 open operation since it is +	 * illegal to send an empty path name (without \\server\share prefix) +	 * when the DFS flag is set in the SMB open header. We could +	 * consider setting the flag on all operations other than open +	 * but it is safer to net set it for now. +	 */ +/*	if (tcon->share_flags & SHI1005_FLAGS_DFS) +		hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + +	if (tcon->ses && tcon->ses->server && tcon->ses->server->sign) +		hdr->Flags |= SMB2_FLAGS_SIGNED; +out: +	pdu->StructureSize2 = cpu_to_le16(parmsize); +	return; +} + +static int +smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) +{ +	int rc = 0; +	struct nls_table *nls_codepage; +	struct cifs_ses *ses; +	struct TCP_Server_Info *server; + +	/* +	 * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so +	 * check for tcp and smb session status done differently +	 * for those three - in the calling routine. +	 */ +	if (tcon == NULL) +		return rc; + +	if (smb2_command == SMB2_TREE_CONNECT) +		return rc; + +	if (tcon->tidStatus == CifsExiting) { +		/* +		 * only tree disconnect, open, and write, +		 * (and ulogoff which does not have tcon) +		 * are allowed as we start force umount. +		 */ +		if ((smb2_command != SMB2_WRITE) && +		   (smb2_command != SMB2_CREATE) && +		   (smb2_command != SMB2_TREE_DISCONNECT)) { +			cifs_dbg(FYI, "can not send cmd %d while umounting\n", +				 smb2_command); +			return -ENODEV; +		} +	} +	if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || +	    (!tcon->ses->server)) +		return -EIO; + +	ses = tcon->ses; +	server = ses->server; + +	/* +	 * Give demultiplex thread up to 10 seconds to reconnect, should be +	 * greater than cifs socket timeout which is 7 seconds +	 */ +	while (server->tcpStatus == CifsNeedReconnect) { +		/* +		 * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE +		 * here since they are implicitly done when session drops. +		 */ +		switch (smb2_command) { +		/* +		 * BB Should we keep oplock break and add flush to exceptions? +		 */ +		case SMB2_TREE_DISCONNECT: +		case SMB2_CANCEL: +		case SMB2_CLOSE: +		case SMB2_OPLOCK_BREAK: +			return -EAGAIN; +		} + +		wait_event_interruptible_timeout(server->response_q, +			(server->tcpStatus != CifsNeedReconnect), 10 * HZ); + +		/* are we still trying to reconnect? */ +		if (server->tcpStatus != CifsNeedReconnect) +			break; + +		/* +		 * on "soft" mounts we wait once. Hard mounts keep +		 * retrying until process is killed or server comes +		 * back on-line +		 */ +		if (!tcon->retry) { +			cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); +			return -EHOSTDOWN; +		} +	} + +	if (!tcon->ses->need_reconnect && !tcon->need_reconnect) +		return rc; + +	nls_codepage = load_nls_default(); + +	/* +	 * need to prevent multiple threads trying to simultaneously reconnect +	 * the same SMB session +	 */ +	mutex_lock(&tcon->ses->session_mutex); +	rc = cifs_negotiate_protocol(0, tcon->ses); +	if (!rc && tcon->ses->need_reconnect) +		rc = cifs_setup_session(0, tcon->ses, nls_codepage); + +	if (rc || !tcon->need_reconnect) { +		mutex_unlock(&tcon->ses->session_mutex); +		goto out; +	} + +	cifs_mark_open_files_invalid(tcon); +	rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); +	mutex_unlock(&tcon->ses->session_mutex); +	cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); +	if (rc) +		goto out; +	atomic_inc(&tconInfoReconnectCount); +	/* +	 * BB FIXME add code to check if wsize needs update due to negotiated +	 * smb buffer size shrinking. +	 */ +out: +	/* +	 * Check if handle based operation so we know whether we can continue +	 * or not without returning to caller to reset file handle. +	 */ +	/* +	 * BB Is flush done by server on drop of tcp session? Should we special +	 * case it and skip above? +	 */ +	switch (smb2_command) { +	case SMB2_FLUSH: +	case SMB2_READ: +	case SMB2_WRITE: +	case SMB2_LOCK: +	case SMB2_IOCTL: +	case SMB2_QUERY_DIRECTORY: +	case SMB2_CHANGE_NOTIFY: +	case SMB2_QUERY_INFO: +	case SMB2_SET_INFO: +		return -EAGAIN; +	} +	unload_nls(nls_codepage); +	return rc; +} + +/* + * Allocate and return pointer to an SMB request hdr, and set basic + * SMB information in the SMB header. If the return code is zero, this + * function must have filled in request_buf pointer. + */ +static int +small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon, +		void **request_buf) +{ +	int rc = 0; + +	rc = smb2_reconnect(smb2_command, tcon); +	if (rc) +		return rc; + +	/* BB eventually switch this to SMB2 specific small buf size */ +	*request_buf = cifs_small_buf_get(); +	if (*request_buf == NULL) { +		/* BB should we add a retry in here if not a writepage? */ +		return -ENOMEM; +	} + +	smb2_hdr_assemble((struct smb2_hdr *) *request_buf, smb2_command, tcon); + +	if (tcon != NULL) { +#ifdef CONFIG_CIFS_STATS2 +		uint16_t com_code = le16_to_cpu(smb2_command); +		cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); +#endif +		cifs_stats_inc(&tcon->num_smbs_sent); +	} + +	return rc; +} + +static void +free_rsp_buf(int resp_buftype, void *rsp) +{ +	if (resp_buftype == CIFS_SMALL_BUFFER) +		cifs_small_buf_release(rsp); +	else if (resp_buftype == CIFS_LARGE_BUFFER) +		cifs_buf_release(rsp); +} + + +/* + * + *	SMB2 Worker functions follow: + * + *	The general structure of the worker functions is: + *	1) Call smb2_init (assembles SMB2 header) + *	2) Initialize SMB2 command specific fields in fixed length area of SMB + *	3) Call smb_sendrcv2 (sends request on socket and waits for response) + *	4) Decode SMB2 command specific fields in the fixed length area + *	5) Decode variable length data area (if any for this SMB2 command type) + *	6) Call free smb buffer + *	7) return + * + */ + +int +SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) +{ +	struct smb2_negotiate_req *req; +	struct smb2_negotiate_rsp *rsp; +	struct kvec iov[1]; +	int rc = 0; +	int resp_buftype; +	struct TCP_Server_Info *server = ses->server; +	int blob_offset, blob_length; +	char *security_blob; +	int flags = CIFS_NEG_OP; + +	cifs_dbg(FYI, "Negotiate protocol\n"); + +	if (!server) { +		WARN(1, "%s: server is NULL!\n", __func__); +		return -EIO; +	} + +	rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req); +	if (rc) +		return rc; + +	req->hdr.SessionId = 0; + +	req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id); + +	req->DialectCount = cpu_to_le16(1); /* One vers= at a time for now */ +	inc_rfc1001_len(req, 2); + +	/* only one of SMB2 signing flags may be set in SMB2 request */ +	if (ses->sign) +		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); +	else if (global_secflags & CIFSSEC_MAY_SIGN) +		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); +	else +		req->SecurityMode = 0; + +	req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); + +	/* ClientGUID must be zero for SMB2.02 dialect */ +	if (ses->server->vals->protocol_id == SMB20_PROT_ID) +		memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); +	else +		memcpy(req->ClientGUID, server->client_guid, +			SMB2_CLIENT_GUID_SIZE); + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; + +	rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags); + +	rsp = (struct smb2_negotiate_rsp *)iov[0].iov_base; +	/* +	 * No tcon so can't do +	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); +	 */ +	if (rc != 0) +		goto neg_exit; + +	cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); + +	/* BB we may eventually want to match the negotiated vs. requested +	   dialect, even though we are only requesting one at a time */ +	if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) +		cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); +	else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) +		cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); +	else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) +		cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); +	else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) +		cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); +	else { +		cifs_dbg(VFS, "Illegal dialect returned by server %d\n", +			 le16_to_cpu(rsp->DialectRevision)); +		rc = -EIO; +		goto neg_exit; +	} +	server->dialect = le16_to_cpu(rsp->DialectRevision); + +	/* SMB2 only has an extended negflavor */ +	server->negflavor = CIFS_NEGFLAVOR_EXTENDED; +	/* set it to the maximum buffer size value we can send with 1 credit */ +	server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), +			       SMB2_MAX_BUFFER_SIZE); +	server->max_read = le32_to_cpu(rsp->MaxReadSize); +	server->max_write = le32_to_cpu(rsp->MaxWriteSize); +	/* BB Do we need to validate the SecurityMode? */ +	server->sec_mode = le16_to_cpu(rsp->SecurityMode); +	server->capabilities = le32_to_cpu(rsp->Capabilities); +	/* Internal types */ +	server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; + +	security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, +					       &rsp->hdr); +	/* +	 * See MS-SMB2 section 2.2.4: if no blob, client picks default which +	 * for us will be +	 *	ses->sectype = RawNTLMSSP; +	 * but for time being this is our only auth choice so doesn't matter. +	 * We just found a server which sets blob length to zero expecting raw. +	 */ +	if (blob_length == 0) +		cifs_dbg(FYI, "missing security blob on negprot\n"); + +	rc = cifs_enable_signing(server, ses->sign); +#ifdef CONFIG_SMB2_ASN1  /* BB REMOVEME when updated asn1.c ready */ +	if (rc) +		goto neg_exit; +	if (blob_length) +		rc = decode_neg_token_init(security_blob, blob_length, +				   &server->sec_type); +	if (rc == 1) +		rc = 0; +	else if (rc == 0) { +		rc = -EIO; +		goto neg_exit; +	} +#endif + +neg_exit: +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc = 0; +	struct validate_negotiate_info_req vneg_inbuf; +	struct validate_negotiate_info_rsp *pneg_rsp; +	u32 rsplen; + +	cifs_dbg(FYI, "validate negotiate\n"); + +	/* +	 * validation ioctl must be signed, so no point sending this if we +	 * can not sign it.  We could eventually change this to selectively +	 * sign just this, the first and only signed request on a connection. +	 * This is good enough for now since a user who wants better security +	 * would also enable signing on the mount. Having validation of +	 * negotiate info for signed connections helps reduce attack vectors +	 */ +	if (tcon->ses->server->sign == false) +		return 0; /* validation requires signing */ + +	vneg_inbuf.Capabilities = +			cpu_to_le32(tcon->ses->server->vals->req_capabilities); +	memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid, +					SMB2_CLIENT_GUID_SIZE); + +	if (tcon->ses->sign) +		vneg_inbuf.SecurityMode = +			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); +	else if (global_secflags & CIFSSEC_MAY_SIGN) +		vneg_inbuf.SecurityMode = +			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); +	else +		vneg_inbuf.SecurityMode = 0; + +	vneg_inbuf.DialectCount = cpu_to_le16(1); +	vneg_inbuf.Dialects[0] = +		cpu_to_le16(tcon->ses->server->vals->protocol_id); + +	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, +		FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */, +		(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req), +		(char **)&pneg_rsp, &rsplen); + +	if (rc != 0) { +		cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); +		return -EIO; +	} + +	if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { +		cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); +		return -EIO; +	} + +	/* check validate negotiate info response matches what we got earlier */ +	if (pneg_rsp->Dialect != +			cpu_to_le16(tcon->ses->server->vals->protocol_id)) +		goto vneg_out; + +	if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) +		goto vneg_out; + +	/* do not validate server guid because not saved at negprot time yet */ + +	if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | +	      SMB2_LARGE_FILES) != tcon->ses->server->capabilities) +		goto vneg_out; + +	/* validate negotiate successful */ +	cifs_dbg(FYI, "validate negotiate info successful\n"); +	return 0; + +vneg_out: +	cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); +	return -EIO; +} + +int +SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, +		const struct nls_table *nls_cp) +{ +	struct smb2_sess_setup_req *req; +	struct smb2_sess_setup_rsp *rsp = NULL; +	struct kvec iov[2]; +	int rc = 0; +	int resp_buftype; +	__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ +	struct TCP_Server_Info *server = ses->server; +	u16 blob_length = 0; +	char *security_blob; +	char *ntlmssp_blob = NULL; +	bool use_spnego = false; /* else use raw ntlmssp */ + +	cifs_dbg(FYI, "Session Setup\n"); + +	if (!server) { +		WARN(1, "%s: server is NULL!\n", __func__); +		return -EIO; +	} + +	/* +	 * If we are here due to reconnect, free per-smb session key +	 * in case signing was required. +	 */ +	kfree(ses->auth_key.response); +	ses->auth_key.response = NULL; + +	/* +	 * If memory allocation is successful, caller of this function +	 * frees it. +	 */ +	ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); +	if (!ses->ntlmssp) +		return -ENOMEM; +	ses->ntlmssp->sesskey_per_smbsess = true; + +	/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ +	ses->sectype = RawNTLMSSP; + +ssetup_ntlmssp_authenticate: +	if (phase == NtLmChallenge) +		phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ + +	rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); +	if (rc) +		return rc; + +	req->hdr.SessionId = 0; /* First session, not a reauthenticate */ +	req->VcNumber = 0; /* MBZ */ +	/* to enable echos and oplocks */ +	req->hdr.CreditRequest = cpu_to_le16(3); + +	/* only one of SMB2 signing flags may be set in SMB2 request */ +	if (server->sign) +		req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; +	else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ +		req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; +	else +		req->SecurityMode = 0; + +	req->Capabilities = 0; +	req->Channel = 0; /* MBZ */ + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field and 1 for pad */ +	iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; +	if (phase == NtLmNegotiate) { +		ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), +				       GFP_KERNEL); +		if (ntlmssp_blob == NULL) { +			rc = -ENOMEM; +			goto ssetup_exit; +		} +		build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); +		if (use_spnego) { +			/* blob_length = build_spnego_ntlmssp_blob( +					&security_blob, +					sizeof(struct _NEGOTIATE_MESSAGE), +					ntlmssp_blob); */ +			/* BB eventually need to add this */ +			cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); +			rc = -EOPNOTSUPP; +			kfree(ntlmssp_blob); +			goto ssetup_exit; +		} else { +			blob_length = sizeof(struct _NEGOTIATE_MESSAGE); +			/* with raw NTLMSSP we don't encapsulate in SPNEGO */ +			security_blob = ntlmssp_blob; +		} +	} else if (phase == NtLmAuthenticate) { +		req->hdr.SessionId = ses->Suid; +		ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, +				       GFP_KERNEL); +		if (ntlmssp_blob == NULL) { +			rc = -ENOMEM; +			goto ssetup_exit; +		} +		rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses, +					     nls_cp); +		if (rc) { +			cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", +				 rc); +			goto ssetup_exit; /* BB double check error handling */ +		} +		if (use_spnego) { +			/* blob_length = build_spnego_ntlmssp_blob( +							&security_blob, +							blob_length, +							ntlmssp_blob); */ +			cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); +			rc = -EOPNOTSUPP; +			kfree(ntlmssp_blob); +			goto ssetup_exit; +		} else { +			security_blob = ntlmssp_blob; +		} +	} else { +		cifs_dbg(VFS, "illegal ntlmssp phase\n"); +		rc = -EIO; +		goto ssetup_exit; +	} + +	/* Testing shows that buffer offset must be at location of Buffer[0] */ +	req->SecurityBufferOffset = +				cpu_to_le16(sizeof(struct smb2_sess_setup_req) - +					    1 /* pad */ - 4 /* rfc1001 len */); +	req->SecurityBufferLength = cpu_to_le16(blob_length); +	iov[1].iov_base = security_blob; +	iov[1].iov_len = blob_length; + +	inc_rfc1001_len(req, blob_length - 1 /* pad */); + +	/* BB add code to build os and lm fields */ + +	rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, +			  CIFS_LOG_ERROR | CIFS_NEG_OP); + +	kfree(security_blob); +	rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; +	if (resp_buftype != CIFS_NO_BUFFER && +	    rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { +		if (phase != NtLmNegotiate) { +			cifs_dbg(VFS, "Unexpected more processing error\n"); +			goto ssetup_exit; +		} +		if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != +				le16_to_cpu(rsp->SecurityBufferOffset)) { +			cifs_dbg(VFS, "Invalid security buffer offset %d\n", +				 le16_to_cpu(rsp->SecurityBufferOffset)); +			rc = -EIO; +			goto ssetup_exit; +		} + +		/* NTLMSSP Negotiate sent now processing challenge (response) */ +		phase = NtLmChallenge; /* process ntlmssp challenge */ +		rc = 0; /* MORE_PROCESSING is not an error here but expected */ +		ses->Suid = rsp->hdr.SessionId; +		rc = decode_ntlmssp_challenge(rsp->Buffer, +				le16_to_cpu(rsp->SecurityBufferLength), ses); +	} + +	/* +	 * BB eventually add code for SPNEGO decoding of NtlmChallenge blob, +	 * but at least the raw NTLMSSP case works. +	 */ +	/* +	 * No tcon so can't do +	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); +	 */ +	if (rc != 0) +		goto ssetup_exit; + +	ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) +		cifs_dbg(VFS, "SMB3 encryption not supported yet\n"); +ssetup_exit: +	free_rsp_buf(resp_buftype, rsp); + +	/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ +	if ((phase == NtLmChallenge) && (rc == 0)) +		goto ssetup_ntlmssp_authenticate; + +	if (!rc) { +		mutex_lock(&server->srv_mutex); +		if (server->sign && server->ops->generate_signingkey) { +			rc = server->ops->generate_signingkey(ses); +			kfree(ses->auth_key.response); +			ses->auth_key.response = NULL; +			if (rc) { +				cifs_dbg(FYI, +					"SMB3 session key generation failed\n"); +				mutex_unlock(&server->srv_mutex); +				goto keygen_exit; +			} +		} +		if (!server->session_estab) { +			server->sequence_number = 0x2; +			server->session_estab = true; +		} +		mutex_unlock(&server->srv_mutex); + +		cifs_dbg(FYI, "SMB2/3 session established successfully\n"); +		spin_lock(&GlobalMid_Lock); +		ses->status = CifsGood; +		ses->need_reconnect = false; +		spin_unlock(&GlobalMid_Lock); +	} + +keygen_exit: +	if (!server->sign) { +		kfree(ses->auth_key.response); +		ses->auth_key.response = NULL; +	} +	kfree(ses->ntlmssp); + +	return rc; +} + +int +SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) +{ +	struct smb2_logoff_req *req; /* response is also trivial struct */ +	int rc = 0; +	struct TCP_Server_Info *server; + +	cifs_dbg(FYI, "disconnect session %p\n", ses); + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	/* no need to send SMB logoff if uid already closed due to reconnect */ +	if (ses->need_reconnect) +		goto smb2_session_already_dead; + +	rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req); +	if (rc) +		return rc; + +	 /* since no tcon, smb2_init can not do this, so do here */ +	req->hdr.SessionId = ses->Suid; +	if (server->sign) +		req->hdr.Flags |= SMB2_FLAGS_SIGNED; + +	rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0); +	/* +	 * No tcon so can't do +	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); +	 */ + +smb2_session_already_dead: +	return rc; +} + +static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) +{ +	cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); +} + +#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) + +/* These are similar values to what Windows uses */ +static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) +{ +	tcon->max_chunks = 256; +	tcon->max_bytes_chunk = 1048576; +	tcon->max_bytes_copy = 16777216; +} + +int +SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, +	  struct cifs_tcon *tcon, const struct nls_table *cp) +{ +	struct smb2_tree_connect_req *req; +	struct smb2_tree_connect_rsp *rsp = NULL; +	struct kvec iov[2]; +	int rc = 0; +	int resp_buftype; +	int unc_path_len; +	struct TCP_Server_Info *server; +	__le16 *unc_path = NULL; + +	cifs_dbg(FYI, "TCON\n"); + +	if ((ses->server) && tree) +		server = ses->server; +	else +		return -EIO; + +	if (tcon && tcon->bad_network_name) +		return -ENOENT; + +	unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); +	if (unc_path == NULL) +		return -ENOMEM; + +	unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; +	unc_path_len *= 2; +	if (unc_path_len < 2) { +		kfree(unc_path); +		return -EINVAL; +	} + +	rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req); +	if (rc) { +		kfree(unc_path); +		return rc; +	} + +	if (tcon == NULL) { +		/* since no tcon, smb2_init can not do this, so do here */ +		req->hdr.SessionId = ses->Suid; +		/* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED) +			req->hdr.Flags |= SMB2_FLAGS_SIGNED; */ +	} + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field and 1 for pad */ +	iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + +	/* Testing shows that buffer offset must be at location of Buffer[0] */ +	req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) +			- 1 /* pad */ - 4 /* do not count rfc1001 len field */); +	req->PathLength = cpu_to_le16(unc_path_len - 2); +	iov[1].iov_base = unc_path; +	iov[1].iov_len = unc_path_len; + +	inc_rfc1001_len(req, unc_path_len - 1 /* pad */); + +	rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0); +	rsp = (struct smb2_tree_connect_rsp *)iov[0].iov_base; + +	if (rc != 0) { +		if (tcon) { +			cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); +			tcon->need_reconnect = true; +		} +		goto tcon_error_exit; +	} + +	if (tcon == NULL) { +		ses->ipc_tid = rsp->hdr.TreeId; +		goto tcon_exit; +	} + +	if (rsp->ShareType & SMB2_SHARE_TYPE_DISK) +		cifs_dbg(FYI, "connection to disk share\n"); +	else if (rsp->ShareType & SMB2_SHARE_TYPE_PIPE) { +		tcon->ipc = true; +		cifs_dbg(FYI, "connection to pipe share\n"); +	} else if (rsp->ShareType & SMB2_SHARE_TYPE_PRINT) { +		tcon->print = true; +		cifs_dbg(FYI, "connection to printer\n"); +	} else { +		cifs_dbg(VFS, "unknown share type %d\n", rsp->ShareType); +		rc = -EOPNOTSUPP; +		goto tcon_error_exit; +	} + +	tcon->share_flags = le32_to_cpu(rsp->ShareFlags); +	tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ +	tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); +	tcon->tidStatus = CifsGood; +	tcon->need_reconnect = false; +	tcon->tid = rsp->hdr.TreeId; +	strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); + +	if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && +	    ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) +		cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); +	init_copy_chunk_defaults(tcon); +	if (tcon->ses->server->ops->validate_negotiate) +		rc = tcon->ses->server->ops->validate_negotiate(xid, tcon); +tcon_exit: +	free_rsp_buf(resp_buftype, rsp); +	kfree(unc_path); +	return rc; + +tcon_error_exit: +	if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) { +		cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); +		tcon->bad_network_name = true; +	} +	goto tcon_exit; +} + +int +SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) +{ +	struct smb2_tree_disconnect_req *req; /* response is trivial */ +	int rc = 0; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; + +	cifs_dbg(FYI, "Tree Disconnect\n"); + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) +		return 0; + +	rc = small_smb2_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req); +	if (rc) +		return rc; + +	rc = SendReceiveNoRsp(xid, ses, (char *)&req->hdr, 0); +	if (rc) +		cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); + +	return rc; +} + + +static struct create_durable * +create_durable_buf(void) +{ +	struct create_durable *buf; + +	buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); +	if (!buf) +		return NULL; + +	buf->ccontext.DataOffset = cpu_to_le16(offsetof +					(struct create_durable, Data)); +	buf->ccontext.DataLength = cpu_to_le32(16); +	buf->ccontext.NameOffset = cpu_to_le16(offsetof +				(struct create_durable, Name)); +	buf->ccontext.NameLength = cpu_to_le16(4); +	/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ +	buf->Name[0] = 'D'; +	buf->Name[1] = 'H'; +	buf->Name[2] = 'n'; +	buf->Name[3] = 'Q'; +	return buf; +} + +static struct create_durable * +create_reconnect_durable_buf(struct cifs_fid *fid) +{ +	struct create_durable *buf; + +	buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); +	if (!buf) +		return NULL; + +	buf->ccontext.DataOffset = cpu_to_le16(offsetof +					(struct create_durable, Data)); +	buf->ccontext.DataLength = cpu_to_le32(16); +	buf->ccontext.NameOffset = cpu_to_le16(offsetof +				(struct create_durable, Name)); +	buf->ccontext.NameLength = cpu_to_le16(4); +	buf->Data.Fid.PersistentFileId = fid->persistent_fid; +	buf->Data.Fid.VolatileFileId = fid->volatile_fid; +	/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ +	buf->Name[0] = 'D'; +	buf->Name[1] = 'H'; +	buf->Name[2] = 'n'; +	buf->Name[3] = 'C'; +	return buf; +} + +static __u8 +parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, +		  unsigned int *epoch) +{ +	char *data_offset; +	struct create_context *cc; +	unsigned int next = 0; +	char *name; + +	data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset); +	cc = (struct create_context *)data_offset; +	do { +		cc = (struct create_context *)((char *)cc + next); +		name = le16_to_cpu(cc->NameOffset) + (char *)cc; +		if (le16_to_cpu(cc->NameLength) != 4 || +		    strncmp(name, "RqLs", 4)) { +			next = le32_to_cpu(cc->Next); +			continue; +		} +		return server->ops->parse_lease_buf(cc, epoch); +	} while (next != 0); + +	return 0; +} + +static int +add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, +		  unsigned int *num_iovec, __u8 *oplock) +{ +	struct smb2_create_req *req = iov[0].iov_base; +	unsigned int num = *num_iovec; + +	iov[num].iov_base = server->ops->create_lease_buf(oplock+1, *oplock); +	if (iov[num].iov_base == NULL) +		return -ENOMEM; +	iov[num].iov_len = server->vals->create_lease_size; +	req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; +	if (!req->CreateContextsOffset) +		req->CreateContextsOffset = cpu_to_le32( +				sizeof(struct smb2_create_req) - 4 + +				iov[num - 1].iov_len); +	le32_add_cpu(&req->CreateContextsLength, +		     server->vals->create_lease_size); +	inc_rfc1001_len(&req->hdr, server->vals->create_lease_size); +	*num_iovec = num + 1; +	return 0; +} + +static int +add_durable_context(struct kvec *iov, unsigned int *num_iovec, +		    struct cifs_open_parms *oparms) +{ +	struct smb2_create_req *req = iov[0].iov_base; +	unsigned int num = *num_iovec; + +	if (oparms->reconnect) { +		iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); +		/* indicate that we don't need to relock the file */ +		oparms->reconnect = false; +	} else +		iov[num].iov_base = create_durable_buf(); +	if (iov[num].iov_base == NULL) +		return -ENOMEM; +	iov[num].iov_len = sizeof(struct create_durable); +	if (!req->CreateContextsOffset) +		req->CreateContextsOffset = +			cpu_to_le32(sizeof(struct smb2_create_req) - 4 + +								iov[1].iov_len); +	le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable)); +	inc_rfc1001_len(&req->hdr, sizeof(struct create_durable)); +	*num_iovec = num + 1; +	return 0; +} + +int +SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, +	  __u8 *oplock, struct smb2_file_all_info *buf, +	  struct smb2_err_rsp **err_buf) +{ +	struct smb2_create_req *req; +	struct smb2_create_rsp *rsp; +	struct TCP_Server_Info *server; +	struct cifs_tcon *tcon = oparms->tcon; +	struct cifs_ses *ses = tcon->ses; +	struct kvec iov[4]; +	int resp_buftype; +	int uni_path_len; +	__le16 *copy_path = NULL; +	int copy_size; +	int rc = 0; +	unsigned int num_iovecs = 2; +	__u32 file_attributes = 0; +	char *dhc_buf = NULL, *lc_buf = NULL; + +	cifs_dbg(FYI, "create/open\n"); + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	rc = small_smb2_init(SMB2_CREATE, tcon, (void **) &req); +	if (rc) +		return rc; + +	if (oparms->create_options & CREATE_OPTION_READONLY) +		file_attributes |= ATTR_READONLY; + +	req->ImpersonationLevel = IL_IMPERSONATION; +	req->DesiredAccess = cpu_to_le32(oparms->desired_access); +	/* File attributes ignored on open (used in create though) */ +	req->FileAttributes = cpu_to_le32(file_attributes); +	req->ShareAccess = FILE_SHARE_ALL_LE; +	req->CreateDisposition = cpu_to_le32(oparms->disposition); +	req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); +	uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; +	/* do not count rfc1001 len field */ +	req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4); + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; + +	/* MUST set path len (NameLength) to 0 opening root of share */ +	req->NameLength = cpu_to_le16(uni_path_len - 2); +	/* -1 since last byte is buf[0] which is sent below (path) */ +	iov[0].iov_len--; +	if (uni_path_len % 8 != 0) { +		copy_size = uni_path_len / 8 * 8; +		if (copy_size < uni_path_len) +			copy_size += 8; + +		copy_path = kzalloc(copy_size, GFP_KERNEL); +		if (!copy_path) +			return -ENOMEM; +		memcpy((char *)copy_path, (const char *)path, +			uni_path_len); +		uni_path_len = copy_size; +		path = copy_path; +	} + +	iov[1].iov_len = uni_path_len; +	iov[1].iov_base = path; +	/* -1 since last byte is buf[0] which was counted in smb2_buf_len */ +	inc_rfc1001_len(req, uni_path_len - 1); + +	if (!server->oplocks) +		*oplock = SMB2_OPLOCK_LEVEL_NONE; + +	if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || +	    *oplock == SMB2_OPLOCK_LEVEL_NONE) +		req->RequestedOplockLevel = *oplock; +	else { +		rc = add_lease_context(server, iov, &num_iovecs, oplock); +		if (rc) { +			cifs_small_buf_release(req); +			kfree(copy_path); +			return rc; +		} +		lc_buf = iov[num_iovecs-1].iov_base; +	} + +	if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { +		/* need to set Next field of lease context if we request it */ +		if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) { +			struct create_context *ccontext = +			    (struct create_context *)iov[num_iovecs-1].iov_base; +			ccontext->Next = +				cpu_to_le32(server->vals->create_lease_size); +		} +		rc = add_durable_context(iov, &num_iovecs, oparms); +		if (rc) { +			cifs_small_buf_release(req); +			kfree(copy_path); +			kfree(lc_buf); +			return rc; +		} +		dhc_buf = iov[num_iovecs-1].iov_base; +	} + +	rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); +	rsp = (struct smb2_create_rsp *)iov[0].iov_base; + +	if (rc != 0) { +		cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); +		if (err_buf) +			*err_buf = kmemdup(rsp, get_rfc1002_length(rsp) + 4, +					   GFP_KERNEL); +		goto creat_exit; +	} + +	oparms->fid->persistent_fid = rsp->PersistentFileId; +	oparms->fid->volatile_fid = rsp->VolatileFileId; + +	if (buf) { +		memcpy(buf, &rsp->CreationTime, 32); +		buf->AllocationSize = rsp->AllocationSize; +		buf->EndOfFile = rsp->EndofFile; +		buf->Attributes = rsp->FileAttributes; +		buf->NumberOfLinks = cpu_to_le32(1); +		buf->DeletePending = 0; +	} + +	if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) +		*oplock = parse_lease_state(server, rsp, &oparms->fid->epoch); +	else +		*oplock = rsp->OplockLevel; +creat_exit: +	kfree(copy_path); +	kfree(lc_buf); +	kfree(dhc_buf); +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +/* + *	SMB2 IOCTL is used for both IOCTLs and FSCTLs + */ +int +SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, +	   u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data, +	   u32 indatalen, char **out_data, u32 *plen /* returned data len */) +{ +	struct smb2_ioctl_req *req; +	struct smb2_ioctl_rsp *rsp; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; +	struct kvec iov[2]; +	int resp_buftype; +	int num_iovecs; +	int rc = 0; + +	cifs_dbg(FYI, "SMB2 IOCTL\n"); + +	*out_data = NULL; +	/* zero out returned data len, in case of error */ +	if (plen) +		*plen = 0; + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req); +	if (rc) +		return rc; + +	req->CtlCode = cpu_to_le32(opcode); +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; + +	if (indatalen) { +		req->InputCount = cpu_to_le32(indatalen); +		/* do not set InputOffset if no input data */ +		req->InputOffset = +		       cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4); +		iov[1].iov_base = in_data; +		iov[1].iov_len = indatalen; +		num_iovecs = 2; +	} else +		num_iovecs = 1; + +	req->OutputOffset = 0; +	req->OutputCount = 0; /* MBZ */ + +	/* +	 * Could increase MaxOutputResponse, but that would require more +	 * than one credit. Windows typically sets this smaller, but for some +	 * ioctls it may be useful to allow server to send more. No point +	 * limiting what the server can send as long as fits in one credit +	 */ +	req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */ + +	if (is_fsctl) +		req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); +	else +		req->Flags = 0; + +	iov[0].iov_base = (char *)req; + +	/* +	 * If no input data, the size of ioctl struct in +	 * protocol spec still includes a 1 byte data buffer, +	 * but if input data passed to ioctl, we do not +	 * want to double count this, so we do not send +	 * the dummy one byte of data in iovec[0] if sending +	 * input data (in iovec[1]). We also must add 4 bytes +	 * in first iovec to allow for rfc1002 length field. +	 */ + +	if (indatalen) { +		iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; +		inc_rfc1001_len(req, indatalen - 1); +	} else +		iov[0].iov_len = get_rfc1002_length(req) + 4; + + +	rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); +	rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; + +	if ((rc != 0) && (rc != -EINVAL)) { +		if (tcon) +			cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); +		goto ioctl_exit; +	} else if (rc == -EINVAL) { +		if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && +		    (opcode != FSCTL_SRV_COPYCHUNK)) { +			if (tcon) +				cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); +			goto ioctl_exit; +		} +	} + +	/* check if caller wants to look at return data or just return rc */ +	if ((plen == NULL) || (out_data == NULL)) +		goto ioctl_exit; + +	*plen = le32_to_cpu(rsp->OutputCount); + +	/* We check for obvious errors in the output buffer length and offset */ +	if (*plen == 0) +		goto ioctl_exit; /* server returned no data */ +	else if (*plen > 0xFF00) { +		cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); +		*plen = 0; +		rc = -EIO; +		goto ioctl_exit; +	} + +	if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) { +		cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, +			le32_to_cpu(rsp->OutputOffset)); +		*plen = 0; +		rc = -EIO; +		goto ioctl_exit; +	} + +	*out_data = kmalloc(*plen, GFP_KERNEL); +	if (*out_data == NULL) { +		rc = -ENOMEM; +		goto ioctl_exit; +	} + +	memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset), +	       *plen); +ioctl_exit: +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +/* + *   Individual callers to ioctl worker function follow + */ + +int +SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		     u64 persistent_fid, u64 volatile_fid) +{ +	int rc; +	struct  compress_ioctl fsctl_input; +	char *ret_data = NULL; + +	fsctl_input.CompressionState = +			__constant_cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + +	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, +			FSCTL_SET_COMPRESSION, true /* is_fsctl */, +			(char *)&fsctl_input /* data input */, +			2 /* in data len */, &ret_data /* out data */, NULL); + +	cifs_dbg(FYI, "set compression rc %d\n", rc); + +	return rc; +} + +int +SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, +	   u64 persistent_fid, u64 volatile_fid) +{ +	struct smb2_close_req *req; +	struct smb2_close_rsp *rsp; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; +	struct kvec iov[1]; +	int resp_buftype; +	int rc = 0; + +	cifs_dbg(FYI, "Close\n"); + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	rc = small_smb2_init(SMB2_CLOSE, tcon, (void **) &req); +	if (rc) +		return rc; + +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; + +	rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); +	rsp = (struct smb2_close_rsp *)iov[0].iov_base; + +	if (rc != 0) { +		if (tcon) +			cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); +		goto close_exit; +	} + +	/* BB FIXME - decode close response, update inode for caching */ + +close_exit: +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +static int +validate_buf(unsigned int offset, unsigned int buffer_length, +	     struct smb2_hdr *hdr, unsigned int min_buf_size) + +{ +	unsigned int smb_len = be32_to_cpu(hdr->smb2_buf_length); +	char *end_of_smb = smb_len + 4 /* RFC1001 length field */ + (char *)hdr; +	char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr; +	char *end_of_buf = begin_of_buf + buffer_length; + + +	if (buffer_length < min_buf_size) { +		cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", +			 buffer_length, min_buf_size); +		return -EINVAL; +	} + +	/* check if beyond RFC1001 maximum length */ +	if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { +		cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", +			 buffer_length, smb_len); +		return -EINVAL; +	} + +	if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { +		cifs_dbg(VFS, "illegal server response, bad offset to data\n"); +		return -EINVAL; +	} + +	return 0; +} + +/* + * If SMB buffer fields are valid, copy into temporary buffer to hold result. + * Caller must free buffer. + */ +static int +validate_and_copy_buf(unsigned int offset, unsigned int buffer_length, +		      struct smb2_hdr *hdr, unsigned int minbufsize, +		      char *data) + +{ +	char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr; +	int rc; + +	if (!data) +		return -EINVAL; + +	rc = validate_buf(offset, buffer_length, hdr, minbufsize); +	if (rc) +		return rc; + +	memcpy(data, begin_of_buf, buffer_length); + +	return 0; +} + +static int +query_info(const unsigned int xid, struct cifs_tcon *tcon, +	   u64 persistent_fid, u64 volatile_fid, u8 info_class, +	   size_t output_len, size_t min_len, void *data) +{ +	struct smb2_query_info_req *req; +	struct smb2_query_info_rsp *rsp = NULL; +	struct kvec iov[2]; +	int rc = 0; +	int resp_buftype; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; + +	cifs_dbg(FYI, "Query Info\n"); + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req); +	if (rc) +		return rc; + +	req->InfoType = SMB2_O_INFO_FILE; +	req->FileInfoClass = info_class; +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; +	/* 4 for rfc1002 length field and 1 for Buffer */ +	req->InputBufferOffset = +		cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4); +	req->OutputBufferLength = cpu_to_le32(output_len); + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; + +	rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); +	rsp = (struct smb2_query_info_rsp *)iov[0].iov_base; + +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); +		goto qinf_exit; +	} + +	rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset), +				   le32_to_cpu(rsp->OutputBufferLength), +				   &rsp->hdr, min_len, data); + +qinf_exit: +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +int +SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, +		u64 persistent_fid, u64 volatile_fid, +		struct smb2_file_all_info *data) +{ +	return query_info(xid, tcon, persistent_fid, volatile_fid, +			  FILE_ALL_INFORMATION, +			  sizeof(struct smb2_file_all_info) + MAX_NAME * 2, +			  sizeof(struct smb2_file_all_info), data); +} + +int +SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, +		 u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) +{ +	return query_info(xid, tcon, persistent_fid, volatile_fid, +			  FILE_INTERNAL_INFORMATION, +			  sizeof(struct smb2_file_internal_info), +			  sizeof(struct smb2_file_internal_info), uniqueid); +} + +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +smb2_echo_callback(struct mid_q_entry *mid) +{ +	struct TCP_Server_Info *server = mid->callback_data; +	struct smb2_echo_rsp *smb2 = (struct smb2_echo_rsp *)mid->resp_buf; +	unsigned int credits_received = 1; + +	if (mid->mid_state == MID_RESPONSE_RECEIVED) +		credits_received = le16_to_cpu(smb2->hdr.CreditRequest); + +	DeleteMidQEntry(mid); +	add_credits(server, credits_received, CIFS_ECHO_OP); +} + +int +SMB2_echo(struct TCP_Server_Info *server) +{ +	struct smb2_echo_req *req; +	int rc = 0; +	struct kvec iov; +	struct smb_rqst rqst = { .rq_iov = &iov, +				 .rq_nvec = 1 }; + +	cifs_dbg(FYI, "In echo request\n"); + +	rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req); +	if (rc) +		return rc; + +	req->hdr.CreditRequest = cpu_to_le16(1); + +	iov.iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov.iov_len = get_rfc1002_length(req) + 4; + +	rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server, +			     CIFS_ECHO_OP); +	if (rc) +		cifs_dbg(FYI, "Echo request failed: %d\n", rc); + +	cifs_small_buf_release(req); +	return rc; +} + +int +SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, +	   u64 volatile_fid) +{ +	struct smb2_flush_req *req; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; +	struct kvec iov[1]; +	int resp_buftype; +	int rc = 0; + +	cifs_dbg(FYI, "Flush\n"); + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	rc = small_smb2_init(SMB2_FLUSH, tcon, (void **) &req); +	if (rc) +		return rc; + +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; + +	rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); + +	if ((rc != 0) && tcon) +		cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); + +	free_rsp_buf(resp_buftype, iov[0].iov_base); +	return rc; +} + +/* + * To form a chain of read requests, any read requests after the first should + * have the end_of_chain boolean set to true. + */ +static int +smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms, +		  unsigned int remaining_bytes, int request_type) +{ +	int rc = -EACCES; +	struct smb2_read_req *req = NULL; + +	rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req); +	if (rc) +		return rc; +	if (io_parms->tcon->ses->server == NULL) +		return -ECONNABORTED; + +	req->hdr.ProcessId = cpu_to_le32(io_parms->pid); + +	req->PersistentFileId = io_parms->persistent_fid; +	req->VolatileFileId = io_parms->volatile_fid; +	req->ReadChannelInfoOffset = 0; /* reserved */ +	req->ReadChannelInfoLength = 0; /* reserved */ +	req->Channel = 0; /* reserved */ +	req->MinimumCount = 0; +	req->Length = cpu_to_le32(io_parms->length); +	req->Offset = cpu_to_le64(io_parms->offset); + +	if (request_type & CHAINED_REQUEST) { +		if (!(request_type & END_OF_CHAIN)) { +			/* 4 for rfc1002 length field */ +			req->hdr.NextCommand = +				cpu_to_le32(get_rfc1002_length(req) + 4); +		} else /* END_OF_CHAIN */ +			req->hdr.NextCommand = 0; +		if (request_type & RELATED_REQUEST) { +			req->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS; +			/* +			 * Related requests use info from previous read request +			 * in chain. +			 */ +			req->hdr.SessionId = 0xFFFFFFFF; +			req->hdr.TreeId = 0xFFFFFFFF; +			req->PersistentFileId = 0xFFFFFFFF; +			req->VolatileFileId = 0xFFFFFFFF; +		} +	} +	if (remaining_bytes > io_parms->length) +		req->RemainingBytes = cpu_to_le32(remaining_bytes); +	else +		req->RemainingBytes = 0; + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; +	return rc; +} + +static void +smb2_readv_callback(struct mid_q_entry *mid) +{ +	struct cifs_readdata *rdata = mid->callback_data; +	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server; +	struct smb2_hdr *buf = (struct smb2_hdr *)rdata->iov.iov_base; +	unsigned int credits_received = 1; +	struct smb_rqst rqst = { .rq_iov = &rdata->iov, +				 .rq_nvec = 1, +				 .rq_pages = rdata->pages, +				 .rq_npages = rdata->nr_pages, +				 .rq_pagesz = rdata->pagesz, +				 .rq_tailsz = rdata->tailsz }; + +	cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", +		 __func__, mid->mid, mid->mid_state, rdata->result, +		 rdata->bytes); + +	switch (mid->mid_state) { +	case MID_RESPONSE_RECEIVED: +		credits_received = le16_to_cpu(buf->CreditRequest); +		/* result already set, check signature */ +		if (server->sign) { +			int rc; + +			rc = smb2_verify_signature(&rqst, server); +			if (rc) +				cifs_dbg(VFS, "SMB signature verification returned error = %d\n", +					 rc); +		} +		/* FIXME: should this be counted toward the initiating task? */ +		task_io_account_read(rdata->bytes); +		cifs_stats_bytes_read(tcon, rdata->bytes); +		break; +	case MID_REQUEST_SUBMITTED: +	case MID_RETRY_NEEDED: +		rdata->result = -EAGAIN; +		break; +	default: +		if (rdata->result != -ENODATA) +			rdata->result = -EIO; +	} + +	if (rdata->result) +		cifs_stats_fail_inc(tcon, SMB2_READ_HE); + +	queue_work(cifsiod_wq, &rdata->work); +	DeleteMidQEntry(mid); +	add_credits(server, credits_received, 0); +} + +/* smb2_async_readv - send an async write, and set up mid to handle result */ +int +smb2_async_readv(struct cifs_readdata *rdata) +{ +	int rc; +	struct smb2_hdr *buf; +	struct cifs_io_parms io_parms; +	struct smb_rqst rqst = { .rq_iov = &rdata->iov, +				 .rq_nvec = 1 }; + +	cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", +		 __func__, rdata->offset, rdata->bytes); + +	io_parms.tcon = tlink_tcon(rdata->cfile->tlink); +	io_parms.offset = rdata->offset; +	io_parms.length = rdata->bytes; +	io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; +	io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; +	io_parms.pid = rdata->pid; +	rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0); +	if (rc) +		return rc; + +	buf = (struct smb2_hdr *)rdata->iov.iov_base; +	/* 4 for rfc1002 length field */ +	rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4; + +	kref_get(&rdata->refcount); +	rc = cifs_call_async(io_parms.tcon->ses->server, &rqst, +			     cifs_readv_receive, smb2_readv_callback, +			     rdata, 0); +	if (rc) { +		kref_put(&rdata->refcount, cifs_readdata_release); +		cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); +	} + +	cifs_small_buf_release(buf); +	return rc; +} + +int +SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, +	  unsigned int *nbytes, char **buf, int *buf_type) +{ +	int resp_buftype, rc = -EACCES; +	struct smb2_read_rsp *rsp = NULL; +	struct kvec iov[1]; + +	*nbytes = 0; +	rc = smb2_new_read_req(iov, io_parms, 0, 0); +	if (rc) +		return rc; + +	rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1, +			  &resp_buftype, CIFS_LOG_ERROR); + +	rsp = (struct smb2_read_rsp *)iov[0].iov_base; + +	if (rsp->hdr.Status == STATUS_END_OF_FILE) { +		free_rsp_buf(resp_buftype, iov[0].iov_base); +		return 0; +	} + +	if (rc) { +		cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); +		cifs_dbg(VFS, "Send error in read = %d\n", rc); +	} else { +		*nbytes = le32_to_cpu(rsp->DataLength); +		if ((*nbytes > CIFS_MAX_MSGSIZE) || +		    (*nbytes > io_parms->length)) { +			cifs_dbg(FYI, "bad length %d for count %d\n", +				 *nbytes, io_parms->length); +			rc = -EIO; +			*nbytes = 0; +		} +	} + +	if (*buf) { +		memcpy(*buf, (char *)rsp->hdr.ProtocolId + rsp->DataOffset, +		       *nbytes); +		free_rsp_buf(resp_buftype, iov[0].iov_base); +	} else if (resp_buftype != CIFS_NO_BUFFER) { +		*buf = iov[0].iov_base; +		if (resp_buftype == CIFS_SMALL_BUFFER) +			*buf_type = CIFS_SMALL_BUFFER; +		else if (resp_buftype == CIFS_LARGE_BUFFER) +			*buf_type = CIFS_LARGE_BUFFER; +	} +	return rc; +} + +/* + * Check the mid_state and signature on received buffer (if any), and queue the + * workqueue completion task. + */ +static void +smb2_writev_callback(struct mid_q_entry *mid) +{ +	struct cifs_writedata *wdata = mid->callback_data; +	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); +	unsigned int written; +	struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; +	unsigned int credits_received = 1; + +	switch (mid->mid_state) { +	case MID_RESPONSE_RECEIVED: +		credits_received = le16_to_cpu(rsp->hdr.CreditRequest); +		wdata->result = smb2_check_receive(mid, tcon->ses->server, 0); +		if (wdata->result != 0) +			break; + +		written = le32_to_cpu(rsp->DataLength); +		/* +		 * Mask off high 16 bits when bytes written as returned +		 * by the server is greater than bytes requested by the +		 * client. OS/2 servers are known to set incorrect +		 * CountHigh values. +		 */ +		if (written > wdata->bytes) +			written &= 0xFFFF; + +		if (written < wdata->bytes) +			wdata->result = -ENOSPC; +		else +			wdata->bytes = written; +		break; +	case MID_REQUEST_SUBMITTED: +	case MID_RETRY_NEEDED: +		wdata->result = -EAGAIN; +		break; +	default: +		wdata->result = -EIO; +		break; +	} + +	if (wdata->result) +		cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); + +	queue_work(cifsiod_wq, &wdata->work); +	DeleteMidQEntry(mid); +	add_credits(tcon->ses->server, credits_received, 0); +} + +/* smb2_async_writev - send an async write, and set up mid to handle result */ +int +smb2_async_writev(struct cifs_writedata *wdata, +		  void (*release)(struct kref *kref)) +{ +	int rc = -EACCES; +	struct smb2_write_req *req = NULL; +	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); +	struct kvec iov; +	struct smb_rqst rqst; + +	rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req); +	if (rc) +		goto async_writev_out; + +	req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid); + +	req->PersistentFileId = wdata->cfile->fid.persistent_fid; +	req->VolatileFileId = wdata->cfile->fid.volatile_fid; +	req->WriteChannelInfoOffset = 0; +	req->WriteChannelInfoLength = 0; +	req->Channel = 0; +	req->Offset = cpu_to_le64(wdata->offset); +	/* 4 for rfc1002 length field */ +	req->DataOffset = cpu_to_le16( +				offsetof(struct smb2_write_req, Buffer) - 4); +	req->RemainingBytes = 0; + +	/* 4 for rfc1002 length field and 1 for Buffer */ +	iov.iov_len = get_rfc1002_length(req) + 4 - 1; +	iov.iov_base = req; + +	rqst.rq_iov = &iov; +	rqst.rq_nvec = 1; +	rqst.rq_pages = wdata->pages; +	rqst.rq_npages = wdata->nr_pages; +	rqst.rq_pagesz = wdata->pagesz; +	rqst.rq_tailsz = wdata->tailsz; + +	cifs_dbg(FYI, "async write at %llu %u bytes\n", +		 wdata->offset, wdata->bytes); + +	req->Length = cpu_to_le32(wdata->bytes); + +	inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */); + +	kref_get(&wdata->refcount); +	rc = cifs_call_async(tcon->ses->server, &rqst, NULL, +				smb2_writev_callback, wdata, 0); + +	if (rc) { +		kref_put(&wdata->refcount, release); +		cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); +	} + +async_writev_out: +	cifs_small_buf_release(req); +	return rc; +} + +/* + * SMB2_write function gets iov pointer to kvec array with n_vec as a length. + * The length field from io_parms must be at least 1 and indicates a number of + * elements with data to write that begins with position 1 in iov array. All + * data length is specified by count. + */ +int +SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, +	   unsigned int *nbytes, struct kvec *iov, int n_vec) +{ +	int rc = 0; +	struct smb2_write_req *req = NULL; +	struct smb2_write_rsp *rsp = NULL; +	int resp_buftype; +	*nbytes = 0; + +	if (n_vec < 1) +		return rc; + +	rc = small_smb2_init(SMB2_WRITE, io_parms->tcon, (void **) &req); +	if (rc) +		return rc; + +	if (io_parms->tcon->ses->server == NULL) +		return -ECONNABORTED; + +	req->hdr.ProcessId = cpu_to_le32(io_parms->pid); + +	req->PersistentFileId = io_parms->persistent_fid; +	req->VolatileFileId = io_parms->volatile_fid; +	req->WriteChannelInfoOffset = 0; +	req->WriteChannelInfoLength = 0; +	req->Channel = 0; +	req->Length = cpu_to_le32(io_parms->length); +	req->Offset = cpu_to_le64(io_parms->offset); +	/* 4 for rfc1002 length field */ +	req->DataOffset = cpu_to_le16( +				offsetof(struct smb2_write_req, Buffer) - 4); +	req->RemainingBytes = 0; + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field and 1 for Buffer */ +	iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + +	/* length of entire message including data to be written */ +	inc_rfc1001_len(req, io_parms->length - 1 /* Buffer */); + +	rc = SendReceive2(xid, io_parms->tcon->ses, iov, n_vec + 1, +			  &resp_buftype, 0); +	rsp = (struct smb2_write_rsp *)iov[0].iov_base; + +	if (rc) { +		cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); +		cifs_dbg(VFS, "Send error in write = %d\n", rc); +	} else +		*nbytes = le32_to_cpu(rsp->DataLength); + +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +static unsigned int +num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) +{ +	int len; +	unsigned int entrycount = 0; +	unsigned int next_offset = 0; +	FILE_DIRECTORY_INFO *entryptr; + +	if (bufstart == NULL) +		return 0; + +	entryptr = (FILE_DIRECTORY_INFO *)bufstart; + +	while (1) { +		entryptr = (FILE_DIRECTORY_INFO *) +					((char *)entryptr + next_offset); + +		if ((char *)entryptr + size > end_of_buf) { +			cifs_dbg(VFS, "malformed search entry would overflow\n"); +			break; +		} + +		len = le32_to_cpu(entryptr->FileNameLength); +		if ((char *)entryptr + len + size > end_of_buf) { +			cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", +				 end_of_buf); +			break; +		} + +		*lastentry = (char *)entryptr; +		entrycount++; + +		next_offset = le32_to_cpu(entryptr->NextEntryOffset); +		if (!next_offset) +			break; +	} + +	return entrycount; +} + +/* + * Readdir/FindFirst + */ +int +SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, +		     u64 persistent_fid, u64 volatile_fid, int index, +		     struct cifs_search_info *srch_inf) +{ +	struct smb2_query_directory_req *req; +	struct smb2_query_directory_rsp *rsp = NULL; +	struct kvec iov[2]; +	int rc = 0; +	int len; +	int resp_buftype; +	unsigned char *bufptr; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; +	__le16 asteriks = cpu_to_le16('*'); +	char *end_of_smb; +	unsigned int output_size = CIFSMaxBufSize; +	size_t info_buf_size; + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	rc = small_smb2_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req); +	if (rc) +		return rc; + +	switch (srch_inf->info_level) { +	case SMB_FIND_FILE_DIRECTORY_INFO: +		req->FileInformationClass = FILE_DIRECTORY_INFORMATION; +		info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; +		break; +	case SMB_FIND_FILE_ID_FULL_DIR_INFO: +		req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; +		info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; +		break; +	default: +		cifs_dbg(VFS, "info level %u isn't supported\n", +			 srch_inf->info_level); +		rc = -EINVAL; +		goto qdir_exit; +	} + +	req->FileIndex = cpu_to_le32(index); +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; + +	len = 0x2; +	bufptr = req->Buffer; +	memcpy(bufptr, &asteriks, len); + +	req->FileNameOffset = +		cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1 - 4); +	req->FileNameLength = cpu_to_le16(len); +	/* +	 * BB could be 30 bytes or so longer if we used SMB2 specific +	 * buffer lengths, but this is safe and close enough. +	 */ +	output_size = min_t(unsigned int, output_size, server->maxBuf); +	output_size = min_t(unsigned int, output_size, 2 << 15); +	req->OutputBufferLength = cpu_to_le32(output_size); + +	iov[0].iov_base = (char *)req; +	/* 4 for RFC1001 length and 1 for Buffer */ +	iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + +	iov[1].iov_base = (char *)(req->Buffer); +	iov[1].iov_len = len; + +	inc_rfc1001_len(req, len - 1 /* Buffer */); + +	rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0); +	rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base; + +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); +		goto qdir_exit; +	} + +	rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset), +			  le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr, +			  info_buf_size); +	if (rc) +		goto qdir_exit; + +	srch_inf->unicode = true; + +	if (srch_inf->ntwrk_buf_start) { +		if (srch_inf->smallBuf) +			cifs_small_buf_release(srch_inf->ntwrk_buf_start); +		else +			cifs_buf_release(srch_inf->ntwrk_buf_start); +	} +	srch_inf->ntwrk_buf_start = (char *)rsp; +	srch_inf->srch_entries_start = srch_inf->last_entry = 4 /* rfclen */ + +		(char *)&rsp->hdr + le16_to_cpu(rsp->OutputBufferOffset); +	/* 4 for rfc1002 length field */ +	end_of_smb = get_rfc1002_length(rsp) + 4 + (char *)&rsp->hdr; +	srch_inf->entries_in_buffer = +			num_entries(srch_inf->srch_entries_start, end_of_smb, +				    &srch_inf->last_entry, info_buf_size); +	srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; +	cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", +		 srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, +		 srch_inf->srch_entries_start, srch_inf->last_entry); +	if (resp_buftype == CIFS_LARGE_BUFFER) +		srch_inf->smallBuf = false; +	else if (resp_buftype == CIFS_SMALL_BUFFER) +		srch_inf->smallBuf = true; +	else +		cifs_dbg(VFS, "illegal search buffer type\n"); + +	if (rsp->hdr.Status == STATUS_NO_MORE_FILES) +		srch_inf->endOfSearch = 1; +	else +		srch_inf->endOfSearch = 0; + +	return rc; + +qdir_exit: +	free_rsp_buf(resp_buftype, rsp); +	return rc; +} + +static int +send_set_info(const unsigned int xid, struct cifs_tcon *tcon, +	       u64 persistent_fid, u64 volatile_fid, u32 pid, int info_class, +	       unsigned int num, void **data, unsigned int *size) +{ +	struct smb2_set_info_req *req; +	struct smb2_set_info_rsp *rsp = NULL; +	struct kvec *iov; +	int rc = 0; +	int resp_buftype; +	unsigned int i; +	struct TCP_Server_Info *server; +	struct cifs_ses *ses = tcon->ses; + +	if (ses && (ses->server)) +		server = ses->server; +	else +		return -EIO; + +	if (!num) +		return -EINVAL; + +	iov = kmalloc(sizeof(struct kvec) * num, GFP_KERNEL); +	if (!iov) +		return -ENOMEM; + +	rc = small_smb2_init(SMB2_SET_INFO, tcon, (void **) &req); +	if (rc) { +		kfree(iov); +		return rc; +	} + +	req->hdr.ProcessId = cpu_to_le32(pid); + +	req->InfoType = SMB2_O_INFO_FILE; +	req->FileInfoClass = info_class; +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; + +	/* 4 for RFC1001 length and 1 for Buffer */ +	req->BufferOffset = +			cpu_to_le16(sizeof(struct smb2_set_info_req) - 1 - 4); +	req->BufferLength = cpu_to_le32(*size); + +	inc_rfc1001_len(req, *size - 1 /* Buffer */); + +	memcpy(req->Buffer, *data, *size); + +	iov[0].iov_base = (char *)req; +	/* 4 for RFC1001 length */ +	iov[0].iov_len = get_rfc1002_length(req) + 4; + +	for (i = 1; i < num; i++) { +		inc_rfc1001_len(req, size[i]); +		le32_add_cpu(&req->BufferLength, size[i]); +		iov[i].iov_base = (char *)data[i]; +		iov[i].iov_len = size[i]; +	} + +	rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0); +	rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; + +	if (rc != 0) +		cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); + +	free_rsp_buf(resp_buftype, rsp); +	kfree(iov); +	return rc; +} + +int +SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, +	    u64 persistent_fid, u64 volatile_fid, __le16 *target_file) +{ +	struct smb2_file_rename_info info; +	void **data; +	unsigned int size[2]; +	int rc; +	int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX)); + +	data = kmalloc(sizeof(void *) * 2, GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	info.ReplaceIfExists = 1; /* 1 = replace existing target with new */ +			      /* 0 = fail if target already exists */ +	info.RootDirectory = 0;  /* MBZ for network ops (why does spec say?) */ +	info.FileNameLength = cpu_to_le32(len); + +	data[0] = &info; +	size[0] = sizeof(struct smb2_file_rename_info); + +	data[1] = target_file; +	size[1] = len + 2 /* null */; + +	rc = send_set_info(xid, tcon, persistent_fid, volatile_fid, +			   current->tgid, FILE_RENAME_INFORMATION, 2, data, +			   size); +	kfree(data); +	return rc; +} + +int +SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, +		  u64 persistent_fid, u64 volatile_fid, __le16 *target_file) +{ +	struct smb2_file_link_info info; +	void **data; +	unsigned int size[2]; +	int rc; +	int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX)); + +	data = kmalloc(sizeof(void *) * 2, GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	info.ReplaceIfExists = 0; /* 1 = replace existing link with new */ +			      /* 0 = fail if link already exists */ +	info.RootDirectory = 0;  /* MBZ for network ops (why does spec say?) */ +	info.FileNameLength = cpu_to_le32(len); + +	data[0] = &info; +	size[0] = sizeof(struct smb2_file_link_info); + +	data[1] = target_file; +	size[1] = len + 2 /* null */; + +	rc = send_set_info(xid, tcon, persistent_fid, volatile_fid, +			   current->tgid, FILE_LINK_INFORMATION, 2, data, size); +	kfree(data); +	return rc; +} + +int +SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, +	     u64 volatile_fid, u32 pid, __le64 *eof) +{ +	struct smb2_file_eof_info info; +	void *data; +	unsigned int size; + +	info.EndOfFile = *eof; + +	data = &info; +	size = sizeof(struct smb2_file_eof_info); + +	return send_set_info(xid, tcon, persistent_fid, volatile_fid, pid, +			     FILE_END_OF_FILE_INFORMATION, 1, &data, &size); +} + +int +SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon, +	      u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf) +{ +	unsigned int size; +	size = sizeof(FILE_BASIC_INFO); +	return send_set_info(xid, tcon, persistent_fid, volatile_fid, +			     current->tgid, FILE_BASIC_INFORMATION, 1, +			     (void **)&buf, &size); +} + +int +SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, +		  const u64 persistent_fid, const u64 volatile_fid, +		  __u8 oplock_level) +{ +	int rc; +	struct smb2_oplock_break *req = NULL; + +	cifs_dbg(FYI, "SMB2_oplock_break\n"); +	rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req); + +	if (rc) +		return rc; + +	req->VolatileFid = volatile_fid; +	req->PersistentFid = persistent_fid; +	req->OplockLevel = oplock_level; +	req->hdr.CreditRequest = cpu_to_le16(1); + +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP); +	/* SMB2 buffer freed by function above */ + +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); +		cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); +	} + +	return rc; +} + +static void +copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, +			struct kstatfs *kst) +{ +	kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * +			  le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); +	kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); +	kst->f_bfree  = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits); +	kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); +	return; +} + +static int +build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, +		   int outbuf_len, u64 persistent_fid, u64 volatile_fid) +{ +	int rc; +	struct smb2_query_info_req *req; + +	cifs_dbg(FYI, "Query FSInfo level %d\n", level); + +	if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) +		return -EIO; + +	rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req); +	if (rc) +		return rc; + +	req->InfoType = SMB2_O_INFO_FILESYSTEM; +	req->FileInfoClass = level; +	req->PersistentFileId = persistent_fid; +	req->VolatileFileId = volatile_fid; +	/* 4 for rfc1002 length field and 1 for pad */ +	req->InputBufferOffset = +			cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4); +	req->OutputBufferLength = cpu_to_le32( +		outbuf_len + sizeof(struct smb2_query_info_rsp) - 1 - 4); + +	iov->iov_base = (char *)req; +	/* 4 for rfc1002 length field */ +	iov->iov_len = get_rfc1002_length(req) + 4; +	return 0; +} + +int +SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, +	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) +{ +	struct smb2_query_info_rsp *rsp = NULL; +	struct kvec iov; +	int rc = 0; +	int resp_buftype; +	struct cifs_ses *ses = tcon->ses; +	struct smb2_fs_full_size_info *info = NULL; + +	rc = build_qfs_info_req(&iov, tcon, FS_FULL_SIZE_INFORMATION, +				sizeof(struct smb2_fs_full_size_info), +				persistent_fid, volatile_fid); +	if (rc) +		return rc; + +	rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); +		goto qfsinf_exit; +	} +	rsp = (struct smb2_query_info_rsp *)iov.iov_base; + +	info = (struct smb2_fs_full_size_info *)(4 /* RFC1001 len */ + +		le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr); +	rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset), +			  le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr, +			  sizeof(struct smb2_fs_full_size_info)); +	if (!rc) +		copy_fs_info_to_kstatfs(info, fsdata); + +qfsinf_exit: +	free_rsp_buf(resp_buftype, iov.iov_base); +	return rc; +} + +int +SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, +	      u64 persistent_fid, u64 volatile_fid, int level) +{ +	struct smb2_query_info_rsp *rsp = NULL; +	struct kvec iov; +	int rc = 0; +	int resp_buftype, max_len, min_len; +	struct cifs_ses *ses = tcon->ses; +	unsigned int rsp_len, offset; + +	if (level == FS_DEVICE_INFORMATION) { +		max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); +		min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); +	} else if (level == FS_ATTRIBUTE_INFORMATION) { +		max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); +		min_len = MIN_FS_ATTR_INFO_SIZE; +	} else if (level == FS_SECTOR_SIZE_INFORMATION) { +		max_len = sizeof(struct smb3_fs_ss_info); +		min_len = sizeof(struct smb3_fs_ss_info); +	} else { +		cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); +		return -EINVAL; +	} + +	rc = build_qfs_info_req(&iov, tcon, level, max_len, +				persistent_fid, volatile_fid); +	if (rc) +		return rc; + +	rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); +		goto qfsattr_exit; +	} +	rsp = (struct smb2_query_info_rsp *)iov.iov_base; + +	rsp_len = le32_to_cpu(rsp->OutputBufferLength); +	offset = le16_to_cpu(rsp->OutputBufferOffset); +	rc = validate_buf(offset, rsp_len, &rsp->hdr, min_len); +	if (rc) +		goto qfsattr_exit; + +	if (level == FS_ATTRIBUTE_INFORMATION) +		memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset +			+ (char *)&rsp->hdr, min_t(unsigned int, +			rsp_len, max_len)); +	else if (level == FS_DEVICE_INFORMATION) +		memcpy(&tcon->fsDevInfo, 4 /* RFC1001 len */ + offset +			+ (char *)&rsp->hdr, sizeof(FILE_SYSTEM_DEVICE_INFO)); +	else if (level == FS_SECTOR_SIZE_INFORMATION) { +		struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) +			(4 /* RFC1001 len */ + offset + (char *)&rsp->hdr); +		tcon->ss_flags = le32_to_cpu(ss_info->Flags); +		tcon->perf_sector_size = +			le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); +	} + +qfsattr_exit: +	free_rsp_buf(resp_buftype, iov.iov_base); +	return rc; +} + +int +smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, +	   const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, +	   const __u32 num_lock, struct smb2_lock_element *buf) +{ +	int rc = 0; +	struct smb2_lock_req *req = NULL; +	struct kvec iov[2]; +	int resp_buf_type; +	unsigned int count; + +	cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); + +	rc = small_smb2_init(SMB2_LOCK, tcon, (void **) &req); +	if (rc) +		return rc; + +	req->hdr.ProcessId = cpu_to_le32(pid); +	req->LockCount = cpu_to_le16(num_lock); + +	req->PersistentFileId = persist_fid; +	req->VolatileFileId = volatile_fid; + +	count = num_lock * sizeof(struct smb2_lock_element); +	inc_rfc1001_len(req, count - sizeof(struct smb2_lock_element)); + +	iov[0].iov_base = (char *)req; +	/* 4 for rfc1002 length field and count for all locks */ +	iov[0].iov_len = get_rfc1002_length(req) + 4 - count; +	iov[1].iov_base = (char *)buf; +	iov[1].iov_len = count; + +	cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); +	rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP); +	if (rc) { +		cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); +		cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); +	} + +	return rc; +} + +int +SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, +	  const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, +	  const __u64 length, const __u64 offset, const __u32 lock_flags, +	  const bool wait) +{ +	struct smb2_lock_element lock; + +	lock.Offset = cpu_to_le64(offset); +	lock.Length = cpu_to_le64(length); +	lock.Flags = cpu_to_le32(lock_flags); +	if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) +		lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); + +	return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); +} + +int +SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, +		 __u8 *lease_key, const __le32 lease_state) +{ +	int rc; +	struct smb2_lease_ack *req = NULL; + +	cifs_dbg(FYI, "SMB2_lease_break\n"); +	rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req); + +	if (rc) +		return rc; + +	req->hdr.CreditRequest = cpu_to_le16(1); +	req->StructureSize = cpu_to_le16(36); +	inc_rfc1001_len(req, 12); + +	memcpy(req->LeaseKey, lease_key, 16); +	req->LeaseState = lease_state; + +	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP); +	/* SMB2 buffer freed by function above */ + +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); +		cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); +	} + +	return rc; +} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h new file mode 100644 index 00000000000..69f3595d395 --- /dev/null +++ b/fs/cifs/smb2pdu.h @@ -0,0 +1,1053 @@ +/* + *   fs/cifs/smb2pdu.h + * + *   Copyright (c) International Business Machines  Corp., 2009, 2013 + *                 Etersoft, 2012 + *   Author(s): Steve French (sfrench@us.ibm.com) + *              Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include <net/sock.h> + +/* + * Note that, due to trying to use names similar to the protocol specifications, + * there are many mixed case field names in the structures below.  Although + * this does not match typical Linux kernel style, it is necessary to be + * be able to match against the protocol specfication. + * + * SMB2 commands + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie no useful data other than the SMB error code itself) and are marked such. + * Knowing this helps avoid response buffer allocations and copy in some cases. + */ + +/* List of commands in host endian */ +#define SMB2_NEGOTIATE_HE	0x0000 +#define SMB2_SESSION_SETUP_HE	0x0001 +#define SMB2_LOGOFF_HE		0x0002 /* trivial request/resp */ +#define SMB2_TREE_CONNECT_HE	0x0003 +#define SMB2_TREE_DISCONNECT_HE	0x0004 /* trivial req/resp */ +#define SMB2_CREATE_HE		0x0005 +#define SMB2_CLOSE_HE		0x0006 +#define SMB2_FLUSH_HE		0x0007 /* trivial resp */ +#define SMB2_READ_HE		0x0008 +#define SMB2_WRITE_HE		0x0009 +#define SMB2_LOCK_HE		0x000A +#define SMB2_IOCTL_HE		0x000B +#define SMB2_CANCEL_HE		0x000C +#define SMB2_ECHO_HE		0x000D +#define SMB2_QUERY_DIRECTORY_HE	0x000E +#define SMB2_CHANGE_NOTIFY_HE	0x000F +#define SMB2_QUERY_INFO_HE	0x0010 +#define SMB2_SET_INFO_HE	0x0011 +#define SMB2_OPLOCK_BREAK_HE	0x0012 + +/* The same list in little endian */ +#define SMB2_NEGOTIATE		cpu_to_le16(SMB2_NEGOTIATE_HE) +#define SMB2_SESSION_SETUP	cpu_to_le16(SMB2_SESSION_SETUP_HE) +#define SMB2_LOGOFF		cpu_to_le16(SMB2_LOGOFF_HE) +#define SMB2_TREE_CONNECT	cpu_to_le16(SMB2_TREE_CONNECT_HE) +#define SMB2_TREE_DISCONNECT	cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +#define SMB2_CREATE		cpu_to_le16(SMB2_CREATE_HE) +#define SMB2_CLOSE		cpu_to_le16(SMB2_CLOSE_HE) +#define SMB2_FLUSH		cpu_to_le16(SMB2_FLUSH_HE) +#define SMB2_READ		cpu_to_le16(SMB2_READ_HE) +#define SMB2_WRITE		cpu_to_le16(SMB2_WRITE_HE) +#define SMB2_LOCK		cpu_to_le16(SMB2_LOCK_HE) +#define SMB2_IOCTL		cpu_to_le16(SMB2_IOCTL_HE) +#define SMB2_CANCEL		cpu_to_le16(SMB2_CANCEL_HE) +#define SMB2_ECHO		cpu_to_le16(SMB2_ECHO_HE) +#define SMB2_QUERY_DIRECTORY	cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +#define SMB2_CHANGE_NOTIFY	cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +#define SMB2_QUERY_INFO		cpu_to_le16(SMB2_QUERY_INFO_HE) +#define SMB2_SET_INFO		cpu_to_le16(SMB2_SET_INFO_HE) +#define SMB2_OPLOCK_BREAK	cpu_to_le16(SMB2_OPLOCK_BREAK_HE) + +#define NUMBER_OF_SMB2_COMMANDS	0x0013 + +/* BB FIXME - analyze following length BB */ +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ + +#define SMB2_PROTO_NUMBER __constant_cpu_to_le32(0x424d53fe) + +/* + * SMB2 Header Definition + * + * "MBZ" :  Must be Zero + * "BB"  :  BugBug, Something to check/review/analyze later + * "PDU" :  "Protocol Data Unit" (ie a network "frame") + * + */ + +#define SMB2_HEADER_STRUCTURE_SIZE __constant_cpu_to_le16(64) + +struct smb2_hdr { +	__be32 smb2_buf_length;	/* big endian on wire */ +				/* length is only two or three bytes - with +				 one or two byte type preceding it that MBZ */ +	__u8   ProtocolId[4];	/* 0xFE 'S' 'M' 'B' */ +	__le16 StructureSize;	/* 64 */ +	__le16 CreditCharge;	/* MBZ */ +	__le32 Status;		/* Error from server */ +	__le16 Command; +	__le16 CreditRequest;  /* CreditResponse */ +	__le32 Flags; +	__le32 NextCommand; +	__u64  MessageId;	/* opaque - so can stay little endian */ +	__le32 ProcessId; +	__u32  TreeId;		/* opaque - so do not make little endian */ +	__u64  SessionId;	/* opaque - so do not make little endian */ +	__u8   Signature[16]; +} __packed; + +struct smb2_pdu { +	struct smb2_hdr hdr; +	__le16 StructureSize2; /* size of wct area (varies, request specific) */ +} __packed; + +struct smb2_transform_hdr { +	__be32 smb2_buf_length;	/* big endian on wire */ +				/* length is only two or three bytes - with +				 one or two byte type preceding it that MBZ */ +	__u8   ProtocolId[4];	/* 0xFD 'S' 'M' 'B' */ +	__u8   Signature[16]; +	__u8   Nonce[11]; +	__u8   Reserved[5]; +	__le32 OriginalMessageSize; +	__u16  Reserved1; +	__le16 EncryptionAlgorithm; +	__u64  SessionId; +} __packed; + +/* Encryption Algorithms */ +#define SMB2_ENCRYPTION_AES128_CCM	__constant_cpu_to_le16(0x0001) + +/* + *	SMB2 flag definitions + */ +#define SMB2_FLAGS_SERVER_TO_REDIR	__constant_cpu_to_le32(0x00000001) +#define SMB2_FLAGS_ASYNC_COMMAND	__constant_cpu_to_le32(0x00000002) +#define SMB2_FLAGS_RELATED_OPERATIONS	__constant_cpu_to_le32(0x00000004) +#define SMB2_FLAGS_SIGNED		__constant_cpu_to_le32(0x00000008) +#define SMB2_FLAGS_DFS_OPERATIONS	__constant_cpu_to_le32(0x10000000) + +/* + *	Definitions for SMB2 Protocol Data Units (network frames) + * + *  See MS-SMB2.PDF specification for protocol details. + *  The Naming convention is the lower case version of the SMB2 + *  command code name for the struct. Note that structures must be packed. + * + */ + +#define SMB2_ERROR_STRUCTURE_SIZE2 __constant_cpu_to_le16(9) + +struct smb2_err_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; +	__le16 Reserved; /* MBZ */ +	__le32 ByteCount;  /* even if zero, at least one byte follows */ +	__u8   ErrorData[1];  /* variable length */ +} __packed; + +struct smb2_symlink_err_rsp { +	__le32 SymLinkLength; +	__le32 SymLinkErrorTag; +	__le32 ReparseTag; +	__le16 ReparseDataLength; +	__le16 UnparsedPathLength; +	__le16 SubstituteNameOffset; +	__le16 SubstituteNameLength; +	__le16 PrintNameOffset; +	__le16 PrintNameLength; +	__le32 Flags; +	__u8  PathBuffer[0]; +} __packed; + +#define SMB2_CLIENT_GUID_SIZE 16 + +struct smb2_negotiate_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 36 */ +	__le16 DialectCount; +	__le16 SecurityMode; +	__le16 Reserved;	/* MBZ */ +	__le32 Capabilities; +	__u8   ClientGUID[SMB2_CLIENT_GUID_SIZE]; +	__le64 ClientStartTime;	/* MBZ */ +	__le16 Dialects[1]; /* One dialect (vers=) at a time for now */ +} __packed; + +/* Dialects */ +#define SMB20_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +#define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 +#define BAD_PROT_ID   0xFFFF + +/* SecurityMode flags */ +#define	SMB2_NEGOTIATE_SIGNING_ENABLED	0x0001 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED	0x0002 +/* Capabilities flags */ +#define SMB2_GLOBAL_CAP_DFS		0x00000001 +#define SMB2_GLOBAL_CAP_LEASING		0x00000002 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU	0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_MULTI_CHANNEL	0x00000008 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING  0x00000020 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_ENCRYPTION	0x00000040 /* New to SMB3 */ +/* Internal types */ +#define SMB2_NT_FIND			0x00100000 +#define SMB2_LARGE_FILES		0x00200000 + +struct smb2_negotiate_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 65 */ +	__le16 SecurityMode; +	__le16 DialectRevision; +	__le16 Reserved;	/* MBZ */ +	__u8   ServerGUID[16]; +	__le32 Capabilities; +	__le32 MaxTransactSize; +	__le32 MaxReadSize; +	__le32 MaxWriteSize; +	__le64 SystemTime;	/* MBZ */ +	__le64 ServerStartTime; +	__le16 SecurityBufferOffset; +	__le16 SecurityBufferLength; +	__le32 Reserved2;	/* may be any value, ignore */ +	__u8   Buffer[1];	/* variable length GSS security buffer */ +} __packed; + +struct smb2_sess_setup_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 25 */ +	__u8   VcNumber; +	__u8   SecurityMode; +	__le32 Capabilities; +	__le32 Channel; +	__le16 SecurityBufferOffset; +	__le16 SecurityBufferLength; +	__le64 PreviousSessionId; +	__u8   Buffer[1];	/* variable length GSS security buffer */ +} __packed; + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST	0x0001 +#define SMB2_SESSION_FLAG_IS_NULL	0x0002 +#define SMB2_SESSION_FLAG_ENCRYPT_DATA	0x0004 +struct smb2_sess_setup_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 9 */ +	__le16 SessionFlags; +	__le16 SecurityBufferOffset; +	__le16 SecurityBufferLength; +	__u8   Buffer[1];	/* variable length GSS security buffer */ +} __packed; + +struct smb2_logoff_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 4 */ +	__le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 4 */ +	__le16 Reserved; +} __packed; + +struct smb2_tree_connect_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 9 */ +	__le16 Reserved; +	__le16 PathOffset; +	__le16 PathLength; +	__u8   Buffer[1];	/* variable length */ +} __packed; + +struct smb2_tree_connect_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 16 */ +	__u8   ShareType;  /* see below */ +	__u8   Reserved; +	__le32 ShareFlags; /* see below */ +	__le32 Capabilities; /* see below */ +	__le32 MaximalAccess; +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK	0x01 +#define SMB2_SHARE_TYPE_PIPE	0x02 +#define	SMB2_SHARE_TYPE_PRINT	0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING			0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING			0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING			0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING			0x00000030 +#define SHI1005_FLAGS_DFS				0x00000001 +#define SHI1005_FLAGS_DFS_ROOT				0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS		0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE		0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING		0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM	0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK		0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH_V1			0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V2			0x00004000 +#define SHI1005_FLAGS_ENCRYPT_DATA			0x00008000 +#define SHI1005_FLAGS_ALL				0x0000FF33 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS	cpu_to_le32(0x00000008) /* all dialects */ +#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ +#define SMB2_SHARE_CAP_SCALEOUT	cpu_to_le32(0x00000020) /* 3.0 */ +#define SMB2_SHARE_CAP_CLUSTER	cpu_to_le32(0x00000040) /* 3.0 */ +#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ + +struct smb2_tree_disconnect_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 4 */ +	__le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 4 */ +	__le16 Reserved; +} __packed; + +/* File Attrubutes */ +#define FILE_ATTRIBUTE_READONLY			0x00000001 +#define FILE_ATTRIBUTE_HIDDEN			0x00000002 +#define FILE_ATTRIBUTE_SYSTEM			0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY		0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE			0x00000020 +#define FILE_ATTRIBUTE_NORMAL			0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY		0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE		0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT		0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED		0x00000800 +#define FILE_ATTRIBUTE_OFFLINE			0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED	0x00002000 +#define FILE_ATTRIBUTE_ENCRYPTED		0x00004000 + +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE		0x00 +#define SMB2_OPLOCK_LEVEL_II		0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE	0x08 +#define SMB2_OPLOCK_LEVEL_BATCH		0x09 +#define SMB2_OPLOCK_LEVEL_LEASE		0xFF +/* Non-spec internal type */ +#define SMB2_OPLOCK_LEVEL_NOCHANGE	0x99 + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE		cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE		cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE		cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE			cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE		cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE			cpu_to_le32(0x00000020) +#define FILE_READ_ATTRIBUTES_LE		cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE	cpu_to_le32(0x00000100) +#define FILE_DELETE_LE			cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE		cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE		cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE		cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE		cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE	cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE		cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE		cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE		cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE		cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE		cpu_to_le32(0x80000000) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE		cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE		cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE		cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE		cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE		cpu_to_le32(0x00000000) +#define FILE_OPEN_LE			cpu_to_le32(0x00000001) +#define FILE_CREATE_LE			cpu_to_le32(0x00000002) +#define	FILE_OPEN_IF_LE			cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE		cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE		cpu_to_le32(0x00000005) + +/* CreateOptions Flags */ +#define FILE_DIRECTORY_FILE_LE		cpu_to_le32(0x00000001) +/* same as #define CREATE_NOT_FILE_LE	cpu_to_le32(0x00000001) */ +#define FILE_WRITE_THROUGH_LE		cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE		cpu_to_le32(0x00000004) +#define FILE_NO_INTERMEDIATE_BUFFERRING_LE cpu_to_le32(0x00000008) +#define FILE_SYNCHRONOUS_IO_ALERT_LE	cpu_to_le32(0x00000010) +#define FILE_SYNCHRONOUS_IO_NON_ALERT_LE	cpu_to_le32(0x00000020) +#define FILE_NON_DIRECTORY_FILE_LE	cpu_to_le32(0x00000040) +#define FILE_COMPLETE_IF_OPLOCKED_LE	cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE		cpu_to_le32(0x00000200) +#define FILE_RANDOM_ACCESS_LE		cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE		cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE		cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE	cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE		cpu_to_le32(0x00008000) +#define FILE_RESERVE_OPFILTER_LE	cpu_to_le32(0x00100000) +#define FILE_OPEN_REPARSE_POINT_LE	cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE		cpu_to_le32(0x00400000) +#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) + +#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ +			| FILE_READ_ATTRIBUTES_LE) +#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \ +			| FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) +#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) + +/* Impersonation Levels */ +#define IL_ANONYMOUS		cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION	cpu_to_le32(0x00000001) +#define IL_IMPERSONATION	cpu_to_le32(0x00000002) +#define IL_DELEGATE		cpu_to_le32(0x00000003) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER			"ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER			"SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST	"DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT	"DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE		"AISi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST		"TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID		"QFid" +#define SMB2_CREATE_REQUEST_LEASE		"RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2	"DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2	"DH2C" +#define SMB2_CREATE_APP_INSTANCE_ID	0x45BCA66AEFA7F74A9008FA462E144D74 +#define SVHDX_OPEN_DEVICE_CONTEXT	0x83CE6F1AD851E0986E34401CC9BCFCE9 + +struct smb2_create_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 57 */ +	__u8   SecurityFlags; +	__u8   RequestedOplockLevel; +	__le32 ImpersonationLevel; +	__le64 SmbCreateFlags; +	__le64 Reserved; +	__le32 DesiredAccess; +	__le32 FileAttributes; +	__le32 ShareAccess; +	__le32 CreateDisposition; +	__le32 CreateOptions; +	__le16 NameOffset; +	__le16 NameLength; +	__le32 CreateContextsOffset; +	__le32 CreateContextsLength; +	__u8   Buffer[0]; +} __packed; + +struct smb2_create_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 89 */ +	__u8   OplockLevel; +	__u8   Reserved; +	__le32 CreateAction; +	__le64 CreationTime; +	__le64 LastAccessTime; +	__le64 LastWriteTime; +	__le64 ChangeTime; +	__le64 AllocationSize; +	__le64 EndofFile; +	__le32 FileAttributes; +	__le32 Reserved2; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__le32 CreateContextsOffset; +	__le32 CreateContextsLength; +	__u8   Buffer[1]; +} __packed; + +struct create_context { +	__le32 Next; +	__le16 NameOffset; +	__le16 NameLength; +	__le16 Reserved; +	__le16 DataOffset; +	__le32 DataLength; +	__u8 Buffer[0]; +} __packed; + +#define SMB2_LEASE_READ_CACHING_HE	0x01 +#define SMB2_LEASE_HANDLE_CACHING_HE	0x02 +#define SMB2_LEASE_WRITE_CACHING_HE	0x04 + +#define SMB2_LEASE_NONE			__constant_cpu_to_le32(0x00) +#define SMB2_LEASE_READ_CACHING		__constant_cpu_to_le32(0x01) +#define SMB2_LEASE_HANDLE_CACHING	__constant_cpu_to_le32(0x02) +#define SMB2_LEASE_WRITE_CACHING	__constant_cpu_to_le32(0x04) + +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x02) + +#define SMB2_LEASE_KEY_SIZE 16 + +struct lease_context { +	__le64 LeaseKeyLow; +	__le64 LeaseKeyHigh; +	__le32 LeaseState; +	__le32 LeaseFlags; +	__le64 LeaseDuration; +} __packed; + +struct lease_context_v2 { +	__le64 LeaseKeyLow; +	__le64 LeaseKeyHigh; +	__le32 LeaseState; +	__le32 LeaseFlags; +	__le64 LeaseDuration; +	__le64 ParentLeaseKeyLow; +	__le64 ParentLeaseKeyHigh; +	__le16 Epoch; +	__le16 Reserved; +} __packed; + +struct create_lease { +	struct create_context ccontext; +	__u8   Name[8]; +	struct lease_context lcontext; +} __packed; + +struct create_lease_v2 { +	struct create_context ccontext; +	__u8   Name[8]; +	struct lease_context_v2 lcontext; +	__u8   Pad[4]; +} __packed; + +struct create_durable { +	struct create_context ccontext; +	__u8   Name[8]; +	union { +		__u8  Reserved[16]; +		struct { +			__u64 PersistentFileId; +			__u64 VolatileFileId; +		} Fid; +	} Data; +} __packed; + +#define COPY_CHUNK_RES_KEY_SIZE	24 +struct resume_key_req { +	char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; +	__le32	ContextLength;	/* MBZ */ +	char	Context[0];	/* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl { +	char SourceKey[COPY_CHUNK_RES_KEY_SIZE]; +	__le32 ChunkCount; /* we are only sending 1 */ +	__le32 Reserved; +	/* array will only be one chunk long for us */ +	__le64 SourceOffset; +	__le64 TargetOffset; +	__le32 Length; /* how many bytes to copy */ +	__u32 Reserved2; +} __packed; + +struct copychunk_ioctl_rsp { +	__le32 ChunksWritten; +	__le32 ChunkBytesWritten; +	__le32 TotalBytesWritten; +} __packed; + +struct validate_negotiate_info_req { +	__le32 Capabilities; +	__u8   Guid[SMB2_CLIENT_GUID_SIZE]; +	__le16 SecurityMode; +	__le16 DialectCount; +	__le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ +} __packed; + +struct validate_negotiate_info_rsp { +	__le32 Capabilities; +	__u8   Guid[SMB2_CLIENT_GUID_SIZE]; +	__le16 SecurityMode; +	__le16 Dialect; /* Dialect in use for the connection */ +} __packed; + +#define RSS_CAPABLE	0x00000001 +#define RDMA_CAPABLE	0x00000002 + +struct network_interface_info_ioctl_rsp { +	__le32 Next; /* next interface. zero if this is last one */ +	__le32 IfIndex; +	__le32 Capability; /* RSS or RDMA Capable */ +	__le32 Reserved; +	__le64 LinkSpeed; +	char	SockAddr_Storage[128]; +} __packed; + +#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ + +struct compress_ioctl { +	__le16 CompressionState; /* See cifspdu.h for possible flag values */ +} __packed; + +struct smb2_ioctl_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 57 */ +	__u16 Reserved; +	__le32 CtlCode; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__le32 InputOffset; +	__le32 InputCount; +	__le32 MaxInputResponse; +	__le32 OutputOffset; +	__le32 OutputCount; +	__le32 MaxOutputResponse; +	__le32 Flags; +	__u32  Reserved2; +	__u8   Buffer[0]; +} __packed; + +struct smb2_ioctl_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 57 */ +	__u16 Reserved; +	__le32 CtlCode; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__le32 InputOffset; +	__le32 InputCount; +	__le32 OutputOffset; +	__le32 OutputCount; +	__le32 Flags; +	__u32  Reserved2; +	/* char * buffer[] */ +} __packed; + +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB	cpu_to_le16(0x0001) +struct smb2_close_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 24 */ +	__le16 Flags; +	__le32 Reserved; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +} __packed; + +struct smb2_close_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* 60 */ +	__le16 Flags; +	__le32 Reserved; +	__le64 CreationTime; +	__le64 LastAccessTime; +	__le64 LastWriteTime; +	__le64 ChangeTime; +	__le64 AllocationSize;	/* Beginning of FILE_STANDARD_INFO equivalent */ +	__le64 EndOfFile; +	__le32 Attributes; +} __packed; + +struct smb2_flush_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 24 */ +	__le16 Reserved1; +	__le32 Reserved2; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +} __packed; + +struct smb2_flush_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; +	__le16 Reserved; +} __packed; + +/* For read request Flags field below, following flag is defined for SMB3.02 */ +#define SMB2_READFLAG_READ_UNBUFFERED	0x01 + +/* Channel field for read and write: exactly one of following flags can be set*/ +#define SMB2_CHANNEL_NONE		0x00000000 +#define SMB2_CHANNEL_RDMA_V1		0x00000001 /* SMB3 or later */ +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE 0x00000001 /* SMB3.02 or later */ + +struct smb2_read_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 49 */ +	__u8   Padding; /* offset from start of SMB2 header to place read */ +	__u8   Flags; /* MBZ unless SMB3.02 or later */ +	__le32 Length; +	__le64 Offset; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__le32 MinimumCount; +	__le32 Channel; /* MBZ except for SMB3 or later */ +	__le32 RemainingBytes; +	__le16 ReadChannelInfoOffset; /* Reserved MBZ */ +	__le16 ReadChannelInfoLength; /* Reserved MBZ */ +	__u8   Buffer[1]; +} __packed; + +struct smb2_read_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 17 */ +	__u8   DataOffset; +	__u8   Reserved; +	__le32 DataLength; +	__le32 DataRemaining; +	__u32  Reserved2; +	__u8   Buffer[1]; +} __packed; + +/* For write request Flags field below the following flags are defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH	0x00000001	/* SMB2.1 or later */ +#define SMB2_WRITEFLAG_WRITE_UNBUFFERED	0x00000002	/* SMB3.02 or later */ + +struct smb2_write_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 49 */ +	__le16 DataOffset; /* offset from start of SMB2 header to write data */ +	__le32 Length; +	__le64 Offset; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__le32 Channel; /* Reserved MBZ */ +	__le32 RemainingBytes; +	__le16 WriteChannelInfoOffset; /* Reserved MBZ */ +	__le16 WriteChannelInfoLength; /* Reserved MBZ */ +	__le32 Flags; +	__u8   Buffer[1]; +} __packed; + +struct smb2_write_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 17 */ +	__u8   DataOffset; +	__u8   Reserved; +	__le32 DataLength; +	__le32 DataRemaining; +	__u32  Reserved2; +	__u8   Buffer[1]; +} __packed; + +#define SMB2_LOCKFLAG_SHARED_LOCK	0x0001 +#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK	0x0002 +#define SMB2_LOCKFLAG_UNLOCK		0x0004 +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY	0x0010 + +struct smb2_lock_element { +	__le64 Offset; +	__le64 Length; +	__le32 Flags; +	__le32 Reserved; +} __packed; + +struct smb2_lock_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 48 */ +	__le16 LockCount; +	__le32 Reserved; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	/* Followed by at least one */ +	struct smb2_lock_element locks[1]; +} __packed; + +struct smb2_lock_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 4 */ +	__le16 Reserved; +} __packed; + +struct smb2_echo_req { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 4 */ +	__u16  Reserved; +} __packed; + +struct smb2_echo_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize;	/* Must be 4 */ +	__u16  Reserved; +} __packed; + +/* search (query_directory) Flags field */ +#define SMB2_RESTART_SCANS		0x01 +#define SMB2_RETURN_SINGLE_ENTRY	0x02 +#define SMB2_INDEX_SPECIFIED		0x04 +#define SMB2_REOPEN			0x10 + +struct smb2_query_directory_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 33 */ +	__u8   FileInformationClass; +	__u8   Flags; +	__le32 FileIndex; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__le16 FileNameOffset; +	__le16 FileNameLength; +	__le32 OutputBufferLength; +	__u8   Buffer[1]; +} __packed; + +struct smb2_query_directory_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 9 */ +	__le16 OutputBufferOffset; +	__le32 OutputBufferLength; +	__u8   Buffer[1]; +} __packed; + +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE	0x01 +#define SMB2_O_INFO_FILESYSTEM	0x02 +#define SMB2_O_INFO_SECURITY	0x03 +#define SMB2_O_INFO_QUOTA	0x04 + +struct smb2_query_info_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 41 */ +	__u8   InfoType; +	__u8   FileInfoClass; +	__le32 OutputBufferLength; +	__le16 InputBufferOffset; +	__u16  Reserved; +	__le32 InputBufferLength; +	__le32 AdditionalInformation; +	__le32 Flags; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__u8   Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 9 */ +	__le16 OutputBufferOffset; +	__le32 OutputBufferLength; +	__u8   Buffer[1]; +} __packed; + +struct smb2_set_info_req { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 33 */ +	__u8   InfoType; +	__u8   FileInfoClass; +	__le32 BufferLength; +	__le16 BufferOffset; +	__u16  Reserved; +	__le32 AdditionalInformation; +	__u64  PersistentFileId; /* opaque endianness */ +	__u64  VolatileFileId; /* opaque endianness */ +	__u8   Buffer[1]; +} __packed; + +struct smb2_set_info_rsp { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 2 */ +} __packed; + +struct smb2_oplock_break { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 24 */ +	__u8   OplockLevel; +	__u8   Reserved; +	__le32 Reserved2; +	__u64  PersistentFid; +	__u64  VolatileFid; +} __packed; + +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + +struct smb2_lease_break { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 44 */ +	__le16 Reserved; +	__le32 Flags; +	__u8   LeaseKey[16]; +	__le32 CurrentLeaseState; +	__le32 NewLeaseState; +	__le32 BreakReason; +	__le32 AccessMaskHint; +	__le32 ShareMaskHint; +} __packed; + +struct smb2_lease_ack { +	struct smb2_hdr hdr; +	__le16 StructureSize; /* Must be 36 */ +	__le16 Reserved; +	__le32 Flags; +	__u8   LeaseKey[16]; +	__le32 LeaseState; +	__le64 LeaseDuration; +} __packed; + +/* + *	PDU infolevel structure definitions + *	BB consider moving to a different header + */ + +/* File System Information Classes */ +#define FS_VOLUME_INFORMATION		1 /* Query */ +#define FS_LABEL_INFORMATION		2 /* Local only */ +#define FS_SIZE_INFORMATION		3 /* Query */ +#define FS_DEVICE_INFORMATION		4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION	5 /* Query */ +#define FS_CONTROL_INFORMATION		6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION	7 /* Query */ +#define FS_OBJECT_ID_INFORMATION	8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION	9 /* Local only */ +#define FS_VOLUME_FLAGS_INFORMATION	10 /* Local only */ +#define FS_SECTOR_SIZE_INFORMATION	11 /* SMB3 or later. Query */ + +struct smb2_fs_full_size_info { +	__le64 TotalAllocationUnits; +	__le64 CallerAvailableAllocationUnits; +	__le64 ActualAvailableAllocationUnits; +	__le32 SectorsPerAllocationUnit; +	__le32 BytesPerSector; +} __packed; + +#define SSINFO_FLAGS_ALIGNED_DEVICE		0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY		0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED		0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { +	__le32 LogicalBytesPerSector; +	__le32 PhysicalBytesPerSectorForAtomicity; +	__le32 PhysicalBytesPerSectorForPerf; +	__le32 FileSystemEffectivePhysicalBytesPerSectorForAtomicity; +	__le32 Flags; +	__le32 ByteOffsetForSectorAlignment; +	__le32 ByteOffsetForPartitionAlignment; +} __packed; + +/* partial list of QUERY INFO levels */ +#define FILE_DIRECTORY_INFORMATION	1 +#define FILE_FULL_DIRECTORY_INFORMATION 2 +#define FILE_BOTH_DIRECTORY_INFORMATION 3 +#define FILE_BASIC_INFORMATION		4 +#define FILE_STANDARD_INFORMATION	5 +#define FILE_INTERNAL_INFORMATION	6 +#define FILE_EA_INFORMATION	        7 +#define FILE_ACCESS_INFORMATION		8 +#define FILE_NAME_INFORMATION		9 +#define FILE_RENAME_INFORMATION		10 +#define FILE_LINK_INFORMATION		11 +#define FILE_NAMES_INFORMATION		12 +#define FILE_DISPOSITION_INFORMATION	13 +#define FILE_POSITION_INFORMATION	14 +#define FILE_FULL_EA_INFORMATION	15 +#define FILE_MODE_INFORMATION		16 +#define FILE_ALIGNMENT_INFORMATION	17 +#define FILE_ALL_INFORMATION		18 +#define FILE_ALLOCATION_INFORMATION	19 +#define FILE_END_OF_FILE_INFORMATION	20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION		22 +#define FILE_PIPE_INFORMATION		23 +#define FILE_PIPE_LOCAL_INFORMATION	24 +#define FILE_PIPE_REMOTE_INFORMATION	25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION	27 +#define FILE_COMPRESSION_INFORMATION	28 +#define FILE_OBJECT_ID_INFORMATION	29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION	31 +#define FILE_QUOTA_INFORMATION		32 +#define FILE_REPARSE_POINT_INFORMATION	33 +#define FILE_NETWORK_OPEN_INFORMATION	34 +#define FILE_ATTRIBUTE_TAG_INFORMATION	35 +#define FILE_TRACKING_INFORMATION	36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 +#define FILEID_FULL_DIRECTORY_INFORMATION 38 +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION	40 +#define FILE_SFIO_RESERVE_INFORMATION	44 +#define FILE_SFIO_VOLUME_INFORMATION	45 +#define FILE_HARD_LINK_INFORMATION	46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION	54 + +struct smb2_file_internal_info { +	__le64 IndexNumber; +} __packed; /* level 6 Query */ + +struct smb2_file_rename_info { /* encoding of request for level 10 */ +	__u8   ReplaceIfExists; /* 1 = replace existing target with new */ +				/* 0 = fail if target already exists */ +	__u8   Reserved[7]; +	__u64  RootDirectory;  /* MBZ for network operations (why says spec?) */ +	__le32 FileNameLength; +	char   FileName[0];     /* New name to be assigned */ +} __packed; /* level 10 Set */ + +struct smb2_file_link_info { /* encoding of request for level 11 */ +	__u8   ReplaceIfExists; /* 1 = replace existing link with new */ +				/* 0 = fail if link already exists */ +	__u8   Reserved[7]; +	__u64  RootDirectory;  /* MBZ for network operations (why says spec?) */ +	__le32 FileNameLength; +	char   FileName[0];     /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ +	__le64 CreationTime;	/* Beginning of FILE_BASIC_INFO equivalent */ +	__le64 LastAccessTime; +	__le64 LastWriteTime; +	__le64 ChangeTime; +	__le32 Attributes; +	__u32  Pad1;		/* End of FILE_BASIC_INFO_INFO equivalent */ +	__le64 AllocationSize;	/* Beginning of FILE_STANDARD_INFO equivalent */ +	__le64 EndOfFile;	/* size ie offset to first free byte in file */ +	__le32 NumberOfLinks;	/* hard links */ +	__u8   DeletePending; +	__u8   Directory; +	__u16  Pad2;		/* End of FILE_STANDARD_INFO equivalent */ +	__le64 IndexNumber; +	__le32 EASize; +	__le32 AccessFlags; +	__le64 CurrentByteOffset; +	__le32 Mode; +	__le32 AlignmentRequirement; +	__le32 FileNameLength; +	char   FileName[1]; +} __packed; /* level 18 Query */ + +struct smb2_file_eof_info { /* encoding of request for level 10 */ +	__le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + +#endif				/* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h new file mode 100644 index 00000000000..0ce48db20a6 --- /dev/null +++ b/fs/cifs/smb2proto.h @@ -0,0 +1,168 @@ +/* + *   fs/cifs/smb2proto.h + * + *   Copyright (c) International Business Machines  Corp., 2002, 2011 + *                 Etersoft, 2012 + *   Author(s): Steve French (sfrench@us.ibm.com) + *              Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _SMB2PROTO_H +#define _SMB2PROTO_H +#include <linux/nls.h> +#include <linux/key-type.h> + +struct statfs; +struct smb_rqst; + +/* + ***************************************************************** + * All Prototypes + ***************************************************************** + */ +extern int map_smb2_to_linux_error(char *buf, bool log_err); +extern int smb2_check_message(char *buf, unsigned int length); +extern unsigned int smb2_calc_size(void *buf); +extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr); +extern __le16 *cifs_convert_path_to_utf16(const char *from, +					  struct cifs_sb_info *cifs_sb); + +extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); +extern int smb2_check_receive(struct mid_q_entry *mid, +			      struct TCP_Server_Info *server, bool log_error); +extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, +			      struct smb_rqst *rqst); +extern struct mid_q_entry *smb2_setup_async_request( +			struct TCP_Server_Info *server, struct smb_rqst *rqst); +extern int smb2_calc_signature(struct smb_rqst *rqst, +				struct TCP_Server_Info *server); +extern int smb3_calc_signature(struct smb_rqst *rqst, +				struct TCP_Server_Info *server); +extern void smb2_echo_request(struct work_struct *work); +extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); +extern bool smb2_is_valid_oplock_break(char *buffer, +				       struct TCP_Server_Info *srv); + +extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, +				   struct smb2_file_all_info *src); +extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, +				struct cifs_sb_info *cifs_sb, +				const char *full_path, FILE_ALL_INFO *data, +				bool *adjust_tz, bool *symlink); +extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, +			      const char *full_path, __u64 size, +			      struct cifs_sb_info *cifs_sb, bool set_alloc); +extern int smb2_set_file_info(struct inode *inode, const char *full_path, +			      FILE_BASIC_INFO *buf, const unsigned int xid); +extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, +		      const char *name, struct cifs_sb_info *cifs_sb); +extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, +			       struct cifs_sb_info *cifs_sb, +			       struct cifs_tcon *tcon, const unsigned int xid); +extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, +		      const char *name, struct cifs_sb_info *cifs_sb); +extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, +		       const char *name, struct cifs_sb_info *cifs_sb); +extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, +			    const char *from_name, const char *to_name, +			    struct cifs_sb_info *cifs_sb); +extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, +				const char *from_name, const char *to_name, +				struct cifs_sb_info *cifs_sb); + +extern int smb2_open_file(const unsigned int xid, +			  struct cifs_open_parms *oparms, +			  __u32 *oplock, FILE_ALL_INFO *buf); +extern int smb2_unlock_range(struct cifsFileInfo *cfile, +			     struct file_lock *flock, const unsigned int xid); +extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); + +/* + * SMB2 Worker functions - most of protocol specific implementation details + * are contained within these calls. + */ +extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); +extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, +			   const struct nls_table *nls_cp); +extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); +extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, +		     const char *tree, struct cifs_tcon *tcon, +		     const struct nls_table *); +extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); +extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, +		     __le16 *path, __u8 *oplock, +		     struct smb2_file_all_info *buf, +		     struct smb2_err_rsp **err_buf); +extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, +		     u64 persistent_fid, u64 volatile_fid, u32 opcode, +		     bool is_fsctl, char *in_data, u32 indatalen, +		     char **out_data, u32 *plen /* returned data len */); +extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, +		      u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, +		      u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, +			   u64 persistent_file_id, u64 volatile_file_id, +			   struct smb2_file_all_info *data); +extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, +			    u64 persistent_fid, u64 volatile_fid, +			    __le64 *uniqueid); +extern int smb2_async_readv(struct cifs_readdata *rdata); +extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, +		     unsigned int *nbytes, char **buf, int *buf_type); +extern int smb2_async_writev(struct cifs_writedata *wdata, +			     void (*release)(struct kref *kref)); +extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, +		      unsigned int *nbytes, struct kvec *iov, int n_vec); +extern int SMB2_echo(struct TCP_Server_Info *server); +extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, +				u64 persistent_fid, u64 volatile_fid, int index, +				struct cifs_search_info *srch_inf); +extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, +		       u64 persistent_fid, u64 volatile_fid, +		       __le16 *target_file); +extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, +			     u64 persistent_fid, u64 volatile_fid, +			     __le16 *target_file); +extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, +			u64 persistent_fid, u64 volatile_fid, u32 pid, +			__le64 *eof); +extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon, +			 u64 persistent_fid, u64 volatile_fid, +			 FILE_BASIC_INFO *buf); +extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +				u64 persistent_fid, u64 volatile_fid); +extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, +			     const u64 persistent_fid, const u64 volatile_fid, +			     const __u8 oplock_level); +extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, +			 u64 persistent_file_id, u64 volatile_file_id, +			 struct kstatfs *FSData); +extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, +			 u64 persistent_file_id, u64 volatile_file_id, int lvl); +extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, +		     const __u64 persist_fid, const __u64 volatile_fid, +		     const __u32 pid, const __u64 length, const __u64 offset, +		     const __u32 lockFlags, const bool wait); +extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, +		      const __u64 persist_fid, const __u64 volatile_fid, +		      const __u32 pid, const __u32 num_lock, +		      struct smb2_lock_element *buf); +extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, +			    __u8 *lease_key, const __le32 lease_state); +extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); + +#endif			/* _SMB2PROTO_H */ diff --git a/fs/cifs/smb2status.h b/fs/cifs/smb2status.h new file mode 100644 index 00000000000..3d5f62150de --- /dev/null +++ b/fs/cifs/smb2status.h @@ -0,0 +1,1782 @@ +/* + *   fs/cifs/smb2status.h + * + *   SMB2 Status code (network error) definitions + *   Definitions are from MS-ERREF + * + *   Copyright (c) International Business Machines  Corp., 2009,2011 + *   Author(s): Steve French (sfrench@us.ibm.com) + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + *  0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + *  SEV C N <-------Facility--------> <------Error Status Code------> + * + *  C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL __constanst_cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING __constanst_cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR __constanst_cpu_to_le32(0x0003) + +struct ntstatus { +	/* Facility is the high 12 bits of the following field */ +	__le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ +	__le32 Code; +}; + +#define STATUS_SUCCESS __constant_cpu_to_le32(0x00000000) +#define STATUS_WAIT_0 __constant_cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 __constant_cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 __constant_cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 __constant_cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 __constant_cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED __constant_cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 __constant_cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 __constant_cpu_to_le32(0x000000BF) +#define STATUS_USER_APC __constant_cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC __constant_cpu_to_le32(0x00000100) +#define STATUS_ALERTED __constant_cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT __constant_cpu_to_le32(0x00000102) +#define STATUS_PENDING __constant_cpu_to_le32(0x00000103) +#define STATUS_REPARSE __constant_cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES __constant_cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED __constant_cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED __constant_cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED __constant_cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED __constant_cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP __constant_cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR __constant_cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT __constant_cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED __constant_cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION __constant_cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO __constant_cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE __constant_cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE __constant_cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE __constant_cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED __constant_cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP __constant_cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS __constant_cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT __constant_cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED __constant_cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE __constant_cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY __constant_cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE __constant_cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB __constant_cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB __constant_cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY __constant_cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY __constant_cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED __constant_cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED __constant_cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED __constant_cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS __constant_cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS __constant_cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY __constant_cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK __constant_cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED __constant_cpu_to_le32(0x00010001) +#define DBG_CONTINUE __constant_cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE __constant_cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS __constant_cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED __constant_cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE __constant_cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE __constant_cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED __constant_cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION __constant_cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY __constant_cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY __constant_cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES __constant_cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED __constant_cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP __constant_cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY __constant_cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT __constant_cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD __constant_cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH __constant_cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL __constant_cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED __constant_cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED __constant_cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE __constant_cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING __constant_cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM __constant_cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT __constant_cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE __constant_cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED __constant_cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION __constant_cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED __constant_cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR __constant_cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 __constant_cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE __constant_cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE __constant_cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP __constant_cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT __constant_cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE __constant_cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE __constant_cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN __constant_cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE __constant_cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED __constant_cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED __constant_cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED __constant_cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED __constant_cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB __constant_cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH __constant_cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED __constant_cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION __constant_cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED __constant_cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES __constant_cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED __constant_cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION __constant_cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST __constant_cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION __constant_cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY __constant_cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE __constant_cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED __constant_cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM __constant_cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN __constant_cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER __constant_cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE __constant_cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD __constant_cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS __constant_cpu_to_le32(0x40010004) +#define DBG_CONTROL_C __constant_cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C __constant_cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION __constant_cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK __constant_cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION __constant_cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY __constant_cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE __constant_cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT __constant_cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT __constant_cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT __constant_cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED __constant_cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED __constant_cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART __constant_cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST __constant_cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED __constant_cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH __constant_cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED __constant_cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE __constant_cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY __constant_cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET __constant_cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED __constant_cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS __constant_cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED __constant_cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY __constant_cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED __constant_cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED __constant_cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION __constant_cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT __constant_cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT __constant_cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP __constant_cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW __constant_cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES __constant_cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER __constant_cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED __constant_cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE __constant_cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE __constant_cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY __constant_cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY __constant_cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF __constant_cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE __constant_cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY __constant_cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS __constant_cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME __constant_cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT __constant_cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG __constant_cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED __constant_cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION __constant_cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY __constant_cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES __constant_cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED __constant_cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED __constant_cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET __constant_cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA __constant_cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA __constant_cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK __constant_cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED __constant_cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED __constant_cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES __constant_cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES __constant_cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED __constant_cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP __constant_cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED __constant_cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED __constant_cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE __constant_cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED __constant_cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE __constant_cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE __constant_cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK __constant_cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING __constant_cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN __constant_cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR __constant_cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED __constant_cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP __constant_cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN __constant_cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE __constant_cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE __constant_cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER __constant_cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG __constant_cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA __constant_cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN __constant_cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT __constant_cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET __constant_cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED __constant_cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL __constant_cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA __constant_cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL __constant_cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED __constant_cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS __constant_cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH __constant_cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION __constant_cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR __constant_cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA __constant_cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE __constant_cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK __constant_cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC __constant_cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID __constant_cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED __constant_cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER __constant_cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE __constant_cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE __constant_cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST __constant_cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE __constant_cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME __constant_cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE __constant_cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA __constant_cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR __constant_cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED __constant_cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY __constant_cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES __constant_cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW __constant_cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM __constant_cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION __constant_cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE __constant_cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION __constant_cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE __constant_cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE __constant_cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION __constant_cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED __constant_cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED __constant_cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL __constant_cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH __constant_cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION __constant_cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION __constant_cpu_to_le32(0xC0000026) +#define STATUS_UNWIND __constant_cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK __constant_cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET __constant_cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED __constant_cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR __constant_cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM __constant_cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED __constant_cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES __constant_cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG __constant_cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX __constant_cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER __constant_cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR __constant_cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID __constant_cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND __constant_cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION __constant_cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED __constant_cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED __constant_cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID __constant_cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND __constant_cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD __constant_cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN __constant_cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR __constant_cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR __constant_cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR __constant_cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG __constant_cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED __constant_cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE __constant_cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION __constant_cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION __constant_cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED __constant_cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET __constant_cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE __constant_cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED __constant_cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING __constant_cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT __constant_cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP __constant_cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION __constant_cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED __constant_cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE __constant_cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY __constant_cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE __constant_cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR __constant_cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT __constant_cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED __constant_cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING __constant_cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED __constant_cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION __constant_cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH __constant_cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER __constant_cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP __constant_cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN __constant_cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY __constant_cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS __constant_cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION __constant_cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE __constant_cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD __constant_cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME __constant_cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS __constant_cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER __constant_cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS __constant_cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP __constant_cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP __constant_cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP __constant_cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN __constant_cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD __constant_cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD __constant_cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION __constant_cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE __constant_cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION __constant_cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS __constant_cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION __constant_cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED __constant_cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED __constant_cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED __constant_cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED __constant_cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED __constant_cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY __constant_cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL __constant_cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID __constant_cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR __constant_cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND __constant_cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT __constant_cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN __constant_cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL __constant_cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED __constant_cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL __constant_cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED __constant_cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED __constant_cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED __constant_cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED __constant_cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY __constant_cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED __constant_cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL __constant_cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED __constant_cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA __constant_cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND __constant_cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND __constant_cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND __constant_cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED __constant_cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND __constant_cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO __constant_cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT __constant_cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION __constant_cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW __constant_cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK __constant_cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW __constant_cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO __constant_cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW __constant_cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION __constant_cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES __constant_cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID __constant_cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED __constant_cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES __constant_cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND __constant_cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR __constant_cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED __constant_cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE __constant_cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE __constant_cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED __constant_cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA __constant_cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED __constant_cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY __constant_cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES __constant_cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL __constant_cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS __constant_cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS __constant_cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE __constant_cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD __constant_cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT __constant_cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE __constant_cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE __constant_cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE __constant_cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY __constant_cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION __constant_cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED __constant_cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING __constant_cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED __constant_cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING __constant_cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE __constant_cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT __constant_cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED __constant_cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED __constant_cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED __constant_cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET __constant_cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY __constant_cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED __constant_cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING __constant_cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME __constant_cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH __constant_cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY __constant_cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST __constant_cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS __constant_cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR __constant_cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE __constant_cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR __constant_cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER __constant_cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL __constant_cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE __constant_cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED __constant_cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED __constant_cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED __constant_cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE __constant_cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME __constant_cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES __constant_cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS __constant_cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED __constant_cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED __constant_cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED __constant_cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT __constant_cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT __constant_cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE __constant_cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED __constant_cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED __constant_cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT __constant_cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT __constant_cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY __constant_cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO __constant_cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF __constant_cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE __constant_cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE __constant_cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE __constant_cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN __constant_cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS __constant_cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED __constant_cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL __constant_cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION __constant_cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR __constant_cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED __constant_cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT __constant_cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER __constant_cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR __constant_cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR __constant_cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR __constant_cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR __constant_cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS __constant_cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS __constant_cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 __constant_cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 __constant_cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 __constant_cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 __constant_cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 __constant_cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 __constant_cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 __constant_cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 __constant_cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 __constant_cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 __constant_cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 __constant_cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 __constant_cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED __constant_cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED __constant_cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW __constant_cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE __constant_cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE __constant_cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND __constant_cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY __constant_cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR __constant_cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY __constant_cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE __constant_cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION __constant_cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG __constant_cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN __constant_cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE __constant_cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND __constant_cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING __constant_cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE __constant_cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION __constant_cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE __constant_cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED __constant_cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT __constant_cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST __constant_cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED __constant_cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER __constant_cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND __constant_cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID __constant_cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE __constant_cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR __constant_cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT __constant_cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE __constant_cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET __constant_cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR __constant_cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT __constant_cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE __constant_cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE __constant_cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO __constant_cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES __constant_cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED __constant_cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE __constant_cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME __constant_cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED __constant_cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT __constant_cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP __constant_cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER __constant_cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP __constant_cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED __constant_cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS __constant_cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS __constant_cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE __constant_cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT __constant_cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT __constant_cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ __constant_cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT __constant_cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 __constant_cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT __constant_cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC __constant_cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED __constant_cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND __constant_cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED __constant_cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED __constant_cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND __constant_cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND __constant_cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT __constant_cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT __constant_cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT __constant_cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES __constant_cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED __constant_cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT __constant_cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION __constant_cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS __constant_cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED __constant_cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE __constant_cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION __constant_cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE __constant_cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED __constant_cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE __constant_cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL __constant_cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE __constant_cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT __constant_cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN __constant_cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT __constant_cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED __constant_cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR __constant_cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME __constant_cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED __constant_cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS __constant_cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS __constant_cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS __constant_cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS __constant_cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED __constant_cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS __constant_cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG __constant_cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR __constant_cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE __constant_cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS __constant_cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED __constant_cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE __constant_cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED __constant_cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR __constant_cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER __constant_cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY __constant_cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER __constant_cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER __constant_cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER __constant_cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME __constant_cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND __constant_cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER __constant_cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR __constant_cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS __constant_cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED __constant_cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED __constant_cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED __constant_cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY __constant_cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING __constant_cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT __constant_cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE __constant_cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH __constant_cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED __constant_cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA __constant_cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA __constant_cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW __constant_cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA __constant_cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER __constant_cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER __constant_cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED __constant_cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE __constant_cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS __constant_cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED __constant_cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN __constant_cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE __constant_cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR __constant_cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR __constant_cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE __constant_cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR __constant_cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR __constant_cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER __constant_cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL __constant_cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE __constant_cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET __constant_cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT __constant_cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE __constant_cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE __constant_cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT __constant_cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START __constant_cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE __constant_cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED __constant_cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED __constant_cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK __constant_cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT __constant_cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT __constant_cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED __constant_cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT __constant_cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT __constant_cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT __constant_cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT __constant_cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED __constant_cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL __constant_cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION __constant_cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY __constant_cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED __constant_cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND __constant_cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES __constant_cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE __constant_cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT __constant_cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD __constant_cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES __constant_cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS __constant_cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED __constant_cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED __constant_cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET __constant_cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES __constant_cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED __constant_cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT __constant_cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE __constant_cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH __constant_cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED __constant_cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID __constant_cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE __constant_cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION __constant_cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION __constant_cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE __constant_cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED __constant_cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED __constant_cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED __constant_cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND __constant_cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR __constant_cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT __constant_cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH __constant_cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT __constant_cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH __constant_cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA __constant_cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID __constant_cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE __constant_cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND __constant_cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM __constant_cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE __constant_cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ __constant_cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK __constant_cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID __constant_cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS __constant_cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE __constant_cpu_to_le32(0xC000022C) +#define STATUS_RETRY __constant_cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE __constant_cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET __constant_cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND __constant_cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW __constant_cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT __constant_cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND __constant_cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT __constant_cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE __constant_cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED __constant_cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT __constant_cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED __constant_cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED __constant_cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID __constant_cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE __constant_cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE __constant_cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE __constant_cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE __constant_cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE __constant_cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED __constant_cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED __constant_cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER __constant_cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE __constant_cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED __constant_cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET __constant_cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT __constant_cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION __constant_cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION __constant_cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH __constant_cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO __constant_cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT __constant_cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT __constant_cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST __constant_cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 __constant_cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 __constant_cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT __constant_cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED __constant_cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE __constant_cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT __constant_cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT __constant_cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT __constant_cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE __constant_cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION __constant_cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE __constant_cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH __constant_cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND __constant_cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND __constant_cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED __constant_cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS __constant_cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT __constant_cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE __constant_cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION __constant_cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION __constant_cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION __constant_cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF __constant_cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD __constant_cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE __constant_cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED __constant_cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR __constant_cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK __constant_cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE __constant_cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH __constant_cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES __constant_cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT __constant_cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID __constant_cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH __constant_cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID __constant_cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED __constant_cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED __constant_cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT __constant_cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT __constant_cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY __constant_cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL __constant_cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS __constant_cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT __constant_cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED __constant_cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED __constant_cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED __constant_cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND __constant_cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY __constant_cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS __constant_cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS __constant_cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS __constant_cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED __constant_cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT __constant_cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED __constant_cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND __constant_cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND __constant_cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND __constant_cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN __constant_cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY __constant_cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND __constant_cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS __constant_cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED __constant_cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE __constant_cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR __constant_cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE __constant_cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH __constant_cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE __constant_cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX __constant_cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED __constant_cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS __constant_cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY __constant_cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE __constant_cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED __constant_cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS __constant_cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER __constant_cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR __constant_cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION __constant_cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF __constant_cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN __constant_cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS __constant_cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED __constant_cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE __constant_cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED __constant_cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT __constant_cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY __constant_cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS __constant_cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS __constant_cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED __constant_cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS __constant_cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE __constant_cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE __constant_cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP __constant_cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED __constant_cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE __constant_cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR __constant_cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY __constant_cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE __constant_cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM __constant_cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION __constant_cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL __constant_cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE __constant_cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED __constant_cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION __constant_cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION __constant_cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED __constant_cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID __constant_cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE __constant_cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED __constant_cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID __constant_cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE __constant_cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN __constant_cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN __constant_cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER __constant_cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER __constant_cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER __constant_cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER __constant_cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER __constant_cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS __constant_cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED __constant_cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER __constant_cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD __constant_cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY __constant_cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START __constant_cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE __constant_cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE __constant_cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED __constant_cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY __constant_cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS __constant_cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION __constant_cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED __constant_cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE __constant_cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN __constant_cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE __constant_cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE __constant_cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED __constant_cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY __constant_cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND __constant_cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES __constant_cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE __constant_cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID __constant_cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED __constant_cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC __constant_cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED __constant_cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS __constant_cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA __constant_cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH __constant_cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED __constant_cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST __constant_cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER __constant_cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE __constant_cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS __constant_cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS __constant_cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS __constant_cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED __constant_cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED __constant_cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED __constant_cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED __constant_cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE __constant_cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE __constant_cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT __constant_cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED __constant_cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR __constant_cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH __constant_cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED __constant_cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE __constant_cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE __constant_cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY __constant_cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN __constant_cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH __constant_cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG __constant_cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET __constant_cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE __constant_cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE __constant_cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED __constant_cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT __constant_cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER __constant_cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 __constant_cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 __constant_cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS __constant_cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED __constant_cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK __constant_cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED __constant_cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER __constant_cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT __constant_cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH __constant_cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER __constant_cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER __constant_cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY __constant_cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR __constant_cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED __constant_cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER __constant_cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURED __constant_cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL __constant_cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED __constant_cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR __constant_cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE __constant_cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL __constant_cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS __constant_cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY __constant_cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH __constant_cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION __constant_cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN __constant_cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED __constant_cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED __constant_cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD __constant_cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER __constant_cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE __constant_cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET __constant_cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR __constant_cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED __constant_cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED __constant_cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED __constant_cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C __constant_cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE __constant_cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED __constant_cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD __constant_cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT __constant_cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE __constant_cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND __constant_cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR __constant_cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE __constant_cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED __constant_cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN __constant_cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT __constant_cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE __constant_cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC __constant_cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC __constant_cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED __constant_cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED __constant_cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE __constant_cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED __constant_cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED __constant_cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED __constant_cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD __constant_cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE __constant_cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER __constant_cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED __constant_cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE __constant_cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP __constant_cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK __constant_cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED __constant_cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED __constant_cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED __constant_cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION __constant_cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH __constant_cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE __constant_cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE __constant_cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT __constant_cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED __constant_cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL __constant_cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS __constant_cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED __constant_cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED __constant_cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION __constant_cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE __constant_cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER __constant_cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE __constant_cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE __constant_cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE __constant_cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL __constant_cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED __constant_cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE __constant_cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND __constant_cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION __constant_cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME __constant_cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX __constant_cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK __constant_cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS __constant_cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED __constant_cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST __constant_cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE __constant_cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED __constant_cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH __constant_cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED __constant_cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE __constant_cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED __constant_cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE __constant_cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR __constant_cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION __constant_cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED __constant_cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION __constant_cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING __constant_cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING __constant_cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED __constant_cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION __constant_cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE __constant_cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED __constant_cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION __constant_cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION __constant_cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED __constant_cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH __constant_cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST __constant_cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY __constant_cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD __constant_cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION __constant_cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK __constant_cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG __constant_cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK __constant_cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY __constant_cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED __constant_cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS __constant_cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED __constant_cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED __constant_cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS __constant_cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY __constant_cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT __constant_cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED __constant_cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE __constant_cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE __constant_cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED __constant_cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED __constant_cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED __constant_cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE __constant_cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION __constant_cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE __constant_cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED __constant_cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW __constant_cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW __constant_cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED __constant_cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED __constant_cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED __constant_cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR __constant_cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR __constant_cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR __constant_cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR __constant_cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT __constant_cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE __constant_cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE __constant_cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE __constant_cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING __constant_cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING __constant_cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING __constant_cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED __constant_cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ __constant_cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID __constant_cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT __constant_cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR __constant_cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND __constant_cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT __constant_cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND __constant_cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED __constant_cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED __constant_cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING __constant_cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED __constant_cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING __constant_cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE __constant_cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF __constant_cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS __constant_cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS __constant_cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT __constant_cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES __constant_cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE __constant_cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY __constant_cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS __constant_cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE __constant_cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED __constant_cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE __constant_cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR __constant_cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN __constant_cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE __constant_cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG __constant_cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND __constant_cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME __constant_cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX __constant_cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX __constant_cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS __constant_cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT __constant_cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE __constant_cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL __constant_cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG __constant_cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND __constant_cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE __constant_cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH __constant_cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE __constant_cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL __constant_cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY __constant_cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE __constant_cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY __constant_cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP __constant_cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED __constant_cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT __constant_cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME __constant_cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION __constant_cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS __constant_cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED __constant_cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND __constant_cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS __constant_cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND __constant_cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE __constant_cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID __constant_cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT __constant_cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE __constant_cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR __constant_cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE __constant_cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR __constant_cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO __constant_cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW __constant_cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW __constant_cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS __constant_cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS __constant_cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND __constant_cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE __constant_cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT __constant_cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES __constant_cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED __constant_cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE __constant_cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE __constant_cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL __constant_cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME __constant_cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR __constant_cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR __constant_cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED __constant_cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE __constant_cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL __constant_cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED __constant_cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES __constant_cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL __constant_cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE __constant_cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT __constant_cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH __constant_cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED __constant_cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH __constant_cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE __constant_cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER __constant_cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE __constant_cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL __constant_cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA __constant_cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION __constant_cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION __constant_cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION __constant_cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT __constant_cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION __constant_cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION __constant_cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED __constant_cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR __constant_cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY __constant_cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE __constant_cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED __constant_cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED __constant_cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID __constant_cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED __constant_cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID __constant_cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD __constant_cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND __constant_cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING __constant_cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF __constant_cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND __constant_cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME __constant_cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR __constant_cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT __constant_cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER __constant_cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE __constant_cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY __constant_cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE __constant_cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR __constant_cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID __constant_cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE __constant_cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED __constant_cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND __constant_cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION __constant_cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY __constant_cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE __constant_cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID __constant_cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE __constant_cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT __constant_cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT __constant_cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT __constant_cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED __constant_cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED __constant_cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD __constant_cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND __constant_cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID __constant_cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED __constant_cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR __constant_cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET __constant_cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE __constant_cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE __constant_cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING __constant_cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED __constant_cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR __constant_cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS __constant_cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND __constant_cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE __constant_cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG __constant_cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME __constant_cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME __constant_cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED __constant_cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP __constant_cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE __constant_cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS __constant_cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS __constant_cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND __constant_cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND __constant_cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS __constant_cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND __constant_cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS __constant_cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND __constant_cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST __constant_cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER __constant_cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN __constant_cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE __constant_cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER __constant_cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS __constant_cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK __constant_cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS __constant_cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP __constant_cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED __constant_cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED __constant_cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT __constant_cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL __constant_cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED __constant_cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE __constant_cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW __constant_cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED __constant_cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX __constant_cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT __constant_cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL __constant_cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME __constant_cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE __constant_cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE __constant_cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE __constant_cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT __constant_cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED __constant_cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE __constant_cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION __constant_cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA __constant_cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION __constant_cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE __constant_cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK __constant_cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED __constant_cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED __constant_cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL __constant_cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED __constant_cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER __constant_cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS __constant_cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE __constant_cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED __constant_cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED __constant_cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND __constant_cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX __constant_cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT __constant_cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND __constant_cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR __constant_cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR __constant_cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED __constant_cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND __constant_cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT __constant_cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE __constant_cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED __constant_cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING __constant_cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET __constant_cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION __constant_cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION __constant_cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION __constant_cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY __constant_cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED __constant_cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK __constant_cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION __constant_cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE __constant_cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME __constant_cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE __constant_cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR __constant_cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT __constant_cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH __constant_cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT __constant_cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT __constant_cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT __constant_cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY __constant_cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED __constant_cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH __constant_cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG __constant_cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED __constant_cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE __constant_cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED __constant_cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED __constant_cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING __constant_cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT __constant_cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION __constant_cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE __constant_cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED __constant_cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE __constant_cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT __constant_cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED __constant_cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM __constant_cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE __constant_cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE __constant_cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH __constant_cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS __constant_cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED __constant_cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND __constant_cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS __constant_cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID __constant_cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED __constant_cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED __constant_cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED __constant_cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER __constant_cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID __constant_cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED __constant_cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS __constant_cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND __constant_cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID __constant_cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION __constant_cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT __constant_cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS __constant_cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID __constant_cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED __constant_cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED __constant_cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR __constant_cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT __constant_cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY __constant_cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY __constant_cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY __constant_cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST __constant_cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE __constant_cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED __constant_cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT __constant_cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION __constant_cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED __constant_cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE __constant_cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION __constant_cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION __constant_cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN __constant_cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS __constant_cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME __constant_cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES __constant_cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION __constant_cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH __constant_cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION __constant_cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK __constant_cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS __constant_cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND __constant_cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND __constant_cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND __constant_cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND __constant_cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE __constant_cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION __constant_cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT __constant_cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED __constant_cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION __constant_cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED __constant_cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG __constant_cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION __constant_cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION __constant_cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED __constant_cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID __constant_cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID __constant_cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED __constant_cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE __constant_cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE __constant_cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED __constant_cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID __constant_cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID __constant_cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION __constant_cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID __constant_cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID __constant_cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT __constant_cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID __constant_cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT __constant_cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID __constant_cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE __constant_cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED __constant_cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG __constant_cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED __constant_cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED __constant_cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID __constant_cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT __constant_cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL __constant_cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT __constant_cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID __constant_cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID __constant_cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID __constant_cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL __constant_cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED __constant_cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED __constant_cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS __constant_cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS __constant_cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL __constant_cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS __constant_cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED __constant_cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED __constant_cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS __constant_cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED __constant_cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED __constant_cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED __constant_cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID __constant_cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID __constant_cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED __constant_cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED __constant_cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY __constant_cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED __constant_cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION __constant_cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD __constant_cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED __constant_cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED __constant_cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST __constant_cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO __constant_cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST __constant_cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION __constant_cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED __constant_cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY __constant_cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP __constant_cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR __constant_cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT __constant_cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL __constant_cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY __constant_cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED __constant_cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH __constant_cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH __constant_cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION __constant_cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION __constant_cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND __constant_cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND __constant_cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND __constant_cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND __constant_cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION __constant_cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS __constant_cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT __constant_cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED __constant_cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED __constant_cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED __constant_cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY __constant_cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR __constant_cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT __constant_cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM __constant_cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK __constant_cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED __constant_cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK __constant_cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK __constant_cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA __constant_cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK __constant_cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER __constant_cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER __constant_cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER __constant_cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET __constant_cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL __constant_cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED __constant_cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED __constant_cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED __constant_cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT __constant_cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY __constant_cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY __constant_cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY __constant_cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES __constant_cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER __constant_cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW __constant_cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID __constant_cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE __constant_cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED __constant_cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION __constant_cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE __constant_cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION __constant_cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED __constant_cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE __constant_cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE __constant_cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE __constant_cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST __constant_cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE __constant_cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY __constant_cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN __constant_cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE __constant_cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET __constant_cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET __constant_cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET __constant_cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY __constant_cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION __constant_cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION __constant_cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE __constant_cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE __constant_cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET __constant_cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET __constant_cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET __constant_cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET __constant_cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH __constant_cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY __constant_cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET __constant_cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE __constant_cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET __constant_cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET __constant_cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET __constant_cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE __constant_cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN __constant_cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION __constant_cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES __constant_cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE __constant_cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET __constant_cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET __constant_cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR __constant_cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET __constant_cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET __constant_cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE __constant_cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED __constant_cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE __constant_cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET __constant_cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER __constant_cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR __constant_cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN __constant_cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY __constant_cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED __constant_cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE __constant_cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE __constant_cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE __constant_cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT __constant_cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS __constant_cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE __constant_cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY __constant_cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT __constant_cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE __constant_cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN __constant_cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL __constant_cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION __constant_cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP __constant_cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET __constant_cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON __constant_cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE __constant_cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE __constant_cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS __constant_cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING __constant_cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED __constant_cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS __constant_cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT __constant_cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM __constant_cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN __constant_cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT __constant_cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED __constant_cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION __constant_cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE __constant_cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET __constant_cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED __constant_cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER __constant_cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED __constant_cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED __constant_cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY __constant_cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED __constant_cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON __constant_cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE __constant_cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER __constant_cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED __constant_cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS __constant_cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST __constant_cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS __constant_cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST __constant_cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS __constant_cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL __constant_cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST __constant_cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME __constant_cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP __constant_cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER __constant_cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE __constant_cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE __constant_cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH __constant_cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED __constant_cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED __constant_cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED __constant_cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM __constant_cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP __constant_cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP __constant_cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA __constant_cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET __constant_cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH __constant_cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE __constant_cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS __constant_cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS __constant_cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST __constant_cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA __constant_cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA __constant_cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA __constant_cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE __constant_cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING __constant_cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND __constant_cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH __constant_cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM __constant_cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE __constant_cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS __constant_cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED __constant_cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME __constant_cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP __constant_cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED __constant_cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER __constant_cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE __constant_cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL __constant_cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR __constant_cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS __constant_cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME __constant_cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED __constant_cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION __constant_cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL __constant_cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS __constant_cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS __constant_cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED __constant_cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED __constant_cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE __constant_cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED __constant_cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA __constant_cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND __constant_cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME __constant_cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR __constant_cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR __constant_cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE __constant_cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE __constant_cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION __constant_cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME __constant_cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND __constant_cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID __constant_cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK __constant_cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED __constant_cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO __constant_cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR __constant_cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK __constant_cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID __constant_cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION __constant_cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG __constant_cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED __constant_cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED __constant_cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER __constant_cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY __constant_cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED __constant_cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS __constant_cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED __constant_cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND __constant_cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND __constant_cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND __constant_cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND __constant_cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND __constant_cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND __constant_cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND __constant_cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND __constant_cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS __constant_cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE __constant_cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS __constant_cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION __constant_cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS __constant_cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS __constant_cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED __constant_cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED __constant_cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN __constant_cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT __constant_cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED __constant_cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER __constant_cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY __constant_cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH __constant_cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT __constant_cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS __constant_cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS __constant_cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED __constant_cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH __constant_cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE __constant_cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER __constant_cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR __constant_cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS __constant_cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK __constant_cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE __constant_cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL __constant_cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY __constant_cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME __constant_cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE __constant_cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT __constant_cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH __constant_cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH __constant_cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS __constant_cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED __constant_cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION __constant_cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD __constant_cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER __constant_cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER __constant_cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER __constant_cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT __constant_cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD __constant_cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP __constant_cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED __constant_cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH __constant_cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH __constant_cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER __constant_cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS __constant_cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED __constant_cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG __constant_cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG __constant_cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY __constant_cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING __constant_cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE __constant_cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND __constant_cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING __constant_cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION __constant_cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS __constant_cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND __constant_cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED __constant_cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED __constant_cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL __constant_cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS __constant_cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND __constant_cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED __constant_cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS __constant_cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET __constant_cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST __constant_cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY __constant_cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH __constant_cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA __constant_cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT __constant_cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID __constant_cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED __constant_cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA __constant_cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE __constant_cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND __constant_cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE __constant_cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED __constant_cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT __constant_cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED __constant_cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS __constant_cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED __constant_cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND __constant_cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION __constant_cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT __constant_cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE __constant_cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE __constant_cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED __constant_cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED __constant_cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE __constant_cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID __constant_cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI __constant_cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED __constant_cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA __constant_cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED __constant_cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET __constant_cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED __constant_cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP __constant_cpu_to_le32(0xC0360007) diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c new file mode 100644 index 00000000000..59c748ce872 --- /dev/null +++ b/fs/cifs/smb2transport.c @@ -0,0 +1,607 @@ +/* + *   fs/cifs/smb2transport.c + * + *   Copyright (C) International Business Machines  Corp., 2002, 2011 + *                 Etersoft, 2012 + *   Author(s): Steve French (sfrench@us.ibm.com) + *              Jeremy Allison (jra@samba.org) 2006 + *              Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/wait.h> +#include <linux/net.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <asm/processor.h> +#include <linux/mempool.h> +#include <linux/highmem.h> +#include "smb2pdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "smb2status.h" +#include "smb2glob.h" + +static int +smb2_crypto_shash_allocate(struct TCP_Server_Info *server) +{ +	int rc; +	unsigned int size; + +	if (server->secmech.sdeschmacsha256 != NULL) +		return 0; /* already allocated */ + +	server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0); +	if (IS_ERR(server->secmech.hmacsha256)) { +		cifs_dbg(VFS, "could not allocate crypto hmacsha256\n"); +		rc = PTR_ERR(server->secmech.hmacsha256); +		server->secmech.hmacsha256 = NULL; +		return rc; +	} + +	size = sizeof(struct shash_desc) + +			crypto_shash_descsize(server->secmech.hmacsha256); +	server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL); +	if (!server->secmech.sdeschmacsha256) { +		crypto_free_shash(server->secmech.hmacsha256); +		server->secmech.hmacsha256 = NULL; +		return -ENOMEM; +	} +	server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256; +	server->secmech.sdeschmacsha256->shash.flags = 0x0; + +	return 0; +} + +static int +smb3_crypto_shash_allocate(struct TCP_Server_Info *server) +{ +	unsigned int size; +	int rc; + +	if (server->secmech.sdesccmacaes != NULL) +		return 0;  /* already allocated */ + +	rc = smb2_crypto_shash_allocate(server); +	if (rc) +		return rc; + +	server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0); +	if (IS_ERR(server->secmech.cmacaes)) { +		cifs_dbg(VFS, "could not allocate crypto cmac-aes"); +		kfree(server->secmech.sdeschmacsha256); +		server->secmech.sdeschmacsha256 = NULL; +		crypto_free_shash(server->secmech.hmacsha256); +		server->secmech.hmacsha256 = NULL; +		rc = PTR_ERR(server->secmech.cmacaes); +		server->secmech.cmacaes = NULL; +		return rc; +	} + +	size = sizeof(struct shash_desc) + +			crypto_shash_descsize(server->secmech.cmacaes); +	server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL); +	if (!server->secmech.sdesccmacaes) { +		cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__); +		kfree(server->secmech.sdeschmacsha256); +		server->secmech.sdeschmacsha256 = NULL; +		crypto_free_shash(server->secmech.hmacsha256); +		crypto_free_shash(server->secmech.cmacaes); +		server->secmech.hmacsha256 = NULL; +		server->secmech.cmacaes = NULL; +		return -ENOMEM; +	} +	server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes; +	server->secmech.sdesccmacaes->shash.flags = 0x0; + +	return 0; +} + +static struct cifs_ses * +smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server) +{ +	struct cifs_ses *ses; + +	spin_lock(&cifs_tcp_ses_lock); +	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { +		if (ses->Suid != smb2hdr->SessionId) +			continue; +		spin_unlock(&cifs_tcp_ses_lock); +		return ses; +	} +	spin_unlock(&cifs_tcp_ses_lock); + +	return NULL; +} + + +int +smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ +	int i, rc; +	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; +	unsigned char *sigptr = smb2_signature; +	struct kvec *iov = rqst->rq_iov; +	int n_vec = rqst->rq_nvec; +	struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; +	struct cifs_ses *ses; + +	ses = smb2_find_smb_ses(smb2_pdu, server); +	if (!ses) { +		cifs_dbg(VFS, "%s: Could not find session\n", __func__); +		return 0; +	} + +	memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); +	memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); + +	rc = smb2_crypto_shash_allocate(server); +	if (rc) { +		cifs_dbg(VFS, "%s: shah256 alloc failed\n", __func__); +		return rc; +	} + +	rc = crypto_shash_setkey(server->secmech.hmacsha256, +		ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with response\n", __func__); +		return rc; +	} + +	rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not init sha256", __func__); +		return rc; +	} + +	for (i = 0; i < n_vec; i++) { +		if (iov[i].iov_len == 0) +			continue; +		if (iov[i].iov_base == NULL) { +			cifs_dbg(VFS, "null iovec entry\n"); +			return -EIO; +		} +		/* +		 * The first entry includes a length field (which does not get +		 * signed that occupies the first 4 bytes before the header). +		 */ +		if (i == 0) { +			if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ +				break; /* nothing to sign or corrupt header */ +			rc = +			crypto_shash_update( +				&server->secmech.sdeschmacsha256->shash, +				iov[i].iov_base + 4, iov[i].iov_len - 4); +		} else { +			rc = +			crypto_shash_update( +				&server->secmech.sdeschmacsha256->shash, +				iov[i].iov_base, iov[i].iov_len); +		} +		if (rc) { +			cifs_dbg(VFS, "%s: Could not update with payload\n", +				 __func__); +			return rc; +		} +	} + +	/* now hash over the rq_pages array */ +	for (i = 0; i < rqst->rq_npages; i++) { +		struct kvec p_iov; + +		cifs_rqst_page_to_kvec(rqst, i, &p_iov); +		crypto_shash_update(&server->secmech.sdeschmacsha256->shash, +					p_iov.iov_base, p_iov.iov_len); +		kunmap(rqst->rq_pages[i]); +	} + +	rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, +				sigptr); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + +	memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); + +	return rc; +} + +int +generate_smb3signingkey(struct cifs_ses *ses) +{ +	unsigned char zero = 0x0; +	__u8 i[4] = {0, 0, 0, 1}; +	__u8 L[4] = {0, 0, 0, 128}; +	int rc = 0; +	unsigned char prfhash[SMB2_HMACSHA256_SIZE]; +	unsigned char *hashptr = prfhash; + +	memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); +	memset(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE); + +	rc = smb3_crypto_shash_allocate(ses->server); +	if (rc) { +		cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_setkey(ses->server->secmech.hmacsha256, +		ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not set with session key\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_init(&ses->server->secmech.sdeschmacsha256->shash); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, +				i, 4); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with n\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, +				"SMB2AESCMAC", 12); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with label\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, +				&zero, 1); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with zero\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, +				"SmbSign", 8); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with context\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, +				L, 4); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with L\n", __func__); +		goto smb3signkey_ret; +	} + +	rc = crypto_shash_final(&ses->server->secmech.sdeschmacsha256->shash, +				hashptr); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); +		goto smb3signkey_ret; +	} + +	memcpy(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE); + +smb3signkey_ret: +	return rc; +} + +int +smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ +	int i; +	int rc = 0; +	unsigned char smb3_signature[SMB2_CMACAES_SIZE]; +	unsigned char *sigptr = smb3_signature; +	struct kvec *iov = rqst->rq_iov; +	int n_vec = rqst->rq_nvec; +	struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; +	struct cifs_ses *ses; + +	ses = smb2_find_smb_ses(smb2_pdu, server); +	if (!ses) { +		cifs_dbg(VFS, "%s: Could not find session\n", __func__); +		return 0; +	} + +	memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); +	memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); + +	rc = crypto_shash_setkey(server->secmech.cmacaes, +		ses->smb3signingkey, SMB2_CMACAES_SIZE); + +	if (rc) { +		cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); +		return rc; +	} + +	/* +	 * we already allocate sdesccmacaes when we init smb3 signing key, +	 * so unlike smb2 case we do not have to check here if secmech are +	 * initialized +	 */ +	rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); +		return rc; +	} + +	for (i = 0; i < n_vec; i++) { +		if (iov[i].iov_len == 0) +			continue; +		if (iov[i].iov_base == NULL) { +			cifs_dbg(VFS, "null iovec entry"); +			return -EIO; +		} +		/* +		 * The first entry includes a length field (which does not get +		 * signed that occupies the first 4 bytes before the header). +		 */ +		if (i == 0) { +			if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ +				break; /* nothing to sign or corrupt header */ +			rc = +			crypto_shash_update( +				&server->secmech.sdesccmacaes->shash, +				iov[i].iov_base + 4, iov[i].iov_len - 4); +		} else { +			rc = +			crypto_shash_update( +				&server->secmech.sdesccmacaes->shash, +				iov[i].iov_base, iov[i].iov_len); +		} +		if (rc) { +			cifs_dbg(VFS, "%s: Couldn't update cmac aes with payload\n", +							__func__); +			return rc; +		} +	} + +	/* now hash over the rq_pages array */ +	for (i = 0; i < rqst->rq_npages; i++) { +		struct kvec p_iov; + +		cifs_rqst_page_to_kvec(rqst, i, &p_iov); +		crypto_shash_update(&server->secmech.sdesccmacaes->shash, +					p_iov.iov_base, p_iov.iov_len); +		kunmap(rqst->rq_pages[i]); +	} + +	rc = crypto_shash_final(&server->secmech.sdesccmacaes->shash, +						sigptr); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__); + +	memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); + +	return rc; +} + +/* must be called with server->srv_mutex held */ +static int +smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ +	int rc = 0; +	struct smb2_hdr *smb2_pdu = rqst->rq_iov[0].iov_base; + +	if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) || +	    server->tcpStatus == CifsNeedNegotiate) +		return rc; + +	if (!server->session_estab) { +		strncpy(smb2_pdu->Signature, "BSRSPYL", 8); +		return rc; +	} + +	rc = server->ops->calc_signature(rqst, server); + +	return rc; +} + +int +smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ +	unsigned int rc; +	char server_response_sig[16]; +	struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + +	if ((smb2_pdu->Command == SMB2_NEGOTIATE) || +	    (smb2_pdu->Command == SMB2_SESSION_SETUP) || +	    (smb2_pdu->Command == SMB2_OPLOCK_BREAK) || +	    (!server->session_estab)) +		return 0; + +	/* +	 * BB what if signatures are supposed to be on for session but +	 * server does not send one? BB +	 */ + +	/* Do not need to verify session setups with signature "BSRSPYL " */ +	if (memcmp(smb2_pdu->Signature, "BSRSPYL ", 8) == 0) +		cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", +			 smb2_pdu->Command); + +	/* +	 * Save off the origiginal signature so we can modify the smb and check +	 * our calculated signature against what the server sent. +	 */ +	memcpy(server_response_sig, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE); + +	memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE); + +	mutex_lock(&server->srv_mutex); +	rc = server->ops->calc_signature(rqst, server); +	mutex_unlock(&server->srv_mutex); + +	if (rc) +		return rc; + +	if (memcmp(server_response_sig, smb2_pdu->Signature, +		   SMB2_SIGNATURE_SIZE)) +		return -EACCES; +	else +		return 0; +} + +/* + * Set message id for the request. Should be called after wait_for_free_request + * and when srv_mutex is held. + */ +static inline void +smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr) +{ +	hdr->MessageId = get_next_mid64(server); +} + +static struct mid_q_entry * +smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer, +		     struct TCP_Server_Info *server) +{ +	struct mid_q_entry *temp; + +	if (server == NULL) { +		cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); +		return NULL; +	} + +	temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); +	if (temp == NULL) +		return temp; +	else { +		memset(temp, 0, sizeof(struct mid_q_entry)); +		temp->mid = smb_buffer->MessageId;	/* always LE */ +		temp->pid = current->pid; +		temp->command = smb_buffer->Command;	/* Always LE */ +		temp->when_alloc = jiffies; +		temp->server = server; + +		/* +		 * The default is for the mid to be synchronous, so the +		 * default callback just wakes up the current task. +		 */ +		temp->callback = cifs_wake_up_task; +		temp->callback_data = current; +	} + +	atomic_inc(&midCount); +	temp->mid_state = MID_REQUEST_ALLOCATED; +	return temp; +} + +static int +smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf, +		   struct mid_q_entry **mid) +{ +	if (ses->server->tcpStatus == CifsExiting) +		return -ENOENT; + +	if (ses->server->tcpStatus == CifsNeedReconnect) { +		cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); +		return -EAGAIN; +	} + +	if (ses->status == CifsNew) { +		if ((buf->Command != SMB2_SESSION_SETUP) && +		    (buf->Command != SMB2_NEGOTIATE)) +			return -EAGAIN; +		/* else ok - we are setting up session */ +	} + +	if (ses->status == CifsExiting) { +		if (buf->Command != SMB2_LOGOFF) +			return -EAGAIN; +		/* else ok - we are shutting down the session */ +	} + +	*mid = smb2_mid_entry_alloc(buf, ses->server); +	if (*mid == NULL) +		return -ENOMEM; +	spin_lock(&GlobalMid_Lock); +	list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q); +	spin_unlock(&GlobalMid_Lock); +	return 0; +} + +int +smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, +		   bool log_error) +{ +	unsigned int len = get_rfc1002_length(mid->resp_buf); +	struct kvec iov; +	struct smb_rqst rqst = { .rq_iov = &iov, +				 .rq_nvec = 1 }; + +	iov.iov_base = (char *)mid->resp_buf; +	iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4; + +	dump_smb(mid->resp_buf, min_t(u32, 80, len)); +	/* convert the length into a more usable form */ +	if (len > 24 && server->sign) { +		int rc; + +		rc = smb2_verify_signature(&rqst, server); +		if (rc) +			cifs_dbg(VFS, "SMB signature verification returned error = %d\n", +				 rc); +	} + +	return map_smb2_to_linux_error(mid->resp_buf, log_error); +} + +struct mid_q_entry * +smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) +{ +	int rc; +	struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; +	struct mid_q_entry *mid; + +	smb2_seq_num_into_buf(ses->server, hdr); + +	rc = smb2_get_mid_entry(ses, hdr, &mid); +	if (rc) +		return ERR_PTR(rc); +	rc = smb2_sign_rqst(rqst, ses->server); +	if (rc) { +		cifs_delete_mid(mid); +		return ERR_PTR(rc); +	} +	return mid; +} + +struct mid_q_entry * +smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ +	int rc; +	struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; +	struct mid_q_entry *mid; + +	smb2_seq_num_into_buf(server, hdr); + +	mid = smb2_mid_entry_alloc(hdr, server); +	if (mid == NULL) +		return ERR_PTR(-ENOMEM); + +	rc = smb2_sign_rqst(rqst, server); +	if (rc) { +		DeleteMidQEntry(mid); +		return ERR_PTR(rc); +	} + +	return mid; +} diff --git a/fs/cifs/smbdes.c b/fs/cifs/smbdes.c deleted file mode 100644 index b6b6dcb500b..00000000000 --- a/fs/cifs/smbdes.c +++ /dev/null @@ -1,419 +0,0 @@ -/* -   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 <linux/slab.h> -#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 unused */ -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 */ diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 192ea51af20..43eb1367b10 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -32,9 +32,8 @@  #include "cifs_unicode.h"  #include "cifspdu.h"  #include "cifsglob.h" -#include "md5.h"  #include "cifs_debug.h" -#include "cifsencrypt.h" +#include "cifsproto.h"  #ifndef false  #define false 0 @@ -48,244 +47,202 @@  #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) +static void +str_to_key(unsigned char *str, unsigned char *key)  { -	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); +	int i; -	memset(p14, 0, 15); -	memset(p21, 0, 21); +	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);  } -/* Routines for Windows NT MD4 Hash functions. */  static int -_my_wcslen(__u16 *str) +smbhash(unsigned char *out, const unsigned char *in, unsigned char *key)  { -	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; +	int rc; +	unsigned char key2[8]; +	struct crypto_blkcipher *tfm_des; +	struct scatterlist sgin, sgout; +	struct blkcipher_desc desc; + +	str_to_key(key, key2); + +	tfm_des = crypto_alloc_blkcipher("ecb(des)", 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(tfm_des)) { +		rc = PTR_ERR(tfm_des); +		cifs_dbg(VFS, "could not allocate des crypto API\n"); +		goto smbhash_err;  	} -	return i; -} -/* - * Creates the MD4 Hash of the users password in NT UNICODE. - */ +	desc.tfm = tfm_des; -void -E_md4hash(const unsigned char *passwd, unsigned char *p16) -{ -	int len; -	__u16 wpwd[129]; +	crypto_blkcipher_setkey(tfm_des, key2, 8); -	/* 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; +	sg_init_one(&sgin, in, 8); +	sg_init_one(&sgout, out, 8); -	wpwd[len] = 0;	/* Ensure string is null terminated */ -	/* Calculate length in bytes */ -	len = _my_wcslen(wpwd) * sizeof(__u16); +	rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, 8); +	if (rc) +		cifs_dbg(VFS, "could not encrypt crypt key rc: %d\n", rc); -	mdfour(p16, (unsigned char *) wpwd, len); -	memset(wpwd, 0, 129 * 2); +	crypto_free_blkcipher(tfm_des); +smbhash_err: +	return rc;  } -#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]) +static int +E_P16(unsigned char *p14, unsigned char *p16)  { -	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)); +	int rc; +	unsigned char sp8[8] = +	    { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; + +	rc = smbhash(p16, sp8, p14); +	if (rc) +		return rc; +	rc = smbhash(p16 + 8, sp8, p14 + 7); +	return rc;  } -#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) +static int +E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24)  { -	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); +	int rc; + +	rc = smbhash(p24, c8, p21); +	if (rc) +		return rc; +	rc = smbhash(p24 + 8, c8, p21 + 7); +	if (rc) +		return rc; +	rc = smbhash(p24 + 16, c8, p21 + 14); +	return rc;  } -#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]) +/* produce a md4 message digest from data of length n bytes */ +int +mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len)  { -	unsigned char p21[21]; +	int rc; +	unsigned int size; +	struct crypto_shash *md4; +	struct sdesc *sdescmd4; + +	md4 = crypto_alloc_shash("md4", 0, 0); +	if (IS_ERR(md4)) { +		rc = PTR_ERR(md4); +		cifs_dbg(VFS, "%s: Crypto md4 allocation error %d\n", +			 __func__, rc); +		return rc; +	} +	size = sizeof(struct shash_desc) + crypto_shash_descsize(md4); +	sdescmd4 = kmalloc(size, GFP_KERNEL); +	if (!sdescmd4) { +		rc = -ENOMEM; +		goto mdfour_err; +	} +	sdescmd4->shash.tfm = md4; +	sdescmd4->shash.flags = 0x0; -	memset(p21, '\0', 21); +	rc = crypto_shash_init(&sdescmd4->shash); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not init md4 shash\n", __func__); +		goto mdfour_err; +	} +	rc = crypto_shash_update(&sdescmd4->shash, link_str, link_len); +	if (rc) { +		cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); +		goto mdfour_err; +	} +	rc = crypto_shash_final(&sdescmd4->shash, md4_hash); +	if (rc) +		cifs_dbg(VFS, "%s: Could not generate md4 hash\n", __func__); -	memcpy(p21, passwd, 16); -	E_P24(p21, c8, p24); +mdfour_err: +	crypto_free_shash(md4); +	kfree(sdescmd4); + +	return rc;  } -/* 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]) +/* +   This implements the X/Open SMB password encryption +   It takes a password, a 8 byte "crypt key" and puts 24 bytes of +   encrypted password into p24 */ +/* Note that password must be uppercased and null terminated */ +int +SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24)  { -	unsigned char p21[21]; +	int rc; +	unsigned char p14[14], p16[16], p21[21]; +	memset(p14, '\0', 14); +	memset(p16, '\0', 16);  	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]; +	memcpy(p14, passwd, 14); +	rc = E_P16(p14, p16); +	if (rc) +		return rc; -	memset(p21, '\0', 21); +	memcpy(p21, p16, 16); +	rc = E_P24(p21, c8, p24); -	E_md4hash(passwd, p21); -	SMBOWFencrypt(p21, c8, p24); +	return rc;  } +/* + * Creates the MD4 Hash of the users password in NT UNICODE. + */ -/* 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]) +int +E_md4hash(const unsigned char *passwd, unsigned char *p16, +	const struct nls_table *codepage)  { -	struct HMACMD5Context ctx; +	int rc; +	int len; +	__le16 wpwd[129]; -	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); -} +	/* Password cannot be longer than 128 characters */ +	if (passwd) /* Password must be converted to NT unicode */ +		len = cifs_strtoUTF16(wpwd, passwd, 128, codepage); +	else { +		len = 0; +		*wpwd = 0; /* Ensure string is null terminated */ +	} -static void -SMBsesskeygen_ntv2(const unsigned char kr[16], -		   const unsigned char *nt_resp, __u8 sess_key[16]) -{ -	struct HMACMD5Context ctx; +	rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__le16)); +	memset(wpwd, 0, 129 * sizeof(__le16)); -	hmac_md5_init_limK_to_64(kr, 16, &ctx); -	hmac_md5_update(nt_resp, 16, &ctx); -	hmac_md5_final((unsigned char *) sess_key, &ctx); +	return rc;  } -static void -SMBsesskeygen_ntv1(const unsigned char kr[16], -		   const unsigned char *nt_resp, __u8 sess_key[16]) +/* Does the NT MD4 hash then des encryption. */ +int +SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24, +		const struct nls_table *codepage)  { -	mdfour((unsigned char *) sess_key, (unsigned char *) kr, 16); +	int rc; +	unsigned char p16[16], p21[21]; + +	memset(p16, '\0', 16); +	memset(p21, '\0', 21); + +	rc = E_md4hash(passwd, p16, codepage); +	if (rc) { +		cifs_dbg(FYI, "%s Can't generate NT hash, error: %d\n", +			 __func__, rc); +		return rc; +	} +	memcpy(p21, p16, 16); +	rc = E_P24(p21, c8, p24); +	return rc;  } -#endif diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index 7056b891e08..0e538b5c962 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -1,7 +1,7 @@  /*   *   fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions   * - *   Copyright (c) International Business Machines  Corp., 2002,2009 + *   Copyright (c) International Business Machines  Corp., 2002,2013   *   Author(s): Steve French (sfrench@us.ibm.com)   *   *   This library is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@  /* IOCTL information */  /*   * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2 client.  There is probably + * future to remote clients like cifs or SMB2/SMB3 client.  This is probably   * a slightly larger set of fsctls that NTFS local filesystem could handle,   * including the seven below that we do not have struct definitions for.   * Even with protocol definitions for most of these now available, we still @@ -30,7 +30,13 @@   * remotely.  Some of the following, such as the encryption/compression ones   * could be invoked from tools via a specialized hook into the VFS rather   * than via the standard vfs entry points + * + * See MS-SMB2 Section 2.2.31 (last checked June 2013, all of that list are + * below). Additional detail on less common ones can be found in MS-FSCC + * section 2.3.   */ +#define FSCTL_DFS_GET_REFERRALS      0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX   0x000601B0  #define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000  #define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004  #define FSCTL_REQUEST_BATCH_OPLOCK   0x00090008 @@ -71,14 +77,45 @@  #define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */  #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */  #define FSCTL_SET_DEFECT_MANAGEMENT  0x00098134 /* BB add struct */ +#define FSCTL_FILE_LEVEL_TRIM        0x00098208 /* BB add struct */  #define FSCTL_SIS_LINK_FILES         0x0009C104  #define FSCTL_PIPE_PEEK              0x0011400C /* BB add struct */  #define FSCTL_PIPE_TRANSCEIVE        0x0011C017 /* BB add struct */  /* strange that the number for this op is not sequential with previous op */  #define FSCTL_PIPE_WAIT              0x00110018 /* BB add struct */ +/* Enumerate previous versions of a file */ +#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 +/* Retrieve an opaque file reference for server-side data movement ie copy */ +#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */  #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */  #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +/* Perform server-side data movement */ +#define FSCTL_SRV_COPYCHUNK 0x001440F2 +#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ +#define FSCTL_SRV_READ_HASH          0x001441BB /* BB add struct */ +/* See FSCC 2.1.2.5 */  #define IO_REPARSE_TAG_MOUNT_POINT   0xA0000003  #define IO_REPARSE_TAG_HSM           0xC0000004  #define IO_REPARSE_TAG_SIS           0x80000007 +#define IO_REPARSE_TAG_HSM2          0x80000006 +#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 +/* Used by the DFS filter. See MS-DFSC */ +#define IO_REPARSE_TAG_DFS           0x8000000A +/* Used by the DFS filter See MS-DFSC */ +#define IO_REPARSE_TAG_DFSR          0x80000012 +#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B +/* See section MS-FSCC 2.1.2.4 */ +#define IO_REPARSE_TAG_SYMLINK       0xA000000C +#define IO_REPARSE_TAG_DEDUP         0x80000013 +#define IO_REPARSE_APPXSTREAM	     0xC0000014 +/* NFS symlinks, Win 8/SMB3 and later */ +#define IO_REPARSE_TAG_NFS           0x80000014 + +/* fsctl flags */ +/* If Flags is set to this value, the request is an FSCTL not ioctl request */ +#define SMB2_0_IOCTL_IS_FSCTL		0x00000001 + diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index e0588cdf4cc..18cd5650a5f 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -26,6 +26,9 @@  #include <linux/wait.h>  #include <linux/net.h>  #include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/tcp.h> +#include <linux/highmem.h>  #include <asm/uaccess.h>  #include <asm/processor.h>  #include <linux/mempool.h> @@ -34,15 +37,19 @@  #include "cifsproto.h"  #include "cifs_debug.h" -extern mempool_t *cifs_mid_poolp; +void +cifs_wake_up_task(struct mid_q_entry *mid) +{ +	wake_up_process(mid->callback_data); +} -static struct mid_q_entry * +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"); +		cifs_dbg(VFS, "Null TCP session in AllocMidQEntry\n");  		return NULL;  	} @@ -51,36 +58,38 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)  		return temp;  	else {  		memset(temp, 0, sizeof(struct mid_q_entry)); -		temp->mid = smb_buffer->Mid;	/* always LE */ +		temp->mid = get_mid(smb_buffer);  		temp->pid = current->pid; -		temp->command = smb_buffer->Command; -		cFYI(1, "For smb_command %d", temp->command); +		temp->command = cpu_to_le16(smb_buffer->Command); +		cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);  	/*	do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */  		/* when mid allocated can be before when sent */  		temp->when_alloc = jiffies; -		temp->tsk = current; +		temp->server = server; + +		/* +		 * The default is for the mid to be synchronous, so the +		 * default callback just wakes up the current task. +		 */ +		temp->callback = cifs_wake_up_task; +		temp->callback_data = current;  	} -	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); +	temp->mid_state = MID_REQUEST_ALLOCATED;  	return temp;  } -static void +void  DeleteMidQEntry(struct mid_q_entry *midEntry)  {  #ifdef CONFIG_CIFS_STATS2 +	__le16 command = midEntry->server->vals->lock_cmd;  	unsigned long now;  #endif -	spin_lock(&GlobalMid_Lock); -	midEntry->midState = MID_FREE; -	list_del(&midEntry->qhead); +	midEntry->mid_state = MID_FREE;  	atomic_dec(&midCount); -	spin_unlock(&GlobalMid_Lock); -	if (midEntry->largeBuf) +	if (midEntry->large_buf)  		cifs_buf_release(midEntry->resp_buf);  	else  		cifs_small_buf_release(midEntry->resp_buf); @@ -89,9 +98,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)  	/* 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", +		if ((cifsFYI & CIFS_TIMER) && (midEntry->command != command)) { +			printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu",  			       midEntry->command, midEntry->mid);  			printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",  			       now - midEntry->when_alloc, @@ -103,23 +111,40 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)  	mempool_free(midEntry, cifs_mid_poolp);  } +void +cifs_delete_mid(struct mid_q_entry *mid) +{ +	spin_lock(&GlobalMid_Lock); +	list_del(&mid->qhead); +	spin_unlock(&GlobalMid_Lock); + +	DeleteMidQEntry(mid); +} + +/* + * smb_send_kvec - send an array of kvecs to the server + * @server:	Server to send the data to + * @iov:	Pointer to array of kvecs + * @n_vec:	length of kvec array + * @sent:	amount of data sent on socket is stored here + * + * Our basic "send data to server" function. Should be called with srv_mutex + * held. The caller is responsible for handling the results. + */  static int -smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) +smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec, +		size_t *sent)  {  	int rc = 0;  	int i = 0;  	struct msghdr smb_msg; -	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; +	unsigned int remaining; +	size_t first_vec = 0;  	struct socket *ssocket = server->ssocket; -	if (ssocket == NULL) -		return -ENOTSOCK; /* BB eventually add reconnect code here */ +	*sent = 0; -	smb_msg.msg_name = (struct sockaddr *) &server->addr.sockAddr; +	smb_msg.msg_name = (struct sockaddr *) &server->dstaddr;  	smb_msg.msg_namelen = sizeof(struct sockaddr);  	smb_msg.msg_control = NULL;  	smb_msg.msg_controllen = 0; @@ -128,71 +153,70 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)  	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; +	remaining = 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); +		remaining += iov[i].iov_len;  	i = 0; -	while (total_len) { +	while (remaining) { +		/* +		 * If blocking send, we try 3 times, since each can block +		 * for 5 seconds. For nonblocking  we have to try more +		 * but wait increasing amounts of time allowing time for +		 * socket to clear.  The overall time we wait in either +		 * case to send on the socket is about 15 seconds. +		 * Similarly we wait for 15 seconds for a response from +		 * the server in SendReceive[2] for the server to send +		 * a response back for most types of requests (except +		 * SMB Write past end of file which can be slow, and +		 * blocking lock operations). NFS waits slightly longer +		 * than CIFS, but this can make it take longer for +		 * nonresponsive servers to be detected and 15 seconds +		 * is more than enough time for modern networks to +		 * send a packet.  In most cases if we fail to send +		 * after the retries we will kill the socket and +		 * reconnect which may clear the network problem. +		 */  		rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec], -				    n_vec - first_vec, total_len); -		if ((rc == -ENOSPC) || (rc == -EAGAIN)) { +				    n_vec - first_vec, remaining); +		if (rc == -EAGAIN) {  			i++; -			/* if blocking send we try 3 times, since each can block -			   for 5 seconds. For nonblocking  we have to try more -			   but wait increasing amounts of time allowing time for -			   socket to clear.  The overall time we wait in either -			   case to send on the socket is about 15 seconds. -			   Similarly we wait for 15 seconds for -			   a response from the server in SendReceive[2] -			   for the server to send a response back for -			   most types of requests (except SMB Write -			   past end of file which can be slow, and -			   blocking lock operations). NFS waits slightly longer -			   than CIFS, but this can make it take longer for -			   nonresponsive servers to be detected and 15 seconds -			   is more than enough time for modern networks to -			   send a packet.  In most cases if we fail to send -			   after the retries we will kill the socket and -			   reconnect which may clear the network problem. -			*/ -			if ((i >= 14) || (!server->noblocksnd && (i > 2))) { -				cERROR(1, "sends on sock %p stuck for 15 seconds", -				    ssocket); +			if (i >= 14 || (!server->noblocksnd && (i > 2))) { +				cifs_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", +					 ssocket);  				rc = -EAGAIN;  				break;  			}  			msleep(1 << i);  			continue;  		} +  		if (rc < 0)  			break; -		if (rc == total_len) { -			total_len = 0; +		/* send was at least partially successful */ +		*sent += rc; + +		if (rc == remaining) { +			remaining = 0;  			break; -		} else if (rc > total_len) { -			cERROR(1, "sent %d requested %d", rc, total_len); +		} + +		if (rc > remaining) { +			cifs_dbg(VFS, "sent %d requested %d\n", rc, remaining);  			break;  		} +  		if (rc == 0) {  			/* should never happen, letting socket clear before  			   retrying is our only obvious option here */ -			cERROR(1, "tcp sent no data"); +			cifs_dbg(VFS, "tcp sent no data\n");  			msleep(500);  			continue;  		} -		total_len -= rc; + +		remaining -= rc; +  		/* the line below resets i */  		for (i = first_vec; i < n_vec; i++) {  			if (iov[i].iov_len) { @@ -207,31 +231,150 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)  				}  			}  		} +  		i = 0; /* in case we get ENOSPC on the next send */ +		rc = 0; +	} +	return rc; +} + +/** + * rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec + * @rqst: pointer to smb_rqst + * @idx: index into the array of the page + * @iov: pointer to struct kvec that will hold the result + * + * Helper function to convert a slot in the rqst->rq_pages array into a kvec. + * The page will be kmapped and the address placed into iov_base. The length + * will then be adjusted according to the ptailoff. + */ +void +cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx, +			struct kvec *iov) +{ +	/* +	 * FIXME: We could avoid this kmap altogether if we used +	 * kernel_sendpage instead of kernel_sendmsg. That will only +	 * work if signing is disabled though as sendpage inlines the +	 * page directly into the fraglist. If userspace modifies the +	 * page after we calculate the signature, then the server will +	 * reject it and may break the connection. kernel_sendmsg does +	 * an extra copy of the data and avoids that issue. +	 */ +	iov->iov_base = kmap(rqst->rq_pages[idx]); + +	/* if last page, don't send beyond this offset into page */ +	if (idx == (rqst->rq_npages - 1)) +		iov->iov_len = rqst->rq_tailsz; +	else +		iov->iov_len = rqst->rq_pagesz; +} + +static unsigned long +rqst_len(struct smb_rqst *rqst) +{ +	unsigned int i; +	struct kvec *iov = rqst->rq_iov; +	unsigned long buflen = 0; + +	/* total up iov array first */ +	for (i = 0; i < rqst->rq_nvec; i++) +		buflen += iov[i].iov_len; + +	/* add in the page array if there is one */ +	if (rqst->rq_npages) { +		buflen += rqst->rq_pagesz * (rqst->rq_npages - 1); +		buflen += rqst->rq_tailsz; +	} + +	return buflen; +} + +static int +smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ +	int rc; +	struct kvec *iov = rqst->rq_iov; +	int n_vec = rqst->rq_nvec; +	unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); +	unsigned long send_length; +	unsigned int i; +	size_t total_len = 0, sent; +	struct socket *ssocket = server->ssocket; +	int val = 1; + +	if (ssocket == NULL) +		return -ENOTSOCK; + +	/* sanity check send length */ +	send_length = rqst_len(rqst); +	if (send_length != smb_buf_length + 4) { +		WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n", +			send_length, smb_buf_length); +		return -EIO;  	} +	cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length); +	dump_smb(iov[0].iov_base, iov[0].iov_len); + +	/* cork the socket */ +	kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, +				(char *)&val, sizeof(val)); + +	rc = smb_send_kvec(server, iov, n_vec, &sent); +	if (rc < 0) +		goto uncork; + +	total_len += sent; + +	/* now walk the page array and send each page in it */ +	for (i = 0; i < rqst->rq_npages; i++) { +		struct kvec p_iov; + +		cifs_rqst_page_to_kvec(rqst, i, &p_iov); +		rc = smb_send_kvec(server, &p_iov, 1, &sent); +		kunmap(rqst->rq_pages[i]); +		if (rc < 0) +			break; + +		total_len += sent; +	} + +uncork: +	/* uncork it */ +	val = 0; +	kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, +				(char *)&val, sizeof(val)); +  	if ((total_len > 0) && (total_len != smb_buf_length + 4)) { -		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 */ +		cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n", +			 smb_buf_length + 4, total_len); +		/* +		 * If we have only sent part of an SMB then the next SMB could +		 * be taken as the remainder of this one. We need to kill the +		 * socket so the server throws away the partial SMB +		 */  		server->tcpStatus = CifsNeedReconnect;  	} -	if (rc < 0) { -		cERROR(1, "Error %d sending data on socket to server", rc); -	} else +	if (rc < 0 && rc != -EINTR) +		cifs_dbg(VFS, "Error %d sending data on socket to server\n", +			 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_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) +{ +	struct smb_rqst rqst = { .rq_iov = iov, +				 .rq_nvec = n_vec }; + +	return smb_send_rqst(server, &rqst); +} +  int  smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,  	 unsigned int smb_buf_length) @@ -244,49 +387,68 @@ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,  	return smb_sendv(server, &iov, 1);  } -static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) +static int +wait_for_free_credits(struct TCP_Server_Info *server, const int timeout, +		      int *credits)  { -	if (long_op == CIFS_ASYNC_OP) { +	int rc; + +	spin_lock(&server->req_lock); +	if (timeout == CIFS_ASYNC_OP) {  		/* oplock breaks must not be held up */ -		atomic_inc(&ses->server->inFlight); +		server->in_flight++; +		*credits -= 1; +		spin_unlock(&server->req_lock);  		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); +		if (*credits <= 0) { +			spin_unlock(&server->req_lock); +			cifs_num_waiters_inc(server); +			rc = wait_event_killable(server->request_q, +						 has_credits(server, credits)); +			cifs_num_waiters_dec(server); +			if (rc) +				return rc; +			spin_lock(&server->req_lock);  		} else { -			if (ses->server->tcpStatus == CifsExiting) { -				spin_unlock(&GlobalMid_Lock); +			if (server->tcpStatus == CifsExiting) { +				spin_unlock(&server->req_lock);  				return -ENOENT;  			} -			/* can not count locking commands against total -			   as they are allowed to block on server */ +			/* +			 * 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); +			if (timeout != CIFS_BLOCKING_OP) { +				*credits -= 1; +				server->in_flight++; +			} +			spin_unlock(&server->req_lock);  			break;  		}  	}  	return 0;  } -static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, +static int +wait_for_free_request(struct TCP_Server_Info *server, const int timeout, +		      const int optype) +{ +	int *val; + +	val = server->ops->get_credits_field(server, optype); +	/* Since an echo is already inflight, no need to wait to send another */ +	if (*val <= 0 && optype == CIFS_ECHO_OP) +		return -EAGAIN; +	return wait_for_free_credits(server, timeout, val); +} + +static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,  			struct mid_q_entry **ppmidQ)  {  	if (ses->server->tcpStatus == CifsExiting) { @@ -294,67 +456,126 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,  	}  	if (ses->server->tcpStatus == CifsNeedReconnect) { -		cFYI(1, "tcp session dead - return to caller to retry"); +		cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");  		return -EAGAIN;  	} -	if (ses->status != CifsGood) { -		/* check if SMB session is bad because we are setting it up */ +	if (ses->status == CifsNew) {  		if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&  			(in_buf->Command != SMB_COM_NEGOTIATE))  			return -EAGAIN;  		/* else ok - we are setting up session */  	} + +	if (ses->status == CifsExiting) { +		/* check if SMB session is bad because we are setting it up */ +		if (in_buf->Command != SMB_COM_LOGOFF_ANDX) +			return -EAGAIN; +		/* else ok - we are shutting down session */ +	} +  	*ppmidQ = AllocMidQEntry(in_buf, ses->server);  	if (*ppmidQ == NULL)  		return -ENOMEM; +	spin_lock(&GlobalMid_Lock); +	list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); +	spin_unlock(&GlobalMid_Lock);  	return 0;  } -static int wait_for_response(struct cifsSesInfo *ses, -			struct mid_q_entry *midQ, -			unsigned long timeout, -			unsigned long time_to_wait) +static int +wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)  { -	unsigned long curr_timeout; +	int error; -	for (;;) { -		curr_timeout = timeout + jiffies; -		wait_event_timeout(ses->server->response_q, -			midQ->midState != MID_REQUEST_SUBMITTED, timeout); +	error = wait_event_freezekillable_unsafe(server->response_q, +				    midQ->mid_state != MID_REQUEST_SUBMITTED); +	if (error < 0) +		return -ERESTARTSYS; -		if (time_after(jiffies, curr_timeout) && -			(midQ->midState == MID_REQUEST_SUBMITTED) && -			((ses->server->tcpStatus == CifsGood) || -			 (ses->server->tcpStatus == CifsNew))) { +	return 0; +} -			unsigned long lrt; +struct mid_q_entry * +cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ +	int rc; +	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; +	struct mid_q_entry *mid; -			/* We timed out. Is the server still -			   sending replies ? */ -			spin_lock(&GlobalMid_Lock); -			lrt = ses->server->lstrp; -			spin_unlock(&GlobalMid_Lock); +	/* enable signing if server requires it */ +	if (server->sign) +		hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; -			/* 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; -		} +	mid = AllocMidQEntry(hdr, server); +	if (mid == NULL) +		return ERR_PTR(-ENOMEM); + +	rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); +	if (rc) { +		DeleteMidQEntry(mid); +		return ERR_PTR(rc);  	} + +	return mid;  } +/* + * Send a SMB request and set the callback function in the mid to handle + * the result. Caller is responsible for dealing with timeouts. + */ +int +cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, +		mid_receive_t *receive, mid_callback_t *callback, +		void *cbdata, const int flags) +{ +	int rc, timeout, optype; +	struct mid_q_entry *mid; + +	timeout = flags & CIFS_TIMEOUT_MASK; +	optype = flags & CIFS_OP_MASK; + +	rc = wait_for_free_request(server, timeout, optype); +	if (rc) +		return rc; + +	mutex_lock(&server->srv_mutex); +	mid = server->ops->setup_async_request(server, rqst); +	if (IS_ERR(mid)) { +		mutex_unlock(&server->srv_mutex); +		add_credits(server, 1, optype); +		wake_up(&server->request_q); +		return PTR_ERR(mid); +	} + +	mid->receive = receive; +	mid->callback = callback; +	mid->callback_data = cbdata; +	mid->mid_state = MID_REQUEST_SUBMITTED; + +	/* put it on the pending_mid_q */ +	spin_lock(&GlobalMid_Lock); +	list_add_tail(&mid->qhead, &server->pending_mid_q); +	spin_unlock(&GlobalMid_Lock); + + +	cifs_in_send_inc(server); +	rc = smb_send_rqst(server, rqst); +	cifs_in_send_dec(server); +	cifs_save_when_sent(mid); + +	if (rc < 0) +		server->sequence_number -= 2; +	mutex_unlock(&server->srv_mutex); + +	if (rc == 0) +		return 0; + +	cifs_delete_mid(mid); +	add_credits(server, 1, optype); +	wake_up(&server->request_q); +	return rc; +}  /*   * @@ -366,234 +587,253 @@ static int wait_for_response(struct cifsSesInfo *ses,   *   */  int -SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses, -		struct smb_hdr *in_buf, int flags) +SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, +		 char *in_buf, int flags)  {  	int rc;  	struct kvec iov[1];  	int resp_buf_type; -	iov[0].iov_base = (char *)in_buf; -	iov[0].iov_len = in_buf->smb_buf_length + 4; +	iov[0].iov_base = in_buf; +	iov[0].iov_len = get_rfc1002_length(in_buf) + 4;  	flags |= CIFS_NO_RESP;  	rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags); -	cFYI(DBG2, "SendRcvNoRsp flags %d rc %d", flags, rc); +	cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);  	return rc;  } +static int +cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) +{ +	int rc = 0; + +	cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n", +		 __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state); + +	spin_lock(&GlobalMid_Lock); +	switch (mid->mid_state) { +	case MID_RESPONSE_RECEIVED: +		spin_unlock(&GlobalMid_Lock); +		return rc; +	case MID_RETRY_NEEDED: +		rc = -EAGAIN; +		break; +	case MID_RESPONSE_MALFORMED: +		rc = -EIO; +		break; +	case MID_SHUTDOWN: +		rc = -EHOSTDOWN; +		break; +	default: +		list_del_init(&mid->qhead); +		cifs_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", +			 __func__, mid->mid, mid->mid_state); +		rc = -EIO; +	} +	spin_unlock(&GlobalMid_Lock); + +	DeleteMidQEntry(mid); +	return rc; +} + +static inline int +send_cancel(struct TCP_Server_Info *server, void *buf, struct mid_q_entry *mid) +{ +	return server->ops->send_cancel ? +				server->ops->send_cancel(server, buf, mid) : 0; +} + +int +cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, +		   bool log_error) +{ +	unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; + +	dump_smb(mid->resp_buf, min_t(u32, 92, len)); + +	/* convert the length into a more usable form */ +	if (server->sign) { +		struct kvec iov; +		int rc = 0; +		struct smb_rqst rqst = { .rq_iov = &iov, +					 .rq_nvec = 1 }; + +		iov.iov_base = mid->resp_buf; +		iov.iov_len = len; +		/* FIXME: add code to kill session */ +		rc = cifs_verify_signature(&rqst, server, +					   mid->sequence_number); +		if (rc) +			cifs_dbg(VFS, "SMB signature verification returned error = %d\n", +				 rc); +	} + +	/* BB special case reconnect tid and uid here? */ +	return map_smb_to_linux_error(mid->resp_buf, log_error); +} + +struct mid_q_entry * +cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) +{ +	int rc; +	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; +	struct mid_q_entry *mid; + +	rc = allocate_mid(ses, hdr, &mid); +	if (rc) +		return ERR_PTR(rc); +	rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number); +	if (rc) { +		cifs_delete_mid(mid); +		return ERR_PTR(rc); +	} +	return mid; +} +  int -SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, -	     struct kvec *iov, int n_vec, int *pRespBufType /* ret */, +SendReceive2(const unsigned int xid, struct cifs_ses *ses, +	     struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,  	     const int flags)  {  	int rc = 0; -	int long_op; -	unsigned int receive_len; -	unsigned long timeout; +	int timeout, optype;  	struct mid_q_entry *midQ; -	struct smb_hdr *in_buf = iov[0].iov_base; +	char *buf = iov[0].iov_base; +	unsigned int credits = 1; +	struct smb_rqst rqst = { .rq_iov = iov, +				 .rq_nvec = n_vec }; -	long_op = flags & CIFS_TIMEOUT_MASK; +	timeout = flags & CIFS_TIMEOUT_MASK; +	optype = flags & CIFS_OP_MASK; -	*pRespBufType = CIFS_NO_BUFFER;  /* no response buf yet */ +	*resp_buf_type = CIFS_NO_BUFFER;  /* no response buf yet */  	if ((ses == NULL) || (ses->server == NULL)) { -		cifs_small_buf_release(in_buf); -		cERROR(1, "Null session"); +		cifs_small_buf_release(buf); +		cifs_dbg(VFS, "Null session\n");  		return -EIO;  	}  	if (ses->server->tcpStatus == CifsExiting) { -		cifs_small_buf_release(in_buf); +		cifs_small_buf_release(buf);  		return -ENOENT;  	} -	/* Ensure that we do not send more than 50 overlapping requests -	   to the same server. We may make this configurable later or -	   use ses->maxReq */ +	/* +	 * 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); +	rc = wait_for_free_request(ses->server, timeout, optype);  	if (rc) { -		cifs_small_buf_release(in_buf); +		cifs_small_buf_release(buf);  		return rc;  	} -	/* make sure that we sign in the same order that we send on this socket -	   and avoid races inside tcp sendmsg code that could cause corruption -	   of smb data */ +	/* +	 * 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) { +	midQ = ses->server->ops->setup_request(ses, &rqst); +	if (IS_ERR(midQ)) {  		mutex_unlock(&ses->server->srv_mutex); -		cifs_small_buf_release(in_buf); +		cifs_small_buf_release(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; +		add_credits(ses->server, 1, optype); +		return PTR_ERR(midQ);  	} -	midQ->midState = MID_REQUEST_SUBMITTED; -#ifdef CONFIG_CIFS_STATS2 -	atomic_inc(&ses->server->inSend); -#endif +	midQ->mid_state = MID_REQUEST_SUBMITTED; +	cifs_in_send_inc(ses->server);  	rc = smb_sendv(ses->server, iov, n_vec); -#ifdef CONFIG_CIFS_STATS2 -	atomic_dec(&ses->server->inSend); -	midQ->when_sent = jiffies; -#endif +	cifs_in_send_dec(ses->server); +	cifs_save_when_sent(midQ); +	if (rc < 0) +		ses->server->sequence_number -= 2;  	mutex_unlock(&ses->server->srv_mutex); -	cifs_small_buf_release(in_buf); -	if (rc < 0) +	if (rc < 0) { +		cifs_small_buf_release(buf);  		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; +	if (timeout == CIFS_ASYNC_OP) { +		cifs_small_buf_release(buf);  		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; +	rc = wait_for_response(ses->server, midQ); +	if (rc != 0) { +		send_cancel(ses->server, buf, midQ); +		spin_lock(&GlobalMid_Lock); +		if (midQ->mid_state == MID_REQUEST_SUBMITTED) { +			midQ->callback = DeleteMidQEntry; +			spin_unlock(&GlobalMid_Lock); +			cifs_small_buf_release(buf); +			add_credits(ses->server, 1, optype); +			return rc; +		} +		spin_unlock(&GlobalMid_Lock);  	} -	/* 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 == NULL) { -		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; -			} -		} +	cifs_small_buf_release(buf); -		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); +	rc = cifs_sync_mid_result(midQ, ses->server); +	if (rc != 0) { +		add_credits(ses->server, 1, optype);  		return rc;  	} -	spin_unlock(&GlobalMid_Lock); -	receive_len = midQ->resp_buf->smb_buf_length; - -	if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { -		cERROR(1, "Frame too large received.  Length: %d  Xid: %d", -			receive_len, xid); +	if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) {  		rc = -EIO; +		cifs_dbg(FYI, "Bad MID state?\n");  		goto out;  	} -	/* rcvd frame is ok */ +	buf = (char *)midQ->resp_buf; +	iov[0].iov_base = buf; +	iov[0].iov_len = get_rfc1002_length(buf) + 4; +	if (midQ->large_buf) +		*resp_buf_type = CIFS_LARGE_BUFFER; +	else +		*resp_buf_type = CIFS_SMALL_BUFFER; -	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, -						midQ->sequence_number+1); -			if (rc) { -				cERROR(1, "Unexpected SMB signature"); -				/* BB FIXME add code to kill session */ -			} -		} +	credits = ses->server->ops->get_credits(midQ); -		/* 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?"); -	} +	rc = ses->server->ops->check_receive(midQ, ses->server, +					     flags & CIFS_LOG_ERROR); +	/* mark it so buf will not be freed by cifs_delete_mid */ +	if ((flags & CIFS_NO_RESP) == 0) +		midQ->resp_buf = NULL;  out: -	DeleteMidQEntry(midQ); -	atomic_dec(&ses->server->inFlight); -	wake_up(&ses->server->request_q); +	cifs_delete_mid(midQ); +	add_credits(ses->server, credits, optype);  	return rc;  }  int -SendReceive(const unsigned int xid, struct cifsSesInfo *ses, +SendReceive(const unsigned int xid, struct cifs_ses *ses,  	    struct smb_hdr *in_buf, struct smb_hdr *out_buf, -	    int *pbytes_returned, const int long_op) +	    int *pbytes_returned, const int timeout)  {  	int rc = 0; -	unsigned int receive_len; -	unsigned long timeout;  	struct mid_q_entry *midQ;  	if (ses == NULL) { -		cERROR(1, "Null smb session"); +		cifs_dbg(VFS, "Null smb session\n");  		return -EIO;  	}  	if (ses->server == NULL) { -		cERROR(1, "Null tcp session"); +		cifs_dbg(VFS, "Null tcp session\n");  		return -EIO;  	} @@ -604,13 +844,14 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,  	   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); +	if (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize + +			MAX_CIFS_HDR_SIZE - 4) { +		cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", +			 be32_to_cpu(in_buf->smb_buf_length));  		return -EIO;  	} -	rc = wait_for_free_request(ses, long_op); +	rc = wait_for_free_request(ses->server, timeout, 0);  	if (rc)  		return rc; @@ -624,8 +865,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,  	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); +		add_credits(ses->server, 1, 0);  		return rc;  	} @@ -635,166 +875,71 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,  		goto out;  	} -	midQ->midState = MID_REQUEST_SUBMITTED; -#ifdef CONFIG_CIFS_STATS2 -	atomic_inc(&ses->server->inSend); -#endif -	rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); -#ifdef CONFIG_CIFS_STATS2 -	atomic_dec(&ses->server->inSend); -	midQ->when_sent = jiffies; -#endif +	midQ->mid_state = MID_REQUEST_SUBMITTED; + +	cifs_in_send_inc(ses->server); +	rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); +	cifs_in_send_dec(ses->server); +	cifs_save_when_sent(midQ); + +	if (rc < 0) +		ses->server->sequence_number -= 2; +  	mutex_unlock(&ses->server->srv_mutex);  	if (rc < 0)  		goto out; -	if (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) +	if (timeout == 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 == NULL) { -		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; -			} +	rc = wait_for_response(ses->server, midQ); +	if (rc != 0) { +		send_cancel(ses->server, in_buf, midQ); +		spin_lock(&GlobalMid_Lock); +		if (midQ->mid_state == MID_REQUEST_SUBMITTED) { +			/* no longer considered to be "in-flight" */ +			midQ->callback = DeleteMidQEntry; +			spin_unlock(&GlobalMid_Lock); +			add_credits(ses->server, 1, 0); +			return rc;  		}  		spin_unlock(&GlobalMid_Lock); -		DeleteMidQEntry(midQ); -		/* Update # of requests on wire to server */ -		atomic_dec(&ses->server->inFlight); -		wake_up(&ses->server->request_q); -		return rc;  	} -	spin_unlock(&GlobalMid_Lock); -	receive_len = midQ->resp_buf->smb_buf_length; - -	if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { -		cERROR(1, "Frame too large received.  Length: %d  Xid: %d", -			receive_len, xid); -		rc = -EIO; -		goto out; +	rc = cifs_sync_mid_result(midQ, ses->server); +	if (rc != 0) { +		add_credits(ses->server, 1, 0); +		return rc;  	} -	/* 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, -						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 { +	if (!midQ->resp_buf || !out_buf || +	    midQ->mid_state != MID_RESPONSE_RECEIVED) {  		rc = -EIO; -		cERROR(1, "Bad MID state?"); +		cifs_dbg(VFS, "Bad MID state?\n"); +		goto out;  	} +	*pbytes_returned = get_rfc1002_length(midQ->resp_buf); +	memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); +	rc = cifs_check_receive(midQ, ses->server, 0);  out: -	DeleteMidQEntry(midQ); -	atomic_dec(&ses->server->inFlight); -	wake_up(&ses->server->request_q); +	cifs_delete_mid(midQ); +	add_credits(ses->server, 1, 0);  	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, in_buf, in_buf->smb_buf_length); -	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, +send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,  			struct smb_hdr *in_buf,  			struct smb_hdr *out_buf)  {  	int bytes_returned; -	struct cifsSesInfo *ses = tcon->ses; +	struct cifs_ses *ses = tcon->ses;  	LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;  	/* We just modify the current in_buf to change @@ -804,31 +949,30 @@ send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon,  	pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;  	pSMB->Timeout = 0; -	pSMB->hdr.Mid = GetNextMid(ses->server); +	pSMB->hdr.Mid = get_next_mid(ses->server);  	return SendReceive(xid, ses, in_buf, out_buf, -			&bytes_returned, CIFS_STD_OP); +			&bytes_returned, 0);  }  int -SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, +SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,  	    struct smb_hdr *in_buf, struct smb_hdr *out_buf,  	    int *pbytes_returned)  {  	int rc = 0;  	int rstart = 0; -	unsigned int receive_len;  	struct mid_q_entry *midQ; -	struct cifsSesInfo *ses; +	struct cifs_ses *ses;  	if (tcon == NULL || tcon->ses == NULL) { -		cERROR(1, "Null smb session"); +		cifs_dbg(VFS, "Null smb session\n");  		return -EIO;  	}  	ses = tcon->ses;  	if (ses->server == NULL) { -		cERROR(1, "Null tcp session"); +		cifs_dbg(VFS, "Null tcp session\n");  		return -EIO;  	} @@ -839,13 +983,14 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,  	   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); +	if (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize + +			MAX_CIFS_HDR_SIZE - 4) { +		cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", +			 be32_to_cpu(in_buf->smb_buf_length));  		return -EIO;  	} -	rc = wait_for_free_request(ses, CIFS_BLOCKING_OP); +	rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0);  	if (rc)  		return rc; @@ -863,46 +1008,45 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,  	rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);  	if (rc) { -		DeleteMidQEntry(midQ); +		cifs_delete_mid(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, in_buf, in_buf->smb_buf_length); -#ifdef CONFIG_CIFS_STATS2 -	atomic_dec(&ses->server->inSend); -	midQ->when_sent = jiffies; -#endif +	midQ->mid_state = MID_REQUEST_SUBMITTED; +	cifs_in_send_inc(ses->server); +	rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); +	cifs_in_send_dec(ses->server); +	cifs_save_when_sent(midQ); + +	if (rc < 0) +		ses->server->sequence_number -= 2; +  	mutex_unlock(&ses->server->srv_mutex);  	if (rc < 0) { -		DeleteMidQEntry(midQ); +		cifs_delete_mid(midQ);  		return rc;  	}  	/* Wait for a reply - allow signals to interrupt. */  	rc = wait_event_interruptible(ses->server->response_q, -		(!(midQ->midState == MID_REQUEST_SUBMITTED)) || +		(!(midQ->mid_state == 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) && +		(midQ->mid_state == MID_REQUEST_SUBMITTED) &&  		((ses->server->tcpStatus == CifsGood) ||  		 (ses->server->tcpStatus == CifsNew))) {  		if (in_buf->Command == SMB_COM_TRANSACTION2) {  			/* POSIX lock. We send a NT_CANCEL SMB to cause the  			   blocking lock to return. */ - -			rc = send_nt_cancel(tcon, in_buf, midQ); +			rc = send_cancel(ses->server, in_buf, midQ);  			if (rc) { -				DeleteMidQEntry(midQ); +				cifs_delete_mid(midQ);  				return rc;  			}  		} else { @@ -914,94 +1058,44 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,  			/* If we get -ENOLCK back the lock may have  			   already been removed. Don't exit in this case. */  			if (rc && rc != -ENOLCK) { -				DeleteMidQEntry(midQ); +				cifs_delete_mid(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; +		rc = wait_for_response(ses->server, midQ); +		if (rc) { +			send_cancel(ses->server, in_buf, midQ); +			spin_lock(&GlobalMid_Lock); +			if (midQ->mid_state == MID_REQUEST_SUBMITTED) { +				/* no longer considered to be "in-flight" */ +				midQ->callback = DeleteMidQEntry; +				spin_unlock(&GlobalMid_Lock); +				return rc;  			} +			spin_unlock(&GlobalMid_Lock);  		} -		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; +		/* We got the response - restart system call. */ +		rstart = 1;  	} -	if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { -		cERROR(1, "Frame too large received.  Length: %d  Xid: %d", -			receive_len, xid); -		rc = -EIO; -		goto out; -	} +	rc = cifs_sync_mid_result(midQ, ses->server); +	if (rc != 0) +		return rc;  	/* rcvd frame is ok */ - -	if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) { +	if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {  		rc = -EIO; -		cERROR(1, "Bad MID state?"); +		cifs_dbg(VFS, "Bad MID state?\n");  		goto out;  	} -	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, -					   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)); - +	*pbytes_returned = get_rfc1002_length(midQ->resp_buf); +	memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); +	rc = cifs_check_receive(midQ, ses->server, 0);  out: -	DeleteMidQEntry(midQ); +	cifs_delete_mid(midQ);  	if (rstart && rc == -EACCES)  		return -ERESTARTSYS;  	return rc; diff --git a/fs/cifs/winucase.c b/fs/cifs/winucase.c new file mode 100644 index 00000000000..1506d4fddb2 --- /dev/null +++ b/fs/cifs/winucase.c @@ -0,0 +1,663 @@ +/* + * fs/cifs/winucase.c + * + * Copyright (c) Jeffrey Layton <jlayton@redhat.com>, 2013 + * + * This program is free software;  you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY;  without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program;  if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * The const tables in this file were converted from the following info + * provided by Microsoft: + * + * 3.1.5.3 Mapping UTF-16 Strings to Upper Case: + * + * http://msdn.microsoft.com/en-us/library/hh877830.aspx + * http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=10921 + * + * In particular, the table in "Windows 8 Upper Case Mapping Table.txt" was + * post-processed using the winucase_convert.pl script. + */ + +#include <linux/nls.h> + +wchar_t cifs_toupper(wchar_t in);  /* quiet sparse */ + +static const wchar_t t2_00[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, +	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, +	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, +	0x0058, 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, +	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, +	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0000, +	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, +}; + +static const wchar_t t2_01[256] = { +	0x0000, 0x0100, 0x0000, 0x0102, 0x0000, 0x0104, 0x0000, 0x0106, +	0x0000, 0x0108, 0x0000, 0x010a, 0x0000, 0x010c, 0x0000, 0x010e, +	0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0000, 0x0116, +	0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x011c, 0x0000, 0x011e, +	0x0000, 0x0120, 0x0000, 0x0122, 0x0000, 0x0124, 0x0000, 0x0126, +	0x0000, 0x0128, 0x0000, 0x012a, 0x0000, 0x012c, 0x0000, 0x012e, +	0x0000, 0x0000, 0x0000, 0x0132, 0x0000, 0x0134, 0x0000, 0x0136, +	0x0000, 0x0000, 0x0139, 0x0000, 0x013b, 0x0000, 0x013d, 0x0000, +	0x013f, 0x0000, 0x0141, 0x0000, 0x0143, 0x0000, 0x0145, 0x0000, +	0x0147, 0x0000, 0x0000, 0x014a, 0x0000, 0x014c, 0x0000, 0x014e, +	0x0000, 0x0150, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156, +	0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x0000, 0x015e, +	0x0000, 0x0160, 0x0000, 0x0162, 0x0000, 0x0164, 0x0000, 0x0166, +	0x0000, 0x0168, 0x0000, 0x016a, 0x0000, 0x016c, 0x0000, 0x016e, +	0x0000, 0x0170, 0x0000, 0x0172, 0x0000, 0x0174, 0x0000, 0x0176, +	0x0000, 0x0000, 0x0179, 0x0000, 0x017b, 0x0000, 0x017d, 0x0000, +	0x0243, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0000, 0x0000, +	0x0187, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x01f6, 0x0000, 0x0000, +	0x0000, 0x0198, 0x023d, 0x0000, 0x0000, 0x0000, 0x0220, 0x0000, +	0x0000, 0x01a0, 0x0000, 0x01a2, 0x0000, 0x01a4, 0x0000, 0x0000, +	0x01a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ac, 0x0000, 0x0000, +	0x01af, 0x0000, 0x0000, 0x0000, 0x01b3, 0x0000, 0x01b5, 0x0000, +	0x0000, 0x01b8, 0x0000, 0x0000, 0x0000, 0x01bc, 0x0000, 0x01f7, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01c4, 0x0000, +	0x0000, 0x01c7, 0x0000, 0x0000, 0x01ca, 0x0000, 0x01cd, 0x0000, +	0x01cf, 0x0000, 0x01d1, 0x0000, 0x01d3, 0x0000, 0x01d5, 0x0000, +	0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x018e, 0x0000, 0x01de, +	0x0000, 0x01e0, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6, +	0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee, +	0x0000, 0x0000, 0x0000, 0x01f1, 0x0000, 0x01f4, 0x0000, 0x0000, +	0x0000, 0x01f8, 0x0000, 0x01fa, 0x0000, 0x01fc, 0x0000, 0x01fe, +}; + +static const wchar_t t2_02[256] = { +	0x0000, 0x0200, 0x0000, 0x0202, 0x0000, 0x0204, 0x0000, 0x0206, +	0x0000, 0x0208, 0x0000, 0x020a, 0x0000, 0x020c, 0x0000, 0x020e, +	0x0000, 0x0210, 0x0000, 0x0212, 0x0000, 0x0214, 0x0000, 0x0216, +	0x0000, 0x0218, 0x0000, 0x021a, 0x0000, 0x021c, 0x0000, 0x021e, +	0x0000, 0x0000, 0x0000, 0x0222, 0x0000, 0x0224, 0x0000, 0x0226, +	0x0000, 0x0228, 0x0000, 0x022a, 0x0000, 0x022c, 0x0000, 0x022e, +	0x0000, 0x0230, 0x0000, 0x0232, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x023b, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0241, 0x0000, 0x0000, 0x0000, 0x0000, 0x0246, +	0x0000, 0x0248, 0x0000, 0x024a, 0x0000, 0x024c, 0x0000, 0x024e, +	0x2c6f, 0x2c6d, 0x0000, 0x0181, 0x0186, 0x0000, 0x0189, 0x018a, +	0x0000, 0x018f, 0x0000, 0x0190, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0193, 0x0000, 0x0000, 0x0194, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0197, 0x0196, 0x0000, 0x2c62, 0x0000, 0x0000, 0x0000, 0x019c, +	0x0000, 0x2c6e, 0x019d, 0x0000, 0x0000, 0x019f, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2c64, 0x0000, 0x0000, +	0x01a6, 0x0000, 0x0000, 0x01a9, 0x0000, 0x0000, 0x0000, 0x0000, +	0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x01b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_03[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0370, 0x0000, 0x0372, 0x0000, 0x0000, 0x0000, 0x0376, +	0x0000, 0x0000, 0x0000, 0x03fd, 0x03fe, 0x03ff, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, +	0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, +	0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, +	0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, +	0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03cf, +	0x0000, 0x03d8, 0x0000, 0x03da, 0x0000, 0x03dc, 0x0000, 0x03de, +	0x0000, 0x03e0, 0x0000, 0x03e2, 0x0000, 0x03e4, 0x0000, 0x03e6, +	0x0000, 0x03e8, 0x0000, 0x03ea, 0x0000, 0x03ec, 0x0000, 0x03ee, +	0x0000, 0x0000, 0x03f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x03f7, 0x0000, 0x0000, 0x03fa, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_04[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, +	0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, +	0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, +	0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, +	0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, +	0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, +	0x0000, 0x0460, 0x0000, 0x0462, 0x0000, 0x0464, 0x0000, 0x0466, +	0x0000, 0x0468, 0x0000, 0x046a, 0x0000, 0x046c, 0x0000, 0x046e, +	0x0000, 0x0470, 0x0000, 0x0472, 0x0000, 0x0474, 0x0000, 0x0476, +	0x0000, 0x0478, 0x0000, 0x047a, 0x0000, 0x047c, 0x0000, 0x047e, +	0x0000, 0x0480, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x048a, 0x0000, 0x048c, 0x0000, 0x048e, +	0x0000, 0x0490, 0x0000, 0x0492, 0x0000, 0x0494, 0x0000, 0x0496, +	0x0000, 0x0498, 0x0000, 0x049a, 0x0000, 0x049c, 0x0000, 0x049e, +	0x0000, 0x04a0, 0x0000, 0x04a2, 0x0000, 0x04a4, 0x0000, 0x04a6, +	0x0000, 0x04a8, 0x0000, 0x04aa, 0x0000, 0x04ac, 0x0000, 0x04ae, +	0x0000, 0x04b0, 0x0000, 0x04b2, 0x0000, 0x04b4, 0x0000, 0x04b6, +	0x0000, 0x04b8, 0x0000, 0x04ba, 0x0000, 0x04bc, 0x0000, 0x04be, +	0x0000, 0x0000, 0x04c1, 0x0000, 0x04c3, 0x0000, 0x04c5, 0x0000, +	0x04c7, 0x0000, 0x04c9, 0x0000, 0x04cb, 0x0000, 0x04cd, 0x04c0, +	0x0000, 0x04d0, 0x0000, 0x04d2, 0x0000, 0x04d4, 0x0000, 0x04d6, +	0x0000, 0x04d8, 0x0000, 0x04da, 0x0000, 0x04dc, 0x0000, 0x04de, +	0x0000, 0x04e0, 0x0000, 0x04e2, 0x0000, 0x04e4, 0x0000, 0x04e6, +	0x0000, 0x04e8, 0x0000, 0x04ea, 0x0000, 0x04ec, 0x0000, 0x04ee, +	0x0000, 0x04f0, 0x0000, 0x04f2, 0x0000, 0x04f4, 0x0000, 0x04f6, +	0x0000, 0x04f8, 0x0000, 0x04fa, 0x0000, 0x04fc, 0x0000, 0x04fe, +}; + +static const wchar_t t2_05[256] = { +	0x0000, 0x0500, 0x0000, 0x0502, 0x0000, 0x0504, 0x0000, 0x0506, +	0x0000, 0x0508, 0x0000, 0x050a, 0x0000, 0x050c, 0x0000, 0x050e, +	0x0000, 0x0510, 0x0000, 0x0512, 0x0000, 0x0514, 0x0000, 0x0516, +	0x0000, 0x0518, 0x0000, 0x051a, 0x0000, 0x051c, 0x0000, 0x051e, +	0x0000, 0x0520, 0x0000, 0x0522, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, +	0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, +	0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, +	0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, +	0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_1d[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0xa77d, 0x0000, 0x0000, 0x0000, 0x2c63, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_1e[256] = { +	0x0000, 0x1e00, 0x0000, 0x1e02, 0x0000, 0x1e04, 0x0000, 0x1e06, +	0x0000, 0x1e08, 0x0000, 0x1e0a, 0x0000, 0x1e0c, 0x0000, 0x1e0e, +	0x0000, 0x1e10, 0x0000, 0x1e12, 0x0000, 0x1e14, 0x0000, 0x1e16, +	0x0000, 0x1e18, 0x0000, 0x1e1a, 0x0000, 0x1e1c, 0x0000, 0x1e1e, +	0x0000, 0x1e20, 0x0000, 0x1e22, 0x0000, 0x1e24, 0x0000, 0x1e26, +	0x0000, 0x1e28, 0x0000, 0x1e2a, 0x0000, 0x1e2c, 0x0000, 0x1e2e, +	0x0000, 0x1e30, 0x0000, 0x1e32, 0x0000, 0x1e34, 0x0000, 0x1e36, +	0x0000, 0x1e38, 0x0000, 0x1e3a, 0x0000, 0x1e3c, 0x0000, 0x1e3e, +	0x0000, 0x1e40, 0x0000, 0x1e42, 0x0000, 0x1e44, 0x0000, 0x1e46, +	0x0000, 0x1e48, 0x0000, 0x1e4a, 0x0000, 0x1e4c, 0x0000, 0x1e4e, +	0x0000, 0x1e50, 0x0000, 0x1e52, 0x0000, 0x1e54, 0x0000, 0x1e56, +	0x0000, 0x1e58, 0x0000, 0x1e5a, 0x0000, 0x1e5c, 0x0000, 0x1e5e, +	0x0000, 0x1e60, 0x0000, 0x1e62, 0x0000, 0x1e64, 0x0000, 0x1e66, +	0x0000, 0x1e68, 0x0000, 0x1e6a, 0x0000, 0x1e6c, 0x0000, 0x1e6e, +	0x0000, 0x1e70, 0x0000, 0x1e72, 0x0000, 0x1e74, 0x0000, 0x1e76, +	0x0000, 0x1e78, 0x0000, 0x1e7a, 0x0000, 0x1e7c, 0x0000, 0x1e7e, +	0x0000, 0x1e80, 0x0000, 0x1e82, 0x0000, 0x1e84, 0x0000, 0x1e86, +	0x0000, 0x1e88, 0x0000, 0x1e8a, 0x0000, 0x1e8c, 0x0000, 0x1e8e, +	0x0000, 0x1e90, 0x0000, 0x1e92, 0x0000, 0x1e94, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x1ea0, 0x0000, 0x1ea2, 0x0000, 0x1ea4, 0x0000, 0x1ea6, +	0x0000, 0x1ea8, 0x0000, 0x1eaa, 0x0000, 0x1eac, 0x0000, 0x1eae, +	0x0000, 0x1eb0, 0x0000, 0x1eb2, 0x0000, 0x1eb4, 0x0000, 0x1eb6, +	0x0000, 0x1eb8, 0x0000, 0x1eba, 0x0000, 0x1ebc, 0x0000, 0x1ebe, +	0x0000, 0x1ec0, 0x0000, 0x1ec2, 0x0000, 0x1ec4, 0x0000, 0x1ec6, +	0x0000, 0x1ec8, 0x0000, 0x1eca, 0x0000, 0x1ecc, 0x0000, 0x1ece, +	0x0000, 0x1ed0, 0x0000, 0x1ed2, 0x0000, 0x1ed4, 0x0000, 0x1ed6, +	0x0000, 0x1ed8, 0x0000, 0x1eda, 0x0000, 0x1edc, 0x0000, 0x1ede, +	0x0000, 0x1ee0, 0x0000, 0x1ee2, 0x0000, 0x1ee4, 0x0000, 0x1ee6, +	0x0000, 0x1ee8, 0x0000, 0x1eea, 0x0000, 0x1eec, 0x0000, 0x1eee, +	0x0000, 0x1ef0, 0x0000, 0x1ef2, 0x0000, 0x1ef4, 0x0000, 0x1ef6, +	0x0000, 0x1ef8, 0x0000, 0x1efa, 0x0000, 0x1efc, 0x0000, 0x1efe, +}; + +static const wchar_t t2_1f[256] = { +	0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x1f59, 0x0000, 0x1f5b, 0x0000, 0x1f5d, 0x0000, 0x1f5f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, +	0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x0000, 0x0000, +	0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1fb8, 0x1fb9, 0x0000, 0x1fbc, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x1fcc, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1fd8, 0x1fd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x1fe8, 0x1fe9, 0x0000, 0x0000, 0x0000, 0x1fec, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x1ffc, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_21[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2132, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, +	0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, +	0x0000, 0x0000, 0x0000, 0x0000, 0x2183, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_24[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, +	0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, +	0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, +	0x24ce, 0x24cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_2c[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, +	0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, +	0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, +	0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, +	0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, +	0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x0000, +	0x0000, 0x2c60, 0x0000, 0x0000, 0x0000, 0x023a, 0x023e, 0x0000, +	0x2c67, 0x0000, 0x2c69, 0x0000, 0x2c6b, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x2c72, 0x0000, 0x0000, 0x2c75, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x2c80, 0x0000, 0x2c82, 0x0000, 0x2c84, 0x0000, 0x2c86, +	0x0000, 0x2c88, 0x0000, 0x2c8a, 0x0000, 0x2c8c, 0x0000, 0x2c8e, +	0x0000, 0x2c90, 0x0000, 0x2c92, 0x0000, 0x2c94, 0x0000, 0x2c96, +	0x0000, 0x2c98, 0x0000, 0x2c9a, 0x0000, 0x2c9c, 0x0000, 0x2c9e, +	0x0000, 0x2ca0, 0x0000, 0x2ca2, 0x0000, 0x2ca4, 0x0000, 0x2ca6, +	0x0000, 0x2ca8, 0x0000, 0x2caa, 0x0000, 0x2cac, 0x0000, 0x2cae, +	0x0000, 0x2cb0, 0x0000, 0x2cb2, 0x0000, 0x2cb4, 0x0000, 0x2cb6, +	0x0000, 0x2cb8, 0x0000, 0x2cba, 0x0000, 0x2cbc, 0x0000, 0x2cbe, +	0x0000, 0x2cc0, 0x0000, 0x2cc2, 0x0000, 0x2cc4, 0x0000, 0x2cc6, +	0x0000, 0x2cc8, 0x0000, 0x2cca, 0x0000, 0x2ccc, 0x0000, 0x2cce, +	0x0000, 0x2cd0, 0x0000, 0x2cd2, 0x0000, 0x2cd4, 0x0000, 0x2cd6, +	0x0000, 0x2cd8, 0x0000, 0x2cda, 0x0000, 0x2cdc, 0x0000, 0x2cde, +	0x0000, 0x2ce0, 0x0000, 0x2ce2, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_2d[256] = { +	0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, +	0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, +	0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, +	0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, +	0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_a6[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0xa640, 0x0000, 0xa642, 0x0000, 0xa644, 0x0000, 0xa646, +	0x0000, 0xa648, 0x0000, 0xa64a, 0x0000, 0xa64c, 0x0000, 0xa64e, +	0x0000, 0xa650, 0x0000, 0xa652, 0x0000, 0xa654, 0x0000, 0xa656, +	0x0000, 0xa658, 0x0000, 0xa65a, 0x0000, 0xa65c, 0x0000, 0xa65e, +	0x0000, 0x0000, 0x0000, 0xa662, 0x0000, 0xa664, 0x0000, 0xa666, +	0x0000, 0xa668, 0x0000, 0xa66a, 0x0000, 0xa66c, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0xa680, 0x0000, 0xa682, 0x0000, 0xa684, 0x0000, 0xa686, +	0x0000, 0xa688, 0x0000, 0xa68a, 0x0000, 0xa68c, 0x0000, 0xa68e, +	0x0000, 0xa690, 0x0000, 0xa692, 0x0000, 0xa694, 0x0000, 0xa696, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_a7[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0xa722, 0x0000, 0xa724, 0x0000, 0xa726, +	0x0000, 0xa728, 0x0000, 0xa72a, 0x0000, 0xa72c, 0x0000, 0xa72e, +	0x0000, 0x0000, 0x0000, 0xa732, 0x0000, 0xa734, 0x0000, 0xa736, +	0x0000, 0xa738, 0x0000, 0xa73a, 0x0000, 0xa73c, 0x0000, 0xa73e, +	0x0000, 0xa740, 0x0000, 0xa742, 0x0000, 0xa744, 0x0000, 0xa746, +	0x0000, 0xa748, 0x0000, 0xa74a, 0x0000, 0xa74c, 0x0000, 0xa74e, +	0x0000, 0xa750, 0x0000, 0xa752, 0x0000, 0xa754, 0x0000, 0xa756, +	0x0000, 0xa758, 0x0000, 0xa75a, 0x0000, 0xa75c, 0x0000, 0xa75e, +	0x0000, 0xa760, 0x0000, 0xa762, 0x0000, 0xa764, 0x0000, 0xa766, +	0x0000, 0xa768, 0x0000, 0xa76a, 0x0000, 0xa76c, 0x0000, 0xa76e, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0xa779, 0x0000, 0xa77b, 0x0000, 0x0000, 0xa77e, +	0x0000, 0xa780, 0x0000, 0xa782, 0x0000, 0xa784, 0x0000, 0xa786, +	0x0000, 0x0000, 0x0000, 0x0000, 0xa78b, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_ff[256] = { +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, +	0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, +	0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, +	0xff38, 0xff39, 0xff3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t *const toplevel[256] = { +	t2_00, t2_01, t2_02, t2_03, t2_04, t2_05,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL, t2_1d, t2_1e, t2_1f, +	NULL, t2_21,  NULL,  NULL, t2_24,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL, t2_2c, t2_2d,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL, t2_a6, t2_a7, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, +	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, t2_ff, +}; + +/** + * cifs_toupper - convert a wchar_t from lower to uppercase + * @in: character to convert from lower to uppercase + * + * This function consults the static tables above to convert a wchar_t from + * lower to uppercase. In the event that there is no mapping, the original + * "in" character is returned. + */ +wchar_t +cifs_toupper(wchar_t in) +{ +	unsigned char idx; +	const wchar_t *tbl; +	wchar_t out; + +	/* grab upper byte */ +	idx = (in & 0xff00) >> 8; + +	/* find pointer to 2nd layer table */ +	tbl = toplevel[idx]; +	if (!tbl) +		return in; + +	/* grab lower byte */ +	idx = in & 0xff; + +	/* look up character in table */ +	out = tbl[idx]; +	if (out) +		return out; + +	return in; +} diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index a264b744bb4..5ac836a86b1 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -22,6 +22,7 @@  #include <linux/fs.h>  #include <linux/posix_acl_xattr.h>  #include <linux/slab.h> +#include <linux/xattr.h>  #include "cifsfs.h"  #include "cifspdu.h"  #include "cifsglob.h" @@ -30,25 +31,18 @@  #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 */ - +#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" +/* BB need to add server (Samba e.g) support for security and trusted prefix */  int cifs_removexattr(struct dentry *direntry, const char *ea_name)  {  	int rc = -EOPNOTSUPP;  #ifdef CONFIG_CIFS_XATTR -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *pTcon;  	struct super_block *sb;  	char *full_path = NULL; @@ -66,7 +60,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)  		return PTR_ERR(tlink);  	pTcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -74,12 +68,12 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)  		goto remove_ea_exit;  	}  	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); +		cifs_dbg(FYI, "Null xattr names not supported\n"); +	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) +		&& (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) { +		cifs_dbg(FYI, +			 "illegal xattr request %s (only user namespace supported)\n", +			 ea_name);  		/* BB what if no namespace prefix? */  		/* Should we just pass them to server, except for  		system and perhaps security prefixes? */ @@ -87,14 +81,16 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)  		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); +		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ +		if (pTcon->ses->server->ops->set_EA) +			rc = pTcon->ses->server->ops->set_EA(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); +	free_xid(xid);  	cifs_put_tlink(tlink);  #endif  	return rc; @@ -105,10 +101,10 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  {  	int rc = -EOPNOTSUPP;  #ifdef CONFIG_CIFS_XATTR -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *pTcon;  	struct super_block *sb;  	char *full_path; @@ -126,7 +122,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  		return PTR_ERR(tlink);  	pTcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -140,31 +136,59 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  		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"); +		cifs_dbg(FYI, "size of EA value too large\n");  		rc = -EOPNOTSUPP;  		goto set_ea_exit;  	}  	if (ea_name == NULL) { -		cFYI(1, "Null xattr names not supported"); -	} else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) { +		cifs_dbg(FYI, "Null xattr names not supported\n"); +	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) +		   == 0) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)  			goto set_ea_exit;  		if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) -			cFYI(1, "attempt to set cifs inode metadata"); +			cifs_dbg(FYI, "attempt to set cifs inode metadata\n"); -		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) { +		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ +		if (pTcon->ses->server->ops->set_EA) +			rc = pTcon->ses->server->ops->set_EA(xid, pTcon, +				full_path, ea_name, ea_value, (__u16)value_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR); +	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) +		   == 0) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)  			goto set_ea_exit; -		ea_name += 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); +		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ +		if (pTcon->ses->server->ops->set_EA) +			rc = pTcon->ses->server->ops->set_EA(xid, pTcon, +				full_path, ea_name, ea_value, (__u16)value_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR); +	} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL, +			strlen(CIFS_XATTR_CIFS_ACL)) == 0) { +#ifdef CONFIG_CIFS_ACL +		struct cifs_ntsd *pacl; +		pacl = kmalloc(value_size, GFP_KERNEL); +		if (!pacl) { +			rc = -ENOMEM; +		} else { +			memcpy(pacl, ea_value, value_size); +			if (pTcon->ses->server->ops->set_acl) +				rc = pTcon->ses->server->ops->set_acl(pacl, +						value_size, direntry->d_inode, +						full_path, CIFS_ACL_DACL); +			else +				rc = -EOPNOTSUPP; +			if (rc == 0) /* force revalidate of the inode */ +				CIFS_I(direntry->d_inode)->time = 0; +			kfree(pacl); +		} +#else +		cifs_dbg(FYI, "Set CIFS ACL not supported yet\n"); +#endif /* CONFIG_CIFS_ACL */  	} else {  		int temp;  		temp = strncmp(ea_name, POSIX_ACL_XATTR_ACCESS, @@ -177,9 +201,9 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  					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); +			cifs_dbg(FYI, "set POSIX ACL rc %d\n", rc);  #else -			cFYI(1, "set POSIX ACL not supported"); +			cifs_dbg(FYI, "set POSIX ACL not supported\n");  #endif  		} else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,  				   strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { @@ -190,13 +214,13 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  					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); +			cifs_dbg(FYI, "set POSIX default ACL rc %d\n", rc);  #else -			cFYI(1, "set default POSIX ACL not supported"); +			cifs_dbg(FYI, "set default POSIX ACL not supported\n");  #endif  		} else { -			cFYI(1, "illegal xattr request %s (only user namespace" -				" supported)", ea_name); +			cifs_dbg(FYI, "illegal xattr request %s (only user namespace supported)\n", +				 ea_name);  		  /* BB what if no namespace prefix? */  		  /* Should we just pass them to server, except for  		  system and perhaps security prefixes? */ @@ -205,7 +229,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  set_ea_exit:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  #endif  	return rc; @@ -216,10 +240,10 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  {  	ssize_t rc = -EOPNOTSUPP;  #ifdef CONFIG_CIFS_XATTR -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *pTcon;  	struct super_block *sb;  	char *full_path; @@ -237,7 +261,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  		return PTR_ERR(tlink);  	pTcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -247,27 +271,32 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  	/* 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) { +		cifs_dbg(FYI, "Null xattr names not supported\n"); +	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) +		   == 0) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)  			goto get_ea_exit;  		if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) { -			cFYI(1, "attempt to query cifs inode metadata"); +			cifs_dbg(FYI, "attempt to query cifs inode metadata\n");  			/* revalidate/getattr then populate from inode */  		} /* BB add else when above is implemented */ -		ea_name += 5; /* skip past user. prefix */ -		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, -			buf_size, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); -	} else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) { +		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ +		if (pTcon->ses->server->ops->query_all_EAs) +			rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, +				full_path, ea_name, ea_value, buf_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR); +	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)  			goto get_ea_exit; -		ea_name += 4; /* skip past os2. prefix */ -		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, -			buf_size, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ +		if (pTcon->ses->server->ops->query_all_EAs) +			rc = pTcon->ses->server->ops->query_all_EAs(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 @@ -277,29 +306,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  				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"); +		cifs_dbg(FYI, "Query POSIX ACL not supported yet\n");  #endif /* CONFIG_CIFS_POSIX */  	} else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,  			  strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { @@ -311,18 +319,46 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  				cifs_sb->mnt_cifs_flags &  					CIFS_MOUNT_MAP_SPECIAL_CHR);  #else -		cFYI(1, "query POSIX default ACL not supported yet"); -#endif +		cifs_dbg(FYI, "Query POSIX default ACL not supported yet\n"); +#endif /* CONFIG_CIFS_POSIX */ +	} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL, +				strlen(CIFS_XATTR_CIFS_ACL)) == 0) { +#ifdef CONFIG_CIFS_ACL +			u32 acllen; +			struct cifs_ntsd *pacl; + +			if (pTcon->ses->server->ops->get_acl == NULL) +				goto get_ea_exit; /* rc already EOPNOTSUPP */ + +			pacl = pTcon->ses->server->ops->get_acl(cifs_sb, +					direntry->d_inode, full_path, &acllen); +			if (IS_ERR(pacl)) { +				rc = PTR_ERR(pacl); +				cifs_dbg(VFS, "%s: error %zd getting sec desc\n", +					 __func__, rc); +			} else { +				if (ea_value) { +					if (acllen > buf_size) +						acllen = -ERANGE; +					else +						memcpy(ea_value, pacl, acllen); +				} +				rc = acllen; +				kfree(pacl); +			} +#else +			cifs_dbg(FYI, "Query CIFS ACL not supported yet\n"); +#endif /* CONFIG_CIFS_ACL */  	} else if (strncmp(ea_name, -		  CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { -		cFYI(1, "Trusted xattr namespace not supported yet"); +		  XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { +		cifs_dbg(FYI, "Trusted xattr namespace not supported yet\n");  	} else if (strncmp(ea_name, -		  CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { -		cFYI(1, "Security xattr namespace not supported yet"); +		  XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { +		cifs_dbg(FYI, "Security xattr namespace not supported yet\n");  	} else -		cFYI(1, -		    "illegal xattr request %s (only user namespace supported)", -		     ea_name); +		cifs_dbg(FYI, +			 "illegal xattr request %s (only user namespace supported)\n", +			 ea_name);  	/* We could add an additional check for streams ie  	    if proc/fs/cifs/streamstoxattr is set then @@ -334,7 +370,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  get_ea_exit:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  #endif  	return rc; @@ -344,10 +380,10 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)  {  	ssize_t rc = -EOPNOTSUPP;  #ifdef CONFIG_CIFS_XATTR -	int xid; +	unsigned int xid;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifsTconInfo *pTcon; +	struct cifs_tcon *pTcon;  	struct super_block *sb;  	char *full_path; @@ -368,7 +404,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)  		return PTR_ERR(tlink);  	pTcon = tlink_tcon(tlink); -	xid = GetXid(); +	xid = get_xid();  	full_path = build_path_from_dentry(direntry);  	if (full_path == NULL) { @@ -381,14 +417,15 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)  	/* if proc/fs/cifs/streamstoxattr is set then  		search server for EAs or streams to  		returns as xattrs */ -	rc = CIFSSMBQAllEAs(xid, pTcon, full_path, NULL, data, -				buf_size, cifs_sb->local_nls, -				cifs_sb->mnt_cifs_flags & -					CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (pTcon->ses->server->ops->query_all_EAs) +		rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, +				full_path, NULL, data, buf_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  list_ea_exit:  	kfree(full_path); -	FreeXid(xid); +	free_xid(xid);  	cifs_put_tlink(tlink);  #endif  	return rc;  | 
