summaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
committerTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
commitfcbb27b0ec6dcbc5a5108cb8fb19eae64593d204 (patch)
tree22962a4387943edc841c72a4e636a068c66d58fd /fs/cifs
downloadast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.zip
ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.tar.gz
Initial import of modified Linux 2.6.28 tree
Original upstream URL: git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git | branch linux-2.6.28.y
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/AUTHORS53
-rw-r--r--fs/cifs/CHANGES979
-rw-r--r--fs/cifs/Kconfig142
-rw-r--r--fs/cifs/Makefile13
-rw-r--r--fs/cifs/README700
-rw-r--r--fs/cifs/TODO129
-rw-r--r--fs/cifs/asn1.c642
-rw-r--r--fs/cifs/cifs_debug.c795
-rw-r--r--fs/cifs/cifs_debug.h76
-rw-r--r--fs/cifs/cifs_dfs_ref.c393
-rw-r--r--fs/cifs/cifs_fs_sb.h52
-rw-r--r--fs/cifs/cifs_spnego.c162
-rw-r--r--fs/cifs/cifs_spnego.h47
-rw-r--r--fs/cifs/cifs_unicode.c90
-rw-r--r--fs/cifs/cifs_unicode.h357
-rw-r--r--fs/cifs/cifs_uniupr.h253
-rw-r--r--fs/cifs/cifsacl.c736
-rw-r--r--fs/cifs/cifsacl.h84
-rw-r--r--fs/cifs/cifsencrypt.c427
-rw-r--r--fs/cifs/cifsencrypt.h32
-rw-r--r--fs/cifs/cifsfs.c1193
-rw-r--r--fs/cifs/cifsfs.h105
-rw-r--r--fs/cifs/cifsglob.h670
-rw-r--r--fs/cifs/cifspdu.h2634
-rw-r--r--fs/cifs/cifsproto.h372
-rw-r--r--fs/cifs/cifssmb.c5690
-rw-r--r--fs/cifs/cn_cifs.h37
-rw-r--r--fs/cifs/connect.c3747
-rw-r--r--fs/cifs/dir.c636
-rw-r--r--fs/cifs/dns_resolve.c182
-rw-r--r--fs/cifs/dns_resolve.h32
-rw-r--r--fs/cifs/export.c67
-rw-r--r--fs/cifs/fcntl.c118
-rw-r--r--fs/cifs/file.c2161
-rw-r--r--fs/cifs/inode.c1953
-rw-r--r--fs/cifs/ioctl.c110
-rw-r--r--fs/cifs/link.c324
-rw-r--r--fs/cifs/md4.c205
-rw-r--r--fs/cifs/md5.c366
-rw-r--r--fs/cifs/md5.h38
-rw-r--r--fs/cifs/misc.c771
-rw-r--r--fs/cifs/netmisc.c940
-rw-r--r--fs/cifs/nterr.c687
-rw-r--r--fs/cifs/nterr.h556
-rw-r--r--fs/cifs/ntlmssp.h97
-rw-r--r--fs/cifs/readdir.c1117
-rw-r--r--fs/cifs/rfc1002pdu.h74
-rw-r--r--fs/cifs/sess.c639
-rw-r--r--fs/cifs/smbdes.c418
-rw-r--r--fs/cifs/smbencrypt.c289
-rw-r--r--fs/cifs/smberr.h182
-rw-r--r--fs/cifs/transport.c1097
-rw-r--r--fs/cifs/xattr.c372
53 files changed, 34041 insertions, 0 deletions
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS
new file mode 100644
index 0000000..9c136d7
--- /dev/null
+++ b/fs/cifs/AUTHORS
@@ -0,0 +1,53 @@
+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
+Igor Mammedov (DFS support)
+
+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
new file mode 100644
index 0000000..4c1cb9a
--- /dev/null
+++ b/fs/cifs/CHANGES
@@ -0,0 +1,979 @@
+Fix oops in cifs_dfs_ref.c when prefixpath is not reachable when using DFS.
+
+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
new file mode 100644
index 0000000..341a989
--- /dev/null
+++ b/fs/cifs/Kconfig
@@ -0,0 +1,142 @@
+config CIFS
+ tristate "CIFS support (advanced network filesystem, SMBFS successor)"
+ depends on INET
+ select NLS
+ help
+ This is the client VFS module for the Common Internet File System
+ (CIFS) protocol which is the successor to the Server Message Block
+ (SMB) protocol, the native file sharing mechanism for most early
+ PC operating systems. The CIFS protocol is fully supported by
+ file servers such as Windows 2000 (including Windows 2003, NT 4
+ and Windows XP) as well by Samba (which provides excellent CIFS
+ server support for Linux and many other operating systems). Limited
+ support for OS/2 and Windows ME and similar servers is provided as
+ well.
+
+ The cifs module provides an advanced network file system
+ client for mounting to CIFS compliant servers. It includes
+ support for DFS (hierarchical name space), secure per-user
+ session establishment via Kerberos or NTLM or NTLMv2,
+ safe distributed caching (oplock), optional packet
+ signing, Unicode and other internationalization improvements.
+ If you need to mount to Samba or Windows from this machine, say Y.
+
+config CIFS_STATS
+ bool "CIFS statistics"
+ depends on CIFS
+ help
+ Enabling this option will cause statistics for each server share
+ mounted by the cifs client to be displayed in /proc/fs/cifs/Stats
+
+config CIFS_STATS2
+ bool "Extended statistics"
+ depends on CIFS_STATS
+ help
+ Enabling this option will allow more detailed statistics on SMB
+ request timing to be displayed in /proc/fs/cifs/DebugData and also
+ allow optional logging of slow responses to dmesg (depending on the
+ value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details).
+ These additional statistics may have a minor effect on performance
+ and memory utilization.
+
+ Unless you are a developer or are doing network performance analysis
+ or tuning, say N.
+
+config CIFS_WEAK_PW_HASH
+ bool "Support legacy servers which use weaker LANMAN security"
+ depends on CIFS
+ help
+ Modern CIFS servers including Samba and most Windows versions
+ (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos)
+ security mechanisms. These hash the password more securely
+ than the mechanisms used in the older LANMAN version of the
+ SMB protocol but LANMAN based authentication is needed to
+ establish sessions with some old SMB servers.
+
+ Enabling this option allows the cifs module to mount to older
+ LANMAN based servers such as OS/2 and Windows 95, but such
+ mounts may be less secure than mounts using NTLM or more recent
+ security mechanisms if you are on a public network. Unless you
+ have a need to access old SMB servers (and are on a private
+ network) you probably want to say N. Even if this support
+ is enabled in the kernel build, LANMAN authentication will not be
+ used automatically. At runtime LANMAN mounts are disabled but
+ can be set to required (or optional) either in
+ /proc/fs/cifs (see fs/cifs/README for more detail) or via an
+ option on the mount command. This support is disabled by
+ default in order to reduce the possibility of a downgrade
+ attack.
+
+ If unsure, say N.
+
+config CIFS_UPCALL
+ bool "Kerberos/SPNEGO advanced session setup"
+ depends on CIFS && KEYS
+ help
+ Enables an upcall mechanism for CIFS which accesses
+ userspace helper utilities to provide SPNEGO packaged (RFC 4178)
+ Kerberos tickets which are needed to mount to certain secure servers
+ (for which more secure Kerberos authentication is required). If
+ unsure, say N.
+
+config CIFS_XATTR
+ bool "CIFS extended attributes"
+ depends on CIFS
+ help
+ Extended attributes are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page, or visit
+ <http://acl.bestbits.at/> for details). CIFS maps the name of
+ extended attributes beginning with the user namespace prefix
+ to SMB/CIFS EAs. EAs are stored on Windows servers without the
+ user namespace prefix, but their names are seen by Linux cifs clients
+ prefaced by the user namespace prefix. The system namespace
+ (used by some filesystems to store ACLs) is not supported at
+ this time.
+
+ If unsure, say N.
+
+config CIFS_POSIX
+ bool "CIFS POSIX Extensions"
+ depends on CIFS_XATTR
+ help
+ Enabling this option will cause the cifs client to attempt to
+ negotiate a newer dialect with servers, such as Samba 3.0.5
+ or later, that optionally can handle more POSIX like (rather
+ than Windows like) file behavior. It also enables
+ support for POSIX ACLs (getfacl and setfacl) to servers
+ (such as Samba 3.10 and later) which can negotiate
+ CIFS POSIX ACL support. If unsure, say N.
+
+config CIFS_DEBUG2
+ bool "Enable additional CIFS debugging routines"
+ depends on CIFS
+ help
+ Enabling this option adds a few more debugging routines
+ to the cifs code which slightly increases the size of
+ the cifs module and can cause additional logging of debug
+ messages in some error paths, slowing performance. This
+ option can be turned off unless you are debugging
+ cifs problems. If unsure, say N.
+
+config CIFS_EXPERIMENTAL
+ bool "CIFS Experimental Features (EXPERIMENTAL)"
+ depends on CIFS && EXPERIMENTAL
+ help
+ Enables cifs features under testing. These features are
+ experimental and currently include DFS support and directory
+ change notification ie fcntl(F_DNOTIFY), as well as the upcall
+ mechanism which will be used for Kerberos session negotiation
+ and uid remapping. Some of these features also may depend on
+ setting a value of 1 to the pseudo-file /proc/fs/cifs/Experimental
+ (which is disabled by default). See the file fs/cifs/README
+ for more details. If unsure, say N.
+
+config CIFS_DFS_UPCALL
+ bool "DFS feature support (EXPERIMENTAL)"
+ depends on CIFS_EXPERIMENTAL
+ depends on KEYS
+ help
+ Enables an upcall mechanism for CIFS which contacts userspace
+ helper utilities to provide server name resolution (host names to
+ IP addresses) which is needed for implicit mounts of DFS junction
+ points. If unsure, say N.
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
new file mode 100644
index 0000000..6ba43fb
--- /dev/null
+++ b/fs/cifs/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for Linux CIFS VFS client
+#
+obj-$(CONFIG_CIFS) += cifs.o
+
+cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
+ link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \
+ md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o \
+ readdir.o ioctl.o sess.o export.o cifsacl.o
+
+cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
+
+cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
diff --git a/fs/cifs/README b/fs/cifs/README
new file mode 100644
index 0000000..a439dc1
--- /dev/null
+++ b/fs/cifs/README
@@ -0,0 +1,700 @@
+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
+ uid Set the default uid for inodes. For mounts to servers
+ which do support the CIFS Unix extensions, such as a
+ properly configured Samba server, the server provides
+ the uid, gid and mode so this parameter should not be
+ specified unless the server and clients uid and gid
+ numbering differ. If the server and client are in the
+ same domain (e.g. running winbind or nss_ldap) and
+ the server supports the Unix Extensions then the uid
+ and gid can be retrieved from the server (and uid
+ and gid would not have to be specifed on the mount.
+ For servers which do not support the CIFS Unix
+ extensions, the default uid (and gid) returned on lookup
+ of existing files will be the uid (gid) of the person
+ who executed the mount (root, except when mount.cifs
+ is configured setuid for user mounts) unless the "uid="
+ (gid) mount option is specified. For the uid (gid) of newly
+ created files and directories, ie files created since
+ the last mount of the server share, the expected uid
+ (gid) is cached as long as the inode remains in
+ memory on the client. Also note that permission
+ checks (authorization checks) on accesses to a file occur
+ at the server, but there are cases in which an administrator
+ may want to restrict at the client as well. For those
+ servers which do not report a uid/gid owner
+ (such as Windows), permissions can also be checked at the
+ client, and a crude form of client side permission checking
+ can be enabled by specifying file_mode and dir_mode on
+ the client. Note that the mount.cifs helper must be
+ at version 1.10 or higher to support specifying the uid
+ (or gid) in non-numeric form.
+ gid Set the default gid for inodes (similar to above).
+ file_mode If CIFS Unix extensions are not supported by the server
+ this overrides the default mode for file inodes.
+ dir_mode If CIFS Unix extensions are not supported by the server
+ this overrides the default mode for directory inodes.
+ port attempt to contact the server on this tcp port, before
+ trying the usual ports (port 445, then 139).
+ iocharset Codepage used to convert local path names to and from
+ Unicode. Unicode is used by default for network path
+ names if the server supports it. If iocharset is
+ not specified then the nls_default specified
+ during the local client kernel build will be used.
+ If server does not support Unicode, this parameter is
+ unused.
+ rsize default read size (usually 16K). The client currently
+ can not use rsize larger than CIFSMaxBufSize. CIFSMaxBufSize
+ defaults to 16K and may be changed (from 8K to the maximum
+ kmalloc size allowed by your kernel) at module install time
+ for cifs.ko. Setting CIFSMaxBufSize to a very large value
+ will cause cifs to use more memory and may reduce performance
+ in some cases. To use rsize greater than 127K (the original
+ cifs protocol maximum) also requires that the server support
+ a new Unix Capability flag (for very large read) which some
+ newer servers (e.g. Samba 3.0.26 or later) do. rsize can be
+ set from a minimum of 2048 to a maximum of 130048 (127K or
+ CIFSMaxBufSize, whichever is smaller)
+ wsize default write size (default 57344)
+ maximum wsize currently allowed by CIFS is 57344 (fourteen
+ 4096 byte pages)
+ rw mount the network share read-write (note that the
+ server may still consider the share read-only)
+ ro mount network share read-only
+ version used to distinguish different versions of the
+ mount helper utility (not typically needed)
+ sep if first mount option (after the -o), overrides
+ the comma as the separator between the mount
+ parms. e.g.
+ -o user=myname,password=mypassword,domain=mydom
+ could be passed instead with period as the separator by
+ -o sep=.user=myname.password=mypassword.domain=mydom
+ this might be useful when comma is contained within username
+ or password or domain. This option is less important
+ when the cifs mount helper cifs.mount (version 1.1 or later)
+ is used.
+ nosuid Do not allow remote executables with the suid bit
+ program to be executed. This is only meaningful for mounts
+ to servers such as Samba which support the CIFS Unix Extensions.
+ If you do not trust the servers in your network (your mount
+ targets) it is recommended that you specify this option for
+ greater security.
+ exec Permit execution of binaries on the mount.
+ noexec Do not permit execution of binaries on the mount.
+ dev Recognize block devices on the remote mount.
+ nodev Do not recognize devices on the remote mount.
+ suid Allow remote files on this mountpoint with suid enabled to
+ be executed (default for mounts when executed as root,
+ nosuid is default for user mounts).
+ credentials Although ignored by the cifs kernel component, it is used by
+ the mount helper, mount.cifs. When mount.cifs is installed it
+ opens and reads the credential file specified in order
+ to obtain the userid and password arguments which are passed to
+ the cifs vfs.
+ guest Although ignored by the kernel component, the mount.cifs
+ mount helper will not prompt the user for a password
+ if guest is specified on the mount options. If no
+ password is specified a null password will be used.
+ perm Client does permission checks (vfs_permission check of uid
+ and gid of the file against the mode and desired operation),
+ Note that this is in addition to the normal ACL check on the
+ target machine done by the server software.
+ Client permission checking is enabled by default.
+ noperm Client does not do permission checks. This can expose
+ files on this mount to access by other users on the local
+ client system. It is typically only needed when the server
+ supports the CIFS Unix Extensions but the UIDs/GIDs on the
+ client and server system do not match closely enough to allow
+ access by the user doing the mount, but it may be useful with
+ non CIFS Unix Extension mounts for cases in which the default
+ mode is specified on the mount but is not to be enforced on the
+ client (e.g. perhaps when MultiUserMount is enabled)
+ Note that this does not affect the normal ACL check on the
+ target machine done by the server software (of the server
+ ACL against the user name provided at mount time).
+ serverino Use server's inode numbers instead of generating automatically
+ incrementing inode numbers on the client. Although this will
+ make it easier to spot hardlinked files (as they will have
+ the same inode numbers) and inode numbers may be persistent,
+ note that the server does not guarantee that the inode numbers
+ are unique if multiple server side mounts are exported under a
+ single share (since inode numbers on the servers might not
+ be unique if multiple filesystems are mounted under the same
+ shared higher level directory). Note that some older
+ (e.g. pre-Windows 2000) do not support returning UniqueIDs
+ or the CIFS Unix Extensions equivalent and for those
+ this mount option will have no effect. Exporting cifs mounts
+ under nfsd requires this mount option on the cifs mount.
+ noserverino Client generates inode numbers (rather than using the actual one
+ from the server) by default.
+ setuids If the CIFS Unix extensions are negotiated with the server
+ the client will attempt to set the effective uid and gid of
+ the local process on newly created files, directories, and
+ devices (create, mkdir, mknod). If the CIFS Unix Extensions
+ are not negotiated, for newly created files and directories
+ instead of using the default uid and gid specified on
+ the mount, cache the new file's uid and gid locally which means
+ that the uid for the file can change when the inode is
+ reloaded (or the user remounts the share).
+ nosetuids The client will not attempt to set the uid and gid on
+ on newly created files, directories, and devices (create,
+ mkdir, mknod) which will result in the server setting the
+ uid and gid to the default (usually the server uid of the
+ user who mounted the share). Letting the server (rather than
+ the client) set the uid and gid is the default. If the CIFS
+ Unix Extensions are not negotiated then the uid and gid for
+ new files will appear to be the uid (gid) of the mounter or the
+ uid (gid) parameter specified on the mount.
+ netbiosname When mounting to servers via port 139, specifies the RFC1001
+ source name to use to represent the client netbios machine
+ name when doing the RFC1001 netbios session initialize.
+ direct Do not do inode data caching on files opened on this mount.
+ This precludes mmaping files on this mount. In some cases
+ with fast networks and little or no caching benefits on the
+ client (e.g. when the application is doing large sequential
+ reads bigger than page size without rereading the same data)
+ this can provide better performance than the default
+ behavior which caches reads (readahead) and writes
+ (writebehind) through the local Linux client pagecache
+ if oplock (caching token) is granted and held. Note that
+ direct allows write operations larger than page size
+ to be sent to the server.
+ acl Allow setfacl and getfacl to manage posix ACLs if server
+ supports them. (default)
+ noacl Do not allow setfacl and getfacl calls on this mount
+ user_xattr Allow getting and setting user xattrs (those attributes whose
+ name begins with "user." or "os2.") as OS/2 EAs (extended
+ attributes) to the server. This allows support of the
+ setfattr and getfattr utilities. (default)
+ nouser_xattr Do not allow getfattr/setfattr to get/set/list xattrs
+ mapchars Translate six of the seven reserved characters (not backslash)
+ *?<>|:
+ to the remap range (above 0xF000), which also
+ allows the CIFS client to recognize files created with
+ such characters by Windows's POSIX emulation. This can
+ also be useful when mounting to most versions of Samba
+ (which also forbids creating and opening files
+ whose names contain any of these seven characters).
+ This has no effect if the server does not support
+ Unicode on the wire.
+ nomapchars Do not translate any of these seven characters (default).
+ nocase Request case insensitive path name matching (case
+ sensitive is the default if the server suports it).
+ (mount option "ignorecase" is identical to "nocase")
+ posixpaths If CIFS Unix extensions are supported, attempt to
+ negotiate posix path name support which allows certain
+ characters forbidden in typical CIFS filenames, without
+ requiring remapping. (default)
+ noposixpaths If CIFS Unix extensions are supported, do not request
+ posix path name support (this may cause servers to
+ reject creatingfile with certain reserved characters).
+ nounix Disable the CIFS Unix Extensions for this mount (tree
+ connection). This is rarely needed, but it may be useful
+ in order to turn off multiple settings all at once (ie
+ posix acls, posix locks, posix paths, symlink support
+ and retrieving uids/gids/mode from the server) or to
+ work around a bug in server which implement the Unix
+ Extensions.
+ nobrl Do not send byte range lock requests to the server.
+ This is necessary for certain applications that break
+ with cifs style mandatory byte range locks (and most
+ cifs servers do not yet support requesting advisory
+ byte range locks).
+ 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 3.0.24 server.
+ remount remount the share (often used to change from ro to rw mounts
+ or vice versa)
+ cifsacl Report mode bits (e.g. on stat) based on the Windows ACL for
+ the file. (EXPERIMENTAL)
+ servern Specify the server 's netbios name (RFC1001 name) to use
+ when attempting to setup a session to the server.
+ This is needed for mounting to some older servers (such
+ as OS/2 or Windows 98 and Windows ME) since they do not
+ support a default server name. A server name can be up
+ to 15 characters long and is usually uppercased.
+ sfu When the CIFS Unix Extensions are not negotiated, attempt to
+ create device files and fifos in a format compatible with
+ Services for Unix (SFU). In addition retrieve bits 10-12
+ of the mode via the SETFILEBITS extended attribute (as
+ SFU does). In the future the bottom 9 bits of the
+ mode also will be emulated using queries of the security
+ descriptor (ACL).
+ sign Must use packet signing (helps avoid unwanted data modification
+ by intermediate systems in the route). Note that signing
+ does not work with lanman or plaintext authentication.
+ seal Must seal (encrypt) all data on this mounted share before
+ sending on the network. Requires support for Unix Extensions.
+ Note that this differs from the sign mount option in that it
+ causes encryption of data sent over this mounted share but other
+ shares mounted to the same server are unaffected.
+ locallease This option is rarely needed. Fcntl F_SETLEASE is
+ used by some applications such as Samba and NFSv4 server to
+ check to see whether a file is cacheable. CIFS has no way
+ to explicitly request a lease, but can check whether a file
+ is cacheable (oplocked). Unfortunately, even if a file
+ is not oplocked, it could still be cacheable (ie cifs client
+ could grant fcntl leases if no other local processes are using
+ the file) for cases for example such as when the server does not
+ support oplocks and the user is sure that the only updates to
+ the file will be from this client. Specifying this mount option
+ will allow the cifs client to check for leases (only) locally
+ for files which are not oplocked instead of denying leases
+ in that case. (EXPERIMENTAL)
+ sec Security mode. Allowed values are:
+ none attempt to connection as a null user (no name)
+ krb5 Use Kerberos version 5 authentication
+ krb5i Use Kerberos authentication and packet signing
+ ntlm Use NTLM password hashing (default)
+ ntlmi Use NTLM password hashing with signing (if
+ /proc/fs/cifs/PacketSigningEnabled on or if
+ server requires signing also can be the default)
+ ntlmv2 Use NTLMv2 password hashing
+ ntlmv2i Use NTLMv2 password hashing with packet signing
+ lanman (if configured in kernel config) use older
+ lanman hash
+hard Retry file operations if server is not responding
+soft Limit retries to unresponsive servers (usually only
+ one retry) before returning an error. (default)
+
+The mount.cifs mount helper also accepts a few mount options before -o
+including:
+
+ -S take password from stdin (equivalent to setting the environment
+ variable "PASSWD_FD=0"
+ -V print mount.cifs version
+ -? display simple usage information
+
+With most 2.6 kernel versions of modutils, the version of the cifs kernel
+module can be displayed via modinfo.
+
+Misc /proc/fs/cifs Flags and Debug Info
+=======================================
+Informational pseudo-files:
+DebugData Displays information about active CIFS sessions
+ and shares, as well as the cifs.ko version.
+Stats Lists summary resource usage information as well as per
+ share statistics, if CONFIG_CIFS_STATS in enabled
+ in the kernel configuration.
+
+Configuration pseudo-files:
+MultiuserMount If set to one, more than one CIFS session to
+ the same server ip address can be established
+ if more than one uid accesses the same mount
+ point and if the uids user/password mapping
+ information is available. (default is 0)
+PacketSigningEnabled If set to one, cifs packet signing is enabled
+ and will be used if the server requires
+ it. If set to two, cifs packet signing is
+ required even if the server considers packet
+ signing optional. (default 1)
+SecurityFlags Flags which control security negotiation and
+ also packet signing. Authentication (may/must)
+ flags (e.g. for NTLM and/or NTLMv2) may be combined with
+ the signing flags. Specifying two different password
+ hashing mechanisms (as "must use") on the other hand
+ does not make much sense. Default flags are
+ 0x07007
+ (NTLM, NTLMv2 and packet signing allowed). The maximum
+ allowable flags if you want to allow mounts to servers
+ using weaker password hashes is 0x37037 (lanman,
+ plaintext, ntlm, ntlmv2, signing allowed). Some
+ SecurityFlags require the corresponding menuconfig
+ options to be enabled (lanman and plaintext require
+ CONFIG_CIFS_WEAK_PW_HASH for example). Enabling
+ plaintext authentication currently requires also
+ enabling lanman authentication in the security flags
+ because the cifs module only supports sending
+ laintext passwords using the older lanman dialect
+ form of the session setup SMB. (e.g. for authentication
+ using plain text passwords, set the SecurityFlags
+ to 0x30030):
+
+ may use packet signing 0x00001
+ must use packet signing 0x01001
+ may use NTLM (most common password hash) 0x00002
+ must use NTLM 0x02002
+ may use NTLMv2 0x00004
+ must use NTLMv2 0x04004
+ may use Kerberos security 0x00008
+ must use Kerberos 0x08008
+ may use lanman (weak) password hash 0x00010
+ must use lanman password hash 0x10010
+ may use plaintext passwords 0x00020
+ must use plaintext passwords 0x20020
+ (reserved for future packet encryption) 0x00040
+
+cifsFYI If set to non-zero value, additional debug information
+ will be logged to the system error log. This field
+ contains three flags controlling different classes of
+ debugging entries. The maximum value it can be set
+ to is 7 which enables all debugging points (default 0).
+ Some debugging statements are not compiled into the
+ cifs kernel unless CONFIG_CIFS_DEBUG2 is enabled in the
+ kernel configuration. cifsFYI may be set to one or
+ nore of the following flags (7 sets them all):
+
+ log cifs informational messages 0x01
+ log return codes from cifs entry points 0x02
+ log slow responses (ie which take longer than 1 second)
+ CONFIG_CIFS_STATS2 must be enabled in .config 0x04
+
+
+traceSMB If set to one, debug information is logged to the
+ system error log with the start of smb requests
+ and responses (default 0)
+LookupCacheEnable If set to one, inode information is kept cached
+ for one second improving performance of lookups
+ (default 1)
+OplockEnabled If set to one, safe distributed caching enabled.
+ (default 1)
+LinuxExtensionsEnabled If set to one then the client will attempt to
+ use the CIFS "UNIX" extensions which are optional
+ protocol enhancements that allow CIFS servers
+ to return accurate UID/GID information as well
+ as support symbolic links. If you use servers
+ such as Samba that support the CIFS Unix
+ extensions but do not want to use symbolic link
+ support and want to map the uid and gid fields
+ to values supplied at mount (rather than the
+ actual values, then set this to zero. (default 1)
+Experimental When set to 1 used to enable certain experimental
+ features (currently enables multipage writes
+ when signing is enabled, the multipage write
+ performance enhancement was disabled when
+ signing turned on in case buffer was modified
+ just before it was sent, also this flag will
+ be used to use the new experimental directory change
+ notification code).
+
+These experimental features and tracing can be enabled by changing flags in
+/proc/fs/cifs (after the cifs module has been installed or built into the
+kernel, e.g. insmod cifs). To enable a feature set it to 1 e.g. to enable
+tracing to the kernel message log type:
+
+ echo 7 > /proc/fs/cifs/cifsFYI
+
+cifsFYI functions as a bit mask. Setting it to 1 enables additional kernel
+logging of various informational messages. 2 enables logging of non-zero
+SMB return codes while 4 enables logging of requests that take longer
+than one second to complete (except for byte range lock requests).
+Setting it to 4 requires defining CONFIG_CIFS_STATS2 manually in the
+source code (typically by setting it in the beginning of cifsglob.h),
+and setting it to seven enables all three. Finally, tracing
+the start of smb requests and responses can be enabled via:
+
+ echo 1 > /proc/fs/cifs/traceSMB
+
+Two other experimental features are under development. To test these
+requires enabling CONFIG_CIFS_EXPERIMENTAL
+
+ cifsacl support needed to retrieve approximated mode bits based on
+ the contents on the CIFS ACL.
+
+ lease support: cifs will check the oplock state before calling into
+ the vfs to see if we can grant a lease on a file.
+
+ DNOTIFY fcntl: needed for support of directory change
+ notification and perhaps later for file leases)
+
+Per share (per client mount) statistics are available in /proc/fs/cifs/Stats
+if the kernel was configured with cifs statistics enabled. The statistics
+represent the number of successful (ie non-zero return code from the server)
+SMB responses to some of the more common commands (open, delete, mkdir etc.).
+Also recorded is the total bytes read and bytes written to the server for
+that share. Note that due to client caching effects this can be less than the
+number of bytes read and written by the application running on the client.
+The statistics for the number of total SMBs and oplock breaks are different in
+that they represent all for that share, not just those for which the server
+returned success.
+
+Also note that "cat /proc/fs/cifs/DebugData" will display information about
+the active sessions and the shares that are mounted.
+
+Enabling Kerberos (extended security) works but requires version 1.2 or later
+of the helper program cifs.upcall to be present and to be configured in the
+/etc/request-key.conf file. The cifs.upcall helper program is from the Samba
+project(http://www.samba.org). NTLM and NTLMv2 and LANMAN support do not
+require this helper. Note that NTLMv2 security (which does not require the
+cifs.upcall helper program), instead of using Kerberos, is sufficient for
+some use cases.
+
+Enabling DFS support (used to access shares transparently in an MS-DFS
+global name space) requires that CONFIG_CIFS_EXPERIMENTAL be enabled. In
+addition, DFS support for target shares which are specified as UNC
+names which begin with host names (rather than IP addresses) requires
+a user space helper (such as cifs.upcall) to be present in order to
+translate host names to ip address, and the user space helper must also
+be configured in the file /etc/request-key.conf
+
+To use cifs Kerberos and DFS support, the Linux keyutils package should be
+installed and something like the following lines should be added to the
+/etc/request-key.conf file:
+
+create cifs.spnego * * /usr/local/sbin/cifs.upcall %k
+create dns_resolver * * /usr/local/sbin/cifs.upcall %k
+
+
diff --git a/fs/cifs/TODO b/fs/cifs/TODO
new file mode 100644
index 0000000..5aff46c
--- /dev/null
+++ b/fs/cifs/TODO
@@ -0,0 +1,129 @@
+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 points for setlease and fallocate
+
+x) Fix Samba 3 server to handle Linux kernel aio so dbench with lots of
+processes can proceed better in parallel (on the server)
+
+y) Fix Samba 3 to handle reads/writes over 127K (and remove the cifs mount
+restriction of wsize max being 127K)
+
+KNOWN BUGS (updated April 24, 2007)
+====================================
+See http://bugzilla.samba.org - search on product "CifsVFS" for
+current bug list.
+
+1) existing symbolic links (Windows reparse points) are recognized but
+can not be created remotely. They are implemented for Samba and those that
+support the CIFS Unix extensions, although earlier versions of Samba
+overly restrict the pathnames.
+2) follow_link and readdir code does not follow dfs junctions
+but recognizes them
+3) create of new files to FAT partitions on Windows servers can
+succeed but still return access denied (appears to be Windows
+server not cifs client problem) and has not been reproduced recently.
+NTFS partitions do not have this problem.
+4) Unix/POSIX capabilities are reset after reconnection, and affect
+a few fields in the tree connection but we do do not know which
+superblocks to apply these changes to. We should probably walk
+the list of superblocks to set these. Also need to check the
+flags on the second mount to the same share, and see if we
+can do the same trick that NFS does to remount duplicate shares.
+
+Misc testing to do
+==================
+1) check out max path names and max path name components against various server
+types. Try nested symlinks (8 deep). Return max path name in stat -f information
+
+2) Modify file portion of ltp so it can run against a mounted network
+share and run it against cifs vfs in automated fashion.
+
+3) Additional performance testing and optimization using iozone and similar -
+there are some easy changes that can be done to parallelize sequential writes,
+and when signing is disabled to request larger read sizes (larger than
+negotiated size) and send larger write sizes to modern servers.
+
+4) More exhaustively test against less common servers. More testing
+against Windows 9x, Windows ME servers.
+
diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c
new file mode 100644
index 0000000..1b09f16
--- /dev/null
+++ b/fs/cifs/asn1.c
@@ -0,0 +1,642 @@
+/*
+ * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
+ * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
+ *
+ * Copyright (c) 2000 RP Internet (www.rpi.net.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifsproto.h"
+
+/*****************************************************************************
+ *
+ * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
+ *
+ *****************************************************************************/
+
+/* Class */
+#define ASN1_UNI 0 /* Universal */
+#define ASN1_APL 1 /* Application */
+#define ASN1_CTX 2 /* Context */
+#define ASN1_PRV 3 /* Private */
+
+/* Tag */
+#define ASN1_EOC 0 /* End Of Contents or N/A */
+#define ASN1_BOL 1 /* Boolean */
+#define ASN1_INT 2 /* Integer */
+#define ASN1_BTS 3 /* Bit String */
+#define ASN1_OTS 4 /* Octet String */
+#define ASN1_NUL 5 /* Null */
+#define ASN1_OJI 6 /* Object Identifier */
+#define ASN1_OJD 7 /* Object Description */
+#define ASN1_EXT 8 /* External */
+#define ASN1_SEQ 16 /* Sequence */
+#define ASN1_SET 17 /* Set */
+#define ASN1_NUMSTR 18 /* Numerical String */
+#define ASN1_PRNSTR 19 /* Printable String */
+#define ASN1_TEXSTR 20 /* Teletext String */
+#define ASN1_VIDSTR 21 /* Video String */
+#define ASN1_IA5STR 22 /* IA5 String */
+#define ASN1_UNITIM 23 /* Universal Time */
+#define ASN1_GENTIM 24 /* General Time */
+#define ASN1_GRASTR 25 /* Graphical String */
+#define ASN1_VISSTR 26 /* Visible String */
+#define ASN1_GENSTR 27 /* General String */
+
+/* Primitive / Constructed methods*/
+#define ASN1_PRI 0 /* Primitive */
+#define ASN1_CON 1 /* Constructed */
+
+/*
+ * Error codes.
+ */
+#define ASN1_ERR_NOERROR 0
+#define ASN1_ERR_DEC_EMPTY 2
+#define ASN1_ERR_DEC_EOC_MISMATCH 3
+#define ASN1_ERR_DEC_LENGTH_MISMATCH 4
+#define ASN1_ERR_DEC_BADVALUE 5
+
+#define SPNEGO_OID_LEN 7
+#define NTLMSSP_OID_LEN 10
+#define KRB5_OID_LEN 7
+#define MSKRB5_OID_LEN 7
+static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 };
+static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 };
+static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 };
+static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 };
+
+/*
+ * ASN.1 context.
+ */
+struct asn1_ctx {
+ int error; /* Error condition */
+ unsigned char *pointer; /* Octet just to be decoded */
+ unsigned char *begin; /* First octet */
+ unsigned char *end; /* Octet after last octet */
+};
+
+/*
+ * Octet string (not null terminated)
+ */
+struct asn1_octstr {
+ unsigned char *data;
+ unsigned int len;
+};
+
+static void
+asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
+{
+ ctx->begin = buf;
+ ctx->end = buf + len;
+ ctx->pointer = buf;
+ ctx->error = ASN1_ERR_NOERROR;
+}
+
+static unsigned char
+asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
+{
+ if (ctx->pointer >= ctx->end) {
+ ctx->error = ASN1_ERR_DEC_EMPTY;
+ return 0;
+ }
+ *ch = *(ctx->pointer)++;
+ return 1;
+}
+
+static unsigned char
+asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
+{
+ unsigned char ch;
+
+ *tag = 0;
+
+ do {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+ *tag <<= 7;
+ *tag |= ch & 0x7F;
+ } while ((ch & 0x80) == 0x80);
+ return 1;
+}
+
+static unsigned char
+asn1_id_decode(struct asn1_ctx *ctx,
+ unsigned int *cls, unsigned int *con, unsigned int *tag)
+{
+ unsigned char ch;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *cls = (ch & 0xC0) >> 6;
+ *con = (ch & 0x20) >> 5;
+ *tag = (ch & 0x1F);
+
+ if (*tag == 0x1F) {
+ if (!asn1_tag_decode(ctx, tag))
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned char
+asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
+{
+ unsigned char ch, cnt;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ if (ch == 0x80)
+ *def = 0;
+ else {
+ *def = 1;
+
+ if (ch < 0x80)
+ *len = ch;
+ else {
+ cnt = (unsigned char) (ch & 0x7F);
+ *len = 0;
+
+ while (cnt > 0) {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+ *len <<= 8;
+ *len |= ch;
+ cnt--;
+ }
+ }
+ }
+
+ /* don't trust len bigger than ctx buffer */
+ if (*len > ctx->end - ctx->pointer)
+ return 0;
+
+ return 1;
+}
+
+static unsigned char
+asn1_header_decode(struct asn1_ctx *ctx,
+ unsigned char **eoc,
+ unsigned int *cls, unsigned int *con, unsigned int *tag)
+{
+ unsigned int def = 0;
+ unsigned int len = 0;
+
+ if (!asn1_id_decode(ctx, cls, con, tag))
+ return 0;
+
+ if (!asn1_length_decode(ctx, &def, &len))
+ return 0;
+
+ /* primitive shall be definite, indefinite shall be constructed */
+ if (*con == ASN1_PRI && !def)
+ return 0;
+
+ if (def)
+ *eoc = ctx->pointer + len;
+ else
+ *eoc = NULL;
+ return 1;
+}
+
+static unsigned char
+asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
+{
+ unsigned char ch;
+
+ if (eoc == NULL) {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ if (ch != 0x00) {
+ ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ if (ch != 0x00) {
+ ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+ return 0;
+ }
+ return 1;
+ } else {
+ if (ctx->pointer != eoc) {
+ ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
+ return 0;
+ }
+ return 1;
+ }
+}
+
+/* static unsigned char asn1_null_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc)
+{
+ ctx->pointer = eoc;
+ return 1;
+}
+
+static unsigned char asn1_long_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc, long *integer)
+{
+ unsigned char ch;
+ unsigned int len;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer = (signed char) ch;
+ len = 1;
+
+ while (ctx->pointer < eoc) {
+ if (++len > sizeof(long)) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer <<= 8;
+ *integer |= ch;
+ }
+ return 1;
+}
+
+static unsigned char asn1_uint_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc,
+ unsigned int *integer)
+{
+ unsigned char ch;
+ unsigned int len;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer = ch;
+ if (ch == 0)
+ len = 0;
+ else
+ len = 1;
+
+ while (ctx->pointer < eoc) {
+ if (++len > sizeof(unsigned int)) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer <<= 8;
+ *integer |= ch;
+ }
+ return 1;
+}
+
+static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc,
+ unsigned long *integer)
+{
+ unsigned char ch;
+ unsigned int len;
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer = ch;
+ if (ch == 0)
+ len = 0;
+ else
+ len = 1;
+
+ while (ctx->pointer < eoc) {
+ if (++len > sizeof(unsigned long)) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ return 0;
+ }
+
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *integer <<= 8;
+ *integer |= ch;
+ }
+ return 1;
+}
+
+static unsigned char
+asn1_octets_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc,
+ unsigned char **octets, unsigned int *len)
+{
+ unsigned char *ptr;
+
+ *len = 0;
+
+ *octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC);
+ if (*octets == NULL) {
+ return 0;
+ }
+
+ ptr = *octets;
+ while (ctx->pointer < eoc) {
+ if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
+ kfree(*octets);
+ *octets = NULL;
+ return 0;
+ }
+ (*len)++;
+ }
+ return 1;
+} */
+
+static unsigned char
+asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
+{
+ unsigned char ch;
+
+ *subid = 0;
+
+ do {
+ if (!asn1_octet_decode(ctx, &ch))
+ return 0;
+
+ *subid <<= 7;
+ *subid |= ch & 0x7F;
+ } while ((ch & 0x80) == 0x80);
+ return 1;
+}
+
+static int
+asn1_oid_decode(struct asn1_ctx *ctx,
+ unsigned char *eoc, unsigned long **oid, unsigned int *len)
+{
+ unsigned long subid;
+ unsigned int size;
+ unsigned long *optr;
+
+ size = eoc - ctx->pointer + 1;
+
+ /* first subid actually encodes first two subids */
+ if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
+ return 0;
+
+ *oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC);
+ if (*oid == NULL)
+ return 0;
+
+ optr = *oid;
+
+ if (!asn1_subid_decode(ctx, &subid)) {
+ kfree(*oid);
+ *oid = NULL;
+ return 0;
+ }
+
+ if (subid < 40) {
+ optr[0] = 0;
+ optr[1] = subid;
+ } else if (subid < 80) {
+ optr[0] = 1;
+ optr[1] = subid - 40;
+ } else {
+ optr[0] = 2;
+ optr[1] = subid - 80;
+ }
+
+ *len = 2;
+ optr += 2;
+
+ while (ctx->pointer < eoc) {
+ if (++(*len) > size) {
+ ctx->error = ASN1_ERR_DEC_BADVALUE;
+ kfree(*oid);
+ *oid = NULL;
+ return 0;
+ }
+
+ if (!asn1_subid_decode(ctx, optr++)) {
+ kfree(*oid);
+ *oid = NULL;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+compare_oid(unsigned long *oid1, unsigned int oid1len,
+ unsigned long *oid2, unsigned int oid2len)
+{
+ unsigned int i;
+
+ if (oid1len != oid2len)
+ return 0;
+ else {
+ for (i = 0; i < oid1len; i++) {
+ if (oid1[i] != oid2[i])
+ return 0;
+ }
+ return 1;
+ }
+}
+
+ /* BB check for endian conversion issues here */
+
+int
+decode_negTokenInit(unsigned char *security_blob, int length,
+ enum securityEnum *secType)
+{
+ struct asn1_ctx ctx;
+ unsigned char *end;
+ unsigned char *sequence_end;
+ unsigned long *oid = NULL;
+ unsigned int cls, con, tag, oidlen, rc;
+ bool use_ntlmssp = false;
+ bool use_kerberos = false;
+ bool use_mskerberos = false;
+
+ *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default*/
+
+ /* cifs_dump_mem(" Received SecBlob ", security_blob, length); */
+
+ asn1_open(&ctx, security_blob, length);
+
+ /* GSSAPI header */
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding negTokenInit header"));
+ return 0;
+ } else if ((cls != ASN1_APL) || (con != ASN1_CON)
+ || (tag != ASN1_EOC)) {
+ cFYI(1, ("cls = %d con = %d tag = %d", cls, con, tag));
+ return 0;
+ }
+
+ /* Check for SPNEGO OID -- remember to free obj->oid */
+ rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
+ if (rc) {
+ if ((tag == ASN1_OJI) && (con == ASN1_PRI) &&
+ (cls == ASN1_UNI)) {
+ rc = asn1_oid_decode(&ctx, end, &oid, &oidlen);
+ if (rc) {
+ rc = compare_oid(oid, oidlen, SPNEGO_OID,
+ SPNEGO_OID_LEN);
+ kfree(oid);
+ }
+ } else
+ rc = 0;
+ }
+
+ /* SPNEGO OID not present or garbled -- bail out */
+ if (!rc) {
+ cFYI(1, ("Error decoding negTokenInit header"));
+ return 0;
+ }
+
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding negTokenInit"));
+ return 0;
+ } else if ((cls != ASN1_CTX) || (con != ASN1_CON)
+ || (tag != ASN1_EOC)) {
+ cFYI(1,
+ ("cls = %d con = %d tag = %d end = %p (%d) exit 0",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding negTokenInit"));
+ return 0;
+ } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
+ || (tag != ASN1_SEQ)) {
+ cFYI(1,
+ ("cls = %d con = %d tag = %d end = %p (%d) exit 1",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding 2nd part of negTokenInit"));
+ return 0;
+ } else if ((cls != ASN1_CTX) || (con != ASN1_CON)
+ || (tag != ASN1_EOC)) {
+ cFYI(1,
+ ("cls = %d con = %d tag = %d end = %p (%d) exit 0",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+
+ if (asn1_header_decode
+ (&ctx, &sequence_end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding 2nd part of negTokenInit"));
+ return 0;
+ } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
+ || (tag != ASN1_SEQ)) {
+ cFYI(1,
+ ("cls = %d con = %d tag = %d end = %p (%d) exit 1",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+
+ while (!asn1_eoc_decode(&ctx, sequence_end)) {
+ rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
+ if (!rc) {
+ cFYI(1,
+ ("Error decoding negTokenInit hdr exit2"));
+ return 0;
+ }
+ if ((tag == ASN1_OJI) && (con == ASN1_PRI)) {
+ if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) {
+
+ cFYI(1, ("OID len = %d oid = 0x%lx 0x%lx "
+ "0x%lx 0x%lx", oidlen, *oid,
+ *(oid + 1), *(oid + 2), *(oid + 3)));
+
+ if (compare_oid(oid, oidlen, MSKRB5_OID,
+ MSKRB5_OID_LEN) &&
+ !use_kerberos)
+ use_mskerberos = true;
+ else if (compare_oid(oid, oidlen, KRB5_OID,
+ KRB5_OID_LEN) &&
+ !use_mskerberos)
+ use_kerberos = true;
+ else if (compare_oid(oid, oidlen, NTLMSSP_OID,
+ NTLMSSP_OID_LEN))
+ use_ntlmssp = true;
+
+ kfree(oid);
+ }
+ } else {
+ cFYI(1, ("Should be an oid what is going on?"));
+ }
+ }
+
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding last part negTokenInit exit3"));
+ return 0;
+ } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) {
+ /* tag = 3 indicating mechListMIC */
+ cFYI(1, ("Exit 4 cls = %d con = %d tag = %d end = %p (%d)",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding last part negTokenInit exit5"));
+ return 0;
+ } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
+ || (tag != ASN1_SEQ)) {
+ cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d)",
+ cls, con, tag, end, *end));
+ }
+
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding last part negTokenInit exit 7"));
+ return 0;
+ } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) {
+ cFYI(1, ("Exit 8 cls = %d con = %d tag = %d end = %p (%d)",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+ if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
+ cFYI(1, ("Error decoding last part negTokenInit exit9"));
+ return 0;
+ } else if ((cls != ASN1_UNI) || (con != ASN1_PRI)
+ || (tag != ASN1_GENSTR)) {
+ cFYI(1, ("Exit10 cls = %d con = %d tag = %d end = %p (%d)",
+ cls, con, tag, end, *end));
+ return 0;
+ }
+ cFYI(1, ("Need to call asn1_octets_decode() function for %s",
+ ctx.pointer)); /* is this UTF-8 or ASCII? */
+
+ if (use_kerberos)
+ *secType = Kerberos;
+ else if (use_mskerberos)
+ *secType = MSKerberos;
+ else if (use_ntlmssp)
+ *secType = NTLMSSP;
+
+ return 1;
+}
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
new file mode 100644
index 0000000..490e34b
--- /dev/null
+++ b/fs/cifs/cifs_debug.c
@@ -0,0 +1,795 @@
+/*
+ * fs/cifs_debug.c
+ *
+ * Copyright (C) International Business Machines Corp., 2000,2005
+ *
+ * Modified by Steve French (sfrench@us.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+
+void
+cifs_dump_mem(char *label, void *data, int length)
+{
+ int i, j;
+ int *intptr = data;
+ char *charptr = data;
+ char buf[10], line[80];
+
+ printk(KERN_DEBUG "%s: dump of %d bytes of data at 0x%p\n",
+ label, length, data);
+ for (i = 0; i < length; i += 16) {
+ line[0] = 0;
+ for (j = 0; (j < 4) && (i + j * 4 < length); j++) {
+ sprintf(buf, " %08x", intptr[i / 4 + j]);
+ strcat(line, buf);
+ }
+ buf[0] = ' ';
+ buf[2] = 0;
+ for (j = 0; (j < 16) && (i + j < length); j++) {
+ buf[1] = isprint(charptr[i + j]) ? charptr[i + j] : '.';
+ strcat(line, buf);
+ }
+ printk(KERN_DEBUG "%s\n", line);
+ }
+}
+
+#ifdef CONFIG_CIFS_DEBUG2
+void cifs_dump_detail(struct smb_hdr *smb)
+{
+ cERROR(1, ("Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d",
+ smb->Command, smb->Status.CifsError,
+ smb->Flags, smb->Flags2, smb->Mid, smb->Pid));
+ cERROR(1, ("smb buf %p len %d", smb, smbCalcSize_LE(smb)));
+}
+
+
+void cifs_dump_mids(struct TCP_Server_Info *server)
+{
+ struct list_head *tmp;
+ struct mid_q_entry *mid_entry;
+
+ if (server == NULL)
+ return;
+
+ cERROR(1, ("Dump pending requests:"));
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ cERROR(1, ("State: %d Cmd: %d Pid: %d Tsk: %p Mid %d",
+ mid_entry->midState,
+ (int)mid_entry->command,
+ mid_entry->pid,
+ mid_entry->tsk,
+ mid_entry->mid));
+#ifdef CONFIG_CIFS_STATS2
+ cERROR(1, ("IsLarge: %d buf: %p time rcv: %ld now: %ld",
+ mid_entry->largeBuf,
+ mid_entry->resp_buf,
+ mid_entry->when_received,
+ jiffies));
+#endif /* STATS2 */
+ cERROR(1, ("IsMult: %d IsEnd: %d", mid_entry->multiRsp,
+ mid_entry->multiEnd));
+ if (mid_entry->resp_buf) {
+ cifs_dump_detail(mid_entry->resp_buf);
+ cifs_dump_mem("existing buf: ",
+ mid_entry->resp_buf, 62);
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+}
+#endif /* CONFIG_CIFS_DEBUG2 */
+
+#ifdef CONFIG_PROC_FS
+static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+{
+ struct list_head *tmp1, *tmp2, *tmp3;
+ struct mid_q_entry *mid_entry;
+ struct TCP_Server_Info *server;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+ int i, j;
+ __u32 dev_type;
+
+ seq_puts(m,
+ "Display Internal CIFS Data Structures for Debugging\n"
+ "---------------------------------------------------\n");
+ seq_printf(m, "CIFS Version %s\n", CIFS_VERSION);
+ seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
+ seq_printf(m, "Servers:");
+
+ i = 0;
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+ i++;
+ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifsSesInfo,
+ smb_ses_list);
+ if ((ses->serverDomain == NULL) ||
+ (ses->serverOS == NULL) ||
+ (ses->serverNOS == NULL)) {
+ seq_printf(m, "\n%d) entry for %s not fully "
+ "displayed\n\t", i, ses->serverName);
+ } else {
+ seq_printf(m,
+ "\n%d) Name: %s Domain: %s Uses: %d OS:"
+ " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
+ " session status: %d\t",
+ i, ses->serverName, ses->serverDomain,
+ ses->ses_count, ses->serverOS, ses->serverNOS,
+ ses->capabilities, ses->status);
+ }
+ seq_printf(m, "TCP status: %d\n\tLocal Users To "
+ "Server: %d SecMode: 0x%x Req On Wire: %d",
+ server->tcpStatus, server->srv_count,
+ server->secMode,
+ atomic_read(&server->inFlight));
+
+#ifdef CONFIG_CIFS_STATS2
+ seq_printf(m, " In Send: %d In MaxReq Wait: %d",
+ atomic_read(&server->inSend),
+ atomic_read(&server->num_waiters));
+#endif
+
+ seq_puts(m, "\n\tShares:");
+ j = 0;
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3, struct cifsTconInfo,
+ tcon_list);
+ ++j;
+ dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
+ seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
+ tcon->treeName, tcon->tc_count);
+ if (tcon->nativeFileSystem) {
+ seq_printf(m, "Type: %s ",
+ tcon->nativeFileSystem);
+ }
+ seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
+ "\nPathComponentMax: %d Status: 0x%d",
+ le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
+ le32_to_cpu(tcon->fsAttrInfo.Attributes),
+ le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
+ tcon->tidStatus);
+ if (dev_type == FILE_DEVICE_DISK)
+ seq_puts(m, " type: DISK ");
+ else if (dev_type == FILE_DEVICE_CD_ROM)
+ seq_puts(m, " type: CDROM ");
+ else
+ seq_printf(m, " type: %d ", dev_type);
+
+ if (tcon->need_reconnect)
+ seq_puts(m, "\tDISCONNECTED ");
+ seq_putc(m, '\n');
+ }
+
+ seq_puts(m, "\n\tMIDs:\n");
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp3, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp3, struct mid_q_entry,
+ qhead);
+ seq_printf(m, "\tState: %d com: %d pid:"
+ " %d tsk: %p mid %d\n",
+ mid_entry->midState,
+ (int)mid_entry->command,
+ mid_entry->pid,
+ mid_entry->tsk,
+ mid_entry->mid);
+ }
+ spin_unlock(&GlobalMid_Lock);
+ }
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ seq_putc(m, '\n');
+
+ /* BB add code to dump additional info such as TCP session info now */
+ return 0;
+}
+
+static int cifs_debug_data_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_debug_data_proc_show, NULL);
+}
+
+static const struct file_operations cifs_debug_data_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_debug_data_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#ifdef CONFIG_CIFS_STATS
+static ssize_t cifs_stats_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+ struct list_head *tmp1, *tmp2, *tmp3;
+ struct TCP_Server_Info *server;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+
+ if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
+#ifdef CONFIG_CIFS_STATS2
+ atomic_set(&totBufAllocCount, 0);
+ atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifsSesInfo,
+ smb_ses_list);
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3,
+ struct cifsTconInfo,
+ tcon_list);
+ atomic_set(&tcon->num_smbs_sent, 0);
+ atomic_set(&tcon->num_writes, 0);
+ atomic_set(&tcon->num_reads, 0);
+ atomic_set(&tcon->num_oplock_brks, 0);
+ atomic_set(&tcon->num_opens, 0);
+ atomic_set(&tcon->num_closes, 0);
+ atomic_set(&tcon->num_deletes, 0);
+ atomic_set(&tcon->num_mkdirs, 0);
+ atomic_set(&tcon->num_rmdirs, 0);
+ atomic_set(&tcon->num_renames, 0);
+ atomic_set(&tcon->num_t2renames, 0);
+ atomic_set(&tcon->num_ffirst, 0);
+ atomic_set(&tcon->num_fnext, 0);
+ atomic_set(&tcon->num_fclose, 0);
+ atomic_set(&tcon->num_hardlinks, 0);
+ atomic_set(&tcon->num_symlinks, 0);
+ atomic_set(&tcon->num_locks, 0);
+ }
+ }
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ }
+
+ return count;
+}
+
+static int cifs_stats_proc_show(struct seq_file *m, void *v)
+{
+ int i;
+ struct list_head *tmp1, *tmp2, *tmp3;
+ struct TCP_Server_Info *server;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+
+ seq_printf(m,
+ "Resources in use\nCIFS Session: %d\n",
+ sesInfoAllocCount.counter);
+ seq_printf(m, "Share (unique mount targets): %d\n",
+ tconInfoAllocCount.counter);
+ seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n",
+ bufAllocCount.counter,
+ cifs_min_rcv + tcpSesAllocCount.counter);
+ seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n",
+ smBufAllocCount.counter, cifs_min_small);
+#ifdef CONFIG_CIFS_STATS2
+ seq_printf(m, "Total Large %d Small %d Allocations\n",
+ atomic_read(&totBufAllocCount),
+ atomic_read(&totSmBufAllocCount));
+#endif /* CONFIG_CIFS_STATS2 */
+
+ seq_printf(m, "Operations (MIDs): %d\n", midCount.counter);
+ seq_printf(m,
+ "\n%d session %d share reconnects\n",
+ tcpSesReconnectCount.counter, tconInfoReconnectCount.counter);
+
+ seq_printf(m,
+ "Total vfs operations: %d maximum at one time: %d\n",
+ GlobalCurrentXid, GlobalMaxActiveXid);
+
+ i = 0;
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifsSesInfo,
+ smb_ses_list);
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3,
+ struct cifsTconInfo,
+ tcon_list);
+ i++;
+ seq_printf(m, "\n%d) %s", i, tcon->treeName);
+ if (tcon->need_reconnect)
+ seq_puts(m, "\tDISCONNECTED ");
+ seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
+ atomic_read(&tcon->num_smbs_sent),
+ atomic_read(&tcon->num_oplock_brks));
+ seq_printf(m, "\nReads: %d Bytes: %lld",
+ atomic_read(&tcon->num_reads),
+ (long long)(tcon->bytes_read));
+ seq_printf(m, "\nWrites: %d Bytes: %lld",
+ atomic_read(&tcon->num_writes),
+ (long long)(tcon->bytes_written));
+ seq_printf(m, "\nLocks: %d HardLinks: %d "
+ "Symlinks: %d",
+ atomic_read(&tcon->num_locks),
+ atomic_read(&tcon->num_hardlinks),
+ atomic_read(&tcon->num_symlinks));
+ seq_printf(m, "\nOpens: %d Closes: %d"
+ "Deletes: %d",
+ atomic_read(&tcon->num_opens),
+ atomic_read(&tcon->num_closes),
+ atomic_read(&tcon->num_deletes));
+ seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
+ atomic_read(&tcon->num_mkdirs),
+ atomic_read(&tcon->num_rmdirs));
+ seq_printf(m, "\nRenames: %d T2 Renames %d",
+ atomic_read(&tcon->num_renames),
+ atomic_read(&tcon->num_t2renames));
+ seq_printf(m, "\nFindFirst: %d FNext %d "
+ "FClose %d",
+ atomic_read(&tcon->num_ffirst),
+ atomic_read(&tcon->num_fnext),
+ atomic_read(&tcon->num_fclose));
+ }
+ }
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+
+ seq_putc(m, '\n');
+ return 0;
+}
+
+static int cifs_stats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_stats_proc_show, NULL);
+}
+
+static const struct file_operations cifs_stats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_stats_proc_write,
+};
+#endif /* STATS */
+
+static struct proc_dir_entry *proc_fs_cifs;
+static const struct file_operations cifsFYI_proc_fops;
+static const struct file_operations cifs_oplock_proc_fops;
+static const struct file_operations cifs_lookup_cache_proc_fops;
+static const struct file_operations traceSMB_proc_fops;
+static const struct file_operations cifs_multiuser_mount_proc_fops;
+static const struct file_operations cifs_security_flags_proc_fops;
+static const struct file_operations cifs_experimental_proc_fops;
+static const struct file_operations cifs_linux_ext_proc_fops;
+
+void
+cifs_proc_init(void)
+{
+ proc_fs_cifs = proc_mkdir("fs/cifs", NULL);
+ if (proc_fs_cifs == NULL)
+ return;
+
+ proc_fs_cifs->owner = THIS_MODULE;
+ proc_create("DebugData", 0, proc_fs_cifs, &cifs_debug_data_proc_fops);
+
+#ifdef CONFIG_CIFS_STATS
+ proc_create("Stats", 0, proc_fs_cifs, &cifs_stats_proc_fops);
+#endif /* STATS */
+ proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops);
+ proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
+ proc_create("OplockEnabled", 0, proc_fs_cifs, &cifs_oplock_proc_fops);
+ proc_create("Experimental", 0, proc_fs_cifs,
+ &cifs_experimental_proc_fops);
+ proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
+ &cifs_linux_ext_proc_fops);
+ proc_create("MultiuserMount", 0, proc_fs_cifs,
+ &cifs_multiuser_mount_proc_fops);
+ proc_create("SecurityFlags", 0, proc_fs_cifs,
+ &cifs_security_flags_proc_fops);
+ proc_create("LookupCacheEnabled", 0, proc_fs_cifs,
+ &cifs_lookup_cache_proc_fops);
+}
+
+void
+cifs_proc_clean(void)
+{
+ if (proc_fs_cifs == NULL)
+ return;
+
+ remove_proc_entry("DebugData", proc_fs_cifs);
+ remove_proc_entry("cifsFYI", proc_fs_cifs);
+ remove_proc_entry("traceSMB", proc_fs_cifs);
+#ifdef CONFIG_CIFS_STATS
+ remove_proc_entry("Stats", proc_fs_cifs);
+#endif
+ remove_proc_entry("MultiuserMount", proc_fs_cifs);
+ remove_proc_entry("OplockEnabled", proc_fs_cifs);
+ remove_proc_entry("SecurityFlags", proc_fs_cifs);
+ remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
+ remove_proc_entry("Experimental", proc_fs_cifs);
+ remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
+ remove_proc_entry("fs/cifs", NULL);
+}
+
+static int cifsFYI_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", cifsFYI);
+ return 0;
+}
+
+static int cifsFYI_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifsFYI_proc_show, NULL);
+}
+
+static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ cifsFYI = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ cifsFYI = 1;
+ else if ((c > '1') && (c <= '9'))
+ cifsFYI = (int) (c - '0'); /* see cifs_debug.h for meanings */
+
+ return count;
+}
+
+static const struct file_operations cifsFYI_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifsFYI_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifsFYI_proc_write,
+};
+
+static int cifs_oplock_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", oplockEnabled);
+ return 0;
+}
+
+static int cifs_oplock_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_oplock_proc_show, NULL);
+}
+
+static ssize_t cifs_oplock_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ oplockEnabled = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ oplockEnabled = 1;
+
+ return count;
+}
+
+static const struct file_operations cifs_oplock_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_oplock_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_oplock_proc_write,
+};
+
+static int cifs_experimental_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", experimEnabled);
+ return 0;
+}
+
+static int cifs_experimental_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_experimental_proc_show, NULL);
+}
+
+static ssize_t cifs_experimental_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ experimEnabled = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ experimEnabled = 1;
+ else if (c == '2')
+ experimEnabled = 2;
+
+ return count;
+}
+
+static const struct file_operations cifs_experimental_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_experimental_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_experimental_proc_write,
+};
+
+static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", linuxExtEnabled);
+ return 0;
+}
+
+static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_linux_ext_proc_show, NULL);
+}
+
+static ssize_t cifs_linux_ext_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ linuxExtEnabled = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ linuxExtEnabled = 1;
+
+ return count;
+}
+
+static const struct file_operations cifs_linux_ext_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_linux_ext_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_linux_ext_proc_write,
+};
+
+static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", lookupCacheEnabled);
+ return 0;
+}
+
+static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_lookup_cache_proc_show, NULL);
+}
+
+static ssize_t cifs_lookup_cache_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ lookupCacheEnabled = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ lookupCacheEnabled = 1;
+
+ return count;
+}
+
+static const struct file_operations cifs_lookup_cache_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_lookup_cache_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_lookup_cache_proc_write,
+};
+
+static int traceSMB_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", traceSMB);
+ return 0;
+}
+
+static int traceSMB_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, traceSMB_proc_show, NULL);
+}
+
+static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ traceSMB = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ traceSMB = 1;
+
+ return count;
+}
+
+static const struct file_operations traceSMB_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = traceSMB_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = traceSMB_proc_write,
+};
+
+static int cifs_multiuser_mount_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%d\n", multiuser_mount);
+ return 0;
+}
+
+static int cifs_multiuser_mount_proc_open(struct inode *inode, struct file *fh)
+{
+ return single_open(fh, cifs_multiuser_mount_proc_show, NULL);
+}
+
+static ssize_t cifs_multiuser_mount_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ char c;
+ int rc;
+
+ rc = get_user(c, buffer);
+ if (rc)
+ return rc;
+ if (c == '0' || c == 'n' || c == 'N')
+ multiuser_mount = 0;
+ else if (c == '1' || c == 'y' || c == 'Y')
+ multiuser_mount = 1;
+
+ return count;
+}
+
+static const struct file_operations cifs_multiuser_mount_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_multiuser_mount_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_multiuser_mount_proc_write,
+};
+
+static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "0x%x\n", extended_security);
+ return 0;
+}
+
+static int cifs_security_flags_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_security_flags_proc_show, NULL);
+}
+
+static ssize_t cifs_security_flags_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ unsigned int flags;
+ char flags_string[12];
+ char c;
+
+ if ((count < 1) || (count > 11))
+ return -EINVAL;
+
+ memset(flags_string, 0, 12);
+
+ if (copy_from_user(flags_string, buffer, count))
+ return -EFAULT;
+
+ if (count < 3) {
+ /* single char or single char followed by null */
+ c = flags_string[0];
+ if (c == '0' || c == 'n' || c == 'N') {
+ extended_security = CIFSSEC_DEF; /* default */
+ return count;
+ } else if (c == '1' || c == 'y' || c == 'Y') {
+ extended_security = CIFSSEC_MAX;
+ return count;
+ } else if (!isdigit(c)) {
+ cERROR(1, ("invalid flag %c", c));
+ return -EINVAL;
+ }
+ }
+ /* else we have a number */
+
+ flags = simple_strtoul(flags_string, NULL, 0);
+
+ cFYI(1, ("sec flags 0x%x", flags));
+
+ if (flags <= 0) {
+ cERROR(1, ("invalid security flags %s", flags_string));
+ return -EINVAL;
+ }
+
+ if (flags & ~CIFSSEC_MASK) {
+ cERROR(1, ("attempt to set unsupported security flags 0x%x",
+ flags & ~CIFSSEC_MASK));
+ return -EINVAL;
+ }
+ /* flags look ok - update the global security flags for cifs module */
+ extended_security = flags;
+ if (extended_security & CIFSSEC_MUST_SIGN) {
+ /* requiring signing implies signing is allowed */
+ extended_security |= CIFSSEC_MAY_SIGN;
+ cFYI(1, ("packet signing now required"));
+ } else if ((extended_security & CIFSSEC_MAY_SIGN) == 0) {
+ cFYI(1, ("packet signing disabled"));
+ }
+ /* BB should we turn on MAY flags for other MUST options? */
+ return count;
+}
+
+static const struct file_operations cifs_security_flags_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = cifs_security_flags_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = cifs_security_flags_proc_write,
+};
+#else
+inline void cifs_proc_init(void)
+{
+}
+
+inline void cifs_proc_clean(void)
+{
+}
+#endif /* PROC_FS */
diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h
new file mode 100644
index 0000000..5eb3b83
--- /dev/null
+++ b/fs/cifs/cifs_debug.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2000,2002
+ * Modified by Steve French (sfrench@us.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+#define CIFS_DEBUG /* BB temporary */
+
+#ifndef _H_CIFS_DEBUG
+#define _H_CIFS_DEBUG
+
+void cifs_dump_mem(char *label, void *data, int length);
+#ifdef CONFIG_CIFS_DEBUG2
+#define DBG2 2
+void cifs_dump_detail(struct smb_hdr *);
+void cifs_dump_mids(struct TCP_Server_Info *);
+#else
+#define DBG2 0
+#endif
+extern int traceSMB; /* flag which enables the function below */
+void dump_smb(struct smb_hdr *, int);
+#define CIFS_INFO 0x01
+#define CIFS_RC 0x02
+#define CIFS_TIMER 0x04
+
+/*
+ * debug ON
+ * --------
+ */
+#ifdef CIFS_DEBUG
+
+
+/* information message: e.g., configuration, major event */
+extern int cifsFYI;
+#define cifsfyi(format,arg...) if (cifsFYI & CIFS_INFO) printk(KERN_DEBUG " " __FILE__ ": " format "\n" "" , ## arg)
+
+#define cFYI(button,prspec) if (button) cifsfyi prspec
+
+#define cifswarn(format, arg...) printk(KERN_WARNING ": " format "\n" , ## arg)
+
+/* debug event message: */
+extern int cifsERROR;
+
+#define cEVENT(format,arg...) if (cifsERROR) printk(KERN_EVENT __FILE__ ": " format "\n" , ## arg)
+
+/* error event message: e.g., i/o error */
+#define cifserror(format,arg...) if (cifsERROR) printk(KERN_ERR " CIFS VFS: " format "\n" "" , ## arg)
+
+#define cERROR(button, prspec) if (button) cifserror prspec
+
+/*
+ * debug OFF
+ * ---------
+ */
+#else /* _CIFS_DEBUG */
+#define cERROR(button, prspec)
+#define cEVENT(format, arg...)
+#define cFYI(button, prspec)
+#define cifserror(format, arg...)
+#endif /* _CIFS_DEBUG */
+
+#endif /* _H_CIFS_DEBUG */
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
new file mode 100644
index 0000000..85c0a74
--- /dev/null
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -0,0 +1,393 @@
+/*
+ * Contains the CIFS DFS referral mounting routines used for handling
+ * traversal via DFS junction point
+ *
+ * Copyright (c) 2007 Igor Mammedov
+ * Copyright (C) International Business Machines Corp., 2008
+ * Author(s): Igor Mammedov (niallain@gmail.com)
+ * Steve French (sfrench@us.ibm.com)
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/dcache.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/vfs.h>
+#include <linux/fs.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifsfs.h"
+#include "dns_resolve.h"
+#include "cifs_debug.h"
+
+static LIST_HEAD(cifs_dfs_automount_list);
+
+static void cifs_dfs_expire_automounts(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cifs_dfs_automount_task,
+ cifs_dfs_expire_automounts);
+static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ;
+
+static void cifs_dfs_expire_automounts(struct work_struct *work)
+{
+ struct list_head *list = &cifs_dfs_automount_list;
+
+ mark_mounts_for_expiry(list);
+ if (!list_empty(list))
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
+}
+
+void cifs_dfs_release_automount_timer(void)
+{
+ BUG_ON(!list_empty(&cifs_dfs_automount_list));
+ cancel_delayed_work(&cifs_dfs_automount_task);
+ flush_scheduled_work();
+}
+
+/**
+ * cifs_get_share_name - extracts share name from UNC
+ * @node_name: pointer to UNC string
+ *
+ * Extracts sharename form full UNC.
+ * i.e. strips from UNC trailing path that is not part of share
+ * name and fixup missing '\' in the begining of DFS node refferal
+ * if neccessary.
+ * Returns pointer to share name on success or NULL on error.
+ * Caller is responsible for freeing returned string.
+ */
+static char *cifs_get_share_name(const char *node_name)
+{
+ int len;
+ char *UNC;
+ char *pSep;
+
+ len = strlen(node_name);
+ UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */,
+ GFP_KERNEL);
+ if (!UNC)
+ return NULL;
+
+ /* get share name and server name */
+ if (node_name[1] != '\\') {
+ UNC[0] = '\\';
+ strncpy(UNC+1, node_name, len);
+ len++;
+ UNC[len] = 0;
+ } else {
+ strncpy(UNC, node_name, len);
+ UNC[len] = 0;
+ }
+
+ /* find server name end */
+ pSep = memchr(UNC+2, '\\', len-2);
+ if (!pSep) {
+ cERROR(1, ("%s: no server name end in node name: %s",
+ __func__, node_name));
+ kfree(UNC);
+ return NULL;
+ }
+
+ /* find sharename end */
+ pSep++;
+ pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC));
+ if (pSep) {
+ /* trim path up to sharename end
+ * now we have share name in UNC */
+ *pSep = 0;
+ }
+
+ return UNC;
+}
+
+
+/**
+ * compose_mount_options - creates mount options for refferral
+ * @sb_mountdata: parent/root DFS mount options (template)
+ * @dentry: point where we are going to mount
+ * @ref: server's referral
+ * @devname: pointer for saving device name
+ *
+ * creates mount options for submount based on template options sb_mountdata
+ * and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
+ *
+ * Returns: pointer to new mount options or ERR_PTR.
+ * Caller is responcible for freeing retunrned value if it is not error.
+ */
+static char *compose_mount_options(const char *sb_mountdata,
+ struct dentry *dentry,
+ const struct dfs_info3_param *ref,
+ char **devname)
+{
+ int rc;
+ char *mountdata = NULL;
+ int md_len;
+ char *tkn_e;
+ char *srvIP = NULL;
+ char sep = ',';
+ int off, noff;
+ char *fullpath;
+
+ if (sb_mountdata == NULL)
+ return ERR_PTR(-EINVAL);
+
+ *devname = cifs_get_share_name(ref->node_name);
+ rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
+ if (rc != 0) {
+ cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d",
+ __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
+ */
+ md_len = strlen(sb_mountdata) + strlen(srvIP) +
+ strlen(ref->node_name) + 12;
+ mountdata = kzalloc(md_len+1, GFP_KERNEL);
+ if (mountdata == NULL) {
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
+ }
+
+ /* copy all options except of unc,ip,prefixpath */
+ off = 0;
+ if (strncmp(sb_mountdata, "sep=", 4) == 0) {
+ sep = sb_mountdata[4];
+ strncpy(mountdata, sb_mountdata, 5);
+ off += 5;
+ }
+
+ do {
+ tkn_e = strchr(sb_mountdata + off, sep);
+ if (tkn_e == NULL)
+ noff = strlen(sb_mountdata + off);
+ else
+ noff = tkn_e - (sb_mountdata + off) + 1;
+
+ if (strnicmp(sb_mountdata + off, "unc=", 4) == 0) {
+ off += noff;
+ continue;
+ }
+ if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) {
+ off += noff;
+ continue;
+ }
+ if (strnicmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
+ off += noff;
+ continue;
+ }
+ strncat(mountdata, sb_mountdata + off, noff);
+ off += noff;
+ } while (tkn_e);
+ strcat(mountdata, sb_mountdata + off);
+ mountdata[md_len] = '\0';
+
+ /* copy new IP and ref share name */
+ if (mountdata[strlen(mountdata) - 1] != sep)
+ strncat(mountdata, &sep, 1);
+ strcat(mountdata, "ip=");
+ strcat(mountdata, srvIP);
+ 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;
+ }
+
+ /*
+ * this function gives us a path with a double backslash prefix. We
+ * require a single backslash for DFS. Temporarily increment fullpath
+ * to put it in the proper form and decrement before freeing it.
+ */
+ fullpath = build_path_from_dentry(dentry);
+ if (!fullpath) {
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
+ }
+ ++fullpath;
+ 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);
+ }
+ --fullpath;
+ kfree(fullpath);
+
+ /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
+ /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/
+
+compose_mount_options_out:
+ kfree(srvIP);
+ return mountdata;
+
+compose_mount_options_err:
+ kfree(mountdata);
+ mountdata = ERR_PTR(rc);
+ goto compose_mount_options_out;
+}
+
+
+static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,
+ struct dentry *dentry, const struct dfs_info3_param *ref)
+{
+ struct cifs_sb_info *cifs_sb;
+ struct vfsmount *mnt;
+ char *mountdata;
+ char *devname = NULL;
+
+ cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
+ mountdata = compose_mount_options(cifs_sb->mountdata,
+ dentry, ref, &devname);
+
+ if (IS_ERR(mountdata))
+ return (struct vfsmount *)mountdata;
+
+ mnt = vfs_kern_mount(&cifs_fs_type, 0, devname, mountdata);
+ kfree(mountdata);
+ kfree(devname);
+ return mnt;
+
+}
+
+static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd,
+ struct list_head *mntlist)
+{
+ /* stolen from afs code */
+ int err;
+
+ mntget(newmnt);
+ err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags, mntlist);
+ switch (err) {
+ case 0:
+ path_put(&nd->path);
+ nd->path.mnt = newmnt;
+ nd->path.dentry = dget(newmnt->mnt_root);
+ schedule_delayed_work(&cifs_dfs_automount_task,
+ cifs_dfs_mountpoint_expiry_timeout);
+ break;
+ case -EBUSY:
+ /* someone else made a mount here whilst we were busy */
+ while (d_mountpoint(nd->path.dentry) &&
+ follow_down(&nd->path.mnt, &nd->path.dentry))
+ ;
+ err = 0;
+ default:
+ mntput(newmnt);
+ break;
+ }
+ return err;
+}
+
+static void dump_referral(const struct dfs_info3_param *ref)
+{
+ cFYI(1, ("DFS: ref path: %s", ref->path_name));
+ cFYI(1, ("DFS: node path: %s", ref->node_name));
+ cFYI(1, ("DFS: fl: %hd, srv_type: %hd", ref->flags, ref->server_type));
+ cFYI(1, ("DFS: ref_flags: %hd, path_consumed: %hd", ref->ref_flag,
+ ref->path_consumed));
+}
+
+
+static void*
+cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
+{
+ struct dfs_info3_param *referrals = NULL;
+ unsigned int num_referrals = 0;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsSesInfo *ses;
+ char *full_path = NULL;
+ int xid, i;
+ int rc = 0;
+ struct vfsmount *mnt = ERR_PTR(-ENOENT);
+
+ cFYI(1, ("in %s", __func__));
+ BUG_ON(IS_ROOT(dentry));
+
+ xid = GetXid();
+
+ dput(nd->path.dentry);
+ nd->path.dentry = dget(dentry);
+
+ cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
+ ses = cifs_sb->tcon->ses;
+
+ if (!ses) {
+ rc = -EINVAL;
+ goto out_err;
+ }
+
+ /*
+ * The MSDFS spec states that paths in DFS referral requests and
+ * responses must be prefixed by a single '\' character instead of
+ * the double backslashes usually used in the UNC. This function
+ * gives us the latter, so we must adjust the result.
+ */
+ full_path = build_path_from_dentry(dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+
+ 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);
+
+ for (i = 0; i < num_referrals; i++) {
+ dump_referral(referrals+i);
+ /* connect to a storage node */
+ if (referrals[i].flags & DFSREF_STORAGE_SERVER) {
+ int len;
+ len = strlen(referrals[i].node_name);
+ if (len < 2) {
+ cERROR(1, ("%s: Net Address path too short: %s",
+ __func__, referrals[i].node_name));
+ rc = -EINVAL;
+ goto out_err;
+ }
+ mnt = cifs_dfs_do_refmount(nd->path.mnt,
+ nd->path.dentry,
+ referrals + i);
+ cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",
+ __func__,
+ referrals[i].node_name, mnt));
+
+ /* complete mount procedure if we accured submount */
+ if (!IS_ERR(mnt))
+ break;
+ }
+ }
+
+ /* we need it cause for() above could exit without valid submount */
+ rc = PTR_ERR(mnt);
+ if (IS_ERR(mnt))
+ goto out_err;
+
+ nd->path.mnt->mnt_flags |= MNT_SHRINKABLE;
+ rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);
+
+out:
+ FreeXid(xid);
+ free_dfs_info_array(referrals, num_referrals);
+ kfree(full_path);
+ cFYI(1, ("leaving %s" , __func__));
+ return ERR_PTR(rc);
+out_err:
+ path_put(&nd->path);
+ goto out;
+}
+
+struct inode_operations cifs_dfs_referral_inode_operations = {
+ .follow_link = cifs_dfs_follow_mountpoint,
+};
+
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
new file mode 100644
index 0000000..877c854
--- /dev/null
+++ b/fs/cifs/cifs_fs_sb.h
@@ -0,0 +1,52 @@
+/*
+ * fs/cifs/cifs_fs_sb.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ */
+#ifndef _CIFS_FS_SB_H
+#define _CIFS_FS_SB_H
+
+#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */
+#define CIFS_MOUNT_SET_UID 2 /* set current->euid in create etc. */
+#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */
+#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */
+#define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */
+#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
+#define CIFS_MOUNT_POSIX_PATHS 0x40 /* Negotiate posix pathnames if possible*/
+#define CIFS_MOUNT_UNX_EMUL 0x80 /* Network compat with SFUnix emulation */
+#define CIFS_MOUNT_NO_BRL 0x100 /* No sending byte range locks to srv */
+#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */
+#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */
+#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */
+#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */
+
+struct cifs_sb_info {
+ struct cifsTconInfo *tcon; /* primary mount */
+ struct list_head nested_tcon_q;
+ struct nls_table *local_nls;
+ unsigned int rsize;
+ unsigned int wsize;
+ uid_t mnt_uid;
+ gid_t mnt_gid;
+ mode_t mnt_file_mode;
+ mode_t mnt_dir_mode;
+ int mnt_cifs_flags;
+ int prepathlen;
+ char *prepath; /* relative path under the share to mount to */
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ char *mountdata; /* mount options received at mount time */
+#endif
+};
+#endif /* _CIFS_FS_SB_H */
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
new file mode 100644
index 0000000..0ab2fb5
--- /dev/null
+++ b/fs/cifs/cifs_spnego.c
@@ -0,0 +1,162 @@
+/*
+ * fs/cifs/cifs_spnego.c -- SPNEGO upcall management for CIFS
+ *
+ * Copyright (c) 2007 Red Hat, Inc.
+ * Author(s): Jeff Layton (jlayton@redhat.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/list.h>
+#include <linux/string.h>
+#include <keys/user-type.h>
+#include <linux/key-type.h>
+#include "cifsglob.h"
+#include "cifs_spnego.h"
+#include "cifs_debug.h"
+
+/* create a new cifs key */
+static int
+cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen)
+{
+ char *payload;
+ int ret;
+
+ ret = -ENOMEM;
+ payload = kmalloc(datalen, GFP_KERNEL);
+ if (!payload)
+ goto error;
+
+ /* attach the data */
+ memcpy(payload, data, datalen);
+ rcu_assign_pointer(key->payload.data, payload);
+ ret = 0;
+
+error:
+ return ret;
+}
+
+static void
+cifs_spnego_key_destroy(struct key *key)
+{
+ kfree(key->payload.data);
+}
+
+
+/*
+ * keytype for CIFS spnego keys
+ */
+struct key_type cifs_spnego_key_type = {
+ .name = "cifs.spnego",
+ .instantiate = cifs_spnego_key_instantiate,
+ .match = user_match,
+ .destroy = cifs_spnego_key_destroy,
+ .describe = user_describe,
+};
+
+/* length of longest version string e.g. strlen("ver=0xFF") */
+#define MAX_VER_STR_LEN 8
+
+/* length of longest security mechanism name, eg in future could have
+ * strlen(";sec=ntlmsspi") */
+#define MAX_MECH_STR_LEN 13
+
+/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */
+#define MAX_IPV6_ADDR_LEN 43
+
+/* strlen of "host=" */
+#define HOST_KEY_LEN 5
+
+/* strlen of ";ip4=" or ";ip6=" */
+#define IP_KEY_LEN 5
+
+/* strlen of ";uid=0x" */
+#define UID_KEY_LEN 7
+
+/* strlen of ";user=" */
+#define USER_KEY_LEN 6
+
+/* get a key struct with a SPNEGO security blob, suitable for session setup */
+struct key *
+cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
+{
+ struct TCP_Server_Info *server = sesInfo->server;
+ char *description, *dp;
+ size_t desc_len;
+ struct key *spnego_key;
+ const char *hostname = server->hostname;
+
+ /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
+ host=hostname sec=mechanism uid=0xFF user=username */
+ desc_len = MAX_VER_STR_LEN +
+ HOST_KEY_LEN + strlen(hostname) +
+ IP_KEY_LEN + MAX_IPV6_ADDR_LEN +
+ MAX_MECH_STR_LEN +
+ UID_KEY_LEN + (sizeof(uid_t) * 2) +
+ USER_KEY_LEN + strlen(sesInfo->userName) + 1;
+
+ spnego_key = ERR_PTR(-ENOMEM);
+ description = kzalloc(desc_len, GFP_KERNEL);
+ if (description == NULL)
+ goto out;
+
+ dp = description;
+ /* start with version and hostname portion of UNC string */
+ spnego_key = ERR_PTR(-EINVAL);
+ sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
+ hostname);
+ dp = description + strlen(description);
+
+ /* add the server address */
+ if (server->addr.sockAddr.sin_family == AF_INET)
+ sprintf(dp, "ip4=" NIPQUAD_FMT,
+ NIPQUAD(server->addr.sockAddr.sin_addr));
+ else if (server->addr.sockAddr.sin_family == AF_INET6)
+ sprintf(dp, "ip6=" NIP6_SEQFMT,
+ NIP6(server->addr.sockAddr6.sin6_addr));
+ else
+ goto out;
+
+ dp = description + strlen(description);
+
+ /* for now, only sec=krb5 and sec=mskrb5 are valid */
+ if (server->secType == Kerberos)
+ sprintf(dp, ";sec=krb5");
+ else if (server->secType == MSKerberos)
+ sprintf(dp, ";sec=mskrb5");
+ else
+ goto out;
+
+ dp = description + strlen(description);
+ sprintf(dp, ";uid=0x%x", sesInfo->linux_uid);
+
+ dp = description + strlen(description);
+ sprintf(dp, ";user=%s", sesInfo->userName);
+
+ cFYI(1, ("key description = %s", description));
+ spnego_key = request_key(&cifs_spnego_key_type, description, "");
+
+#ifdef CONFIG_CIFS_DEBUG2
+ if (cifsFYI && !IS_ERR(spnego_key)) {
+ struct cifs_spnego_msg *msg = spnego_key->payload.data;
+ cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
+ msg->secblob_len + msg->sesskey_len));
+ }
+#endif /* CONFIG_CIFS_DEBUG2 */
+
+out:
+ kfree(description);
+ return spnego_key;
+}
diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h
new file mode 100644
index 0000000..e4041ec
--- /dev/null
+++ b/fs/cifs/cifs_spnego.h
@@ -0,0 +1,47 @@
+/*
+ * fs/cifs/cifs_spnego.h -- SPNEGO upcall management for CIFS
+ *
+ * Copyright (c) 2007 Red Hat, Inc.
+ * Author(s): Jeff Layton (jlayton@redhat.com)
+ * Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFS_SPNEGO_H
+#define _CIFS_SPNEGO_H
+
+#define CIFS_SPNEGO_UPCALL_VERSION 2
+
+/*
+ * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION.
+ * The flags field is for future use. The request-key callout should set
+ * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob
+ * and stuff it in the data field.
+ */
+struct cifs_spnego_msg {
+ uint32_t version;
+ uint32_t flags;
+ uint32_t sesskey_len;
+ uint32_t secblob_len;
+ uint8_t data[1];
+};
+
+#ifdef __KERNEL__
+extern struct key_type cifs_spnego_key_type;
+extern struct key *cifs_get_spnego_key(struct cifsSesInfo *sesInfo);
+#endif /* KERNEL */
+
+#endif /* _CIFS_SPNEGO_H */
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
new file mode 100644
index 0000000..7d75272
--- /dev/null
+++ b/fs/cifs/cifs_unicode.c
@@ -0,0 +1,90 @@
+/*
+ * fs/cifs/cifs_unicode.c
+ *
+ * Copyright (c) International Business Machines Corp., 2000,2005
+ * Modified by Steve French (sfrench@us.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include "cifs_unicode.h"
+#include "cifs_uniupr.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+
+/*
+ * NAME: cifs_strfromUCS()
+ *
+ * FUNCTION: Convert little-endian unicode string to character string
+ *
+ */
+int
+cifs_strfromUCS_le(char *to, const __le16 *from,
+ int len, const struct nls_table *codepage)
+{
+ int i;
+ int outlen = 0;
+
+ for (i = 0; (i < len) && from[i]; i++) {
+ int charlen;
+ /* 2.4.0 kernel or greater */
+ charlen =
+ codepage->uni2char(le16_to_cpu(from[i]), &to[outlen],
+ NLS_MAX_CHARSET_SIZE);
+ if (charlen > 0) {
+ outlen += charlen;
+ } else {
+ to[outlen++] = '?';
+ }
+ }
+ to[outlen] = 0;
+ return outlen;
+}
+
+/*
+ * NAME: cifs_strtoUCS()
+ *
+ * FUNCTION: Convert character string to unicode string
+ *
+ */
+int
+cifs_strtoUCS(__le16 *to, const char *from, int len,
+ const struct nls_table *codepage)
+{
+ int charlen;
+ int i;
+ wchar_t *wchar_to = (wchar_t *)to; /* needed to quiet sparse */
+
+ for (i = 0; len && *from; i++, from += charlen, len -= charlen) {
+
+ /* works for 2.4.0 kernel or later */
+ charlen = codepage->char2uni(from, len, &wchar_to[i]);
+ if (charlen < 1) {
+ cERROR(1,
+ ("strtoUCS: char2uni of %d returned %d",
+ (int)*from, charlen));
+ /* A question mark */
+ to[i] = cpu_to_le16(0x003f);
+ charlen = 1;
+ } else
+ to[i] = cpu_to_le16(wchar_to[i]);
+
+ }
+
+ to[i] = 0;
+ return i;
+}
+
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
new file mode 100644
index 0000000..14eb9a2
--- /dev/null
+++ b/fs/cifs/cifs_unicode.h
@@ -0,0 +1,357 @@
+/*
+ * cifs_unicode: Unicode kernel case support
+ *
+ * Function:
+ * Convert a unicode character to upper or lower case using
+ * compressed tables.
+ *
+ * Copyright (c) International Business Machines Corp., 2000,2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Notes:
+ * These APIs are based on the C library functions. The semantics
+ * should match the C functions but with expanded size operands.
+ *
+ * The upper/lower functions are based on a table created by mkupr.
+ * This is a compressed table of upper and lower case conversion.
+ *
+ */
+
+#include <asm/byteorder.h>
+#include <linux/types.h>
+#include <linux/nls.h>
+
+#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
+
+/* Just define what we want from uniupr.h. We don't want to define the tables
+ * in each source file.
+ */
+#ifndef UNICASERANGE_DEFINED
+struct UniCaseRange {
+ wchar_t start;
+ wchar_t end;
+ signed char *table;
+};
+#endif /* UNICASERANGE_DEFINED */
+
+#ifndef UNIUPR_NOUPPER
+extern signed char CifsUniUpperTable[512];
+extern const struct UniCaseRange CifsUniUpperRange[];
+#endif /* UNIUPR_NOUPPER */
+
+#ifndef UNIUPR_NOLOWER
+extern signed char UniLowerTable[512];
+extern struct UniCaseRange UniLowerRange[];
+#endif /* UNIUPR_NOLOWER */
+
+#ifdef __KERNEL__
+int cifs_strfromUCS_le(char *, const __le16 *, int, const struct nls_table *);
+int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *);
+#endif
+
+/*
+ * UniStrcat: Concatenate the second string to the first
+ *
+ * Returns:
+ * Address of the first string
+ */
+static inline wchar_t *
+UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
+{
+ wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
+
+ while (*ucs1++) ; /* To end of first string */
+ ucs1--; /* Return to the null */
+ while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */
+ return anchor;
+}
+
+/*
+ * UniStrchr: Find a character in a string
+ *
+ * Returns:
+ * Address of first occurrence of character in string
+ * or NULL if the character is not in the string
+ */
+static inline wchar_t *
+UniStrchr(const wchar_t *ucs, wchar_t uc)
+{
+ while ((*ucs != uc) && *ucs)
+ ucs++;
+
+ if (*ucs == uc)
+ return (wchar_t *) ucs;
+ return NULL;
+}
+
+/*
+ * UniStrcmp: Compare two strings
+ *
+ * Returns:
+ * < 0: First string is less than second
+ * = 0: Strings are equal
+ * > 0: First string is greater than second
+ */
+static inline int
+UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2)
+{
+ while ((*ucs1 == *ucs2) && *ucs1) {
+ ucs1++;
+ ucs2++;
+ }
+ return (int) *ucs1 - (int) *ucs2;
+}
+
+/*
+ * UniStrcpy: Copy a string
+ */
+static inline wchar_t *
+UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2)
+{
+ wchar_t *anchor = ucs1; /* save the start of result string */
+
+ while ((*ucs1++ = *ucs2++)) ;
+ return anchor;
+}
+
+/*
+ * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
+ */
+static inline size_t
+UniStrlen(const wchar_t *ucs1)
+{
+ int i = 0;
+
+ while (*ucs1++)
+ i++;
+ return i;
+}
+
+/*
+ * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a
+ * string (length limited)
+ */
+static inline size_t
+UniStrnlen(const wchar_t *ucs1, int maxlen)
+{
+ int i = 0;
+
+ while (*ucs1++) {
+ i++;
+ if (i >= maxlen)
+ break;
+ }
+ return i;
+}
+
+/*
+ * UniStrncat: Concatenate length limited string
+ */
+static inline wchar_t *
+UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
+{
+ wchar_t *anchor = ucs1; /* save pointer to string 1 */
+
+ while (*ucs1++) ;
+ ucs1--; /* point to null terminator of s1 */
+ while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
+ ucs1++;
+ ucs2++;
+ }
+ *ucs1 = 0; /* Null terminate the result */
+ return (anchor);
+}
+
+/*
+ * UniStrncmp: Compare length limited string
+ */
+static inline int
+UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
+{
+ if (!n)
+ return 0; /* Null strings are equal */
+ while ((*ucs1 == *ucs2) && *ucs1 && --n) {
+ ucs1++;
+ ucs2++;
+ }
+ return (int) *ucs1 - (int) *ucs2;
+}
+
+/*
+ * UniStrncmp_le: Compare length limited string - native to little-endian
+ */
+static inline int
+UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
+{
+ if (!n)
+ return 0; /* Null strings are equal */
+ while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
+ ucs1++;
+ ucs2++;
+ }
+ return (int) *ucs1 - (int) __le16_to_cpu(*ucs2);
+}
+
+/*
+ * UniStrncpy: Copy length limited string with pad
+ */
+static inline wchar_t *
+UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
+{
+ wchar_t *anchor = ucs1;
+
+ while (n-- && *ucs2) /* Copy the strings */
+ *ucs1++ = *ucs2++;
+
+ n++;
+ while (n--) /* Pad with nulls */
+ *ucs1++ = 0;
+ return anchor;
+}
+
+/*
+ * UniStrncpy_le: Copy length limited string with pad to little-endian
+ */
+static inline wchar_t *
+UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
+{
+ wchar_t *anchor = ucs1;
+
+ while (n-- && *ucs2) /* Copy the strings */
+ *ucs1++ = __le16_to_cpu(*ucs2++);
+
+ n++;
+ while (n--) /* Pad with nulls */
+ *ucs1++ = 0;
+ return anchor;
+}
+
+/*
+ * UniStrstr: Find a string in a string
+ *
+ * Returns:
+ * Address of first match found
+ * NULL if no matching string is found
+ */
+static inline wchar_t *
+UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2)
+{
+ const wchar_t *anchor1 = ucs1;
+ const wchar_t *anchor2 = ucs2;
+
+ while (*ucs1) {
+ if (*ucs1 == *ucs2) {
+ /* Partial match found */
+ ucs1++;
+ ucs2++;
+ } else {
+ if (!*ucs2) /* Match found */
+ return (wchar_t *) anchor1;
+ ucs1 = ++anchor1; /* No match */
+ ucs2 = anchor2;
+ }
+ }
+
+ if (!*ucs2) /* Both end together */
+ return (wchar_t *) anchor1; /* Match found */
+ return NULL; /* No match */
+}
+
+#ifndef UNIUPR_NOUPPER
+/*
+ * UniToupper: Convert a unicode character to upper case
+ */
+static inline wchar_t
+UniToupper(register wchar_t uc)
+{
+ register const struct UniCaseRange *rp;
+
+ if (uc < sizeof(CifsUniUpperTable)) {
+ /* Latin characters */
+ return uc + CifsUniUpperTable[uc]; /* Use base tables */
+ } else {
+ rp = CifsUniUpperRange; /* Use range tables */
+ while (rp->start) {
+ if (uc < rp->start) /* Before start of range */
+ return uc; /* Uppercase = input */
+ if (uc <= rp->end) /* In range */
+ return uc + rp->table[uc - rp->start];
+ rp++; /* Try next range */
+ }
+ }
+ return uc; /* Past last range */
+}
+
+/*
+ * UniStrupr: Upper case a unicode string
+ */
+static inline wchar_t *
+UniStrupr(register wchar_t *upin)
+{
+ register wchar_t *up;
+
+ up = upin;
+ while (*up) { /* For all characters */
+ *up = UniToupper(*up);
+ up++;
+ }
+ return upin; /* Return input pointer */
+}
+#endif /* UNIUPR_NOUPPER */
+
+#ifndef UNIUPR_NOLOWER
+/*
+ * UniTolower: Convert a unicode character to lower case
+ */
+static inline wchar_t
+UniTolower(wchar_t uc)
+{
+ register struct UniCaseRange *rp;
+
+ if (uc < sizeof(UniLowerTable)) {
+ /* Latin characters */
+ return uc + UniLowerTable[uc]; /* Use base tables */
+ } else {
+ rp = UniLowerRange; /* Use range tables */
+ while (rp->start) {
+ if (uc < rp->start) /* Before start of range */
+ return uc; /* Uppercase = input */
+ if (uc <= rp->end) /* In range */
+ return uc + rp->table[uc - rp->start];
+ rp++; /* Try next range */
+ }
+ }
+ return uc; /* Past last range */
+}
+
+/*
+ * UniStrlwr: Lower case a unicode string
+ */
+static inline wchar_t *
+UniStrlwr(register wchar_t *upin)
+{
+ register wchar_t *up;
+
+ up = upin;
+ while (*up) { /* For all characters */
+ *up = UniTolower(*up);
+ up++;
+ }
+ return upin; /* Return input pointer */
+}
+
+#endif
diff --git a/fs/cifs/cifs_uniupr.h b/fs/cifs/cifs_uniupr.h
new file mode 100644
index 0000000..18a9d97
--- /dev/null
+++ b/fs/cifs/cifs_uniupr.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000,2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * uniupr.h - Unicode compressed case ranges
+ *
+*/
+
+#ifndef UNIUPR_NOUPPER
+/*
+ * Latin upper case
+ */
+signed char CifsUniUpperTable[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
+ 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 060-06f */
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, /* 070-07f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 0e0-0ef */
+ -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, /* 0f0-0ff */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
+ 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
+ -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
+ 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
+ 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
+ 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
+ -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
+ 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
+ -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
+ 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
+};
+
+/* Upper case range - Greek */
+static signed char UniCaseRangeU03a0[47] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */
+ 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 3b0-3bf */
+ -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64,
+ -63, -63,
+};
+
+/* Upper case range - Cyrillic */
+static signed char UniCaseRangeU0430[48] = {
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 430-43f */
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 440-44f */
+ 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, 0, -80, -80, /* 450-45f */
+};
+
+/* Upper case range - Extended cyrillic */
+static signed char UniCaseRangeU0490[61] = {
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
+ 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
+};
+
+/* Upper case range - Extended latin and greek */
+static signed char UniCaseRangeU1e00[509] = {
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
+ 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
+ 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
+ 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
+ 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
+ 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, 126, 126, 0, 0, /* 1f70-1f7f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
+ 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
+ 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
+ 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
+ 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
+ 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* Upper case range - Wide latin */
+static signed char UniCaseRangeUff40[27] = {
+ 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* ff40-ff4f */
+ -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+};
+
+/*
+ * Upper Case Range
+ */
+const struct UniCaseRange CifsUniUpperRange[] = {
+ {0x03a0, 0x03ce, UniCaseRangeU03a0},
+ {0x0430, 0x045f, UniCaseRangeU0430},
+ {0x0490, 0x04cc, UniCaseRangeU0490},
+ {0x1e00, 0x1ffc, UniCaseRangeU1e00},
+ {0xff40, 0xff5a, UniCaseRangeUff40},
+ {0}
+};
+#endif
+
+#ifndef UNIUPR_NOLOWER
+/*
+ * Latin lower case
+ */
+static signed char CifsUniLowerTable[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
+ 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 040-04f */
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, /* 050-05f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 0c0-0cf */
+ 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0, /* 0d0-0df */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */
+ 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */
+ 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, 0, /* 170-17f */
+ 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, 0, /* 180-18f */
+ 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */
+ 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */
+ 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */
+ 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */
+ 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */
+};
+
+/* Lower case range - Greek */
+static signed char UniCaseRangeL0380[44] = {
+ 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */
+ 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 390-39f */
+ 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+};
+
+/* Lower case range - Cyrillic */
+static signed char UniCaseRangeL0400[48] = {
+ 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 80, 80, /* 400-40f */
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 410-41f */
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 420-42f */
+};
+
+/* Lower case range - Extended cyrillic */
+static signed char UniCaseRangeL0490[60] = {
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */
+ 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
+};
+
+/* Lower case range - Extended latin and greek */
+static signed char UniCaseRangeL1e00[504] = {
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */
+ 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */
+ 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, 0, 0, /* 1fc0-1fcf */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */
+ 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, 0, 0, /* 1fe0-1fef */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* Lower case range - Wide latin */
+static signed char UniCaseRangeLff20[27] = {
+ 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* ff20-ff2f */
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+};
+
+/*
+ * Lower Case Range
+ */
+static const struct UniCaseRange CifsUniLowerRange[] = {
+ 0x0380, 0x03ab, UniCaseRangeL0380,
+ 0x0400, 0x042f, UniCaseRangeL0400,
+ 0x0490, 0x04cb, UniCaseRangeL0490,
+ 0x1e00, 0x1ff7, UniCaseRangeL1e00,
+ 0xff20, 0xff3a, UniCaseRangeLff20,
+ 0, 0, 0
+};
+#endif
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
new file mode 100644
index 0000000..57ecdc8
--- /dev/null
+++ b/fs/cifs/cifsacl.c
@@ -0,0 +1,736 @@
+/*
+ * fs/cifs/cifsacl.c
+ *
+ * Copyright (C) International Business Machines Corp., 2007,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Contains the routines for mapping CIFS/NTFS ACLs
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsacl.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+
+static struct cifs_wksid wksidarr[NUM_WK_SIDS] = {
+ {{1, 0, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0} }, "null user"},
+ {{1, 1, {0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0} }, "nobody"},
+ {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(11), 0, 0, 0, 0} }, "net-users"},
+ {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(18), 0, 0, 0, 0} }, "sys"},
+ {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(544), 0, 0, 0} }, "root"},
+ {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(545), 0, 0, 0} }, "users"},
+ {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(546), 0, 0, 0} }, "guest"} }
+;
+
+
+/* security id for everyone */
+static const struct cifs_sid sid_everyone = {
+ 1, 1, {0, 0, 0, 0, 0, 1}, {0} };
+/* group users */
+static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
+
+
+int match_sid(struct cifs_sid *ctsid)
+{
+ int i, j;
+ int num_subauth, num_sat, num_saw;
+ struct cifs_sid *cwsid;
+
+ if (!ctsid)
+ return -1;
+
+ for (i = 0; i < NUM_WK_SIDS; ++i) {
+ cwsid = &(wksidarr[i].cifssid);
+
+ /* compare the revision */
+ if (ctsid->revision != cwsid->revision)
+ continue;
+
+ /* compare all of the six auth values */
+ for (j = 0; j < 6; ++j) {
+ if (ctsid->authority[j] != cwsid->authority[j])
+ break;
+ }
+ if (j < 6)
+ continue; /* all of the auth values did not match */
+
+ /* compare all of the subauth values if any */
+ num_sat = ctsid->num_subauth;
+ num_saw = cwsid->num_subauth;
+ num_subauth = num_sat < num_saw ? num_sat : num_saw;
+ if (num_subauth) {
+ for (j = 0; j < num_subauth; ++j) {
+ if (ctsid->sub_auth[j] != cwsid->sub_auth[j])
+ break;
+ }
+ if (j < num_subauth)
+ continue; /* all sub_auth values do not match */
+ }
+
+ cFYI(1, ("matching sid: %s\n", wksidarr[i].sidname));
+ return 0; /* sids compare/match */
+ }
+
+ cFYI(1, ("No matching sid"));
+ return -1;
+}
+
+/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
+ the same returns 1, if they do not match returns 0 */
+int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
+{
+ int i;
+ int num_subauth, num_sat, num_saw;
+
+ if ((!ctsid) || (!cwsid))
+ return 0;
+
+ /* compare the revision */
+ if (ctsid->revision != cwsid->revision)
+ return 0;
+
+ /* compare all of the six auth values */
+ for (i = 0; i < 6; ++i) {
+ if (ctsid->authority[i] != cwsid->authority[i])
+ return 0;
+ }
+
+ /* compare all of the subauth values if any */
+ num_sat = ctsid->num_subauth;
+ num_saw = cwsid->num_subauth;
+ num_subauth = num_sat < num_saw ? num_sat : num_saw;
+ if (num_subauth) {
+ for (i = 0; i < num_subauth; ++i) {
+ if (ctsid->sub_auth[i] != cwsid->sub_auth[i])
+ return 0;
+ }
+ }
+
+ return 1; /* sids compare/match */
+}
+
+
+/* copy ntsd, owner sid, and group sid from a security descriptor to another */
+static void copy_sec_desc(const struct cifs_ntsd *pntsd,
+ struct cifs_ntsd *pnntsd, __u32 sidsoffset)
+{
+ int i;
+
+ struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+
+ /* copy security descriptor control portion */
+ pnntsd->revision = pntsd->revision;
+ pnntsd->type = pntsd->type;
+ pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
+ pnntsd->sacloffset = 0;
+ pnntsd->osidoffset = cpu_to_le32(sidsoffset);
+ pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
+
+ /* copy owner sid */
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->osidoffset));
+ nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
+
+ nowner_sid_ptr->revision = owner_sid_ptr->revision;
+ nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
+ for (i = 0; i < 6; i++)
+ nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
+ for (i = 0; i < 5; i++)
+ nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
+
+ /* copy group sid */
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->gsidoffset));
+ ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
+ sizeof(struct cifs_sid));
+
+ ngroup_sid_ptr->revision = group_sid_ptr->revision;
+ ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
+ for (i = 0; i < 6; i++)
+ ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
+ for (i = 0; i < 5; i++)
+ ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
+
+ return;
+}
+
+
+/*
+ change posix mode to reflect permissions
+ pmode is the existing mode (we only want to overwrite part of this
+ bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
+*/
+static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
+ umode_t *pbits_to_set)
+{
+ __u32 flags = le32_to_cpu(ace_flags);
+ /* the order of ACEs is important. The canonical order is to begin with
+ DENY entries followed by ALLOW, otherwise an allow entry could be
+ encountered first, making the subsequent deny entry like "dead code"
+ which would be superflous since Windows stops when a match is made
+ for the operation you are trying to perform for your user */
+
+ /* For deny ACEs we change the mask so that subsequent allow access
+ control entries do not turn on the bits we are denying */
+ if (type == ACCESS_DENIED) {
+ if (flags & GENERIC_ALL)
+ *pbits_to_set &= ~S_IRWXUGO;
+
+ if ((flags & GENERIC_WRITE) ||
+ ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
+ *pbits_to_set &= ~S_IWUGO;
+ if ((flags & GENERIC_READ) ||
+ ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
+ *pbits_to_set &= ~S_IRUGO;
+ if ((flags & GENERIC_EXECUTE) ||
+ ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
+ *pbits_to_set &= ~S_IXUGO;
+ return;
+ } else if (type != ACCESS_ALLOWED) {
+ cERROR(1, ("unknown access control type %d", type));
+ return;
+ }
+ /* else ACCESS_ALLOWED type */
+
+ if (flags & GENERIC_ALL) {
+ *pmode |= (S_IRWXUGO & (*pbits_to_set));
+ cFYI(DBG2, ("all perms"));
+ return;
+ }
+ if ((flags & GENERIC_WRITE) ||
+ ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
+ *pmode |= (S_IWUGO & (*pbits_to_set));
+ if ((flags & GENERIC_READ) ||
+ ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
+ *pmode |= (S_IRUGO & (*pbits_to_set));
+ if ((flags & GENERIC_EXECUTE) ||
+ ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
+ *pmode |= (S_IXUGO & (*pbits_to_set));
+
+ cFYI(DBG2, ("access flags 0x%x mode now 0x%x", flags, *pmode));
+ return;
+}
+
+/*
+ Generate access flags to reflect permissions mode is the existing mode.
+ This function is called for every ACE in the DACL whose SID matches
+ with either owner or group or everyone.
+*/
+
+static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
+ __u32 *pace_flags)
+{
+ /* reset access mask */
+ *pace_flags = 0x0;
+
+ /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
+ mode &= bits_to_use;
+
+ /* check for R/W/X UGO since we do not know whose flags
+ is this but we have cleared all the bits sans RWX for
+ either user or group or other as per bits_to_use */
+ if (mode & S_IRUGO)
+ *pace_flags |= SET_FILE_READ_RIGHTS;
+ if (mode & S_IWUGO)
+ *pace_flags |= SET_FILE_WRITE_RIGHTS;
+ if (mode & S_IXUGO)
+ *pace_flags |= SET_FILE_EXEC_RIGHTS;
+
+ cFYI(DBG2, ("mode: 0x%x, access flags now 0x%x", mode, *pace_flags));
+ return;
+}
+
+static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
+ const struct cifs_sid *psid, __u64 nmode, umode_t bits)
+{
+ int i;
+ __u16 size = 0;
+ __u32 access_req = 0;
+
+ pntace->type = ACCESS_ALLOWED;
+ pntace->flags = 0x0;
+ mode_to_access_flags(nmode, bits, &access_req);
+ if (!access_req)
+ access_req = SET_MINIMUM_RIGHTS;
+ pntace->access_req = cpu_to_le32(access_req);
+
+ pntace->sid.revision = psid->revision;
+ pntace->sid.num_subauth = psid->num_subauth;
+ for (i = 0; i < 6; i++)
+ pntace->sid.authority[i] = psid->authority[i];
+ for (i = 0; i < psid->num_subauth; i++)
+ pntace->sid.sub_auth[i] = psid->sub_auth[i];
+
+ size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
+ pntace->size = cpu_to_le16(size);
+
+ return size;
+}
+
+
+#ifdef CONFIG_CIFS_DEBUG2
+static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
+{
+ int num_subauth;
+
+ /* validate that we do not go past end of acl */
+
+ if (le16_to_cpu(pace->size) < 16) {
+ cERROR(1, ("ACE too small, %d", le16_to_cpu(pace->size)));
+ return;
+ }
+
+ if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) {
+ cERROR(1, ("ACL too small to parse ACE"));
+ return;
+ }
+
+ num_subauth = pace->sid.num_subauth;
+ if (num_subauth) {
+ int i;
+ cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d",
+ pace->sid.revision, pace->sid.num_subauth, pace->type,
+ pace->flags, le16_to_cpu(pace->size)));
+ for (i = 0; i < num_subauth; ++i) {
+ cFYI(1, ("ACE sub_auth[%d]: 0x%x", i,
+ le32_to_cpu(pace->sid.sub_auth[i])));
+ }
+
+ /* BB add length check to make sure that we do not have huge
+ num auths and therefore go off the end */
+ }
+
+ return;
+}
+#endif
+
+
+static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
+ struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
+ struct inode *inode)
+{
+ int i;
+ int num_aces = 0;
+ int acl_size;
+ char *acl_base;
+ struct cifs_ace **ppace;
+
+ /* BB need to add parm so we can store the SID BB */
+
+ if (!pdacl) {
+ /* no DACL in the security descriptor, set
+ all the permissions for user/group/other */
+ inode->i_mode |= S_IRWXUGO;
+ return;
+ }
+
+ /* validate that we do not go past end of acl */
+ if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
+ cERROR(1, ("ACL too small to parse DACL"));
+ return;
+ }
+
+ cFYI(DBG2, ("DACL revision %d size %d num aces %d",
+ le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
+ le32_to_cpu(pdacl->num_aces)));
+
+ /* reset rwx permissions for user/group/other.
+ Also, if num_aces is 0 i.e. DACL has no ACEs,
+ user/group/other have no permissions */
+ inode->i_mode &= ~(S_IRWXUGO);
+
+ acl_base = (char *)pdacl;
+ acl_size = sizeof(struct cifs_acl);
+
+ num_aces = le32_to_cpu(pdacl->num_aces);
+ if (num_aces > 0) {
+ umode_t user_mask = S_IRWXU;
+ umode_t group_mask = S_IRWXG;
+ umode_t other_mask = S_IRWXO;
+
+ ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
+ GFP_KERNEL);
+
+ for (i = 0; i < num_aces; ++i) {
+ ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
+#ifdef CONFIG_CIFS_DEBUG2
+ dump_ace(ppace[i], end_of_acl);
+#endif
+ if (compare_sids(&(ppace[i]->sid), pownersid))
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &(inode->i_mode),
+ &user_mask);
+ if (compare_sids(&(ppace[i]->sid), pgrpsid))
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &(inode->i_mode),
+ &group_mask);
+ if (compare_sids(&(ppace[i]->sid), &sid_everyone))
+ access_flags_to_mode(ppace[i]->access_req,
+ ppace[i]->type,
+ &(inode->i_mode),
+ &other_mask);
+
+/* memcpy((void *)(&(cifscred->aces[i])),
+ (void *)ppace[i],
+ sizeof(struct cifs_ace)); */
+
+ acl_base = (char *)ppace[i];
+ acl_size = le16_to_cpu(ppace[i]->size);
+ }
+
+ kfree(ppace);
+ }
+
+ return;
+}
+
+
+static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
+ struct cifs_sid *pgrpsid, __u64 nmode)
+{
+ u16 size = 0;
+ struct cifs_acl *pnndacl;
+
+ pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
+
+ size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
+ pownersid, nmode, S_IRWXU);
+ size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+ pgrpsid, nmode, S_IRWXG);
+ size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+ &sid_everyone, nmode, S_IRWXO);
+
+ pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
+ pndacl->num_aces = cpu_to_le32(3);
+
+ return 0;
+}
+
+
+static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
+{
+ /* BB need to add parm so we can store the SID BB */
+
+ /* validate that we do not go past end of ACL - sid must be at least 8
+ bytes long (assuming no sub-auths - e.g. the null SID */
+ if (end_of_acl < (char *)psid + 8) {
+ cERROR(1, ("ACL too small to parse SID %p", psid));
+ return -EINVAL;
+ }
+
+ if (psid->num_subauth) {
+#ifdef CONFIG_CIFS_DEBUG2
+ int i;
+ cFYI(1, ("SID revision %d num_auth %d",
+ psid->revision, psid->num_subauth));
+
+ for (i = 0; i < psid->num_subauth; i++) {
+ cFYI(1, ("SID sub_auth[%d]: 0x%x ", i,
+ le32_to_cpu(psid->sub_auth[i])));
+ }
+
+ /* BB add length check to make sure that we do not have huge
+ num auths and therefore go off the end */
+ cFYI(1, ("RID 0x%x",
+ le32_to_cpu(psid->sub_auth[psid->num_subauth-1])));
+#endif
+ }
+
+ return 0;
+}
+
+
+/* Convert CIFS ACL to POSIX form */
+static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
+ struct inode *inode)
+{
+ int rc;
+ struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
+ char *end_of_acl = ((char *)pntsd) + acl_len;
+ __u32 dacloffset;
+
+ if ((inode == NULL) || (pntsd == NULL))
+ return -EIO;
+
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->osidoffset));
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->gsidoffset));
+ dacloffset = le32_to_cpu(pntsd->dacloffset);
+ dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+ cFYI(DBG2, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x "
+ "sacloffset 0x%x dacloffset 0x%x",
+ pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
+ le32_to_cpu(pntsd->gsidoffset),
+ le32_to_cpu(pntsd->sacloffset), dacloffset));
+/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
+ rc = parse_sid(owner_sid_ptr, end_of_acl);
+ if (rc)
+ return rc;
+
+ rc = parse_sid(group_sid_ptr, end_of_acl);
+ if (rc)
+ return rc;
+
+ if (dacloffset)
+ parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
+ group_sid_ptr, inode);
+ else
+ cFYI(1, ("no ACL")); /* BB grant all or default perms? */
+
+/* cifscred->uid = owner_sid_ptr->rid;
+ cifscred->gid = group_sid_ptr->rid;
+ memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr,
+ sizeof(struct cifs_sid));
+ memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
+ sizeof(struct cifs_sid)); */
+
+
+ return 0;
+}
+
+
+/* Convert permission bits from mode to equivalent CIFS ACL */
+static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+ struct inode *inode, __u64 nmode)
+{
+ int rc = 0;
+ __u32 dacloffset;
+ __u32 ndacloffset;
+ __u32 sidsoffset;
+ struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+ struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
+ struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
+
+ if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
+ return -EIO;
+
+ owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->osidoffset));
+ group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+ le32_to_cpu(pntsd->gsidoffset));
+
+ dacloffset = le32_to_cpu(pntsd->dacloffset);
+ dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+
+ ndacloffset = sizeof(struct cifs_ntsd);
+ ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
+ ndacl_ptr->revision = dacl_ptr->revision;
+ ndacl_ptr->size = 0;
+ ndacl_ptr->num_aces = 0;
+
+ rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
+
+ sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
+
+ /* copy security descriptor control portion and owner and group sid */
+ copy_sec_desc(pntsd, pnntsd, sidsoffset);
+
+ return rc;
+}
+
+
+/* Retrieve an ACL from the server */
+static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
+ const char *path, const __u16 *pfid)
+{
+ struct cifsFileInfo *open_file = NULL;
+ bool unlock_file = false;
+ int xid;
+ int rc = -EIO;
+ __u16 fid;
+ struct super_block *sb;
+ struct cifs_sb_info *cifs_sb;
+ struct cifs_ntsd *pntsd = NULL;
+
+ cFYI(1, ("get mode from ACL for %s", path));
+
+ if (inode == NULL)
+ return NULL;
+
+ xid = GetXid();
+ if (pfid == NULL)
+ open_file = find_readable_file(CIFS_I(inode));
+ else
+ fid = *pfid;
+
+ sb = inode->i_sb;
+ if (sb == NULL) {
+ FreeXid(xid);
+ return NULL;
+ }
+ cifs_sb = CIFS_SB(sb);
+
+ if (open_file) {
+ unlock_file = true;
+ fid = open_file->netfid;
+ } else if (pfid == NULL) {
+ int oplock = 0;
+ /* open file */
+ rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
+ READ_CONTROL, 0, &fid, &oplock, NULL,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc != 0) {
+ cERROR(1, ("Unable to open file to get ACL"));
+ FreeXid(xid);
+ return NULL;
+ }
+ }
+
+ rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen);
+ cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen));
+ if (unlock_file == true) /* find_readable_file increments ref count */
+ atomic_dec(&open_file->wrtPending);
+ else if (pfid == NULL) /* if opened above we have to close the handle */
+ CIFSSMBClose(xid, cifs_sb->tcon, fid);
+ /* else handle was passed in by caller */
+
+ FreeXid(xid);
+ return pntsd;
+}
+
+/* Set an ACL on the server */
+static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
+ struct inode *inode, const char *path)
+{
+ struct cifsFileInfo *open_file;
+ bool unlock_file = false;
+ int xid;
+ int rc = -EIO;
+ __u16 fid;
+ struct super_block *sb;
+ struct cifs_sb_info *cifs_sb;
+
+ cFYI(DBG2, ("set ACL for %s from mode 0x%x", path, inode->i_mode));
+
+ if (!inode)
+ return rc;
+
+ sb = inode->i_sb;
+ if (sb == NULL)
+ return rc;
+
+ cifs_sb = CIFS_SB(sb);
+ xid = GetXid();
+
+ open_file = find_readable_file(CIFS_I(inode));
+ if (open_file) {
+ unlock_file = true;
+ fid = open_file->netfid;
+ } else {
+ int oplock = 0;
+ /* open file */
+ rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
+ WRITE_DAC, 0, &fid, &oplock, NULL,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc != 0) {
+ cERROR(1, ("Unable to open file to set ACL"));
+ FreeXid(xid);
+ return rc;
+ }
+ }
+
+ rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
+ cFYI(DBG2, ("SetCIFSACL rc = %d", rc));
+ if (unlock_file)
+ atomic_dec(&open_file->wrtPending);
+ else
+ CIFSSMBClose(xid, cifs_sb->tcon, fid);
+
+ FreeXid(xid);
+
+ return rc;
+}
+
+/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
+void acl_to_uid_mode(struct inode *inode, const char *path, const __u16 *pfid)
+{
+ struct cifs_ntsd *pntsd = NULL;
+ u32 acllen = 0;
+ int rc = 0;
+
+ cFYI(DBG2, ("converting ACL to mode for %s", path));
+ pntsd = get_cifs_acl(&acllen, inode, path, pfid);
+
+ /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
+ if (pntsd)
+ rc = parse_sec_desc(pntsd, acllen, inode);
+ if (rc)
+ cFYI(1, ("parse sec desc failed rc = %d", rc));
+
+ kfree(pntsd);
+ return;
+}
+
+/* Convert mode bits to an ACL so we can update the ACL on the server */
+int mode_to_acl(struct inode *inode, const char *path, __u64 nmode)
+{
+ int rc = 0;
+ __u32 secdesclen = 0;
+ struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
+ struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
+
+ cFYI(DBG2, ("set ACL from mode for %s", path));
+
+ /* Get the security descriptor */
+ pntsd = get_cifs_acl(&secdesclen, inode, path, NULL);
+
+ /* Add three ACEs for owner, group, everyone getting rid of
+ other ACEs as chmod disables ACEs and set the security descriptor */
+
+ if (pntsd) {
+ /* allocate memory for the smb header,
+ set security descriptor request security descriptor
+ parameters, and secuirty descriptor itself */
+
+ secdesclen = secdesclen < DEFSECDESCLEN ?
+ DEFSECDESCLEN : secdesclen;
+ pnntsd = kmalloc(secdesclen, GFP_KERNEL);
+ if (!pnntsd) {
+ cERROR(1, ("Unable to allocate security descriptor"));
+ kfree(pntsd);
+ return -ENOMEM;
+ }
+
+ rc = build_sec_desc(pntsd, pnntsd, inode, nmode);
+
+ cFYI(DBG2, ("build_sec_desc rc: %d", rc));
+
+ if (!rc) {
+ /* Set the security descriptor */
+ rc = set_cifs_acl(pnntsd, secdesclen, inode, path);
+ cFYI(DBG2, ("set_cifs_acl rc: %d", rc));
+ }
+
+ kfree(pnntsd);
+ kfree(pntsd);
+ }
+
+ return rc;
+}
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h
new file mode 100644
index 0000000..6c8096c
--- /dev/null
+++ b/fs/cifs/cifsacl.h
@@ -0,0 +1,84 @@
+/*
+ * fs/cifs/cifsacl.h
+ *
+ * Copyright (c) International Business Machines Corp., 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSACL_H
+#define _CIFSACL_H
+
+
+#define NUM_AUTHS 6 /* number of authority fields */
+#define NUM_SUBAUTHS 5 /* number of sub authority fields */
+#define NUM_WK_SIDS 7 /* number of well known sids */
+#define SIDNAMELENGTH 20 /* long enough for the ones we care about */
+#define DEFSECDESCLEN 192 /* sec desc len contaiting a dacl with three aces */
+
+#define READ_BIT 0x4
+#define WRITE_BIT 0x2
+#define EXEC_BIT 0x1
+
+#define UBITSHIFT 6
+#define GBITSHIFT 3
+
+#define ACCESS_ALLOWED 0
+#define ACCESS_DENIED 1
+
+struct cifs_ntsd {
+ __le16 revision; /* revision level */
+ __le16 type;
+ __le32 osidoffset;
+ __le32 gsidoffset;
+ __le32 sacloffset;
+ __le32 dacloffset;
+} __attribute__((packed));
+
+struct cifs_sid {
+ __u8 revision; /* revision level */
+ __u8 num_subauth;
+ __u8 authority[6];
+ __le32 sub_auth[5]; /* sub_auth[num_subauth] */
+} __attribute__((packed));
+
+struct cifs_acl {
+ __le16 revision; /* revision level */
+ __le16 size;
+ __le32 num_aces;
+} __attribute__((packed));
+
+struct cifs_ace {
+ __u8 type;
+ __u8 flags;
+ __le16 size;
+ __le32 access_req;
+ struct cifs_sid sid; /* ie UUID of user or group who gets these perms */
+} __attribute__((packed));
+
+struct cifs_wksid {
+ struct cifs_sid cifssid;
+ char sidname[SIDNAMELENGTH];
+} __attribute__((packed));
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+
+extern int match_sid(struct cifs_sid *);
+extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *);
+
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+
+#endif /* _CIFSACL_H */
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
new file mode 100644
index 0000000..bd5f13d
--- /dev/null
+++ b/fs/cifs/cifsencrypt.c
@@ -0,0 +1,427 @@
+/*
+ * fs/cifs/cifsencrypt.c
+ *
+ * Copyright (C) International Business Machines Corp., 2005,2006
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "md5.h"
+#include "cifs_unicode.h"
+#include "cifsproto.h"
+#include <linux/ctype.h>
+#include <linux/random.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, unsigned char *c8,
+ unsigned char *p24);
+
+static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
+ const struct mac_key *key, char *signature)
+{
+ struct MD5Context context;
+
+ if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL))
+ return -EINVAL;
+
+ MD5Init(&context);
+ MD5Update(&context, (char *)&key->data, key->len);
+ MD5Update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length);
+
+ MD5Final(signature, &context);
+ return 0;
+}
+
+int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number)
+{
+ int rc = 0;
+ char smb_signature[20];
+
+ if ((cifs_pdu == NULL) || (server == NULL))
+ return -EINVAL;
+
+ if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
+ return rc;
+
+ spin_lock(&GlobalMid_Lock);
+ cifs_pdu->Signature.Sequence.SequenceNumber =
+ cpu_to_le32(server->sequence_number);
+ cifs_pdu->Signature.Sequence.Reserved = 0;
+
+ *pexpected_response_sequence_number = server->sequence_number++;
+ server->sequence_number++;
+ spin_unlock(&GlobalMid_Lock);
+
+ rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key,
+ smb_signature);
+ if (rc)
+ memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
+ else
+ memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
+
+ return rc;
+}
+
+static int cifs_calc_signature2(const struct kvec *iov, int n_vec,
+ const struct mac_key *key, char *signature)
+{
+ struct MD5Context context;
+ int i;
+
+ if ((iov == NULL) || (signature == NULL) || (key == NULL))
+ return -EINVAL;
+
+ MD5Init(&context);
+ MD5Update(&context, (char *)&key->data, key->len);
+ for (i = 0; i < n_vec; i++) {
+ if (iov[i].iov_len == 0)
+ continue;
+ if (iov[i].iov_base == NULL) {
+ cERROR(1, ("null iovec entry"));
+ return -EIO;
+ }
+ /* The first entry includes a length field (which does not get
+ signed that occupies the first 4 bytes before the header */
+ if (i == 0) {
+ if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
+ break; /* nothing to sign or corrupt header */
+ MD5Update(&context, iov[0].iov_base+4,
+ iov[0].iov_len-4);
+ } else
+ MD5Update(&context, iov[i].iov_base, iov[i].iov_len);
+ }
+
+ MD5Final(signature, &context);
+
+ return 0;
+}
+
+
+int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number)
+{
+ int rc = 0;
+ char smb_signature[20];
+ struct smb_hdr *cifs_pdu = iov[0].iov_base;
+
+ if ((cifs_pdu == NULL) || (server == NULL))
+ return -EINVAL;
+
+ if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
+ return rc;
+
+ spin_lock(&GlobalMid_Lock);
+ cifs_pdu->Signature.Sequence.SequenceNumber =
+ cpu_to_le32(server->sequence_number);
+ cifs_pdu->Signature.Sequence.Reserved = 0;
+
+ *pexpected_response_sequence_number = server->sequence_number++;
+ server->sequence_number++;
+ spin_unlock(&GlobalMid_Lock);
+
+ rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key,
+ smb_signature);
+ if (rc)
+ memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
+ else
+ memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
+
+ return rc;
+}
+
+int cifs_verify_signature(struct smb_hdr *cifs_pdu,
+ const struct mac_key *mac_key,
+ __u32 expected_sequence_number)
+{
+ unsigned int rc;
+ char server_response_sig[8];
+ char what_we_think_sig_should_be[20];
+
+ if ((cifs_pdu == NULL) || (mac_key == NULL))
+ return -EINVAL;
+
+ if (cifs_pdu->Command == SMB_COM_NEGOTIATE)
+ return 0;
+
+ if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) {
+ struct smb_com_lock_req *pSMB =
+ (struct smb_com_lock_req *)cifs_pdu;
+ if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)
+ return 0;
+ }
+
+ /* BB what if signatures are supposed to be on for session but
+ server does not send one? BB */
+
+ /* Do not need to verify session setups with signature "BSRSPYL " */
+ if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0)
+ cFYI(1, ("dummy signature received for smb command 0x%x",
+ cifs_pdu->Command));
+
+ /* save off the origiginal signature so we can modify the smb and check
+ its signature against what the server sent */
+ memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8);
+
+ cifs_pdu->Signature.Sequence.SequenceNumber =
+ cpu_to_le32(expected_sequence_number);
+ cifs_pdu->Signature.Sequence.Reserved = 0;
+
+ rc = cifs_calculate_signature(cifs_pdu, mac_key,
+ what_we_think_sig_should_be);
+
+ if (rc)
+ return rc;
+
+/* cifs_dump_mem("what we think it should be: ",
+ what_we_think_sig_should_be, 16); */
+
+ if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
+ return -EACCES;
+ else
+ return 0;
+
+}
+
+/* We fill in key by putting in 40 byte array which was allocated by caller */
+int cifs_calculate_mac_key(struct mac_key *key, const char *rn,
+ const char *password)
+{
+ char temp_key[16];
+ if ((key == NULL) || (rn == NULL))
+ return -EINVAL;
+
+ E_md4hash(password, temp_key);
+ mdfour(key->data.ntlm, temp_key, 16);
+ memcpy(key->data.ntlm+16, rn, CIFS_SESS_KEY_SIZE);
+ key->len = 40;
+ return 0;
+}
+
+int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses,
+ const struct nls_table *nls_info)
+{
+ char temp_hash[16];
+ struct HMACMD5Context ctx;
+ char *ucase_buf;
+ __le16 *unicode_buf;
+ unsigned int i, user_name_len, dom_name_len;
+
+ if (ses == NULL)
+ return -EINVAL;
+
+ E_md4hash(ses->password, temp_hash);
+
+ hmac_md5_init_limK_to_64(temp_hash, 16, &ctx);
+ user_name_len = strlen(ses->userName);
+ if (user_name_len > MAX_USERNAME_SIZE)
+ return -EINVAL;
+ if (ses->domainName == NULL)
+ return -EINVAL; /* BB should we use CIFS_LINUX_DOM */
+ dom_name_len = strlen(ses->domainName);
+ if (dom_name_len > MAX_USERNAME_SIZE)
+ return -EINVAL;
+
+ ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL);
+ if (ucase_buf == NULL)
+ return -ENOMEM;
+ unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL);
+ if (unicode_buf == NULL) {
+ kfree(ucase_buf);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < user_name_len; i++)
+ ucase_buf[i] = nls_info->charset2upper[(int)ses->userName[i]];
+ ucase_buf[i] = 0;
+ user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf,
+ MAX_USERNAME_SIZE*2, nls_info);
+ unicode_buf[user_name_len] = 0;
+ user_name_len++;
+
+ for (i = 0; i < dom_name_len; i++)
+ ucase_buf[i] = nls_info->charset2upper[(int)ses->domainName[i]];
+ ucase_buf[i] = 0;
+ dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf,
+ MAX_USERNAME_SIZE*2, nls_info);
+
+ unicode_buf[user_name_len + dom_name_len] = 0;
+ hmac_md5_update((const unsigned char *) unicode_buf,
+ (user_name_len+dom_name_len)*2, &ctx);
+
+ hmac_md5_final(ses->server->ntlmv2_hash, &ctx);
+ kfree(ucase_buf);
+ kfree(unicode_buf);
+ return 0;
+}
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key)
+{
+ int i;
+ char password_with_pad[CIFS_ENCPWD_SIZE];
+
+ if (ses->server == NULL)
+ return;
+
+ memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
+ if (ses->password)
+ strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
+
+ if ((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
+ if (extended_security & CIFSSEC_MAY_PLNTXT) {
+ memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE);
+ memcpy(lnm_session_key, password_with_pad,
+ CIFS_ENCPWD_SIZE);
+ return;
+ }
+
+ /* calculate old style session key */
+ /* calling toupper is less broken than repeatedly
+ calling nls_toupper would be since that will never
+ work for UTF8, but neither handles multibyte code pages
+ but the only alternative would be converting to UCS-16 (Unicode)
+ (using a routine something like UniStrupr) then
+ uppercasing and then converting back from Unicode - which
+ would only worth doing it if we knew it were utf8. Basically
+ utf8 and other multibyte codepages each need their own strupper
+ function since a byte at a time will ont work. */
+
+ for (i = 0; i < CIFS_ENCPWD_SIZE; i++)
+ password_with_pad[i] = toupper(password_with_pad[i]);
+
+ SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key);
+ /* clear password before we return/free memory */
+ memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
+}
+#endif /* CIFS_WEAK_PW_HASH */
+
+static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ int len;
+ char nt_hash[16];
+ struct HMACMD5Context *pctxt;
+ wchar_t *user;
+ wchar_t *domain;
+
+ pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL);
+
+ if (pctxt == NULL)
+ return -ENOMEM;
+
+ /* calculate md4 hash of password */
+ E_md4hash(ses->password, nt_hash);
+
+ /* convert Domainname to unicode and uppercase */
+ hmac_md5_init_limK_to_64(nt_hash, 16, pctxt);
+
+ /* convert ses->userName to unicode and uppercase */
+ len = strlen(ses->userName);
+ user = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if (user == NULL)
+ goto calc_exit_2;
+ len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp);
+ UniStrupr(user);
+ hmac_md5_update((char *)user, 2*len, pctxt);
+
+ /* convert ses->domainName to unicode and uppercase */
+ if (ses->domainName) {
+ len = strlen(ses->domainName);
+
+ domain = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if (domain == NULL)
+ goto calc_exit_1;
+ len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len,
+ nls_cp);
+ /* the following line was removed since it didn't work well
+ with lower cased domain name that passed as an option.
+ Maybe converting the domain name earlier makes sense */
+ /* UniStrupr(domain); */
+
+ hmac_md5_update((char *)domain, 2*len, pctxt);
+
+ kfree(domain);
+ }
+calc_exit_1:
+ kfree(user);
+calc_exit_2:
+ /* BB FIXME what about bytes 24 through 40 of the signing key?
+ compare with the NTLM example */
+ hmac_md5_final(ses->server->ntlmv2_hash, pctxt);
+
+ return rc;
+}
+
+void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
+ const struct nls_table *nls_cp)
+{
+ int rc;
+ struct ntlmv2_resp *buf = (struct ntlmv2_resp *)resp_buf;
+ struct HMACMD5Context context;
+
+ buf->blob_signature = cpu_to_le32(0x00000101);
+ buf->reserved = 0;
+ buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
+ buf->reserved2 = 0;
+ buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
+ buf->names[0].length = 0;
+ buf->names[1].type = 0;
+ buf->names[1].length = 0;
+
+ /* calculate buf->ntlmv2_hash */
+ rc = calc_ntlmv2_hash(ses, nls_cp);
+ if (rc)
+ cERROR(1, ("could not get v2 hash rc %d", rc));
+ CalcNTLMv2_response(ses, resp_buf);
+
+ /* now calculate the MAC key for NTLMv2 */
+ hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
+ hmac_md5_update(resp_buf, 16, &context);
+ hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context);
+
+ memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf,
+ sizeof(struct ntlmv2_resp));
+ ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp);
+}
+
+void CalcNTLMv2_response(const struct cifsSesInfo *ses,
+ char *v2_session_response)
+{
+ struct HMACMD5Context context;
+ /* rest of v2 struct already generated */
+ memcpy(v2_session_response + 8, ses->server->cryptKey, 8);
+ hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
+
+ hmac_md5_update(v2_session_response+8,
+ sizeof(struct ntlmv2_resp) - 8, &context);
+
+ hmac_md5_final(v2_session_response, &context);
+/* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
+}
diff --git a/fs/cifs/cifsencrypt.h b/fs/cifs/cifsencrypt.h
new file mode 100644
index 0000000..152fa2d
--- /dev/null
+++ b/fs/cifs/cifsencrypt.h
@@ -0,0 +1,32 @@
+/*
+ * 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, unsigned char *c8, unsigned char *p24);
+
+
+
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
new file mode 100644
index 0000000..d9cf467
--- /dev/null
+++ b/fs/cifs/cifsfs.c
@@ -0,0 +1,1193 @@
+/*
+ * fs/cifs/cifsfs.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Common Internet FileSystem (CIFS) client
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Note that BB means BUGBUG (ie something to fix eventually) */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/vfs.h>
+#include <linux/mempool.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#define DECLARE_GLOBALS_HERE
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include <linux/mm.h>
+#include <linux/key-type.h>
+#include "dns_resolve.h"
+#include "cifs_spnego.h"
+#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
+
+#ifdef CONFIG_CIFS_QUOTA
+static struct quotactl_ops cifs_quotactl_ops;
+#endif /* QUOTA */
+
+int cifsFYI = 0;
+int cifsERROR = 1;
+int traceSMB = 0;
+unsigned int oplockEnabled = 1;
+unsigned int experimEnabled = 0;
+unsigned int linuxExtEnabled = 1;
+unsigned int lookupCacheEnabled = 1;
+unsigned int multiuser_mount = 0;
+unsigned int extended_security = CIFSSEC_DEF;
+/* unsigned int ntlmv2_support = 0; */
+unsigned int sign_CIFS_PDUs = 1;
+extern struct task_struct *oplockThread; /* remove sparse warning */
+struct task_struct *oplockThread = NULL;
+/* extern struct task_struct * dnotifyThread; remove sparse warning */
+static struct task_struct *dnotifyThread = NULL;
+static const struct super_operations cifs_super_ops;
+unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
+module_param(CIFSMaxBufSize, int, 0);
+MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "
+ "Default: 16384 Range: 8192 to 130048");
+unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
+module_param(cifs_min_rcv, int, 0);
+MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "
+ "1 to 64");
+unsigned int cifs_min_small = 30;
+module_param(cifs_min_small, int, 0);
+MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "
+ "Range: 2 to 256");
+unsigned int cifs_max_pending = CIFS_MAX_REQ;
+module_param(cifs_max_pending, int, 0);
+MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. "
+ "Default: 50 Range: 2 to 256");
+
+extern mempool_t *cifs_sm_req_poolp;
+extern mempool_t *cifs_req_poolp;
+extern mempool_t *cifs_mid_poolp;
+
+extern struct kmem_cache *cifs_oplock_cachep;
+
+static int
+cifs_read_super(struct super_block *sb, void *data,
+ const char *devname, int silent)
+{
+ struct inode *inode;
+ struct cifs_sb_info *cifs_sb;
+ int rc = 0;
+
+ /* BB should we make this contingent on mount parm? */
+ sb->s_flags |= MS_NODIRATIME | MS_NOATIME;
+ sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL);
+ cifs_sb = CIFS_SB(sb);
+ if (cifs_sb == NULL)
+ return -ENOMEM;
+
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ /* copy mount params to sb for use in submounts */
+ /* BB: should we move this after the mount so we
+ * do not have to do the copy on failed mounts?
+ * BB: May be it is better to do simple copy before
+ * complex operation (mount), and in case of fail
+ * just exit instead of doing mount and attempting
+ * undo it if this copy fails?*/
+ if (data) {
+ int len = strlen(data);
+ cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL);
+ if (cifs_sb->mountdata == NULL) {
+ kfree(sb->s_fs_info);
+ sb->s_fs_info = NULL;
+ return -ENOMEM;
+ }
+ strncpy(cifs_sb->mountdata, data, len + 1);
+ cifs_sb->mountdata[len] = '\0';
+ }
+#endif
+
+ rc = cifs_mount(sb, cifs_sb, data, devname);
+
+ if (rc) {
+ if (!silent)
+ cERROR(1,
+ ("cifs_mount failed w/return code = %d", rc));
+ goto out_mount_failed;
+ }
+
+ sb->s_magic = CIFS_MAGIC_NUMBER;
+ sb->s_op = &cifs_super_ops;
+/* if (cifs_sb->tcon->ses->server->maxBuf > MAX_CIFS_HDR_SIZE + 512)
+ sb->s_blocksize =
+ cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */
+#ifdef CONFIG_CIFS_QUOTA
+ sb->s_qcop = &cifs_quotactl_ops;
+#endif
+ sb->s_blocksize = CIFS_MAX_MSGSIZE;
+ sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */
+ inode = cifs_iget(sb, ROOT_I);
+
+ if (IS_ERR(inode)) {
+ rc = PTR_ERR(inode);
+ inode = NULL;
+ goto out_no_root;
+ }
+
+ sb->s_root = d_alloc_root(inode);
+
+ if (!sb->s_root) {
+ rc = -ENOMEM;
+ goto out_no_root;
+ }
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ cFYI(1, ("export ops supported"));
+ sb->s_export_op = &cifs_export_ops;
+ }
+#endif /* EXPERIMENTAL */
+
+ return 0;
+
+out_no_root:
+ cERROR(1, ("cifs_read_super: get root inode failed"));
+ if (inode)
+ iput(inode);
+
+ cifs_umount(sb, cifs_sb);
+
+out_mount_failed:
+ if (cifs_sb) {
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ if (cifs_sb->mountdata) {
+ kfree(cifs_sb->mountdata);
+ cifs_sb->mountdata = NULL;
+ }
+#endif
+ if (cifs_sb->local_nls)
+ unload_nls(cifs_sb->local_nls);
+ kfree(cifs_sb);
+ }
+ return rc;
+}
+
+static void
+cifs_put_super(struct super_block *sb)
+{
+ int rc = 0;
+ struct cifs_sb_info *cifs_sb;
+
+ cFYI(1, ("In cifs_put_super"));
+ cifs_sb = CIFS_SB(sb);
+ if (cifs_sb == NULL) {
+ cFYI(1, ("Empty cifs superblock info passed to unmount"));
+ return;
+ }
+ rc = cifs_umount(sb, cifs_sb);
+ if (rc)
+ cERROR(1, ("cifs_umount failed with return code %d", rc));
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ if (cifs_sb->mountdata) {
+ kfree(cifs_sb->mountdata);
+ cifs_sb->mountdata = NULL;
+ }
+#endif
+
+ unload_nls(cifs_sb->local_nls);
+ kfree(cifs_sb);
+ return;
+}
+
+static int
+cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *sb = dentry->d_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *tcon = cifs_sb->tcon;
+ int rc = -EOPNOTSUPP;
+ int xid;
+
+ xid = GetXid();
+
+ buf->f_type = CIFS_MAGIC_NUMBER;
+
+ /*
+ * PATH_MAX may be too long - it would presumably be total path,
+ * but note that some servers (includinng Samba 3) have a shorter
+ * maximum path.
+ *
+ * Instead could get the real value via SMB_QUERY_FS_ATTRIBUTE_INFO.
+ */
+ buf->f_namelen = PATH_MAX;
+ buf->f_files = 0; /* undefined */
+ buf->f_ffree = 0; /* unlimited */
+
+ /*
+ * We could add a second check for a QFS Unix capability bit
+ */
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
+ rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
+
+ /*
+ * Only need to call the old QFSInfo if failed on newer one,
+ * e.g. by OS/2.
+ **/
+ if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
+ rc = CIFSSMBQFSInfo(xid, tcon, buf);
+
+ /*
+ * Some old Windows servers also do not support level 103, retry with
+ * older level one if old server failed the previous call or we
+ * bypassed it because we detected that this was an older LANMAN sess
+ */
+ if (rc)
+ rc = SMBOldQFSInfo(xid, tcon, buf);
+
+ FreeXid(xid);
+ return 0;
+}
+
+static int cifs_permission(struct inode *inode, int mask)
+{
+ struct cifs_sb_info *cifs_sb;
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
+ if ((mask & MAY_EXEC) && !execute_ok(inode))
+ return -EACCES;
+ else
+ return 0;
+ } else /* file mode might have been restricted at mount time
+ on the client (above and beyond ACL on servers) for
+ servers which do not support setting and viewing mode bits,
+ so allowing client to check permissions is useful */
+ return generic_permission(inode, mask, NULL);
+}
+
+static struct kmem_cache *cifs_inode_cachep;
+static struct kmem_cache *cifs_req_cachep;
+static struct kmem_cache *cifs_mid_cachep;
+struct kmem_cache *cifs_oplock_cachep;
+static struct kmem_cache *cifs_sm_req_cachep;
+mempool_t *cifs_sm_req_poolp;
+mempool_t *cifs_req_poolp;
+mempool_t *cifs_mid_poolp;
+
+static struct inode *
+cifs_alloc_inode(struct super_block *sb)
+{
+ struct cifsInodeInfo *cifs_inode;
+ cifs_inode = kmem_cache_alloc(cifs_inode_cachep, GFP_KERNEL);
+ if (!cifs_inode)
+ return NULL;
+ cifs_inode->cifsAttrs = 0x20; /* default */
+ atomic_set(&cifs_inode->inUse, 0);
+ cifs_inode->time = 0;
+ cifs_inode->write_behind_rc = 0;
+ /* Until the file is open and we have gotten oplock
+ info back from the server, can not assume caching of
+ file data or metadata */
+ cifs_inode->clientCanCacheRead = false;
+ cifs_inode->clientCanCacheAll = false;
+ cifs_inode->delete_pending = false;
+ cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */
+
+ /* Can not set i_flags here - they get immediately overwritten
+ to zero by the VFS */
+/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;*/
+ INIT_LIST_HEAD(&cifs_inode->openFileList);
+ return &cifs_inode->vfs_inode;
+}
+
+static void
+cifs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
+}
+
+/*
+ * cifs_show_options() is for displaying mount options in /proc/mounts.
+ * Not all settable options are displayed but most of the important
+ * ones are.
+ */
+static int
+cifs_show_options(struct seq_file *s, struct vfsmount *m)
+{
+ struct cifs_sb_info *cifs_sb;
+
+ cifs_sb = CIFS_SB(m->mnt_sb);
+
+ if (cifs_sb) {
+ if (cifs_sb->tcon) {
+/* BB add prepath to mount options displayed */
+ seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName);
+ if (cifs_sb->tcon->ses) {
+ if (cifs_sb->tcon->ses->userName)
+ seq_printf(s, ",username=%s",
+ cifs_sb->tcon->ses->userName);
+ if (cifs_sb->tcon->ses->domainName)
+ seq_printf(s, ",domain=%s",
+ cifs_sb->tcon->ses->domainName);
+ }
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ||
+ !(cifs_sb->tcon->unix_ext))
+ seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ||
+ !(cifs_sb->tcon->unix_ext))
+ seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
+ if (!cifs_sb->tcon->unix_ext) {
+ seq_printf(s, ",file_mode=0%o,dir_mode=0%o",
+ cifs_sb->mnt_file_mode,
+ cifs_sb->mnt_dir_mode);
+ }
+ if (cifs_sb->tcon->seal)
+ seq_printf(s, ",seal");
+ if (cifs_sb->tcon->nocase)
+ seq_printf(s, ",nocase");
+ if (cifs_sb->tcon->retry)
+ seq_printf(s, ",hard");
+ }
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
+ seq_printf(s, ",posixpaths");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
+ seq_printf(s, ",setuids");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
+ seq_printf(s, ",serverino");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
+ seq_printf(s, ",directio");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ seq_printf(s, ",nouser_xattr");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+ seq_printf(s, ",mapchars");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
+ seq_printf(s, ",sfu");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ seq_printf(s, ",nobrl");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ seq_printf(s, ",cifsacl");
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ seq_printf(s, ",dynperm");
+ if (m->mnt_sb->s_flags & MS_POSIXACL)
+ seq_printf(s, ",acl");
+
+ seq_printf(s, ",rsize=%d", cifs_sb->rsize);
+ seq_printf(s, ",wsize=%d", cifs_sb->wsize);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_CIFS_QUOTA
+int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid,
+ struct fs_disk_quota *pdquota)
+{
+ int xid;
+ int rc = 0;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *pTcon;
+
+ if (cifs_sb)
+ pTcon = cifs_sb->tcon;
+ else
+ return -EIO;
+
+
+ xid = GetXid();
+ if (pTcon) {
+ cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
+ } else {
+ rc = -EIO;
+ }
+
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid,
+ struct fs_disk_quota *pdquota)
+{
+ int xid;
+ int rc = 0;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *pTcon;
+
+ if (cifs_sb)
+ pTcon = cifs_sb->tcon;
+ else
+ return -EIO;
+
+ xid = GetXid();
+ if (pTcon) {
+ cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
+ } else {
+ rc = -EIO;
+ }
+
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation)
+{
+ int xid;
+ int rc = 0;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *pTcon;
+
+ if (cifs_sb)
+ pTcon = cifs_sb->tcon;
+ else
+ return -EIO;
+
+ xid = GetXid();
+ if (pTcon) {
+ cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation));
+ } else {
+ rc = -EIO;
+ }
+
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats)
+{
+ int xid;
+ int rc = 0;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *pTcon;
+
+ if (cifs_sb) {
+ pTcon = cifs_sb->tcon;
+ } else {
+ return -EIO;
+ }
+ xid = GetXid();
+ if (pTcon) {
+ cFYI(1, ("pqstats %p", qstats));
+ } else {
+ rc = -EIO;
+ }
+
+ FreeXid(xid);
+ return rc;
+}
+
+static struct quotactl_ops cifs_quotactl_ops = {
+ .set_xquota = cifs_xquota_set,
+ .get_xquota = cifs_xquota_get,
+ .set_xstate = cifs_xstate_set,
+ .get_xstate = cifs_xstate_get,
+};
+#endif
+
+static void cifs_umount_begin(struct super_block *sb)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *tcon;
+
+ if (cifs_sb == NULL)
+ return;
+
+ tcon = cifs_sb->tcon;
+ if (tcon == NULL)
+ return;
+
+ read_lock(&cifs_tcp_ses_lock);
+ if (tcon->tc_count == 1)
+ tcon->tidStatus = CifsExiting;
+ read_unlock(&cifs_tcp_ses_lock);
+
+ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
+ /* cancel_notify_requests(tcon); */
+ if (tcon->ses && tcon->ses->server) {
+ cFYI(1, ("wake up tasks now - umount begin not complete"));
+ wake_up_all(&tcon->ses->server->request_q);
+ wake_up_all(&tcon->ses->server->response_q);
+ msleep(1); /* yield */
+ /* we have to kick the requests once more */
+ wake_up_all(&tcon->ses->server->response_q);
+ msleep(1);
+ }
+/* BB FIXME - finish add checks for tidStatus BB */
+
+ return;
+}
+
+#ifdef CONFIG_CIFS_STATS2
+static int cifs_show_stats(struct seq_file *s, struct vfsmount *mnt)
+{
+ /* BB FIXME */
+ return 0;
+}
+#endif
+
+static int cifs_remount(struct super_block *sb, int *flags, char *data)
+{
+ *flags |= MS_NODIRATIME;
+ return 0;
+}
+
+static const struct super_operations cifs_super_ops = {
+ .put_super = cifs_put_super,
+ .statfs = cifs_statfs,
+ .alloc_inode = cifs_alloc_inode,
+ .destroy_inode = cifs_destroy_inode,
+/* .drop_inode = generic_delete_inode,
+ .delete_inode = cifs_delete_inode, */ /* Do not need above two
+ functions unless later we add lazy close of inodes or unless the
+ kernel forgets to call us with the same number of releases (closes)
+ as opens */
+ .show_options = cifs_show_options,
+ .umount_begin = cifs_umount_begin,
+ .remount_fs = cifs_remount,
+#ifdef CONFIG_CIFS_STATS2
+ .show_stats = cifs_show_stats,
+#endif
+};
+
+static int
+cifs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ int rc;
+ struct super_block *sb = sget(fs_type, NULL, set_anon_super, NULL);
+
+ cFYI(1, ("Devname: %s flags: %d ", dev_name, flags));
+
+ if (IS_ERR(sb))
+ return PTR_ERR(sb);
+
+ sb->s_flags = flags;
+
+ rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0);
+ if (rc) {
+ up_write(&sb->s_umount);
+ deactivate_super(sb);
+ return rc;
+ }
+ sb->s_flags |= MS_ACTIVE;
+ return simple_set_mnt(mnt, sb);
+}
+
+static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
+ ssize_t written;
+
+ written = generic_file_aio_write(iocb, iov, nr_segs, pos);
+ if (!CIFS_I(inode)->clientCanCacheAll)
+ filemap_fdatawrite(inode->i_mapping);
+ return written;
+}
+
+static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
+{
+ /* origin == SEEK_END => we must revalidate the cached file length */
+ if (origin == SEEK_END) {
+ int retval;
+
+ /* some applications poll for the file length in this strange
+ way so we must seek to end on non-oplocked files by
+ setting the revalidate time to zero */
+ CIFS_I(file->f_path.dentry->d_inode)->time = 0;
+
+ retval = cifs_revalidate(file->f_path.dentry);
+ if (retval < 0)
+ return (loff_t)retval;
+ }
+ return generic_file_llseek_unlocked(file, offset, origin);
+}
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
+{
+ /* note that this is called by vfs setlease with the BKL held
+ although I doubt that BKL is needed here in cifs */
+ struct inode *inode = file->f_path.dentry->d_inode;
+
+ if (!(S_ISREG(inode->i_mode)))
+ return -EINVAL;
+
+ /* check if file is oplocked */
+ if (((arg == F_RDLCK) &&
+ (CIFS_I(inode)->clientCanCacheRead)) ||
+ ((arg == F_WRLCK) &&
+ (CIFS_I(inode)->clientCanCacheAll)))
+ return generic_setlease(file, arg, lease);
+ else if (CIFS_SB(inode->i_sb)->tcon->local_lease &&
+ !CIFS_I(inode)->clientCanCacheRead)
+ /* If the server claims to support oplock on this
+ file, then we still need to check oplock even
+ if the local_lease mount option is set, but there
+ are servers which do not support oplock for which
+ this mount option may be useful if the user
+ knows that the file won't be changed on the server
+ by anyone else */
+ return generic_setlease(file, arg, lease);
+ else
+ return -EAGAIN;
+}
+#endif
+
+struct file_system_type cifs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "cifs",
+ .get_sb = cifs_get_sb,
+ .kill_sb = kill_anon_super,
+ /* .fs_flags */
+};
+const struct inode_operations cifs_dir_inode_ops = {
+ .create = cifs_create,
+ .lookup = cifs_lookup,
+ .getattr = cifs_getattr,
+ .unlink = cifs_unlink,
+ .link = cifs_hardlink,
+ .mkdir = cifs_mkdir,
+ .rmdir = cifs_rmdir,
+ .rename = cifs_rename,
+ .permission = cifs_permission,
+/* revalidate:cifs_revalidate, */
+ .setattr = cifs_setattr,
+ .symlink = cifs_symlink,
+ .mknod = cifs_mknod,
+#ifdef CONFIG_CIFS_XATTR
+ .setxattr = cifs_setxattr,
+ .getxattr = cifs_getxattr,
+ .listxattr = cifs_listxattr,
+ .removexattr = cifs_removexattr,
+#endif
+};
+
+const struct inode_operations cifs_file_inode_ops = {
+/* revalidate:cifs_revalidate, */
+ .setattr = cifs_setattr,
+ .getattr = cifs_getattr, /* do we need this anymore? */
+ .rename = cifs_rename,
+ .permission = cifs_permission,
+#ifdef CONFIG_CIFS_XATTR
+ .setxattr = cifs_setxattr,
+ .getxattr = cifs_getxattr,
+ .listxattr = cifs_listxattr,
+ .removexattr = cifs_removexattr,
+#endif
+};
+
+const struct inode_operations cifs_symlink_inode_ops = {
+ .readlink = generic_readlink,
+ .follow_link = cifs_follow_link,
+ .put_link = cifs_put_link,
+ .permission = cifs_permission,
+ /* BB add the following two eventually */
+ /* revalidate: cifs_revalidate,
+ setattr: cifs_notify_change, *//* BB do we need notify change */
+#ifdef CONFIG_CIFS_XATTR
+ .setxattr = cifs_setxattr,
+ .getxattr = cifs_getxattr,
+ .listxattr = cifs_listxattr,
+ .removexattr = cifs_removexattr,
+#endif
+};
+
+const struct file_operations cifs_file_ops = {
+ .read = do_sync_read,
+ .write = do_sync_write,
+ .aio_read = generic_file_aio_read,
+ .aio_write = cifs_file_aio_write,
+ .open = cifs_open,
+ .release = cifs_close,
+ .lock = cifs_lock,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_mmap,
+ .splice_read = generic_file_splice_read,
+ .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_POSIX
+ .unlocked_ioctl = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ .dir_notify = cifs_dir_notify,
+ .setlease = cifs_setlease,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+};
+
+const struct file_operations cifs_file_direct_ops = {
+ /* no mmap, no aio, no readv -
+ BB reevaluate whether they can be done with directio, no cache */
+ .read = cifs_user_read,
+ .write = cifs_user_write,
+ .open = cifs_open,
+ .release = cifs_close,
+ .lock = cifs_lock,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .splice_read = generic_file_splice_read,
+#ifdef CONFIG_CIFS_POSIX
+ .unlocked_ioctl = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+ .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ .dir_notify = cifs_dir_notify,
+ .setlease = cifs_setlease,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+};
+const struct file_operations cifs_file_nobrl_ops = {
+ .read = do_sync_read,
+ .write = do_sync_write,
+ .aio_read = generic_file_aio_read,
+ .aio_write = cifs_file_aio_write,
+ .open = cifs_open,
+ .release = cifs_close,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .mmap = cifs_file_mmap,
+ .splice_read = generic_file_splice_read,
+ .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_POSIX
+ .unlocked_ioctl = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ .dir_notify = cifs_dir_notify,
+ .setlease = cifs_setlease,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+};
+
+const struct file_operations cifs_file_direct_nobrl_ops = {
+ /* no mmap, no aio, no readv -
+ BB reevaluate whether they can be done with directio, no cache */
+ .read = cifs_user_read,
+ .write = cifs_user_write,
+ .open = cifs_open,
+ .release = cifs_close,
+ .fsync = cifs_fsync,
+ .flush = cifs_flush,
+ .splice_read = generic_file_splice_read,
+#ifdef CONFIG_CIFS_POSIX
+ .unlocked_ioctl = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+ .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ .dir_notify = cifs_dir_notify,
+ .setlease = cifs_setlease,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+};
+
+const struct file_operations cifs_dir_ops = {
+ .readdir = cifs_readdir,
+ .release = cifs_closedir,
+ .read = generic_read_dir,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ .dir_notify = cifs_dir_notify,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+ .unlocked_ioctl = cifs_ioctl,
+ .llseek = generic_file_llseek,
+};
+
+static void
+cifs_init_once(void *inode)
+{
+ struct cifsInodeInfo *cifsi = inode;
+
+ inode_init_once(&cifsi->vfs_inode);
+ INIT_LIST_HEAD(&cifsi->lockList);
+}
+
+static int
+cifs_init_inodecache(void)
+{
+ cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
+ sizeof(struct cifsInodeInfo),
+ 0, (SLAB_RECLAIM_ACCOUNT|
+ SLAB_MEM_SPREAD),
+ cifs_init_once);
+ if (cifs_inode_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void
+cifs_destroy_inodecache(void)
+{
+ kmem_cache_destroy(cifs_inode_cachep);
+}
+
+static int
+cifs_init_request_bufs(void)
+{
+ if (CIFSMaxBufSize < 8192) {
+ /* Buffer size can not be smaller than 2 * PATH_MAX since maximum
+ Unicode path name has to fit in any SMB/CIFS path based frames */
+ CIFSMaxBufSize = 8192;
+ } else if (CIFSMaxBufSize > 1024*127) {
+ CIFSMaxBufSize = 1024 * 127;
+ } else {
+ CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/
+ }
+/* cERROR(1,("CIFSMaxBufSize %d 0x%x",CIFSMaxBufSize,CIFSMaxBufSize)); */
+ cifs_req_cachep = kmem_cache_create("cifs_request",
+ CIFSMaxBufSize +
+ MAX_CIFS_HDR_SIZE, 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (cifs_req_cachep == NULL)
+ return -ENOMEM;
+
+ if (cifs_min_rcv < 1)
+ cifs_min_rcv = 1;
+ else if (cifs_min_rcv > 64) {
+ cifs_min_rcv = 64;
+ cERROR(1, ("cifs_min_rcv set to maximum (64)"));
+ }
+
+ cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv,
+ cifs_req_cachep);
+
+ if (cifs_req_poolp == NULL) {
+ kmem_cache_destroy(cifs_req_cachep);
+ return -ENOMEM;
+ }
+ /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and
+ almost all handle based requests (but not write response, nor is it
+ sufficient for path based requests). A smaller size would have
+ been more efficient (compacting multiple slab items on one 4k page)
+ for the case in which debug was on, but this larger size allows
+ more SMBs to use small buffer alloc and is still much more
+ efficient to alloc 1 per page off the slab compared to 17K (5page)
+ alloc of large cifs buffers even when page debugging is on */
+ cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
+ MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (cifs_sm_req_cachep == NULL) {
+ mempool_destroy(cifs_req_poolp);
+ kmem_cache_destroy(cifs_req_cachep);
+ return -ENOMEM;
+ }
+
+ if (cifs_min_small < 2)
+ cifs_min_small = 2;
+ else if (cifs_min_small > 256) {
+ cifs_min_small = 256;
+ cFYI(1, ("cifs_min_small set to maximum (256)"));
+ }
+
+ cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small,
+ cifs_sm_req_cachep);
+
+ if (cifs_sm_req_poolp == NULL) {
+ mempool_destroy(cifs_req_poolp);
+ kmem_cache_destroy(cifs_req_cachep);
+ kmem_cache_destroy(cifs_sm_req_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+cifs_destroy_request_bufs(void)
+{
+ mempool_destroy(cifs_req_poolp);
+ kmem_cache_destroy(cifs_req_cachep);
+ mempool_destroy(cifs_sm_req_poolp);
+ kmem_cache_destroy(cifs_sm_req_cachep);
+}
+
+static int
+cifs_init_mids(void)
+{
+ cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids",
+ sizeof(struct mid_q_entry), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (cifs_mid_cachep == NULL)
+ return -ENOMEM;
+
+ /* 3 is a reasonable minimum number of simultaneous operations */
+ cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep);
+ if (cifs_mid_poolp == NULL) {
+ kmem_cache_destroy(cifs_mid_cachep);
+ return -ENOMEM;
+ }
+
+ cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs",
+ sizeof(struct oplock_q_entry), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (cifs_oplock_cachep == NULL) {
+ mempool_destroy(cifs_mid_poolp);
+ kmem_cache_destroy(cifs_mid_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+cifs_destroy_mids(void)
+{
+ mempool_destroy(cifs_mid_poolp);
+ kmem_cache_destroy(cifs_mid_cachep);
+ kmem_cache_destroy(cifs_oplock_cachep);
+}
+
+static int cifs_oplock_thread(void *dummyarg)
+{
+ struct oplock_q_entry *oplock_item;
+ struct cifsTconInfo *pTcon;
+ struct inode *inode;
+ __u16 netfid;
+ int rc, waitrc = 0;
+
+ set_freezable();
+ do {
+ if (try_to_freeze())
+ continue;
+
+ spin_lock(&GlobalMid_Lock);
+ if (list_empty(&GlobalOplock_Q)) {
+ spin_unlock(&GlobalMid_Lock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(39*HZ);
+ } else {
+ oplock_item = list_entry(GlobalOplock_Q.next,
+ struct oplock_q_entry, qhead);
+ cFYI(1, ("found oplock item to write out"));
+ pTcon = oplock_item->tcon;
+ inode = oplock_item->pinode;
+ netfid = oplock_item->netfid;
+ spin_unlock(&GlobalMid_Lock);
+ DeleteOplockQEntry(oplock_item);
+ /* can not grab inode sem here since it would
+ deadlock when oplock received on delete
+ since vfs_unlink holds the i_mutex across
+ the call */
+ /* mutex_lock(&inode->i_mutex);*/
+ if (S_ISREG(inode->i_mode)) {
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (CIFS_I(inode)->clientCanCacheAll == 0)
+ break_lease(inode, FMODE_READ);
+ else if (CIFS_I(inode)->clientCanCacheRead == 0)
+ break_lease(inode, FMODE_WRITE);
+#endif
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (CIFS_I(inode)->clientCanCacheRead == 0) {
+ waitrc = filemap_fdatawait(
+ inode->i_mapping);
+ invalidate_remote_inode(inode);
+ }
+ if (rc == 0)
+ rc = waitrc;
+ } else
+ rc = 0;
+ /* mutex_unlock(&inode->i_mutex);*/
+ if (rc)
+ CIFS_I(inode)->write_behind_rc = rc;
+ cFYI(1, ("Oplock flush inode %p rc %d",
+ inode, rc));
+
+ /* releasing stale oplock after recent reconnect
+ of smb session using a now incorrect file
+ handle is not a data integrity issue but do
+ not bother sending an oplock release if session
+ to server still is disconnected since oplock
+ already released by the server in that case */
+ if (!pTcon->need_reconnect) {
+ rc = CIFSSMBLock(0, pTcon, netfid,
+ 0 /* len */ , 0 /* offset */, 0,
+ 0, LOCKING_ANDX_OPLOCK_RELEASE,
+ false /* wait flag */);
+ cFYI(1, ("Oplock release rc = %d", rc));
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1); /* yield in case q were corrupt */
+ }
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int cifs_dnotify_thread(void *dummyarg)
+{
+ struct list_head *tmp;
+ struct TCP_Server_Info *server;
+
+ do {
+ if (try_to_freeze())
+ continue;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(15*HZ);
+ /* check if any stuck requests that need
+ to be woken up and wakeq so the
+ thread can wake up and error out */
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &cifs_tcp_ses_list) {
+ server = list_entry(tmp, struct TCP_Server_Info,
+ tcp_ses_list);
+ if (atomic_read(&server->inFlight))
+ wake_up_all(&server->response_q);
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int __init
+init_cifs(void)
+{
+ int rc = 0;
+ cifs_proc_init();
+ INIT_LIST_HEAD(&cifs_tcp_ses_list);
+ INIT_LIST_HEAD(&GlobalOplock_Q);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ INIT_LIST_HEAD(&GlobalDnotifyReqList);
+ INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
+#endif
+/*
+ * Initialize Global counters
+ */
+ atomic_set(&sesInfoAllocCount, 0);
+ atomic_set(&tconInfoAllocCount, 0);
+ atomic_set(&tcpSesAllocCount, 0);
+ atomic_set(&tcpSesReconnectCount, 0);
+ atomic_set(&tconInfoReconnectCount, 0);
+
+ atomic_set(&bufAllocCount, 0);
+ atomic_set(&smBufAllocCount, 0);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_set(&totBufAllocCount, 0);
+ atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
+
+ atomic_set(&midCount, 0);
+ GlobalCurrentXid = 0;
+ GlobalTotalActiveXid = 0;
+ GlobalMaxActiveXid = 0;
+ memset(Local_System_Name, 0, 15);
+ rwlock_init(&GlobalSMBSeslock);
+ rwlock_init(&cifs_tcp_ses_lock);
+ spin_lock_init(&GlobalMid_Lock);
+
+ if (cifs_max_pending < 2) {
+ cifs_max_pending = 2;
+ cFYI(1, ("cifs_max_pending set to min of 2"));
+ } else if (cifs_max_pending > 256) {
+ cifs_max_pending = 256;
+ cFYI(1, ("cifs_max_pending set to max of 256"));
+ }
+
+ rc = cifs_init_inodecache();
+ if (rc)
+ goto out_clean_proc;
+
+ rc = cifs_init_mids();
+ if (rc)
+ goto out_destroy_inodecache;
+
+ rc = cifs_init_request_bufs();
+ if (rc)
+ goto out_destroy_mids;
+
+ rc = register_filesystem(&cifs_fs_type);
+ if (rc)
+ goto out_destroy_request_bufs;
+#ifdef CONFIG_CIFS_UPCALL
+ rc = register_key_type(&cifs_spnego_key_type);
+ if (rc)
+ goto out_unregister_filesystem;
+#endif
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ rc = register_key_type(&key_type_dns_resolver);
+ if (rc)
+ goto out_unregister_key_type;
+#endif
+ oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd");
+ if (IS_ERR(oplockThread)) {
+ rc = PTR_ERR(oplockThread);
+ cERROR(1, ("error %d create oplock thread", rc));
+ goto out_unregister_dfs_key_type;
+ }
+
+ dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd");
+ if (IS_ERR(dnotifyThread)) {
+ rc = PTR_ERR(dnotifyThread);
+ cERROR(1, ("error %d create dnotify thread", rc));
+ goto out_stop_oplock_thread;
+ }
+
+ return 0;
+
+ out_stop_oplock_thread:
+ kthread_stop(oplockThread);
+ out_unregister_dfs_key_type:
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ unregister_key_type(&key_type_dns_resolver);
+ out_unregister_key_type:
+#endif
+#ifdef CONFIG_CIFS_UPCALL
+ unregister_key_type(&cifs_spnego_key_type);
+ out_unregister_filesystem:
+#endif
+ unregister_filesystem(&cifs_fs_type);
+ out_destroy_request_bufs:
+ cifs_destroy_request_bufs();
+ out_destroy_mids:
+ cifs_destroy_mids();
+ out_destroy_inodecache:
+ cifs_destroy_inodecache();
+ out_clean_proc:
+ cifs_proc_clean();
+ return rc;
+}
+
+static void __exit
+exit_cifs(void)
+{
+ cFYI(DBG2, ("exit_cifs"));
+ cifs_proc_clean();
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ cifs_dfs_release_automount_timer();
+ unregister_key_type(&key_type_dns_resolver);
+#endif
+#ifdef CONFIG_CIFS_UPCALL
+ unregister_key_type(&cifs_spnego_key_type);
+#endif
+ unregister_filesystem(&cifs_fs_type);
+ cifs_destroy_inodecache();
+ cifs_destroy_mids();
+ cifs_destroy_request_bufs();
+ kthread_stop(oplockThread);
+ kthread_stop(dnotifyThread);
+}
+
+MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
+MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */
+MODULE_DESCRIPTION
+ ("VFS to access servers complying with the SNIA CIFS Specification "
+ "e.g. Samba and Windows");
+MODULE_VERSION(CIFS_VERSION);
+module_init(init_cifs)
+module_exit(exit_cifs)
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
new file mode 100644
index 0000000..074de0b
--- /dev/null
+++ b/fs/cifs/cifsfs.h
@@ -0,0 +1,105 @@
+/*
+ * fs/cifs/cifsfs.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002, 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSFS_H
+#define _CIFSFS_H
+
+#define ROOT_I 2
+
+extern struct file_system_type cifs_fs_type;
+extern const struct address_space_operations cifs_addr_ops;
+extern const struct address_space_operations cifs_addr_ops_smallbuf;
+
+/* Functions related to super block operations */
+/* extern const struct super_operations cifs_super_ops;*/
+extern void cifs_read_inode(struct inode *);
+/*extern void cifs_delete_inode(struct inode *);*/ /* BB not needed yet */
+/* extern void cifs_write_inode(struct inode *); */ /* BB not needed yet */
+
+/* Functions related to inodes */
+extern const struct inode_operations cifs_dir_inode_ops;
+extern struct inode *cifs_iget(struct super_block *, unsigned long);
+extern int cifs_create(struct inode *, struct dentry *, int,
+ struct nameidata *);
+extern struct dentry *cifs_lookup(struct inode *, struct dentry *,
+ struct nameidata *);
+extern int cifs_unlink(struct inode *dir, struct dentry *dentry);
+extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
+extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t);
+extern int cifs_mkdir(struct inode *, struct dentry *, int);
+extern int cifs_rmdir(struct inode *, struct dentry *);
+extern int cifs_rename(struct inode *, struct dentry *, struct inode *,
+ struct dentry *);
+extern int cifs_revalidate(struct dentry *);
+extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+extern int cifs_setattr(struct dentry *, struct iattr *);
+
+extern const struct inode_operations cifs_file_inode_ops;
+extern const struct inode_operations cifs_symlink_inode_ops;
+extern struct inode_operations cifs_dfs_referral_inode_operations;
+
+
+/* Functions related to files and directories */
+extern const struct file_operations cifs_file_ops;
+extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */
+extern const struct file_operations cifs_file_nobrl_ops;
+extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */
+extern int cifs_open(struct inode *inode, struct file *file);
+extern int cifs_close(struct inode *inode, struct file *file);
+extern int cifs_closedir(struct inode *inode, struct file *file);
+extern ssize_t cifs_user_read(struct file *file, char __user *read_data,
+ size_t read_size, loff_t *poffset);
+extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
+ size_t write_size, loff_t *poffset);
+extern int cifs_lock(struct file *, int, struct file_lock *);
+extern int cifs_fsync(struct file *, struct dentry *, int);
+extern int cifs_flush(struct file *, fl_owner_t id);
+extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
+extern const struct file_operations cifs_dir_ops;
+extern int cifs_dir_open(struct inode *inode, struct file *file);
+extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
+extern int cifs_dir_notify(struct file *, unsigned long arg);
+
+/* Functions related to dir entries */
+extern struct dentry_operations cifs_dentry_ops;
+extern struct dentry_operations cifs_ci_dentry_ops;
+
+/* Functions related to symlinks */
+extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
+extern void cifs_put_link(struct dentry *direntry,
+ struct nameidata *nd, void *);
+extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
+ int buflen);
+extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
+ const char *symname);
+extern int cifs_removexattr(struct dentry *, const char *);
+extern int cifs_setxattr(struct dentry *, const char *, const void *,
+ size_t, int);
+extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
+extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
+extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+extern const struct export_operations cifs_export_ops;
+#endif /* EXPERIMENTAL */
+
+#define CIFS_VERSION "1.55"
+#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
new file mode 100644
index 0000000..c57c056
--- /dev/null
+++ b/fs/cifs/cifsglob.h
@@ -0,0 +1,670 @@
+/*
+ * fs/cifs/cifsglob.h
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ */
+#include <linux/in.h>
+#include <linux/in6.h>
+#include "cifs_fs_sb.h"
+#include "cifsacl.h"
+/*
+ * The sizes of various internal tables and strings
+ */
+#define MAX_UID_INFO 16
+#define MAX_SES_INFO 2
+#define MAX_TCON_INFO 4
+
+#define MAX_TREE_SIZE (2 + MAX_SERVER_SIZE + 1 + MAX_SHARE_SIZE + 1)
+#define MAX_SERVER_SIZE 15
+#define MAX_SHARE_SIZE 64 /* used to be 20, this should still be enough */
+#define MAX_USERNAME_SIZE 32 /* 32 is to allow for 15 char names + null
+ termination then *2 for unicode versions */
+#define MAX_PASSWORD_SIZE 16
+
+#define CIFS_MIN_RCV_POOL 4
+
+/*
+ * MAX_REQ is the maximum number of requests that WE will send
+ * on one socket concurently. It also matches the most common
+ * value of max multiplex returned by servers. We may
+ * eventually want to use the negotiated value (in case
+ * future servers can handle more) when we are more confident that
+ * we will not have problems oveloading the socket with pending
+ * write data.
+ */
+#define CIFS_MAX_REQ 50
+
+#define SERVER_NAME_LENGTH 15
+#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1)
+
+/* used to define string lengths for reversing unicode strings */
+/* (256+1)*2 = 514 */
+/* (max path length + 1 for null) * 2 for unicode */
+#define MAX_NAME 514
+
+#include "cifspdu.h"
+
+#ifndef XATTR_DOS_ATTRIB
+#define XATTR_DOS_ATTRIB "user.DOSATTRIB"
+#endif
+
+/*
+ * CIFS vfs client Status information (based on what we know.)
+ */
+
+ /* associated with each tcp and smb session */
+enum statusEnum {
+ CifsNew = 0,
+ CifsGood,
+ CifsExiting,
+ CifsNeedReconnect
+};
+
+enum securityEnum {
+ PLAINTXT = 0, /* Legacy with Plaintext passwords */
+ LANMAN, /* Legacy LANMAN auth */
+ NTLM, /* Legacy NTLM012 auth with NTLM hash */
+ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */
+ RawNTLMSSP, /* NTLMSSP without SPNEGO */
+ NTLMSSP, /* NTLMSSP via SPNEGO */
+ Kerberos, /* Kerberos via SPNEGO */
+ MSKerberos, /* MS Kerberos via SPNEGO */
+};
+
+enum protocolEnum {
+ TCP = 0,
+ SCTP
+ /* Netbios frames protocol not supported at this time */
+};
+
+struct mac_key {
+ unsigned int len;
+ union {
+ char ntlm[CIFS_SESS_KEY_SIZE + 16];
+ char krb5[CIFS_SESS_KEY_SIZE + 16]; /* BB: length correct? */
+ struct {
+ char key[16];
+ struct ntlmv2_resp resp;
+ } ntlmv2;
+ } data;
+};
+
+struct cifs_cred {
+ int uid;
+ int gid;
+ int mode;
+ int cecount;
+ struct cifs_sid osid;
+ struct cifs_sid gsid;
+ struct cifs_ntace *ntaces;
+ struct cifs_ace *aces;
+};
+
+/*
+ *****************************************************************
+ * Except the CIFS PDUs themselves all the
+ * globally interesting structs should go here
+ *****************************************************************
+ */
+
+struct TCP_Server_Info {
+ struct list_head tcp_ses_list;
+ struct list_head smb_ses_list;
+ int srv_count; /* reference counter */
+ /* 15 character server name + 0x20 16th byte indicating type = srv */
+ char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
+ char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
+ char *hostname; /* hostname portion of UNC string */
+ struct socket *ssocket;
+ union {
+ struct sockaddr_in sockAddr;
+ struct sockaddr_in6 sockAddr6;
+ } addr;
+ wait_queue_head_t response_q;
+ wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
+ struct list_head pending_mid_q;
+ void *Server_NlsInfo; /* BB - placeholder for future NLS info */
+ unsigned short server_codepage; /* codepage for the server */
+ unsigned long ip_address; /* IP addr for the server if known */
+ enum protocolEnum protocolType;
+ char versionMajor;
+ char versionMinor;
+ bool svlocal:1; /* local server or remote */
+ bool noblocksnd; /* use blocking sendmsg */
+ bool noautotune; /* do not autotune send buf sizes */
+ atomic_t inFlight; /* number of requests on the wire to server */
+#ifdef CONFIG_CIFS_STATS2
+ atomic_t inSend; /* requests trying to send */
+ atomic_t num_waiters; /* blocked waiting to get in sendrecv */
+#endif
+ enum statusEnum tcpStatus; /* what we think the status is */
+ struct semaphore tcpSem;
+ struct task_struct *tsk;
+ char server_GUID[16];
+ char secMode;
+ enum securityEnum secType;
+ unsigned int maxReq; /* Clients should submit no more */
+ /* than maxReq distinct unanswered SMBs to the server when using */
+ /* multiplexed reads or writes */
+ unsigned int maxBuf; /* maxBuf specifies the maximum */
+ /* message size the server can send or receive for non-raw SMBs */
+ unsigned int maxRw; /* maxRw specifies the maximum */
+ /* message size the server can send or receive for */
+ /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
+ char sessid[4]; /* unique token id for this session */
+ /* (returned on Negotiate */
+ int capabilities; /* allow selective disabling of caps by smb sess */
+ int timeAdj; /* Adjust for difference in server time zone in sec */
+ __u16 CurrentMid; /* multiplex id - rotating counter */
+ char cryptKey[CIFS_CRYPTO_KEY_SIZE];
+ /* 16th byte of RFC1001 workstation name is always null */
+ char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
+ __u32 sequence_number; /* needed for CIFS PDU signature */
+ struct mac_key mac_signing_key;
+ char ntlmv2_hash[16];
+ unsigned long lstrp; /* when we got last response from this server */
+};
+
+/*
+ * The following is our shortcut to user information. We surface the uid,
+ * and name. We always get the password on the fly in case it
+ * has changed. We also hang a list of sessions owned by this user off here.
+ */
+struct cifsUidInfo {
+ struct list_head userList;
+ struct list_head sessionList; /* SMB sessions for this user */
+ uid_t linux_uid;
+ char user[MAX_USERNAME_SIZE + 1]; /* ascii name of user */
+ /* BB may need ptr or callback for PAM or WinBind info */
+};
+
+/*
+ * Session structure. One of these for each uid session with a particular host
+ */
+struct cifsSesInfo {
+ struct list_head smb_ses_list;
+ struct list_head tcon_list;
+ struct semaphore sesSem;
+#if 0
+ struct cifsUidInfo *uidInfo; /* pointer to user info */
+#endif
+ struct TCP_Server_Info *server; /* pointer to server info */
+ int ses_count; /* reference counter */
+ enum statusEnum status;
+ unsigned overrideSecFlg; /* if non-zero override global sec flags */
+ __u16 ipc_tid; /* special tid for connection to IPC share */
+ __u16 flags;
+ char *serverOS; /* name of operating system underlying server */
+ char *serverNOS; /* name of network operating system of server */
+ char *serverDomain; /* security realm of server */
+ int Suid; /* remote smb uid */
+ uid_t linux_uid; /* local Linux uid */
+ int capabilities;
+ char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for
+ TCP names - will ipv6 and sctp addresses fit? */
+ char userName[MAX_USERNAME_SIZE + 1];
+ char *domainName;
+ char *password;
+ bool need_reconnect:1; /* connection reset, uid now invalid */
+};
+/* no more than one of the following three session flags may be set */
+#define CIFS_SES_NT4 1
+#define CIFS_SES_OS2 2
+#define CIFS_SES_W9X 4
+/* following flag is set for old servers such as OS2 (and Win95?)
+ which do not negotiate NTLM or POSIX dialects, but instead
+ negotiate one of the older LANMAN dialects */
+#define CIFS_SES_LANMAN 8
+/*
+ * there is one of these for each connection to a resource on a particular
+ * session
+ */
+struct cifsTconInfo {
+ struct list_head tcon_list;
+ int tc_count;
+ struct list_head openFileList;
+ struct cifsSesInfo *ses; /* pointer to session associated with */
+ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
+ char *nativeFileSystem;
+ __u16 tid; /* The 2 byte tree id */
+ __u16 Flags; /* optional support bits */
+ enum statusEnum tidStatus;
+#ifdef CONFIG_CIFS_STATS
+ atomic_t num_smbs_sent;
+ atomic_t num_writes;
+ atomic_t num_reads;
+ atomic_t num_oplock_brks;
+ atomic_t num_opens;
+ atomic_t num_closes;
+ atomic_t num_deletes;
+ atomic_t num_mkdirs;
+ atomic_t num_rmdirs;
+ atomic_t num_renames;
+ atomic_t num_t2renames;
+ atomic_t num_ffirst;
+ atomic_t num_fnext;
+ atomic_t num_fclose;
+ atomic_t num_hardlinks;
+ atomic_t num_symlinks;
+ atomic_t num_locks;
+ atomic_t num_acl_get;
+ atomic_t num_acl_set;
+#ifdef CONFIG_CIFS_STATS2
+ unsigned long long time_writes;
+ unsigned long long time_reads;
+ unsigned long long time_opens;
+ unsigned long long time_deletes;
+ unsigned long long time_closes;
+ unsigned long long time_mkdirs;
+ unsigned long long time_rmdirs;
+ unsigned long long time_renames;
+ unsigned long long time_t2renames;
+ unsigned long long time_ffirst;
+ unsigned long long time_fnext;
+ unsigned long long time_fclose;
+#endif /* CONFIG_CIFS_STATS2 */
+ __u64 bytes_read;
+ __u64 bytes_written;
+ spinlock_t stat_lock;
+#endif /* CONFIG_CIFS_STATS */
+ FILE_SYSTEM_DEVICE_INFO fsDevInfo;
+ FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
+ FILE_SYSTEM_UNIX_INFO fsUnixInfo;
+ bool ipc:1; /* set if connection to IPC$ eg for RPC/PIPES */
+ bool retry:1;
+ bool nocase:1;
+ bool seal:1; /* transport encryption for this mounted share */
+ bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol
+ for this mount even if server would support */
+ bool local_lease:1; /* check leases (only) on local system not remote */
+ bool need_reconnect:1; /* connection reset, tid now invalid */
+ /* BB add field for back pointer to sb struct(s)? */
+};
+
+/*
+ * This info hangs off the cifsFileInfo structure, pointed to by llist.
+ * This is used to track byte stream locks on the file
+ */
+struct cifsLockInfo {
+ struct list_head llist; /* pointer to next cifsLockInfo */
+ __u64 offset;
+ __u64 length;
+ __u8 type;
+};
+
+/*
+ * One of these for each open instance of a file
+ */
+struct cifs_search_info {
+ loff_t index_of_last_entry;
+ __u16 entries_in_buffer;
+ __u16 info_level;
+ __u32 resume_key;
+ char *ntwrk_buf_start;
+ char *srch_entries_start;
+ char *last_entry;
+ char *presume_name;
+ unsigned int resume_name_len;
+ bool endOfSearch:1;
+ bool emptyDir:1;
+ bool unicode:1;
+ bool smallBuf:1; /* so we know which buf_release function to call */
+};
+
+struct cifsFileInfo {
+ struct list_head tlist; /* pointer to next fid owned by tcon */
+ struct list_head flist; /* next fid (file instance) for this inode */
+ unsigned int uid; /* allows finding which FileInfo structure */
+ __u32 pid; /* process id who opened file */
+ __u16 netfid; /* file id from remote */
+ /* BB add lock scope info here if needed */ ;
+ /* lock scope id (0 if none) */
+ struct file *pfile; /* needed for writepage */
+ struct inode *pInode; /* needed for oplock break */
+ struct mutex lock_mutex;
+ struct list_head llist; /* list of byte range locks we have. */
+ bool closePend:1; /* file is marked to close */
+ bool invalidHandle:1; /* file closed via session abend */
+ bool messageMode:1; /* for pipes: message vs byte mode */
+ atomic_t wrtPending; /* handle in use - defer close */
+ struct semaphore fh_sem; /* prevents reopen race after dead ses*/
+ struct cifs_search_info srch_inf;
+};
+
+/*
+ * One of these for each file inode
+ */
+
+struct cifsInodeInfo {
+ struct list_head lockList;
+ /* BB add in lists for dirty pages i.e. write caching info for oplock */
+ struct list_head openFileList;
+ int write_behind_rc;
+ __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
+ atomic_t inUse; /* num concurrent users (local openers cifs) of file*/
+ unsigned long time; /* jiffies of last update/check of inode */
+ bool clientCanCacheRead:1; /* read oplock */
+ bool clientCanCacheAll:1; /* read and writebehind oplock */
+ bool oplockPending:1;
+ bool delete_pending:1; /* DELETE_ON_CLOSE is set */
+ struct inode vfs_inode;
+};
+
+static inline struct cifsInodeInfo *
+CIFS_I(struct inode *inode)
+{
+ return container_of(inode, struct cifsInodeInfo, vfs_inode);
+}
+
+static inline struct cifs_sb_info *
+CIFS_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
+{
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
+ return '/';
+ else
+ return '\\';
+}
+
+#ifdef CONFIG_CIFS_STATS
+#define cifs_stats_inc atomic_inc
+
+static inline void cifs_stats_bytes_written(struct cifsTconInfo *tcon,
+ unsigned int bytes)
+{
+ if (bytes) {
+ spin_lock(&tcon->stat_lock);
+ tcon->bytes_written += bytes;
+ spin_unlock(&tcon->stat_lock);
+ }
+}
+
+static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon,
+ unsigned int bytes)
+{
+ spin_lock(&tcon->stat_lock);
+ tcon->bytes_read += bytes;
+ spin_unlock(&tcon->stat_lock);
+}
+#else
+
+#define cifs_stats_inc(field) do {} while (0)
+#define cifs_stats_bytes_written(tcon, bytes) do {} while (0)
+#define cifs_stats_bytes_read(tcon, bytes) do {} while (0)
+
+#endif
+
+/* one of these for every pending CIFS request to the server */
+struct mid_q_entry {
+ struct list_head qhead; /* mids waiting on reply from this server */
+ __u16 mid; /* multiplex id */
+ __u16 pid; /* process id */
+ __u32 sequence_number; /* for CIFS signing */
+ unsigned long when_alloc; /* when mid was created */
+#ifdef CONFIG_CIFS_STATS2
+ unsigned long when_sent; /* time when smb send finished */
+ unsigned long when_received; /* when demux complete (taken off wire) */
+#endif
+ struct cifsSesInfo *ses; /* smb was sent to this server */
+ struct task_struct *tsk; /* task waiting for response */
+ struct smb_hdr *resp_buf; /* response buffer */
+ int midState; /* wish this were enum but can not pass to wait_event */
+ __u8 command; /* smb command code */
+ bool largeBuf:1; /* if valid response, is pointer to large buf */
+ bool multiRsp:1; /* multiple trans2 responses for one request */
+ bool multiEnd:1; /* both received */
+};
+
+struct oplock_q_entry {
+ struct list_head qhead;
+ struct inode *pinode;
+ struct cifsTconInfo *tcon;
+ __u16 netfid;
+};
+
+/* for pending dnotify requests */
+struct dir_notify_req {
+ struct list_head lhead;
+ __le16 Pid;
+ __le16 PidHigh;
+ __u16 Mid;
+ __u16 Tid;
+ __u16 Uid;
+ __u16 netfid;
+ __u32 filter; /* CompletionFilter (for multishot) */
+ int multishot;
+ struct file *pfile;
+};
+
+struct dfs_info3_param {
+ int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/
+ int path_consumed;
+ int server_type;
+ int ref_flag;
+ char *path_name;
+ char *node_name;
+};
+
+static inline void free_dfs_info_param(struct dfs_info3_param *param)
+{
+ if (param) {
+ kfree(param->path_name);
+ kfree(param->node_name);
+ kfree(param);
+ }
+}
+
+static inline void free_dfs_info_array(struct dfs_info3_param *param,
+ int number_of_items)
+{
+ int i;
+ if ((number_of_items == 0) || (param == NULL))
+ return;
+ for (i = 0; i < number_of_items; i++) {
+ kfree(param[i].path_name);
+ kfree(param[i].node_name);
+ }
+ kfree(param);
+}
+
+#define MID_FREE 0
+#define MID_REQUEST_ALLOCATED 1
+#define MID_REQUEST_SUBMITTED 2
+#define MID_RESPONSE_RECEIVED 4
+#define MID_RETRY_NEEDED 8 /* session closed while this request out */
+#define MID_NO_RESP_NEEDED 0x10
+
+/* Types of response buffer returned from SendReceive2 */
+#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
+#define CIFS_SMALL_BUFFER 1
+#define CIFS_LARGE_BUFFER 2
+#define CIFS_IOVEC 4 /* array of response buffers */
+
+/* Type of Request to SendReceive2 */
+#define CIFS_STD_OP 0 /* normal request timeout */
+#define CIFS_LONG_OP 1 /* long op (up to 45 sec, oplock time) */
+#define CIFS_VLONG_OP 2 /* sloow op - can take up to 180 seconds */
+#define CIFS_BLOCKING_OP 4 /* operation can block */
+#define CIFS_ASYNC_OP 8 /* do not wait for response */
+#define CIFS_TIMEOUT_MASK 0x00F /* only one of 5 above set in req */
+#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */
+#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */
+#define CIFS_NO_RESP 0x040 /* no response buffer required */
+
+/* Security Flags: indicate type of session setup needed */
+#define CIFSSEC_MAY_SIGN 0x00001
+#define CIFSSEC_MAY_NTLM 0x00002
+#define CIFSSEC_MAY_NTLMV2 0x00004
+#define CIFSSEC_MAY_KRB5 0x00008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFSSEC_MAY_LANMAN 0x00010
+#define CIFSSEC_MAY_PLNTXT 0x00020
+#else
+#define CIFSSEC_MAY_LANMAN 0
+#define CIFSSEC_MAY_PLNTXT 0
+#endif /* weak passwords */
+#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */
+
+#define CIFSSEC_MUST_SIGN 0x01001
+/* note that only one of the following can be set so the
+result of setting MUST flags more than once will be to
+require use of the stronger protocol */
+#define CIFSSEC_MUST_NTLM 0x02002
+#define CIFSSEC_MUST_NTLMV2 0x04004
+#define CIFSSEC_MUST_KRB5 0x08008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFSSEC_MUST_LANMAN 0x10010
+#define CIFSSEC_MUST_PLNTXT 0x20020
+#ifdef CONFIG_CIFS_UPCALL
+#define CIFSSEC_MASK 0x3F03F /* allows weak security but also krb5 */
+#else
+#define CIFSSEC_MASK 0x37037 /* current flags supported if weak */
+#endif /* UPCALL */
+#else /* do not allow weak pw hash */
+#ifdef CONFIG_CIFS_UPCALL
+#define CIFSSEC_MASK 0x0F00F /* flags supported if no weak allowed */
+#else
+#define CIFSSEC_MASK 0x07007 /* flags supported if no weak allowed */
+#endif /* UPCALL */
+#endif /* WEAK_PW_HASH */
+#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
+
+#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2)
+#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)
+#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5)
+/*
+ *****************************************************************
+ * All constants go here
+ *****************************************************************
+ */
+
+#define UID_HASH (16)
+
+/*
+ * Note that ONE module should define _DECLARE_GLOBALS_HERE to cause the
+ * following to be declared.
+ */
+
+/****************************************************************************
+ * Locking notes. All updates to global variables and lists should be
+ * protected by spinlocks or semaphores.
+ *
+ * Spinlocks
+ * ---------
+ * GlobalMid_Lock protects:
+ * list operations on pending_mid_q and oplockQ
+ * updates to XID counters, multiplex id and SMB sequence numbers
+ * GlobalSMBSesLock protects:
+ * list operations on tcp and SMB session lists and tCon lists
+ * f_owner.lock protects certain per file struct operations
+ * mapping->page_lock protects certain per page operations
+ *
+ * Semaphores
+ * ----------
+ * sesSem operations on smb session
+ * tconSem operations on tree connection
+ * fh_sem file handle reconnection operations
+ *
+ ****************************************************************************/
+
+#ifdef DECLARE_GLOBALS_HERE
+#define GLOBAL_EXTERN
+#else
+#define GLOBAL_EXTERN extern
+#endif
+
+/*
+ * the list of TCP_Server_Info structures, ie each of the sockets
+ * connecting our client to a distinct server (ip address), is
+ * chained together by cifs_tcp_ses_list. The list of all our SMB
+ * sessions (and from that the tree connections) can be found
+ * by iterating over cifs_tcp_ses_list
+ */
+GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
+
+/*
+ * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
+ * tcp session, and the list of tcon's per smb session. It also protects
+ * the reference counters for the server, smb session, and tcon. Finally,
+ * changes to the tcon->tidStatus should be done while holding this lock.
+ */
+GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
+
+/*
+ * This lock protects the cifs_file->llist and cifs_file->flist
+ * list operations, and updates to some flags (cifs_file->invalidHandle)
+ * It will be moved to either use the tcon->stat_lock or equivalent later.
+ * If cifs_tcp_ses_lock and the lock below are both needed to be held, then
+ * the cifs_tcp_ses_lock must be grabbed first and released last.
+ */
+GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;
+
+GLOBAL_EXTERN struct list_head GlobalOplock_Q;
+
+/* Outstanding dir notify requests */
+GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
+/* DirNotify response queue */
+GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q;
+
+/*
+ * Global transaction id (XID) information
+ */
+GLOBAL_EXTERN unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */
+GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */
+GLOBAL_EXTERN unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */
+GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above & list operations */
+ /* on midQ entries */
+GLOBAL_EXTERN char Local_System_Name[15];
+
+/*
+ * Global counters, updated atomically
+ */
+GLOBAL_EXTERN atomic_t sesInfoAllocCount;
+GLOBAL_EXTERN atomic_t tconInfoAllocCount;
+GLOBAL_EXTERN atomic_t tcpSesAllocCount;
+GLOBAL_EXTERN atomic_t tcpSesReconnectCount;
+GLOBAL_EXTERN atomic_t tconInfoReconnectCount;
+
+/* Various Debug counters */
+GLOBAL_EXTERN atomic_t bufAllocCount; /* current number allocated */
+#ifdef CONFIG_CIFS_STATS2
+GLOBAL_EXTERN atomic_t totBufAllocCount; /* total allocated over all time */
+GLOBAL_EXTERN atomic_t totSmBufAllocCount;
+#endif
+GLOBAL_EXTERN atomic_t smBufAllocCount;
+GLOBAL_EXTERN atomic_t midCount;
+
+/* Misc globals */
+GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
+ to be established on existing mount if we
+ have the uid/password or Kerberos credential
+ or equivalent for current user */
+GLOBAL_EXTERN unsigned int oplockEnabled;
+GLOBAL_EXTERN unsigned int experimEnabled;
+GLOBAL_EXTERN unsigned int lookupCacheEnabled;
+GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent
+ with more secure ntlmssp2 challenge/resp */
+GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
+GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
+GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */
+GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
+GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */
+GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
+
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
new file mode 100644
index 0000000..d2a073e
--- /dev/null
+++ b/fs/cifs/cifspdu.h
@@ -0,0 +1,2634 @@
+/*
+ * fs/cifs/cifspdu.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CIFSPDU_H
+#define _CIFSPDU_H
+
+#include <net/sock.h>
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define LANMAN_PROT 0
+#define LANMAN2_PROT 1
+#define CIFS_PROT 2
+#else
+#define CIFS_PROT 0
+#endif
+#define POSIX_PROT (CIFS_PROT+1)
+#define BAD_PROT 0xFFFF
+
+/* SMB command codes */
+/*
+ * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
+ * (ie which include no useful data other than the SMB error code itself).
+ * Knowing this helps avoid response buffer allocations and copy in some cases
+ */
+#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */
+#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */
+#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */
+#define SMB_COM_DELETE 0x06 /* trivial response */
+#define SMB_COM_RENAME 0x07 /* trivial response */
+#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */
+#define SMB_COM_SETATTR 0x09 /* trivial response */
+#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */
+#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/
+#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */
+#define SMB_COM_READ_ANDX 0x2E
+#define SMB_COM_WRITE_ANDX 0x2F
+#define SMB_COM_TRANSACTION2 0x32
+#define SMB_COM_TRANSACTION2_SECONDARY 0x33
+#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */
+#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */
+#define SMB_COM_NEGOTIATE 0x72
+#define SMB_COM_SESSION_SETUP_ANDX 0x73
+#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */
+#define SMB_COM_TREE_CONNECT_ANDX 0x75
+#define SMB_COM_NT_TRANSACT 0xA0
+#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1
+#define SMB_COM_NT_CREATE_ANDX 0xA2
+#define SMB_COM_NT_CANCEL 0xA4 /* no response */
+#define SMB_COM_NT_RENAME 0xA5 /* trivial response */
+
+/* Transact2 subcommand codes */
+#define TRANS2_OPEN 0x00
+#define TRANS2_FIND_FIRST 0x01
+#define TRANS2_FIND_NEXT 0x02
+#define TRANS2_QUERY_FS_INFORMATION 0x03
+#define TRANS2_SET_FS_INFORMATION 0x04
+#define TRANS2_QUERY_PATH_INFORMATION 0x05
+#define TRANS2_SET_PATH_INFORMATION 0x06
+#define TRANS2_QUERY_FILE_INFORMATION 0x07
+#define TRANS2_SET_FILE_INFORMATION 0x08
+#define TRANS2_GET_DFS_REFERRAL 0x10
+#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11
+
+/* SMB Transact (Named Pipe) subcommand codes */
+#define TRANS_SET_NMPIPE_STATE 0x0001
+#define TRANS_RAW_READ_NMPIPE 0x0011
+#define TRANS_QUERY_NMPIPE_STATE 0x0021
+#define TRANS_QUERY_NMPIPE_INFO 0x0022
+#define TRANS_PEEK_NMPIPE 0x0023
+#define TRANS_TRANSACT_NMPIPE 0x0026
+#define TRANS_RAW_WRITE_NMPIPE 0x0031
+#define TRANS_READ_NMPIPE 0x0036
+#define TRANS_WRITE_NMPIPE 0x0037
+#define TRANS_WAIT_NMPIPE 0x0053
+#define TRANS_CALL_NMPIPE 0x0054
+
+/* NT Transact subcommand codes */
+#define NT_TRANSACT_CREATE 0x01
+#define NT_TRANSACT_IOCTL 0x02
+#define NT_TRANSACT_SET_SECURITY_DESC 0x03
+#define NT_TRANSACT_NOTIFY_CHANGE 0x04
+#define NT_TRANSACT_RENAME 0x05
+#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06
+#define NT_TRANSACT_GET_USER_QUOTA 0x07
+#define NT_TRANSACT_SET_USER_QUOTA 0x08
+
+#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
+/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */
+/* among the requests (NTCreateX response is bigger with wct of 34) */
+#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */
+#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */
+
+/* internal cifs vfs structures */
+/*****************************************************************
+ * All constants go here
+ *****************************************************************
+ */
+
+/*
+ * Starting value for maximum SMB size negotiation
+ */
+#define CIFS_MAX_MSGSIZE (4*4096)
+
+/*
+ * Size of encrypted user password in bytes
+ */
+#define CIFS_ENCPWD_SIZE (16)
+
+/*
+ * Size of the crypto key returned on the negotiate SMB in bytes
+ */
+#define CIFS_CRYPTO_KEY_SIZE (8)
+
+/*
+ * Size of the session key (crypto key encrypted with the password
+ */
+#define CIFS_SESS_KEY_SIZE (24)
+
+/*
+ * Maximum user name length
+ */
+#define CIFS_UNLEN (20)
+
+/*
+ * Flags on SMB open
+ */
+#define SMBOPEN_WRITE_THROUGH 0x4000
+#define SMBOPEN_DENY_ALL 0x0010
+#define SMBOPEN_DENY_WRITE 0x0020
+#define SMBOPEN_DENY_READ 0x0030
+#define SMBOPEN_DENY_NONE 0x0040
+#define SMBOPEN_READ 0x0000
+#define SMBOPEN_WRITE 0x0001
+#define SMBOPEN_READWRITE 0x0002
+#define SMBOPEN_EXECUTE 0x0003
+
+#define SMBOPEN_OCREATE 0x0010
+#define SMBOPEN_OTRUNC 0x0002
+#define SMBOPEN_OAPPEND 0x0001
+
+/*
+ * SMB flag definitions
+ */
+#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */
+#define SMBFLG_RCV_POSTED 0x02 /* obsolete */
+#define SMBFLG_RSVD 0x04
+#define SMBFLG_CASELESS 0x08 /* all pathnames treated as caseless (off
+ implies case sensitive file handling request) */
+#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */
+#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */
+#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */
+#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */
+
+/*
+ * SMB flag2 definitions
+ */
+#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3)
+ path names in response */
+#define SMBFLG2_KNOWS_EAS cpu_to_le16(2)
+#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4)
+#define SMBFLG2_COMPRESSED (8)
+#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10)
+#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40)
+#define SMBFLG2_REPARSE_PATH (0x400)
+#define SMBFLG2_EXT_SEC cpu_to_le16(0x800)
+#define SMBFLG2_DFS cpu_to_le16(0x1000)
+#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000)
+#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000)
+#define SMBFLG2_UNICODE cpu_to_le16(0x8000)
+
+/*
+ * These are the file access permission bits defined in CIFS for the
+ * NTCreateAndX as well as the level 0x107
+ * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO
+ * responds with the AccessFlags.
+ * The AccessFlags specifies the access permissions a caller has to the
+ * file and can have any suitable combination of the following values:
+ */
+
+#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
+#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
+#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
+#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
+ /* with the file can be read */
+#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
+ /* with the file can be written */
+#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
+ /* the file using system paging I/O */
+#define FILE_DELETE_CHILD 0x00000040
+#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
+ /* file can be read */
+#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
+ /* file can be written */
+#define DELETE 0x00010000 /* The file can be deleted */
+#define READ_CONTROL 0x00020000 /* The access control list and */
+ /* ownership associated with the */
+ /* file can be read */
+#define WRITE_DAC 0x00040000 /* The access control list and */
+ /* ownership associated with the */
+ /* file can be written. */
+#define WRITE_OWNER 0x00080000 /* Ownership information associated */
+ /* with the file can be written */
+#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
+ /* synchronize with the completion */
+ /* of an input/output request */
+#define GENERIC_ALL 0x10000000
+#define GENERIC_EXECUTE 0x20000000
+#define GENERIC_WRITE 0x40000000
+#define GENERIC_READ 0x80000000
+ /* In summary - Relevant file */
+ /* access flags from CIFS are */
+ /* file_read_data, file_write_data */
+ /* file_execute, file_read_attributes*/
+ /* write_dac, and delete. */
+
+#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
+#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
+
+#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \
+ | FILE_READ_ATTRIBUTES \
+ | FILE_WRITE_ATTRIBUTES \
+ | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER | SYNCHRONIZE)
+#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | FILE_READ_EA | FILE_WRITE_EA \
+ | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES \
+ | FILE_WRITE_ATTRIBUTES \
+ | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER | SYNCHRONIZE)
+#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
+ | FILE_READ_ATTRIBUTES \
+ | FILE_WRITE_ATTRIBUTES \
+ | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER | SYNCHRONIZE)
+
+#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
+ | READ_CONTROL | SYNCHRONIZE)
+
+
+/*
+ * Invalid readdir handle
+ */
+#define CIFS_NO_HANDLE 0xFFFF
+
+#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL
+#define NO_CHANGE_32 0xFFFFFFFFUL
+
+/* IPC$ in ASCII */
+#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24"
+
+/* IPC$ in Unicode */
+#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00"
+
+/* Unicode Null terminate 2 bytes of 0 */
+#define UNICODE_NULL "\x00\x00"
+#define ASCII_NULL 0x00
+
+/*
+ * Server type values (returned on EnumServer API
+ */
+#define CIFS_SV_TYPE_DC 0x00000008
+#define CIFS_SV_TYPE_BACKDC 0x00000010
+
+/*
+ * Alias type flags (From EnumAlias API call
+ */
+#define CIFS_ALIAS_TYPE_FILE 0x0001
+#define CIFS_SHARE_TYPE_FILE 0x0000
+
+/*
+ * File Attribute flags
+ */
+#define ATTR_READONLY 0x0001
+#define ATTR_HIDDEN 0x0002
+#define ATTR_SYSTEM 0x0004
+#define ATTR_VOLUME 0x0008
+#define ATTR_DIRECTORY 0x0010
+#define ATTR_ARCHIVE 0x0020
+#define ATTR_DEVICE 0x0040
+#define ATTR_NORMAL 0x0080
+#define ATTR_TEMPORARY 0x0100
+#define ATTR_SPARSE 0x0200
+#define ATTR_REPARSE 0x0400
+#define ATTR_COMPRESSED 0x0800
+#define ATTR_OFFLINE 0x1000 /* ie file not immediately available -
+ on offline storage */
+#define ATTR_NOT_CONTENT_INDEXED 0x2000
+#define ATTR_ENCRYPTED 0x4000
+#define ATTR_POSIX_SEMANTICS 0x01000000
+#define ATTR_BACKUP_SEMANTICS 0x02000000
+#define ATTR_DELETE_ON_CLOSE 0x04000000
+#define ATTR_SEQUENTIAL_SCAN 0x08000000
+#define ATTR_RANDOM_ACCESS 0x10000000
+#define ATTR_NO_BUFFERING 0x20000000
+#define ATTR_WRITE_THROUGH 0x80000000
+
+/* ShareAccess flags */
+#define FILE_NO_SHARE 0x00000000
+#define FILE_SHARE_READ 0x00000001
+#define FILE_SHARE_WRITE 0x00000002
+#define FILE_SHARE_DELETE 0x00000004
+#define FILE_SHARE_ALL 0x00000007
+
+/* CreateDisposition flags, similar to CreateAction as well */
+#define FILE_SUPERSEDE 0x00000000
+#define FILE_OPEN 0x00000001
+#define FILE_CREATE 0x00000002
+#define FILE_OPEN_IF 0x00000003
+#define FILE_OVERWRITE 0x00000004
+#define FILE_OVERWRITE_IF 0x00000005
+
+/* CreateOptions */
+#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */
+#define CREATE_WRITE_THROUGH 0x00000002
+#define CREATE_SEQUENTIAL 0x00000004
+#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */
+#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */
+#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */
+#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */
+#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */
+#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */
+#define CREATE_NO_EA_KNOWLEDGE 0x00000200
+#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete
+ "open for recovery" flag should
+ be zero in any case */
+#define CREATE_OPEN_FOR_RECOVERY 0x00000400
+#define CREATE_RANDOM_ACCESS 0x00000800
+#define CREATE_DELETE_ON_CLOSE 0x00001000
+#define CREATE_OPEN_BY_ID 0x00002000
+#define CREATE_OPEN_BACKUP_INTENT 0x00004000
+#define CREATE_NO_COMPRESSION 0x00008000
+#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */
+#define OPEN_REPARSE_POINT 0x00200000
+#define OPEN_NO_RECALL 0x00400000
+#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */
+#define CREATE_OPTIONS_MASK 0x007FFFFF
+#define CREATE_OPTION_READONLY 0x10000000
+#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */
+
+/* ImpersonationLevel flags */
+#define SECURITY_ANONYMOUS 0
+#define SECURITY_IDENTIFICATION 1
+#define SECURITY_IMPERSONATION 2
+#define SECURITY_DELEGATION 3
+
+/* SecurityFlags */
+#define SECURITY_CONTEXT_TRACKING 0x01
+#define SECURITY_EFFECTIVE_ONLY 0x02
+
+/*
+ * Default PID value, used in all SMBs where the PID is not important
+ */
+#define CIFS_DFT_PID 0x1234
+
+/*
+ * We use the same routine for Copy and Move SMBs. This flag is used to
+ * distinguish
+ */
+#define CIFS_COPY_OP 1
+#define CIFS_RENAME_OP 2
+
+#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */
+#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */
+
+struct smb_hdr {
+ __u32 smb_buf_length; /* big endian on wire *//* BB length is only two
+ or three bytes - with one or two byte type preceding it that are
+ zero - we could mask the type byte off just in case BB */
+ __u8 Protocol[4];
+ __u8 Command;
+ union {
+ struct {
+ __u8 ErrorClass;
+ __u8 Reserved;
+ __le16 Error;
+ } __attribute__((packed)) DosError;
+ __le32 CifsError;
+ } __attribute__((packed)) Status;
+ __u8 Flags;
+ __le16 Flags2; /* note: le */
+ __le16 PidHigh;
+ union {
+ struct {
+ __le32 SequenceNumber; /* le */
+ __u32 Reserved; /* zero */
+ } __attribute__((packed)) Sequence;
+ __u8 SecuritySignature[8]; /* le */
+ } __attribute__((packed)) Signature;
+ __u8 pad[2];
+ __u16 Tid;
+ __le16 Pid;
+ __u16 Uid;
+ __u16 Mid;
+ __u8 WordCount;
+} __attribute__((packed));
+/* given a pointer to an smb_hdr retrieve the value of byte count */
+#define BCC(smb_var) (*(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount)))
+#define BCC_LE(smb_var) (*(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount)))
+/* given a pointer to an smb_hdr retrieve the pointer to the byte area */
+#define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2 * smb_var->WordCount) + 2)
+
+/*
+ * Computer Name Length (since Netbios name was length 16 with last byte 0x20)
+ * No longer as important, now that TCP names are more commonly used to
+ * resolve hosts.
+ */
+#define CNLEN 15
+
+/*
+ * Share Name Length (SNLEN)
+ * Note: This length was limited by the SMB used to get
+ * the Share info. NetShareEnum only returned 13
+ * chars, including the null termination.
+ * This was removed because it no longer is limiting.
+ */
+
+/*
+ * Comment Length
+ */
+#define MAXCOMMENTLEN 40
+
+/*
+ * The OS/2 maximum path name
+ */
+#define MAX_PATHCONF 256
+
+/*
+ * SMB frame definitions (following must be packed structs)
+ * See the SNIA CIFS Specification for details.
+ *
+ * The Naming convention is the lower case version of the
+ * smb command code name for the struct and this is typedef to the
+ * uppercase version of the same name with the prefix SMB_ removed
+ * for brevity. Although typedefs are not commonly used for
+ * structure definitions in the Linux kernel, their use in the
+ * CIFS standards document, which this code is based on, may
+ * make this one of the cases where typedefs for structures make
+ * sense to improve readability for readers of the standards doc.
+ * Typedefs can always be removed later if they are too distracting
+ * and they are only used for the CIFSs PDUs themselves, not
+ * internal cifs vfs structures
+ *
+ */
+
+typedef struct negotiate_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ unsigned char DialectsArray[1];
+} __attribute__((packed)) NEGOTIATE_REQ;
+
+/* Dialect index is 13 for LANMAN */
+
+#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */
+
+typedef struct lanman_neg_rsp {
+ struct smb_hdr hdr; /* wct = 13 */
+ __le16 DialectIndex;
+ __le16 SecurityMode;
+ __le16 MaxBufSize;
+ __le16 MaxMpxCount;
+ __le16 MaxNumberVcs;
+ __le16 RawMode;
+ __le32 SessionKey;
+ struct {
+ __le16 Time;
+ __le16 Date;
+ } __attribute__((packed)) SrvTime;
+ __le16 ServerTimeZone;
+ __le16 EncryptionKeyLength;
+ __le16 Reserved;
+ __u16 ByteCount;
+ unsigned char EncryptionKey[1];
+} __attribute__((packed)) LANMAN_NEG_RSP;
+
+#define READ_RAW_ENABLE 1
+#define WRITE_RAW_ENABLE 2
+#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE)
+
+typedef struct negotiate_rsp {
+ struct smb_hdr hdr; /* wct = 17 */
+ __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
+ __u8 SecurityMode;
+ __le16 MaxMpxCount;
+ __le16 MaxNumberVcs;
+ __le32 MaxBufferSize;
+ __le32 MaxRawSize;
+ __le32 SessionKey;
+ __le32 Capabilities; /* see below */
+ __le32 SystemTimeLow;
+ __le32 SystemTimeHigh;
+ __le16 ServerTimeZone;
+ __u8 EncryptionKeyLength;
+ __u16 ByteCount;
+ union {
+ unsigned char EncryptionKey[1]; /* cap extended security off */
+ /* followed by Domain name - if extended security is off */
+ /* followed by 16 bytes of server GUID */
+ /* then security blob if cap_extended_security negotiated */
+ struct {
+ unsigned char GUID[16];
+ unsigned char SecurityBlob[1];
+ } __attribute__((packed)) extended_response;
+ } __attribute__((packed)) u;
+} __attribute__((packed)) NEGOTIATE_RSP;
+
+/* SecurityMode bits */
+#define SECMODE_USER 0x01 /* off indicates share level security */
+#define SECMODE_PW_ENCRYPT 0x02
+#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */
+#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */
+
+/* Negotiate response Capabilities */
+#define CAP_RAW_MODE 0x00000001
+#define CAP_MPX_MODE 0x00000002
+#define CAP_UNICODE 0x00000004
+#define CAP_LARGE_FILES 0x00000008
+#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */
+#define CAP_RPC_REMOTE_APIS 0x00000020
+#define CAP_STATUS32 0x00000040
+#define CAP_LEVEL_II_OPLOCKS 0x00000080
+#define CAP_LOCK_AND_READ 0x00000100
+#define CAP_NT_FIND 0x00000200
+#define CAP_DFS 0x00001000
+#define CAP_INFOLEVEL_PASSTHRU 0x00002000
+#define CAP_LARGE_READ_X 0x00004000
+#define CAP_LARGE_WRITE_X 0x00008000
+#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */
+#define CAP_UNIX 0x00800000
+#define CAP_COMPRESSED_DATA 0x02000000
+#define CAP_DYNAMIC_REAUTH 0x20000000
+#define CAP_PERSISTENT_HANDLES 0x40000000
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+typedef union smb_com_session_setup_andx {
+ struct { /* request format */
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 MaxBufferSize;
+ __le16 MaxMpxCount;
+ __le16 VcNumber;
+ __u32 SessionKey;
+ __le16 SecurityBlobLength;
+ __u32 Reserved;
+ __le32 Capabilities; /* see below */
+ __le16 ByteCount;
+ unsigned char SecurityBlob[1]; /* followed by */
+ /* STRING NativeOS */
+ /* STRING NativeLanMan */
+ } __attribute__((packed)) req; /* NTLM request format (with
+ extended security */
+
+ struct { /* request format */
+ struct smb_hdr hdr; /* wct = 13 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 MaxBufferSize;
+ __le16 MaxMpxCount;
+ __le16 VcNumber;
+ __u32 SessionKey;
+ __le16 CaseInsensitivePasswordLength; /* ASCII password len */
+ __le16 CaseSensitivePasswordLength; /* Unicode password length*/
+ __u32 Reserved; /* see below */
+ __le32 Capabilities;
+ __le16 ByteCount;
+ unsigned char CaseInsensitivePassword[1]; /* followed by: */
+ /* unsigned char * CaseSensitivePassword; */
+ /* STRING AccountName */
+ /* STRING PrimaryDomain */
+ /* STRING NativeOS */
+ /* STRING NativeLanMan */
+ } __attribute__((packed)) req_no_secext; /* NTLM request format (without
+ extended security */
+
+ struct { /* default (NTLM) response format */
+ struct smb_hdr hdr; /* wct = 4 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Action; /* see below */
+ __le16 SecurityBlobLength;
+ __u16 ByteCount;
+ unsigned char SecurityBlob[1]; /* followed by */
+/* unsigned char * NativeOS; */
+/* unsigned char * NativeLanMan; */
+/* unsigned char * PrimaryDomain; */
+ } __attribute__((packed)) resp; /* NTLM response
+ (with or without extended sec) */
+
+ struct { /* request format */
+ struct smb_hdr hdr; /* wct = 10 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 MaxBufferSize;
+ __le16 MaxMpxCount;
+ __le16 VcNumber;
+ __u32 SessionKey;
+ __le16 PasswordLength;
+ __u32 Reserved; /* encrypt key len and offset */
+ __le16 ByteCount;
+ unsigned char AccountPassword[1]; /* followed by */
+ /* STRING AccountName */
+ /* STRING PrimaryDomain */
+ /* STRING NativeOS */
+ /* STRING NativeLanMan */
+ } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */
+
+ struct { /* default (NTLM) response format */
+ struct smb_hdr hdr; /* wct = 3 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Action; /* see below */
+ __u16 ByteCount;
+ unsigned char NativeOS[1]; /* followed by */
+/* unsigned char * NativeLanMan; */
+/* unsigned char * PrimaryDomain; */
+ } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */
+} __attribute__((packed)) SESSION_SETUP_ANDX;
+
+/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */
+
+#define NTLMSSP_SERVER_TYPE 1
+#define NTLMSSP_DOMAIN_TYPE 2
+#define NTLMSSP_FQ_DOMAIN_TYPE 3
+#define NTLMSSP_DNS_DOMAIN_TYPE 4
+#define NTLMSSP_DNS_PARENT_TYPE 5
+
+struct ntlmssp2_name {
+ __le16 type;
+ __le16 length;
+/* char name[length]; */
+} __attribute__((packed));
+
+struct ntlmv2_resp {
+ char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+ __le32 blob_signature;
+ __u32 reserved;
+ __le64 time;
+ __u64 client_chal; /* random */
+ __u32 reserved2;
+ struct ntlmssp2_name names[2];
+ /* array of name entries could follow ending in minimum 4 byte struct */
+} __attribute__((packed));
+
+
+#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
+
+/* Capabilities bits (for NTLM SessSetup request) */
+#define CAP_UNICODE 0x00000004
+#define CAP_LARGE_FILES 0x00000008
+#define CAP_NT_SMBS 0x00000010
+#define CAP_STATUS32 0x00000040
+#define CAP_LEVEL_II_OPLOCKS 0x00000080
+#define CAP_NT_FIND 0x00000200 /* reserved should be zero
+ (because NT_SMBs implies the same thing?) */
+#define CAP_BULK_TRANSFER 0x20000000
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/* Action bits */
+#define GUEST_LOGIN 1
+
+typedef struct smb_com_tconx_req {
+ struct smb_hdr hdr; /* wct = 4 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Flags; /* see below */
+ __le16 PasswordLength;
+ __le16 ByteCount;
+ unsigned char Password[1]; /* followed by */
+/* STRING Path *//* \\server\share name */
+ /* STRING Service */
+} __attribute__((packed)) TCONX_REQ;
+
+typedef struct smb_com_tconx_rsp {
+ struct smb_hdr hdr; /* wct = 3 , not extended response */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 OptionalSupport; /* see below */
+ __u16 ByteCount;
+ unsigned char Service[1]; /* always ASCII, not Unicode */
+ /* STRING NativeFileSystem */
+} __attribute__((packed)) TCONX_RSP;
+
+typedef struct smb_com_tconx_rsp_ext {
+ struct smb_hdr hdr; /* wct = 7, extended response */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 OptionalSupport; /* see below */
+ __le32 MaximalShareAccessRights;
+ __le32 GuestMaximalShareAccessRights;
+ __u16 ByteCount;
+ unsigned char Service[1]; /* always ASCII, not Unicode */
+ /* STRING NativeFileSystem */
+} __attribute__((packed)) TCONX_RSP_EXT;
+
+
+/* tree connect Flags */
+#define DISCONNECT_TID 0x0001
+#define TCON_EXTENDED_SIGNATURES 0x0004
+#define TCON_EXTENDED_SECINFO 0x0008
+
+/* OptionalSupport bits */
+#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits
+ (exclusive searches supported) */
+#define SMB_SHARE_IS_IN_DFS 0x0002
+#define SMB_CSC_MASK 0x000C
+/* CSC flags defined as follows */
+#define SMB_CSC_CACHE_MANUAL_REINT 0x0000
+#define SMB_CSC_CACHE_AUTO_REINT 0x0004
+#define SMB_CSC_CACHE_VDO 0x0008
+#define SMB_CSC_NO_CACHING 0x000C
+#define SMB_UNIQUE_FILE_NAME 0x0010
+#define SMB_EXTENDED_SIGNATURES 0x0020
+
+/* services
+ *
+ * A: ie disk
+ * LPT1: ie printer
+ * IPC ie named pipe
+ * COMM
+ * ????? ie any type
+ *
+ */
+
+typedef struct smb_com_logoff_andx_req {
+ struct smb_hdr hdr; /* wct = 2 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __u16 AndXOffset;
+ __u16 ByteCount;
+} __attribute__((packed)) LOGOFF_ANDX_REQ;
+
+typedef struct smb_com_logoff_andx_rsp {
+ struct smb_hdr hdr; /* wct = 2 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __u16 AndXOffset;
+ __u16 ByteCount;
+} __attribute__((packed)) LOGOFF_ANDX_RSP;
+
+typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on
+ tree_connect PDU to effect disconnect */
+ /* tdis is probably simplest SMB PDU */
+ struct {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bcc = 0 */
+ } __attribute__((packed)) req;
+ struct {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bcc = 0 */
+ } __attribute__((packed)) resp;
+} __attribute__((packed)) TREE_DISCONNECT;
+
+typedef struct smb_com_close_req {
+ struct smb_hdr hdr; /* wct = 3 */
+ __u16 FileID;
+ __u32 LastWriteTime; /* should be zero or -1 */
+ __u16 ByteCount; /* 0 */
+} __attribute__((packed)) CLOSE_REQ;
+
+typedef struct smb_com_close_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) CLOSE_RSP;
+
+typedef struct smb_com_findclose_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __u16 FileID;
+ __u16 ByteCount; /* 0 */
+} __attribute__((packed)) FINDCLOSE_REQ;
+
+/* OpenFlags */
+#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */
+#define REQ_OPLOCK 0x00000002
+#define REQ_BATCHOPLOCK 0x00000004
+#define REQ_OPENDIRONLY 0x00000008
+#define REQ_EXTENDED_INFO 0x00000010
+
+/* File type */
+#define DISK_TYPE 0x0000
+#define BYTE_PIPE_TYPE 0x0001
+#define MESSAGE_PIPE_TYPE 0x0002
+#define PRINTER_TYPE 0x0003
+#define COMM_DEV_TYPE 0x0004
+#define UNKNOWN_TYPE 0xFFFF
+
+/* Device Type or File Status Flags */
+#define NO_EAS 0x0001
+#define NO_SUBSTREAMS 0x0002
+#define NO_REPARSETAG 0x0004
+/* following flags can apply if pipe */
+#define ICOUNT_MASK 0x00FF
+#define PIPE_READ_MODE 0x0100
+#define NAMED_PIPE_TYPE 0x0400
+#define PIPE_END_POINT 0x4000
+#define BLOCKING_NAMED_PIPE 0x8000
+
+typedef struct smb_com_open_req { /* also handles create */
+ struct smb_hdr hdr; /* wct = 24 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u8 Reserved; /* Must Be Zero */
+ __le16 NameLength;
+ __le32 OpenFlags;
+ __u32 RootDirectoryFid;
+ __le32 DesiredAccess;
+ __le64 AllocationSize;
+ __le32 FileAttributes;
+ __le32 ShareAccess;
+ __le32 CreateDisposition;
+ __le32 CreateOptions;
+ __le32 ImpersonationLevel;
+ __u8 SecurityFlags;
+ __le16 ByteCount;
+ char fileName[1];
+} __attribute__((packed)) OPEN_REQ;
+
+/* open response: oplock levels */
+#define OPLOCK_NONE 0
+#define OPLOCK_EXCLUSIVE 1
+#define OPLOCK_BATCH 2
+#define OPLOCK_READ 3 /* level 2 oplock */
+
+/* open response for CreateAction shifted left */
+#define CIFS_CREATE_ACTION 0x20000 /* file created */
+
+typedef struct smb_com_open_rsp {
+ struct smb_hdr hdr; /* wct = 34 BB */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u8 OplockLevel;
+ __u16 Fid;
+ __le32 CreateAction;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 FileAttributes;
+ __le64 AllocationSize;
+ __le64 EndOfFile;
+ __le16 FileType;
+ __le16 DeviceState;
+ __u8 DirectoryFlag;
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) OPEN_RSP;
+
+typedef struct smb_com_open_rsp_ext {
+ struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u8 OplockLevel;
+ __u16 Fid;
+ __le32 CreateAction;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 FileAttributes;
+ __le64 AllocationSize;
+ __le64 EndOfFile;
+ __le16 FileType;
+ __le16 DeviceState;
+ __u8 DirectoryFlag;
+ __u8 VolumeGUID[16];
+ __u64 FileId; /* note no endian conversion - is opaque UniqueID */
+ __le32 MaximalAccessRights;
+ __le32 GuestMaximalAccessRights;
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) OPEN_RSP_EXT;
+
+
+/* format of legacy open request */
+typedef struct smb_com_openx_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 OpenFlags;
+ __le16 Mode;
+ __le16 Sattr; /* search attributes */
+ __le16 FileAttributes; /* dos attrs */
+ __le32 CreateTime; /* os2 format */
+ __le16 OpenFunction;
+ __le32 EndOfFile;
+ __le32 Timeout;
+ __le32 Reserved;
+ __le16 ByteCount; /* file name follows */
+ char fileName[1];
+} __attribute__((packed)) OPENX_REQ;
+
+typedef struct smb_com_openx_rsp {
+ struct smb_hdr hdr; /* wct = 15 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le16 FileAttributes;
+ __le32 LastWriteTime; /* os2 format */
+ __le32 EndOfFile;
+ __le16 Access;
+ __le16 FileType;
+ __le16 IPCState;
+ __le16 Action;
+ __u32 FileId;
+ __u16 Reserved;
+ __u16 ByteCount;
+} __attribute__((packed)) OPENX_RSP;
+
+/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */
+
+/* Legacy write request for older servers */
+typedef struct smb_com_writex_req {
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __u32 Reserved; /* Timeout */
+ __le16 WriteMode; /* 1 = write through */
+ __le16 Remaining;
+ __le16 Reserved2;
+ __le16 DataLengthLow;
+ __le16 DataOffset;
+ __le16 ByteCount;
+ __u8 Pad; /* BB check for whether padded to DWORD
+ boundary and optimum performance here */
+ char Data[0];
+} __attribute__((packed)) WRITEX_REQ;
+
+typedef struct smb_com_write_req {
+ struct smb_hdr hdr; /* wct = 14 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __u32 Reserved;
+ __le16 WriteMode;
+ __le16 Remaining;
+ __le16 DataLengthHigh;
+ __le16 DataLengthLow;
+ __le16 DataOffset;
+ __le32 OffsetHigh;
+ __le16 ByteCount;
+ __u8 Pad; /* BB check for whether padded to DWORD
+ boundary and optimum performance here */
+ char Data[0];
+} __attribute__((packed)) WRITE_REQ;
+
+typedef struct smb_com_write_rsp {
+ struct smb_hdr hdr; /* wct = 6 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Count;
+ __le16 Remaining;
+ __le16 CountHigh;
+ __u16 Reserved;
+ __u16 ByteCount;
+} __attribute__((packed)) WRITE_RSP;
+
+/* legacy read request for older servers */
+typedef struct smb_com_readx_req {
+ struct smb_hdr hdr; /* wct = 10 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __le16 MaxCount;
+ __le16 MinCount; /* obsolete */
+ __le32 Reserved;
+ __le16 Remaining;
+ __le16 ByteCount;
+} __attribute__((packed)) READX_REQ;
+
+typedef struct smb_com_read_req {
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __le32 OffsetLow;
+ __le16 MaxCount;
+ __le16 MinCount; /* obsolete */
+ __le32 MaxCountHigh;
+ __le16 Remaining;
+ __le32 OffsetHigh;
+ __le16 ByteCount;
+} __attribute__((packed)) READ_REQ;
+
+typedef struct smb_com_read_rsp {
+ struct smb_hdr hdr; /* wct = 12 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __le16 Remaining;
+ __le16 DataCompactionMode;
+ __le16 Reserved;
+ __le16 DataLength;
+ __le16 DataOffset;
+ __le16 DataLengthHigh;
+ __u64 Reserved2;
+ __u16 ByteCount;
+ __u8 Pad; /* BB check for whether padded to DWORD
+ boundary and optimum performance here */
+ char Data[1];
+} __attribute__((packed)) READ_RSP;
+
+typedef struct locking_andx_range {
+ __le16 Pid;
+ __le16 Pad;
+ __le32 OffsetHigh;
+ __le32 OffsetLow;
+ __le32 LengthHigh;
+ __le32 LengthLow;
+} __attribute__((packed)) LOCKING_ANDX_RANGE;
+
+#define LOCKING_ANDX_SHARED_LOCK 0x01
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x02
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04
+#define LOCKING_ANDX_CANCEL_LOCK 0x08
+#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */
+
+typedef struct smb_com_lock_req {
+ struct smb_hdr hdr; /* wct = 8 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 Fid;
+ __u8 LockType;
+ __u8 OplockLevel;
+ __le32 Timeout;
+ __le16 NumberOfUnlocks;
+ __le16 NumberOfLocks;
+ __le16 ByteCount;
+ LOCKING_ANDX_RANGE Locks[1];
+} __attribute__((packed)) LOCK_REQ;
+
+/* lock type */
+#define CIFS_RDLCK 0
+#define CIFS_WRLCK 1
+#define CIFS_UNLCK 2
+typedef struct cifs_posix_lock {
+ __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */
+ __le16 lock_flags; /* 1 = Wait (only valid for setlock) */
+ __le32 pid;
+ __le64 start;
+ __le64 length;
+ /* BB what about additional owner info to identify network client */
+} __attribute__((packed)) CIFS_POSIX_LOCK;
+
+typedef struct smb_com_lock_rsp {
+ struct smb_hdr hdr; /* wct = 2 */
+ __u8 AndXCommand;
+ __u8 AndXReserved;
+ __le16 AndXOffset;
+ __u16 ByteCount;
+} __attribute__((packed)) LOCK_RSP;
+
+typedef struct smb_com_rename_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __le16 SearchAttributes; /* target file attributes */
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII or Unicode */
+ unsigned char OldFileName[1];
+ /* followed by __u8 BufferFormat2 */
+ /* followed by NewFileName */
+} __attribute__((packed)) RENAME_REQ;
+
+ /* copy request flags */
+#define COPY_MUST_BE_FILE 0x0001
+#define COPY_MUST_BE_DIR 0x0002
+#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */
+#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */
+#define COPY_VERIFY_WRITES 0x0010
+#define COPY_TREE 0x0020
+
+typedef struct smb_com_copy_req {
+ struct smb_hdr hdr; /* wct = 3 */
+ __u16 Tid2;
+ __le16 OpenFunction;
+ __le16 Flags;
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII or Unicode */
+ unsigned char OldFileName[1];
+ /* followed by __u8 BufferFormat2 */
+ /* followed by NewFileName string */
+} __attribute__((packed)) COPY_REQ;
+
+typedef struct smb_com_copy_rsp {
+ struct smb_hdr hdr; /* wct = 1 */
+ __le16 CopyCount; /* number of files copied */
+ __u16 ByteCount; /* may be zero */
+ __u8 BufferFormat; /* 0x04 - only present if errored file follows */
+ unsigned char ErrorFileName[1]; /* only present if error in copy */
+} __attribute__((packed)) COPY_RSP;
+
+#define CREATE_HARD_LINK 0x103
+#define MOVEFILE_COPY_ALLOWED 0x0002
+#define MOVEFILE_REPLACE_EXISTING 0x0001
+
+typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */
+ struct smb_hdr hdr; /* wct = 4 */
+ __le16 SearchAttributes; /* target file attributes */
+ __le16 Flags; /* spec says Information Level */
+ __le32 ClusterCount;
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII or Unicode */
+ unsigned char OldFileName[1];
+ /* followed by __u8 BufferFormat2 */
+ /* followed by NewFileName */
+} __attribute__((packed)) NT_RENAME_REQ;
+
+typedef struct smb_com_rename_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) RENAME_RSP;
+
+typedef struct smb_com_delete_file_req {
+ struct smb_hdr hdr; /* wct = 1 */
+ __le16 SearchAttributes;
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char fileName[1];
+} __attribute__((packed)) DELETE_FILE_REQ;
+
+typedef struct smb_com_delete_file_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) DELETE_FILE_RSP;
+
+typedef struct smb_com_delete_directory_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char DirName[1];
+} __attribute__((packed)) DELETE_DIRECTORY_REQ;
+
+typedef struct smb_com_delete_directory_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) DELETE_DIRECTORY_RSP;
+
+typedef struct smb_com_create_directory_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char DirName[1];
+} __attribute__((packed)) CREATE_DIRECTORY_REQ;
+
+typedef struct smb_com_create_directory_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) CREATE_DIRECTORY_RSP;
+
+typedef struct smb_com_query_information_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount; /* 1 + namelen + 1 */
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char FileName[1];
+} __attribute__((packed)) QUERY_INFORMATION_REQ;
+
+typedef struct smb_com_query_information_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ __le16 attr;
+ __le32 last_write_time;
+ __le32 size;
+ __u16 reserved[5];
+ __le16 ByteCount; /* bcc = 0 */
+} __attribute__((packed)) QUERY_INFORMATION_RSP;
+
+typedef struct smb_com_setattr_req {
+ struct smb_hdr hdr; /* wct = 8 */
+ __le16 attr;
+ __le16 time_low;
+ __le16 time_high;
+ __le16 reserved[5]; /* must be zero */
+ __u16 ByteCount;
+ __u8 BufferFormat; /* 4 = ASCII */
+ unsigned char fileName[1];
+} __attribute__((packed)) SETATTR_REQ;
+
+typedef struct smb_com_setattr_rsp {
+ struct smb_hdr hdr; /* wct = 0 */
+ __u16 ByteCount; /* bct = 0 */
+} __attribute__((packed)) SETATTR_RSP;
+
+/* empty wct response to setattr */
+
+/*******************************************************/
+/* NT Transact structure defintions follow */
+/* Currently only ioctl, acl (get security descriptor) */
+/* and notify are implemented */
+/*******************************************************/
+typedef struct smb_com_ntransact_req {
+ struct smb_hdr hdr; /* wct >= 19 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 2 = IOCTL/FSCTL */
+ /* SetupCount words follow then */
+ __le16 ByteCount;
+ __u8 Pad[3];
+ __u8 Parms[0];
+} __attribute__((packed)) NTRANSACT_REQ;
+
+typedef struct smb_com_ntransact_rsp {
+ struct smb_hdr hdr; /* wct = 18 */
+ __u8 Reserved[3];
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 ParameterDisplacement;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __le32 DataDisplacement;
+ __u8 SetupCount; /* 0 */
+ __u16 ByteCount;
+ /* __u8 Pad[3]; */
+ /* parms and data follow */
+} __attribute__((packed)) NTRANSACT_RSP;
+
+typedef struct smb_com_transaction_ioctl_req {
+ struct smb_hdr hdr; /* wct = 23 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 2 = IOCTL/FSCTL */
+ __le32 FunctionCode;
+ __u16 Fid;
+ __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */
+ __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */
+ __le16 ByteCount;
+ __u8 Pad[3];
+ __u8 Data[1];
+} __attribute__((packed)) TRANSACT_IOCTL_REQ;
+
+typedef struct smb_com_transaction_ioctl_rsp {
+ struct smb_hdr hdr; /* wct = 19 */
+ __u8 Reserved[3];
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 ParameterDisplacement;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __le32 DataDisplacement;
+ __u8 SetupCount; /* 1 */
+ __le16 ReturnedDataLen;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACT_IOCTL_RSP;
+
+#define CIFS_ACL_OWNER 1
+#define CIFS_ACL_GROUP 2
+#define CIFS_ACL_DACL 4
+#define CIFS_ACL_SACL 8
+
+typedef struct smb_com_transaction_qsec_req {
+ struct smb_hdr hdr; /* wct = 19 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* no setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */
+ __le16 ByteCount; /* bcc = 3 + 8 */
+ __u8 Pad[3];
+ __u16 Fid;
+ __u16 Reserved2;
+ __le32 AclFlags;
+} __attribute__((packed)) QUERY_SEC_DESC_REQ;
+
+
+typedef struct smb_com_transaction_ssec_req {
+ struct smb_hdr hdr; /* wct = 19 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* no setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand; /* 3 = SET_SECURITY_DESC */
+ __le16 ByteCount; /* bcc = 3 + 8 */
+ __u8 Pad[3];
+ __u16 Fid;
+ __u16 Reserved2;
+ __le32 AclFlags;
+} __attribute__((packed)) SET_SEC_DESC_REQ;
+
+typedef struct smb_com_transaction_change_notify_req {
+ struct smb_hdr hdr; /* wct = 23 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 MaxParameterCount;
+ __le32 MaxDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __le16 SubCommand;/* 4 = Change Notify */
+ __le32 CompletionFilter; /* operation to monitor */
+ __u16 Fid;
+ __u8 WatchTree; /* 1 = Monitor subdirectories */
+ __u8 Reserved2;
+ __le16 ByteCount;
+/* __u8 Pad[3];*/
+/* __u8 Data[1];*/
+} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ;
+
+/* BB eventually change to use generic ntransact rsp struct
+ and validation routine */
+typedef struct smb_com_transaction_change_notify_rsp {
+ struct smb_hdr hdr; /* wct = 18 */
+ __u8 Reserved[3];
+ __le32 TotalParameterCount;
+ __le32 TotalDataCount;
+ __le32 ParameterCount;
+ __le32 ParameterOffset;
+ __le32 ParameterDisplacement;
+ __le32 DataCount;
+ __le32 DataOffset;
+ __le32 DataDisplacement;
+ __u8 SetupCount; /* 0 */
+ __u16 ByteCount;
+ /* __u8 Pad[3]; */
+} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP;
+/* Completion Filter flags for Notify */
+#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
+#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
+#define FILE_NOTIFY_CHANGE_NAME 0x00000003
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
+#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
+#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
+#define FILE_NOTIFY_CHANGE_EA 0x00000080
+#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
+#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
+#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
+#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+
+#define FILE_ACTION_ADDED 0x00000001
+#define FILE_ACTION_REMOVED 0x00000002
+#define FILE_ACTION_MODIFIED 0x00000003
+#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004
+#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005
+#define FILE_ACTION_ADDED_STREAM 0x00000006
+#define FILE_ACTION_REMOVED_STREAM 0x00000007
+#define FILE_ACTION_MODIFIED_STREAM 0x00000008
+
+/* response contains array of the following structures */
+struct file_notify_information {
+ __le32 NextEntryOffset;
+ __le32 Action;
+ __le32 FileNameLength;
+ __u8 FileName[0];
+} __attribute__((packed));
+
+struct reparse_data {
+ __u32 ReparseTag;
+ __u16 ReparseDataLength;
+ __u16 Reserved;
+ __u16 AltNameOffset;
+ __u16 AltNameLen;
+ __u16 TargetNameOffset;
+ __u16 TargetNameLen;
+ char LinkNamesBuf[1];
+} __attribute__((packed));
+
+struct cifs_quota_data {
+ __u32 rsrvd1; /* 0 */
+ __u32 sid_size;
+ __u64 rsrvd2; /* 0 */
+ __u64 space_used;
+ __u64 soft_limit;
+ __u64 hard_limit;
+ char sid[1]; /* variable size? */
+} __attribute__((packed));
+
+/* quota sub commands */
+#define QUOTA_LIST_CONTINUE 0
+#define QUOTA_LIST_START 0x100
+#define QUOTA_FOR_SID 0x101
+
+struct trans2_req {
+ /* struct smb_hdr hdr precedes. Set wct = 14+ */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* 1st setup word - SetupCount words follow */
+ __le16 ByteCount;
+} __attribute__((packed));
+
+struct smb_t2_req {
+ struct smb_hdr hdr;
+ struct trans2_req t2_req;
+} __attribute__((packed));
+
+struct trans2_resp {
+ /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __u16 Reserved;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 ParameterDisplacement;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __le16 DataDisplacement;
+ __u8 SetupCount;
+ __u8 Reserved1;
+ /* SetupWords[SetupCount];
+ __u16 ByteCount;
+ __u16 Reserved2;*/
+ /* data area follows */
+} __attribute__((packed));
+
+struct smb_t2_rsp {
+ struct smb_hdr hdr;
+ struct trans2_resp t2_rsp;
+} __attribute__((packed));
+
+/* PathInfo/FileInfo infolevels */
+#define SMB_INFO_STANDARD 1
+#define SMB_SET_FILE_EA 2
+#define SMB_QUERY_FILE_EA_SIZE 2
+#define SMB_INFO_QUERY_EAS_FROM_LIST 3
+#define SMB_INFO_QUERY_ALL_EAS 4
+#define SMB_INFO_IS_NAME_VALID 6
+#define SMB_QUERY_FILE_BASIC_INFO 0x101
+#define SMB_QUERY_FILE_STANDARD_INFO 0x102
+#define SMB_QUERY_FILE_EA_INFO 0x103
+#define SMB_QUERY_FILE_NAME_INFO 0x104
+#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105
+#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106
+#define SMB_QUERY_FILE_ALL_INFO 0x107
+#define SMB_QUERY_ALT_NAME_INFO 0x108
+#define SMB_QUERY_FILE_STREAM_INFO 0x109
+#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B
+#define SMB_QUERY_FILE_UNIX_BASIC 0x200
+#define SMB_QUERY_FILE_UNIX_LINK 0x201
+#define SMB_QUERY_POSIX_ACL 0x204
+#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */
+#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */
+#define SMB_QUERY_POSIX_PERMISSION 0x207
+#define SMB_QUERY_POSIX_LOCK 0x208
+/* #define SMB_POSIX_OPEN 0x209 */
+/* #define SMB_POSIX_UNLINK 0x20a */
+#define SMB_QUERY_FILE__UNIX_INFO2 0x20b
+#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee
+#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0
+#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */
+#define SMB_QUERY_FILE_POSITION_INFO 0x3f6
+#define SMB_QUERY_FILE_MODE_INFO 0x3f8
+#define SMB_QUERY_FILE_ALGN_INFO 0x3f9
+
+
+#define SMB_SET_FILE_BASIC_INFO 0x101
+#define SMB_SET_FILE_DISPOSITION_INFO 0x102
+#define SMB_SET_FILE_ALLOCATION_INFO 0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO 0x104
+#define SMB_SET_FILE_UNIX_BASIC 0x200
+#define SMB_SET_FILE_UNIX_LINK 0x201
+#define SMB_SET_FILE_UNIX_HLINK 0x203
+#define SMB_SET_POSIX_ACL 0x204
+#define SMB_SET_XATTR 0x205
+#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */
+#define SMB_SET_POSIX_LOCK 0x208
+#define SMB_POSIX_OPEN 0x209
+#define SMB_POSIX_UNLINK 0x20a
+#define SMB_SET_FILE_UNIX_INFO2 0x20b
+#define SMB_SET_FILE_BASIC_INFO2 0x3ec
+#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */
+#define SMB_FILE_ALL_INFO2 0x3fa
+#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb
+#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc
+#define SMB_FILE_MOVE_CLUSTER_INFO 0x407
+#define SMB_FILE_QUOTA_INFO 0x408
+#define SMB_FILE_REPARSEPOINT_INFO 0x409
+#define SMB_FILE_MAXIMUM_INFO 0x40d
+
+/* Find File infolevels */
+#define SMB_FIND_FILE_INFO_STANDARD 0x001
+#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002
+#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003
+#define SMB_FIND_FILE_DIRECTORY_INFO 0x101
+#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_FILE_NAMES_INFO 0x103
+#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105
+#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106
+#define SMB_FIND_FILE_UNIX 0x202
+
+typedef struct smb_com_transaction2_qpi_req {
+ struct smb_hdr hdr; /* wct = 14+ */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __le16 InformationLevel;
+ __u32 Reserved4;
+ char FileName[1];
+} __attribute__((packed)) TRANSACTION2_QPI_REQ;
+
+typedef struct smb_com_transaction2_qpi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word is present for infolevels > 100 */
+} __attribute__((packed)) TRANSACTION2_QPI_RSP;
+
+typedef struct smb_com_transaction2_spi_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 Pad1;
+ __le16 InformationLevel;
+ __u32 Reserved4;
+ char FileName[1];
+} __attribute__((packed)) TRANSACTION2_SPI_REQ;
+
+typedef struct smb_com_transaction2_spi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word is present for infolevels > 100 */
+} __attribute__((packed)) TRANSACTION2_SPI_RSP;
+
+struct set_file_rename {
+ __le32 overwrite; /* 1 = overwrite dest */
+ __u32 root_fid; /* zero */
+ __le32 target_name_len;
+ char target_name[0]; /* Must be unicode */
+} __attribute__((packed));
+
+struct smb_com_transaction2_sfi_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 Pad1;
+ __u16 Fid;
+ __le16 InformationLevel;
+ __u16 Reserved4;
+} __attribute__((packed));
+
+struct smb_com_transaction2_sfi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word reserved -
+ present for infolevels > 100 */
+} __attribute__((packed));
+
+struct smb_t2_qfi_req {
+ struct smb_hdr hdr;
+ struct trans2_req t2;
+ __u8 Pad;
+ __u16 Fid;
+ __le16 InformationLevel;
+} __attribute__((packed));
+
+struct smb_t2_qfi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u16 Reserved2; /* parameter word reserved -
+ present for infolevels > 100 */
+} __attribute__((packed));
+
+/*
+ * Flags on T2 FINDFIRST and FINDNEXT
+ */
+#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001
+#define CIFS_SEARCH_CLOSE_AT_END 0x0002
+#define CIFS_SEARCH_RETURN_RESUME 0x0004
+#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008
+#define CIFS_SEARCH_BACKUP_SEARCH 0x0010
+
+/*
+ * Size of the resume key on FINDFIRST and FINDNEXT calls
+ */
+#define CIFS_SMB_RESUME_KEY_SIZE 4
+
+typedef struct smb_com_transaction2_ffirst_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_FIND_FIRST */
+ __le16 ByteCount;
+ __u8 Pad;
+ __le16 SearchAttributes;
+ __le16 SearchCount;
+ __le16 SearchFlags;
+ __le16 InformationLevel;
+ __le32 SearchStorageType;
+ char FileName[1];
+} __attribute__((packed)) TRANSACTION2_FFIRST_REQ;
+
+typedef struct smb_com_transaction2_ffirst_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_FFIRST_RSP;
+
+typedef struct smb_com_transaction2_ffirst_rsp_parms {
+ __u16 SearchHandle;
+ __le16 SearchCount;
+ __le16 EndofSearch;
+ __le16 EAErrorOffset;
+ __le16 LastNameOffset;
+} __attribute__((packed)) T2_FFIRST_RSP_PARMS;
+
+typedef struct smb_com_transaction2_fnext_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_FIND_NEXT */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 SearchHandle;
+ __le16 SearchCount;
+ __le16 InformationLevel;
+ __u32 ResumeKey;
+ __le16 SearchFlags;
+ char ResumeFileName[1];
+} __attribute__((packed)) TRANSACTION2_FNEXT_REQ;
+
+typedef struct smb_com_transaction2_fnext_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_FNEXT_RSP;
+
+typedef struct smb_com_transaction2_fnext_rsp_parms {
+ __le16 SearchCount;
+ __le16 EndofSearch;
+ __le16 EAErrorOffset;
+ __le16 LastNameOffset;
+} __attribute__((packed)) T2_FNEXT_RSP_PARMS;
+
+/* QFSInfo Levels */
+#define SMB_INFO_ALLOCATION 1
+#define SMB_INFO_VOLUME 2
+#define SMB_QUERY_FS_VOLUME_INFO 0x102
+#define SMB_QUERY_FS_SIZE_INFO 0x103
+#define SMB_QUERY_FS_DEVICE_INFO 0x104
+#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105
+#define SMB_QUERY_CIFS_UNIX_INFO 0x200
+#define SMB_QUERY_POSIX_FS_INFO 0x201
+#define SMB_QUERY_POSIX_WHO_AM_I 0x202
+#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203
+#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure
+ FILE_SYSTEM__UNIX_INFO to tell
+ whether new NTIOCTL available
+ (0xACE) for WAN friendly SMB
+ operations to be carried */
+#define SMB_QUERY_LABEL_INFO 0x3ea
+#define SMB_QUERY_FS_QUOTA_INFO 0x3ee
+#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef
+#define SMB_QUERY_OBJECTID_INFO 0x3f0
+
+typedef struct smb_com_transaction2_qfsi_req {
+ struct smb_hdr hdr; /* wct = 14+ */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad;
+ __le16 InformationLevel;
+} __attribute__((packed)) TRANSACTION2_QFSI_REQ;
+
+typedef struct smb_com_transaction_qfsi_rsp {
+ struct smb_hdr hdr; /* wct = 10 + SetupCount */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u8 Pad; /* may be three bytes? *//* followed by data area */
+} __attribute__((packed)) TRANSACTION2_QFSI_RSP;
+
+typedef struct whoami_rsp_data { /* Query level 0x202 */
+ __u32 flags; /* 0 = Authenticated user 1 = GUEST */
+ __u32 mask; /* which flags bits server understands ie 0x0001 */
+ __u64 unix_user_id;
+ __u64 unix_user_gid;
+ __u32 number_of_supplementary_gids; /* may be zero */
+ __u32 number_of_sids; /* may be zero */
+ __u32 length_of_sid_array; /* in bytes - may be zero */
+ __u32 pad; /* reserved - MBZ */
+ /* __u64 gid_array[0]; */ /* may be empty */
+ /* __u8 * psid_list */ /* may be empty */
+} __attribute__((packed)) WHOAMI_RSP_DATA;
+
+/* SETFSInfo Levels */
+#define SMB_SET_CIFS_UNIX_INFO 0x200
+typedef struct smb_com_transaction2_setfsi_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount; /* 4 */
+ __le16 ParameterOffset;
+ __le16 DataCount; /* 12 */
+ __le16 DataOffset;
+ __u8 SetupCount; /* one */
+ __u8 Reserved3;
+ __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */
+ __le16 ByteCount;
+ __u8 Pad;
+ __u16 FileNum; /* Parameters start. */
+ __le16 InformationLevel;/* Parameters end. */
+ __le16 ClientUnixMajor; /* Data start. */
+ __le16 ClientUnixMinor;
+ __le64 ClientUnixCap; /* Data end */
+} __attribute__((packed)) TRANSACTION2_SETFSI_REQ;
+
+typedef struct smb_com_transaction2_setfsi_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+} __attribute__((packed)) TRANSACTION2_SETFSI_RSP;
+
+
+typedef struct smb_com_transaction2_get_dfs_refer_req {
+ struct smb_hdr hdr; /* wct = 15 */
+ __le16 TotalParameterCount;
+ __le16 TotalDataCount;
+ __le16 MaxParameterCount;
+ __le16 MaxDataCount;
+ __u8 MaxSetupCount;
+ __u8 Reserved;
+ __le16 Flags;
+ __le32 Timeout;
+ __u16 Reserved2;
+ __le16 ParameterCount;
+ __le16 ParameterOffset;
+ __le16 DataCount;
+ __le16 DataOffset;
+ __u8 SetupCount;
+ __u8 Reserved3;
+ __le16 SubCommand; /* one setup word */
+ __le16 ByteCount;
+ __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length
+ perhaps?) followed by one byte pad - doesn't
+ seem to matter though */
+ __le16 MaxReferralLevel;
+ char RequestFileName[1];
+} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ;
+
+#define DFS_VERSION cpu_to_le16(0x0003)
+
+/* DFS server target type */
+#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */
+#define DFS_TYPE_ROOT 0x0001
+
+/* Referral Entry Flags */
+#define DFS_NAME_LIST_REF 0x0200
+
+typedef struct dfs_referral_level_3 {
+ __le16 VersionNumber;
+ __le16 Size;
+ __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */
+ __le16 ReferralEntryFlags; /* 0x0200 bit set only for domain
+ or DC referral responce */
+ __le32 TimeToLive;
+ __le16 DfsPathOffset;
+ __le16 DfsAlternatePathOffset;
+ __le16 NetworkAddressOffset; /* offset of the link target */
+ __le16 ServiceSiteGuid;
+} __attribute__((packed)) REFERRAL3;
+
+typedef struct smb_com_transaction_get_dfs_refer_rsp {
+ struct smb_hdr hdr; /* wct = 10 */
+ struct trans2_resp t2;
+ __u16 ByteCount;
+ __u8 Pad;
+ __le16 PathConsumed;
+ __le16 NumberOfReferrals;
+ __le16 DFSFlags;
+ __u16 Pad2;
+ REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
+ /* followed by the strings pointed to by the referral structures */
+} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_RSP;
+
+/* DFS Flags */
+#define DFSREF_REFERRAL_SERVER 0x0001
+#define DFSREF_STORAGE_SERVER 0x0002
+
+/* IOCTL information */
+/*
+ * List of ioctl function codes that look to be of interest to remote clients
+ * like this one. Need to do some experimentation to make sure they all work
+ * remotely. Some of the following, such as the encryption/compression ones
+ * would be invoked from tools via a specialized hook into the VFS rather
+ * than via the standard vfs entry points
+ */
+#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
+#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004
+#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008
+#define FSCTL_LOCK_VOLUME 0x00090018
+#define FSCTL_UNLOCK_VOLUME 0x0009001C
+#define FSCTL_GET_COMPRESSION 0x0009003C
+#define FSCTL_SET_COMPRESSION 0x0009C040
+#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C
+#define FSCTL_FILESYS_GET_STATISTICS 0x00090090
+#define FSCTL_SET_REPARSE_POINT 0x000900A4
+#define FSCTL_GET_REPARSE_POINT 0x000900A8
+#define FSCTL_DELETE_REPARSE_POINT 0x000900AC
+#define FSCTL_SET_SPARSE 0x000900C4
+#define FSCTL_SET_ZERO_DATA 0x000900C8
+#define FSCTL_SET_ENCRYPTION 0x000900D7
+#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB
+#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF
+#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3
+#define FSCTL_SIS_COPYFILE 0x00090100
+#define FSCTL_SIS_LINK_FILES 0x0009C104
+
+#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
+#define IO_REPARSE_TAG_HSM 0xC0000004
+#define IO_REPARSE_TAG_SIS 0x80000007
+
+/*
+ ************************************************************************
+ * All structs for everything above the SMB PDUs themselves
+ * (such as the T2 level specific data) go here
+ ************************************************************************
+ */
+
+/*
+ * Information on a server
+ */
+
+struct serverInfo {
+ char name[16];
+ unsigned char versionMajor;
+ unsigned char versionMinor;
+ unsigned long type;
+ unsigned int commentOffset;
+} __attribute__((packed));
+
+/*
+ * The following structure is the format of the data returned on a NetShareEnum
+ * with level "90" (x5A)
+ */
+
+struct shareInfo {
+ char shareName[13];
+ char pad;
+ unsigned short type;
+ unsigned int commentOffset;
+} __attribute__((packed));
+
+struct aliasInfo {
+ char aliasName[9];
+ char pad;
+ unsigned int commentOffset;
+ unsigned char type[2];
+} __attribute__((packed));
+
+struct aliasInfo92 {
+ int aliasNameOffset;
+ int serverNameOffset;
+ int shareNameOffset;
+} __attribute__((packed));
+
+typedef struct {
+ __le64 TotalAllocationUnits;
+ __le64 FreeAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */
+
+typedef struct {
+ __le32 fsid;
+ __le32 SectorsPerAllocationUnit;
+ __le32 TotalAllocationUnits;
+ __le32 FreeAllocationUnits;
+ __le16 BytesPerSector;
+} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO;
+
+typedef struct {
+ __le16 MajorVersionNumber;
+ __le16 MinorVersionNumber;
+ __le64 Capability;
+} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/
+
+/* Version numbers for CIFS UNIX major and minor. */
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
+/* Linux/Unix extensions capability flags */
+#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */
+#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */
+#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */
+#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */
+#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */
+#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based
+ calls including posix open
+ and posix unlink */
+#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up
+ to 0xFFFF00 */
+#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080
+#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */
+#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */
+#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and
+ QFS PROXY call */
+#ifdef CONFIG_CIFS_POSIX
+/* Can not set pathnames cap yet until we send new posix create SMB since
+ otherwise server can treat such handles opened with older ntcreatex
+ (by a new client which knows how to send posix path ops)
+ as non-posix handles (can affect write behavior with byte range locks.
+ We can add back in POSIX_PATH_OPS cap when Posix Create/Mkdir finished */
+/* #define CIFS_UNIX_CAP_MASK 0x000000fb */
+#define CIFS_UNIX_CAP_MASK 0x000000db
+#else
+#define CIFS_UNIX_CAP_MASK 0x00000013
+#endif /* CONFIG_CIFS_POSIX */
+
+
+#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */
+
+typedef struct {
+ /* For undefined recommended transfer size return -1 in that field */
+ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
+ __le32 BlockSize;
+ /* The next three fields are in terms of the block size.
+ (above). If block size is unknown, 4096 would be a
+ reasonable block size for a server to report.
+ Note that returning the blocks/blocksavail removes need
+ to make a second call (to QFSInfo level 0x103 to get this info.
+ UserBlockAvail is typically less than or equal to BlocksAvail,
+ if no distinction is made return the same value in each */
+ __le64 TotalBlocks;
+ __le64 BlocksAvail; /* bfree */
+ __le64 UserBlocksAvail; /* bavail */
+ /* For undefined Node fields or FSID return -1 */
+ __le64 TotalFileNodes;
+ __le64 FreeFileNodes;
+ __le64 FileSysIdentifier; /* fsid */
+ /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
+ /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
+} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO;
+
+/* DeviceType Flags */
+#define FILE_DEVICE_CD_ROM 0x00000002
+#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003
+#define FILE_DEVICE_DFS 0x00000006
+#define FILE_DEVICE_DISK 0x00000007
+#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008
+#define FILE_DEVICE_FILE_SYSTEM 0x00000009
+#define FILE_DEVICE_NAMED_PIPE 0x00000011
+#define FILE_DEVICE_NETWORK 0x00000012
+#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014
+#define FILE_DEVICE_NULL 0x00000015
+#define FILE_DEVICE_PARALLEL_PORT 0x00000016
+#define FILE_DEVICE_PRINTER 0x00000018
+#define FILE_DEVICE_SERIAL_PORT 0x0000001b
+#define FILE_DEVICE_STREAMS 0x0000001e
+#define FILE_DEVICE_TAPE 0x0000001f
+#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020
+#define FILE_DEVICE_VIRTUAL_DISK 0x00000024
+#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028
+
+typedef struct {
+ __le32 DeviceType;
+ __le32 DeviceCharacteristics;
+} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
+
+typedef struct {
+ __le32 Attributes;
+ __le32 MaxPathNameComponentLength;
+ __le32 FileSystemNameLen;
+ char FileSystemName[52]; /* do not have to save this - get subset? */
+} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
+
+/******************************************************************************/
+/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */
+/******************************************************************************/
+typedef struct { /* data block encoding of response to level 263 QPathInfo */
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 Attributes;
+ __u32 Pad1;
+ __le64 AllocationSize;
+ __le64 EndOfFile; /* size ie offset to first free byte in file */
+ __le32 NumberOfLinks; /* hard links */
+ __u8 DeletePending;
+ __u8 Directory;
+ __u16 Pad2;
+ __u64 IndexNumber;
+ __le32 EASize;
+ __le32 AccessFlags;
+ __u64 IndexNumber1;
+ __le64 CurrentByteOffset;
+ __le32 Mode;
+ __le32 AlignmentRequirement;
+ __le32 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */
+
+/* defines for enumerating possible values of the Unix type field below */
+#define UNIX_FILE 0
+#define UNIX_DIR 1
+#define UNIX_SYMLINK 2
+#define UNIX_CHARDEV 3
+#define UNIX_BLOCKDEV 4
+#define UNIX_FIFO 5
+#define UNIX_SOCKET 6
+typedef struct {
+ __le64 EndOfFile;
+ __le64 NumOfBytes;
+ __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */
+ __le64 LastAccessTime;
+ __le64 LastModificationTime;
+ __le64 Uid;
+ __le64 Gid;
+ __le32 Type;
+ __le64 DevMajor;
+ __le64 DevMinor;
+ __u64 UniqueId;
+ __le64 Permissions;
+ __le64 Nlinks;
+} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
+
+typedef struct {
+ char LinkDest[1];
+} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */
+
+/* The following three structures are needed only for
+ setting time to NT4 and some older servers via
+ the primitive DOS time format */
+typedef struct {
+ __u16 Day:5;
+ __u16 Month:4;
+ __u16 Year:7;
+} __attribute__((packed)) SMB_DATE;
+
+typedef struct {
+ __u16 TwoSeconds:5;
+ __u16 Minutes:6;
+ __u16 Hours:5;
+} __attribute__((packed)) SMB_TIME;
+
+typedef struct {
+ __le16 CreationDate; /* SMB Date see above */
+ __le16 CreationTime; /* SMB Time */
+ __le16 LastAccessDate;
+ __le16 LastAccessTime;
+ __le16 LastWriteDate;
+ __le16 LastWriteTime;
+ __le32 DataSize; /* File Size (EOF) */
+ __le32 AllocationSize;
+ __le16 Attributes; /* verify not u32 */
+ __le32 EASize;
+} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
+
+typedef struct {
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le32 Attributes;
+ __u32 Pad;
+} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */
+
+struct file_allocation_info {
+ __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
+} __attribute__((packed)); /* size used on disk, for level 0x103 for set,
+ 0x105 for query */
+
+struct file_end_of_file_info {
+ __le64 FileSize; /* offset to end of file */
+} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */
+
+struct file_alt_name_info {
+ __u8 alt_name[1];
+} __attribute__((packed)); /* level 0x0108 */
+
+struct file_stream_info {
+ __le32 number_of_streams; /* BB check sizes and verify location */
+ /* followed by info on streams themselves
+ u64 size;
+ u64 allocation_size
+ stream info */
+}; /* level 0x109 */
+
+struct file_compression_info {
+ __le64 compressed_size;
+ __le16 format;
+ __u8 unit_shift;
+ __u8 ch_shift;
+ __u8 cl_shift;
+ __u8 pad[3];
+} __attribute__((packed)); /* level 0x10b */
+
+/* POSIX ACL set/query path info structures */
+#define CIFS_ACL_VERSION 1
+struct cifs_posix_ace { /* access control entry (ACE) */
+ __u8 cifs_e_tag;
+ __u8 cifs_e_perm;
+ __le64 cifs_uid; /* or gid */
+} __attribute__((packed));
+
+struct cifs_posix_acl { /* access conrol list (ACL) */
+ __le16 version;
+ __le16 access_entry_count; /* access ACL - count of entries */
+ __le16 default_entry_count; /* default ACL - count of entries */
+ struct cifs_posix_ace ace_array[0];
+ /* followed by
+ struct cifs_posix_ace default_ace_arraay[] */
+} __attribute__((packed)); /* level 0x204 */
+
+/* types of access control entries already defined in posix_acl.h */
+/* #define CIFS_POSIX_ACL_USER_OBJ 0x01
+#define CIFS_POSIX_ACL_USER 0x02
+#define CIFS_POSIX_ACL_GROUP_OBJ 0x04
+#define CIFS_POSIX_ACL_GROUP 0x08
+#define CIFS_POSIX_ACL_MASK 0x10
+#define CIFS_POSIX_ACL_OTHER 0x20 */
+
+/* types of perms */
+/* #define CIFS_POSIX_ACL_EXECUTE 0x01
+#define CIFS_POSIX_ACL_WRITE 0x02
+#define CIFS_POSIX_ACL_READ 0x04 */
+
+/* end of POSIX ACL definitions */
+
+/* POSIX Open Flags */
+#define SMB_O_RDONLY 0x1
+#define SMB_O_WRONLY 0x2
+#define SMB_O_RDWR 0x4
+#define SMB_O_CREAT 0x10
+#define SMB_O_EXCL 0x20
+#define SMB_O_TRUNC 0x40
+#define SMB_O_APPEND 0x80
+#define SMB_O_SYNC 0x100
+#define SMB_O_DIRECTORY 0x200
+#define SMB_O_NOFOLLOW 0x400
+#define SMB_O_DIRECT 0x800
+
+typedef struct {
+ __le32 OpenFlags; /* same as NT CreateX */
+ __le32 PosixOpenFlags;
+ __le64 Permissions;
+ __le16 Level; /* reply level requested (see QPathInfo levels) */
+} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
+
+typedef struct {
+ __le16 OplockFlags;
+ __u16 Fid;
+ __le32 CreateAction;
+ __le16 ReturnedLevel;
+ __le16 Pad;
+ /* struct following varies based on requested level */
+} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
+
+#define SMB_POSIX_UNLINK_FILE_TARGET 0
+#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1
+
+struct unlink_psx_rq { /* level 0x20a SetPathInfo */
+ __le16 type;
+} __attribute__((packed));
+
+struct file_internal_info {
+ __u64 UniqueId; /* inode number */
+} __attribute__((packed)); /* level 0x3ee */
+
+struct file_mode_info {
+ __le32 Mode;
+} __attribute__((packed)); /* level 0x3f8 */
+
+struct file_attrib_tag {
+ __le32 Attribute;
+ __le32 ReparseTag;
+} __attribute__((packed)); /* level 0x40b */
+
+
+/********************************************************/
+/* FindFirst/FindNext transact2 data buffer formats */
+/********************************************************/
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 ResumeKey; /* as with FileIndex - no need to convert */
+ __le64 EndOfFile;
+ __le64 NumOfBytes;
+ __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */
+ __le64 LastAccessTime;
+ __le64 LastModificationTime;
+ __le64 Uid;
+ __le64 Gid;
+ __le32 Type;
+ __le64 DevMajor;
+ __le64 DevMinor;
+ __u64 UniqueId;
+ __le64 Permissions;
+ __le64 Nlinks;
+ char FileName[1];
+} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* length of the xattrs */
+ char FileName[1];
+} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* EA size */
+ __le32 Reserved;
+ __u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
+ char FileName[1];
+} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
+
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* length of the xattrs */
+ __u8 ShortNameLength;
+ __u8 Reserved;
+ __u8 ShortName[12];
+ char FileName[1];
+} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+
+typedef struct {
+ __u32 ResumeKey;
+ __le16 CreationDate; /* SMB Date */
+ __le16 CreationTime; /* SMB Time */
+ __le16 LastAccessDate;
+ __le16 LastAccessTime;
+ __le16 LastWriteDate;
+ __le16 LastWriteTime;
+ __le32 DataSize; /* File Size (EOF) */
+ __le32 AllocationSize;
+ __le16 Attributes; /* verify not u32 */
+ __u8 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
+
+
+struct win_dev {
+ unsigned char type[8]; /* IntxCHR or IntxBLK */
+ __le64 major;
+ __le64 minor;
+} __attribute__((packed));
+
+struct gea {
+ unsigned char name_len;
+ char name[1];
+} __attribute__((packed));
+
+struct gealist {
+ unsigned long list_len;
+ struct gea list[1];
+} __attribute__((packed));
+
+struct fea {
+ unsigned char EA_flags;
+ __u8 name_len;
+ __le16 value_len;
+ char name[1];
+ /* optionally followed by value */
+} __attribute__((packed));
+/* flags for _FEA.fEA */
+#define FEA_NEEDEA 0x80 /* need EA bit */
+
+struct fealist {
+ __le32 list_len;
+ struct fea list[1];
+} __attribute__((packed));
+
+/* used to hold an arbitrary blob of data */
+struct data_blob {
+ __u8 *data;
+ size_t length;
+ void (*free) (struct data_blob *data_blob);
+} __attribute__((packed));
+
+
+#ifdef CONFIG_CIFS_POSIX
+/*
+ For better POSIX semantics from Linux client, (even better
+ than the existing CIFS Unix Extensions) we need updated PDUs for:
+
+ 1) PosixCreateX - to set and return the mode, inode#, device info and
+ perhaps add a CreateDevice - to create Pipes and other special .inodes
+ Also note POSIX open flags
+ 2) Close - to return the last write time to do cache across close
+ more safely
+ 3) FindFirst return unique inode number - what about resume key, two
+ forms short (matches readdir) and full (enough info to cache inodes)
+ 4) Mkdir - set mode
+
+ And under consideration:
+ 5) FindClose2 (return nanosecond timestamp ??)
+ 6) Use nanosecond timestamps throughout all time fields if
+ corresponding attribute flag is set
+ 7) sendfile - handle based copy
+ 8) Direct i/o
+ 9) Misc fcntls?
+
+ what about fixing 64 bit alignment
+
+ There are also various legacy SMB/CIFS requests used as is
+
+ From existing Lanman and NTLM dialects:
+ --------------------------------------
+ NEGOTIATE
+ SESSION_SETUP_ANDX (BB which?)
+ TREE_CONNECT_ANDX (BB which wct?)
+ TREE_DISCONNECT (BB add volume timestamp on response)
+ LOGOFF_ANDX
+ DELETE (note delete open file behavior)
+ DELETE_DIRECTORY
+ READ_AND_X
+ WRITE_AND_X
+ LOCKING_AND_X (note posix lock semantics)
+ RENAME (note rename across dirs and open file rename posix behaviors)
+ NT_RENAME (for hardlinks) Is this good enough for all features?
+ FIND_CLOSE2
+ TRANSACTION2 (18 cases)
+ SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2
+ (BB verify that never need to set allocation size)
+ SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via
+ Unix ext?)
+
+ COPY (note support for copy across directories) - FUTURE, OPTIONAL
+ setting/getting OS/2 EAs - FUTURE (BB can this handle
+ setting Linux xattrs perfectly) - OPTIONAL
+ dnotify - FUTURE, OPTIONAL
+ quota - FUTURE, OPTIONAL
+
+ Note that various requests implemented for NT interop such as
+ NT_TRANSACT (IOCTL) QueryReparseInfo
+ are unneeded to servers compliant with the CIFS POSIX extensions
+
+ From CIFS Unix Extensions:
+ -------------------------
+ T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks
+ T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2)
+ T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK)
+ T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing
+ inode fields
+ Actually a need QUERY_FILE_UNIX_INFO
+ since has inode num
+ BB what about a) blksize/blkbits/blocks
+ b) i_version
+ c) i_rdev
+ d) notify mask?
+ e) generation
+ f) size_seqcount
+ T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX
+ TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended
+ T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL
+ */
+
+/* xsymlink is a symlink format (used by MacOS) that can be used
+ to save symlink info in a regular file when
+ mounted to operating systems that do not
+ support the cifs Unix extensions or EAs (for xattr
+ based symlinks). For such a file to be recognized
+ as containing symlink data:
+
+ 1) file size must be 1067,
+ 2) signature must begin file data,
+ 3) length field must be set to ASCII representation
+ of a number which is less than or equal to 1024,
+ 4) md5 must match that of the path data */
+
+struct xsymlink {
+ /* 1067 bytes */
+ char signature[4]; /* XSym */ /* not null terminated */
+ char cr0; /* \n */
+/* ASCII representation of length (4 bytes decimal) terminated by \n not null */
+ char length[4];
+ char cr1; /* \n */
+/* md5 of valid subset of path ie path[0] through path[length-1] */
+ __u8 md5[32];
+ char cr2; /* \n */
+/* if room left, then end with \n then 0x20s by convention but not required */
+ char path[1024];
+} __attribute__((packed));
+
+typedef struct file_xattr_info {
+ /* BB do we need another field for flags? BB */
+ __u32 xattr_name_len;
+ __u32 xattr_value_len;
+ char xattr_name[0];
+ /* followed by xattr_value[xattr_value_len], no pad */
+} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info
+ level 0x205 */
+
+
+/* flags for chattr command */
+#define EXT_SECURE_DELETE 0x00000001 /* EXT3_SECRM_FL */
+#define EXT_ENABLE_UNDELETE 0x00000002 /* EXT3_UNRM_FL */
+/* Reserved for compress file 0x4 */
+#define EXT_SYNCHRONOUS 0x00000008 /* EXT3_SYNC_FL */
+#define EXT_IMMUTABLE_FL 0x00000010 /* EXT3_IMMUTABLE_FL */
+#define EXT_OPEN_APPEND_ONLY 0x00000020 /* EXT3_APPEND_FL */
+#define EXT_DO_NOT_BACKUP 0x00000040 /* EXT3_NODUMP_FL */
+#define EXT_NO_UPDATE_ATIME 0x00000080 /* EXT3_NOATIME_FL */
+/* 0x100 through 0x800 reserved for compression flags and are GET-ONLY */
+#define EXT_HASH_TREE_INDEXED_DIR 0x00001000 /* GET-ONLY EXT3_INDEX_FL */
+/* 0x2000 reserved for IMAGIC_FL */
+#define EXT_JOURNAL_THIS_FILE 0x00004000 /* GET-ONLY EXT3_JOURNAL_DATA_FL */
+/* 0x8000 reserved for EXT3_NOTAIL_FL */
+#define EXT_SYNCHRONOUS_DIR 0x00010000 /* EXT3_DIRSYNC_FL */
+#define EXT_TOPDIR 0x00020000 /* EXT3_TOPDIR_FL */
+
+#define EXT_SET_MASK 0x000300FF
+#define EXT_GET_MASK 0x0003DFFF
+
+typedef struct file_chattr_info {
+ __le64 mask; /* list of all possible attribute bits */
+ __le64 mode; /* list of actual attribute bits on this inode */
+} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes
+ (chattr, chflags) level 0x206 */
+
+#endif
+
+#endif /* _CIFSPDU_H */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
new file mode 100644
index 0000000..6f21ecb
--- /dev/null
+++ b/fs/cifs/cifsproto.h
@@ -0,0 +1,372 @@
+/*
+ * fs/cifs/cifsproto.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _CIFSPROTO_H
+#define _CIFSPROTO_H
+#include <linux/nls.h>
+
+struct statfs;
+struct smb_vol;
+
+/*
+ *****************************************************************
+ * All Prototypes
+ *****************************************************************
+ */
+
+extern struct smb_hdr *cifs_buf_get(void);
+extern void cifs_buf_release(void *);
+extern struct smb_hdr *cifs_small_buf_get(void);
+extern void cifs_small_buf_release(void *);
+extern int smb_send(struct socket *, struct smb_hdr *,
+ unsigned int /* length */ , struct sockaddr *, bool);
+extern unsigned int _GetXid(void);
+extern void _FreeXid(unsigned int);
+#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current->fsuid));
+#define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));}
+extern char *build_path_from_dentry(struct dentry *);
+extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
+/* extern void renew_parental_timestamps(struct dentry *direntry);*/
+extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
+ struct smb_hdr * /* input */ ,
+ struct smb_hdr * /* out */ ,
+ int * /* bytes returned */ , const int long_op);
+extern int SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses,
+ struct smb_hdr *in_buf, int flags);
+extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
+ struct kvec *, int /* nvec to send */,
+ int * /* type of buf returned */ , const int flags);
+extern int SendReceiveBlockingLock(const unsigned int xid,
+ struct cifsTconInfo *ptcon,
+ struct smb_hdr *in_buf ,
+ struct smb_hdr *out_buf,
+ int *bytes_returned);
+extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
+extern bool is_valid_oplock_break(struct smb_hdr *smb,
+ struct TCP_Server_Info *);
+extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
+extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *);
+#endif
+extern unsigned int smbCalcSize(struct smb_hdr *ptr);
+extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
+extern int decode_negTokenInit(unsigned char *security_blob, int length,
+ enum securityEnum *secType);
+extern int cifs_inet_pton(const int, const char *source, void *dst);
+extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr);
+extern void header_assemble(struct smb_hdr *, char /* command */ ,
+ const struct cifsTconInfo *, int /* length of
+ fixed section (word count) in two byte units */);
+extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
+ struct cifsSesInfo *ses,
+ void **request_buf);
+extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
+ const int stage,
+ const struct nls_table *nls_cp);
+extern __u16 GetNextMid(struct TCP_Server_Info *server);
+extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16,
+ struct cifsTconInfo *);
+extern void DeleteOplockQEntry(struct oplock_q_entry *);
+extern void DeleteTconOplockQEntries(struct cifsTconInfo *);
+extern struct timespec cifs_NTtimeToUnix(u64 utc_nanoseconds_since_1601);
+extern u64 cifs_UnixTimeToNT(struct timespec);
+extern __le64 cnvrtDosCifsTm(__u16 date, __u16 time);
+extern struct timespec cnvrtDosUnixTm(__u16 date, __u16 time);
+
+extern int cifs_get_inode_info(struct inode **pinode,
+ const unsigned char *search_path,
+ FILE_ALL_INFO *pfile_info,
+ struct super_block *sb, int xid, const __u16 *pfid);
+extern int cifs_get_inode_info_unix(struct inode **pinode,
+ const unsigned char *search_path,
+ struct super_block *sb, int xid);
+extern void acl_to_uid_mode(struct inode *inode, const char *path,
+ const __u16 *pfid);
+extern int mode_to_acl(struct inode *inode, const char *path, __u64);
+
+extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
+ const char *);
+extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
+extern void cifs_dfs_release_automount_timer(void);
+void cifs_proc_init(void);
+void cifs_proc_clean(void);
+
+extern int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
+ struct nls_table *nls_info);
+extern int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses);
+
+extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
+ const char *tree, struct cifsTconInfo *tcon,
+ const struct nls_table *);
+
+extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
+ const char *searchName, const struct nls_table *nls_codepage,
+ __u16 *searchHandle, struct cifs_search_info *psrch_inf,
+ int map, const char dirsep);
+
+extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
+ __u16 searchHandle, struct cifs_search_info *psrch_inf);
+
+extern int CIFSFindClose(const int, struct cifsTconInfo *tcon,
+ const __u16 search_handle);
+
+extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_ALL_INFO *findData,
+ int legacy /* whether to use old info level */,
+ const struct nls_table *nls_codepage, int remap);
+extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_ALL_INFO *findData,
+ const struct nls_table *nls_codepage, int remap);
+
+extern int CIFSSMBUnixQPathInfo(const int xid,
+ struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_UNIX_BASIC_INFO *pFindData,
+ const struct nls_table *nls_codepage, int remap);
+
+extern int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
+ const unsigned char *searchName,
+ struct dfs_info3_param **target_nodes,
+ unsigned int *number_of_nodes_in_array,
+ const struct nls_table *nls_codepage, int remap);
+
+extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
+ const char *old_path,
+ const struct nls_table *nls_codepage,
+ unsigned int *pnum_referrals,
+ struct dfs_info3_param **preferrals,
+ int remap);
+extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
+ struct super_block *sb, struct smb_vol *vol);
+extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
+ struct kstatfs *FSData);
+extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon,
+ struct kstatfs *FSData);
+extern int CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon,
+ __u64 cap);
+
+extern int CIFSSMBQFSAttributeInfo(const int xid,
+ struct cifsTconInfo *tcon);
+extern int CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon);
+extern int CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon);
+extern int CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
+ struct kstatfs *FSData);
+
+extern int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const FILE_BASIC_INFO *data,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
+ const FILE_BASIC_INFO *data, __u16 fid,
+ __u32 pid_of_opener);
+extern int CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
+ bool delete_file, __u16 fid, __u32 pid_of_opener);
+#if 0
+extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon,
+ char *fileName, __u16 dos_attributes,
+ const struct nls_table *nls_codepage);
+#endif /* possibly unneeded function */
+extern int CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, __u64 size,
+ bool setAllocationSizeFlag,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon,
+ __u64 size, __u16 fileHandle, __u32 opener_pid,
+ bool AllocSizeFlag);
+
+struct cifs_unix_set_info_args {
+ __u64 ctime;
+ __u64 atime;
+ __u64 mtime;
+ __u64 mode;
+ __u64 uid;
+ __u64 gid;
+ dev_t device;
+};
+
+extern int CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *pTcon,
+ char *fileName,
+ const struct cifs_unix_set_info_args *args,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+
+extern int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
+ const char *newName,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon,
+ const char *name, const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon,
+ const char *name, __u16 type,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon,
+ const char *name,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
+ int netfid, const char *target_name,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSCreateHardLink(const int xid,
+ struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSUnixCreateHardLink(const int xid,
+ struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSUnixCreateSymLink(const int xid,
+ struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage);
+extern int CIFSSMBUnixQuerySymLink(const int xid,
+ struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *syminfo, const int buflen,
+ const struct nls_table *nls_codepage);
+extern int CIFSSMBQueryReparseLinkInfo(const int xid,
+ struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *symlinkinfo, const int buflen, __u16 fid,
+ const struct nls_table *nls_codepage);
+
+extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const int disposition,
+ const int access_flags, const int omode,
+ __u16 *netfid, int *pOplock, FILE_ALL_INFO *,
+ const struct nls_table *nls_codepage, int remap);
+extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const int disposition,
+ const int access_flags, const int omode,
+ __u16 *netfid, int *pOplock, FILE_ALL_INFO *,
+ const struct nls_table *nls_codepage, int remap);
+extern int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon,
+ u32 posix_flags, __u64 mode, __u16 *netfid,
+ FILE_UNIX_BASIC_INFO *pRetData,
+ __u32 *pOplock, const char *name,
+ const struct nls_table *nls_codepage, int remap);
+extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon,
+ const int smb_file_id);
+
+extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, unsigned int count,
+ const __u64 lseek, unsigned int *nbytes, char **buf,
+ int *return_buf_type);
+extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 lseek, unsigned int *nbytes,
+ const char *buf, const char __user *ubuf,
+ const int long_op);
+extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 offset, unsigned int *nbytes,
+ struct kvec *iov, const int nvec, const int long_op);
+extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName, __u64 *inode_number,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int cifs_convertUCSpath(char *target, const __le16 *source, int maxlen,
+ const struct nls_table *codepage);
+extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen,
+ const struct nls_table *cp, int mapChars);
+
+extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
+ const __u16 netfid, const __u64 len,
+ const __u64 offset, const __u32 numUnlock,
+ const __u32 numLock, const __u8 lockType,
+ const bool waitFlag);
+extern int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
+ const __u16 smb_file_id, const int get_flag,
+ const __u64 len, struct file_lock *,
+ const __u16 lock_type, const bool waitFlag);
+extern int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon);
+extern int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses);
+
+extern struct cifsSesInfo *sesInfoAlloc(void);
+extern void sesInfoFree(struct cifsSesInfo *);
+extern struct cifsTconInfo *tconInfoAlloc(void);
+extern void tconInfoFree(struct cifsTconInfo *);
+
+extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
+extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
+ __u32 *);
+extern int cifs_verify_signature(struct smb_hdr *,
+ const struct mac_key *mac_key,
+ __u32 expected_sequence_number);
+extern int cifs_calculate_mac_key(struct mac_key *key, const char *rn,
+ const char *pass);
+extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *,
+ const struct nls_table *);
+extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *);
+extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *,
+ const struct nls_table *);
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key);
+#endif /* CIFS_WEAK_PW_HASH */
+extern int CIFSSMBCopy(int xid,
+ struct cifsTconInfo *source_tcon,
+ const char *fromName,
+ const __u16 target_tid,
+ const char *toName, const int flags,
+ const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
+ const int notify_subdirs, const __u16 netfid,
+ __u32 filter, struct file *file, int multishot,
+ const struct nls_table *nls_codepage);
+extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName, char *EAData,
+ size_t bufsize, const struct nls_table *nls_codepage,
+ int remap_special_chars);
+extern ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName, const unsigned char *ea_name,
+ unsigned char *ea_value, size_t buf_size,
+ const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const char *ea_name,
+ const void *ea_value, const __u16 ea_value_len,
+ const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
+ __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
+extern int CIFSSMBSetCIFSACL(const int, struct cifsTconInfo *, __u16,
+ struct cifs_ntsd *, __u32);
+extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *acl_inf, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *fileName,
+ const char *local_acl, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage, int remap_special_chars);
+extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
+#endif /* _CIFSPROTO_H */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
new file mode 100644
index 0000000..6d51696
--- /dev/null
+++ b/fs/cifs/cifssmb.c
@@ -0,0 +1,5690 @@
+/*
+ * fs/cifs/cifssmb.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Contains the routines for constructing the SMB PDUs themselves
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */
+ /* These are mostly routines that operate on a pathname, or on a tree id */
+ /* (mounted volume), but there are eight handle based routines which must be */
+ /* treated slightly differently for reconnection purposes since we never */
+ /* want to reuse a stale file handle and only the caller knows the file info */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/vfs.h>
+#include <linux/posix_acl_xattr.h>
+#include <asm/uaccess.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsacl.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+
+#ifdef CONFIG_CIFS_POSIX
+static struct {
+ int index;
+ char *name;
+} protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ {LANMAN_PROT, "\2LM1.2X002"},
+ {LANMAN2_PROT, "\2LANMAN2.1"},
+#endif /* weak password hashing for legacy clients */
+ {CIFS_PROT, "\2NT LM 0.12"},
+ {POSIX_PROT, "\2POSIX 2"},
+ {BAD_PROT, "\2"}
+};
+#else
+static struct {
+ int index;
+ char *name;
+} protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ {LANMAN_PROT, "\2LM1.2X002"},
+ {LANMAN2_PROT, "\2LANMAN2.1"},
+#endif /* weak password hashing for legacy clients */
+ {CIFS_PROT, "\2NT LM 0.12"},
+ {BAD_PROT, "\2"}
+};
+#endif
+
+/* define the number of elements in the cifs dialect array */
+#ifdef CONFIG_CIFS_POSIX
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 4
+#else
+#define CIFS_NUM_PROT 2
+#endif /* CIFS_WEAK_PW_HASH */
+#else /* not posix */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 3
+#else
+#define CIFS_NUM_PROT 1
+#endif /* CONFIG_CIFS_WEAK_PW_HASH */
+#endif /* CIFS_POSIX */
+
+/* Allocates buffer into dst and copies smb string from src to it.
+ * caller is responsible for freeing dst if function returned 0.
+ * returns:
+ * on success - 0
+ * on failure - errno
+ */
+static int
+cifs_strncpy_to_host(char **dst, const char *src, const int maxlen,
+ const bool is_unicode, const struct nls_table *nls_codepage)
+{
+ int plen;
+
+ if (is_unicode) {
+ plen = UniStrnlen((wchar_t *)src, maxlen);
+ *dst = kmalloc(plen + 2, GFP_KERNEL);
+ if (!*dst)
+ goto cifs_strncpy_to_host_ErrExit;
+ cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage);
+ } else {
+ plen = strnlen(src, maxlen);
+ *dst = kmalloc(plen + 2, GFP_KERNEL);
+ if (!*dst)
+ goto cifs_strncpy_to_host_ErrExit;
+ strncpy(*dst, src, plen);
+ }
+ (*dst)[plen] = 0;
+ (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */
+ return 0;
+
+cifs_strncpy_to_host_ErrExit:
+ cERROR(1, ("Failed to allocate buffer for string\n"));
+ return -ENOMEM;
+}
+
+
+/* Mark as invalid, all open files on tree connections since they
+ were closed when session to server was lost */
+static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
+{
+ struct cifsFileInfo *open_file = NULL;
+ struct list_head *tmp;
+ struct list_head *tmp1;
+
+/* list all files open on tree connection and mark them invalid */
+ write_lock(&GlobalSMBSeslock);
+ list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
+ open_file = list_entry(tmp, struct cifsFileInfo, tlist);
+ open_file->invalidHandle = true;
+ }
+ write_unlock(&GlobalSMBSeslock);
+ /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
+ to this tcon */
+}
+
+/* Allocate and return pointer to an SMB request buffer, and set basic
+ SMB information in the SMB header. If the return code is zero, this
+ function must have filled in request_buf pointer */
+static int
+small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+ void **request_buf)
+{
+ int rc = 0;
+
+ /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
+ check for tcp and smb session status done differently
+ for those three - in the calling routine */
+ if (tcon) {
+ if (tcon->tidStatus == CifsExiting) {
+ /* only tree disconnect, open, and write,
+ (and ulogoff which does not have tcon)
+ are allowed as we start force umount */
+ if ((smb_command != SMB_COM_WRITE_ANDX) &&
+ (smb_command != SMB_COM_OPEN_ANDX) &&
+ (smb_command != SMB_COM_TREE_DISCONNECT)) {
+ cFYI(1, ("can not send cmd %d while umounting",
+ smb_command));
+ return -ENODEV;
+ }
+ }
+ if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
+ (tcon->ses->server)) {
+ struct nls_table *nls_codepage;
+ /* Give Demultiplex thread up to 10 seconds to
+ reconnect, should be greater than cifs socket
+ timeout which is 7 seconds */
+ while (tcon->ses->server->tcpStatus ==
+ CifsNeedReconnect) {
+ wait_event_interruptible_timeout(tcon->ses->server->response_q,
+ (tcon->ses->server->tcpStatus ==
+ CifsGood), 10 * HZ);
+ if (tcon->ses->server->tcpStatus ==
+ CifsNeedReconnect) {
+ /* on "soft" mounts we wait once */
+ if (!tcon->retry ||
+ (tcon->ses->status == CifsExiting)) {
+ cFYI(1, ("gave up waiting on "
+ "reconnect in smb_init"));
+ return -EHOSTDOWN;
+ } /* else "hard" mount - keep retrying
+ until process is killed or server
+ comes back on-line */
+ } else /* TCP session is reestablished now */
+ break;
+ }
+
+ nls_codepage = load_nls_default();
+ /* need to prevent multiple threads trying to
+ simultaneously reconnect the same SMB session */
+ down(&tcon->ses->sesSem);
+ if (tcon->ses->need_reconnect)
+ rc = cifs_setup_session(0, tcon->ses,
+ nls_codepage);
+ if (!rc && (tcon->need_reconnect)) {
+ mark_open_files_invalid(tcon);
+ rc = CIFSTCon(0, tcon->ses, tcon->treeName,
+ tcon, nls_codepage);
+ up(&tcon->ses->sesSem);
+ /* BB FIXME add code to check if wsize needs
+ update due to negotiated smb buffer size
+ shrinking */
+ if (rc == 0) {
+ atomic_inc(&tconInfoReconnectCount);
+ /* tell server Unix caps we support */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ reset_cifs_unix_caps(
+ 0 /* no xid */,
+ tcon,
+ NULL /* we do not know sb */,
+ NULL /* no vol info */);
+ }
+
+ cFYI(1, ("reconnect tcon rc = %d", rc));
+ /* Removed call to reopen open files here.
+ It is safer (and faster) to reopen files
+ one at a time as needed in read and write */
+
+ /* Check if handle based operation so we
+ know whether we can continue or not without
+ returning to caller to reset file handle */
+ switch (smb_command) {
+ case SMB_COM_READ_ANDX:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_FIND_CLOSE2:
+ case SMB_COM_LOCKING_ANDX: {
+ unload_nls(nls_codepage);
+ return -EAGAIN;
+ }
+ }
+ } else {
+ up(&tcon->ses->sesSem);
+ }
+ unload_nls(nls_codepage);
+
+ } else {
+ return -EIO;
+ }
+ }
+ if (rc)
+ return rc;
+
+ *request_buf = cifs_small_buf_get();
+ if (*request_buf == NULL) {
+ /* BB should we add a retry in here if not a writepage? */
+ return -ENOMEM;
+ }
+
+ header_assemble((struct smb_hdr *) *request_buf, smb_command,
+ tcon, wct);
+
+ if (tcon != NULL)
+ cifs_stats_inc(&tcon->num_smbs_sent);
+
+ return rc;
+}
+
+int
+small_smb_init_no_tc(const int smb_command, const int wct,
+ struct cifsSesInfo *ses, void **request_buf)
+{
+ int rc;
+ struct smb_hdr *buffer;
+
+ rc = small_smb_init(smb_command, wct, NULL, request_buf);
+ if (rc)
+ return rc;
+
+ buffer = (struct smb_hdr *)*request_buf;
+ buffer->Mid = GetNextMid(ses->server);
+ if (ses->capabilities & CAP_UNICODE)
+ buffer->Flags2 |= SMBFLG2_UNICODE;
+ if (ses->capabilities & CAP_STATUS32)
+ buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+
+ /* uid, tid can stay at zero as set in header assemble */
+
+ /* BB add support for turning on the signing when
+ this function is used after 1st of session setup requests */
+
+ return rc;
+}
+
+/* If the return code is zero, this function must fill in request_buf pointer */
+static int
+smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+ void **request_buf /* returned */ ,
+ void **response_buf /* returned */ )
+{
+ int rc = 0;
+
+ /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
+ check for tcp and smb session status done differently
+ for those three - in the calling routine */
+ if (tcon) {
+ if (tcon->tidStatus == CifsExiting) {
+ /* only tree disconnect, open, and write,
+ (and ulogoff which does not have tcon)
+ are allowed as we start force umount */
+ if ((smb_command != SMB_COM_WRITE_ANDX) &&
+ (smb_command != SMB_COM_OPEN_ANDX) &&
+ (smb_command != SMB_COM_TREE_DISCONNECT)) {
+ cFYI(1, ("can not send cmd %d while umounting",
+ smb_command));
+ return -ENODEV;
+ }
+ }
+
+ if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
+ (tcon->ses->server)) {
+ struct nls_table *nls_codepage;
+ /* Give Demultiplex thread up to 10 seconds to
+ reconnect, should be greater than cifs socket
+ timeout which is 7 seconds */
+ while (tcon->ses->server->tcpStatus ==
+ CifsNeedReconnect) {
+ wait_event_interruptible_timeout(tcon->ses->server->response_q,
+ (tcon->ses->server->tcpStatus ==
+ CifsGood), 10 * HZ);
+ if (tcon->ses->server->tcpStatus ==
+ CifsNeedReconnect) {
+ /* on "soft" mounts we wait once */
+ if (!tcon->retry ||
+ (tcon->ses->status == CifsExiting)) {
+ cFYI(1, ("gave up waiting on "
+ "reconnect in smb_init"));
+ return -EHOSTDOWN;
+ } /* else "hard" mount - keep retrying
+ until process is killed or server
+ comes on-line */
+ } else /* TCP session is reestablished now */
+ break;
+ }
+ nls_codepage = load_nls_default();
+ /* need to prevent multiple threads trying to
+ simultaneously reconnect the same SMB session */
+ down(&tcon->ses->sesSem);
+ if (tcon->ses->need_reconnect)
+ rc = cifs_setup_session(0, tcon->ses,
+ nls_codepage);
+ if (!rc && (tcon->need_reconnect)) {
+ mark_open_files_invalid(tcon);
+ rc = CIFSTCon(0, tcon->ses, tcon->treeName,
+ tcon, nls_codepage);
+ up(&tcon->ses->sesSem);
+ /* BB FIXME add code to check if wsize needs
+ update due to negotiated smb buffer size
+ shrinking */
+ if (rc == 0) {
+ atomic_inc(&tconInfoReconnectCount);
+ /* tell server Unix caps we support */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ reset_cifs_unix_caps(
+ 0 /* no xid */,
+ tcon,
+ NULL /* do not know sb */,
+ NULL /* no vol info */);
+ }
+
+ cFYI(1, ("reconnect tcon rc = %d", rc));
+ /* Removed call to reopen open files here.
+ It is safer (and faster) to reopen files
+ one at a time as needed in read and write */
+
+ /* Check if handle based operation so we
+ know whether we can continue or not without
+ returning to caller to reset file handle */
+ switch (smb_command) {
+ case SMB_COM_READ_ANDX:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_FIND_CLOSE2:
+ case SMB_COM_LOCKING_ANDX: {
+ unload_nls(nls_codepage);
+ return -EAGAIN;
+ }
+ }
+ } else {
+ up(&tcon->ses->sesSem);
+ }
+ unload_nls(nls_codepage);
+
+ } else {
+ return -EIO;
+ }
+ }
+ if (rc)
+ return rc;
+
+ *request_buf = cifs_buf_get();
+ if (*request_buf == NULL) {
+ /* BB should we add a retry in here if not a writepage? */
+ return -ENOMEM;
+ }
+ /* Although the original thought was we needed the response buf for */
+ /* potential retries of smb operations it turns out we can determine */
+ /* from the mid flags when the request buffer can be resent without */
+ /* having to use a second distinct buffer for the response */
+ if (response_buf)
+ *response_buf = *request_buf;
+
+ header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
+ wct);
+
+ if (tcon != NULL)
+ cifs_stats_inc(&tcon->num_smbs_sent);
+
+ return rc;
+}
+
+static int validate_t2(struct smb_t2_rsp *pSMB)
+{
+ int rc = -EINVAL;
+ int total_size;
+ char *pBCC;
+
+ /* check for plausible wct, bcc and t2 data and parm sizes */
+ /* check for parm and data offset going beyond end of smb */
+ if (pSMB->hdr.WordCount >= 10) {
+ if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
+ (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
+ /* check that bcc is at least as big as parms + data */
+ /* check that bcc is less than negotiated smb buffer */
+ total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
+ if (total_size < 512) {
+ total_size +=
+ le16_to_cpu(pSMB->t2_rsp.DataCount);
+ /* BCC le converted in SendReceive */
+ pBCC = (pSMB->hdr.WordCount * 2) +
+ sizeof(struct smb_hdr) +
+ (char *)pSMB;
+ if ((total_size <= (*(u16 *)pBCC)) &&
+ (total_size <
+ CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
+ return 0;
+ }
+ }
+ }
+ }
+ cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
+ sizeof(struct smb_t2_rsp) + 16);
+ return rc;
+}
+int
+CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
+{
+ NEGOTIATE_REQ *pSMB;
+ NEGOTIATE_RSP *pSMBr;
+ int rc = 0;
+ int bytes_returned;
+ int i;
+ struct TCP_Server_Info *server;
+ u16 count;
+ unsigned int secFlags;
+ u16 dialect;
+
+ if (ses->server)
+ server = ses->server;
+ else {
+ rc = -EIO;
+ return rc;
+ }
+ rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
+ (void **) &pSMB, (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ /* if any of auth flags (ie not sign or seal) are overriden use them */
+ if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
+ secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
+ else /* if override flags set only sign/seal OR them with global auth */
+ secFlags = extended_security | ses->overrideSecFlg;
+
+ cFYI(1, ("secFlags 0x%x", secFlags));
+
+ pSMB->hdr.Mid = GetNextMid(server);
+ pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
+
+ if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
+ pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
+ cFYI(1, ("Kerberos only mechanism, enable extended security"));
+ pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ }
+
+ count = 0;
+ for (i = 0; i < CIFS_NUM_PROT; i++) {
+ strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
+ count += strlen(protocols[i].name) + 1;
+ /* null at end of source and target buffers anyway */
+ }
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc != 0)
+ goto neg_err_exit;
+
+ dialect = le16_to_cpu(pSMBr->DialectIndex);
+ cFYI(1, ("Dialect: %d", dialect));
+ /* Check wct = 1 error case */
+ if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
+ /* core returns wct = 1, but we do not ask for core - otherwise
+ small wct just comes when dialect index is -1 indicating we
+ could not negotiate a common dialect */
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ } else if ((pSMBr->hdr.WordCount == 13)
+ && ((dialect == LANMAN_PROT)
+ || (dialect == LANMAN2_PROT))) {
+ __s16 tmp;
+ struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
+
+ if ((secFlags & CIFSSEC_MAY_LANMAN) ||
+ (secFlags & CIFSSEC_MAY_PLNTXT))
+ server->secType = LANMAN;
+ else {
+ cERROR(1, ("mount failed weak security disabled"
+ " in /proc/fs/cifs/SecurityFlags"));
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+ }
+ server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
+ server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
+ server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
+ (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+ GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
+ /* even though we do not use raw we might as well set this
+ accurately, in case we ever find a need for it */
+ if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
+ server->maxRw = 0xFF00;
+ server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
+ } else {
+ server->maxRw = 0;/* we do not need to use raw anyway */
+ server->capabilities = CAP_MPX_MODE;
+ }
+ tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
+ if (tmp == -1) {
+ /* OS/2 often does not set timezone therefore
+ * we must use server time to calc time zone.
+ * Could deviate slightly from the right zone.
+ * Smallest defined timezone difference is 15 minutes
+ * (i.e. Nepal). Rounding up/down is done to match
+ * this requirement.
+ */
+ int val, seconds, remain, result;
+ struct timespec ts, utc;
+ utc = CURRENT_TIME;
+ ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date),
+ le16_to_cpu(rsp->SrvTime.Time));
+ cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d",
+ (int)ts.tv_sec, (int)utc.tv_sec,
+ (int)(utc.tv_sec - ts.tv_sec)));
+ val = (int)(utc.tv_sec - ts.tv_sec);
+ seconds = abs(val);
+ result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
+ remain = seconds % MIN_TZ_ADJ;
+ if (remain >= (MIN_TZ_ADJ / 2))
+ result += MIN_TZ_ADJ;
+ if (val < 0)
+ result = -result;
+ server->timeAdj = result;
+ } else {
+ server->timeAdj = (int)tmp;
+ server->timeAdj *= 60; /* also in seconds */
+ }
+ cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj));
+
+
+ /* BB get server time for time conversions and add
+ code to use it and timezone since this is not UTC */
+
+ if (rsp->EncryptionKeyLength ==
+ cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
+ memcpy(server->cryptKey, rsp->EncryptionKey,
+ CIFS_CRYPTO_KEY_SIZE);
+ } else if (server->secMode & SECMODE_PW_ENCRYPT) {
+ rc = -EIO; /* need cryptkey unless plain text */
+ goto neg_err_exit;
+ }
+
+ cFYI(1, ("LANMAN negotiated"));
+ /* we will not end up setting signing flags - as no signing
+ was in LANMAN and server did not return the flags on */
+ goto signing_check;
+#else /* weak security disabled */
+ } else if (pSMBr->hdr.WordCount == 13) {
+ cERROR(1, ("mount failed, cifs module not built "
+ "with CIFS_WEAK_PW_HASH support"));
+ rc = -EOPNOTSUPP;
+#endif /* WEAK_PW_HASH */
+ goto neg_err_exit;
+ } else if (pSMBr->hdr.WordCount != 17) {
+ /* unknown wct */
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+ }
+ /* else wct == 17 NTLM */
+ server->secMode = pSMBr->SecurityMode;
+ if ((server->secMode & SECMODE_USER) == 0)
+ cFYI(1, ("share mode security"));
+
+ if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
+#endif /* CIFS_WEAK_PW_HASH */
+ cERROR(1, ("Server requests plain text password"
+ " but client support disabled"));
+
+ if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
+ server->secType = NTLMv2;
+ else if (secFlags & CIFSSEC_MAY_NTLM)
+ server->secType = NTLM;
+ else if (secFlags & CIFSSEC_MAY_NTLMV2)
+ server->secType = NTLMv2;
+ else if (secFlags & CIFSSEC_MAY_KRB5)
+ server->secType = Kerberos;
+ else if (secFlags & CIFSSEC_MAY_LANMAN)
+ server->secType = LANMAN;
+/* #ifdef CONFIG_CIFS_EXPERIMENTAL
+ else if (secFlags & CIFSSEC_MAY_PLNTXT)
+ server->secType = ??
+#endif */
+ else {
+ rc = -EOPNOTSUPP;
+ cERROR(1, ("Invalid security type"));
+ goto neg_err_exit;
+ }
+ /* else ... any others ...? */
+
+ /* one byte, so no need to convert this or EncryptionKeyLen from
+ little endian */
+ server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
+ /* probably no need to store and check maxvcs */
+ server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
+ (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+ server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
+ cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
+ GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
+ server->capabilities = le32_to_cpu(pSMBr->Capabilities);
+ server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
+ server->timeAdj *= 60;
+ if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+ memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
+ CIFS_CRYPTO_KEY_SIZE);
+ } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
+ && (pSMBr->EncryptionKeyLength == 0)) {
+ /* decode security blob */
+ } else if (server->secMode & SECMODE_PW_ENCRYPT) {
+ rc = -EIO; /* no crypt key only if plain text pwd */
+ goto neg_err_exit;
+ }
+
+ /* BB might be helpful to save off the domain of server here */
+
+ if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
+ (server->capabilities & CAP_EXTENDED_SECURITY)) {
+ count = pSMBr->ByteCount;
+ if (count < 16) {
+ rc = -EIO;
+ goto neg_err_exit;
+ }
+ read_lock(&cifs_tcp_ses_lock);
+ if (server->srv_count > 1) {
+ read_unlock(&cifs_tcp_ses_lock);
+ if (memcmp(server->server_GUID,
+ pSMBr->u.extended_response.
+ GUID, 16) != 0) {
+ cFYI(1, ("server UID changed"));
+ memcpy(server->server_GUID,
+ pSMBr->u.extended_response.GUID,
+ 16);
+ }
+ } else {
+ read_unlock(&cifs_tcp_ses_lock);
+ memcpy(server->server_GUID,
+ pSMBr->u.extended_response.GUID, 16);
+ }
+
+ if (count == 16) {
+ server->secType = RawNTLMSSP;
+ } else {
+ rc = decode_negTokenInit(pSMBr->u.extended_response.
+ SecurityBlob,
+ count - 16,
+ &server->secType);
+ if (rc == 1)
+ rc = 0;
+ else
+ rc = -EINVAL;
+ }
+ } else
+ server->capabilities &= ~CAP_EXTENDED_SECURITY;
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+signing_check:
+#endif
+ if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
+ /* MUST_SIGN already includes the MAY_SIGN FLAG
+ so if this is zero it means that signing is disabled */
+ cFYI(1, ("Signing disabled"));
+ if (server->secMode & SECMODE_SIGN_REQUIRED) {
+ cERROR(1, ("Server requires "
+ "packet signing to be enabled in "
+ "/proc/fs/cifs/SecurityFlags."));
+ rc = -EOPNOTSUPP;
+ }
+ server->secMode &=
+ ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+ } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
+ /* signing required */
+ cFYI(1, ("Must sign - secFlags 0x%x", secFlags));
+ if ((server->secMode &
+ (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
+ cERROR(1,
+ ("signing required but server lacks support"));
+ rc = -EOPNOTSUPP;
+ } else
+ server->secMode |= SECMODE_SIGN_REQUIRED;
+ } else {
+ /* signing optional ie CIFSSEC_MAY_SIGN */
+ if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
+ server->secMode &=
+ ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+ }
+
+neg_err_exit:
+ cifs_buf_release(pSMB);
+
+ cFYI(1, ("negprot rc %d", rc));
+ return rc;
+}
+
+int
+CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
+{
+ struct smb_hdr *smb_buffer;
+ int rc = 0;
+
+ cFYI(1, ("In tree disconnect"));
+
+ /* BB: do we need to check this? These should never be NULL. */
+ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+ return -EIO;
+
+ /*
+ * No need to return error on this operation if tid invalidated and
+ * closed on server already e.g. due to tcp session crashing. Also,
+ * the tcon is no longer on the list, so no need to take lock before
+ * checking this.
+ */
+ if (tcon->need_reconnect)
+ return 0;
+
+ rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
+ (void **)&smb_buffer);
+ if (rc)
+ return rc;
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
+ if (rc)
+ cFYI(1, ("Tree disconnect failed %d", rc));
+
+ /* No need to return error on this operation if tid invalidated and
+ closed on server already e.g. due to tcp session crashing */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
+{
+ LOGOFF_ANDX_REQ *pSMB;
+ int rc = 0;
+
+ cFYI(1, ("In SMBLogoff for session disconnect"));
+
+ /*
+ * BB: do we need to check validity of ses and server? They should
+ * always be valid since we have an active reference. If not, that
+ * should probably be a BUG()
+ */
+ if (!ses || !ses->server)
+ return -EIO;
+
+ down(&ses->sesSem);
+ if (ses->need_reconnect)
+ goto session_already_dead; /* no need to send SMBlogoff if uid
+ already closed due to reconnect */
+ rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
+ if (rc) {
+ up(&ses->sesSem);
+ return rc;
+ }
+
+ pSMB->hdr.Mid = GetNextMid(ses->server);
+
+ if (ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ pSMB->hdr.Uid = ses->Suid;
+
+ pSMB->AndXCommand = 0xFF;
+ rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
+session_already_dead:
+ up(&ses->sesSem);
+
+ /* if session dead then we do not need to do ulogoff,
+ since server closed smb session, no sense reporting
+ error */
+ if (rc == -EAGAIN)
+ rc = 0;
+ return rc;
+}
+
+int
+CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
+ __u16 type, const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ struct unlink_psx_rq *pRqD;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cFYI(1, ("In POSIX delete"));
+PsxDelete:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB add path length overrun check */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = 0; /* BB double check this with jra */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+
+ /* Setup pointer to Request Data (inode type) */
+ pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
+ pRqD->type = cpu_to_le16(type);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
+
+ pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
+ pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("Posix delete returned %d", rc));
+ cifs_buf_release(pSMB);
+
+ cifs_stats_inc(&tcon->num_deletes);
+
+ if (rc == -EAGAIN)
+ goto PsxDelete;
+
+ return rc;
+}
+
+int
+CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ DELETE_FILE_REQ *pSMB = NULL;
+ DELETE_FILE_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+DelFileRetry:
+ rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_deletes);
+ if (rc)
+ cFYI(1, ("Error in RMFile = %d", rc));
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto DelFileRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ DELETE_DIRECTORY_REQ *pSMB = NULL;
+ DELETE_DIRECTORY_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In CIFSSMBRmDir"));
+RmDirRetry:
+ rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ name_len = strnlen(dirName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, dirName, name_len);
+ }
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_rmdirs);
+ if (rc)
+ cFYI(1, ("Error in RMDir = %d", rc));
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto RmDirRetry;
+ return rc;
+}
+
+int
+CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
+ const char *name, const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ CREATE_DIRECTORY_REQ *pSMB = NULL;
+ CREATE_DIRECTORY_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In CIFSSMBMkDir"));
+MkDirRetry:
+ rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->DirName, name, name_len);
+ }
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_mkdirs);
+ if (rc)
+ cFYI(1, ("Error in Mkdir = %d", rc));
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto MkDirRetry;
+ return rc;
+}
+
+int
+CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
+ __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
+ __u32 *pOplock, const char *name,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+ OPEN_PSX_REQ *pdata;
+ OPEN_PSX_RSP *psx_rsp;
+
+ cFYI(1, ("In POSIX Create"));
+PsxCreat:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, name,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(name, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, name, name_len);
+ }
+
+ params = 6 + name_len;
+ count = sizeof(OPEN_PSX_REQ);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
+ pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
+ pdata->Permissions = cpu_to_le64(mode);
+ pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
+ pdata->OpenFlags = cpu_to_le32(*pOplock);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Posix create returned %d", rc));
+ goto psx_create_err;
+ }
+
+ cFYI(1, ("copying inode info"));
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
+ rc = -EIO; /* bad smb */
+ goto psx_create_err;
+ }
+
+ /* copy return information to pRetData */
+ psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
+ + le16_to_cpu(pSMBr->t2.DataOffset));
+
+ *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
+ if (netfid)
+ *netfid = psx_rsp->Fid; /* cifs fid stays in le */
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
+ *pOplock |= CIFS_CREATE_ACTION;
+ /* check to make sure response data is there */
+ if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
+ pRetData->Type = cpu_to_le32(-1); /* unknown */
+ cFYI(DBG2, ("unknown type"));
+ } else {
+ if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
+ + sizeof(FILE_UNIX_BASIC_INFO)) {
+ cERROR(1, ("Open response data too small"));
+ pRetData->Type = cpu_to_le32(-1);
+ goto psx_create_err;
+ }
+ memcpy((char *) pRetData,
+ (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
+ sizeof(FILE_UNIX_BASIC_INFO));
+ }
+
+psx_create_err:
+ cifs_buf_release(pSMB);
+
+ cifs_stats_inc(&tcon->num_mkdirs);
+
+ if (rc == -EAGAIN)
+ goto PsxCreat;
+
+ return rc;
+}
+
+static __u16 convert_disposition(int disposition)
+{
+ __u16 ofun = 0;
+
+ switch (disposition) {
+ case FILE_SUPERSEDE:
+ ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+ break;
+ case FILE_OPEN:
+ ofun = SMBOPEN_OAPPEND;
+ break;
+ case FILE_CREATE:
+ ofun = SMBOPEN_OCREATE;
+ break;
+ case FILE_OPEN_IF:
+ ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
+ break;
+ case FILE_OVERWRITE:
+ ofun = SMBOPEN_OTRUNC;
+ break;
+ case FILE_OVERWRITE_IF:
+ ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
+ break;
+ default:
+ cFYI(1, ("unknown disposition %d", disposition));
+ ofun = SMBOPEN_OAPPEND; /* regular open */
+ }
+ return ofun;
+}
+
+static int
+access_flags_to_smbopen_mode(const int access_flags)
+{
+ int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
+
+ if (masked_flags == GENERIC_READ)
+ return SMBOPEN_READ;
+ else if (masked_flags == GENERIC_WRITE)
+ return SMBOPEN_WRITE;
+
+ /* just go for read/write */
+ return SMBOPEN_READWRITE;
+}
+
+int
+SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const int openDisposition,
+ const int access_flags, const int create_options, __u16 *netfid,
+ int *pOplock, FILE_ALL_INFO *pfile_info,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = -EACCES;
+ OPENX_REQ *pSMB = NULL;
+ OPENX_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+ __u16 count;
+
+OldOpenRetry:
+ rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ count = 1; /* account for one byte pad to word boundary */
+ name_len =
+ cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
+ fileName, PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve check for buffer overruns BB */
+ count = 0; /* no pad */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ if (*pOplock & REQ_OPLOCK)
+ pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
+ else if (*pOplock & REQ_BATCHOPLOCK)
+ pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
+
+ pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
+ pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
+ pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
+ /* set file as system file if special file such
+ as fifo and server expecting SFU style and
+ no Unix extensions */
+
+ if (create_options & CREATE_OPTION_SPECIAL)
+ pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
+ else /* BB FIXME BB */
+ pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
+
+ if (create_options & CREATE_OPTION_READONLY)
+ pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
+
+ /* BB FIXME BB */
+/* pSMB->CreateOptions = cpu_to_le32(create_options &
+ CREATE_OPTIONS_MASK); */
+ /* BB FIXME END BB */
+
+ pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
+ pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
+ count += name_len;
+ pSMB->hdr.smb_buf_length += count;
+
+ pSMB->ByteCount = cpu_to_le16(count);
+ /* long_op set to 1 to allow for oplock break timeouts */
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
+ cifs_stats_inc(&tcon->num_opens);
+ if (rc) {
+ cFYI(1, ("Error in Open = %d", rc));
+ } else {
+ /* BB verify if wct == 15 */
+
+/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
+
+ *netfid = pSMBr->Fid; /* cifs fid stays in le */
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ /* BB FIXME BB */
+/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
+ *pOplock |= CIFS_CREATE_ACTION; */
+ /* BB FIXME END */
+
+ if (pfile_info) {
+ pfile_info->CreationTime = 0; /* BB convert CreateTime*/
+ pfile_info->LastAccessTime = 0; /* BB fixme */
+ pfile_info->LastWriteTime = 0; /* BB fixme */
+ pfile_info->ChangeTime = 0; /* BB fixme */
+ pfile_info->Attributes =
+ cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
+ /* the file_info buf is endian converted by caller */
+ pfile_info->AllocationSize =
+ cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
+ pfile_info->EndOfFile = pfile_info->AllocationSize;
+ pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
+ }
+ }
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto OldOpenRetry;
+ return rc;
+}
+
+int
+CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const int openDisposition,
+ const int access_flags, const int create_options, __u16 *netfid,
+ int *pOplock, FILE_ALL_INFO *pfile_info,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = -EACCES;
+ OPEN_REQ *pSMB = NULL;
+ OPEN_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len;
+ __u16 count;
+
+openRetry:
+ rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ count = 1; /* account for one byte pad to word boundary */
+ name_len =
+ cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
+ fileName, PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->NameLength = cpu_to_le16(name_len);
+ } else { /* BB improve check for buffer overruns BB */
+ count = 0; /* no pad */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ pSMB->NameLength = cpu_to_le16(name_len);
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ if (*pOplock & REQ_OPLOCK)
+ pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
+ else if (*pOplock & REQ_BATCHOPLOCK)
+ pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
+ pSMB->DesiredAccess = cpu_to_le32(access_flags);
+ pSMB->AllocationSize = 0;
+ /* set file as system file if special file such
+ as fifo and server expecting SFU style and
+ no Unix extensions */
+ if (create_options & CREATE_OPTION_SPECIAL)
+ pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
+ else
+ pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
+
+ /* XP does not handle ATTR_POSIX_SEMANTICS */
+ /* but it helps speed up case sensitive checks for other
+ servers such as Samba */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
+
+ if (create_options & CREATE_OPTION_READONLY)
+ pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
+
+ pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
+ pSMB->CreateDisposition = cpu_to_le32(openDisposition);
+ pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
+ /* BB Expirement with various impersonation levels and verify */
+ pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
+ pSMB->SecurityFlags =
+ SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
+
+ count += name_len;
+ pSMB->hdr.smb_buf_length += count;
+
+ pSMB->ByteCount = cpu_to_le16(count);
+ /* long_op set to 1 to allow for oplock break timeouts */
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
+ cifs_stats_inc(&tcon->num_opens);
+ if (rc) {
+ cFYI(1, ("Error in Open = %d", rc));
+ } else {
+ *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
+ *netfid = pSMBr->Fid; /* cifs fid stays in le */
+ /* Let caller know file was created so we can set the mode. */
+ /* Do we care about the CreateAction in any other cases? */
+ if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
+ *pOplock |= CIFS_CREATE_ACTION;
+ if (pfile_info) {
+ memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
+ 36 /* CreationTime to Attributes */);
+ /* the file_info buf is endian converted by caller */
+ pfile_info->AllocationSize = pSMBr->AllocationSize;
+ pfile_info->EndOfFile = pSMBr->EndOfFile;
+ pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
+ }
+ }
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto openRetry;
+ return rc;
+}
+
+int
+CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
+ const unsigned int count, const __u64 lseek, unsigned int *nbytes,
+ char **buf, int *pbuf_type)
+{
+ int rc = -EACCES;
+ READ_REQ *pSMB = NULL;
+ READ_RSP *pSMBr = NULL;
+ char *pReadData = NULL;
+ int wct;
+ int resp_buf_type = 0;
+ struct kvec iov[1];
+
+ cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 12;
+ else
+ wct = 10; /* old style read */
+
+ *nbytes = 0;
+ rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
+ if (wct == 12)
+ pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
+ else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
+ return -EIO;
+
+ pSMB->Remaining = 0;
+ pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
+ pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
+ if (wct == 12)
+ pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
+ else {
+ /* old style read */
+ struct smb_com_readx_req *pSMBW =
+ (struct smb_com_readx_req *)pSMB;
+ pSMBW->ByteCount = 0;
+ }
+
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
+ rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
+ &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
+ cifs_stats_inc(&tcon->num_reads);
+ pSMBr = (READ_RSP *)iov[0].iov_base;
+ if (rc) {
+ cERROR(1, ("Send error in read = %d", rc));
+ } else {
+ int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
+ data_length = data_length << 16;
+ data_length += le16_to_cpu(pSMBr->DataLength);
+ *nbytes = data_length;
+
+ /*check that DataLength would not go beyond end of SMB */
+ if ((data_length > CIFSMaxBufSize)
+ || (data_length > count)) {
+ cFYI(1, ("bad length %d for count %d",
+ data_length, count));
+ rc = -EIO;
+ *nbytes = 0;
+ } else {
+ pReadData = (char *) (&pSMBr->hdr.Protocol) +
+ le16_to_cpu(pSMBr->DataOffset);
+/* if (rc = copy_to_user(buf, pReadData, data_length)) {
+ cERROR(1,("Faulting on read rc = %d",rc));
+ rc = -EFAULT;
+ }*/ /* can not use copy_to_user when using page cache*/
+ if (*buf)
+ memcpy(*buf, pReadData, data_length);
+ }
+ }
+
+/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
+ if (*buf) {
+ if (resp_buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(iov[0].iov_base);
+ else if (resp_buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(iov[0].iov_base);
+ } else if (resp_buf_type != CIFS_NO_BUFFER) {
+ /* return buffer to caller to free */
+ *buf = iov[0].iov_base;
+ if (resp_buf_type == CIFS_SMALL_BUFFER)
+ *pbuf_type = CIFS_SMALL_BUFFER;
+ else if (resp_buf_type == CIFS_LARGE_BUFFER)
+ *pbuf_type = CIFS_LARGE_BUFFER;
+ } /* else no valid buffer on return - leave as null */
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+ return rc;
+}
+
+
+int
+CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 offset, unsigned int *nbytes, const char *buf,
+ const char __user *ubuf, const int long_op)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ WRITE_RSP *pSMBr = NULL;
+ int bytes_returned, wct;
+ __u32 bytes_sent;
+ __u16 byte_count;
+
+ /* cFYI(1, ("write at %lld %d bytes", offset, count));*/
+ if (tcon->ses == NULL)
+ return -ECONNABORTED;
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 14;
+ else
+ wct = 12;
+
+ rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ if (wct == 14)
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+ else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
+ return -EIO;
+
+ pSMB->Reserved = 0xFFFFFFFF;
+ pSMB->WriteMode = 0;
+ pSMB->Remaining = 0;
+
+ /* Can increase buffer size if buffer is big enough in some cases ie we
+ can send more if LARGE_WRITE_X capability returned by the server and if
+ our buffer is big enough or if we convert to iovecs on socket writes
+ and eliminate the copy to the CIFS buffer */
+ if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
+ bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
+ } else {
+ bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
+ & ~0xFF;
+ }
+
+ if (bytes_sent > count)
+ bytes_sent = count;
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+ if (buf)
+ memcpy(pSMB->Data, buf, bytes_sent);
+ else if (ubuf) {
+ if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
+ cifs_buf_release(pSMB);
+ return -EFAULT;
+ }
+ } else if (count != 0) {
+ /* No buffer */
+ cifs_buf_release(pSMB);
+ return -EINVAL;
+ } /* else setting file size with write of zero bytes */
+ if (wct == 14)
+ byte_count = bytes_sent + 1; /* pad */
+ else /* wct == 12 */
+ byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
+
+ pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
+ pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
+ pSMB->hdr.smb_buf_length += byte_count;
+
+ if (wct == 14)
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ else { /* old style write has byte count 4 bytes earlier
+ so 4 bytes pad */
+ struct smb_com_writex_req *pSMBW =
+ (struct smb_com_writex_req *)pSMB;
+ pSMBW->ByteCount = cpu_to_le16(byte_count);
+ }
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
+ cifs_stats_inc(&tcon->num_writes);
+ if (rc) {
+ cFYI(1, ("Send error in write = %d", rc));
+ *nbytes = 0;
+ } else {
+ *nbytes = le16_to_cpu(pSMBr->CountHigh);
+ *nbytes = (*nbytes) << 16;
+ *nbytes += le16_to_cpu(pSMBr->Count);
+ }
+
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, const unsigned int count,
+ const __u64 offset, unsigned int *nbytes, struct kvec *iov,
+ int n_vec, const int long_op)
+{
+ int rc = -EACCES;
+ WRITE_REQ *pSMB = NULL;
+ int wct;
+ int smb_hdr_len;
+ int resp_buf_type = 0;
+
+ cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
+
+ if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ wct = 14;
+ else
+ wct = 12;
+ rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+ /* tcon and ses pointer are checked in smb_init */
+ if (tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = netfid;
+ pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
+ if (wct == 14)
+ pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
+ else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
+ return -EIO;
+ pSMB->Reserved = 0xFFFFFFFF;
+ pSMB->WriteMode = 0;
+ pSMB->Remaining = 0;
+
+ pSMB->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+
+ pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
+ pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
+ smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
+ if (wct == 14)
+ pSMB->hdr.smb_buf_length += count+1;
+ else /* wct == 12 */
+ pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
+ if (wct == 14)
+ pSMB->ByteCount = cpu_to_le16(count + 1);
+ else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
+ struct smb_com_writex_req *pSMBW =
+ (struct smb_com_writex_req *)pSMB;
+ pSMBW->ByteCount = cpu_to_le16(count + 5);
+ }
+ iov[0].iov_base = pSMB;
+ if (wct == 14)
+ iov[0].iov_len = smb_hdr_len + 4;
+ else /* wct == 12 pad bigger by four bytes */
+ iov[0].iov_len = smb_hdr_len + 8;
+
+
+ rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
+ long_op);
+ cifs_stats_inc(&tcon->num_writes);
+ if (rc) {
+ cFYI(1, ("Send error Write2 = %d", rc));
+ *nbytes = 0;
+ } else if (resp_buf_type == 0) {
+ /* presumably this can not happen, but best to be safe */
+ rc = -EIO;
+ *nbytes = 0;
+ } else {
+ WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
+ *nbytes = le16_to_cpu(pSMBr->CountHigh);
+ *nbytes = (*nbytes) << 16;
+ *nbytes += le16_to_cpu(pSMBr->Count);
+ }
+
+/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
+ if (resp_buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(iov[0].iov_base);
+ else if (resp_buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(iov[0].iov_base);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+
+int
+CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
+ const __u16 smb_file_id, const __u64 len,
+ const __u64 offset, const __u32 numUnlock,
+ const __u32 numLock, const __u8 lockType, const bool waitFlag)
+{
+ int rc = 0;
+ LOCK_REQ *pSMB = NULL;
+/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
+ int bytes_returned;
+ int timeout = 0;
+ __u16 count;
+
+ cFYI(1, ("CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock));
+ rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
+ timeout = CIFS_ASYNC_OP; /* no response expected */
+ pSMB->Timeout = 0;
+ } else if (waitFlag) {
+ timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
+ pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
+ } else {
+ pSMB->Timeout = 0;
+ }
+
+ pSMB->NumberOfLocks = cpu_to_le16(numLock);
+ pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
+ pSMB->LockType = lockType;
+ pSMB->AndXCommand = 0xFF; /* none */
+ pSMB->Fid = smb_file_id; /* netfid stays le */
+
+ if ((numLock != 0) || (numUnlock != 0)) {
+ pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
+ /* BB where to store pid high? */
+ pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
+ pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
+ pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
+ pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
+ count = sizeof(LOCKING_ANDX_RANGE);
+ } else {
+ /* oplock break */
+ count = 0;
+ }
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMB, &bytes_returned);
+ cifs_small_buf_release(pSMB);
+ } else {
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
+ timeout);
+ /* SMB buffer freed by function above */
+ }
+ cifs_stats_inc(&tcon->num_locks);
+ if (rc)
+ cFYI(1, ("Send error in Lock = %d", rc));
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+ return rc;
+}
+
+int
+CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
+ const __u16 smb_file_id, const int get_flag, const __u64 len,
+ struct file_lock *pLockData, const __u16 lock_type,
+ const bool waitFlag)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
+ struct cifs_posix_lock *parm_data;
+ int rc = 0;
+ int timeout = 0;
+ int bytes_returned = 0;
+ int resp_buf_type = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+ struct kvec iov[1];
+
+ cFYI(1, ("Posix Lock"));
+
+ if (pLockData == NULL)
+ return -EINVAL;
+
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ count = sizeof(struct cifs_posix_lock);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ if (get_flag)
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+ else
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ parm_data = (struct cifs_posix_lock *)
+ (((char *) &pSMB->hdr.Protocol) + offset);
+
+ parm_data->lock_type = cpu_to_le16(lock_type);
+ if (waitFlag) {
+ timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
+ parm_data->lock_flags = cpu_to_le16(1);
+ pSMB->Timeout = cpu_to_le32(-1);
+ } else
+ pSMB->Timeout = 0;
+
+ parm_data->pid = cpu_to_le32(current->tgid);
+ parm_data->start = cpu_to_le64(pLockData->fl_start);
+ parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
+
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = smb_file_id;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned);
+ } else {
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
+ rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
+ &resp_buf_type, timeout);
+ pSMB = NULL; /* request buf already freed by SendReceive2. Do
+ not try to free it twice below on exit */
+ pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
+ }
+
+ if (rc) {
+ cFYI(1, ("Send error in Posix Lock = %d", rc));
+ } else if (get_flag) {
+ /* lock structure can be returned on get */
+ __u16 data_offset;
+ __u16 data_count;
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
+ rc = -EIO; /* bad smb */
+ goto plk_err_exit;
+ }
+ if (pLockData == NULL) {
+ rc = -EINVAL;
+ goto plk_err_exit;
+ }
+ data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ data_count = le16_to_cpu(pSMBr->t2.DataCount);
+ if (data_count < sizeof(struct cifs_posix_lock)) {
+ rc = -EIO;
+ goto plk_err_exit;
+ }
+ parm_data = (struct cifs_posix_lock *)
+ ((char *)&pSMBr->hdr.Protocol + data_offset);
+ if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
+ pLockData->fl_type = F_UNLCK;
+ }
+
+plk_err_exit:
+ if (pSMB)
+ cifs_small_buf_release(pSMB);
+
+ if (resp_buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(iov[0].iov_base);
+ else if (resp_buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(iov[0].iov_base);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+
+int
+CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
+{
+ int rc = 0;
+ CLOSE_REQ *pSMB = NULL;
+ cFYI(1, ("In CIFSSMBClose"));
+
+/* do not retry on dead session on close */
+ rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
+ if (rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMB->FileID = (__u16) smb_file_id;
+ pSMB->LastWriteTime = 0xFFFFFFFF;
+ pSMB->ByteCount = 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ cifs_stats_inc(&tcon->num_closes);
+ if (rc) {
+ if (rc != -EINTR) {
+ /* EINTR is expected when user ctl-c to kill app */
+ cERROR(1, ("Send error in Close = %d", rc));
+ }
+ }
+
+ /* Since session is dead, file will be closed on server already */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ RENAME_REQ *pSMB = NULL;
+ RENAME_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+ __u16 count;
+
+ cFYI(1, ("In CIFSSMBRename"));
+renameRetry:
+ rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0x04; /* pad */
+ /* protocol requires ASCII signature byte on Unicode string */
+ pSMB->OldFileName[name_len + 1] = 0x00;
+ name_len2 =
+ cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
+ toName, PATH_MAX, nls_codepage, remap);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, PATH_MAX);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ count = 1 /* 1st signature byte */ + name_len + name_len2;
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_renames);
+ if (rc)
+ cFYI(1, ("Send error in rename = %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto renameRetry;
+
+ return rc;
+}
+
+int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
+ int netfid, const char *target_name,
+ const struct nls_table *nls_codepage, int remap)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
+ struct set_file_rename *rename_info;
+ char *data_offset;
+ char dummy_string[30];
+ int rc = 0;
+ int bytes_returned = 0;
+ int len_of_str;
+ __u16 params, param_offset, offset, count, byte_count;
+
+ cFYI(1, ("Rename to File by handle"));
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ rename_info = (struct set_file_rename *) data_offset;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ /* construct random name ".cifs_tmp<inodenum><mid>" */
+ rename_info->overwrite = cpu_to_le32(1);
+ rename_info->root_fid = 0;
+ /* unicode only call */
+ if (target_name == NULL) {
+ sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
+ len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
+ dummy_string, 24, nls_codepage, remap);
+ } else {
+ len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
+ target_name, PATH_MAX, nls_codepage,
+ remap);
+ }
+ rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
+ count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
+ byte_count += count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->Fid = netfid;
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&pTcon->num_t2renames);
+ if (rc)
+ cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
+ const __u16 target_tid, const char *toName, const int flags,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ COPY_REQ *pSMB = NULL;
+ COPY_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+ __u16 count;
+
+ cFYI(1, ("In CIFSSMBCopy"));
+copyRetry:
+ rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->BufferFormat = 0x04;
+ pSMB->Tid2 = target_tid;
+
+ pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
+ fromName, PATH_MAX, nls_codepage,
+ remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0x04; /* pad */
+ /* protocol requires ASCII signature byte on Unicode string */
+ pSMB->OldFileName[name_len + 1] = 0x00;
+ name_len2 =
+ cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
+ toName, PATH_MAX, nls_codepage, remap);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, PATH_MAX);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ count = 1 /* 1st signature byte */ + name_len + name_len2;
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in copy = %d with %d files copied",
+ rc, le16_to_cpu(pSMBr->CopyCount)));
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto copyRetry;
+
+ return rc;
+}
+
+int
+CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ char *data_offset;
+ int name_len;
+ int name_len_target;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cFYI(1, ("In Symlink Unix style"));
+createSymLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fromName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len_target =
+ cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
+ /* find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len_target++; /* trailing null */
+ name_len_target *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len_target = strnlen(toName, PATH_MAX);
+ name_len_target++; /* trailing null */
+ strncpy(data_offset, toName, name_len_target);
+ }
+
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max on data count below from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + name_len_target;
+ pSMB->DataCount = cpu_to_le16(name_len_target);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_symlinks);
+ if (rc)
+ cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto createSymLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ char *data_offset;
+ int name_len;
+ int name_len_target;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cFYI(1, ("In Create Hard link Unix style"));
+createHardLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(toName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, toName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len_target =
+ cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
+ nls_codepage, remap);
+ name_len_target++; /* trailing null */
+ name_len_target *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len_target = strnlen(fromName, PATH_MAX);
+ name_len_target++; /* trailing null */
+ strncpy(data_offset, fromName, name_len_target);
+ }
+
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max on data count below from sess*/
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + name_len_target;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->DataCount = cpu_to_le16(name_len_target);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_hardlinks);
+ if (rc)
+ cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto createHardLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
+ const char *fromName, const char *toName,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ NT_RENAME_REQ *pSMB = NULL;
+ RENAME_RSP *pSMBr = NULL;
+ int bytes_returned;
+ int name_len, name_len2;
+ __u16 count;
+
+ cFYI(1, ("In CIFSCreateHardLink"));
+winCreateHardLinkRetry:
+
+ rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
+ pSMB->ClusterCount = 0;
+
+ pSMB->BufferFormat = 0x04;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->OldFileName[name_len] = 0; /* pad */
+ pSMB->OldFileName[name_len + 1] = 0x04;
+ name_len2 =
+ cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
+ toName, PATH_MAX, nls_codepage, remap);
+ name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
+ name_len2 *= 2; /* convert to bytes */
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fromName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->OldFileName, fromName, name_len);
+ name_len2 = strnlen(toName, PATH_MAX);
+ name_len2++; /* trailing null */
+ pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
+ strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ name_len2++; /* trailing null */
+ name_len2++; /* signature byte */
+ }
+
+ count = 1 /* string type byte */ + name_len + name_len2;
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_hardlinks);
+ if (rc)
+ cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto winCreateHardLinkRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *symlinkinfo, const int buflen,
+ const struct nls_table *nls_codepage)
+{
+/* SMB_QUERY_FILE_UNIX_LINK */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
+
+querySymLinkRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
+ } else {
+ /* decode response */
+
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc || (pSMBr->ByteCount < 2))
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = UniStrnlen((wchar_t *) ((char *)
+ &pSMBr->hdr.Protocol + data_offset),
+ min_t(const int, buflen, count) / 2);
+ /* BB FIXME investigate remapping reserved chars here */
+ cifs_strfromUCS_le(symlinkinfo,
+ (__le16 *) ((char *)&pSMBr->hdr.Protocol
+ + data_offset),
+ name_len, nls_codepage);
+ } else {
+ strncpy(symlinkinfo,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset,
+ min_t(const int, buflen, count));
+ }
+ symlinkinfo[buflen] = 0;
+ /* just in case so calling code does not go off the end of buffer */
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto querySymLinkRetry;
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+/* Initialize NT TRANSACT SMB into small smb request buffer.
+ This assumes that all NT TRANSACTS that we init here have
+ total parm and data under about 400 bytes (to fit in small cifs
+ buffer size), which is the case so far, it easily fits. NB:
+ Setup words themselves and ByteCount
+ MaxSetupCount (size of returned setup area) and
+ MaxParameterCount (returned parms size) must be set by caller */
+static int
+smb_init_nttransact(const __u16 sub_command, const int setup_count,
+ const int parm_len, struct cifsTconInfo *tcon,
+ void **ret_buf)
+{
+ int rc;
+ __u32 temp_offset;
+ struct smb_com_ntransact_req *pSMB;
+
+ rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
+ (void **)&pSMB);
+ if (rc)
+ return rc;
+ *ret_buf = (void *)pSMB;
+ pSMB->Reserved = 0;
+ pSMB->TotalParameterCount = cpu_to_le32(parm_len);
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+ MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->DataCount = pSMB->TotalDataCount;
+ temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
+ (setup_count * 2) - 4 /* for rfc1001 length itself */;
+ pSMB->ParameterOffset = cpu_to_le32(temp_offset);
+ pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
+ pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
+ pSMB->SubCommand = cpu_to_le16(sub_command);
+ return 0;
+}
+
+static int
+validate_ntransact(char *buf, char **ppparm, char **ppdata,
+ __u32 *pparmlen, __u32 *pdatalen)
+{
+ char *end_of_smb;
+ __u32 data_count, data_offset, parm_count, parm_offset;
+ struct smb_com_ntransact_rsp *pSMBr;
+
+ *pdatalen = 0;
+ *pparmlen = 0;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ pSMBr = (struct smb_com_ntransact_rsp *)buf;
+
+ /* ByteCount was converted from little endian in SendReceive */
+ end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
+ (char *)&pSMBr->ByteCount;
+
+ data_offset = le32_to_cpu(pSMBr->DataOffset);
+ data_count = le32_to_cpu(pSMBr->DataCount);
+ parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
+ parm_count = le32_to_cpu(pSMBr->ParameterCount);
+
+ *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
+ *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
+
+ /* should we also check that parm and data areas do not overlap? */
+ if (*ppparm > end_of_smb) {
+ cFYI(1, ("parms start after end of smb"));
+ return -EINVAL;
+ } else if (parm_count + *ppparm > end_of_smb) {
+ cFYI(1, ("parm end after end of smb"));
+ return -EINVAL;
+ } else if (*ppdata > end_of_smb) {
+ cFYI(1, ("data starts after end of smb"));
+ return -EINVAL;
+ } else if (data_count + *ppdata > end_of_smb) {
+ cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
+ *ppdata, data_count, (data_count + *ppdata),
+ end_of_smb, pSMBr));
+ return -EINVAL;
+ } else if (parm_count + data_count > pSMBr->ByteCount) {
+ cFYI(1, ("parm count and data count larger than SMB"));
+ return -EINVAL;
+ }
+ *pdatalen = data_count;
+ *pparmlen = parm_count;
+ return 0;
+}
+#endif /* CIFS_EXPERIMENTAL */
+
+int
+CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *symlinkinfo, const int buflen, __u16 fid,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ struct smb_com_transaction_ioctl_req *pSMB;
+ struct smb_com_transaction_ioctl_rsp *pSMBr;
+
+ cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ /* BB find exact data count max from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+ MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4;
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
+ pSMB->IsFsctl = 1; /* FSCTL */
+ pSMB->IsRootFlag = 0;
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
+ } else { /* decode response */
+ __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
+ __u32 data_count = le32_to_cpu(pSMBr->DataCount);
+ if ((pSMBr->ByteCount < 2) || (data_offset > 512))
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ if (data_count && (data_count < 2048)) {
+ char *end_of_smb = 2 /* sizeof byte count */ +
+ pSMBr->ByteCount +
+ (char *)&pSMBr->ByteCount;
+
+ struct reparse_data *reparse_buf =
+ (struct reparse_data *)
+ ((char *)&pSMBr->hdr.Protocol
+ + data_offset);
+ if ((char *)reparse_buf >= end_of_smb) {
+ rc = -EIO;
+ goto qreparse_out;
+ }
+ if ((reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset +
+ reparse_buf->TargetNameLen) >
+ end_of_smb) {
+ cFYI(1, ("reparse buf beyond SMB"));
+ rc = -EIO;
+ goto qreparse_out;
+ }
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len = UniStrnlen((wchar_t *)
+ (reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset),
+ min(buflen/2,
+ reparse_buf->TargetNameLen / 2));
+ cifs_strfromUCS_le(symlinkinfo,
+ (__le16 *) (reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset),
+ name_len, nls_codepage);
+ } else { /* ASCII names */
+ strncpy(symlinkinfo,
+ reparse_buf->LinkNamesBuf +
+ reparse_buf->TargetNameOffset,
+ min_t(const int, buflen,
+ reparse_buf->TargetNameLen));
+ }
+ } else {
+ rc = -EIO;
+ cFYI(1, ("Invalid return data count on "
+ "get reparse info ioctl"));
+ }
+ symlinkinfo[buflen] = 0; /* just in case so the caller
+ does not go off the end of the buffer */
+ cFYI(1, ("readlink result - %s", symlinkinfo));
+ }
+ }
+qreparse_out:
+ cifs_buf_release(pSMB);
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+#ifdef CONFIG_CIFS_POSIX
+
+/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
+static void cifs_convert_ace(posix_acl_xattr_entry *ace,
+ struct cifs_posix_ace *cifs_ace)
+{
+ /* u8 cifs fields do not need le conversion */
+ ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
+ ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
+ ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
+ /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
+
+ return;
+}
+
+/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
+static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
+ const int acl_type, const int size_of_data_area)
+{
+ int size = 0;
+ int i;
+ __u16 count;
+ struct cifs_posix_ace *pACE;
+ struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
+ posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
+
+ if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
+ return -EOPNOTSUPP;
+
+ if (acl_type & ACL_TYPE_ACCESS) {
+ count = le16_to_cpu(cifs_acl->access_entry_count);
+ pACE = &cifs_acl->ace_array[0];
+ size = sizeof(struct cifs_posix_acl);
+ size += sizeof(struct cifs_posix_ace) * count;
+ /* check if we would go beyond end of SMB */
+ if (size_of_data_area < size) {
+ cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
+ size_of_data_area, size));
+ return -EINVAL;
+ }
+ } else if (acl_type & ACL_TYPE_DEFAULT) {
+ count = le16_to_cpu(cifs_acl->access_entry_count);
+ size = sizeof(struct cifs_posix_acl);
+ size += sizeof(struct cifs_posix_ace) * count;
+/* skip past access ACEs to get to default ACEs */
+ pACE = &cifs_acl->ace_array[count];
+ count = le16_to_cpu(cifs_acl->default_entry_count);
+ size += sizeof(struct cifs_posix_ace) * count;
+ /* check if we would go beyond end of SMB */
+ if (size_of_data_area < size)
+ return -EINVAL;
+ } else {
+ /* illegal type */
+ return -EINVAL;
+ }
+
+ size = posix_acl_xattr_size(count);
+ if ((buflen == 0) || (local_acl == NULL)) {
+ /* used to query ACL EA size */
+ } else if (size > buflen) {
+ return -ERANGE;
+ } else /* buffer big enough */ {
+ local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
+ for (i = 0; i < count ; i++) {
+ cifs_convert_ace(&local_acl->a_entries[i], pACE);
+ pACE++;
+ }
+ }
+ return size;
+}
+
+static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
+ const posix_acl_xattr_entry *local_ace)
+{
+ __u16 rc = 0; /* 0 = ACL converted ok */
+
+ cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
+ cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
+ /* BB is there a better way to handle the large uid? */
+ if (local_ace->e_id == cpu_to_le32(-1)) {
+ /* Probably no need to le convert -1 on any arch but can not hurt */
+ cifs_ace->cifs_uid = cpu_to_le64(-1);
+ } else
+ cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
+ /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
+ return rc;
+}
+
+/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
+static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
+ const int buflen, const int acl_type)
+{
+ __u16 rc = 0;
+ struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
+ posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
+ int count;
+ int i;
+
+ if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
+ return 0;
+
+ count = posix_acl_xattr_count((size_t)buflen);
+ cFYI(1, ("setting acl with %d entries from buf of length %d and "
+ "version of %d",
+ count, buflen, le32_to_cpu(local_acl->a_version)));
+ if (le32_to_cpu(local_acl->a_version) != 2) {
+ cFYI(1, ("unknown POSIX ACL version %d",
+ le32_to_cpu(local_acl->a_version)));
+ return 0;
+ }
+ cifs_acl->version = cpu_to_le16(1);
+ if (acl_type == ACL_TYPE_ACCESS)
+ cifs_acl->access_entry_count = cpu_to_le16(count);
+ else if (acl_type == ACL_TYPE_DEFAULT)
+ cifs_acl->default_entry_count = cpu_to_le16(count);
+ else {
+ cFYI(1, ("unknown ACL type %d", acl_type));
+ return 0;
+ }
+ for (i = 0; i < count; i++) {
+ rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
+ &local_acl->a_entries[i]);
+ if (rc != 0) {
+ /* ACE not converted */
+ break;
+ }
+ }
+ if (rc == 0) {
+ rc = (__u16)(count * sizeof(struct cifs_posix_ace));
+ rc += sizeof(struct cifs_posix_acl);
+ /* BB add check to make sure ACL does not overflow SMB */
+ }
+ return rc;
+}
+
+int
+CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *acl_inf, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* SMB_QUERY_POSIX_ACL */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
+
+queryAclRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->FileName[name_len] = 0;
+ pSMB->FileName[name_len+1] = 0;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_qpi_req,
+ InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_acl_get);
+ if (rc) {
+ cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
+ } else {
+ /* decode response */
+
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc || (pSMBr->ByteCount < 2))
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ rc = cifs_copy_posix_acl(acl_inf,
+ (char *)&pSMBr->hdr.Protocol+data_offset,
+ buflen, acl_type, count);
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto queryAclRetry;
+ return rc;
+}
+
+int
+CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *fileName,
+ const char *local_acl, const int buflen,
+ const int acl_type,
+ const struct nls_table *nls_codepage, int remap)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ char *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count, data_count, param_offset, offset;
+
+ cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
+setAclRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB size from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+
+ /* convert to on the wire format for POSIX ACL */
+ data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
+
+ if (data_count == 0) {
+ rc = -EOPNOTSUPP;
+ goto setACLerrorExit;
+ }
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
+ byte_count = 3 /* pad */ + params + data_count;
+ pSMB->DataCount = cpu_to_le16(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("Set POSIX ACL returned %d", rc));
+
+setACLerrorExit:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto setAclRetry;
+ return rc;
+}
+
+/* BB fix tabs in this function FIXME BB */
+int
+CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
+ const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
+{
+ int rc = 0;
+ struct smb_t2_qfi_req *pSMB = NULL;
+ struct smb_t2_qfi_rsp *pSMBr = NULL;
+ int bytes_returned;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In GetExtAttr"));
+ if (tcon == NULL)
+ return -ENODEV;
+
+GetExtAttrRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2 /* level */ + 2 /* fid */;
+ pSMB->t2.TotalDataCount = 0;
+ pSMB->t2.MaxParameterCount = cpu_to_le16(4);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->t2.MaxDataCount = cpu_to_le16(4000);
+ pSMB->t2.MaxSetupCount = 0;
+ pSMB->t2.Reserved = 0;
+ pSMB->t2.Flags = 0;
+ pSMB->t2.Timeout = 0;
+ pSMB->t2.Reserved2 = 0;
+ pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
+ Fid) - 4);
+ pSMB->t2.DataCount = 0;
+ pSMB->t2.DataOffset = 0;
+ pSMB->t2.SetupCount = 1;
+ pSMB->t2.Reserved3 = 0;
+ pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->t2.TotalParameterCount = cpu_to_le16(params);
+ pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
+ pSMB->Pad = 0;
+ pSMB->Fid = netfid;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->t2.ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("error %d in GetExtAttr", rc));
+ } else {
+ /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc || (pSMBr->ByteCount < 2))
+ /* BB also check enough total bytes returned */
+ /* If rc should we check for EOPNOSUPP and
+ disable the srvino flag? or in caller? */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ struct file_chattr_info *pfinfo;
+ /* BB Do we need a cast or hash here ? */
+ if (count != 16) {
+ cFYI(1, ("Illegal size ret in GetExtAttr"));
+ rc = -EIO;
+ goto GetExtAttrOut;
+ }
+ pfinfo = (struct file_chattr_info *)
+ (data_offset + (char *) &pSMBr->hdr.Protocol);
+ *pExtAttrBits = le64_to_cpu(pfinfo->mode);
+ *pMask = le64_to_cpu(pfinfo->mask);
+ }
+ }
+GetExtAttrOut:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto GetExtAttrRetry;
+ return rc;
+}
+
+#endif /* CONFIG_POSIX */
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+/* Get Security Descriptor (by handle) from remote server for a file or dir */
+int
+CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
+ struct cifs_ntsd **acl_inf, __u32 *pbuflen)
+{
+ int rc = 0;
+ int buf_type = 0;
+ QUERY_SEC_DESC_REQ *pSMB;
+ struct kvec iov[1];
+
+ cFYI(1, ("GetCifsACL"));
+
+ *pbuflen = 0;
+ *acl_inf = NULL;
+
+ rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
+ 8 /* parm len */, tcon, (void **) &pSMB);
+ if (rc)
+ return rc;
+
+ pSMB->MaxParameterCount = cpu_to_le32(4);
+ /* BB TEST with big acls that might need to be e.g. larger than 16K */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
+ CIFS_ACL_DACL);
+ pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
+ pSMB->hdr.smb_buf_length += 11;
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
+
+ rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
+ CIFS_STD_OP);
+ cifs_stats_inc(&tcon->num_acl_get);
+ if (rc) {
+ cFYI(1, ("Send error in QuerySecDesc = %d", rc));
+ } else { /* decode response */
+ __le32 *parm;
+ __u32 parm_len;
+ __u32 acl_len;
+ struct smb_com_ntransact_rsp *pSMBr;
+ char *pdata;
+
+/* validate_nttransact */
+ rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
+ &pdata, &parm_len, pbuflen);
+ if (rc)
+ goto qsec_out;
+ pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
+
+ cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
+
+ if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
+ rc = -EIO; /* bad smb */
+ *pbuflen = 0;
+ goto qsec_out;
+ }
+
+/* BB check that data area is minimum length and as big as acl_len */
+
+ acl_len = le32_to_cpu(*parm);
+ if (acl_len != *pbuflen) {
+ cERROR(1, ("acl length %d does not match %d",
+ acl_len, *pbuflen));
+ if (*pbuflen > acl_len)
+ *pbuflen = acl_len;
+ }
+
+ /* check if buffer is big enough for the acl
+ header followed by the smallest SID */
+ if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
+ (*pbuflen >= 64 * 1024)) {
+ cERROR(1, ("bad acl length %d", *pbuflen));
+ rc = -EINVAL;
+ *pbuflen = 0;
+ } else {
+ *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
+ if (*acl_inf == NULL) {
+ *pbuflen = 0;
+ rc = -ENOMEM;
+ }
+ memcpy(*acl_inf, pdata, *pbuflen);
+ }
+ }
+qsec_out:
+ if (buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(iov[0].iov_base);
+ else if (buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(iov[0].iov_base);
+/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
+ return rc;
+}
+
+int
+CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
+ struct cifs_ntsd *pntsd, __u32 acllen)
+{
+ __u16 byte_count, param_count, data_count, param_offset, data_offset;
+ int rc = 0;
+ int bytes_returned = 0;
+ SET_SEC_DESC_REQ *pSMB = NULL;
+ NTRANSACT_RSP *pSMBr = NULL;
+
+setCifsAclRetry:
+ rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return (rc);
+
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+
+ param_count = 8;
+ param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
+ data_count = acllen;
+ data_offset = param_offset + param_count;
+ byte_count = 3 /* pad */ + param_count;
+
+ pSMB->DataCount = cpu_to_le32(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->MaxParameterCount = cpu_to_le32(4);
+ pSMB->MaxDataCount = cpu_to_le32(16384);
+ pSMB->ParameterCount = cpu_to_le32(param_count);
+ pSMB->ParameterOffset = cpu_to_le32(param_offset);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->DataOffset = cpu_to_le32(data_offset);
+ pSMB->SetupCount = 0;
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
+ pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
+
+ pSMB->Fid = fid; /* file handle always le */
+ pSMB->Reserved2 = 0;
+ pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
+
+ if (pntsd && acllen) {
+ memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
+ (char *) pntsd,
+ acllen);
+ pSMB->hdr.smb_buf_length += (byte_count + data_count);
+
+ } else
+ pSMB->hdr.smb_buf_length += byte_count;
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+
+ cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
+ if (rc)
+ cFYI(1, ("Set CIFS ACL returned %d", rc));
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto setCifsAclRetry;
+
+ return (rc);
+}
+
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+
+/* Legacy Query Path Information call for lookup to old servers such
+ as Win9x/WinME */
+int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_ALL_INFO *pFinfo,
+ const struct nls_table *nls_codepage, int remap)
+{
+ QUERY_INFORMATION_REQ *pSMB;
+ QUERY_INFORMATION_RSP *pSMBr;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In SMBQPath path %s", searchName));
+QInfRetry:
+ rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else {
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+ pSMB->BufferFormat = 0x04;
+ name_len++; /* account for buffer type byte */
+ pSMB->hdr.smb_buf_length += (__u16) name_len;
+ pSMB->ByteCount = cpu_to_le16(name_len);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QueryInfo = %d", rc));
+ } else if (pFinfo) {
+ struct timespec ts;
+ __u32 time = le32_to_cpu(pSMBr->last_write_time);
+
+ /* decode response */
+ /* BB FIXME - add time zone adjustment BB */
+ memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
+ ts.tv_nsec = 0;
+ ts.tv_sec = time;
+ /* decode time fields */
+ pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
+ pFinfo->LastWriteTime = pFinfo->ChangeTime;
+ pFinfo->LastAccessTime = 0;
+ pFinfo->AllocationSize =
+ cpu_to_le64(le32_to_cpu(pSMBr->size));
+ pFinfo->EndOfFile = pFinfo->AllocationSize;
+ pFinfo->Attributes =
+ cpu_to_le32(le16_to_cpu(pSMBr->attr));
+ } else
+ rc = -EIO; /* bad buffer passed in */
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QInfRetry;
+
+ return rc;
+}
+
+
+
+
+int
+CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_ALL_INFO *pFindData,
+ int legacy /* old style infolevel */,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* level 263 SMB_QUERY_FILE_ALL_INFO */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+
+/* cFYI(1, ("In QPathInfo path %s", searchName)); */
+QPathInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ if (legacy)
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QPathInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc) /* BB add auto retry on EOPNOTSUPP? */
+ rc = -EIO;
+ else if (!legacy && (pSMBr->ByteCount < 40))
+ rc = -EIO; /* bad smb */
+ else if (legacy && (pSMBr->ByteCount < 24))
+ rc = -EIO; /* 24 or 26 expected but we do not read
+ last field */
+ else if (pFindData) {
+ int size;
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+
+ /* On legacy responses we do not read the last field,
+ EAsize, fortunately since it varies by subdialect and
+ also note it differs on Set vs. Get, ie two bytes or 4
+ bytes depending but we don't care here */
+ if (legacy)
+ size = sizeof(FILE_INFO_STANDARD);
+ else
+ size = sizeof(FILE_ALL_INFO);
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset, size);
+ } else
+ rc = -ENOMEM;
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QPathInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ FILE_UNIX_BASIC_INFO *pFindData,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* SMB_QUERY_FILE_UNIX_BASIC */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned = 0;
+ int name_len;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
+UnixQPathInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QPathInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
+ cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
+ "Unix Extensions can be disabled on mount "
+ "by specifying the nosfu mount option."));
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset,
+ sizeof(FILE_UNIX_BASIC_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto UnixQPathInfoRetry;
+
+ return rc;
+}
+
+/* xid, tcon, searchName and codepage are input parms, rest are returned */
+int
+CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
+ const char *searchName,
+ const struct nls_table *nls_codepage,
+ __u16 *pnetfid,
+ struct cifs_search_info *psrch_inf, int remap, const char dirsep)
+{
+/* level 257 SMB_ */
+ TRANSACTION2_FFIRST_REQ *pSMB = NULL;
+ TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
+ T2_FFIRST_RSP_PARMS *parms;
+ int rc = 0;
+ int bytes_returned = 0;
+ int name_len;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In FindFirst for %s", searchName));
+
+findFirstRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ /* We can not add the asterik earlier in case
+ it got remapped to 0xF03A as if it were part of the
+ directory name instead of a wildcard */
+ name_len *= 2;
+ pSMB->FileName[name_len] = dirsep;
+ pSMB->FileName[name_len+1] = 0;
+ pSMB->FileName[name_len+2] = '*';
+ pSMB->FileName[name_len+3] = 0;
+ name_len += 4; /* now the trailing null */
+ pSMB->FileName[name_len] = 0; /* null terminate just in case */
+ pSMB->FileName[name_len+1] = 0;
+ name_len += 2;
+ } else { /* BB add check for overrun of SMB buf BB */
+ name_len = strnlen(searchName, PATH_MAX);
+/* BB fix here and in unicode clause above ie
+ if (name_len > buffersize-header)
+ free buffer exit; BB */
+ strncpy(pSMB->FileName, searchName, name_len);
+ pSMB->FileName[name_len] = dirsep;
+ pSMB->FileName[name_len+1] = '*';
+ pSMB->FileName[name_len+2] = 0;
+ name_len += 3;
+ }
+
+ params = 12 + name_len /* includes null */ ;
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(10);
+ pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
+ MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
+ - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
+ pSMB->SearchAttributes =
+ cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
+ ATTR_DIRECTORY);
+ pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
+ pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
+ CIFS_SEARCH_RETURN_RESUME);
+ pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
+
+ /* BB what should we set StorageType to? Does it matter? BB */
+ pSMB->SearchStorageType = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_ffirst);
+
+ if (rc) {/* BB add logic to retry regular search if Unix search
+ rejected unexpectedly by server */
+ /* BB Add code to handle unsupported level rc */
+ cFYI(1, ("Error in FindFirst = %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ /* BB eventually could optimize out free and realloc of buf */
+ /* for this case */
+ if (rc == -EAGAIN)
+ goto findFirstRetry;
+ } else { /* decode response */
+ /* BB remember to free buffer if error BB */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc == 0) {
+ unsigned int lnoff;
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ psrch_inf->unicode = true;
+ else
+ psrch_inf->unicode = false;
+
+ psrch_inf->ntwrk_buf_start = (char *)pSMBr;
+ psrch_inf->smallBuf = 0;
+ psrch_inf->srch_entries_start =
+ (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.DataOffset);
+ parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.ParameterOffset));
+
+ if (parms->EndofSearch)
+ psrch_inf->endOfSearch = true;
+ else
+ psrch_inf->endOfSearch = false;
+
+ psrch_inf->entries_in_buffer =
+ le16_to_cpu(parms->SearchCount);
+ psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
+ psrch_inf->entries_in_buffer;
+ lnoff = le16_to_cpu(parms->LastNameOffset);
+ if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
+ lnoff) {
+ cERROR(1, ("ignoring corrupt resume name"));
+ psrch_inf->last_entry = NULL;
+ return rc;
+ }
+
+ psrch_inf->last_entry = psrch_inf->srch_entries_start +
+ lnoff;
+
+ *pnetfid = parms->SearchHandle;
+ } else {
+ cifs_buf_release(pSMB);
+ }
+ }
+
+ return rc;
+}
+
+int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
+ __u16 searchHandle, struct cifs_search_info *psrch_inf)
+{
+ TRANSACTION2_FNEXT_REQ *pSMB = NULL;
+ TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
+ T2_FNEXT_RSP_PARMS *parms;
+ char *response_data;
+ int rc = 0;
+ int bytes_returned, name_len;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In FindNext"));
+
+ if (psrch_inf->endOfSearch)
+ return -ENOENT;
+
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 14; /* includes 2 bytes of null string, converted to LE below*/
+ byte_count = 0;
+ pSMB->TotalDataCount = 0; /* no EAs */
+ pSMB->MaxParameterCount = cpu_to_le16(8);
+ pSMB->MaxDataCount =
+ cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
+ 0xFFFFFF00);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(
+ offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
+ pSMB->SearchHandle = searchHandle; /* always kept as le */
+ pSMB->SearchCount =
+ cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
+ pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
+ pSMB->ResumeKey = psrch_inf->resume_key;
+ pSMB->SearchFlags =
+ cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
+
+ name_len = psrch_inf->resume_name_len;
+ params += name_len;
+ if (name_len < PATH_MAX) {
+ memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
+ byte_count += name_len;
+ /* 14 byte parm len above enough for 2 byte null terminator */
+ pSMB->ResumeFileName[name_len] = 0;
+ pSMB->ResumeFileName[name_len+1] = 0;
+ } else {
+ rc = -EINVAL;
+ goto FNext2_err_exit;
+ }
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ cifs_stats_inc(&tcon->num_fnext);
+ if (rc) {
+ if (rc == -EBADF) {
+ psrch_inf->endOfSearch = true;
+ cifs_buf_release(pSMB);
+ rc = 0; /* search probably was closed at end of search*/
+ } else
+ cFYI(1, ("FindNext returned = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc == 0) {
+ unsigned int lnoff;
+
+ /* BB fixme add lock for file (srch_info) struct here */
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ psrch_inf->unicode = true;
+ else
+ psrch_inf->unicode = false;
+ response_data = (char *) &pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.ParameterOffset);
+ parms = (T2_FNEXT_RSP_PARMS *)response_data;
+ response_data = (char *)&pSMBr->hdr.Protocol +
+ le16_to_cpu(pSMBr->t2.DataOffset);
+ if (psrch_inf->smallBuf)
+ cifs_small_buf_release(
+ psrch_inf->ntwrk_buf_start);
+ else
+ cifs_buf_release(psrch_inf->ntwrk_buf_start);
+ psrch_inf->srch_entries_start = response_data;
+ psrch_inf->ntwrk_buf_start = (char *)pSMB;
+ psrch_inf->smallBuf = 0;
+ if (parms->EndofSearch)
+ psrch_inf->endOfSearch = true;
+ else
+ psrch_inf->endOfSearch = false;
+ psrch_inf->entries_in_buffer =
+ le16_to_cpu(parms->SearchCount);
+ psrch_inf->index_of_last_entry +=
+ psrch_inf->entries_in_buffer;
+ lnoff = le16_to_cpu(parms->LastNameOffset);
+ if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
+ lnoff) {
+ cERROR(1, ("ignoring corrupt resume name"));
+ psrch_inf->last_entry = NULL;
+ return rc;
+ } else
+ psrch_inf->last_entry =
+ psrch_inf->srch_entries_start + lnoff;
+
+/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
+ psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
+
+ /* BB fixme add unlock here */
+ }
+
+ }
+
+ /* BB On error, should we leave previous search buf (and count and
+ last entry fields) intact or free the previous one? */
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+FNext2_err_exit:
+ if (rc != 0)
+ cifs_buf_release(pSMB);
+ return rc;
+}
+
+int
+CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
+ const __u16 searchHandle)
+{
+ int rc = 0;
+ FINDCLOSE_REQ *pSMB = NULL;
+
+ cFYI(1, ("In CIFSSMBFindClose"));
+ rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
+
+ /* no sense returning error if session restarted
+ as file handle has been closed */
+ if (rc == -EAGAIN)
+ return 0;
+ if (rc)
+ return rc;
+
+ pSMB->FileID = searchHandle;
+ pSMB->ByteCount = 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ if (rc)
+ cERROR(1, ("Send error in FindClose = %d", rc));
+
+ cifs_stats_inc(&tcon->num_fclose);
+
+ /* Since session is dead, search handle closed on server already */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+ return rc;
+}
+
+int
+CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ __u64 *inode_number,
+ const struct nls_table *nls_codepage, int remap)
+{
+ int rc = 0;
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int name_len, bytes_returned;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In GetSrvInodeNum for %s", searchName));
+ if (tcon == NULL)
+ return -ENODEV;
+
+GetInodeNumberRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("error %d in QueryInternalInfo", rc));
+ } else {
+ /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc || (pSMBr->ByteCount < 2))
+ /* BB also check enough total bytes returned */
+ /* If rc should we check for EOPNOSUPP and
+ disable the srvino flag? or in caller? */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ struct file_internal_info *pfinfo;
+ /* BB Do we need a cast or hash here ? */
+ if (count < 8) {
+ cFYI(1, ("Illegal size ret in QryIntrnlInf"));
+ rc = -EIO;
+ goto GetInodeNumOut;
+ }
+ pfinfo = (struct file_internal_info *)
+ (data_offset + (char *) &pSMBr->hdr.Protocol);
+ *inode_number = pfinfo->UniqueId;
+ }
+ }
+GetInodeNumOut:
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto GetInodeNumberRetry;
+ return rc;
+}
+
+/* computes length of UCS string converted to host codepage
+ * @src: UCS string
+ * @maxlen: length of the input string in UCS characters
+ * (not in bytes)
+ *
+ * return: size of input string in host codepage
+ */
+static int hostlen_fromUCS(const __le16 *src, const int maxlen,
+ const struct nls_table *nls_codepage) {
+ int i;
+ int hostlen = 0;
+ char to[4];
+ int charlen;
+ for (i = 0; (i < maxlen) && src[i]; ++i) {
+ charlen = nls_codepage->uni2char(le16_to_cpu(src[i]),
+ to, NLS_MAX_CHARSET_SIZE);
+ hostlen += charlen > 0 ? charlen : 1;
+ }
+ return hostlen;
+}
+
+/* parses DFS refferal V3 structure
+ * caller is responsible for freeing target_nodes
+ * returns:
+ * on success - 0
+ * on failure - errno
+ */
+static int
+parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
+ unsigned int *num_of_nodes,
+ struct dfs_info3_param **target_nodes,
+ const struct nls_table *nls_codepage, int remap,
+ const char *searchName)
+{
+ int i, rc = 0;
+ char *data_end;
+ bool is_unicode;
+ struct dfs_referral_level_3 *ref;
+
+ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+ is_unicode = true;
+ else
+ is_unicode = false;
+ *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
+
+ if (*num_of_nodes < 1) {
+ cERROR(1, ("num_referrals: must be at least > 0,"
+ "but we get num_referrals = %d\n", *num_of_nodes));
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
+ if (ref->VersionNumber != cpu_to_le16(3)) {
+ cERROR(1, ("Referrals of V%d version are not supported,"
+ "should be V3", le16_to_cpu(ref->VersionNumber)));
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ /* get the upper boundary of the resp buffer */
+ data_end = (char *)(&(pSMBr->PathConsumed)) +
+ le16_to_cpu(pSMBr->t2.DataCount);
+
+ cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
+ *num_of_nodes,
+ le16_to_cpu(pSMBr->DFSFlags)));
+
+ *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
+ *num_of_nodes, GFP_KERNEL);
+ if (*target_nodes == NULL) {
+ cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
+ rc = -ENOMEM;
+ goto parse_DFS_referrals_exit;
+ }
+
+ /* collect neccessary data from referrals */
+ for (i = 0; i < *num_of_nodes; i++) {
+ char *temp;
+ int max_len;
+ struct dfs_info3_param *node = (*target_nodes)+i;
+
+ node->flags = le16_to_cpu(pSMBr->DFSFlags);
+ if (is_unicode) {
+ __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
+ GFP_KERNEL);
+ cifsConvertToUCS((__le16 *) tmp, searchName,
+ PATH_MAX, nls_codepage, remap);
+ node->path_consumed = hostlen_fromUCS(tmp,
+ le16_to_cpu(pSMBr->PathConsumed)/2,
+ nls_codepage);
+ kfree(tmp);
+ } else
+ node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
+
+ node->server_type = le16_to_cpu(ref->ServerType);
+ node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
+
+ /* copy DfsPath */
+ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
+ max_len = data_end - temp;
+ rc = cifs_strncpy_to_host(&(node->path_name), temp,
+ max_len, is_unicode, nls_codepage);
+ if (rc)
+ goto parse_DFS_referrals_exit;
+
+ /* copy link target UNC */
+ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
+ max_len = data_end - temp;
+ rc = cifs_strncpy_to_host(&(node->node_name), temp,
+ max_len, is_unicode, nls_codepage);
+ if (rc)
+ goto parse_DFS_referrals_exit;
+
+ ref += le16_to_cpu(ref->Size);
+ }
+
+parse_DFS_referrals_exit:
+ if (rc) {
+ free_dfs_info_array(*target_nodes, *num_of_nodes);
+ *target_nodes = NULL;
+ *num_of_nodes = 0;
+ }
+ return rc;
+}
+
+int
+CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
+ const unsigned char *searchName,
+ struct dfs_info3_param **target_nodes,
+ unsigned int *num_of_nodes,
+ const struct nls_table *nls_codepage, int remap)
+{
+/* TRANS2_GET_DFS_REFERRAL */
+ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
+ TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+ *num_of_nodes = 0;
+ *target_nodes = NULL;
+
+ cFYI(1, ("In GetDFSRefer the path %s", searchName));
+ if (ses == NULL)
+ return -ENODEV;
+getDFSRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ /* server pointer checked in called function,
+ but should never be null here anyway */
+ pSMB->hdr.Mid = GetNextMid(ses->server);
+ pSMB->hdr.Tid = ses->ipc_tid;
+ pSMB->hdr.Uid = ses->Suid;
+ if (ses->capabilities & CAP_STATUS32)
+ pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+ if (ses->capabilities & CAP_DFS)
+ pSMB->hdr.Flags2 |= SMBFLG2_DFS;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
+ searchName, PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->RequestFileName, searchName, name_len);
+ }
+
+ if (ses->server) {
+ if (ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+ }
+
+ pSMB->hdr.Uid = ses->Suid;
+
+ params = 2 /* level */ + name_len /*includes null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = 0;
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
+ byte_count = params + 3 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->MaxReferralLevel = cpu_to_le16(3);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in GetDFSRefer = %d", rc));
+ goto GetDFSRefExit;
+ }
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ /* BB Also check if enough total bytes returned? */
+ if (rc || (pSMBr->ByteCount < 17)) {
+ rc = -EIO; /* bad smb */
+ goto GetDFSRefExit;
+ }
+
+ cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d",
+ pSMBr->ByteCount,
+ le16_to_cpu(pSMBr->t2.DataOffset)));
+
+ /* parse returned result into more usable form */
+ rc = parse_DFS_referrals(pSMBr, num_of_nodes,
+ target_nodes, nls_codepage, remap,
+ searchName);
+
+GetDFSRefExit:
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto getDFSRetry;
+
+ return rc;
+}
+
+/* Query File System Info such as free space to old servers such as Win 9x */
+int
+SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
+{
+/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_ALLOC_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cFYI(1, ("OldQFSInfo"));
+oldQFSInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QFSInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < 18))
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ cFYI(1, ("qfsinf resp BCC: %d Offset %d",
+ pSMBr->ByteCount, data_offset));
+
+ response_data = (FILE_SYSTEM_ALLOC_INFO *)
+ (((char *) &pSMBr->hdr.Protocol) + data_offset);
+ FSData->f_bsize =
+ le16_to_cpu(response_data->BytesPerSector) *
+ le32_to_cpu(response_data->
+ SectorsPerAllocationUnit);
+ FSData->f_blocks =
+ le32_to_cpu(response_data->TotalAllocationUnits);
+ FSData->f_bfree = FSData->f_bavail =
+ le32_to_cpu(response_data->FreeAllocationUnits);
+ cFYI(1,
+ ("Blocks: %lld Free: %lld Block size %ld",
+ (unsigned long long)FSData->f_blocks,
+ (unsigned long long)FSData->f_bfree,
+ FSData->f_bsize));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto oldQFSInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
+{
+/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QFSInfo"));
+QFSInfoRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QFSInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < 24))
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+
+ response_data =
+ (FILE_SYSTEM_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ FSData->f_bsize =
+ le32_to_cpu(response_data->BytesPerSector) *
+ le32_to_cpu(response_data->
+ SectorsPerAllocationUnit);
+ FSData->f_blocks =
+ le64_to_cpu(response_data->TotalAllocationUnits);
+ FSData->f_bfree = FSData->f_bavail =
+ le64_to_cpu(response_data->FreeAllocationUnits);
+ cFYI(1,
+ ("Blocks: %lld Free: %lld Block size %ld",
+ (unsigned long long)FSData->f_blocks,
+ (unsigned long long)FSData->f_bfree,
+ FSData->f_bsize));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSInfoRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
+{
+/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QFSAttributeInfo"));
+QFSAttributeRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < 13)) {
+ /* BB also check if enough bytes returned */
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_ATTRIBUTE_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ memcpy(&tcon->fsAttrInfo, response_data,
+ sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSAttributeRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
+{
+/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_DEVICE_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QFSDeviceInfo"));
+QFSDeviceRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_DEVICE_INFO *)
+ (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ memcpy(&tcon->fsDevInfo, response_data,
+ sizeof(FILE_SYSTEM_DEVICE_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSDeviceRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
+{
+/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_UNIX_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QFSUnixInfo"));
+QFSUnixRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(100);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
+ smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < 13)) {
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_UNIX_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ memcpy(&tcon->fsUnixInfo, response_data,
+ sizeof(FILE_SYSTEM_UNIX_INFO));
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSUnixRetry;
+
+
+ return rc;
+}
+
+int
+CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
+{
+/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
+ TRANSACTION2_SETFSI_REQ *pSMB = NULL;
+ TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, offset, byte_count;
+
+ cFYI(1, ("In SETFSUnixInfo"));
+SETFSUnixRetry:
+ /* BB switch to small buf init to save memory */
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 4; /* 2 bytes zero followed by info level. */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
+ - 4;
+ offset = param_offset + params;
+
+ pSMB->MaxParameterCount = cpu_to_le16(4);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(100);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
+ byte_count = 1 /* pad */ + params + 12;
+
+ pSMB->DataCount = cpu_to_le16(12);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+
+ /* Params. */
+ pSMB->FileNum = 0;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
+
+ /* Data. */
+ pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
+ pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
+ pSMB->ClientUnixCap = cpu_to_le64(cap);
+
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc)
+ rc = -EIO; /* bad smb */
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SETFSUnixRetry;
+
+ return rc;
+}
+
+
+
+int
+CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
+ struct kstatfs *FSData)
+{
+/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
+ TRANSACTION2_QFSI_REQ *pSMB = NULL;
+ TRANSACTION2_QFSI_RSP *pSMBr = NULL;
+ FILE_SYSTEM_POSIX_INFO *response_data;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In QFSPosixInfo"));
+QFSPosixRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ params = 2; /* level */
+ pSMB->TotalDataCount = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(100);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ byte_count = params + 1 /* pad */ ;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
+ smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ if (rc || (pSMBr->ByteCount < 13)) {
+ rc = -EIO; /* bad smb */
+ } else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ response_data =
+ (FILE_SYSTEM_POSIX_INFO
+ *) (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ FSData->f_bsize =
+ le32_to_cpu(response_data->BlockSize);
+ FSData->f_blocks =
+ le64_to_cpu(response_data->TotalBlocks);
+ FSData->f_bfree =
+ le64_to_cpu(response_data->BlocksAvail);
+ if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
+ FSData->f_bavail = FSData->f_bfree;
+ } else {
+ FSData->f_bavail =
+ le64_to_cpu(response_data->UserBlocksAvail);
+ }
+ if (response_data->TotalFileNodes != cpu_to_le64(-1))
+ FSData->f_files =
+ le64_to_cpu(response_data->TotalFileNodes);
+ if (response_data->FreeFileNodes != cpu_to_le64(-1))
+ FSData->f_ffree =
+ le64_to_cpu(response_data->FreeFileNodes);
+ }
+ }
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto QFSPosixRetry;
+
+ return rc;
+}
+
+
+/* We can not use write of zero bytes trick to
+ set file size due to need for large file support. Also note that
+ this SetPathInfo is preferred to SetFileInfo based method in next
+ routine which is only needed to work around a sharing violation bug
+ in Samba which this routine can run into */
+
+int
+CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
+ __u64 size, bool SetAllocation,
+ const struct nls_table *nls_codepage, int remap)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ struct file_end_of_file_info *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count, data_count, param_offset, offset;
+
+ cFYI(1, ("In SetEOF"));
+SetEOFRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+ params = 6 + name_len;
+ data_count = sizeof(struct file_end_of_file_info);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(4100);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ if (SetAllocation) {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
+ } else /* Set File Size */ {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
+ }
+
+ parm_data =
+ (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
+ offset);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + data_count;
+ pSMB->DataCount = cpu_to_le16(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ parm_data->FileSize = cpu_to_le64(size);
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("SetPathInfo (file size) returned %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetEOFRetry;
+
+ return rc;
+}
+
+int
+CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
+ __u16 fid, __u32 pid_of_opener, bool SetAllocation)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ struct file_end_of_file_info *parm_data;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
+ (long long)size));
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+
+ count = sizeof(struct file_end_of_file_info);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ parm_data =
+ (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
+ + offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ parm_data->FileSize = cpu_to_le64(size);
+ pSMB->Fid = fid;
+ if (SetAllocation) {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
+ } else /* Set File Size */ {
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
+ else
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
+ }
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ if (rc) {
+ cFYI(1,
+ ("Send error in SetFileInfo (SetFileSize) = %d",
+ rc));
+ }
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+/* Some legacy servers such as NT4 require that the file times be set on
+ an open handle, rather than by pathname - this is awkward due to
+ potential access conflicts on the open, but it is unavoidable for these
+ old servers since the only other choice is to go from 100 nanosecond DCE
+ time and resort to the original setpathinfo level which takes the ancient
+ DOS time format with 2 second granularity */
+int
+CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
+ const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cFYI(1, ("Set Times (via SetFileInfo)"));
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+
+ count = sizeof(FILE_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = fid;
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ if (rc)
+ cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
+
+ /* Note: On -EAGAIN error only caller can retry on handle based calls
+ since file handle passed in no longer valid */
+
+ return rc;
+}
+
+int
+CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
+ bool delete_file, __u16 fid, __u32 pid_of_opener)
+{
+ struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ char *data_offset;
+ int rc = 0;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cFYI(1, ("Set File Disposition (via SetFileInfo)"));
+ rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
+
+ if (rc)
+ return rc;
+
+ pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+
+ params = 6;
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ offset = param_offset + params;
+
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+
+ count = 1;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->Fid = fid;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ *data_offset = delete_file ? 1 : 0;
+ rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+ if (rc)
+ cFYI(1, ("Send error in SetFileDisposition = %d", rc));
+
+ return rc;
+}
+
+int
+CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
+ const char *fileName, const FILE_BASIC_INFO *data,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ char *data_offset;
+ __u16 params, param_offset, offset, byte_count, count;
+
+ cFYI(1, ("In SetTimes"));
+
+SetTimesRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+ count = sizeof(FILE_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
+ else
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("SetPathInfo (times) returned %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetTimesRetry;
+
+ return rc;
+}
+
+/* Can not be used to set time stamps yet (due to old DOS time format) */
+/* Can be used to set attributes */
+#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
+ handling it anyway and NT4 was what we thought it would be needed for
+ Do not delete it until we prove whether needed for Win9x though */
+int
+CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
+ __u16 dos_attrs, const struct nls_table *nls_codepage)
+{
+ SETATTR_REQ *pSMB = NULL;
+ SETATTR_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+
+ cFYI(1, ("In SetAttrLegacy"));
+
+SetAttrLgcyRetry:
+ rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ ConvertToUCS((__le16 *) pSMB->fileName, fileName,
+ PATH_MAX, nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->fileName, fileName, name_len);
+ }
+ pSMB->attr = cpu_to_le16(dos_attrs);
+ pSMB->BufferFormat = 0x04;
+ pSMB->hdr.smb_buf_length += name_len + 1;
+ pSMB->ByteCount = cpu_to_le16(name_len + 1);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("Error in LegacySetAttr = %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetAttrLgcyRetry;
+
+ return rc;
+}
+#endif /* temporarily unneeded SetAttr legacy function */
+
+int
+CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
+ const struct cifs_unix_set_info_args *args,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_SPI_REQ *pSMB = NULL;
+ TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ FILE_UNIX_BASIC_INFO *data_offset;
+ __u16 params, param_offset, offset, count, byte_count;
+ __u64 mode = args->mode;
+
+ cFYI(1, ("In SetUID/GID/Mode"));
+setPermsRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+ count = sizeof(FILE_UNIX_BASIC_INFO);
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ data_offset =
+ (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
+ offset);
+ memset(data_offset, 0, count);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->DataCount = cpu_to_le16(count);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ /* Samba server ignores set of file size to zero due to bugs in some
+ older clients, but we should be precise - we use SetFileSize to
+ set file size and do not want to truncate file size to zero
+ accidently as happened on one Samba server beta by putting
+ zero instead of -1 here */
+ data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
+ data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
+ data_offset->LastStatusChange = cpu_to_le64(args->ctime);
+ data_offset->LastAccessTime = cpu_to_le64(args->atime);
+ data_offset->LastModificationTime = cpu_to_le64(args->mtime);
+ data_offset->Uid = cpu_to_le64(args->uid);
+ data_offset->Gid = cpu_to_le64(args->gid);
+ /* better to leave device as zero when it is */
+ data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
+ data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
+ data_offset->Permissions = cpu_to_le64(mode);
+
+ if (S_ISREG(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FILE);
+ else if (S_ISDIR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_DIR);
+ else if (S_ISLNK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
+ else if (S_ISCHR(mode))
+ data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
+ else if (S_ISBLK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
+ else if (S_ISFIFO(mode))
+ data_offset->Type = cpu_to_le32(UNIX_FIFO);
+ else if (S_ISSOCK(mode))
+ data_offset->Type = cpu_to_le32(UNIX_SOCKET);
+
+
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("SetPathInfo (perms) returned %d", rc));
+
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto setPermsRetry;
+ return rc;
+}
+
+int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
+ const int notify_subdirs, const __u16 netfid,
+ __u32 filter, struct file *pfile, int multishot,
+ const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ struct smb_com_transaction_change_notify_req *pSMB = NULL;
+ struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
+ struct dir_notify_req *dnotify_req;
+ int bytes_returned;
+
+ cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ /* BB find exact data count max from sess structure BB */
+ pSMB->MaxDataCount = 0; /* same in little endian or be */
+/* BB VERIFY verify which is correct for above BB */
+ pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
+ MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
+
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4; /* single byte does not need le conversion */
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ if (notify_subdirs)
+ pSMB->WatchTree = 1; /* one byte - no le conversion needed */
+ pSMB->Reserved2 = 0;
+ pSMB->CompletionFilter = cpu_to_le32(filter);
+ pSMB->Fid = netfid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *)pSMBr, &bytes_returned,
+ CIFS_ASYNC_OP);
+ if (rc) {
+ cFYI(1, ("Error in Notify = %d", rc));
+ } else {
+ /* Add file to outstanding requests */
+ /* BB change to kmem cache alloc */
+ dnotify_req = kmalloc(
+ sizeof(struct dir_notify_req),
+ GFP_KERNEL);
+ if (dnotify_req) {
+ dnotify_req->Pid = pSMB->hdr.Pid;
+ dnotify_req->PidHigh = pSMB->hdr.PidHigh;
+ dnotify_req->Mid = pSMB->hdr.Mid;
+ dnotify_req->Tid = pSMB->hdr.Tid;
+ dnotify_req->Uid = pSMB->hdr.Uid;
+ dnotify_req->netfid = netfid;
+ dnotify_req->pfile = pfile;
+ dnotify_req->filter = filter;
+ dnotify_req->multishot = multishot;
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&dnotify_req->lhead,
+ &GlobalDnotifyReqList);
+ spin_unlock(&GlobalMid_Lock);
+ } else
+ rc = -ENOMEM;
+ }
+ cifs_buf_release(pSMB);
+ return rc;
+}
+#ifdef CONFIG_CIFS_XATTR
+ssize_t
+CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *EAData, size_t buf_size,
+ const struct nls_table *nls_codepage, int remap)
+{
+ /* BB assumes one setup word */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ struct fea *temp_fea;
+ char *temp_ptr;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In Query All EAs path %s", searchName));
+QAllEAsRetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in QueryAllEAs = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ /* BB also check enough total bytes returned */
+ /* BB we need to improve the validity checking
+ of these trans2 responses */
+ if (rc || (pSMBr->ByteCount < 4))
+ rc = -EIO; /* bad smb */
+ /* else if (pFindData){
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset, kl);
+ }*/ else {
+ /* check that length of list is not more than bcc */
+ /* check that each entry does not go beyond length
+ of list */
+ /* check that each element of each entry does not
+ go beyond end of list */
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ struct fealist *ea_response_data;
+ rc = 0;
+ /* validate_trans2_offsets() */
+ /* BB check if start of smb + data_offset > &bcc+ bcc */
+ ea_response_data = (struct fealist *)
+ (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ name_len = le32_to_cpu(ea_response_data->list_len);
+ cFYI(1, ("ea length %d", name_len));
+ if (name_len <= 8) {
+ /* returned EA size zeroed at top of function */
+ cFYI(1, ("empty EA list returned from server"));
+ } else {
+ /* account for ea list len */
+ name_len -= 4;
+ temp_fea = ea_response_data->list;
+ temp_ptr = (char *)temp_fea;
+ while (name_len > 0) {
+ __u16 value_len;
+ name_len -= 4;
+ temp_ptr += 4;
+ rc += temp_fea->name_len;
+ /* account for prefix user. and trailing null */
+ rc = rc + 5 + 1;
+ if (rc < (int)buf_size) {
+ memcpy(EAData, "user.", 5);
+ EAData += 5;
+ memcpy(EAData, temp_ptr,
+ temp_fea->name_len);
+ EAData += temp_fea->name_len;
+ /* null terminate name */
+ *EAData = 0;
+ EAData = EAData + 1;
+ } else if (buf_size == 0) {
+ /* skip copy - calc size only */
+ } else {
+ /* stop before overrun buffer */
+ rc = -ERANGE;
+ break;
+ }
+ name_len -= temp_fea->name_len;
+ temp_ptr += temp_fea->name_len;
+ /* account for trailing null */
+ name_len--;
+ temp_ptr++;
+ value_len =
+ le16_to_cpu(temp_fea->value_len);
+ name_len -= value_len;
+ temp_ptr += value_len;
+ /* BB check that temp_ptr is still
+ within the SMB BB*/
+
+ /* no trailing null to account for
+ in value len */
+ /* go on to next EA */
+ temp_fea = (struct fea *)temp_ptr;
+ }
+ }
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QAllEAsRetry;
+
+ return (ssize_t)rc;
+}
+
+ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName, const unsigned char *ea_name,
+ unsigned char *ea_value, size_t buf_size,
+ const struct nls_table *nls_codepage, int remap)
+{
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ struct fea *temp_fea;
+ char *temp_ptr;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In Query EA path %s", searchName));
+QEARetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max SMB PDU from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ pSMB->ParameterOffset = cpu_to_le16(offsetof(
+ struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in Query EA = %d", rc));
+ } else { /* decode response */
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+
+ /* BB also check enough total bytes returned */
+ /* BB we need to improve the validity checking
+ of these trans2 responses */
+ if (rc || (pSMBr->ByteCount < 4))
+ rc = -EIO; /* bad smb */
+ /* else if (pFindData){
+ memcpy((char *) pFindData,
+ (char *) &pSMBr->hdr.Protocol +
+ data_offset, kl);
+ }*/ else {
+ /* check that length of list is not more than bcc */
+ /* check that each entry does not go beyond length
+ of list */
+ /* check that each element of each entry does not
+ go beyond end of list */
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ struct fealist *ea_response_data;
+ rc = -ENODATA;
+ /* validate_trans2_offsets() */
+ /* BB check if start of smb + data_offset > &bcc+ bcc*/
+ ea_response_data = (struct fealist *)
+ (((char *) &pSMBr->hdr.Protocol) +
+ data_offset);
+ name_len = le32_to_cpu(ea_response_data->list_len);
+ cFYI(1, ("ea length %d", name_len));
+ if (name_len <= 8) {
+ /* returned EA size zeroed at top of function */
+ cFYI(1, ("empty EA list returned from server"));
+ } else {
+ /* account for ea list len */
+ name_len -= 4;
+ temp_fea = ea_response_data->list;
+ temp_ptr = (char *)temp_fea;
+ /* loop through checking if we have a matching
+ name and then return the associated value */
+ while (name_len > 0) {
+ __u16 value_len;
+ name_len -= 4;
+ temp_ptr += 4;
+ value_len =
+ le16_to_cpu(temp_fea->value_len);
+ /* BB validate that value_len falls within SMB,
+ even though maximum for name_len is 255 */
+ if (memcmp(temp_fea->name, ea_name,
+ temp_fea->name_len) == 0) {
+ /* found a match */
+ rc = value_len;
+ /* account for prefix user. and trailing null */
+ if (rc <= (int)buf_size) {
+ memcpy(ea_value,
+ temp_fea->name+temp_fea->name_len+1,
+ rc);
+ /* ea values, unlike ea
+ names, are not null
+ terminated */
+ } else if (buf_size == 0) {
+ /* skip copy - calc size only */
+ } else {
+ /* stop before overrun buffer */
+ rc = -ERANGE;
+ }
+ break;
+ }
+ name_len -= temp_fea->name_len;
+ temp_ptr += temp_fea->name_len;
+ /* account for trailing null */
+ name_len--;
+ temp_ptr++;
+ name_len -= value_len;
+ temp_ptr += value_len;
+ /* No trailing null to account for in
+ value_len. Go on to next EA */
+ temp_fea = (struct fea *)temp_ptr;
+ }
+ }
+ }
+ }
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto QEARetry;
+
+ return (ssize_t)rc;
+}
+
+int
+CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
+ const char *ea_name, const void *ea_value,
+ const __u16 ea_value_len, const struct nls_table *nls_codepage,
+ int remap)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ struct fealist *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, param_offset, byte_count, offset, count;
+
+ cFYI(1, ("In SetEA"));
+SetEARetry:
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
+ PATH_MAX, nls_codepage, remap);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+
+ params = 6 + name_len;
+
+ /* done calculating parms using name_len of file name,
+ now use name_len to calculate length of ea name
+ we are going to create in the inode xattrs */
+ if (ea_name == NULL)
+ name_len = 0;
+ else
+ name_len = strnlen(ea_name, 255);
+
+ count = sizeof(*parm_data) + ea_value_len + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find max SMB PDU from sess */
+ pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ pSMB->InformationLevel =
+ cpu_to_le16(SMB_SET_FILE_EA);
+
+ parm_data =
+ (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
+ offset);
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ byte_count = 3 /* pad */ + params + count;
+ pSMB->DataCount = cpu_to_le16(count);
+ parm_data->list_len = cpu_to_le32(count);
+ parm_data->list[0].EA_flags = 0;
+ /* we checked above that name len is less than 255 */
+ parm_data->list[0].name_len = (__u8)name_len;
+ /* EA names are always ASCII */
+ if (ea_name)
+ strncpy(parm_data->list[0].name, ea_name, name_len);
+ parm_data->list[0].name[name_len] = 0;
+ parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
+ /* caller ensures that ea_value_len is less than 64K but
+ we need to ensure that it fits within the smb */
+
+ /*BB add length check to see if it would fit in
+ negotiated SMB buffer size BB */
+ /* if (ea_value_len > buffer_size - 512 (enough for header)) */
+ if (ea_value_len)
+ memcpy(parm_data->list[0].name+name_len+1,
+ ea_value, ea_value_len);
+
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cFYI(1, ("SetPathInfo (EA) returned %d", rc));
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto SetEARetry;
+
+ return rc;
+}
+
+#endif
diff --git a/fs/cifs/cn_cifs.h b/fs/cifs/cn_cifs.h
new file mode 100644
index 0000000..ea59cca
--- /dev/null
+++ b/fs/cifs/cn_cifs.h
@@ -0,0 +1,37 @@
+/*
+ * fs/cifs/cn_cifs.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CN_CIFS_H
+#define _CN_CIFS_H
+#ifdef CONFIG_CIFS_UPCALL
+#include <linux/types.h>
+#include <linux/connector.h>
+
+struct cifs_upcall {
+ char signature[4]; /* CIFS */
+ enum command {
+ CIFS_GET_IP = 0x00000001, /* get ip address for hostname */
+ CIFS_GET_SECBLOB = 0x00000002, /* get SPNEGO wrapped blob */
+ } command;
+ /* union cifs upcall data follows */
+};
+#endif /* CIFS_UPCALL */
+#endif /* _CN_CIFS_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
new file mode 100644
index 0000000..b5b8649
--- /dev/null
+++ b/fs/cifs/connect.c
@@ -0,0 +1,3747 @@
+/*
+ * fs/cifs/connect.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/ipv6.h>
+#include <linux/pagemap.h>
+#include <linux/ctype.h>
+#include <linux/utsname.h>
+#include <linux/mempool.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+#include <linux/pagevec.h>
+#include <linux/freezer.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "ntlmssp.h"
+#include "nterr.h"
+#include "rfc1002pdu.h"
+#include "cn_cifs.h"
+
+#define CIFS_PORT 445
+#define RFC1001_PORT 139
+
+extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
+ unsigned char *p24);
+
+extern mempool_t *cifs_req_poolp;
+
+struct smb_vol {
+ char *username;
+ char *password;
+ char *domainname;
+ char *UNC;
+ char *UNCip;
+ char *in6_addr; /* ipv6 address as human readable form of in6_addr */
+ char *iocharset; /* local code page for mapping to and from Unicode */
+ char source_rfc1001_name[16]; /* netbios name of client */
+ char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */
+ uid_t linux_uid;
+ gid_t linux_gid;
+ mode_t file_mode;
+ mode_t dir_mode;
+ unsigned secFlg;
+ bool rw:1;
+ bool retry:1;
+ bool intr:1;
+ bool setuids:1;
+ bool override_uid:1;
+ bool override_gid:1;
+ bool dynperm:1;
+ bool noperm:1;
+ bool no_psx_acl:1; /* set if posix acl support should be disabled */
+ bool cifs_acl:1;
+ bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
+ bool server_ino:1; /* use inode numbers from server ie UniqueId */
+ bool direct_io:1;
+ bool remap:1; /* set to remap seven reserved chars in filenames */
+ bool posix_paths:1; /* unset to not ask for posix pathnames. */
+ bool no_linux_ext:1;
+ bool sfu_emul:1;
+ bool nullauth:1; /* attempt to authenticate with null user */
+ bool nocase:1; /* request case insensitive filenames */
+ bool nobrl:1; /* disable sending byte range locks to srv */
+ bool seal:1; /* request transport encryption on share */
+ bool nodfs:1; /* Do not request DFS, even if available */
+ bool local_lease:1; /* check leases only on local system, not remote */
+ bool noblocksnd:1;
+ bool noautotune:1;
+ unsigned int rsize;
+ unsigned int wsize;
+ unsigned int sockopt;
+ unsigned short int port;
+ char *prepath;
+};
+
+static int ipv4_connect(struct sockaddr_in *psin_server,
+ struct socket **csocket,
+ char *netb_name,
+ char *server_netb_name,
+ bool noblocksnd,
+ bool nosndbuf); /* ipv6 never set sndbuf size */
+static int ipv6_connect(struct sockaddr_in6 *psin_server,
+ struct socket **csocket, bool noblocksnd);
+
+
+ /*
+ * cifs tcp session reconnection
+ *
+ * mark tcp session as reconnecting so temporarily locked
+ * mark all smb sessions as reconnecting for tcp session
+ * reconnect tcp session
+ * wake up waiters on reconnection? - (not needed currently)
+ */
+
+static int
+cifs_reconnect(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ struct list_head *tmp, *tmp2;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+ struct mid_q_entry *mid_entry;
+
+ spin_lock(&GlobalMid_Lock);
+ if (server->tcpStatus == CifsExiting) {
+ /* the demux thread will exit normally
+ next time through the loop */
+ spin_unlock(&GlobalMid_Lock);
+ return rc;
+ } else
+ server->tcpStatus = CifsNeedReconnect;
+ spin_unlock(&GlobalMid_Lock);
+ server->maxBuf = 0;
+
+ cFYI(1, ("Reconnecting tcp session"));
+
+ /* before reconnecting the tcp session, mark the smb session (uid)
+ and the tid bad so they are not used until reconnected */
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+ ses->need_reconnect = true;
+ ses->ipc_tid = 0;
+ list_for_each(tmp2, &ses->tcon_list) {
+ tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
+ tcon->need_reconnect = true;
+ }
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ /* do not want to be sending data on a socket we are freeing */
+ down(&server->tcpSem);
+ if (server->ssocket) {
+ cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
+ server->ssocket->flags));
+ kernel_sock_shutdown(server->ssocket, SHUT_WR);
+ cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx",
+ server->ssocket->state,
+ server->ssocket->flags));
+ sock_release(server->ssocket);
+ server->ssocket = NULL;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct
+ mid_q_entry,
+ qhead);
+ if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
+ /* Mark other intransit requests as needing
+ retry so we do not immediately mark the
+ session bad again (ie after we reconnect
+ below) as they timeout too */
+ mid_entry->midState = MID_RETRY_NEEDED;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ up(&server->tcpSem);
+
+ while ((server->tcpStatus != CifsExiting) &&
+ (server->tcpStatus != CifsGood)) {
+ try_to_freeze();
+ if (server->addr.sockAddr6.sin6_family == AF_INET6) {
+ rc = ipv6_connect(&server->addr.sockAddr6,
+ &server->ssocket, server->noautotune);
+ } else {
+ rc = ipv4_connect(&server->addr.sockAddr,
+ &server->ssocket,
+ server->workstation_RFC1001_name,
+ server->server_RFC1001_name,
+ server->noblocksnd, server->noautotune);
+ }
+ if (rc) {
+ cFYI(1, ("reconnect error %d", rc));
+ msleep(3000);
+ } else {
+ atomic_inc(&tcpSesReconnectCount);
+ spin_lock(&GlobalMid_Lock);
+ if (server->tcpStatus != CifsExiting)
+ server->tcpStatus = CifsGood;
+ server->sequence_number = 0;
+ spin_unlock(&GlobalMid_Lock);
+ /* atomic_set(&server->inFlight,0);*/
+ wake_up(&server->response_q);
+ }
+ }
+ return rc;
+}
+
+/*
+ return codes:
+ 0 not a transact2, or all data present
+ >0 transact2 with that much data missing
+ -EINVAL = invalid transact2
+
+ */
+static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
+{
+ struct smb_t2_rsp *pSMBt;
+ int total_data_size;
+ int data_in_this_rsp;
+ int remaining;
+
+ if (pSMB->Command != SMB_COM_TRANSACTION2)
+ return 0;
+
+ /* check for plausible wct, bcc and t2 data and parm sizes */
+ /* check for parm and data offset going beyond end of smb */
+ if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
+ cFYI(1, ("invalid transact2 word count"));
+ return -EINVAL;
+ }
+
+ pSMBt = (struct smb_t2_rsp *)pSMB;
+
+ total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
+ data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount);
+
+ remaining = total_data_size - data_in_this_rsp;
+
+ if (remaining == 0)
+ return 0;
+ else if (remaining < 0) {
+ cFYI(1, ("total data %d smaller than data in frame %d",
+ total_data_size, data_in_this_rsp));
+ return -EINVAL;
+ } else {
+ cFYI(1, ("missing %d bytes from transact2, check next response",
+ remaining));
+ if (total_data_size > maxBufSize) {
+ cERROR(1, ("TotalDataSize %d is over maximum buffer %d",
+ total_data_size, maxBufSize));
+ return -EINVAL;
+ }
+ return remaining;
+ }
+}
+
+static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
+{
+ struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond;
+ struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB;
+ int total_data_size;
+ int total_in_buf;
+ int remaining;
+ int total_in_buf2;
+ char *data_area_of_target;
+ char *data_area_of_buf2;
+ __u16 byte_count;
+
+ total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
+
+ if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) {
+ cFYI(1, ("total data size of primary and secondary t2 differ"));
+ }
+
+ total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount);
+
+ remaining = total_data_size - total_in_buf;
+
+ if (remaining < 0)
+ return -EINVAL;
+
+ if (remaining == 0) /* nothing to do, ignore */
+ return 0;
+
+ total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount);
+ if (remaining < total_in_buf2) {
+ cFYI(1, ("transact2 2nd response contains too much data"));
+ }
+
+ /* find end of first SMB data area */
+ data_area_of_target = (char *)&pSMBt->hdr.Protocol +
+ le16_to_cpu(pSMBt->t2_rsp.DataOffset);
+ /* validate target area */
+
+ data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol +
+ le16_to_cpu(pSMB2->t2_rsp.DataOffset);
+
+ data_area_of_target += total_in_buf;
+
+ /* copy second buffer into end of first buffer */
+ memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2);
+ total_in_buf += total_in_buf2;
+ pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf);
+ byte_count = le16_to_cpu(BCC_LE(pTargetSMB));
+ byte_count += total_in_buf2;
+ BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
+
+ byte_count = pTargetSMB->smb_buf_length;
+ byte_count += total_in_buf2;
+
+ /* BB also add check that we are not beyond maximum buffer size */
+
+ pTargetSMB->smb_buf_length = byte_count;
+
+ if (remaining == total_in_buf2) {
+ cFYI(1, ("found the last secondary response"));
+ return 0; /* we are done */
+ } else /* more responses to go */
+ return 1;
+
+}
+
+static int
+cifs_demultiplex_thread(struct TCP_Server_Info *server)
+{
+ int length;
+ unsigned int pdu_length, total_read;
+ struct smb_hdr *smb_buffer = NULL;
+ struct smb_hdr *bigbuf = NULL;
+ struct smb_hdr *smallbuf = NULL;
+ struct msghdr smb_msg;
+ struct kvec iov;
+ struct socket *csocket = server->ssocket;
+ struct list_head *tmp;
+ struct cifsSesInfo *ses;
+ struct task_struct *task_to_wake = NULL;
+ struct mid_q_entry *mid_entry;
+ char temp;
+ bool isLargeBuf = false;
+ bool isMultiRsp;
+ int reconnect;
+
+ current->flags |= PF_MEMALLOC;
+ cFYI(1, ("Demultiplex PID: %d", task_pid_nr(current)));
+
+ length = atomic_inc_return(&tcpSesAllocCount);
+ if (length > 1)
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
+ GFP_KERNEL);
+
+ set_freezable();
+ while (server->tcpStatus != CifsExiting) {
+ if (try_to_freeze())
+ continue;
+ if (bigbuf == NULL) {
+ bigbuf = cifs_buf_get();
+ if (!bigbuf) {
+ cERROR(1, ("No memory for large SMB response"));
+ msleep(3000);
+ /* retry will check if exiting */
+ continue;
+ }
+ } else if (isLargeBuf) {
+ /* we are reusing a dirty large buf, clear its start */
+ memset(bigbuf, 0, sizeof(struct smb_hdr));
+ }
+
+ if (smallbuf == NULL) {
+ smallbuf = cifs_small_buf_get();
+ if (!smallbuf) {
+ cERROR(1, ("No memory for SMB response"));
+ msleep(1000);
+ /* retry will check if exiting */
+ continue;
+ }
+ /* beginning of smb buffer is cleared in our buf_get */
+ } else /* if existing small buf clear beginning */
+ memset(smallbuf, 0, sizeof(struct smb_hdr));
+
+ isLargeBuf = false;
+ isMultiRsp = false;
+ smb_buffer = smallbuf;
+ iov.iov_base = smb_buffer;
+ iov.iov_len = 4;
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+ pdu_length = 4; /* enough to get RFC1001 header */
+incomplete_rcv:
+ length =
+ kernel_recvmsg(csocket, &smb_msg,
+ &iov, 1, pdu_length, 0 /* BB other flags? */);
+
+ if (server->tcpStatus == CifsExiting) {
+ break;
+ } else if (server->tcpStatus == CifsNeedReconnect) {
+ cFYI(1, ("Reconnect after server stopped responding"));
+ cifs_reconnect(server);
+ cFYI(1, ("call to reconnect done"));
+ csocket = server->ssocket;
+ continue;
+ } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
+ msleep(1); /* minimum sleep to prevent looping
+ allowing socket to clear and app threads to set
+ tcpStatus CifsNeedReconnect if server hung */
+ if (pdu_length < 4) {
+ iov.iov_base = (4 - pdu_length) +
+ (char *)smb_buffer;
+ iov.iov_len = pdu_length;
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+ goto incomplete_rcv;
+ } else
+ continue;
+ } else if (length <= 0) {
+ if (server->tcpStatus == CifsNew) {
+ cFYI(1, ("tcp session abend after SMBnegprot"));
+ /* some servers kill the TCP session rather than
+ returning an SMB negprot error, in which
+ case reconnecting here is not going to help,
+ and so simply return error to mount */
+ break;
+ }
+ if (!try_to_freeze() && (length == -EINTR)) {
+ cFYI(1, ("cifsd thread killed"));
+ break;
+ }
+ cFYI(1, ("Reconnect after unexpected peek error %d",
+ length));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ } else if (length < pdu_length) {
+ cFYI(1, ("requested %d bytes but only got %d bytes",
+ pdu_length, length));
+ pdu_length -= length;
+ msleep(1);
+ goto incomplete_rcv;
+ }
+
+ /* The right amount was read from socket - 4 bytes */
+ /* so we can now interpret the length field */
+
+ /* the first byte big endian of the length field,
+ is actually not part of the length but the type
+ with the most common, zero, as regular data */
+ temp = *((char *) smb_buffer);
+
+ /* Note that FC 1001 length is big endian on the wire,
+ but we convert it here so it is always manipulated
+ as host byte order */
+ pdu_length = be32_to_cpu((__force __be32)smb_buffer->smb_buf_length);
+ smb_buffer->smb_buf_length = pdu_length;
+
+ cFYI(1, ("rfc1002 length 0x%x", pdu_length+4));
+
+ if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
+ continue;
+ } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
+ cFYI(1, ("Good RFC 1002 session rsp"));
+ continue;
+ } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
+ /* we get this from Windows 98 instead of
+ an error on SMB negprot response */
+ cFYI(1, ("Negative RFC1002 Session Response Error 0x%x)",
+ pdu_length));
+ if (server->tcpStatus == CifsNew) {
+ /* if nack on negprot (rather than
+ ret of smb negprot error) reconnecting
+ not going to help, ret error to mount */
+ break;
+ } else {
+ /* give server a second to
+ clean up before reconnect attempt */
+ msleep(1000);
+ /* always try 445 first on reconnect
+ since we get NACK on some if we ever
+ connected to port 139 (the NACK is
+ since we do not begin with RFC1001
+ session initialize frame) */
+ server->addr.sockAddr.sin_port =
+ htons(CIFS_PORT);
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ }
+ } else if (temp != (char) 0) {
+ cERROR(1, ("Unknown RFC 1002 frame"));
+ cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
+ length);
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ continue;
+ }
+
+ /* else we have an SMB response */
+ if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
+ (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
+ cERROR(1, ("Invalid size SMB length %d pdu_length %d",
+ length, pdu_length+4));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ }
+
+ /* else length ok */
+ reconnect = 0;
+
+ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
+ isLargeBuf = true;
+ memcpy(bigbuf, smallbuf, 4);
+ smb_buffer = bigbuf;
+ }
+ length = 0;
+ iov.iov_base = 4 + (char *)smb_buffer;
+ iov.iov_len = pdu_length;
+ for (total_read = 0; total_read < pdu_length;
+ total_read += length) {
+ length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
+ pdu_length - total_read, 0);
+ if ((server->tcpStatus == CifsExiting) ||
+ (length == -EINTR)) {
+ /* then will exit */
+ reconnect = 2;
+ break;
+ } else if (server->tcpStatus == CifsNeedReconnect) {
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ /* Reconnect wakes up rspns q */
+ /* Now we will reread sock */
+ reconnect = 1;
+ break;
+ } else if ((length == -ERESTARTSYS) ||
+ (length == -EAGAIN)) {
+ msleep(1); /* minimum sleep to prevent looping,
+ allowing socket to clear and app
+ threads to set tcpStatus
+ CifsNeedReconnect if server hung*/
+ length = 0;
+ continue;
+ } else if (length <= 0) {
+ cERROR(1, ("Received no data, expecting %d",
+ pdu_length - total_read));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ reconnect = 1;
+ break;
+ }
+ }
+ if (reconnect == 2)
+ break;
+ else if (reconnect == 1)
+ continue;
+
+ length += 4; /* account for rfc1002 hdr */
+
+
+ dump_smb(smb_buffer, length);
+ if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) {
+ cifs_dump_mem("Bad SMB: ", smb_buffer, 48);
+ continue;
+ }
+
+
+ task_to_wake = NULL;
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+
+ if ((mid_entry->mid == smb_buffer->Mid) &&
+ (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
+ (mid_entry->command == smb_buffer->Command)) {
+ if (check2ndT2(smb_buffer,server->maxBuf) > 0) {
+ /* We have a multipart transact2 resp */
+ isMultiRsp = true;
+ if (mid_entry->resp_buf) {
+ /* merge response - fix up 1st*/
+ if (coalesce_t2(smb_buffer,
+ mid_entry->resp_buf)) {
+ mid_entry->multiRsp =
+ true;
+ break;
+ } else {
+ /* all parts received */
+ mid_entry->multiEnd =
+ true;
+ goto multi_t2_fnd;
+ }
+ } else {
+ if (!isLargeBuf) {
+ cERROR(1,("1st trans2 resp needs bigbuf"));
+ /* BB maybe we can fix this up, switch
+ to already allocated large buffer? */
+ } else {
+ /* Have first buffer */
+ mid_entry->resp_buf =
+ smb_buffer;
+ mid_entry->largeBuf =
+ true;
+ bigbuf = NULL;
+ }
+ }
+ break;
+ }
+ mid_entry->resp_buf = smb_buffer;
+ mid_entry->largeBuf = isLargeBuf;
+multi_t2_fnd:
+ task_to_wake = mid_entry->tsk;
+ mid_entry->midState = MID_RESPONSE_RECEIVED;
+#ifdef CONFIG_CIFS_STATS2
+ mid_entry->when_received = jiffies;
+#endif
+ /* so we do not time out requests to server
+ which is still responding (since server could
+ be busy but not dead) */
+ server->lstrp = jiffies;
+ break;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ if (task_to_wake) {
+ /* Was previous buf put in mpx struct for multi-rsp? */
+ if (!isMultiRsp) {
+ /* smb buffer will be freed by user thread */
+ if (isLargeBuf)
+ bigbuf = NULL;
+ else
+ smallbuf = NULL;
+ }
+ wake_up_process(task_to_wake);
+ } else if (!is_valid_oplock_break(smb_buffer, server) &&
+ !isMultiRsp) {
+ cERROR(1, ("No task to wake, unknown frame received! "
+ "NumMids %d", midCount.counter));
+ cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
+ sizeof(struct smb_hdr));
+#ifdef CONFIG_CIFS_DEBUG2
+ cifs_dump_detail(smb_buffer);
+ cifs_dump_mids(server);
+#endif /* CIFS_DEBUG2 */
+
+ }
+ } /* end while !EXITING */
+
+ /* take it off the list, if it's not already */
+ write_lock(&cifs_tcp_ses_lock);
+ list_del_init(&server->tcp_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ spin_lock(&GlobalMid_Lock);
+ server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+ wake_up_all(&server->response_q);
+
+ /* check if we have blocked requests that need to free */
+ /* Note that cifs_max_pending is normally 50, but
+ can be set at module install time to as little as two */
+ spin_lock(&GlobalMid_Lock);
+ if (atomic_read(&server->inFlight) >= cifs_max_pending)
+ atomic_set(&server->inFlight, cifs_max_pending - 1);
+ /* We do not want to set the max_pending too low or we
+ could end up with the counter going negative */
+ spin_unlock(&GlobalMid_Lock);
+ /* Although there should not be any requests blocked on
+ this queue it can not hurt to be paranoid and try to wake up requests
+ that may haven been blocked when more than 50 at time were on the wire
+ to the same server - they now will see the session is in exit state
+ and get out of SendReceive. */
+ wake_up_all(&server->request_q);
+ /* give those requests time to exit */
+ msleep(125);
+
+ if (server->ssocket) {
+ sock_release(csocket);
+ server->ssocket = NULL;
+ }
+ /* buffer usuallly freed in free_mid - need to free it here on exit */
+ cifs_buf_release(bigbuf);
+ if (smallbuf) /* no sense logging a debug message if NULL */
+ cifs_small_buf_release(smallbuf);
+
+ /*
+ * BB: we shouldn't have to do any of this. It shouldn't be
+ * possible to exit from the thread with active SMB sessions
+ */
+ read_lock(&cifs_tcp_ses_lock);
+ if (list_empty(&server->pending_mid_q)) {
+ /* loop through server session structures attached to this and
+ mark them dead */
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo,
+ smb_ses_list);
+ ses->status = CifsExiting;
+ ses->server = NULL;
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ } else {
+ /* although we can not zero the server struct pointer yet,
+ since there are active requests which may depnd on them,
+ mark the corresponding SMB sessions as exiting too */
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo,
+ smb_ses_list);
+ ses->status = CifsExiting;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
+ cFYI(1, ("Clearing Mid 0x%x - waking up ",
+ mid_entry->mid));
+ task_to_wake = mid_entry->tsk;
+ if (task_to_wake)
+ wake_up_process(task_to_wake);
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ read_unlock(&cifs_tcp_ses_lock);
+ /* 1/8th of sec is more than enough time for them to exit */
+ msleep(125);
+ }
+
+ if (!list_empty(&server->pending_mid_q)) {
+ /* mpx threads have not exited yet give them
+ at least the smb send timeout time for long ops */
+ /* due to delays on oplock break requests, we need
+ to wait at least 45 seconds before giving up
+ on a request getting a response and going ahead
+ and killing cifsd */
+ cFYI(1, ("Wait for exit from demultiplex thread"));
+ msleep(46000);
+ /* if threads still have not exited they are probably never
+ coming home not much else we can do but free the memory */
+ }
+
+ /* last chance to mark ses pointers invalid
+ if there are any pointing to this (e.g
+ if a crazy root user tried to kill cifsd
+ kernel thread explicitly this might happen) */
+ /* BB: This shouldn't be necessary, see above */
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+ ses->server = NULL;
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+
+ kfree(server->hostname);
+ task_to_wake = xchg(&server->tsk, NULL);
+ kfree(server);
+
+ length = atomic_dec_return(&tcpSesAllocCount);
+ if (length > 0)
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
+ GFP_KERNEL);
+
+ /* if server->tsk was NULL then wait for a signal before exiting */
+ if (!task_to_wake) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!signal_pending(current)) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ }
+
+ return 0;
+}
+
+/* extract the host portion of the UNC string */
+static char *
+extract_hostname(const char *unc)
+{
+ const char *src;
+ char *dst, *delim;
+ unsigned int len;
+
+ /* skip double chars at beginning of string */
+ /* BB: check validity of these bytes? */
+ src = unc + 2;
+
+ /* delimiter between hostname and sharename is always '\\' now */
+ delim = strchr(src, '\\');
+ if (!delim)
+ return ERR_PTR(-EINVAL);
+
+ len = delim - src;
+ dst = kmalloc((len + 1), GFP_KERNEL);
+ if (dst == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+
+ return dst;
+}
+
+static int
+cifs_parse_mount_options(char *options, const char *devname,
+ struct smb_vol *vol)
+{
+ char *value;
+ char *data;
+ unsigned int temp_len, i, j;
+ char separator[2];
+
+ separator[0] = ',';
+ separator[1] = 0;
+
+ if (Local_System_Name[0] != 0)
+ memcpy(vol->source_rfc1001_name, Local_System_Name, 15);
+ else {
+ char *nodename = utsname()->nodename;
+ int n = strnlen(nodename, 15);
+ memset(vol->source_rfc1001_name, 0x20, 15);
+ for (i = 0; i < n; i++) {
+ /* does not have to be perfect mapping since field is
+ informational, only used for servers that do not support
+ port 445 and it can be overridden at mount time */
+ vol->source_rfc1001_name[i] = toupper(nodename[i]);
+ }
+ }
+ vol->source_rfc1001_name[15] = 0;
+ /* null target name indicates to use *SMBSERVR default called name
+ if we end up sending RFC1001 session initialize */
+ vol->target_rfc1001_name[0] = 0;
+ vol->linux_uid = current->uid; /* current->euid instead? */
+ vol->linux_gid = current->gid;
+ vol->dir_mode = S_IRWXUGO;
+ /* 2767 perms indicate mandatory locking support */
+ vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
+
+ /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
+ vol->rw = true;
+ /* default is always to request posix paths. */
+ vol->posix_paths = 1;
+
+ if (!options)
+ return 1;
+
+ if (strncmp(options, "sep=", 4) == 0) {
+ if (options[4] != 0) {
+ separator[0] = options[4];
+ options += 5;
+ } else {
+ cFYI(1, ("Null separator not allowed"));
+ }
+ }
+
+ while ((data = strsep(&options, separator)) != NULL) {
+ if (!*data)
+ continue;
+ if ((value = strchr(data, '=')) != NULL)
+ *value++ = '\0';
+
+ /* Have to parse this before we parse for "user" */
+ if (strnicmp(data, "user_xattr", 10) == 0) {
+ vol->no_xattr = 0;
+ } else if (strnicmp(data, "nouser_xattr", 12) == 0) {
+ vol->no_xattr = 1;
+ } else if (strnicmp(data, "user", 4) == 0) {
+ if (!value) {
+ printk(KERN_WARNING
+ "CIFS: invalid or missing username\n");
+ return 1; /* needs_arg; */
+ } else if (!*value) {
+ /* null user, ie anonymous, authentication */
+ vol->nullauth = 1;
+ }
+ if (strnlen(value, 200) < 200) {
+ vol->username = value;
+ } else {
+ printk(KERN_WARNING "CIFS: username too long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "pass", 4) == 0) {
+ if (!value) {
+ vol->password = NULL;
+ continue;
+ } else if (value[0] == 0) {
+ /* check if string begins with double comma
+ since that would mean the password really
+ does start with a comma, and would not
+ indicate an empty string */
+ if (value[1] != separator[0]) {
+ vol->password = NULL;
+ continue;
+ }
+ }
+ temp_len = strlen(value);
+ /* removed password length check, NTLM passwords
+ can be arbitrarily long */
+
+ /* if comma in password, the string will be
+ prematurely null terminated. Commas in password are
+ specified across the cifs mount interface by a double
+ comma ie ,, and a comma used as in other cases ie ','
+ as a parameter delimiter/separator is single and due
+ to the strsep above is temporarily zeroed. */
+
+ /* NB: password legally can have multiple commas and
+ the only illegal character in a password is null */
+
+ if ((value[temp_len] == 0) &&
+ (value[temp_len+1] == separator[0])) {
+ /* reinsert comma */
+ value[temp_len] = separator[0];
+ temp_len += 2; /* move after second comma */
+ while (value[temp_len] != 0) {
+ if (value[temp_len] == separator[0]) {
+ if (value[temp_len+1] ==
+ separator[0]) {
+ /* skip second comma */
+ temp_len++;
+ } else {
+ /* single comma indicating start
+ of next parm */
+ break;
+ }
+ }
+ temp_len++;
+ }
+ if (value[temp_len] == 0) {
+ options = NULL;
+ } else {
+ value[temp_len] = 0;
+ /* point option to start of next parm */
+ options = value + temp_len + 1;
+ }
+ /* go from value to value + temp_len condensing
+ double commas to singles. Note that this ends up
+ allocating a few bytes too many, which is ok */
+ vol->password = kzalloc(temp_len, GFP_KERNEL);
+ if (vol->password == NULL) {
+ printk(KERN_WARNING "CIFS: no memory "
+ "for password\n");
+ return 1;
+ }
+ for (i = 0, j = 0; i < temp_len; i++, j++) {
+ vol->password[j] = value[i];
+ if (value[i] == separator[0]
+ && value[i+1] == separator[0]) {
+ /* skip second comma */
+ i++;
+ }
+ }
+ vol->password[j] = 0;
+ } else {
+ vol->password = kzalloc(temp_len+1, GFP_KERNEL);
+ if (vol->password == NULL) {
+ printk(KERN_WARNING "CIFS: no memory "
+ "for password\n");
+ return 1;
+ }
+ strcpy(vol->password, value);
+ }
+ } else if (strnicmp(data, "ip", 2) == 0) {
+ if (!value || !*value) {
+ vol->UNCip = NULL;
+ } else if (strnlen(value, 35) < 35) {
+ vol->UNCip = value;
+ } else {
+ printk(KERN_WARNING "CIFS: ip address "
+ "too long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "sec", 3) == 0) {
+ if (!value || !*value) {
+ cERROR(1, ("no security value specified"));
+ continue;
+ } else if (strnicmp(value, "krb5i", 5) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_KRB5 |
+ CIFSSEC_MUST_SIGN;
+ } else if (strnicmp(value, "krb5p", 5) == 0) {
+ /* vol->secFlg |= CIFSSEC_MUST_SEAL |
+ CIFSSEC_MAY_KRB5; */
+ cERROR(1, ("Krb5 cifs privacy not supported"));
+ return 1;
+ } else if (strnicmp(value, "krb5", 4) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_KRB5;
+ } else if (strnicmp(value, "ntlmv2i", 7) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
+ CIFSSEC_MUST_SIGN;
+ } else if (strnicmp(value, "ntlmv2", 6) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_NTLMV2;
+ } else if (strnicmp(value, "ntlmi", 5) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_NTLM |
+ CIFSSEC_MUST_SIGN;
+ } else if (strnicmp(value, "ntlm", 4) == 0) {
+ /* ntlm is default so can be turned off too */
+ vol->secFlg |= CIFSSEC_MAY_NTLM;
+ } else if (strnicmp(value, "nontlm", 6) == 0) {
+ /* BB is there a better way to do this? */
+ vol->secFlg |= CIFSSEC_MAY_NTLMV2;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ } else if (strnicmp(value, "lanman", 6) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_LANMAN;
+#endif
+ } else if (strnicmp(value, "none", 4) == 0) {
+ vol->nullauth = 1;
+ } else {
+ cERROR(1, ("bad security option: %s", value));
+ return 1;
+ }
+ } else if ((strnicmp(data, "unc", 3) == 0)
+ || (strnicmp(data, "target", 6) == 0)
+ || (strnicmp(data, "path", 4) == 0)) {
+ if (!value || !*value) {
+ printk(KERN_WARNING "CIFS: invalid path to "
+ "network resource\n");
+ return 1; /* needs_arg; */
+ }
+ if ((temp_len = strnlen(value, 300)) < 300) {
+ vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
+ if (vol->UNC == NULL)
+ return 1;
+ strcpy(vol->UNC, value);
+ if (strncmp(vol->UNC, "//", 2) == 0) {
+ vol->UNC[0] = '\\';
+ vol->UNC[1] = '\\';
+ } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
+ printk(KERN_WARNING
+ "CIFS: UNC Path does not begin "
+ "with // or \\\\ \n");
+ return 1;
+ }
+ } else {
+ printk(KERN_WARNING "CIFS: UNC name too long\n");
+ return 1;
+ }
+ } else if ((strnicmp(data, "domain", 3) == 0)
+ || (strnicmp(data, "workgroup", 5) == 0)) {
+ if (!value || !*value) {
+ printk(KERN_WARNING "CIFS: invalid domain name\n");
+ return 1; /* needs_arg; */
+ }
+ /* BB are there cases in which a comma can be valid in
+ a domain name and need special handling? */
+ if (strnlen(value, 256) < 256) {
+ vol->domainname = value;
+ cFYI(1, ("Domain name set"));
+ } else {
+ printk(KERN_WARNING "CIFS: domain name too "
+ "long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "prefixpath", 10) == 0) {
+ if (!value || !*value) {
+ printk(KERN_WARNING
+ "CIFS: invalid path prefix\n");
+ return 1; /* needs_argument */
+ }
+ if ((temp_len = strnlen(value, 1024)) < 1024) {
+ if (value[0] != '/')
+ temp_len++; /* missing leading slash */
+ vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
+ if (vol->prepath == NULL)
+ return 1;
+ if (value[0] != '/') {
+ vol->prepath[0] = '/';
+ strcpy(vol->prepath+1, value);
+ } else
+ strcpy(vol->prepath, value);
+ cFYI(1, ("prefix path %s", vol->prepath));
+ } else {
+ printk(KERN_WARNING "CIFS: prefix too long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "iocharset", 9) == 0) {
+ if (!value || !*value) {
+ printk(KERN_WARNING "CIFS: invalid iocharset "
+ "specified\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 65) < 65) {
+ if (strnicmp(value, "default", 7))
+ vol->iocharset = value;
+ /* if iocharset not set then load_nls_default
+ is used by caller */
+ cFYI(1, ("iocharset set to %s", value));
+ } else {
+ printk(KERN_WARNING "CIFS: iocharset name "
+ "too long.\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "uid", 3) == 0) {
+ if (value && *value) {
+ vol->linux_uid =
+ simple_strtoul(value, &value, 0);
+ vol->override_uid = 1;
+ }
+ } else if (strnicmp(data, "gid", 3) == 0) {
+ if (value && *value) {
+ vol->linux_gid =
+ simple_strtoul(value, &value, 0);
+ vol->override_gid = 1;
+ }
+ } else if (strnicmp(data, "file_mode", 4) == 0) {
+ if (value && *value) {
+ vol->file_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "dir_mode", 4) == 0) {
+ if (value && *value) {
+ vol->dir_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "dirmode", 4) == 0) {
+ if (value && *value) {
+ vol->dir_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "port", 4) == 0) {
+ if (value && *value) {
+ vol->port =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "rsize", 5) == 0) {
+ if (value && *value) {
+ vol->rsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "wsize", 5) == 0) {
+ if (value && *value) {
+ vol->wsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "sockopt", 5) == 0) {
+ if (value && *value) {
+ vol->sockopt =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "netbiosname", 4) == 0) {
+ if (!value || !*value || (*value == ' ')) {
+ cFYI(1, ("invalid (empty) netbiosname"));
+ } else {
+ memset(vol->source_rfc1001_name, 0x20, 15);
+ for (i = 0; i < 15; i++) {
+ /* BB are there cases in which a comma can be
+ valid in this workstation netbios name (and need
+ special handling)? */
+
+ /* We do not uppercase netbiosname for user */
+ if (value[i] == 0)
+ break;
+ else
+ vol->source_rfc1001_name[i] =
+ value[i];
+ }
+ /* The string has 16th byte zero still from
+ set at top of the function */
+ if ((i == 15) && (value[i] != 0))
+ printk(KERN_WARNING "CIFS: netbiosname"
+ " longer than 15 truncated.\n");
+ }
+ } else if (strnicmp(data, "servern", 7) == 0) {
+ /* servernetbiosname specified override *SMBSERVER */
+ if (!value || !*value || (*value == ' ')) {
+ cFYI(1, ("empty server netbiosname specified"));
+ } else {
+ /* last byte, type, is 0x20 for servr type */
+ memset(vol->target_rfc1001_name, 0x20, 16);
+
+ for (i = 0; i < 15; i++) {
+ /* BB are there cases in which a comma can be
+ valid in this workstation netbios name
+ (and need special handling)? */
+
+ /* user or mount helper must uppercase
+ the netbiosname */
+ if (value[i] == 0)
+ break;
+ else
+ vol->target_rfc1001_name[i] =
+ value[i];
+ }
+ /* The string has 16th byte zero still from
+ set at top of the function */
+ if ((i == 15) && (value[i] != 0))
+ printk(KERN_WARNING "CIFS: server net"
+ "biosname longer than 15 truncated.\n");
+ }
+ } else if (strnicmp(data, "credentials", 4) == 0) {
+ /* ignore */
+ } else if (strnicmp(data, "version", 3) == 0) {
+ /* ignore */
+ } else if (strnicmp(data, "guest", 5) == 0) {
+ /* ignore */
+ } else if (strnicmp(data, "rw", 2) == 0) {
+ vol->rw = true;
+ } else if (strnicmp(data, "noblocksend", 11) == 0) {
+ vol->noblocksnd = 1;
+ } else if (strnicmp(data, "noautotune", 10) == 0) {
+ vol->noautotune = 1;
+ } else if ((strnicmp(data, "suid", 4) == 0) ||
+ (strnicmp(data, "nosuid", 6) == 0) ||
+ (strnicmp(data, "exec", 4) == 0) ||
+ (strnicmp(data, "noexec", 6) == 0) ||
+ (strnicmp(data, "nodev", 5) == 0) ||
+ (strnicmp(data, "noauto", 6) == 0) ||
+ (strnicmp(data, "dev", 3) == 0)) {
+ /* The mount tool or mount.cifs helper (if present)
+ uses these opts to set flags, and the flags are read
+ by the kernel vfs layer before we get here (ie
+ before read super) so there is no point trying to
+ parse these options again and set anything and it
+ is ok to just ignore them */
+ continue;
+ } else if (strnicmp(data, "ro", 2) == 0) {
+ vol->rw = false;
+ } else if (strnicmp(data, "hard", 4) == 0) {
+ vol->retry = 1;
+ } else if (strnicmp(data, "soft", 4) == 0) {
+ vol->retry = 0;
+ } else if (strnicmp(data, "perm", 4) == 0) {
+ vol->noperm = 0;
+ } else if (strnicmp(data, "noperm", 6) == 0) {
+ vol->noperm = 1;
+ } else if (strnicmp(data, "mapchars", 8) == 0) {
+ vol->remap = 1;
+ } else if (strnicmp(data, "nomapchars", 10) == 0) {
+ vol->remap = 0;
+ } else if (strnicmp(data, "sfu", 3) == 0) {
+ vol->sfu_emul = 1;
+ } else if (strnicmp(data, "nosfu", 5) == 0) {
+ vol->sfu_emul = 0;
+ } else if (strnicmp(data, "nodfs", 5) == 0) {
+ vol->nodfs = 1;
+ } else if (strnicmp(data, "posixpaths", 10) == 0) {
+ vol->posix_paths = 1;
+ } else if (strnicmp(data, "noposixpaths", 12) == 0) {
+ vol->posix_paths = 0;
+ } else if (strnicmp(data, "nounix", 6) == 0) {
+ vol->no_linux_ext = 1;
+ } else if (strnicmp(data, "nolinux", 7) == 0) {
+ vol->no_linux_ext = 1;
+ } else if ((strnicmp(data, "nocase", 6) == 0) ||
+ (strnicmp(data, "ignorecase", 10) == 0)) {
+ vol->nocase = 1;
+ } else if (strnicmp(data, "brl", 3) == 0) {
+ vol->nobrl = 0;
+ } else if ((strnicmp(data, "nobrl", 5) == 0) ||
+ (strnicmp(data, "nolock", 6) == 0)) {
+ vol->nobrl = 1;
+ /* turn off mandatory locking in mode
+ if remote locking is turned off since the
+ local vfs will do advisory */
+ if (vol->file_mode ==
+ (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
+ vol->file_mode = S_IALLUGO;
+ } else if (strnicmp(data, "setuids", 7) == 0) {
+ vol->setuids = 1;
+ } else if (strnicmp(data, "nosetuids", 9) == 0) {
+ vol->setuids = 0;
+ } else if (strnicmp(data, "dynperm", 7) == 0) {
+ vol->dynperm = true;
+ } else if (strnicmp(data, "nodynperm", 9) == 0) {
+ vol->dynperm = false;
+ } else if (strnicmp(data, "nohard", 6) == 0) {
+ vol->retry = 0;
+ } else if (strnicmp(data, "nosoft", 6) == 0) {
+ vol->retry = 1;
+ } else if (strnicmp(data, "nointr", 6) == 0) {
+ vol->intr = 0;
+ } else if (strnicmp(data, "intr", 4) == 0) {
+ vol->intr = 1;
+ } else if (strnicmp(data, "serverino", 7) == 0) {
+ vol->server_ino = 1;
+ } else if (strnicmp(data, "noserverino", 9) == 0) {
+ vol->server_ino = 0;
+ } else if (strnicmp(data, "cifsacl", 7) == 0) {
+ vol->cifs_acl = 1;
+ } else if (strnicmp(data, "nocifsacl", 9) == 0) {
+ vol->cifs_acl = 0;
+ } else if (strnicmp(data, "acl", 3) == 0) {
+ vol->no_psx_acl = 0;
+ } else if (strnicmp(data, "noacl", 5) == 0) {
+ vol->no_psx_acl = 1;
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ } else if (strnicmp(data, "locallease", 6) == 0) {
+ vol->local_lease = 1;
+#endif
+ } else if (strnicmp(data, "sign", 4) == 0) {
+ vol->secFlg |= CIFSSEC_MUST_SIGN;
+ } else if (strnicmp(data, "seal", 4) == 0) {
+ /* we do not do the following in secFlags because seal
+ is a per tree connection (mount) not a per socket
+ or per-smb connection option in the protocol */
+ /* vol->secFlg |= CIFSSEC_MUST_SEAL; */
+ vol->seal = 1;
+ } else if (strnicmp(data, "direct", 6) == 0) {
+ vol->direct_io = 1;
+ } else if (strnicmp(data, "forcedirectio", 13) == 0) {
+ vol->direct_io = 1;
+ } else if (strnicmp(data, "in6_addr", 8) == 0) {
+ if (!value || !*value) {
+ vol->in6_addr = NULL;
+ } else if (strnlen(value, 49) == 48) {
+ vol->in6_addr = value;
+ } else {
+ printk(KERN_WARNING "CIFS: ip v6 address not "
+ "48 characters long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "noac", 4) == 0) {
+ printk(KERN_WARNING "CIFS: Mount option noac not "
+ "supported. Instead set "
+ "/proc/fs/cifs/LookupCacheEnabled to 0\n");
+ } else
+ printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
+ data);
+ }
+ if (vol->UNC == NULL) {
+ if (devname == NULL) {
+ printk(KERN_WARNING "CIFS: Missing UNC name for mount "
+ "target\n");
+ return 1;
+ }
+ if ((temp_len = strnlen(devname, 300)) < 300) {
+ vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
+ if (vol->UNC == NULL)
+ return 1;
+ strcpy(vol->UNC, devname);
+ if (strncmp(vol->UNC, "//", 2) == 0) {
+ vol->UNC[0] = '\\';
+ vol->UNC[1] = '\\';
+ } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
+ printk(KERN_WARNING "CIFS: UNC Path does not "
+ "begin with // or \\\\ \n");
+ return 1;
+ }
+ value = strpbrk(vol->UNC+2, "/\\");
+ if (value)
+ *value = '\\';
+ } else {
+ printk(KERN_WARNING "CIFS: UNC name too long\n");
+ return 1;
+ }
+ }
+ if (vol->UNCip == NULL)
+ vol->UNCip = &vol->UNC[2];
+
+ return 0;
+}
+
+static struct TCP_Server_Info *
+cifs_find_tcp_session(struct sockaddr_storage *addr)
+{
+ struct list_head *tmp;
+ struct TCP_Server_Info *server;
+ struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
+
+ write_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &cifs_tcp_ses_list) {
+ server = list_entry(tmp, struct TCP_Server_Info,
+ tcp_ses_list);
+ /*
+ * the demux thread can exit on its own while still in CifsNew
+ * so don't accept any sockets in that state. Since the
+ * tcpStatus never changes back to CifsNew it's safe to check
+ * for this without a lock.
+ */
+ if (server->tcpStatus == CifsNew)
+ continue;
+
+ if (addr->ss_family == AF_INET &&
+ (addr4->sin_addr.s_addr !=
+ server->addr.sockAddr.sin_addr.s_addr))
+ continue;
+ else if (addr->ss_family == AF_INET6 &&
+ memcmp(&server->addr.sockAddr6.sin6_addr,
+ &addr6->sin6_addr, sizeof(addr6->sin6_addr)))
+ continue;
+
+ ++server->srv_count;
+ write_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, ("Existing tcp session with server found"));
+ return server;
+ }
+ write_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+static void
+cifs_put_tcp_session(struct TCP_Server_Info *server)
+{
+ struct task_struct *task;
+
+ write_lock(&cifs_tcp_ses_lock);
+ if (--server->srv_count > 0) {
+ write_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+
+ list_del_init(&server->tcp_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ spin_lock(&GlobalMid_Lock);
+ server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+
+ task = xchg(&server->tsk, NULL);
+ if (task)
+ force_sig(SIGKILL, task);
+}
+
+static struct cifsSesInfo *
+cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
+{
+ struct list_head *tmp;
+ struct cifsSesInfo *ses;
+
+ write_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+ if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
+ continue;
+
+ ++ses->ses_count;
+ write_unlock(&cifs_tcp_ses_lock);
+ return ses;
+ }
+ write_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+static void
+cifs_put_smb_ses(struct cifsSesInfo *ses)
+{
+ int xid;
+ struct TCP_Server_Info *server = ses->server;
+
+ write_lock(&cifs_tcp_ses_lock);
+ if (--ses->ses_count > 0) {
+ write_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+
+ list_del_init(&ses->smb_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ if (ses->status == CifsGood) {
+ xid = GetXid();
+ CIFSSMBLogoff(xid, ses);
+ _FreeXid(xid);
+ }
+ sesInfoFree(ses);
+ cifs_put_tcp_session(server);
+}
+
+static struct cifsTconInfo *
+cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
+{
+ struct list_head *tmp;
+ struct cifsTconInfo *tcon;
+
+ write_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &ses->tcon_list) {
+ tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
+ if (tcon->tidStatus == CifsExiting)
+ continue;
+ if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
+ continue;
+
+ ++tcon->tc_count;
+ write_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+ }
+ write_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
+static void
+cifs_put_tcon(struct cifsTconInfo *tcon)
+{
+ int xid;
+ struct cifsSesInfo *ses = tcon->ses;
+
+ write_lock(&cifs_tcp_ses_lock);
+ if (--tcon->tc_count > 0) {
+ write_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+
+ list_del_init(&tcon->tcon_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ xid = GetXid();
+ CIFSSMBTDis(xid, tcon);
+ _FreeXid(xid);
+
+ DeleteTconOplockQEntries(tcon);
+ tconInfoFree(tcon);
+ cifs_put_smb_ses(ses);
+}
+
+int
+get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
+ const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
+ struct dfs_info3_param **preferrals, int remap)
+{
+ char *temp_unc;
+ int rc = 0;
+
+ *pnum_referrals = 0;
+ *preferrals = NULL;
+
+ if (pSesInfo->ipc_tid == 0) {
+ temp_unc = kmalloc(2 /* for slashes */ +
+ strnlen(pSesInfo->serverName,
+ SERVER_NAME_LEN_WITH_NULL * 2)
+ + 1 + 4 /* slash IPC$ */ + 2,
+ GFP_KERNEL);
+ if (temp_unc == NULL)
+ return -ENOMEM;
+ temp_unc[0] = '\\';
+ temp_unc[1] = '\\';
+ strcpy(temp_unc + 2, pSesInfo->serverName);
+ strcpy(temp_unc + 2 + strlen(pSesInfo->serverName), "\\IPC$");
+ rc = CIFSTCon(xid, pSesInfo, temp_unc, NULL, nls_codepage);
+ cFYI(1,
+ ("CIFS Tcon rc = %d ipc_tid = %d", rc, pSesInfo->ipc_tid));
+ kfree(temp_unc);
+ }
+ if (rc == 0)
+ rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
+ pnum_referrals, nls_codepage, remap);
+ /* BB map targetUNCs to dfs_info3 structures, here or
+ in CIFSGetDFSRefer BB */
+
+ return rc;
+}
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key cifs_key[2];
+static struct lock_class_key cifs_slock_key[2];
+
+static inline void
+cifs_reclassify_socket4(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ BUG_ON(sock_owned_by_user(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
+ &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
+}
+
+static inline void
+cifs_reclassify_socket6(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ BUG_ON(sock_owned_by_user(sk));
+ sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
+ &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
+}
+#else
+static inline void
+cifs_reclassify_socket4(struct socket *sock)
+{
+}
+
+static inline void
+cifs_reclassify_socket6(struct socket *sock)
+{
+}
+#endif
+
+/* See RFC1001 section 14 on representation of Netbios names */
+static void rfc1002mangle(char *target, char *source, unsigned int length)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; i < (length); i++) {
+ /* mask a nibble at a time and encode */
+ target[j] = 'A' + (0x0F & (source[i] >> 4));
+ target[j+1] = 'A' + (0x0F & source[i]);
+ j += 2;
+ }
+
+}
+
+
+static int
+ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
+ char *netbios_name, char *target_name,
+ bool noblocksnd, bool noautotune)
+{
+ int rc = 0;
+ int connected = 0;
+ __be16 orig_port = 0;
+
+ if (*csocket == NULL) {
+ rc = sock_create_kern(PF_INET, SOCK_STREAM,
+ IPPROTO_TCP, csocket);
+ if (rc < 0) {
+ cERROR(1, ("Error %d creating socket", rc));
+ *csocket = NULL;
+ return rc;
+ } else {
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1, ("Socket created"));
+ (*csocket)->sk->sk_allocation = GFP_NOFS;
+ cifs_reclassify_socket4(*csocket);
+ }
+ }
+
+ psin_server->sin_family = AF_INET;
+ if (psin_server->sin_port) { /* user overrode default port */
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof(struct sockaddr_in), 0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ if (!connected) {
+ /* save original port so we can retry user specified port
+ later if fall back ports fail this time */
+ orig_port = psin_server->sin_port;
+
+ /* do not retry on the same port we just failed on */
+ if (psin_server->sin_port != htons(CIFS_PORT)) {
+ psin_server->sin_port = htons(CIFS_PORT);
+
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof(struct sockaddr_in), 0);
+ if (rc >= 0)
+ connected = 1;
+ }
+ }
+ if (!connected) {
+ psin_server->sin_port = htons(RFC1001_PORT);
+ rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
+ psin_server,
+ sizeof(struct sockaddr_in), 0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ /* give up here - unless we want to retry on different
+ protocol families some day */
+ if (!connected) {
+ if (orig_port)
+ psin_server->sin_port = orig_port;
+ cFYI(1, ("Error %d connecting to server via ipv4", rc));
+ sock_release(*csocket);
+ *csocket = NULL;
+ return rc;
+ }
+ /* Eventually check for other socket options to change from
+ the default. sock_setsockopt not used because it expects
+ user space buffer */
+ cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
+ (*csocket)->sk->sk_sndbuf,
+ (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
+ (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
+ if (!noblocksnd)
+ (*csocket)->sk->sk_sndtimeo = 3 * HZ;
+
+ /* make the bufsizes depend on wsize/rsize and max requests */
+ if (noautotune) {
+ if ((*csocket)->sk->sk_sndbuf < (200 * 1024))
+ (*csocket)->sk->sk_sndbuf = 200 * 1024;
+ if ((*csocket)->sk->sk_rcvbuf < (140 * 1024))
+ (*csocket)->sk->sk_rcvbuf = 140 * 1024;
+ }
+
+ /* send RFC1001 sessinit */
+ if (psin_server->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 (target_name && (target_name[0] != 0)) {
+ rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+ target_name, 16);
+ } else {
+ rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+ DEFAULT_CIFS_CALLED_NAME, 16);
+ }
+
+ ses_init_buf->trailer.session_req.calling_len = 32;
+ /* calling name ends in null (byte 16) from old smb
+ convention. */
+ if (netbios_name && (netbios_name[0] != 0)) {
+ rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
+ netbios_name, 16);
+ } else {
+ rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
+ "LINUX_CIFS_CLNT", 16);
+ }
+ 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(*csocket, smb_buf, 0x44,
+ (struct sockaddr *)psin_server, noblocksnd);
+ kfree(ses_init_buf);
+ msleep(1); /* RFC1001 layer in at least one server
+ requires very short break before negprot
+ presumably because not expecting negprot
+ to follow so fast. This is a simple
+ solution that works without
+ complicating the code and causes no
+ significant slowing down on mount
+ for everyone else */
+ }
+ /* else the negprot may still work without this
+ even though malloc failed */
+
+ }
+
+ return rc;
+}
+
+static int
+ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket,
+ bool noblocksnd)
+{
+ int rc = 0;
+ int connected = 0;
+ __be16 orig_port = 0;
+
+ if (*csocket == NULL) {
+ rc = sock_create_kern(PF_INET6, SOCK_STREAM,
+ IPPROTO_TCP, csocket);
+ if (rc < 0) {
+ cERROR(1, ("Error %d creating ipv6 socket", rc));
+ *csocket = NULL;
+ return rc;
+ } else {
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1, ("ipv6 Socket created"));
+ (*csocket)->sk->sk_allocation = GFP_NOFS;
+ cifs_reclassify_socket6(*csocket);
+ }
+ }
+
+ psin_server->sin6_family = AF_INET6;
+
+ if (psin_server->sin6_port) { /* user overrode default port */
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof(struct sockaddr_in6), 0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ if (!connected) {
+ /* save original port so we can retry user specified port
+ later if fall back ports fail this time */
+
+ orig_port = psin_server->sin6_port;
+ /* do not retry on the same port we just failed on */
+ if (psin_server->sin6_port != htons(CIFS_PORT)) {
+ psin_server->sin6_port = htons(CIFS_PORT);
+
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof(struct sockaddr_in6), 0);
+ if (rc >= 0)
+ connected = 1;
+ }
+ }
+ if (!connected) {
+ psin_server->sin6_port = htons(RFC1001_PORT);
+ rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
+ psin_server, sizeof(struct sockaddr_in6), 0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ /* give up here - unless we want to retry on different
+ protocol families some day */
+ if (!connected) {
+ if (orig_port)
+ psin_server->sin6_port = orig_port;
+ cFYI(1, ("Error %d connecting to server via ipv6", rc));
+ sock_release(*csocket);
+ *csocket = NULL;
+ return rc;
+ }
+ /* Eventually check for other socket options to change from
+ the default. sock_setsockopt not used because it expects
+ user space buffer */
+ (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
+ if (!noblocksnd)
+ (*csocket)->sk->sk_sndtimeo = 3 * HZ;
+
+
+ return rc;
+}
+
+void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
+ struct super_block *sb, struct smb_vol *vol_info)
+{
+ /* if we are reconnecting then should we check to see if
+ * any requested capabilities changed locally e.g. via
+ * remount but we can not do much about it here
+ * if they have (even if we could detect it by the following)
+ * Perhaps we could add a backpointer to array of sb from tcon
+ * or if we change to make all sb to same share the same
+ * sb as NFS - then we only have one backpointer to sb.
+ * What if we wanted to mount the server share twice once with
+ * and once without posixacls or posix paths? */
+ __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+ if (vol_info && vol_info->no_linux_ext) {
+ tcon->fsUnixInfo.Capability = 0;
+ tcon->unix_ext = 0; /* Unix Extensions disabled */
+ cFYI(1, ("Linux protocol extensions disabled"));
+ return;
+ } else if (vol_info)
+ tcon->unix_ext = 1; /* Unix Extensions supported */
+
+ if (tcon->unix_ext == 0) {
+ cFYI(1, ("Unix extensions disabled so not set on reconnect"));
+ return;
+ }
+
+ if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
+ __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+
+ /* check for reconnect case in which we do not
+ want to change the mount behavior if we can avoid it */
+ if (vol_info == NULL) {
+ /* turn off POSIX ACL and PATHNAMES if not set
+ originally at mount time */
+ if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
+ cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+ if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+ if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cERROR(1, ("POSIXPATH support change"));
+ cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+ } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+ cERROR(1, ("possible reconnect error"));
+ cERROR(1,
+ ("server disabled POSIX path support"));
+ }
+ }
+
+ cap &= CIFS_UNIX_CAP_MASK;
+ if (vol_info && vol_info->no_psx_acl)
+ cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+ else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
+ cFYI(1, ("negotiated posix acl support"));
+ if (sb)
+ sb->s_flags |= MS_POSIXACL;
+ }
+
+ if (vol_info && vol_info->posix_paths == 0)
+ cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+ else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+ cFYI(1, ("negotiate posix pathnames"));
+ if (sb)
+ CIFS_SB(sb)->mnt_cifs_flags |=
+ CIFS_MOUNT_POSIX_PATHS;
+ }
+
+ /* We might be setting the path sep back to a different
+ form if we are reconnecting and the server switched its
+ posix path capability for this share */
+ if (sb && (CIFS_SB(sb)->prepathlen > 0))
+ CIFS_SB(sb)->prepath[0] = CIFS_DIR_SEP(CIFS_SB(sb));
+
+ if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) {
+ if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
+ CIFS_SB(sb)->rsize = 127 * 1024;
+ cFYI(DBG2,
+ ("larger reads not supported by srv"));
+ }
+ }
+
+
+ cFYI(1, ("Negotiate caps 0x%x", (int)cap));
+#ifdef CONFIG_CIFS_DEBUG2
+ if (cap & CIFS_UNIX_FCNTL_CAP)
+ cFYI(1, ("FCNTL cap"));
+ if (cap & CIFS_UNIX_EXTATTR_CAP)
+ cFYI(1, ("EXTATTR cap"));
+ if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cFYI(1, ("POSIX path cap"));
+ if (cap & CIFS_UNIX_XATTR_CAP)
+ cFYI(1, ("XATTR cap"));
+ if (cap & CIFS_UNIX_POSIX_ACL_CAP)
+ cFYI(1, ("POSIX ACL cap"));
+ if (cap & CIFS_UNIX_LARGE_READ_CAP)
+ cFYI(1, ("very large read cap"));
+ if (cap & CIFS_UNIX_LARGE_WRITE_CAP)
+ cFYI(1, ("very large write cap"));
+#endif /* CIFS_DEBUG2 */
+ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
+ if (vol_info == NULL) {
+ cFYI(1, ("resetting capabilities failed"));
+ } else
+ cERROR(1, ("Negotiating Unix capabilities "
+ "with the server failed. Consider "
+ "mounting with the Unix Extensions\n"
+ "disabled, if problems are found, "
+ "by specifying the nounix mount "
+ "option."));
+
+ }
+ }
+}
+
+static void
+convert_delimiter(char *path, char delim)
+{
+ int i;
+ char old_delim;
+
+ if (path == NULL)
+ return;
+
+ if (delim == '/')
+ old_delim = '\\';
+ else
+ old_delim = '/';
+
+ for (i = 0; path[i] != '\0'; i++) {
+ if (path[i] == old_delim)
+ path[i] = delim;
+ }
+}
+
+static void setup_cifs_sb(struct smb_vol *pvolume_info,
+ struct cifs_sb_info *cifs_sb)
+{
+ if (pvolume_info->rsize > CIFSMaxBufSize) {
+ cERROR(1, ("rsize %d too large, using MaxBufSize",
+ pvolume_info->rsize));
+ cifs_sb->rsize = CIFSMaxBufSize;
+ } else if ((pvolume_info->rsize) &&
+ (pvolume_info->rsize <= CIFSMaxBufSize))
+ cifs_sb->rsize = pvolume_info->rsize;
+ else /* default */
+ cifs_sb->rsize = CIFSMaxBufSize;
+
+ if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
+ cERROR(1, ("wsize %d too large, using 4096 instead",
+ pvolume_info->wsize));
+ cifs_sb->wsize = 4096;
+ } else if (pvolume_info->wsize)
+ cifs_sb->wsize = pvolume_info->wsize;
+ else
+ cifs_sb->wsize = min_t(const int,
+ PAGEVEC_SIZE * PAGE_CACHE_SIZE,
+ 127*1024);
+ /* old default of CIFSMaxBufSize was too small now
+ that SMB Write2 can send multiple pages in kvec.
+ RFC1001 does not describe what happens when frame
+ bigger than 128K is sent so use that as max in
+ conjunction with 52K kvec constraint on arch with 4K
+ page size */
+
+ if (cifs_sb->rsize < 2048) {
+ cifs_sb->rsize = 2048;
+ /* Windows ME may prefer this */
+ cFYI(1, ("readsize set to minimum: 2048"));
+ }
+ /* calculate prepath */
+ cifs_sb->prepath = pvolume_info->prepath;
+ if (cifs_sb->prepath) {
+ cifs_sb->prepathlen = strlen(cifs_sb->prepath);
+ /* we can not convert the / to \ in the path
+ separators in the prefixpath yet because we do not
+ know (until reset_cifs_unix_caps is called later)
+ whether POSIX PATH CAP is available. We normalize
+ the / to \ after reset_cifs_unix_caps is called */
+ pvolume_info->prepath = NULL;
+ } else
+ cifs_sb->prepathlen = 0;
+ cifs_sb->mnt_uid = pvolume_info->linux_uid;
+ cifs_sb->mnt_gid = pvolume_info->linux_gid;
+ cifs_sb->mnt_file_mode = pvolume_info->file_mode;
+ cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
+ cFYI(1, ("file mode: 0x%x dir mode: 0x%x",
+ cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
+
+ if (pvolume_info->noperm)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
+ if (pvolume_info->setuids)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+ if (pvolume_info->server_ino)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
+ if (pvolume_info->remap)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
+ if (pvolume_info->no_xattr)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+ if (pvolume_info->sfu_emul)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
+ if (pvolume_info->nobrl)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+ if (pvolume_info->cifs_acl)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
+ if (pvolume_info->override_uid)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
+ if (pvolume_info->override_gid)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
+ if (pvolume_info->dynperm)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
+ if (pvolume_info->direct_io) {
+ cFYI(1, ("mounting share using direct i/o"));
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
+ }
+
+ if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
+ cERROR(1, ("mount option dynperm ignored if cifsacl "
+ "mount option supported"));
+}
+
+int
+cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
+ char *mount_data, const char *devname)
+{
+ int rc = 0;
+ int xid;
+ struct socket *csocket = NULL;
+ struct sockaddr_storage addr;
+ struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
+ struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
+ struct smb_vol volume_info;
+ struct cifsSesInfo *pSesInfo = NULL;
+ struct cifsTconInfo *tcon = NULL;
+ struct TCP_Server_Info *srvTcp = NULL;
+
+ xid = GetXid();
+
+/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
+
+ memset(&addr, 0, sizeof(struct sockaddr_storage));
+ memset(&volume_info, 0, sizeof(struct smb_vol));
+ if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (volume_info.nullauth) {
+ cFYI(1, ("null user"));
+ volume_info.username = "";
+ } else if (volume_info.username) {
+ /* BB fixme parse for domain name here */
+ cFYI(1, ("Username: %s", volume_info.username));
+ } else {
+ cifserror("No username specified");
+ /* In userspace mount helper we can get user name from alternate
+ locations such as env variables and files on disk */
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (volume_info.UNCip && volume_info.UNC) {
+ rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
+ &sin_server->sin_addr.s_addr);
+
+ if (rc <= 0) {
+ /* not ipv4 address, try ipv6 */
+ rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
+ &sin_server6->sin6_addr.in6_u);
+ if (rc > 0)
+ addr.ss_family = AF_INET6;
+ } else {
+ addr.ss_family = AF_INET;
+ }
+
+ if (rc <= 0) {
+ /* we failed translating address */
+ rc = -EINVAL;
+ goto out;
+ }
+
+ cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
+ /* success */
+ rc = 0;
+ } else if (volume_info.UNCip) {
+ /* BB using ip addr as server name to connect to the
+ DFS root below */
+ cERROR(1, ("Connecting to DFS root not implemented yet"));
+ rc = -EINVAL;
+ goto out;
+ } else /* which servers 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;
+ }
+
+ /* this is needed for ASCII cp to Unicode converts */
+ if (volume_info.iocharset == NULL) {
+ cifs_sb->local_nls = load_nls_default();
+ /* load_nls_default can not return null */
+ } else {
+ cifs_sb->local_nls = load_nls(volume_info.iocharset);
+ if (cifs_sb->local_nls == NULL) {
+ cERROR(1, ("CIFS mount error: iocharset %s not found",
+ volume_info.iocharset));
+ rc = -ELIBACC;
+ goto out;
+ }
+ }
+
+ srvTcp = cifs_find_tcp_session(&addr);
+ if (!srvTcp) { /* create socket */
+ 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 */
+ sin_server6->sin6_port = htons(volume_info.port);
+ rc = ipv6_connect(sin_server6, &csocket,
+ volume_info.noblocksnd);
+ } else {
+ sin_server->sin_port = htons(volume_info.port);
+ rc = ipv4_connect(sin_server, &csocket,
+ volume_info.source_rfc1001_name,
+ volume_info.target_rfc1001_name,
+ volume_info.noblocksnd,
+ volume_info.noautotune);
+ }
+ if (rc < 0) {
+ cERROR(1, ("Error connecting to socket. "
+ "Aborting operation"));
+ if (csocket != NULL)
+ sock_release(csocket);
+ goto out;
+ }
+
+ srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
+ if (!srvTcp) {
+ rc = -ENOMEM;
+ sock_release(csocket);
+ goto out;
+ } else {
+ srvTcp->noblocksnd = volume_info.noblocksnd;
+ srvTcp->noautotune = volume_info.noautotune;
+ if (addr.ss_family == AF_INET6)
+ memcpy(&srvTcp->addr.sockAddr6, sin_server6,
+ sizeof(struct sockaddr_in6));
+ else
+ memcpy(&srvTcp->addr.sockAddr, sin_server,
+ sizeof(struct sockaddr_in));
+ atomic_set(&srvTcp->inFlight, 0);
+ /* BB Add code for ipv6 case too */
+ srvTcp->ssocket = csocket;
+ srvTcp->hostname = extract_hostname(volume_info.UNC);
+ if (IS_ERR(srvTcp->hostname)) {
+ rc = PTR_ERR(srvTcp->hostname);
+ sock_release(csocket);
+ goto out;
+ }
+ init_waitqueue_head(&srvTcp->response_q);
+ init_waitqueue_head(&srvTcp->request_q);
+ INIT_LIST_HEAD(&srvTcp->pending_mid_q);
+ /* at this point we are the only ones with the pointer
+ to the struct since the kernel thread not created yet
+ so no need to spinlock this init of tcpStatus */
+ srvTcp->tcpStatus = CifsNew;
+ init_MUTEX(&srvTcp->tcpSem);
+ srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
+ if (IS_ERR(srvTcp->tsk)) {
+ rc = PTR_ERR(srvTcp->tsk);
+ cERROR(1, ("error %d create cifsd thread", rc));
+ srvTcp->tsk = NULL;
+ sock_release(csocket);
+ kfree(srvTcp->hostname);
+ goto out;
+ }
+ rc = 0;
+ memcpy(srvTcp->workstation_RFC1001_name,
+ volume_info.source_rfc1001_name, 16);
+ memcpy(srvTcp->server_RFC1001_name,
+ volume_info.target_rfc1001_name, 16);
+ srvTcp->sequence_number = 0;
+ INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
+ INIT_LIST_HEAD(&srvTcp->smb_ses_list);
+ ++srvTcp->srv_count;
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&srvTcp->tcp_ses_list,
+ &cifs_tcp_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+ }
+ }
+
+ pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
+ if (pSesInfo) {
+ cFYI(1, ("Existing smb sess found (status=%d)",
+ pSesInfo->status));
+ /*
+ * The existing SMB session already has a reference to srvTcp,
+ * so we can put back the extra one we got before
+ */
+ cifs_put_tcp_session(srvTcp);
+
+ down(&pSesInfo->sesSem);
+ if (pSesInfo->need_reconnect) {
+ cFYI(1, ("Session needs reconnect"));
+ rc = cifs_setup_session(xid, pSesInfo,
+ cifs_sb->local_nls);
+ }
+ up(&pSesInfo->sesSem);
+ } else if (!rc) {
+ cFYI(1, ("Existing smb sess not found"));
+ pSesInfo = sesInfoAlloc();
+ if (pSesInfo == NULL) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+
+ /* new SMB session uses our srvTcp ref */
+ pSesInfo->server = srvTcp;
+ sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
+ NIPQUAD(sin_server->sin_addr.s_addr));
+
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ /* volume_info.password freed at unmount */
+ if (volume_info.password) {
+ pSesInfo->password = volume_info.password;
+ /* set to NULL to prevent freeing on exit */
+ volume_info.password = NULL;
+ }
+ if (volume_info.username)
+ strncpy(pSesInfo->userName, volume_info.username,
+ MAX_USERNAME_SIZE);
+ if (volume_info.domainname) {
+ int len = strlen(volume_info.domainname);
+ pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
+ if (pSesInfo->domainName)
+ strcpy(pSesInfo->domainName,
+ volume_info.domainname);
+ }
+ pSesInfo->linux_uid = volume_info.linux_uid;
+ pSesInfo->overrideSecFlg = volume_info.secFlg;
+ down(&pSesInfo->sesSem);
+
+ /* BB FIXME need to pass vol->secFlgs BB */
+ rc = cifs_setup_session(xid, pSesInfo,
+ cifs_sb->local_nls);
+ up(&pSesInfo->sesSem);
+ }
+
+ /* search for existing tcon to this server share */
+ if (!rc) {
+ setup_cifs_sb(&volume_info, cifs_sb);
+
+ tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
+ if (tcon) {
+ cFYI(1, ("Found match on UNC path"));
+ /* existing tcon already has a reference */
+ cifs_put_smb_ses(pSesInfo);
+ if (tcon->seal != volume_info.seal)
+ cERROR(1, ("transport encryption setting "
+ "conflicts with existing tid"));
+ } else {
+ tcon = tconInfoAlloc();
+ if (tcon == NULL) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+ tcon->ses = pSesInfo;
+
+ /* check for null share name ie connect to dfs root */
+ if ((strchr(volume_info.UNC + 3, '\\') == NULL)
+ && (strchr(volume_info.UNC + 3, '/') == NULL)) {
+ /* rc = connect_to_dfs_path(...) */
+ cFYI(1, ("DFS root not supported"));
+ rc = -ENODEV;
+ goto mount_fail_check;
+ } else {
+ /* BB Do we need to wrap sesSem around
+ * this TCon call and Unix SetFS as
+ * we do on SessSetup and reconnect? */
+ rc = CIFSTCon(xid, pSesInfo, volume_info.UNC,
+ tcon, cifs_sb->local_nls);
+ cFYI(1, ("CIFS Tcon rc = %d", rc));
+ if (volume_info.nodfs) {
+ tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
+ cFYI(1, ("DFS disabled (%d)",
+ tcon->Flags));
+ }
+ }
+ if (rc)
+ goto mount_fail_check;
+ tcon->seal = volume_info.seal;
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
+ write_unlock(&cifs_tcp_ses_lock);
+ }
+
+ /* we can have only one retry value for a connection
+ to a share so for resources mounted more than once
+ to the same server share the last value passed in
+ for the retry flag is used */
+ tcon->retry = volume_info.retry;
+ tcon->nocase = volume_info.nocase;
+ tcon->local_lease = volume_info.local_lease;
+ }
+ if (pSesInfo) {
+ if (pSesInfo->capabilities & CAP_LARGE_FILES) {
+ sb->s_maxbytes = (u64) 1 << 63;
+ } else
+ sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */
+ }
+
+ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
+ sb->s_time_gran = 100;
+
+mount_fail_check:
+ /* on error free sesinfo and tcon struct if needed */
+ if (rc) {
+ /* If find_unc succeeded then rc == 0 so we can not end */
+ /* up accidently freeing someone elses tcon struct */
+ if (tcon)
+ cifs_put_tcon(tcon);
+ else if (pSesInfo)
+ cifs_put_smb_ses(pSesInfo);
+ else
+ cifs_put_tcp_session(srvTcp);
+ goto out;
+ }
+ cifs_sb->tcon = tcon;
+
+ /* do not care if following two calls succeed - informational */
+ if (!tcon->ipc) {
+ CIFSSMBQFSDeviceInfo(xid, tcon);
+ CIFSSMBQFSAttributeInfo(xid, tcon);
+ }
+
+ /* tell server which Unix caps we support */
+ if (tcon->ses->capabilities & CAP_UNIX)
+ /* reset of caps checks mount to see if unix extensions
+ disabled for just this mount */
+ reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+ else
+ tcon->unix_ext = 0; /* server does not support them */
+
+ /* convert forward to back slashes in prepath here if needed */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
+ convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+
+ if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
+ cifs_sb->rsize = 1024 * 127;
+ cFYI(DBG2, ("no very large read support, rsize now 127K"));
+ }
+ if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
+ cifs_sb->wsize = min(cifs_sb->wsize,
+ (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
+ if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
+ cifs_sb->rsize = min(cifs_sb->rsize,
+ (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
+
+ /* volume_info.password is freed above when existing session found
+ (in which case it is not needed anymore) but when new sesion is created
+ the password ptr is put in the new session structure (in which case the
+ password will be freed at unmount time) */
+out:
+ /* zero out password before freeing */
+ if (volume_info.password != NULL) {
+ memset(volume_info.password, 0, strlen(volume_info.password));
+ kfree(volume_info.password);
+ }
+ kfree(volume_info.UNC);
+ kfree(volume_info.prepath);
+ FreeXid(xid);
+ return rc;
+}
+
+static int
+CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
+ char session_key[CIFS_SESS_KEY_SIZE],
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *user;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ __u32 capabilities;
+ __u16 count;
+
+ cFYI(1, ("In sesssetup"));
+ if (ses == NULL)
+ return -EINVAL;
+ user = ses->userName;
+ domain = ses->domainName;
+ smb_buffer = cifs_buf_get();
+
+ if (smb_buffer == NULL)
+ return -ENOMEM;
+
+ smb_buffer_response = smb_buffer;
+ pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 13 /* wct */ );
+
+ smb_buffer->Mid = GetNextMid(ses->server);
+ pSMB->req_no_secext.AndXCommand = 0xFF;
+ pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ if (ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+
+ pSMB->req_no_secext.CaseInsensitivePasswordLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ bcc_ptr = pByteArea(smb_buffer);
+ memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ if (user == NULL)
+ bytes_returned = 0; /* skip null user */
+ else
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, user, 100,
+ nls_codepage);
+ /* convert number of 16 bit words to bytes */
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2; /* trailing null */
+ if (domain == NULL)
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr,
+ "CIFS_LINUX_DOM", 32, nls_codepage);
+ else
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release,
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ } else {
+ if (user != NULL) {
+ strncpy(bcc_ptr, user, 200);
+ bcc_ptr += strnlen(user, 200);
+ }
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ if (domain == NULL) {
+ strcpy(bcc_ptr, "CIFS_LINUX_DOM");
+ bcc_ptr += strlen("CIFS_LINUX_DOM") + 1;
+ } else {
+ strncpy(bcc_ptr, domain, 64);
+ bcc_ptr += strnlen(domain, 64);
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, utsname()->release);
+ bcc_ptr += strlen(utsname()->release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ }
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, CIFS_LONG_OP);
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
+ } else if ((smb_buffer_response->WordCount == 3)
+ || (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
+ ses->Suid = smb_buffer_response->Uid; /* UID left in wire format
+ (little endian) */
+ cFYI(1, ("UID = %d ", ses->Suid));
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+ bcc_ptr = pByteArea(smb_buffer_response);
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len < pSMBr->resp.ByteCount))) {
+ if (pSMBr->resp.hdr.WordCount == 4)
+ bcc_ptr += blob_len;
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response) - 1) / 2;
+ /* Unicode strings must be word
+ aligned */
+ bcc_ptr++;
+ } else {
+ remaining_words =
+ BCC(smb_buffer_response) / 2;
+ }
+ len =
+ UniStrnlen((wchar_t *) bcc_ptr,
+ remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ if (ses->serverOS)
+ kfree(ses->serverOS);
+ ses->serverOS = kzalloc(2 * (len + 1),
+ GFP_KERNEL);
+ if (ses->serverOS == NULL)
+ goto sesssetup_nomem;
+ cifs_strfromUCS_le(ses->serverOS,
+ (__le16 *)bcc_ptr,
+ len, nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)bcc_ptr,
+ remaining_words-1);
+ kfree(ses->serverNOS);
+ ses->serverNOS = kzalloc(2 * (len + 1),
+ GFP_KERNEL);
+ if (ses->serverNOS == NULL)
+ goto sesssetup_nomem;
+ cifs_strfromUCS_le(ses->serverNOS,
+ (__le16 *)bcc_ptr,
+ len, nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1 + (2 * len)] = 0;
+ if (strncmp(ses->serverNOS,
+ "NT LAN Manager 4", 16) == 0) {
+ cFYI(1, ("NT4 server"));
+ ses->flags |= CIFS_SES_NT4;
+ }
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string is not always null terminated
+ (for e.g. for Windows XP & 2000) */
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2*(len+1),
+ GFP_KERNEL);
+ if (ses->serverDomain == NULL)
+ goto sesssetup_nomem;
+ cifs_strfromUCS_le(ses->serverDomain,
+ (__le16 *)bcc_ptr,
+ len, nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverDomain[2*len] = 0;
+ ses->serverDomain[1+(2*len)] = 0;
+ } else { /* else no more room so create
+ dummy domain string */
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2, GFP_KERNEL);
+ }
+ } else { /* no room so create dummy domain
+ and NOS string */
+
+ /* if these kcallocs fail not much we
+ can do, but better to not fail the
+ sesssetup itself */
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2, GFP_KERNEL);
+ kfree(ses->serverNOS);
+ ses->serverNOS =
+ kzalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) - (long)
+ pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ kfree(ses->serverOS);
+ ses->serverOS = kzalloc(len + 1,
+ GFP_KERNEL);
+ if (ses->serverOS == NULL)
+ goto sesssetup_nomem;
+ strncpy(ses->serverOS, bcc_ptr, len);
+
+ bcc_ptr += len;
+ /* null terminate the string */
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ kfree(ses->serverNOS);
+ ses->serverNOS = kzalloc(len + 1,
+ GFP_KERNEL);
+ if (ses->serverNOS == NULL)
+ goto sesssetup_nomem;
+ strncpy(ses->serverNOS, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain = kzalloc(len + 1,
+ GFP_KERNEL);
+ if (ses->serverDomain == NULL)
+ goto sesssetup_nomem;
+ strncpy(ses->serverDomain, bcc_ptr,
+ len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1,
+ ("Variable field of length %d "
+ "extends beyond end of smb ",
+ len));
+ }
+ } else {
+ cERROR(1,
+ (" Security Blob Length extends beyond "
+ "end of SMB"));
+ }
+ } else {
+ cERROR(1,
+ (" Invalid Word count %d: ",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+sesssetup_nomem: /* do not return an error on nomem for the info strings,
+ since that could make reconnection harder, and
+ reconnection might be needed to free memory */
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+
+static int
+CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
+ struct cifsSesInfo *ses, bool *pNTLMv2_flag,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ int SecurityBlobLength = sizeof(NEGOTIATE_MESSAGE);
+ PNEGOTIATE_MESSAGE SecurityBlob;
+ PCHALLENGE_MESSAGE SecurityBlob2;
+ __u32 negotiate_flags, capabilities;
+ __u16 count;
+
+ cFYI(1, ("In NTLMSSP sesssetup (negotiate)"));
+ if (ses == NULL)
+ return -EINVAL;
+ domain = ses->domainName;
+ *pNTLMv2_flag = false;
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+ pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
+ pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 12 /* wct */ );
+
+ smb_buffer->Mid = GetNextMid(ses->server);
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
+
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_EXTENDED_SECURITY;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+
+ bcc_ptr = (char *) &pSMB->req.SecurityBlob;
+ SecurityBlob = (PNEGOTIATE_MESSAGE) bcc_ptr;
+ strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
+ SecurityBlob->MessageType = NtLmNegotiate;
+ negotiate_flags =
+ NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM |
+ NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_56 |
+ /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128;
+ if (sign_CIFS_PDUs)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN;
+/* if (ntlmv2_support)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/
+ /* setup pointers to domain name and workstation name */
+ bcc_ptr += SecurityBlobLength;
+
+ SecurityBlob->WorkstationName.Buffer = 0;
+ SecurityBlob->WorkstationName.Length = 0;
+ SecurityBlob->WorkstationName.MaximumLength = 0;
+
+ /* Domain not sent on first Sesssetup in NTLMSSP, instead it is sent
+ along with username on auth request (ie the response to challenge) */
+ SecurityBlob->DomainName.Buffer = 0;
+ SecurityBlob->DomainName.Length = 0;
+ SecurityBlob->DomainName.MaximumLength = 0;
+ if (ses->capabilities & CAP_UNICODE) {
+ if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2; /* null terminate Linux version */
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null terminate network opsys string */
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null domain */
+ } else { /* ASCII */
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, utsname()->release);
+ bcc_ptr += strlen(utsname()->release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ bcc_ptr++; /* empty domain field */
+ *bcc_ptr = 0;
+ }
+ SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
+ pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, CIFS_LONG_OP);
+
+ if (smb_buffer_response->Status.CifsError ==
+ cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
+ rc = 0;
+
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
+ } else if ((smb_buffer_response->WordCount == 3)
+ || (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login"));
+ /* Do we want to set anything in SesInfo struct when guest login? */
+
+ bcc_ptr = pByteArea(smb_buffer_response);
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+
+ SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr;
+ if (SecurityBlob2->MessageType != NtLmChallenge) {
+ cFYI(1,
+ ("Unexpected NTLMSSP message type received %d",
+ SecurityBlob2->MessageType));
+ } else if (ses) {
+ ses->Suid = smb_buffer_response->Uid; /* UID left in le format */
+ cFYI(1, ("UID = %d", ses->Suid));
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len <
+ pSMBr->resp.ByteCount))) {
+
+ if (pSMBr->resp.hdr.WordCount == 4) {
+ bcc_ptr += blob_len;
+ cFYI(1, ("Security Blob Length %d",
+ blob_len));
+ }
+
+ cFYI(1, ("NTLMSSP Challenge rcvd"));
+
+ memcpy(ses->server->cryptKey,
+ SecurityBlob2->Challenge,
+ CIFS_CRYPTO_KEY_SIZE);
+ if (SecurityBlob2->NegotiateFlags &
+ cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2))
+ *pNTLMv2_flag = true;
+
+ if ((SecurityBlob2->NegotiateFlags &
+ cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
+ || (sign_CIFS_PDUs > 1))
+ ses->server->secMode |=
+ SECMODE_SIGN_REQUIRED;
+ if ((SecurityBlob2->NegotiateFlags &
+ cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs))
+ ses->server->secMode |=
+ SECMODE_SIGN_ENABLED;
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response)
+ - 1) / 2;
+ /* Must word align unicode strings */
+ bcc_ptr++;
+ } else {
+ remaining_words =
+ BCC
+ (smb_buffer_response) / 2;
+ }
+ len =
+ UniStrnlen((wchar_t *) bcc_ptr,
+ remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ if (ses->serverOS)
+ kfree(ses->serverOS);
+ ses->serverOS =
+ kzalloc(2 * (len + 1), GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverOS,
+ (__le16 *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)
+ bcc_ptr,
+ remaining_words
+ - 1);
+ kfree(ses->serverNOS);
+ ses->serverNOS =
+ kzalloc(2 * (len + 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le(ses->
+ serverNOS,
+ (__le16 *)
+ bcc_ptr,
+ len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1 +
+ (2 * len)] = 0;
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string not always null terminated
+ (for e.g. for Windows XP & 2000) */
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2 *
+ (len +
+ 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le
+ (ses->serverDomain,
+ (__le16 *)bcc_ptr,
+ len, nls_codepage);
+ bcc_ptr +=
+ 2 * (len + 1);
+ ses->serverDomain[2*len]
+ = 0;
+ ses->serverDomain
+ [1 + (2 * len)]
+ = 0;
+ } /* else no more room so create dummy domain string */
+ else {
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2,
+ GFP_KERNEL);
+ }
+ } else { /* no room so create dummy domain and NOS string */
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2, GFP_KERNEL);
+ kfree(ses->serverNOS);
+ ses->serverNOS =
+ kzalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) - (long)
+ pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ if (ses->serverOS)
+ kfree(ses->serverOS);
+ ses->serverOS =
+ kzalloc(len + 1,
+ GFP_KERNEL);
+ strncpy(ses->serverOS,
+ bcc_ptr, len);
+
+ bcc_ptr += len;
+ bcc_ptr[0] = 0; /* null terminate string */
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ kfree(ses->serverNOS);
+ ses->serverNOS =
+ kzalloc(len + 1,
+ GFP_KERNEL);
+ strncpy(ses->serverNOS, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(len + 1,
+ GFP_KERNEL);
+ strncpy(ses->serverDomain,
+ bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1,
+ ("field of length %d "
+ "extends beyond end of smb",
+ len));
+ }
+ } else {
+ cERROR(1, ("Security Blob Length extends beyond"
+ " end of SMB"));
+ }
+ } else {
+ cERROR(1, ("No session structure passed in."));
+ }
+ } else {
+ cERROR(1,
+ (" Invalid Word count %d:",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+static int
+CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
+ char *ntlm_session_key, bool ntlmv2_flag,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *user;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ int SecurityBlobLength = sizeof(AUTHENTICATE_MESSAGE);
+ PAUTHENTICATE_MESSAGE SecurityBlob;
+ __u32 negotiate_flags, capabilities;
+ __u16 count;
+
+ cFYI(1, ("In NTLMSSPSessSetup (Authenticate)"));
+ if (ses == NULL)
+ return -EINVAL;
+ user = ses->userName;
+ domain = ses->domainName;
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+ pSMB = (SESSION_SETUP_ANDX *)smb_buffer;
+ pSMBr = (SESSION_SETUP_ANDX *)smb_buffer_response;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 12 /* wct */ );
+
+ smb_buffer->Mid = GetNextMid(ses->server);
+ pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ pSMB->req.hdr.Uid = ses->Suid;
+
+ if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_EXTENDED_SECURITY;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+
+ bcc_ptr = (char *)&pSMB->req.SecurityBlob;
+ SecurityBlob = (PAUTHENTICATE_MESSAGE)bcc_ptr;
+ strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
+ SecurityBlob->MessageType = NtLmAuthenticate;
+ bcc_ptr += SecurityBlobLength;
+ negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
+ 0x80000000 | NTLMSSP_NEGOTIATE_128;
+ if (sign_CIFS_PDUs)
+ negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN;
+ if (ntlmv2_flag)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
+
+/* setup pointers to domain name and workstation name */
+
+ SecurityBlob->WorkstationName.Buffer = 0;
+ SecurityBlob->WorkstationName.Length = 0;
+ SecurityBlob->WorkstationName.MaximumLength = 0;
+ SecurityBlob->SessionKey.Length = 0;
+ SecurityBlob->SessionKey.MaximumLength = 0;
+ SecurityBlob->SessionKey.Buffer = 0;
+
+ SecurityBlob->LmChallengeResponse.Length = 0;
+ SecurityBlob->LmChallengeResponse.MaximumLength = 0;
+ SecurityBlob->LmChallengeResponse.Buffer = 0;
+
+ SecurityBlob->NtChallengeResponse.Length =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ SecurityBlob->NtChallengeResponse.MaximumLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE);
+ SecurityBlob->NtChallengeResponse.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ SecurityBlobLength += CIFS_SESS_KEY_SIZE;
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ if (domain == NULL) {
+ SecurityBlob->DomainName.Buffer = 0;
+ SecurityBlob->DomainName.Length = 0;
+ SecurityBlob->DomainName.MaximumLength = 0;
+ } else {
+ __u16 ln = cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
+ nls_codepage);
+ ln *= 2;
+ SecurityBlob->DomainName.MaximumLength =
+ cpu_to_le16(ln);
+ SecurityBlob->DomainName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += ln;
+ SecurityBlobLength += ln;
+ SecurityBlob->DomainName.Length = cpu_to_le16(ln);
+ }
+ if (user == NULL) {
+ SecurityBlob->UserName.Buffer = 0;
+ SecurityBlob->UserName.Length = 0;
+ SecurityBlob->UserName.MaximumLength = 0;
+ } else {
+ __u16 ln = cifs_strtoUCS((__le16 *) bcc_ptr, user, 64,
+ nls_codepage);
+ ln *= 2;
+ SecurityBlob->UserName.MaximumLength =
+ cpu_to_le16(ln);
+ SecurityBlob->UserName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += ln;
+ SecurityBlobLength += ln;
+ SecurityBlob->UserName.Length = cpu_to_le16(ln);
+ }
+
+ /* SecurityBlob->WorkstationName.Length =
+ cifs_strtoUCS((__le16 *) bcc_ptr, "AMACHINE",64, nls_codepage);
+ SecurityBlob->WorkstationName.Length *= 2;
+ SecurityBlob->WorkstationName.MaximumLength =
+ cpu_to_le16(SecurityBlob->WorkstationName.Length);
+ SecurityBlob->WorkstationName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += SecurityBlob->WorkstationName.Length;
+ SecurityBlobLength += SecurityBlob->WorkstationName.Length;
+ SecurityBlob->WorkstationName.Length =
+ cpu_to_le16(SecurityBlob->WorkstationName.Length); */
+
+ if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2; /* null term version string */
+ bytes_returned =
+ cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null terminate network opsys string */
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null domain */
+ } else { /* ASCII */
+ if (domain == NULL) {
+ SecurityBlob->DomainName.Buffer = 0;
+ SecurityBlob->DomainName.Length = 0;
+ SecurityBlob->DomainName.MaximumLength = 0;
+ } else {
+ __u16 ln;
+ negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
+ strncpy(bcc_ptr, domain, 63);
+ ln = strnlen(domain, 64);
+ SecurityBlob->DomainName.MaximumLength =
+ cpu_to_le16(ln);
+ SecurityBlob->DomainName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += ln;
+ SecurityBlobLength += ln;
+ SecurityBlob->DomainName.Length = cpu_to_le16(ln);
+ }
+ if (user == NULL) {
+ SecurityBlob->UserName.Buffer = 0;
+ SecurityBlob->UserName.Length = 0;
+ SecurityBlob->UserName.MaximumLength = 0;
+ } else {
+ __u16 ln;
+ strncpy(bcc_ptr, user, 63);
+ ln = strnlen(user, 64);
+ SecurityBlob->UserName.MaximumLength = cpu_to_le16(ln);
+ SecurityBlob->UserName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += ln;
+ SecurityBlobLength += ln;
+ SecurityBlob->UserName.Length = cpu_to_le16(ln);
+ }
+ /* BB fill in our workstation name if known BB */
+
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, utsname()->release);
+ bcc_ptr += strlen(utsname()->release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ bcc_ptr++; /* null domain */
+ *bcc_ptr = 0;
+ }
+ SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
+ pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, CIFS_LONG_OP);
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */
+ } else if ((smb_buffer_response->WordCount == 3) ||
+ (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login")); /* BB Should we set anything
+ in SesInfo struct ? */
+/* if (SecurityBlob2->MessageType != NtLm??) {
+ cFYI("Unexpected message type on auth response is %d"));
+ } */
+
+ if (ses) {
+ cFYI(1,
+ ("Check challenge UID %d vs auth response UID %d",
+ ses->Suid, smb_buffer_response->Uid));
+ /* UID left in wire format */
+ ses->Suid = smb_buffer_response->Uid;
+ bcc_ptr = pByteArea(smb_buffer_response);
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len <
+ pSMBr->resp.ByteCount))) {
+ if (pSMBr->resp.hdr.WordCount == 4) {
+ bcc_ptr +=
+ blob_len;
+ cFYI(1,
+ ("Security Blob Length %d ",
+ blob_len));
+ }
+
+ cFYI(1,
+ ("NTLMSSP response to Authenticate "));
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response)
+ - 1) / 2;
+ bcc_ptr++; /* Unicode strings must be word aligned */
+ } else {
+ remaining_words = BCC(smb_buffer_response) / 2;
+ }
+ len = UniStrnlen((wchar_t *) bcc_ptr,
+ remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ if (ses->serverOS)
+ kfree(ses->serverOS);
+ ses->serverOS =
+ kzalloc(2 * (len + 1), GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverOS,
+ (__le16 *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)
+ bcc_ptr,
+ remaining_words
+ - 1);
+ kfree(ses->serverNOS);
+ ses->serverNOS =
+ kzalloc(2 * (len + 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le(ses->
+ serverNOS,
+ (__le16 *)
+ bcc_ptr,
+ len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1+(2*len)] = 0;
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string not always null terminated (e.g. for Windows XP & 2000) */
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(2 *
+ (len +
+ 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le
+ (ses->
+ serverDomain,
+ (__le16 *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr +=
+ 2 * (len + 1);
+ ses->
+ serverDomain[2
+ * len]
+ = 0;
+ ses->
+ serverDomain[1
+ +
+ (2
+ *
+ len)]
+ = 0;
+ } /* else no more room so create dummy domain string */
+ else {
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain = kzalloc(2,GFP_KERNEL);
+ }
+ } else { /* no room so create dummy domain and NOS string */
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain = kzalloc(2, GFP_KERNEL);
+ kfree(ses->serverNOS);
+ ses->serverNOS = kzalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) -
+ (long) pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ if (ses->serverOS)
+ kfree(ses->serverOS);
+ ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
+ strncpy(ses->serverOS,bcc_ptr, len);
+
+ bcc_ptr += len;
+ bcc_ptr[0] = 0; /* null terminate the string */
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ kfree(ses->serverNOS);
+ ses->serverNOS = kzalloc(len+1,
+ GFP_KERNEL);
+ strncpy(ses->serverNOS,
+ bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ if (ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain =
+ kzalloc(len+1,
+ GFP_KERNEL);
+ strncpy(ses->serverDomain,
+ bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1, ("field of length %d "
+ "extends beyond end of smb ",
+ len));
+ }
+ } else {
+ cERROR(1, ("Security Blob extends beyond end "
+ "of SMB"));
+ }
+ } else {
+ cERROR(1, ("No session structure passed in."));
+ }
+ } else {
+ cERROR(1, ("Invalid Word count %d: ",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+
+int
+CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
+ const char *tree, struct cifsTconInfo *tcon,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ TCONX_REQ *pSMB;
+ TCONX_RSP *pSMBr;
+ unsigned char *bcc_ptr;
+ int rc = 0;
+ int length;
+ __u16 count;
+
+ if (ses == NULL)
+ return -EIO;
+
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+
+ header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
+ NULL /*no tid */ , 4 /*wct */ );
+
+ smb_buffer->Mid = GetNextMid(ses->server);
+ smb_buffer->Uid = ses->Suid;
+ pSMB = (TCONX_REQ *) smb_buffer;
+ pSMBr = (TCONX_RSP *) smb_buffer_response;
+
+ pSMB->AndXCommand = 0xFF;
+ pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
+ bcc_ptr = &pSMB->Password[0];
+ if ((ses->server->secMode) & SECMODE_USER) {
+ pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
+ *bcc_ptr = 0; /* password is null byte */
+ bcc_ptr++; /* skip password */
+ /* already aligned so no need to do it below */
+ } else {
+ pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ /* BB FIXME add code to fail this if NTLMv2 or Kerberos
+ specified as required (when that support is added to
+ the vfs in the future) as only NTLM or the much
+ weaker LANMAN (which we do not send by default) is accepted
+ by Samba (not sure whether other servers allow
+ NTLMv2 password here) */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ if ((extended_security & CIFSSEC_MAY_LANMAN) &&
+ (ses->server->secType == LANMAN))
+ calc_lanman_hash(ses, bcc_ptr);
+ else
+#endif /* CIFS_WEAK_PW_HASH */
+ SMBNTencrypt(ses->password,
+ ses->server->cryptKey,
+ bcc_ptr);
+
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ if (ses->capabilities & CAP_UNICODE) {
+ /* must align unicode strings */
+ *bcc_ptr = 0; /* null byte password */
+ bcc_ptr++;
+ }
+ }
+
+ if (ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ }
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ length =
+ cifs_strtoUCS((__le16 *) bcc_ptr, tree,
+ 6 /* max utf8 char length in bytes */ *
+ (/* server len*/ + 256 /* share len */), nls_codepage);
+ bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */
+ bcc_ptr += 2; /* skip trailing null */
+ } else { /* ASCII */
+ strcpy(bcc_ptr, tree);
+ bcc_ptr += strlen(tree) + 1;
+ }
+ strcpy(bcc_ptr, "?????");
+ bcc_ptr += strlen("?????");
+ bcc_ptr += 1;
+ count = bcc_ptr - &pSMB->Password[0];
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
+ CIFS_STD_OP);
+
+ /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
+ /* above now done in SendReceive */
+ if ((rc == 0) && (tcon != NULL)) {
+ tcon->tidStatus = CifsGood;
+ tcon->need_reconnect = false;
+ tcon->tid = smb_buffer_response->Tid;
+ bcc_ptr = pByteArea(smb_buffer_response);
+ length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
+ /* skip service field (NB: this field is always ASCII) */
+ if (length == 3) {
+ if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
+ (bcc_ptr[2] == 'C')) {
+ cFYI(1, ("IPC connection"));
+ tcon->ipc = 1;
+ }
+ } else if (length == 2) {
+ if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
+ /* the most common case */
+ cFYI(1, ("disk share connection"));
+ }
+ }
+ bcc_ptr += length + 1;
+ strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ length = UniStrnlen((wchar_t *) bcc_ptr, 512);
+ if ((bcc_ptr + (2 * length)) -
+ pByteArea(smb_buffer_response) <=
+ BCC(smb_buffer_response)) {
+ kfree(tcon->nativeFileSystem);
+ tcon->nativeFileSystem =
+ kzalloc(length + 2, GFP_KERNEL);
+ if (tcon->nativeFileSystem)
+ cifs_strfromUCS_le(
+ tcon->nativeFileSystem,
+ (__le16 *) bcc_ptr,
+ length, nls_codepage);
+ bcc_ptr += 2 * length;
+ bcc_ptr[0] = 0; /* null terminate the string */
+ bcc_ptr[1] = 0;
+ bcc_ptr += 2;
+ }
+ /* else do not bother copying these information fields*/
+ } else {
+ length = strnlen(bcc_ptr, 1024);
+ if ((bcc_ptr + length) -
+ pByteArea(smb_buffer_response) <=
+ BCC(smb_buffer_response)) {
+ kfree(tcon->nativeFileSystem);
+ tcon->nativeFileSystem =
+ kzalloc(length + 1, GFP_KERNEL);
+ if (tcon->nativeFileSystem)
+ strncpy(tcon->nativeFileSystem, bcc_ptr,
+ length);
+ }
+ /* else do not bother copying these information fields*/
+ }
+ if ((smb_buffer_response->WordCount == 3) ||
+ (smb_buffer_response->WordCount == 7))
+ /* field is in same location */
+ tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+ else
+ tcon->Flags = 0;
+ cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags));
+ } else if ((rc == 0) && tcon == NULL) {
+ /* all we need to save for IPC$ connection */
+ ses->ipc_tid = smb_buffer_response->Tid;
+ }
+
+ cifs_buf_release(smb_buffer);
+ return rc;
+}
+
+int
+cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
+{
+ int rc = 0;
+ char *tmp;
+
+ if (cifs_sb->tcon)
+ cifs_put_tcon(cifs_sb->tcon);
+
+ cifs_sb->tcon = NULL;
+ tmp = cifs_sb->prepath;
+ cifs_sb->prepathlen = 0;
+ cifs_sb->prepath = NULL;
+ kfree(tmp);
+
+ return rc;
+}
+
+int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
+ struct nls_table *nls_info)
+{
+ int rc = 0;
+ char ntlm_session_key[CIFS_SESS_KEY_SIZE];
+ bool ntlmv2_flag = false;
+ int first_time = 0;
+ struct TCP_Server_Info *server = pSesInfo->server;
+
+ /* what if server changes its buffer size after dropping the session? */
+ if (server->maxBuf == 0) /* no need to send on reconnect */ {
+ rc = CIFSSMBNegotiate(xid, pSesInfo);
+ if (rc == -EAGAIN) {
+ /* retry only once on 1st time connection */
+ rc = CIFSSMBNegotiate(xid, pSesInfo);
+ if (rc == -EAGAIN)
+ rc = -EHOSTDOWN;
+ }
+ if (rc == 0) {
+ spin_lock(&GlobalMid_Lock);
+ if (server->tcpStatus != CifsExiting)
+ server->tcpStatus = CifsGood;
+ else
+ rc = -EHOSTDOWN;
+ spin_unlock(&GlobalMid_Lock);
+
+ }
+ first_time = 1;
+ }
+
+ if (rc)
+ goto ss_err_exit;
+
+ pSesInfo->flags = 0;
+ pSesInfo->capabilities = server->capabilities;
+ if (linuxExtEnabled == 0)
+ pSesInfo->capabilities &= (~CAP_UNIX);
+ /* pSesInfo->sequence_number = 0;*/
+ cFYI(1, ("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d",
+ server->secMode, server->capabilities, server->timeAdj));
+
+ if (experimEnabled < 2)
+ rc = CIFS_SessSetup(xid, pSesInfo, first_time, nls_info);
+ else if (extended_security
+ && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
+ && (server->secType == NTLMSSP)) {
+ rc = -EOPNOTSUPP;
+ } else if (extended_security
+ && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
+ && (server->secType == RawNTLMSSP)) {
+ cFYI(1, ("NTLMSSP sesssetup"));
+ rc = CIFSNTLMSSPNegotiateSessSetup(xid, pSesInfo, &ntlmv2_flag,
+ nls_info);
+ if (!rc) {
+ if (ntlmv2_flag) {
+ char *v2_response;
+ cFYI(1, ("more secure NTLM ver2 hash"));
+ if (CalcNTLMv2_partial_mac_key(pSesInfo,
+ nls_info)) {
+ rc = -ENOMEM;
+ goto ss_err_exit;
+ } else
+ v2_response = kmalloc(16 + 64 /* blob*/,
+ GFP_KERNEL);
+ if (v2_response) {
+ CalcNTLMv2_response(pSesInfo,
+ v2_response);
+ /* if (first_time)
+ cifs_calculate_ntlmv2_mac_key */
+ kfree(v2_response);
+ /* BB Put dummy sig in SessSetup PDU? */
+ } else {
+ rc = -ENOMEM;
+ goto ss_err_exit;
+ }
+
+ } else {
+ SMBNTencrypt(pSesInfo->password,
+ server->cryptKey,
+ ntlm_session_key);
+
+ if (first_time)
+ cifs_calculate_mac_key(
+ &server->mac_signing_key,
+ ntlm_session_key,
+ pSesInfo->password);
+ }
+ /* for better security the weaker lanman hash not sent
+ in AuthSessSetup so we no longer calculate it */
+
+ rc = CIFSNTLMSSPAuthSessSetup(xid, pSesInfo,
+ ntlm_session_key,
+ ntlmv2_flag,
+ nls_info);
+ }
+ } else { /* old style NTLM 0.12 session setup */
+ SMBNTencrypt(pSesInfo->password, server->cryptKey,
+ ntlm_session_key);
+
+ if (first_time)
+ cifs_calculate_mac_key(&server->mac_signing_key,
+ ntlm_session_key,
+ pSesInfo->password);
+
+ rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info);
+ }
+ if (rc) {
+ cERROR(1, ("Send error in SessSetup = %d", rc));
+ } else {
+ cFYI(1, ("CIFS Session Established successfully"));
+ spin_lock(&GlobalMid_Lock);
+ pSesInfo->status = CifsGood;
+ pSesInfo->need_reconnect = false;
+ spin_unlock(&GlobalMid_Lock);
+ }
+
+ss_err_exit:
+ return rc;
+}
+
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
new file mode 100644
index 0000000..e962e75
--- /dev/null
+++ b/fs/cifs/dir.c
@@ -0,0 +1,636 @@
+/*
+ * fs/cifs/dir.c
+ *
+ * vfs operations that deal with dentries
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/namei.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+
+static void
+renew_parental_timestamps(struct dentry *direntry)
+{
+ /* BB check if there is a way to get the kernel to do this or if we
+ really need this */
+ do {
+ direntry->d_time = jiffies;
+ direntry = direntry->d_parent;
+ } while (!IS_ROOT(direntry));
+}
+
+/* Note: caller must free return buffer */
+char *
+build_path_from_dentry(struct dentry *direntry)
+{
+ struct dentry *temp;
+ int namelen;
+ int pplen;
+ int dfsplen;
+ char *full_path;
+ char dirsep;
+ struct cifs_sb_info *cifs_sb;
+
+ if (direntry == NULL)
+ return NULL; /* not much we can do if dentry is freed and
+ we need to reopen the file after it was closed implicitly
+ when the server crashed */
+
+ cifs_sb = CIFS_SB(direntry->d_sb);
+ dirsep = CIFS_DIR_SEP(cifs_sb);
+ pplen = cifs_sb->prepathlen;
+ if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
+ dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
+ else
+ dfsplen = 0;
+cifs_bp_rename_retry:
+ namelen = pplen + dfsplen;
+ for (temp = direntry; !IS_ROOT(temp);) {
+ namelen += (1 + temp->d_name.len);
+ temp = temp->d_parent;
+ if (temp == NULL) {
+ cERROR(1, ("corrupt dentry"));
+ return NULL;
+ }
+ }
+
+ full_path = kmalloc(namelen+1, GFP_KERNEL);
+ if (full_path == NULL)
+ return full_path;
+ full_path[namelen] = 0; /* trailing null */
+ for (temp = direntry; !IS_ROOT(temp);) {
+ namelen -= 1 + temp->d_name.len;
+ if (namelen < 0) {
+ break;
+ } else {
+ full_path[namelen] = dirsep;
+ strncpy(full_path + namelen + 1, temp->d_name.name,
+ temp->d_name.len);
+ cFYI(0, ("name: %s", full_path + namelen));
+ }
+ temp = temp->d_parent;
+ if (temp == NULL) {
+ cERROR(1, ("corrupt dentry"));
+ kfree(full_path);
+ return NULL;
+ }
+ }
+ if (namelen != pplen + dfsplen) {
+ cERROR(1,
+ ("did not end path lookup where expected namelen is %d",
+ namelen));
+ /* presumably this is only possible if racing with a rename
+ of one of the parent directories (we can not lock the dentries
+ above us to prevent this, but retrying should be harmless) */
+ kfree(full_path);
+ goto cifs_bp_rename_retry;
+ }
+ /* DIR_SEP already set for byte 0 / vs \ but not for
+ subsequent slashes in prepath which currently must
+ be entered the right way - not sure if there is an alternative
+ since the '\' is a valid posix character so we can not switch
+ those safely to '/' if any are found in the middle of the prepath */
+ /* BB test paths to Windows with '/' in the midst of prepath */
+
+ if (dfsplen) {
+ strncpy(full_path, cifs_sb->tcon->treeName, dfsplen);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
+ int i;
+ for (i = 0; i < dfsplen; i++) {
+ if (full_path[i] == '\\')
+ full_path[i] = '/';
+ }
+ }
+ }
+ strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen);
+ return full_path;
+}
+
+/* Inode operations in similar order to how they appear in Linux file fs.h */
+
+int
+cifs_create(struct inode *inode, struct dentry *direntry, int mode,
+ struct nameidata *nd)
+{
+ int rc = -ENOENT;
+ int xid;
+ int create_options = CREATE_NOT_DIR;
+ int oplock = 0;
+ int desiredAccess = GENERIC_READ | GENERIC_WRITE;
+ __u16 fileHandle;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ FILE_ALL_INFO *buf = NULL;
+ struct inode *newinode = NULL;
+ struct cifsFileInfo *pCifsFile = NULL;
+ struct cifsInodeInfo *pCifsInode;
+ int disposition = FILE_OVERWRITE_IF;
+ bool write_only = false;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ if (nd && (nd->flags & LOOKUP_OPEN)) {
+ int oflags = nd->intent.open.flags;
+
+ desiredAccess = 0;
+ if (oflags & FMODE_READ)
+ desiredAccess |= GENERIC_READ;
+ if (oflags & FMODE_WRITE) {
+ desiredAccess |= GENERIC_WRITE;
+ if (!(oflags & FMODE_READ))
+ write_only = true;
+ }
+
+ if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ disposition = FILE_CREATE;
+ else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+ disposition = FILE_OVERWRITE_IF;
+ else if ((oflags & O_CREAT) == O_CREAT)
+ disposition = FILE_OPEN_IF;
+ else
+ cFYI(1, ("Create flag not set in create function"));
+ }
+
+ /* BB add processing to set equivalent of mode - e.g. via CreateX with
+ ACLs */
+ if (oplockEnabled)
+ oplock = REQ_OPLOCK;
+
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (buf == NULL) {
+ kfree(full_path);
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ mode &= ~current->fs->umask;
+
+ /*
+ * if we're not using unix extensions, see if we need to set
+ * ATTR_READONLY on the create call
+ */
+ if (!pTcon->unix_ext && (mode & S_IWUGO) == 0)
+ create_options |= CREATE_OPTION_READONLY;
+
+ if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
+ desiredAccess, create_options,
+ &fileHandle, &oplock, buf, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ else
+ rc = -EIO; /* no NT SMB support fall into legacy open below */
+
+ if (rc == -EIO) {
+ /* old server, retry the open legacy style */
+ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+ desiredAccess, create_options,
+ &fileHandle, &oplock, buf, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ }
+ if (rc) {
+ cFYI(1, ("cifs_create returned 0x%x", rc));
+ } else {
+ /* If Open reported that we actually created a file
+ then we now have to set the mode if possible */
+ if ((pTcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = (__u64) current->fsuid;
+ if (inode->i_mode & S_ISGID)
+ args.gid = (__u64) inode->i_gid;
+ else
+ args.gid = (__u64) current->fsgid;
+ } else {
+ args.uid = NO_CHANGE_64;
+ args.gid = NO_CHANGE_64;
+ }
+ CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else {
+ /* BB implement mode setting via Windows security
+ descriptors e.g. */
+ /* CIFSSMBWinSetPerms(xid,pTcon,path,mode,-1,-1,nls);*/
+
+ /* Could set r/o dos attribute if mode & 0222 == 0 */
+ }
+
+ /* server might mask mode so we have to query for it */
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+ else {
+ rc = cifs_get_inode_info(&newinode, full_path,
+ buf, inode->i_sb, xid,
+ &fileHandle);
+ if (newinode) {
+ if (cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_DYNPERM)
+ newinode->i_mode = mode;
+ if ((oplock & CIFS_CREATE_ACTION) &&
+ (cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_SET_UID)) {
+ newinode->i_uid = current->fsuid;
+ if (inode->i_mode & S_ISGID)
+ newinode->i_gid =
+ inode->i_gid;
+ else
+ newinode->i_gid =
+ current->fsgid;
+ }
+ }
+ }
+
+ if (rc != 0) {
+ cFYI(1,
+ ("Create worked but get_inode_info failed rc = %d",
+ rc));
+ } else {
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ d_instantiate(direntry, newinode);
+ }
+ if ((nd == NULL /* nfsd case - nfs srv does not set nd */) ||
+ (!(nd->flags & LOOKUP_OPEN))) {
+ /* mknod case - do not leave file open */
+ CIFSSMBClose(xid, pTcon, fileHandle);
+ } else if (newinode) {
+ pCifsFile =
+ kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+
+ if (pCifsFile == NULL)
+ goto cifs_create_out;
+ pCifsFile->netfid = fileHandle;
+ pCifsFile->pid = current->tgid;
+ pCifsFile->pInode = newinode;
+ pCifsFile->invalidHandle = false;
+ pCifsFile->closePend = false;
+ init_MUTEX(&pCifsFile->fh_sem);
+ mutex_init(&pCifsFile->lock_mutex);
+ INIT_LIST_HEAD(&pCifsFile->llist);
+ atomic_set(&pCifsFile->wrtPending, 0);
+
+ /* set the following in open now
+ pCifsFile->pfile = file; */
+ write_lock(&GlobalSMBSeslock);
+ list_add(&pCifsFile->tlist, &pTcon->openFileList);
+ pCifsInode = CIFS_I(newinode);
+ if (pCifsInode) {
+ /* if readable file instance put first in list*/
+ if (write_only) {
+ list_add_tail(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ } else {
+ list_add(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ }
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
+ pCifsInode->clientCanCacheAll = true;
+ pCifsInode->clientCanCacheRead = true;
+ cFYI(1, ("Exclusive Oplock inode %p",
+ newinode));
+ } else if ((oplock & 0xF) == OPLOCK_READ)
+ pCifsInode->clientCanCacheRead = true;
+ }
+ write_unlock(&GlobalSMBSeslock);
+ }
+ }
+cifs_create_out:
+ kfree(buf);
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
+ dev_t device_number)
+{
+ int rc = -EPERM;
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ struct inode *newinode = NULL;
+
+ if (!old_valid_dev(device_number))
+ return -EINVAL;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL)
+ rc = -ENOMEM;
+ else if (pTcon->unix_ext) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode & ~current->fs->umask,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = device_number,
+ };
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = (__u64) current->fsuid;
+ args.gid = (__u64) current->fsgid;
+ } else {
+ args.uid = NO_CHANGE_64;
+ args.gid = NO_CHANGE_64;
+ }
+ rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path,
+ &args, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (!rc) {
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ if (rc == 0)
+ d_instantiate(direntry, newinode);
+ }
+ } else {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ int oplock = 0;
+ u16 fileHandle;
+ FILE_ALL_INFO *buf;
+
+ cFYI(1, ("sfu compat create special file"));
+
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (buf == NULL) {
+ kfree(full_path);
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ rc = CIFSSMBOpen(xid, pTcon, full_path,
+ FILE_CREATE, /* fail if exists */
+ GENERIC_WRITE /* BB would
+ WRITE_OWNER | WRITE_DAC be better? */,
+ /* Create a file and set the
+ file attribute to SYSTEM */
+ CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
+ &fileHandle, &oplock, buf,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ /* BB FIXME - add handling for backlevel servers
+ which need legacy open and check for all
+ calls to SMBOpen for fallback to SMBLeagcyOpen */
+ if (!rc) {
+ /* BB Do not bother to decode buf since no
+ local inode yet to put timestamps in,
+ but we can reuse it safely */
+ unsigned int bytes_written;
+ struct win_dev *pdev;
+ pdev = (struct win_dev *)buf;
+ if (S_ISCHR(mode)) {
+ memcpy(pdev->type, "IntxCHR", 8);
+ pdev->major =
+ cpu_to_le64(MAJOR(device_number));
+ pdev->minor =
+ cpu_to_le64(MINOR(device_number));
+ rc = CIFSSMBWrite(xid, pTcon,
+ fileHandle,
+ sizeof(struct win_dev),
+ 0, &bytes_written, (char *)pdev,
+ NULL, 0);
+ } else if (S_ISBLK(mode)) {
+ memcpy(pdev->type, "IntxBLK", 8);
+ pdev->major =
+ cpu_to_le64(MAJOR(device_number));
+ pdev->minor =
+ cpu_to_le64(MINOR(device_number));
+ rc = CIFSSMBWrite(xid, pTcon,
+ fileHandle,
+ sizeof(struct win_dev),
+ 0, &bytes_written, (char *)pdev,
+ NULL, 0);
+ } /* else if(S_ISFIFO */
+ CIFSSMBClose(xid, pTcon, fileHandle);
+ d_drop(direntry);
+ }
+ kfree(buf);
+ /* add code here to set EAs */
+ }
+ }
+
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+
+struct dentry *
+cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
+ struct nameidata *nd)
+{
+ int xid;
+ int rc = 0; /* to get around spurious gcc warning, set to zero here */
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct inode *newInode = NULL;
+ char *full_path = NULL;
+
+ xid = GetXid();
+
+ cFYI(1, (" parent inode = 0x%p name is: %s and dentry = 0x%p",
+ parent_dir_inode, direntry->d_name.name, direntry));
+
+ /* check whether path exists */
+
+ cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ /*
+ * Don't allow the separator character in a path component.
+ * The VFS will not allow "/", but "\" is allowed by posix.
+ */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
+ int i;
+ for (i = 0; i < direntry->d_name.len; i++)
+ if (direntry->d_name.name[i] == '\\') {
+ cFYI(1, ("Invalid file name"));
+ FreeXid(xid);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ /* can not grab the rename sem here since it would
+ deadlock in the cases (beginning of sys_rename itself)
+ in which we already have the sb rename sem */
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (direntry->d_inode != NULL) {
+ cFYI(1, (" non-NULL inode in lookup"));
+ } else {
+ cFYI(1, (" NULL inode in lookup"));
+ }
+ cFYI(1,
+ (" Full path: %s inode = 0x%p", full_path, direntry->d_inode));
+
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newInode, full_path,
+ parent_dir_inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&newInode, full_path, NULL,
+ parent_dir_inode->i_sb, xid, NULL);
+
+ if ((rc == 0) && (newInode != NULL)) {
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ d_add(direntry, newInode);
+
+ /* since paths are not looked up by component - the parent
+ directories are presumed to be good here */
+ renew_parental_timestamps(direntry);
+
+ } else if (rc == -ENOENT) {
+ rc = 0;
+ direntry->d_time = jiffies;
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ d_add(direntry, NULL);
+ /* if it was once a directory (but how can we tell?) we could do
+ shrink_dcache_parent(direntry); */
+ } else if (rc != -EACCES) {
+ cERROR(1, ("Unexpected lookup error %d", rc));
+ /* We special case check for Access Denied - since that
+ is a common return code */
+ }
+
+ kfree(full_path);
+ FreeXid(xid);
+ return ERR_PTR(rc);
+}
+
+static int
+cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
+{
+ int isValid = 1;
+
+ if (direntry->d_inode) {
+ if (cifs_revalidate(direntry))
+ return 0;
+ } else {
+ cFYI(1, ("neg dentry 0x%p name = %s",
+ direntry, direntry->d_name.name));
+ if (time_after(jiffies, direntry->d_time + HZ) ||
+ !lookupCacheEnabled) {
+ d_drop(direntry);
+ isValid = 0;
+ }
+ }
+
+ return isValid;
+}
+
+/* static int cifs_d_delete(struct dentry *direntry)
+{
+ int rc = 0;
+
+ cFYI(1, ("In cifs d_delete, name = %s", direntry->d_name.name));
+
+ return rc;
+} */
+
+struct dentry_operations cifs_dentry_ops = {
+ .d_revalidate = cifs_d_revalidate,
+/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
+};
+
+static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
+{
+ struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+ unsigned long hash;
+ int i;
+
+ hash = init_name_hash();
+ for (i = 0; i < q->len; i++)
+ hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
+ hash);
+ q->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
+ struct qstr *b)
+{
+ struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+
+ if ((a->len == b->len) &&
+ (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
+ /*
+ * To preserve case, don't let an existing negative dentry's
+ * case take precedence. If a is not a negative dentry, this
+ * should have no side effects
+ */
+ memcpy((void *)a->name, b->name, a->len);
+ return 0;
+ }
+ return 1;
+}
+
+struct dentry_operations cifs_ci_dentry_ops = {
+ .d_revalidate = cifs_d_revalidate,
+ .d_hash = cifs_ci_hash,
+ .d_compare = cifs_ci_compare,
+};
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
new file mode 100644
index 0000000..1e0c1bd
--- /dev/null
+++ b/fs/cifs/dns_resolve.c
@@ -0,0 +1,182 @@
+/*
+ * fs/cifs/dns_resolve.c
+ *
+ * Copyright (c) 2007 Igor Mammedov
+ * Author(s): Igor Mammedov (niallain@gmail.com)
+ * Steve French (sfrench@us.ibm.com)
+ *
+ * Contains the CIFS DFS upcall routines used for hostname to
+ * IP address translation.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <keys/user-type.h>
+#include "dns_resolve.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+/* Checks if supplied name is IP address
+ * returns:
+ * 1 - name is IP
+ * 0 - name is not IP
+ */
+static int
+is_ip(const char *name)
+{
+ int rc;
+ struct sockaddr_in sin_server;
+ struct sockaddr_in6 sin_server6;
+
+ rc = cifs_inet_pton(AF_INET, name,
+ &sin_server.sin_addr.s_addr);
+
+ if (rc <= 0) {
+ /* not ipv4 address, try ipv6 */
+ rc = cifs_inet_pton(AF_INET6, name,
+ &sin_server6.sin6_addr.in6_u);
+ if (rc > 0)
+ return 1;
+ } else {
+ return 1;
+ }
+ /* we failed translating address */
+ return 0;
+}
+
+static int
+dns_resolver_instantiate(struct key *key, const void *data,
+ size_t datalen)
+{
+ int rc = 0;
+ char *ip;
+
+ ip = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!ip)
+ return -ENOMEM;
+
+ memcpy(ip, data, datalen);
+ ip[datalen] = '\0';
+
+ /* make sure this looks like an address */
+ if (!is_ip((const char *) ip)) {
+ kfree(ip);
+ return -EINVAL;
+ }
+
+ key->type_data.x[0] = datalen;
+ rcu_assign_pointer(key->payload.data, ip);
+
+ return rc;
+}
+
+static void
+dns_resolver_destroy(struct key *key)
+{
+ kfree(key->payload.data);
+}
+
+struct key_type key_type_dns_resolver = {
+ .name = "dns_resolver",
+ .def_datalen = sizeof(struct in_addr),
+ .describe = user_describe,
+ .instantiate = dns_resolver_instantiate,
+ .destroy = dns_resolver_destroy,
+ .match = user_match,
+};
+
+/* Resolves server name to ip address.
+ * input:
+ * unc - server UNC
+ * output:
+ * *ip_addr - pointer to server ip, caller responcible for freeing it.
+ * return 0 on success
+ */
+int
+dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+{
+ int rc = -EAGAIN;
+ struct key *rkey = ERR_PTR(-EAGAIN);
+ char *name;
+ char *data = NULL;
+ int len;
+
+ if (!ip_addr || !unc)
+ return -EINVAL;
+
+ /* search for server name delimiter */
+ len = strlen(unc);
+ if (len < 3) {
+ cFYI(1, ("%s: unc is too short: %s", __func__, unc));
+ return -EINVAL;
+ }
+ len -= 2;
+ name = memchr(unc+2, '\\', len);
+ if (!name) {
+ cFYI(1, ("%s: probably server name is whole unc: %s",
+ __func__, unc));
+ } else {
+ len = (name - unc) - 2/* leading // */;
+ }
+
+ name = kmalloc(len+1, GFP_KERNEL);
+ if (!name) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ memcpy(name, unc+2, len);
+ name[len] = 0;
+
+ if (is_ip(name)) {
+ cFYI(1, ("%s: it is IP, skipping dns upcall: %s",
+ __func__, name));
+ data = name;
+ goto skip_upcall;
+ }
+
+ rkey = request_key(&key_type_dns_resolver, name, "");
+ if (!IS_ERR(rkey)) {
+ len = rkey->type_data.x[0];
+ data = rkey->payload.data;
+ } else {
+ cERROR(1, ("%s: unable to resolve: %s", __func__, name));
+ goto out;
+ }
+
+skip_upcall:
+ if (data) {
+ *ip_addr = kmalloc(len + 1, GFP_KERNEL);
+ if (*ip_addr) {
+ memcpy(*ip_addr, data, len + 1);
+ if (!IS_ERR(rkey))
+ cFYI(1, ("%s: resolved: %s to %s", __func__,
+ name,
+ *ip_addr
+ ));
+ rc = 0;
+ } else {
+ rc = -ENOMEM;
+ }
+ if (!IS_ERR(rkey))
+ key_put(rkey);
+ }
+
+out:
+ kfree(name);
+ return rc;
+}
+
+
diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
new file mode 100644
index 0000000..966e928
--- /dev/null
+++ b/fs/cifs/dns_resolve.h
@@ -0,0 +1,32 @@
+/*
+ * fs/cifs/dns_resolve.h -- DNS Resolver upcall management for CIFS DFS
+ * Handles host name to IP address resolution
+ *
+ * Copyright (c) International Business Machines Corp., 2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DNS_RESOLVE_H
+#define _DNS_RESOLVE_H
+
+#ifdef __KERNEL__
+#include <linux/key-type.h>
+extern struct key_type key_type_dns_resolver;
+extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
+#endif /* KERNEL */
+
+#endif /* _DNS_RESOLVE_H */
diff --git a/fs/cifs/export.c b/fs/cifs/export.c
new file mode 100644
index 0000000..75949d6
--- /dev/null
+++ b/fs/cifs/export.c
@@ -0,0 +1,67 @@
+/*
+ * fs/cifs/export.c
+ *
+ * Copyright (C) International Business Machines Corp., 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Common Internet FileSystem (CIFS) client
+ *
+ * Operations related to support for exporting files via NFSD
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /*
+ * See Documentation/filesystems/Exporting
+ * and examples in fs/exportfs
+ *
+ * Since cifs is a network file system, an "fsid" must be included for
+ * any nfs exports file entries which refer to cifs paths. In addition
+ * the cifs mount must be mounted with the "serverino" option (ie use stable
+ * server inode numbers instead of locally generated temporary ones).
+ * Although cifs inodes do not use generation numbers (have generation number
+ * of zero) - the inode number alone should be good enough for simple cases
+ * in which users want to export cifs shares with NFS. The decode and encode
+ * could be improved by using a new routine which expects 64 bit inode numbers
+ * instead of the default 32 bit routines in fs/exportfs
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/exportfs.h>
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+static struct dentry *cifs_get_parent(struct dentry *dentry)
+{
+ /* BB need to add code here eventually to enable export via NFSD */
+ cFYI(1, ("get parent for %p", dentry));
+ return ERR_PTR(-EACCES);
+}
+
+const struct export_operations cifs_export_ops = {
+ .get_parent = cifs_get_parent,
+/* Following five export operations are unneeded so far and can default:
+ .get_dentry =
+ .get_name =
+ .find_exported_dentry =
+ .decode_fh =
+ .encode_fs = */
+};
+
+#endif /* EXPERIMENTAL */
+
diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c
new file mode 100644
index 0000000..5a57581
--- /dev/null
+++ b/fs/cifs/fcntl.c
@@ -0,0 +1,118 @@
+/*
+ * fs/cifs/fcntl.c
+ *
+ * vfs operations that deal with the file control API
+ *
+ * Copyright (C) International Business Machines Corp., 2003,2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+
+static __u32 convert_to_cifs_notify_flags(unsigned long fcntl_notify_flags)
+{
+ __u32 cifs_ntfy_flags = 0;
+
+ /* No way on Linux VFS to ask to monitor xattr
+ changes (and no stream support either */
+ if (fcntl_notify_flags & DN_ACCESS)
+ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ if (fcntl_notify_flags & DN_MODIFY) {
+ /* What does this mean on directories? */
+ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE |
+ FILE_NOTIFY_CHANGE_SIZE;
+ }
+ if (fcntl_notify_flags & DN_CREATE) {
+ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_CREATION |
+ FILE_NOTIFY_CHANGE_LAST_WRITE;
+ }
+ if (fcntl_notify_flags & DN_DELETE)
+ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ if (fcntl_notify_flags & DN_RENAME) {
+ /* BB review this - checking various server behaviors */
+ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_FILE_NAME;
+ }
+ if (fcntl_notify_flags & DN_ATTRIB) {
+ cifs_ntfy_flags |= FILE_NOTIFY_CHANGE_SECURITY |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+/* if (fcntl_notify_flags & DN_MULTISHOT) {
+ cifs_ntfy_flags |= ;
+ } */ /* BB fixme - not sure how to handle this with CIFS yet */
+
+ return cifs_ntfy_flags;
+}
+
+int cifs_dir_notify(struct file *file, unsigned long arg)
+{
+ int xid;
+ int rc = -EINVAL;
+ int oplock = 0;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ __u16 netfid;
+
+ if (experimEnabled == 0)
+ return 0;
+
+ xid = GetXid();
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(file->f_path.dentry);
+
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ } else {
+ cFYI(1, ("dir notify on file %s Arg 0x%lx", full_path, arg));
+ rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
+ GENERIC_READ | SYNCHRONIZE, 0 /* create options */,
+ &netfid, &oplock, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ /* BB fixme - add this handle to a notify handle list */
+ if (rc) {
+ cFYI(1, ("Could not open directory for notify"));
+ } else {
+ filter = convert_to_cifs_notify_flags(arg);
+ if (filter != 0) {
+ rc = CIFSSMBNotify(xid, pTcon,
+ 0 /* no subdirs */, netfid,
+ filter, file, arg & DN_MULTISHOT,
+ cifs_sb->local_nls);
+ } else {
+ rc = -EINVAL;
+ }
+ /* BB add code to close file eventually (at unmount
+ it would close automatically but may be a way
+ to do it easily when inode freed or when
+ notify info is cleared/changed */
+ cFYI(1, ("notify rc %d", rc));
+ }
+ }
+
+ FreeXid(xid);
+ return rc;
+}
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
new file mode 100644
index 0000000..586d2be
--- /dev/null
+++ b/fs/cifs/file.c
@@ -0,0 +1,2161 @@
+/*
+ * fs/cifs/file.c
+ *
+ * vfs operations that deal with files
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/backing-dev.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/pagemap.h>
+#include <linux/pagevec.h>
+#include <linux/writeback.h>
+#include <linux/task_io_accounting_ops.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+
+static inline struct cifsFileInfo *cifs_init_private(
+ struct cifsFileInfo *private_data, struct inode *inode,
+ struct file *file, __u16 netfid)
+{
+ memset(private_data, 0, sizeof(struct cifsFileInfo));
+ private_data->netfid = netfid;
+ private_data->pid = current->tgid;
+ init_MUTEX(&private_data->fh_sem);
+ mutex_init(&private_data->lock_mutex);
+ INIT_LIST_HEAD(&private_data->llist);
+ private_data->pfile = file; /* needed for writepage */
+ private_data->pInode = inode;
+ private_data->invalidHandle = false;
+ private_data->closePend = false;
+ /* we have to track num writers to the inode, since writepages
+ does not tell us which handle the write is for so there can
+ be a close (overlapping with write) of the filehandle that
+ cifs_writepages chose to use */
+ atomic_set(&private_data->wrtPending, 0);
+
+ return private_data;
+}
+
+static inline int cifs_convert_flags(unsigned int flags)
+{
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ return GENERIC_READ;
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ return GENERIC_WRITE;
+ else if ((flags & O_ACCMODE) == O_RDWR) {
+ /* GENERIC_ALL is too much permission to request
+ can cause unnecessary access denied on create */
+ /* return GENERIC_ALL; */
+ return (GENERIC_READ | GENERIC_WRITE);
+ }
+
+ return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES |
+ FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA |
+ FILE_READ_DATA);
+
+
+}
+
+static inline int cifs_get_disposition(unsigned int flags)
+{
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ return FILE_CREATE;
+ else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+ return FILE_OVERWRITE_IF;
+ else if ((flags & O_CREAT) == O_CREAT)
+ return FILE_OPEN_IF;
+ else if ((flags & O_TRUNC) == O_TRUNC)
+ return FILE_OVERWRITE;
+ else
+ return FILE_OPEN;
+}
+
+/* all arguments to this function must be checked for validity in caller */
+static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
+ struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile,
+ struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf,
+ char *full_path, int xid)
+{
+ struct timespec temp;
+ int rc;
+
+ /* want handles we can use to read with first
+ in the list so we do not have to walk the
+ list to search for one in write_begin */
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ list_add_tail(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ } else {
+ list_add(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ }
+ write_unlock(&GlobalSMBSeslock);
+ if (pCifsInode->clientCanCacheRead) {
+ /* we have the inode open somewhere else
+ no need to discard cache data */
+ goto client_can_cache;
+ }
+
+ /* BB need same check in cifs_create too? */
+ /* if not oplocked, invalidate inode pages if mtime or file
+ size changed */
+ temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime));
+ if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) &&
+ (file->f_path.dentry->d_inode->i_size ==
+ (loff_t)le64_to_cpu(buf->EndOfFile))) {
+ cFYI(1, ("inode unchanged on server"));
+ } else {
+ if (file->f_path.dentry->d_inode->i_mapping) {
+ /* BB no need to lock inode until after invalidate
+ since namei code should already have it locked? */
+ rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
+ if (rc != 0)
+ CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc;
+ }
+ cFYI(1, ("invalidating remote inode since open detected it "
+ "changed"));
+ invalidate_remote_inode(file->f_path.dentry->d_inode);
+ }
+
+client_can_cache:
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode,
+ full_path, inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&file->f_path.dentry->d_inode,
+ full_path, buf, inode->i_sb, xid, NULL);
+
+ if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
+ pCifsInode->clientCanCacheAll = true;
+ pCifsInode->clientCanCacheRead = true;
+ cFYI(1, ("Exclusive Oplock granted on inode %p",
+ file->f_path.dentry->d_inode));
+ } else if ((*oplock & 0xF) == OPLOCK_READ)
+ pCifsInode->clientCanCacheRead = true;
+
+ return rc;
+}
+
+int cifs_open(struct inode *inode, struct file *file)
+{
+ int rc = -EACCES;
+ int xid, oplock;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct cifsFileInfo *pCifsFile;
+ struct cifsInodeInfo *pCifsInode;
+ struct list_head *tmp;
+ char *full_path = NULL;
+ int desiredAccess;
+ int disposition;
+ __u16 netfid;
+ FILE_ALL_INFO *buf = NULL;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ if (file->f_flags & O_CREAT) {
+ /* search inode for this file and fill in file->private_data */
+ pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
+ read_lock(&GlobalSMBSeslock);
+ list_for_each(tmp, &pCifsInode->openFileList) {
+ pCifsFile = list_entry(tmp, struct cifsFileInfo,
+ flist);
+ if ((pCifsFile->pfile == NULL) &&
+ (pCifsFile->pid == current->tgid)) {
+ /* mode set in cifs_create */
+
+ /* needed for writepage */
+ pCifsFile->pfile = file;
+
+ file->private_data = pCifsFile;
+ break;
+ }
+ }
+ read_unlock(&GlobalSMBSeslock);
+ if (file->private_data != NULL) {
+ rc = 0;
+ FreeXid(xid);
+ return rc;
+ } else {
+ if (file->f_flags & O_EXCL)
+ cERROR(1, ("could not find file instance for "
+ "new file %p", file));
+ }
+ }
+
+ full_path = build_path_from_dentry(file->f_path.dentry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ cFYI(1, ("inode = 0x%p file flags are 0x%x for %s",
+ inode, file->f_flags, full_path));
+ desiredAccess = cifs_convert_flags(file->f_flags);
+
+/*********************************************************************
+ * open flag mapping table:
+ *
+ * POSIX Flag CIFS Disposition
+ * ---------- ----------------
+ * O_CREAT FILE_OPEN_IF
+ * O_CREAT | O_EXCL FILE_CREATE
+ * O_CREAT | O_TRUNC FILE_OVERWRITE_IF
+ * O_TRUNC FILE_OVERWRITE
+ * none of the above FILE_OPEN
+ *
+ * Note that there is not a direct match between disposition
+ * FILE_SUPERSEDE (ie create whether or not file exists although
+ * O_CREAT | O_TRUNC is similar but truncates the existing
+ * file rather than creating a new file as FILE_SUPERSEDE does
+ * (which uses the attributes / metadata passed in on open call)
+ *?
+ *? O_SYNC is a reasonable match to CIFS writethrough flag
+ *? and the read write flags match reasonably. O_LARGEFILE
+ *? is irrelevant because largefile support is always used
+ *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY,
+ * O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation
+ *********************************************************************/
+
+ disposition = cifs_get_disposition(file->f_flags);
+
+ if (oplockEnabled)
+ oplock = REQ_OPLOCK;
+ else
+ oplock = 0;
+
+ /* BB pass O_SYNC flag through on file attributes .. BB */
+
+ /* Also refresh inode by passing in file_info buf returned by SMBOpen
+ and calling get_inode_info with returned buf (at least helps
+ non-Unix server case) */
+
+ /* BB we can not do this if this is the second open of a file
+ and the first handle has writebehind data, we might be
+ able to simply do a filemap_fdatawrite/filemap_fdatawait first */
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
+ desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+ & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ else
+ rc = -EIO; /* no NT SMB support fall into legacy open below */
+
+ if (rc == -EIO) {
+ /* Old server, try legacy style OpenX */
+ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+ desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+ & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ }
+ if (rc) {
+ cFYI(1, ("cifs_open returned 0x%x", rc));
+ goto out;
+ }
+ file->private_data =
+ kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+ if (file->private_data == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
+ write_lock(&GlobalSMBSeslock);
+ list_add(&pCifsFile->tlist, &pTcon->openFileList);
+
+ pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
+ if (pCifsInode) {
+ rc = cifs_open_inode_helper(inode, file, pCifsInode,
+ pCifsFile, pTcon,
+ &oplock, buf, full_path, xid);
+ } else {
+ write_unlock(&GlobalSMBSeslock);
+ }
+
+ if (oplock & CIFS_CREATE_ACTION) {
+ /* time to set mode which we can not set earlier due to
+ problems creating new read-only files */
+ if (pTcon->unix_ext) {
+ struct cifs_unix_set_info_args args = {
+ .mode = inode->i_mode,
+ .uid = NO_CHANGE_64,
+ .gid = NO_CHANGE_64,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+ CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ }
+ }
+
+out:
+ kfree(buf);
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+/* Try to reacquire byte range locks that were released when session */
+/* to server was lost */
+static int cifs_relock_file(struct cifsFileInfo *cifsFile)
+{
+ int rc = 0;
+
+/* BB list all locks open on this file and relock */
+
+ return rc;
+}
+
+static int cifs_reopen_file(struct file *file, bool can_flush)
+{
+ int rc = -EACCES;
+ int xid, oplock;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct cifsFileInfo *pCifsFile;
+ struct cifsInodeInfo *pCifsInode;
+ struct inode *inode;
+ char *full_path = NULL;
+ int desiredAccess;
+ int disposition = FILE_OPEN;
+ __u16 netfid;
+
+ if (file->private_data)
+ pCifsFile = (struct cifsFileInfo *)file->private_data;
+ else
+ return -EBADF;
+
+ xid = GetXid();
+ down(&pCifsFile->fh_sem);
+ if (!pCifsFile->invalidHandle) {
+ up(&pCifsFile->fh_sem);
+ FreeXid(xid);
+ return 0;
+ }
+
+ if (file->f_path.dentry == NULL) {
+ cERROR(1, ("no valid name if dentry freed"));
+ dump_stack();
+ rc = -EBADF;
+ goto reopen_error_exit;
+ }
+
+ inode = file->f_path.dentry->d_inode;
+ if (inode == NULL) {
+ cERROR(1, ("inode not valid"));
+ dump_stack();
+ rc = -EBADF;
+ goto reopen_error_exit;
+ }
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+/* can not grab rename sem here because various ops, including
+ those that already have the rename sem can end up causing writepage
+ to get called and if the server was down that means we end up here,
+ and we can never tell if the caller already has the rename_sem */
+ full_path = build_path_from_dentry(file->f_path.dentry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+reopen_error_exit:
+ up(&pCifsFile->fh_sem);
+ FreeXid(xid);
+ return rc;
+ }
+
+ cFYI(1, ("inode = 0x%p file flags 0x%x for %s",
+ inode, file->f_flags, full_path));
+ desiredAccess = cifs_convert_flags(file->f_flags);
+
+ if (oplockEnabled)
+ oplock = REQ_OPLOCK;
+ else
+ oplock = 0;
+
+ /* Can not refresh inode by passing in file_info buf to be returned
+ by SMBOpen and then calling get_inode_info with returned buf
+ since file might have write behind data that needs to be flushed
+ and server version of file size can be stale. If we knew for sure
+ that inode was not dirty locally we could do this */
+
+ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
+ CREATE_NOT_DIR, &netfid, &oplock, NULL,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc) {
+ up(&pCifsFile->fh_sem);
+ cFYI(1, ("cifs_open returned 0x%x", rc));
+ cFYI(1, ("oplock: %d", oplock));
+ } else {
+ pCifsFile->netfid = netfid;
+ pCifsFile->invalidHandle = false;
+ up(&pCifsFile->fh_sem);
+ pCifsInode = CIFS_I(inode);
+ if (pCifsInode) {
+ if (can_flush) {
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (rc != 0)
+ CIFS_I(inode)->write_behind_rc = rc;
+ /* temporarily disable caching while we
+ go to server to get inode info */
+ pCifsInode->clientCanCacheAll = false;
+ pCifsInode->clientCanCacheRead = false;
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&inode,
+ full_path, inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&inode,
+ full_path, NULL, inode->i_sb,
+ xid, NULL);
+ } /* else we are writing out data to server already
+ and could deadlock if we tried to flush data, and
+ since we do not know if we have data that would
+ invalidate the current end of file on the server
+ we can not go to the server to get the new inod
+ info */
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
+ pCifsInode->clientCanCacheAll = true;
+ pCifsInode->clientCanCacheRead = true;
+ cFYI(1, ("Exclusive Oplock granted on inode %p",
+ file->f_path.dentry->d_inode));
+ } else if ((oplock & 0xF) == OPLOCK_READ) {
+ pCifsInode->clientCanCacheRead = true;
+ pCifsInode->clientCanCacheAll = false;
+ } else {
+ pCifsInode->clientCanCacheRead = false;
+ pCifsInode->clientCanCacheAll = false;
+ }
+ cifs_relock_file(pCifsFile);
+ }
+ }
+
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_close(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ int xid, timeout;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct cifsFileInfo *pSMBFile =
+ (struct cifsFileInfo *)file->private_data;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+ if (pSMBFile) {
+ struct cifsLockInfo *li, *tmp;
+ write_lock(&GlobalSMBSeslock);
+ pSMBFile->closePend = true;
+ if (pTcon) {
+ /* no sense reconnecting to close a file that is
+ already closed */
+ if (!pTcon->need_reconnect) {
+ write_unlock(&GlobalSMBSeslock);
+ timeout = 2;
+ while ((atomic_read(&pSMBFile->wrtPending) != 0)
+ && (timeout <= 2048)) {
+ /* Give write a better chance to get to
+ server ahead of the close. We do not
+ want to add a wait_q here as it would
+ increase the memory utilization as
+ the struct would be in each open file,
+ but this should give enough time to
+ clear the socket */
+ cFYI(DBG2,
+ ("close delay, write pending"));
+ msleep(timeout);
+ timeout *= 4;
+ }
+ if (atomic_read(&pSMBFile->wrtPending))
+ cERROR(1, ("close with pending write"));
+ if (!pTcon->need_reconnect &&
+ !pSMBFile->invalidHandle)
+ rc = CIFSSMBClose(xid, pTcon,
+ pSMBFile->netfid);
+ } else
+ write_unlock(&GlobalSMBSeslock);
+ } else
+ write_unlock(&GlobalSMBSeslock);
+
+ /* Delete any outstanding lock records.
+ We'll lose them when the file is closed anyway. */
+ mutex_lock(&pSMBFile->lock_mutex);
+ list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) {
+ list_del(&li->llist);
+ kfree(li);
+ }
+ mutex_unlock(&pSMBFile->lock_mutex);
+
+ write_lock(&GlobalSMBSeslock);
+ list_del(&pSMBFile->flist);
+ list_del(&pSMBFile->tlist);
+ write_unlock(&GlobalSMBSeslock);
+ timeout = 10;
+ /* We waited above to give the SMBWrite a chance to issue
+ on the wire (so we do not get SMBWrite returning EBADF
+ if writepages is racing with close. Note that writepages
+ does not specify a file handle, so it is possible for a file
+ to be opened twice, and the application close the "wrong"
+ file handle - in these cases we delay long enough to allow
+ the SMBWrite to get on the wire before the SMB Close.
+ We allow total wait here over 45 seconds, more than
+ oplock break time, and more than enough to allow any write
+ to complete on the server, or to time out on the client */
+ while ((atomic_read(&pSMBFile->wrtPending) != 0)
+ && (timeout <= 50000)) {
+ cERROR(1, ("writes pending, delay free of handle"));
+ msleep(timeout);
+ timeout *= 8;
+ }
+ kfree(file->private_data);
+ file->private_data = NULL;
+ } else
+ rc = -EBADF;
+
+ read_lock(&GlobalSMBSeslock);
+ if (list_empty(&(CIFS_I(inode)->openFileList))) {
+ cFYI(1, ("closing last open instance for inode %p", inode));
+ /* if the file is not open we do not know if we can cache info
+ on this inode, much less write behind and read ahead */
+ CIFS_I(inode)->clientCanCacheRead = false;
+ CIFS_I(inode)->clientCanCacheAll = false;
+ }
+ read_unlock(&GlobalSMBSeslock);
+ if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
+ rc = CIFS_I(inode)->write_behind_rc;
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_closedir(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ int xid;
+ struct cifsFileInfo *pCFileStruct =
+ (struct cifsFileInfo *)file->private_data;
+ char *ptmp;
+
+ cFYI(1, ("Closedir inode = 0x%p", inode));
+
+ xid = GetXid();
+
+ if (pCFileStruct) {
+ struct cifsTconInfo *pTcon;
+ struct cifs_sb_info *cifs_sb =
+ CIFS_SB(file->f_path.dentry->d_sb);
+
+ pTcon = cifs_sb->tcon;
+
+ cFYI(1, ("Freeing private data in close dir"));
+ write_lock(&GlobalSMBSeslock);
+ if (!pCFileStruct->srch_inf.endOfSearch &&
+ !pCFileStruct->invalidHandle) {
+ pCFileStruct->invalidHandle = true;
+ write_unlock(&GlobalSMBSeslock);
+ rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid);
+ cFYI(1, ("Closing uncompleted readdir with rc %d",
+ rc));
+ /* not much we can do if it fails anyway, ignore rc */
+ rc = 0;
+ } else
+ write_unlock(&GlobalSMBSeslock);
+ ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
+ if (ptmp) {
+ cFYI(1, ("closedir free smb buf in srch struct"));
+ pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
+ if (pCFileStruct->srch_inf.smallBuf)
+ cifs_small_buf_release(ptmp);
+ else
+ cifs_buf_release(ptmp);
+ }
+ kfree(file->private_data);
+ file->private_data = NULL;
+ }
+ /* BB can we lock the filestruct while this is going on? */
+ FreeXid(xid);
+ return rc;
+}
+
+static int store_file_lock(struct cifsFileInfo *fid, __u64 len,
+ __u64 offset, __u8 lockType)
+{
+ struct cifsLockInfo *li =
+ kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
+ if (li == NULL)
+ return -ENOMEM;
+ li->offset = offset;
+ li->length = len;
+ li->type = lockType;
+ mutex_lock(&fid->lock_mutex);
+ list_add(&li->llist, &fid->llist);
+ mutex_unlock(&fid->lock_mutex);
+ return 0;
+}
+
+int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
+{
+ int rc, xid;
+ __u32 numLock = 0;
+ __u32 numUnlock = 0;
+ __u64 length;
+ bool wait_flag = false;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ __u16 netfid;
+ __u8 lockType = LOCKING_ANDX_LARGE_FILES;
+ bool posix_locking;
+
+ length = 1 + pfLock->fl_end - pfLock->fl_start;
+ rc = -EACCES;
+ xid = GetXid();
+
+ cFYI(1, ("Lock parm: 0x%x flockflags: "
+ "0x%x flocktype: 0x%x start: %lld end: %lld",
+ cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start,
+ pfLock->fl_end));
+
+ if (pfLock->fl_flags & FL_POSIX)
+ cFYI(1, ("Posix"));
+ if (pfLock->fl_flags & FL_FLOCK)
+ cFYI(1, ("Flock"));
+ if (pfLock->fl_flags & FL_SLEEP) {
+ cFYI(1, ("Blocking lock"));
+ wait_flag = true;
+ }
+ if (pfLock->fl_flags & FL_ACCESS)
+ cFYI(1, ("Process suspended by mandatory locking - "
+ "not implemented yet"));
+ if (pfLock->fl_flags & FL_LEASE)
+ cFYI(1, ("Lease on file - not implemented yet"));
+ if (pfLock->fl_flags &
+ (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
+ cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags));
+
+ if (pfLock->fl_type == F_WRLCK) {
+ cFYI(1, ("F_WRLCK "));
+ numLock = 1;
+ } else if (pfLock->fl_type == F_UNLCK) {
+ cFYI(1, ("F_UNLCK"));
+ numUnlock = 1;
+ /* Check if unlock includes more than
+ one lock range */
+ } else if (pfLock->fl_type == F_RDLCK) {
+ cFYI(1, ("F_RDLCK"));
+ lockType |= LOCKING_ANDX_SHARED_LOCK;
+ numLock = 1;
+ } else if (pfLock->fl_type == F_EXLCK) {
+ cFYI(1, ("F_EXLCK"));
+ numLock = 1;
+ } else if (pfLock->fl_type == F_SHLCK) {
+ cFYI(1, ("F_SHLCK"));
+ lockType |= LOCKING_ANDX_SHARED_LOCK;
+ numLock = 1;
+ } else
+ cFYI(1, ("Unknown type of lock"));
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+
+ if (file->private_data == NULL) {
+ FreeXid(xid);
+ return -EBADF;
+ }
+ netfid = ((struct cifsFileInfo *)file->private_data)->netfid;
+
+ posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability));
+
+ /* 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, pTcon, netfid, 1 /* get */,
+ length, pfLock,
+ posix_lock_type, wait_flag);
+ FreeXid(xid);
+ return rc;
+ }
+
+ /* BB we could chain these into one lock request BB */
+ rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
+ 0, 1, lockType, 0 /* wait flag */ );
+ if (rc == 0) {
+ rc = CIFSSMBLock(xid, pTcon, netfid, length,
+ pfLock->fl_start, 1 /* numUnlock */ ,
+ 0 /* numLock */ , lockType,
+ 0 /* wait flag */ );
+ pfLock->fl_type = F_UNLCK;
+ if (rc != 0)
+ cERROR(1, ("Error unlocking previously locked "
+ "range %d during test of lock", rc));
+ rc = 0;
+
+ } else {
+ /* if rc == ERR_SHARING_VIOLATION ? */
+ rc = 0; /* do not change lock type to unlock
+ since range in use */
+ }
+
+ FreeXid(xid);
+ return rc;
+ }
+
+ if (!numLock && !numUnlock) {
+ /* if no lock or unlock then nothing
+ to do since we do not know what it is */
+ FreeXid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ if (posix_locking) {
+ int posix_lock_type;
+ if (lockType & LOCKING_ANDX_SHARED_LOCK)
+ posix_lock_type = CIFS_RDLCK;
+ else
+ posix_lock_type = CIFS_WRLCK;
+
+ if (numUnlock == 1)
+ posix_lock_type = CIFS_UNLCK;
+
+ rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */,
+ length, pfLock,
+ posix_lock_type, wait_flag);
+ } else {
+ struct cifsFileInfo *fid =
+ (struct cifsFileInfo *)file->private_data;
+
+ if (numLock) {
+ rc = CIFSSMBLock(xid, pTcon, netfid, length,
+ pfLock->fl_start,
+ 0, numLock, lockType, wait_flag);
+
+ if (rc == 0) {
+ /* For Windows locks we must store them. */
+ rc = store_file_lock(fid, length,
+ pfLock->fl_start, lockType);
+ }
+ } else if (numUnlock) {
+ /* For each stored lock that this unlock overlaps
+ completely, unlock it. */
+ int stored_rc = 0;
+ struct cifsLockInfo *li, *tmp;
+
+ rc = 0;
+ mutex_lock(&fid->lock_mutex);
+ list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
+ if (pfLock->fl_start <= li->offset &&
+ (pfLock->fl_start + length) >=
+ (li->offset + li->length)) {
+ stored_rc = CIFSSMBLock(xid, pTcon,
+ netfid,
+ li->length, li->offset,
+ 1, 0, li->type, false);
+ if (stored_rc)
+ rc = stored_rc;
+
+ list_del(&li->llist);
+ kfree(li);
+ }
+ }
+ mutex_unlock(&fid->lock_mutex);
+ }
+ }
+
+ if (pfLock->fl_flags & FL_POSIX)
+ posix_lock_file_wait(file, pfLock);
+ FreeXid(xid);
+ return rc;
+}
+
+ssize_t cifs_user_write(struct file *file, const char __user *write_data,
+ size_t write_size, loff_t *poffset)
+{
+ int rc = 0;
+ unsigned int bytes_written = 0;
+ unsigned int total_written;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ int xid, long_op;
+ struct cifsFileInfo *open_file;
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+
+ pTcon = cifs_sb->tcon;
+
+ /* cFYI(1,
+ (" write %d bytes to offset %lld of %s", write_size,
+ *poffset, file->f_path.dentry->d_name.name)); */
+
+ if (file->private_data == NULL)
+ return -EBADF;
+ open_file = (struct cifsFileInfo *) file->private_data;
+
+ rc = generic_write_checks(file, poffset, &write_size, 0);
+ if (rc)
+ return rc;
+
+ xid = GetXid();
+
+ if (*poffset > file->f_path.dentry->d_inode->i_size)
+ long_op = CIFS_VLONG_OP; /* writes past EOF take long time */
+ else
+ long_op = CIFS_LONG_OP;
+
+ for (total_written = 0; write_size > total_written;
+ total_written += bytes_written) {
+ rc = -EAGAIN;
+ while (rc == -EAGAIN) {
+ if (file->private_data == NULL) {
+ /* file has been closed on us */
+ FreeXid(xid);
+ /* if we have gotten here we have written some data
+ and blocked, and the file has been freed on us while
+ we blocked so return what we managed to write */
+ return total_written;
+ }
+ if (open_file->closePend) {
+ FreeXid(xid);
+ if (total_written)
+ return total_written;
+ else
+ return -EBADF;
+ }
+ if (open_file->invalidHandle) {
+ /* we could deadlock if we called
+ filemap_fdatawait from here so tell
+ reopen_file not to flush data to server
+ now */
+ rc = cifs_reopen_file(file, false);
+ if (rc != 0)
+ break;
+ }
+
+ rc = CIFSSMBWrite(xid, pTcon,
+ open_file->netfid,
+ min_t(const int, cifs_sb->wsize,
+ write_size - total_written),
+ *poffset, &bytes_written,
+ NULL, write_data + total_written, long_op);
+ }
+ if (rc || (bytes_written == 0)) {
+ if (total_written)
+ break;
+ else {
+ FreeXid(xid);
+ return rc;
+ }
+ } else
+ *poffset += bytes_written;
+ long_op = CIFS_STD_OP; /* subsequent writes fast -
+ 15 seconds is plenty */
+ }
+
+ cifs_stats_bytes_written(pTcon, total_written);
+
+ /* since the write may have blocked check these pointers again */
+ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {
+ struct inode *inode = file->f_path.dentry->d_inode;
+/* Do not update local mtime - server will set its actual value on write
+ * inode->i_ctime = inode->i_mtime =
+ * current_fs_time(inode->i_sb);*/
+ if (total_written > 0) {
+ spin_lock(&inode->i_lock);
+ if (*poffset > file->f_path.dentry->d_inode->i_size)
+ i_size_write(file->f_path.dentry->d_inode,
+ *poffset);
+ spin_unlock(&inode->i_lock);
+ }
+ mark_inode_dirty_sync(file->f_path.dentry->d_inode);
+ }
+ FreeXid(xid);
+ return total_written;
+}
+
+static ssize_t cifs_write(struct file *file, const char *write_data,
+ size_t write_size, loff_t *poffset)
+{
+ int rc = 0;
+ unsigned int bytes_written = 0;
+ unsigned int total_written;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ int xid, long_op;
+ struct cifsFileInfo *open_file;
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+
+ pTcon = cifs_sb->tcon;
+
+ cFYI(1, ("write %zd bytes to offset %lld of %s", write_size,
+ *poffset, file->f_path.dentry->d_name.name));
+
+ if (file->private_data == NULL)
+ return -EBADF;
+ open_file = (struct cifsFileInfo *)file->private_data;
+
+ xid = GetXid();
+
+ if (*poffset > file->f_path.dentry->d_inode->i_size)
+ long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */
+ else
+ long_op = CIFS_LONG_OP;
+
+ for (total_written = 0; write_size > total_written;
+ total_written += bytes_written) {
+ rc = -EAGAIN;
+ while (rc == -EAGAIN) {
+ if (file->private_data == NULL) {
+ /* file has been closed on us */
+ FreeXid(xid);
+ /* if we have gotten here we have written some data
+ and blocked, and the file has been freed on us
+ while we blocked so return what we managed to
+ write */
+ return total_written;
+ }
+ if (open_file->closePend) {
+ FreeXid(xid);
+ if (total_written)
+ return total_written;
+ else
+ return -EBADF;
+ }
+ if (open_file->invalidHandle) {
+ /* we could deadlock if we called
+ filemap_fdatawait from here so tell
+ reopen_file not to flush data to
+ server now */
+ rc = cifs_reopen_file(file, false);
+ if (rc != 0)
+ break;
+ }
+ if (experimEnabled || (pTcon->ses->server &&
+ ((pTcon->ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ == 0))) {
+ struct kvec iov[2];
+ unsigned int len;
+
+ len = min((size_t)cifs_sb->wsize,
+ write_size - total_written);
+ /* iov[0] is reserved for smb header */
+ iov[1].iov_base = (char *)write_data +
+ total_written;
+ iov[1].iov_len = len;
+ rc = CIFSSMBWrite2(xid, pTcon,
+ open_file->netfid, len,
+ *poffset, &bytes_written,
+ iov, 1, long_op);
+ } else
+ rc = CIFSSMBWrite(xid, pTcon,
+ open_file->netfid,
+ min_t(const int, cifs_sb->wsize,
+ write_size - total_written),
+ *poffset, &bytes_written,
+ write_data + total_written,
+ NULL, long_op);
+ }
+ if (rc || (bytes_written == 0)) {
+ if (total_written)
+ break;
+ else {
+ FreeXid(xid);
+ return rc;
+ }
+ } else
+ *poffset += bytes_written;
+ long_op = CIFS_STD_OP; /* subsequent writes fast -
+ 15 seconds is plenty */
+ }
+
+ cifs_stats_bytes_written(pTcon, total_written);
+
+ /* since the write may have blocked check these pointers again */
+ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {
+/*BB We could make this contingent on superblock ATIME flag too */
+/* file->f_path.dentry->d_inode->i_ctime =
+ file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/
+ if (total_written > 0) {
+ spin_lock(&file->f_path.dentry->d_inode->i_lock);
+ if (*poffset > file->f_path.dentry->d_inode->i_size)
+ i_size_write(file->f_path.dentry->d_inode,
+ *poffset);
+ spin_unlock(&file->f_path.dentry->d_inode->i_lock);
+ }
+ mark_inode_dirty_sync(file->f_path.dentry->d_inode);
+ }
+ FreeXid(xid);
+ return total_written;
+}
+
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
+{
+ struct cifsFileInfo *open_file = NULL;
+
+ read_lock(&GlobalSMBSeslock);
+ /* we could simply get the first_list_entry since write-only entries
+ are always at the end of the list but since the first entry might
+ have a close pending, we go through the whole list */
+ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+ if (open_file->closePend)
+ continue;
+ if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) ||
+ (open_file->pfile->f_flags & O_RDONLY))) {
+ if (!open_file->invalidHandle) {
+ /* found a good file */
+ /* lock it so it will not be closed on us */
+ atomic_inc(&open_file->wrtPending);
+ read_unlock(&GlobalSMBSeslock);
+ return open_file;
+ } /* else might as well continue, and look for
+ another, or simply have the caller reopen it
+ again rather than trying to fix this handle */
+ } else /* write only file */
+ break; /* write only files are last so must be done */
+ }
+ read_unlock(&GlobalSMBSeslock);
+ return NULL;
+}
+#endif
+
+struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
+{
+ struct cifsFileInfo *open_file;
+ bool any_available = false;
+ int rc;
+
+ /* Having a null inode here (because mapping->host was set to zero by
+ the VFS or MM) should not happen but we had reports of on oops (due to
+ it being zero) during stress testcases so we need to check for it */
+
+ if (cifs_inode == NULL) {
+ cERROR(1, ("Null inode passed to cifs_writeable_file"));
+ dump_stack();
+ return NULL;
+ }
+
+ read_lock(&GlobalSMBSeslock);
+refind_writable:
+ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+ if (open_file->closePend ||
+ (!any_available && open_file->pid != current->tgid))
+ continue;
+
+ if (open_file->pfile &&
+ ((open_file->pfile->f_flags & O_RDWR) ||
+ (open_file->pfile->f_flags & O_WRONLY))) {
+ atomic_inc(&open_file->wrtPending);
+
+ if (!open_file->invalidHandle) {
+ /* found a good writable file */
+ read_unlock(&GlobalSMBSeslock);
+ return open_file;
+ }
+
+ read_unlock(&GlobalSMBSeslock);
+ /* Had to unlock since following call can block */
+ rc = cifs_reopen_file(open_file->pfile, false);
+ if (!rc) {
+ if (!open_file->closePend)
+ return open_file;
+ else { /* start over in case this was deleted */
+ /* since the list could be modified */
+ read_lock(&GlobalSMBSeslock);
+ atomic_dec(&open_file->wrtPending);
+ goto refind_writable;
+ }
+ }
+
+ /* if it fails, try another handle if possible -
+ (we can not do this if closePending since
+ loop could be modified - in which case we
+ have to start at the beginning of the list
+ again. Note that it would be bad
+ to hold up writepages here (rather than
+ in caller) with continuous retries */
+ cFYI(1, ("wp failed on reopen file"));
+ read_lock(&GlobalSMBSeslock);
+ /* can not use this handle, no write
+ pending on this one after all */
+ atomic_dec(&open_file->wrtPending);
+
+ if (open_file->closePend) /* list could have changed */
+ goto refind_writable;
+ /* else we simply continue to the next entry. Thus
+ we do not loop on reopen errors. If we
+ can not reopen the file, for example if we
+ reconnected to a server with another client
+ racing to delete or lock the file we would not
+ make progress if we restarted before the beginning
+ of the loop here. */
+ }
+ }
+ /* couldn't find useable FH with same pid, try any available */
+ if (!any_available) {
+ any_available = true;
+ goto refind_writable;
+ }
+ read_unlock(&GlobalSMBSeslock);
+ return NULL;
+}
+
+static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
+{
+ struct address_space *mapping = page->mapping;
+ loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+ char *write_data;
+ int rc = -EFAULT;
+ int bytes_written = 0;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct inode *inode;
+ struct cifsFileInfo *open_file;
+
+ if (!mapping || !mapping->host)
+ return -EFAULT;
+
+ inode = page->mapping->host;
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ offset += (loff_t)from;
+ write_data = kmap(page);
+ write_data += from;
+
+ if ((to > PAGE_CACHE_SIZE) || (from > to)) {
+ kunmap(page);
+ return -EIO;
+ }
+
+ /* racing with truncate? */
+ if (offset > mapping->host->i_size) {
+ kunmap(page);
+ return 0; /* don't care */
+ }
+
+ /* check to make sure that we are not extending the file */
+ if (mapping->host->i_size - offset < (loff_t)to)
+ to = (unsigned)(mapping->host->i_size - offset);
+
+ open_file = find_writable_file(CIFS_I(mapping->host));
+ if (open_file) {
+ bytes_written = cifs_write(open_file->pfile, write_data,
+ to-from, &offset);
+ atomic_dec(&open_file->wrtPending);
+ /* Does mm or vfs already set times? */
+ inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
+ if ((bytes_written > 0) && (offset))
+ rc = 0;
+ else if (bytes_written < 0)
+ rc = bytes_written;
+ } else {
+ cFYI(1, ("No writeable filehandles for inode"));
+ rc = -EIO;
+ }
+
+ kunmap(page);
+ return rc;
+}
+
+static int cifs_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ struct backing_dev_info *bdi = mapping->backing_dev_info;
+ unsigned int bytes_to_write;
+ unsigned int bytes_written;
+ struct cifs_sb_info *cifs_sb;
+ int done = 0;
+ pgoff_t end;
+ pgoff_t index;
+ int range_whole = 0;
+ struct kvec *iov;
+ int len;
+ int n_iov = 0;
+ pgoff_t next;
+ int nr_pages;
+ __u64 offset = 0;
+ struct cifsFileInfo *open_file;
+ struct page *page;
+ struct pagevec pvec;
+ int rc = 0;
+ int scanned = 0;
+ int xid;
+
+ cifs_sb = CIFS_SB(mapping->host->i_sb);
+
+ /*
+ * If wsize is smaller that the page cache size, default to writing
+ * one page at a time via cifs_writepage
+ */
+ if (cifs_sb->wsize < PAGE_CACHE_SIZE)
+ return generic_writepages(mapping, wbc);
+
+ if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
+ if (cifs_sb->tcon->ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ if (!experimEnabled)
+ return generic_writepages(mapping, wbc);
+
+ iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL);
+ if (iov == NULL)
+ return generic_writepages(mapping, wbc);
+
+
+ /*
+ * BB: Is this meaningful for a non-block-device file system?
+ * If it is, we should test it again after we do I/O
+ */
+ if (wbc->nonblocking && bdi_write_congested(bdi)) {
+ wbc->encountered_congestion = 1;
+ kfree(iov);
+ return 0;
+ }
+
+ xid = GetXid();
+
+ pagevec_init(&pvec, 0);
+ if (wbc->range_cyclic) {
+ index = mapping->writeback_index; /* Start from prev offset */
+ end = -1;
+ } else {
+ index = wbc->range_start >> PAGE_CACHE_SHIFT;
+ end = wbc->range_end >> PAGE_CACHE_SHIFT;
+ if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
+ range_whole = 1;
+ scanned = 1;
+ }
+retry:
+ while (!done && (index <= end) &&
+ (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
+ PAGECACHE_TAG_DIRTY,
+ min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1))) {
+ int first;
+ unsigned int i;
+
+ first = -1;
+ next = 0;
+ n_iov = 0;
+ bytes_to_write = 0;
+
+ for (i = 0; i < nr_pages; i++) {
+ page = pvec.pages[i];
+ /*
+ * At this point we hold neither mapping->tree_lock nor
+ * lock on the page itself: the page may be truncated or
+ * invalidated (changing page->mapping to NULL), or even
+ * swizzled back from swapper_space to tmpfs file
+ * mapping
+ */
+
+ if (first < 0)
+ lock_page(page);
+ else if (!trylock_page(page))
+ break;
+
+ if (unlikely(page->mapping != mapping)) {
+ unlock_page(page);
+ break;
+ }
+
+ if (!wbc->range_cyclic && page->index > end) {
+ done = 1;
+ unlock_page(page);
+ break;
+ }
+
+ if (next && (page->index != next)) {
+ /* Not next consecutive page */
+ unlock_page(page);
+ break;
+ }
+
+ if (wbc->sync_mode != WB_SYNC_NONE)
+ wait_on_page_writeback(page);
+
+ if (PageWriteback(page) ||
+ !clear_page_dirty_for_io(page)) {
+ unlock_page(page);
+ break;
+ }
+
+ /*
+ * This actually clears the dirty bit in the radix tree.
+ * See cifs_writepage() for more commentary.
+ */
+ set_page_writeback(page);
+
+ if (page_offset(page) >= mapping->host->i_size) {
+ done = 1;
+ unlock_page(page);
+ end_page_writeback(page);
+ break;
+ }
+
+ /*
+ * BB can we get rid of this? pages are held by pvec
+ */
+ page_cache_get(page);
+
+ len = min(mapping->host->i_size - page_offset(page),
+ (loff_t)PAGE_CACHE_SIZE);
+
+ /* reserve iov[0] for the smb header */
+ n_iov++;
+ iov[n_iov].iov_base = kmap(page);
+ iov[n_iov].iov_len = len;
+ bytes_to_write += len;
+
+ if (first < 0) {
+ first = i;
+ offset = page_offset(page);
+ }
+ next = page->index + 1;
+ if (bytes_to_write + PAGE_CACHE_SIZE > cifs_sb->wsize)
+ break;
+ }
+ if (n_iov) {
+ /* Search for a writable handle every time we call
+ * CIFSSMBWrite2. We can't rely on the last handle
+ * we used to still be valid
+ */
+ open_file = find_writable_file(CIFS_I(mapping->host));
+ if (!open_file) {
+ cERROR(1, ("No writable handles for inode"));
+ rc = -EBADF;
+ } else {
+ rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
+ open_file->netfid,
+ bytes_to_write, offset,
+ &bytes_written, iov, n_iov,
+ CIFS_LONG_OP);
+ atomic_dec(&open_file->wrtPending);
+ if (rc || bytes_written < bytes_to_write) {
+ cERROR(1, ("Write2 ret %d, wrote %d",
+ rc, bytes_written));
+ /* BB what if continued retry is
+ requested via mount flags? */
+ if (rc == -ENOSPC)
+ set_bit(AS_ENOSPC, &mapping->flags);
+ else
+ set_bit(AS_EIO, &mapping->flags);
+ } else {
+ cifs_stats_bytes_written(cifs_sb->tcon,
+ bytes_written);
+ }
+ }
+ for (i = 0; i < n_iov; i++) {
+ page = pvec.pages[first + i];
+ /* Should we also set page error on
+ success rc but too little data written? */
+ /* BB investigate retry logic on temporary
+ server crash cases and how recovery works
+ when page marked as error */
+ if (rc)
+ SetPageError(page);
+ kunmap(page);
+ unlock_page(page);
+ end_page_writeback(page);
+ page_cache_release(page);
+ }
+ if ((wbc->nr_to_write -= n_iov) <= 0)
+ done = 1;
+ index = next;
+ } else
+ /* Need to re-find the pages we skipped */
+ index = pvec.pages[0]->index + 1;
+
+ pagevec_release(&pvec);
+ }
+ if (!scanned && !done) {
+ /*
+ * We hit the last page and there is more work to be done: wrap
+ * back to the start of the file
+ */
+ scanned = 1;
+ index = 0;
+ goto retry;
+ }
+ if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
+ mapping->writeback_index = index;
+
+ FreeXid(xid);
+ kfree(iov);
+ return rc;
+}
+
+static int cifs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ int rc = -EFAULT;
+ int xid;
+
+ xid = GetXid();
+/* BB add check for wbc flags */
+ page_cache_get(page);
+ if (!PageUptodate(page))
+ cFYI(1, ("ppw - page not up to date"));
+
+ /*
+ * Set the "writeback" flag, and clear "dirty" in the radix tree.
+ *
+ * A writepage() implementation always needs to do either this,
+ * or re-dirty the page with "redirty_page_for_writepage()" in
+ * the case of a failure.
+ *
+ * Just unlocking the page will cause the radix tree tag-bits
+ * to fail to update with the state of the page correctly.
+ */
+ set_page_writeback(page);
+ rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE);
+ SetPageUptodate(page); /* BB add check for error and Clearuptodate? */
+ unlock_page(page);
+ end_page_writeback(page);
+ page_cache_release(page);
+ FreeXid(xid);
+ return rc;
+}
+
+static int cifs_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct page *page, void *fsdata)
+{
+ int rc;
+ struct inode *inode = mapping->host;
+
+ cFYI(1, ("write_end for page %p from pos %lld with %d bytes",
+ page, pos, copied));
+
+ if (PageChecked(page)) {
+ if (copied == len)
+ SetPageUptodate(page);
+ ClearPageChecked(page);
+ } else if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
+ SetPageUptodate(page);
+
+ if (!PageUptodate(page)) {
+ char *page_data;
+ unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
+ int xid;
+
+ xid = GetXid();
+ /* this is probably better than directly calling
+ partialpage_write since in this function the file handle is
+ known which we might as well leverage */
+ /* BB check if anything else missing out of ppw
+ such as updating last write time */
+ page_data = kmap(page);
+ rc = cifs_write(file, page_data + offset, copied, &pos);
+ /* if (rc < 0) should we set writebehind rc? */
+ kunmap(page);
+
+ FreeXid(xid);
+ } else {
+ rc = copied;
+ pos += copied;
+ set_page_dirty(page);
+ }
+
+ if (rc > 0) {
+ spin_lock(&inode->i_lock);
+ if (pos > inode->i_size)
+ i_size_write(inode, pos);
+ spin_unlock(&inode->i_lock);
+ }
+
+ unlock_page(page);
+ page_cache_release(page);
+
+ return rc;
+}
+
+int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ int xid;
+ int rc = 0;
+ struct inode *inode = file->f_path.dentry->d_inode;
+
+ xid = GetXid();
+
+ cFYI(1, ("Sync file - name: %s datasync: 0x%x",
+ dentry->d_name.name, datasync));
+
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (rc == 0) {
+ rc = CIFS_I(inode)->write_behind_rc;
+ CIFS_I(inode)->write_behind_rc = 0;
+ }
+ FreeXid(xid);
+ return rc;
+}
+
+/* static void cifs_sync_page(struct page *page)
+{
+ struct address_space *mapping;
+ struct inode *inode;
+ unsigned long index = page->index;
+ unsigned int rpages = 0;
+ int rc = 0;
+
+ cFYI(1, ("sync page %p",page));
+ mapping = page->mapping;
+ if (!mapping)
+ return 0;
+ inode = mapping->host;
+ if (!inode)
+ return; */
+
+/* fill in rpages then
+ result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */
+
+/* cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index));
+
+#if 0
+ if (rc < 0)
+ return rc;
+ return 0;
+#endif
+} */
+
+/*
+ * As file closes, flush all cached write data for this inode checking
+ * for write behind errors.
+ */
+int cifs_flush(struct file *file, fl_owner_t id)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ int rc = 0;
+
+ /* Rather than do the steps manually:
+ lock the inode for writing
+ loop through pages looking for write behind data (dirty pages)
+ coalesce into contiguous 16K (or smaller) chunks to write to server
+ send to server (prefer in parallel)
+ deal with writebehind errors
+ unlock inode for writing
+ filemapfdatawrite appears easier for the time being */
+
+ rc = filemap_fdatawrite(inode->i_mapping);
+ /* reset wb rc if we were able to write out dirty pages */
+ if (!rc) {
+ rc = CIFS_I(inode)->write_behind_rc;
+ CIFS_I(inode)->write_behind_rc = 0;
+ }
+
+ cFYI(1, ("Flush inode %p file %p rc %d", inode, file, rc));
+
+ return rc;
+}
+
+ssize_t cifs_user_read(struct file *file, char __user *read_data,
+ size_t read_size, loff_t *poffset)
+{
+ int rc = -EACCES;
+ unsigned int bytes_read = 0;
+ unsigned int total_read = 0;
+ unsigned int current_read_size;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ int xid;
+ struct cifsFileInfo *open_file;
+ char *smb_read_data;
+ char __user *current_offset;
+ struct smb_com_read_rsp *pSMBr;
+
+ xid = GetXid();
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+
+ if (file->private_data == NULL) {
+ FreeXid(xid);
+ return -EBADF;
+ }
+ open_file = (struct cifsFileInfo *)file->private_data;
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+ cFYI(1, ("attempting read on write only file instance"));
+
+ for (total_read = 0, current_offset = read_data;
+ read_size > total_read;
+ total_read += bytes_read, current_offset += bytes_read) {
+ current_read_size = min_t(const int, read_size - total_read,
+ cifs_sb->rsize);
+ rc = -EAGAIN;
+ smb_read_data = NULL;
+ while (rc == -EAGAIN) {
+ int buf_type = CIFS_NO_BUFFER;
+ if ((open_file->invalidHandle) &&
+ (!open_file->closePend)) {
+ rc = cifs_reopen_file(file, true);
+ if (rc != 0)
+ break;
+ }
+ rc = CIFSSMBRead(xid, pTcon,
+ open_file->netfid,
+ current_read_size, *poffset,
+ &bytes_read, &smb_read_data,
+ &buf_type);
+ pSMBr = (struct smb_com_read_rsp *)smb_read_data;
+ if (smb_read_data) {
+ if (copy_to_user(current_offset,
+ smb_read_data +
+ 4 /* RFC1001 length field */ +
+ le16_to_cpu(pSMBr->DataOffset),
+ bytes_read))
+ rc = -EFAULT;
+
+ if (buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(smb_read_data);
+ else if (buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(smb_read_data);
+ smb_read_data = NULL;
+ }
+ }
+ if (rc || (bytes_read == 0)) {
+ if (total_read) {
+ break;
+ } else {
+ FreeXid(xid);
+ return rc;
+ }
+ } else {
+ cifs_stats_bytes_read(pTcon, bytes_read);
+ *poffset += bytes_read;
+ }
+ }
+ FreeXid(xid);
+ return total_read;
+}
+
+
+static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
+ loff_t *poffset)
+{
+ int rc = -EACCES;
+ unsigned int bytes_read = 0;
+ unsigned int total_read;
+ unsigned int current_read_size;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ int xid;
+ char *current_offset;
+ struct cifsFileInfo *open_file;
+ int buf_type = CIFS_NO_BUFFER;
+
+ xid = GetXid();
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+
+ if (file->private_data == NULL) {
+ FreeXid(xid);
+ return -EBADF;
+ }
+ open_file = (struct cifsFileInfo *)file->private_data;
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+ cFYI(1, ("attempting read on write only file instance"));
+
+ for (total_read = 0, current_offset = read_data;
+ read_size > total_read;
+ total_read += bytes_read, current_offset += bytes_read) {
+ current_read_size = min_t(const int, read_size - total_read,
+ cifs_sb->rsize);
+ /* For windows me and 9x we do not want to request more
+ than it negotiated since it will refuse the read then */
+ if ((pTcon->ses) &&
+ !(pTcon->ses->capabilities & CAP_LARGE_FILES)) {
+ current_read_size = min_t(const int, current_read_size,
+ pTcon->ses->server->maxBuf - 128);
+ }
+ rc = -EAGAIN;
+ while (rc == -EAGAIN) {
+ if ((open_file->invalidHandle) &&
+ (!open_file->closePend)) {
+ rc = cifs_reopen_file(file, true);
+ if (rc != 0)
+ break;
+ }
+ rc = CIFSSMBRead(xid, pTcon,
+ open_file->netfid,
+ current_read_size, *poffset,
+ &bytes_read, &current_offset,
+ &buf_type);
+ }
+ if (rc || (bytes_read == 0)) {
+ if (total_read) {
+ break;
+ } else {
+ FreeXid(xid);
+ return rc;
+ }
+ } else {
+ cifs_stats_bytes_read(pTcon, total_read);
+ *poffset += bytes_read;
+ }
+ }
+ FreeXid(xid);
+ return total_read;
+}
+
+int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ int rc, xid;
+
+ xid = GetXid();
+ rc = cifs_revalidate(dentry);
+ if (rc) {
+ cFYI(1, ("Validation prior to mmap failed, error=%d", rc));
+ FreeXid(xid);
+ return rc;
+ }
+ rc = generic_file_mmap(file, vma);
+ FreeXid(xid);
+ return rc;
+}
+
+
+static void cifs_copy_cache_pages(struct address_space *mapping,
+ struct list_head *pages, int bytes_read, char *data,
+ struct pagevec *plru_pvec)
+{
+ struct page *page;
+ char *target;
+
+ while (bytes_read > 0) {
+ if (list_empty(pages))
+ break;
+
+ page = list_entry(pages->prev, struct page, lru);
+ list_del(&page->lru);
+
+ if (add_to_page_cache(page, mapping, page->index,
+ GFP_KERNEL)) {
+ page_cache_release(page);
+ cFYI(1, ("Add page cache failed"));
+ data += PAGE_CACHE_SIZE;
+ bytes_read -= PAGE_CACHE_SIZE;
+ continue;
+ }
+
+ target = kmap_atomic(page, KM_USER0);
+
+ if (PAGE_CACHE_SIZE > bytes_read) {
+ memcpy(target, data, bytes_read);
+ /* zero the tail end of this partial page */
+ memset(target + bytes_read, 0,
+ PAGE_CACHE_SIZE - bytes_read);
+ bytes_read = 0;
+ } else {
+ memcpy(target, data, PAGE_CACHE_SIZE);
+ bytes_read -= PAGE_CACHE_SIZE;
+ }
+ kunmap_atomic(target, KM_USER0);
+
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+ if (!pagevec_add(plru_pvec, page))
+ __pagevec_lru_add_file(plru_pvec);
+ data += PAGE_CACHE_SIZE;
+ }
+ return;
+}
+
+static int cifs_readpages(struct file *file, struct address_space *mapping,
+ struct list_head *page_list, unsigned num_pages)
+{
+ int rc = -EACCES;
+ int xid;
+ loff_t offset;
+ struct page *page;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ unsigned int bytes_read = 0;
+ unsigned int read_size, i;
+ char *smb_read_data = NULL;
+ struct smb_com_read_rsp *pSMBr;
+ struct pagevec lru_pvec;
+ struct cifsFileInfo *open_file;
+ int buf_type = CIFS_NO_BUFFER;
+
+ xid = GetXid();
+ if (file->private_data == NULL) {
+ FreeXid(xid);
+ return -EBADF;
+ }
+ open_file = (struct cifsFileInfo *)file->private_data;
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+
+ pagevec_init(&lru_pvec, 0);
+ cFYI(DBG2, ("rpages: num pages %d", num_pages));
+ for (i = 0; i < num_pages; ) {
+ unsigned contig_pages;
+ struct page *tmp_page;
+ unsigned long expected_index;
+
+ if (list_empty(page_list))
+ break;
+
+ page = list_entry(page_list->prev, struct page, lru);
+ offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+
+ /* count adjacent pages that we will read into */
+ contig_pages = 0;
+ expected_index =
+ list_entry(page_list->prev, struct page, lru)->index;
+ list_for_each_entry_reverse(tmp_page, page_list, lru) {
+ if (tmp_page->index == expected_index) {
+ contig_pages++;
+ expected_index++;
+ } else
+ break;
+ }
+ if (contig_pages + i > num_pages)
+ contig_pages = num_pages - i;
+
+ /* for reads over a certain size could initiate async
+ read ahead */
+
+ read_size = contig_pages * PAGE_CACHE_SIZE;
+ /* Read size needs to be in multiples of one page */
+ read_size = min_t(const unsigned int, read_size,
+ cifs_sb->rsize & PAGE_CACHE_MASK);
+ cFYI(DBG2, ("rpages: read size 0x%x contiguous pages %d",
+ read_size, contig_pages));
+ rc = -EAGAIN;
+ while (rc == -EAGAIN) {
+ if ((open_file->invalidHandle) &&
+ (!open_file->closePend)) {
+ rc = cifs_reopen_file(file, true);
+ if (rc != 0)
+ break;
+ }
+
+ rc = CIFSSMBRead(xid, pTcon,
+ open_file->netfid,
+ read_size, offset,
+ &bytes_read, &smb_read_data,
+ &buf_type);
+ /* BB more RC checks ? */
+ if (rc == -EAGAIN) {
+ if (smb_read_data) {
+ if (buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(smb_read_data);
+ else if (buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(smb_read_data);
+ smb_read_data = NULL;
+ }
+ }
+ }
+ if ((rc < 0) || (smb_read_data == NULL)) {
+ cFYI(1, ("Read error in readpages: %d", rc));
+ break;
+ } else if (bytes_read > 0) {
+ task_io_account_read(bytes_read);
+ pSMBr = (struct smb_com_read_rsp *)smb_read_data;
+ cifs_copy_cache_pages(mapping, page_list, bytes_read,
+ smb_read_data + 4 /* RFC1001 hdr */ +
+ le16_to_cpu(pSMBr->DataOffset), &lru_pvec);
+
+ i += bytes_read >> PAGE_CACHE_SHIFT;
+ cifs_stats_bytes_read(pTcon, bytes_read);
+ if ((bytes_read & PAGE_CACHE_MASK) != bytes_read) {
+ i++; /* account for partial page */
+
+ /* server copy of file can have smaller size
+ than client */
+ /* BB do we need to verify this common case ?
+ this case is ok - if we are at server EOF
+ we will hit it on next read */
+
+ /* break; */
+ }
+ } else {
+ cFYI(1, ("No bytes read (%d) at offset %lld . "
+ "Cleaning remaining pages from readahead list",
+ bytes_read, offset));
+ /* BB turn off caching and do new lookup on
+ file size at server? */
+ break;
+ }
+ if (smb_read_data) {
+ if (buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(smb_read_data);
+ else if (buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(smb_read_data);
+ smb_read_data = NULL;
+ }
+ bytes_read = 0;
+ }
+
+ pagevec_lru_add_file(&lru_pvec);
+
+/* need to free smb_read_data buf before exit */
+ if (smb_read_data) {
+ if (buf_type == CIFS_SMALL_BUFFER)
+ cifs_small_buf_release(smb_read_data);
+ else if (buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(smb_read_data);
+ smb_read_data = NULL;
+ }
+
+ FreeXid(xid);
+ return rc;
+}
+
+static int cifs_readpage_worker(struct file *file, struct page *page,
+ loff_t *poffset)
+{
+ char *read_data;
+ int rc;
+
+ page_cache_get(page);
+ read_data = kmap(page);
+ /* for reads over a certain size could initiate async read ahead */
+
+ rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset);
+
+ if (rc < 0)
+ goto io_error;
+ else
+ cFYI(1, ("Bytes read %d", rc));
+
+ file->f_path.dentry->d_inode->i_atime =
+ current_fs_time(file->f_path.dentry->d_inode->i_sb);
+
+ if (PAGE_CACHE_SIZE > rc)
+ memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc);
+
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ rc = 0;
+
+io_error:
+ kunmap(page);
+ page_cache_release(page);
+ return rc;
+}
+
+static int cifs_readpage(struct file *file, struct page *page)
+{
+ loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+ int rc = -EACCES;
+ int xid;
+
+ xid = GetXid();
+
+ if (file->private_data == NULL) {
+ FreeXid(xid);
+ return -EBADF;
+ }
+
+ cFYI(1, ("readpage %p at offset %d 0x%x\n",
+ page, (int)offset, (int)offset));
+
+ rc = cifs_readpage_worker(file, page, &offset);
+
+ unlock_page(page);
+
+ FreeXid(xid);
+ return rc;
+}
+
+static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
+{
+ struct cifsFileInfo *open_file;
+
+ read_lock(&GlobalSMBSeslock);
+ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
+ if (open_file->closePend)
+ continue;
+ if (open_file->pfile &&
+ ((open_file->pfile->f_flags & O_RDWR) ||
+ (open_file->pfile->f_flags & O_WRONLY))) {
+ read_unlock(&GlobalSMBSeslock);
+ return 1;
+ }
+ }
+ read_unlock(&GlobalSMBSeslock);
+ return 0;
+}
+
+/* We do not want to update the file size from server for inodes
+ open for write - to avoid races with writepage extending
+ the file - in the future we could consider allowing
+ refreshing the inode only on increases in the file size
+ but this is tricky to do without racing with writebehind
+ page caching in the current Linux kernel design */
+bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
+{
+ if (!cifsInode)
+ return true;
+
+ if (is_inode_writable(cifsInode)) {
+ /* This inode is open for write at least once */
+ struct cifs_sb_info *cifs_sb;
+
+ cifs_sb = CIFS_SB(cifsInode->vfs_inode.i_sb);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+ /* since no page cache to corrupt on directio
+ we can change size safely */
+ return true;
+ }
+
+ if (i_size_read(&cifsInode->vfs_inode) < end_of_file)
+ return true;
+
+ return false;
+ } else
+ return true;
+}
+
+static int cifs_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned flags,
+ struct page **pagep, void **fsdata)
+{
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+ loff_t offset = pos & (PAGE_CACHE_SIZE - 1);
+ loff_t page_start = pos & PAGE_MASK;
+ loff_t i_size;
+ struct page *page;
+ int rc = 0;
+
+ cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
+
+ page = grab_cache_page_write_begin(mapping, index, flags);
+ if (!page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (PageUptodate(page))
+ goto out;
+
+ /*
+ * If we write a full page it will be up to date, no need to read from
+ * the server. If the write is short, we'll end up doing a sync write
+ * instead.
+ */
+ if (len == PAGE_CACHE_SIZE)
+ goto out;
+
+ /*
+ * optimize away the read when we have an oplock, and we're not
+ * expecting to use any of the data we'd be reading in. That
+ * is, when the page lies beyond the EOF, or straddles the EOF
+ * and the write will cover all of the existing data.
+ */
+ if (CIFS_I(mapping->host)->clientCanCacheRead) {
+ i_size = i_size_read(mapping->host);
+ if (page_start >= i_size ||
+ (offset == 0 && (pos + len) >= i_size)) {
+ zero_user_segments(page, 0, offset,
+ offset + len,
+ PAGE_CACHE_SIZE);
+ /*
+ * PageChecked means that the parts of the page
+ * to which we're not writing are considered up
+ * to date. Once the data is copied to the
+ * page, it can be set uptodate.
+ */
+ SetPageChecked(page);
+ goto out;
+ }
+ }
+
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * might as well read a page, it is fast enough. If we get
+ * an error, we don't need to return it. cifs_write_end will
+ * do a sync write instead since PG_uptodate isn't set.
+ */
+ cifs_readpage_worker(file, page, &page_start);
+ } else {
+ /* we could try using another file handle if there is one -
+ but how would we lock it to prevent close of that handle
+ racing with this read? In any case
+ this will be written out by write_end so is fine */
+ }
+out:
+ *pagep = page;
+ return rc;
+}
+
+const struct address_space_operations cifs_addr_ops = {
+ .readpage = cifs_readpage,
+ .readpages = cifs_readpages,
+ .writepage = cifs_writepage,
+ .writepages = cifs_writepages,
+ .write_begin = cifs_write_begin,
+ .write_end = cifs_write_end,
+ .set_page_dirty = __set_page_dirty_nobuffers,
+ /* .sync_page = cifs_sync_page, */
+ /* .direct_IO = */
+};
+
+/*
+ * cifs_readpages requires the server to support a buffer large enough to
+ * contain the header plus one complete page of data. Otherwise, we need
+ * to leave cifs_readpages out of the address space operations.
+ */
+const struct address_space_operations cifs_addr_ops_smallbuf = {
+ .readpage = cifs_readpage,
+ .writepage = cifs_writepage,
+ .writepages = cifs_writepages,
+ .write_begin = cifs_write_begin,
+ .write_end = cifs_write_end,
+ .set_page_dirty = __set_page_dirty_nobuffers,
+ /* .sync_page = cifs_sync_page, */
+ /* .direct_IO = */
+};
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
new file mode 100644
index 0000000..ff8c68d
--- /dev/null
+++ b/fs/cifs/inode.c
@@ -0,0 +1,1953 @@
+/*
+ * fs/cifs/inode.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.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"
+
+
+static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_op = &cifs_file_inode_ops;
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ inode->i_fop = &cifs_file_direct_nobrl_ops;
+ else
+ inode->i_fop = &cifs_file_direct_ops;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ inode->i_fop = &cifs_file_nobrl_ops;
+ else { /* not direct, send byte range locks */
+ inode->i_fop = &cifs_file_ops;
+ }
+
+
+ /* check if server can support readpages */
+ if (cifs_sb->tcon->ses->server->maxBuf <
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
+ inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ inode->i_data.a_ops = &cifs_addr_ops;
+ break;
+ case S_IFDIR:
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ if (is_dfs_referral) {
+ inode->i_op = &cifs_dfs_referral_inode_operations;
+ } else {
+#else /* NO DFS support, treat as a directory */
+ {
+#endif
+ inode->i_op = &cifs_dir_inode_ops;
+ inode->i_fop = &cifs_dir_ops;
+ }
+ break;
+ case S_IFLNK:
+ inode->i_op = &cifs_symlink_inode_ops;
+ break;
+ default:
+ init_special_inode(inode, inode->i_mode, inode->i_rdev);
+ break;
+ }
+}
+
+static void cifs_unix_info_to_inode(struct inode *inode,
+ FILE_UNIX_BASIC_INFO *info, int force_uid_gid)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
+ __u64 num_of_bytes = le64_to_cpu(info->NumOfBytes);
+ __u64 end_of_file = le64_to_cpu(info->EndOfFile);
+
+ inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(info->LastAccessTime));
+ inode->i_mtime =
+ cifs_NTtimeToUnix(le64_to_cpu(info->LastModificationTime));
+ inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(info->LastStatusChange));
+ inode->i_mode = le64_to_cpu(info->Permissions);
+
+ /*
+ * Since we set the inode type below we need to mask off
+ * to avoid strange results if bits set above.
+ */
+ inode->i_mode &= ~S_IFMT;
+ switch (le32_to_cpu(info->Type)) {
+ case UNIX_FILE:
+ inode->i_mode |= S_IFREG;
+ break;
+ case UNIX_SYMLINK:
+ inode->i_mode |= S_IFLNK;
+ break;
+ case UNIX_DIR:
+ inode->i_mode |= S_IFDIR;
+ break;
+ case UNIX_CHARDEV:
+ inode->i_mode |= S_IFCHR;
+ inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+ le64_to_cpu(info->DevMinor) & MINORMASK);
+ break;
+ case UNIX_BLOCKDEV:
+ inode->i_mode |= S_IFBLK;
+ inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+ le64_to_cpu(info->DevMinor) & MINORMASK);
+ break;
+ case UNIX_FIFO:
+ inode->i_mode |= S_IFIFO;
+ break;
+ case UNIX_SOCKET:
+ inode->i_mode |= S_IFSOCK;
+ break;
+ default:
+ /* safest to call it a file if we do not know */
+ inode->i_mode |= S_IFREG;
+ cFYI(1, ("unknown type %d", le32_to_cpu(info->Type)));
+ break;
+ }
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) &&
+ !force_uid_gid)
+ inode->i_uid = cifs_sb->mnt_uid;
+ else
+ inode->i_uid = le64_to_cpu(info->Uid);
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) &&
+ !force_uid_gid)
+ inode->i_gid = cifs_sb->mnt_gid;
+ else
+ inode->i_gid = le64_to_cpu(info->Gid);
+
+ inode->i_nlink = le64_to_cpu(info->Nlinks);
+
+ spin_lock(&inode->i_lock);
+ if (is_size_safe_to_change(cifsInfo, end_of_file)) {
+ /*
+ * We can not safely change the file size here if the client
+ * is writing to it due to potential races.
+ */
+ i_size_write(inode, end_of_file);
+
+ /*
+ * i_blocks is not related to (i_size / i_blksize),
+ * but instead 512 byte (2**9) size is required for
+ * calculating num blocks.
+ */
+ inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
+ }
+ spin_unlock(&inode->i_lock);
+}
+
+
+/*
+ * Needed to setup inode data for the directory which is the
+ * junction to the new submount (ie to setup the fake directory
+ * which represents a DFS referral)
+ */
+static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat,
+ struct super_block *sb)
+{
+ struct inode *pinode = NULL;
+
+ memset(pfnd_dat, 0, sizeof(FILE_UNIX_BASIC_INFO));
+
+/* __le64 pfnd_dat->EndOfFile = cpu_to_le64(0);
+ __le64 pfnd_dat->NumOfBytes = cpu_to_le64(0);
+ __u64 UniqueId = 0; */
+ pfnd_dat->LastStatusChange =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->LastAccessTime =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->LastModificationTime =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->Type = cpu_to_le32(UNIX_DIR);
+ pfnd_dat->Permissions = cpu_to_le64(S_IXUGO | S_IRWXU);
+ pfnd_dat->Nlinks = cpu_to_le64(2);
+ if (sb->s_root)
+ pinode = sb->s_root->d_inode;
+ if (pinode == NULL)
+ return;
+
+ /* fill in default values for the remaining based on root
+ inode since we can not query the server for this inode info */
+ pfnd_dat->DevMajor = cpu_to_le64(MAJOR(pinode->i_rdev));
+ pfnd_dat->DevMinor = cpu_to_le64(MINOR(pinode->i_rdev));
+ pfnd_dat->Uid = cpu_to_le64(pinode->i_uid);
+ pfnd_dat->Gid = cpu_to_le64(pinode->i_gid);
+}
+
+int cifs_get_inode_info_unix(struct inode **pinode,
+ const unsigned char *full_path, struct super_block *sb, int xid)
+{
+ int rc = 0;
+ FILE_UNIX_BASIC_INFO find_data;
+ struct cifsTconInfo *pTcon;
+ struct inode *inode;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ bool is_dfs_referral = false;
+ struct cifsInodeInfo *cifsInfo;
+ __u64 num_of_bytes;
+ __u64 end_of_file;
+
+ pTcon = cifs_sb->tcon;
+ cFYI(1, ("Getting info on %s", full_path));
+
+ /* could have done a find first instead but this returns more info */
+ rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == -EREMOTE && !is_dfs_referral) {
+ is_dfs_referral = true;
+ cFYI(DBG2, ("DFS ref"));
+ /* for DFS, server does not give us real inode data */
+ fill_fake_finddataunix(&find_data, sb);
+ rc = 0;
+ } else if (rc)
+ goto cgiiu_exit;
+
+ num_of_bytes = le64_to_cpu(find_data.NumOfBytes);
+ end_of_file = le64_to_cpu(find_data.EndOfFile);
+
+ /* get new inode */
+ if (*pinode == NULL) {
+ *pinode = new_inode(sb);
+ if (*pinode == NULL) {
+ rc = -ENOMEM;
+ goto cgiiu_exit;
+ }
+ /* Is an i_ino of zero legal? */
+ /* note ino incremented to unique num in new_inode */
+ /* Are there sanity checks we can use to ensure that
+ the server is really filling in that field? */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
+ (*pinode)->i_ino = (unsigned long)find_data.UniqueId;
+
+ if (sb->s_flags & MS_NOATIME)
+ (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
+
+ insert_inode_hash(*pinode);
+ }
+
+ inode = *pinode;
+ cifsInfo = CIFS_I(inode);
+
+ cFYI(1, ("Old time %ld", cifsInfo->time));
+ cifsInfo->time = jiffies;
+ cFYI(1, ("New time %ld", cifsInfo->time));
+ /* this is ok to set on every inode revalidate */
+ atomic_set(&cifsInfo->inUse, 1);
+
+ cifs_unix_info_to_inode(inode, &find_data, 0);
+
+ if (num_of_bytes < end_of_file)
+ cFYI(1, ("allocation size less than end of file"));
+ cFYI(1, ("Size %ld and blocks %llu",
+ (unsigned long) inode->i_size,
+ (unsigned long long)inode->i_blocks));
+
+ cifs_set_ops(inode, is_dfs_referral);
+cgiiu_exit:
+ return rc;
+}
+
+static int decode_sfu_inode(struct inode *inode, __u64 size,
+ const unsigned char *path,
+ struct cifs_sb_info *cifs_sb, int xid)
+{
+ int rc;
+ int oplock = 0;
+ __u16 netfid;
+ struct cifsTconInfo *pTcon = cifs_sb->tcon;
+ char buf[24];
+ unsigned int bytes_read;
+ char *pbuf;
+
+ pbuf = buf;
+
+ if (size == 0) {
+ inode->i_mode |= S_IFIFO;
+ return 0;
+ } else if (size < 8) {
+ return -EINVAL; /* EOPNOTSUPP? */
+ }
+
+ rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
+ CREATE_NOT_DIR, &netfid, &oplock, NULL,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == 0) {
+ int buf_type = CIFS_NO_BUFFER;
+ /* Read header */
+ rc = CIFSSMBRead(xid, pTcon,
+ netfid,
+ 24 /* length */, 0 /* offset */,
+ &bytes_read, &pbuf, &buf_type);
+ if ((rc == 0) && (bytes_read >= 8)) {
+ if (memcmp("IntxBLK", pbuf, 8) == 0) {
+ cFYI(1, ("Block device"));
+ inode->i_mode |= S_IFBLK;
+ if (bytes_read == 24) {
+ /* we have enough to decode dev num */
+ __u64 mjr; /* major */
+ __u64 mnr; /* minor */
+ mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
+ mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
+ inode->i_rdev = MKDEV(mjr, mnr);
+ }
+ } else if (memcmp("IntxCHR", pbuf, 8) == 0) {
+ cFYI(1, ("Char device"));
+ inode->i_mode |= S_IFCHR;
+ if (bytes_read == 24) {
+ /* we have enough to decode dev num */
+ __u64 mjr; /* major */
+ __u64 mnr; /* minor */
+ mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
+ mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
+ inode->i_rdev = MKDEV(mjr, mnr);
+ }
+ } else if (memcmp("IntxLNK", pbuf, 7) == 0) {
+ cFYI(1, ("Symlink"));
+ inode->i_mode |= S_IFLNK;
+ } else {
+ inode->i_mode |= S_IFREG; /* file? */
+ rc = -EOPNOTSUPP;
+ }
+ } else {
+ inode->i_mode |= S_IFREG; /* then it is a file */
+ rc = -EOPNOTSUPP; /* or some unknown SFU type */
+ }
+ CIFSSMBClose(xid, pTcon, netfid);
+ }
+ return rc;
+}
+
+#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */
+
+static int get_sfu_mode(struct inode *inode,
+ const unsigned char *path,
+ struct cifs_sb_info *cifs_sb, int xid)
+{
+#ifdef CONFIG_CIFS_XATTR
+ ssize_t rc;
+ char ea_value[4];
+ __u32 mode;
+
+ rc = CIFSSMBQueryEA(xid, cifs_sb->tcon, path, "SETFILEBITS",
+ ea_value, 4 /* size of buf */, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc < 0)
+ return (int)rc;
+ else if (rc > 3) {
+ mode = le32_to_cpu(*((__le32 *)ea_value));
+ inode->i_mode &= ~SFBITS_MASK;
+ cFYI(1, ("special bits 0%o org mode 0%o", mode, inode->i_mode));
+ inode->i_mode = (mode & SFBITS_MASK) | inode->i_mode;
+ cFYI(1, ("special mode bits 0%o", mode));
+ return 0;
+ } else {
+ return 0;
+ }
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+/*
+ * Needed to setup inode data for the directory which is the
+ * junction to the new submount (ie to setup the fake directory
+ * which represents a DFS referral)
+ */
+static void fill_fake_finddata(FILE_ALL_INFO *pfnd_dat,
+ struct super_block *sb)
+{
+ memset(pfnd_dat, 0, sizeof(FILE_ALL_INFO));
+
+/* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0);
+ __le64 pfnd_dat->EndOfFile = cpu_to_le64(0);
+ __u8 pfnd_dat->DeletePending = 0;
+ __u8 pfnd_data->Directory = 0;
+ __le32 pfnd_dat->EASize = 0;
+ __u64 pfnd_dat->IndexNumber = 0;
+ __u64 pfnd_dat->IndexNumber1 = 0; */
+ pfnd_dat->CreationTime =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->LastAccessTime =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->LastWriteTime =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->ChangeTime =
+ cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY);
+ pfnd_dat->NumberOfLinks = cpu_to_le32(2);
+}
+
+int cifs_get_inode_info(struct inode **pinode,
+ const unsigned char *full_path, FILE_ALL_INFO *pfindData,
+ struct super_block *sb, int xid, const __u16 *pfid)
+{
+ int rc = 0;
+ __u32 attr;
+ struct cifsInodeInfo *cifsInfo;
+ struct cifsTconInfo *pTcon;
+ struct inode *inode;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ char *buf = NULL;
+ bool adjustTZ = false;
+ bool is_dfs_referral = false;
+ umode_t default_mode;
+
+ pTcon = cifs_sb->tcon;
+ cFYI(1, ("Getting info on %s", full_path));
+
+ if ((pfindData == NULL) && (*pinode != NULL)) {
+ if (CIFS_I(*pinode)->clientCanCacheRead) {
+ cFYI(1, ("No need to revalidate cached inode sizes"));
+ return rc;
+ }
+ }
+
+ /* if file info not passed in then get it from server */
+ if (pfindData == NULL) {
+ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ pfindData = (FILE_ALL_INFO *)buf;
+
+ /* could do find first instead but this returns more info */
+ rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData,
+ 0 /* not legacy */,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ /* BB optimize code so we do not make the above call
+ when server claims no NT SMB support and the above call
+ failed at least once - set flag in tcon or mount */
+ if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
+ rc = SMBQueryInformation(xid, pTcon, full_path,
+ pfindData, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ adjustTZ = true;
+ }
+ }
+ /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
+ if (rc == -EREMOTE) {
+ is_dfs_referral = true;
+ fill_fake_finddata(pfindData, sb);
+ rc = 0;
+ } else if (rc)
+ goto cgii_exit;
+
+ attr = le32_to_cpu(pfindData->Attributes);
+
+ /* get new inode */
+ if (*pinode == NULL) {
+ *pinode = new_inode(sb);
+ if (*pinode == NULL) {
+ rc = -ENOMEM;
+ goto cgii_exit;
+ }
+ /* Is an i_ino of zero legal? Can we use that to check
+ if the server supports returning inode numbers? Are
+ there other sanity checks we can use to ensure that
+ the server is really filling in that field? */
+
+ /* We can not use the IndexNumber field by default from
+ Windows or Samba (in ALL_INFO buf) but we can request
+ it explicitly. It may not be unique presumably if
+ the server has multiple devices mounted under one share */
+
+ /* There may be higher info levels that work but are
+ there Windows server or network appliances for which
+ IndexNumber field is not guaranteed unique? */
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ int rc1 = 0;
+ __u64 inode_num;
+
+ rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
+ full_path, &inode_num,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc1) {
+ cFYI(1, ("GetSrvInodeNum rc %d", rc1));
+ /* BB EOPNOSUPP disable SERVER_INUM? */
+ } else /* do we need cast or hash to ino? */
+ (*pinode)->i_ino = inode_num;
+ } /* else ino incremented to unique num in new_inode*/
+ if (sb->s_flags & MS_NOATIME)
+ (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
+ insert_inode_hash(*pinode);
+ }
+ inode = *pinode;
+ cifsInfo = CIFS_I(inode);
+ cifsInfo->cifsAttrs = attr;
+ cifsInfo->delete_pending = pfindData->DeletePending ? true : false;
+ cFYI(1, ("Old time %ld", cifsInfo->time));
+ cifsInfo->time = jiffies;
+ cFYI(1, ("New time %ld", cifsInfo->time));
+
+ /* blksize needs to be multiple of two. So safer to default to
+ blksize and blkbits set in superblock so 2**blkbits and blksize
+ will match rather than setting to:
+ (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
+
+ /* Linux can not store file creation time so ignore it */
+ if (pfindData->LastAccessTime)
+ inode->i_atime = cifs_NTtimeToUnix
+ (le64_to_cpu(pfindData->LastAccessTime));
+ else /* do not need to use current_fs_time - time not stored */
+ inode->i_atime = CURRENT_TIME;
+ inode->i_mtime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
+ inode->i_ctime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
+ cFYI(DBG2, ("Attributes came in as 0x%x", attr));
+ if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) {
+ inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj;
+ inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj;
+ }
+
+ /* get default inode mode */
+ if (attr & ATTR_DIRECTORY)
+ default_mode = cifs_sb->mnt_dir_mode;
+ else
+ default_mode = cifs_sb->mnt_file_mode;
+
+ /* set permission bits */
+ if (atomic_read(&cifsInfo->inUse) == 0 ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ inode->i_mode = default_mode;
+ else {
+ /* just reenable write bits if !ATTR_READONLY */
+ if ((inode->i_mode & S_IWUGO) == 0 &&
+ (attr & ATTR_READONLY) == 0)
+ inode->i_mode |= (S_IWUGO & default_mode);
+
+ inode->i_mode &= ~S_IFMT;
+ }
+ /* clear write bits if ATTR_READONLY is set */
+ if (attr & ATTR_READONLY)
+ inode->i_mode &= ~S_IWUGO;
+
+ /* set inode type */
+ if ((attr & ATTR_SYSTEM) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
+ /* no need to fix endianness on 0 */
+ if (pfindData->EndOfFile == 0)
+ inode->i_mode |= S_IFIFO;
+ else if (decode_sfu_inode(inode,
+ le64_to_cpu(pfindData->EndOfFile),
+ full_path, cifs_sb, xid))
+ cFYI(1, ("unknown SFU file type\n"));
+ } else {
+ if (attr & ATTR_DIRECTORY)
+ inode->i_mode |= S_IFDIR;
+ else
+ inode->i_mode |= S_IFREG;
+ }
+
+ spin_lock(&inode->i_lock);
+ if (is_size_safe_to_change(cifsInfo,
+ le64_to_cpu(pfindData->EndOfFile))) {
+ /* can not safely shrink the file size here if the
+ client is writing to it due to potential races */
+ i_size_write(inode, le64_to_cpu(pfindData->EndOfFile));
+
+ /* 512 bytes (2**9) is the fake blocksize that must be
+ used for this calculation */
+ inode->i_blocks = (512 - 1 + le64_to_cpu(
+ pfindData->AllocationSize)) >> 9;
+ }
+ spin_unlock(&inode->i_lock);
+
+ inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
+
+ /* BB fill in uid and gid here? with help from winbind?
+ or retrieve from NTFS stream extended attribute */
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ /* fill in 0777 bits from ACL */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ cFYI(1, ("Getting mode bits from ACL"));
+ acl_to_uid_mode(inode, full_path, pfid);
+ }
+#endif
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ /* fill in remaining high mode bits e.g. SUID, VTX */
+ get_sfu_mode(inode, full_path, cifs_sb, xid);
+ } else if (atomic_read(&cifsInfo->inUse) == 0) {
+ inode->i_uid = cifs_sb->mnt_uid;
+ inode->i_gid = cifs_sb->mnt_gid;
+ /* set so we do not keep refreshing these fields with
+ bad data after user has changed them in memory */
+ atomic_set(&cifsInfo->inUse, 1);
+ }
+
+ cifs_set_ops(inode, is_dfs_referral);
+
+
+
+
+cgii_exit:
+ kfree(buf);
+ return rc;
+}
+
+static const struct inode_operations cifs_ipc_inode_ops = {
+ .lookup = cifs_lookup,
+};
+
+/* gets root inode */
+struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
+{
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct inode *inode;
+ long rc;
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ xid = GetXid();
+
+ if (cifs_sb->tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid,
+ NULL);
+ if (rc && cifs_sb->tcon->ipc) {
+ cFYI(1, ("ipc connection - fake read inode"));
+ inode->i_mode |= S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_op = &cifs_ipc_inode_ops;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_uid = cifs_sb->mnt_uid;
+ inode->i_gid = cifs_sb->mnt_gid;
+ } else if (rc) {
+ _FreeXid(xid);
+ iget_failed(inode);
+ return ERR_PTR(rc);
+ }
+
+ unlock_new_inode(inode);
+
+ /* can not call macro FreeXid here since in a void func
+ * TODO: This is no longer true
+ */
+ _FreeXid(xid);
+ return inode;
+}
+
+static int
+cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
+ char *full_path, __u32 dosattr)
+{
+ int rc;
+ int oplock = 0;
+ __u16 netfid;
+ __u32 netpid;
+ bool set_time = false;
+ struct cifsFileInfo *open_file;
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsTconInfo *pTcon = cifs_sb->tcon;
+ FILE_BASIC_INFO info_buf;
+
+ if (attrs->ia_valid & ATTR_ATIME) {
+ set_time = true;
+ info_buf.LastAccessTime =
+ cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
+ } else
+ info_buf.LastAccessTime = 0;
+
+ if (attrs->ia_valid & ATTR_MTIME) {
+ set_time = true;
+ info_buf.LastWriteTime =
+ cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
+ } else
+ info_buf.LastWriteTime = 0;
+
+ /*
+ * Samba throws this field away, but windows may actually use it.
+ * Do not set ctime unless other time stamps are changed explicitly
+ * (i.e. by utimes()) since we would then have a mix of client and
+ * server times.
+ */
+ if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
+ cFYI(1, ("CIFS - CTIME changed"));
+ info_buf.ChangeTime =
+ cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
+ } else
+ info_buf.ChangeTime = 0;
+
+ info_buf.CreationTime = 0; /* don't change */
+ info_buf.Attributes = cpu_to_le32(dosattr);
+
+ /*
+ * If the file is already open for write, just use that fileid
+ */
+ open_file = find_writable_file(cifsInode);
+ if (open_file) {
+ netfid = open_file->netfid;
+ netpid = open_file->pid;
+ goto set_via_filehandle;
+ }
+
+ /*
+ * NT4 apparently returns success on this call, but it doesn't
+ * really work.
+ */
+ if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
+ rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
+ &info_buf, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == 0) {
+ cifsInode->cifsAttrs = dosattr;
+ goto out;
+ } else if (rc != -EOPNOTSUPP && rc != -EINVAL)
+ goto out;
+ }
+
+ cFYI(1, ("calling SetFileInfo since SetPathInfo for "
+ "times not supported by this server"));
+ rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
+ SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
+ CREATE_NOT_DIR, &netfid, &oplock,
+ NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (rc != 0) {
+ if (rc == -EIO)
+ rc = -EINVAL;
+ goto out;
+ }
+
+ netpid = current->tgid;
+
+set_via_filehandle:
+ rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
+ if (!rc)
+ cifsInode->cifsAttrs = dosattr;
+
+ if (open_file == NULL)
+ CIFSSMBClose(xid, pTcon, netfid);
+ else
+ atomic_dec(&open_file->wrtPending);
+out:
+ return rc;
+}
+
+/*
+ * open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
+ * and rename it to a random name that hopefully won't conflict with
+ * anything else.
+ */
+static int
+cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
+{
+ int oplock = 0;
+ int rc;
+ __u16 netfid;
+ struct inode *inode = dentry->d_inode;
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsTconInfo *tcon = cifs_sb->tcon;
+ __u32 dosattr, origattr;
+ FILE_BASIC_INFO *info_buf = NULL;
+
+ rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
+ DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
+ &netfid, &oplock, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc != 0)
+ goto out;
+
+ origattr = cifsInode->cifsAttrs;
+ if (origattr == 0)
+ origattr |= ATTR_NORMAL;
+
+ dosattr = origattr & ~ATTR_READONLY;
+ if (dosattr == 0)
+ dosattr |= ATTR_NORMAL;
+ dosattr |= ATTR_HIDDEN;
+
+ /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */
+ if (dosattr != origattr) {
+ info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL);
+ if (info_buf == NULL) {
+ rc = -ENOMEM;
+ goto out_close;
+ }
+ info_buf->Attributes = cpu_to_le32(dosattr);
+ rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid,
+ current->tgid);
+ /* although we would like to mark the file hidden
+ if that fails we will still try to rename it */
+ if (rc != 0)
+ cifsInode->cifsAttrs = dosattr;
+ else
+ dosattr = origattr; /* since not able to change them */
+ }
+
+ /* rename the file */
+ rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc != 0) {
+ rc = -ETXTBSY;
+ goto undo_setattr;
+ }
+
+ /* try to set DELETE_ON_CLOSE */
+ if (!cifsInode->delete_pending) {
+ rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid,
+ current->tgid);
+ /*
+ * some samba versions return -ENOENT when we try to set the
+ * file disposition here. Likely a samba bug, but work around
+ * it for now. This means that some cifsXXX files may hang
+ * around after they shouldn't.
+ *
+ * BB: remove this hack after more servers have the fix
+ */
+ if (rc == -ENOENT)
+ rc = 0;
+ else if (rc != 0) {
+ rc = -ETXTBSY;
+ goto undo_rename;
+ }
+ cifsInode->delete_pending = true;
+ }
+
+out_close:
+ CIFSSMBClose(xid, tcon, netfid);
+out:
+ kfree(info_buf);
+ return rc;
+
+ /*
+ * reset everything back to the original state. Don't bother
+ * dealing with errors here since we can't do anything about
+ * them anyway.
+ */
+undo_rename:
+ CIFSSMBRenameOpenFile(xid, tcon, netfid, dentry->d_name.name,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+undo_setattr:
+ if (dosattr != origattr) {
+ info_buf->Attributes = cpu_to_le32(origattr);
+ if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid,
+ current->tgid))
+ cifsInode->cifsAttrs = origattr;
+ }
+
+ goto out_close;
+}
+
+int cifs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int rc = 0;
+ int xid;
+ char *full_path = NULL;
+ struct inode *inode = dentry->d_inode;
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct super_block *sb = dir->i_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifsTconInfo *tcon = cifs_sb->tcon;
+ struct iattr *attrs = NULL;
+ __u32 dosattr = 0, origattr = 0;
+
+ cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry));
+
+ xid = GetXid();
+
+ /* Unlink can be called from rename so we can not take the
+ * sb->s_vfs_rename_mutex here */
+ full_path = build_path_from_dentry(dentry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
+ rc = CIFSPOSIXDelFile(xid, tcon, full_path,
+ SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ cFYI(1, ("posix del rc %d", rc));
+ if ((rc == 0) || (rc == -ENOENT))
+ goto psx_del_no_retry;
+ }
+
+retry_std_delete:
+ rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+psx_del_no_retry:
+ if (!rc) {
+ if (inode)
+ drop_nlink(inode);
+ } else if (rc == -ENOENT) {
+ d_drop(dentry);
+ } else if (rc == -ETXTBSY) {
+ rc = cifs_rename_pending_delete(full_path, dentry, xid);
+ if (rc == 0)
+ drop_nlink(inode);
+ } else if (rc == -EACCES && dosattr == 0) {
+ attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
+ if (attrs == NULL) {
+ rc = -ENOMEM;
+ goto out_reval;
+ }
+
+ /* try to reset dos attributes */
+ origattr = cifsInode->cifsAttrs;
+ if (origattr == 0)
+ origattr |= ATTR_NORMAL;
+ dosattr = origattr & ~ATTR_READONLY;
+ if (dosattr == 0)
+ dosattr |= ATTR_NORMAL;
+ dosattr |= ATTR_HIDDEN;
+
+ rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
+ if (rc != 0)
+ goto out_reval;
+
+ goto retry_std_delete;
+ }
+
+ /* undo the setattr if we errored out and it's needed */
+ if (rc != 0 && dosattr != 0)
+ cifs_set_file_info(inode, attrs, xid, full_path, origattr);
+
+out_reval:
+ if (inode) {
+ cifsInode = CIFS_I(inode);
+ cifsInode->time = 0; /* will force revalidate to get info
+ when needed */
+ inode->i_ctime = current_fs_time(sb);
+ }
+ dir->i_ctime = dir->i_mtime = current_fs_time(sb);
+ cifsInode = CIFS_I(dir);
+ CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
+
+ kfree(full_path);
+ kfree(attrs);
+ FreeXid(xid);
+ return rc;
+}
+
+static void posix_fill_in_inode(struct inode *tmp_inode,
+ FILE_UNIX_BASIC_INFO *pData, int isNewInode)
+{
+ struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
+ loff_t local_size;
+ struct timespec local_mtime;
+
+ cifsInfo->time = jiffies;
+ atomic_inc(&cifsInfo->inUse);
+
+ /* save mtime and size */
+ local_mtime = tmp_inode->i_mtime;
+ local_size = tmp_inode->i_size;
+
+ cifs_unix_info_to_inode(tmp_inode, pData, 1);
+ cifs_set_ops(tmp_inode, false);
+
+ if (!S_ISREG(tmp_inode->i_mode))
+ return;
+
+ /*
+ * No sense invalidating pages for new inode
+ * since we we have not started caching
+ * readahead file data yet.
+ */
+ if (isNewInode)
+ return;
+
+ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
+ (local_size == tmp_inode->i_size)) {
+ cFYI(1, ("inode exists but unchanged"));
+ } else {
+ /* file may have changed on server */
+ cFYI(1, ("invalidate inode, readdir detected change"));
+ invalidate_remote_inode(tmp_inode);
+ }
+}
+
+int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
+{
+ int rc = 0, tmprc;
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ struct inode *newinode = NULL;
+
+ cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode));
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ if ((pTcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_POSIX_PATH_OPS_CAP &
+ le64_to_cpu(pTcon->fsUnixInfo.Capability))) {
+ u32 oplock = 0;
+ FILE_UNIX_BASIC_INFO *pInfo =
+ kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
+ if (pInfo == NULL) {
+ rc = -ENOMEM;
+ goto mkdir_out;
+ }
+
+ mode &= ~current->fs->umask;
+ rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT,
+ mode, NULL /* netfid */, pInfo, &oplock,
+ full_path, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == -EOPNOTSUPP) {
+ kfree(pInfo);
+ goto mkdir_retry_old;
+ } else if (rc) {
+ cFYI(1, ("posix mkdir returned 0x%x", rc));
+ d_drop(direntry);
+ } else {
+ if (pInfo->Type == cpu_to_le32(-1)) {
+ /* no return info, go query for it */
+ kfree(pInfo);
+ goto mkdir_get_info;
+ }
+/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need
+ to set uid/gid */
+ inc_nlink(inode);
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+
+ newinode = new_inode(inode->i_sb);
+ if (newinode == NULL) {
+ kfree(pInfo);
+ goto mkdir_get_info;
+ }
+
+ /* Is an i_ino of zero legal? */
+ /* Are there sanity checks we can use to ensure that
+ the server is really filling in that field? */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ newinode->i_ino =
+ (unsigned long)pInfo->UniqueId;
+ } /* note ino incremented to unique num in new_inode */
+ if (inode->i_sb->s_flags & MS_NOATIME)
+ newinode->i_flags |= S_NOATIME | S_NOCMTIME;
+ newinode->i_nlink = 2;
+
+ insert_inode_hash(newinode);
+ d_instantiate(direntry, newinode);
+
+ /* we already checked in POSIXCreate whether
+ frame was long enough */
+ posix_fill_in_inode(direntry->d_inode,
+ pInfo, 1 /* NewInode */);
+#ifdef CONFIG_CIFS_DEBUG2
+ cFYI(1, ("instantiated dentry %p %s to inode %p",
+ direntry, direntry->d_name.name, newinode));
+
+ if (newinode->i_nlink != 2)
+ cFYI(1, ("unexpected number of links %d",
+ newinode->i_nlink));
+#endif
+ }
+ kfree(pInfo);
+ goto mkdir_out;
+ }
+mkdir_retry_old:
+ /* BB add setting the equivalent of mode via CreateX w/ACLs */
+ rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc) {
+ cFYI(1, ("cifs_mkdir returned 0x%x", rc));
+ d_drop(direntry);
+ } else {
+mkdir_get_info:
+ inc_nlink(inode);
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&newinode, full_path, NULL,
+ inode->i_sb, xid, NULL);
+
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ d_instantiate(direntry, newinode);
+ /* setting nlink not necessary except in cases where we
+ * failed to get it from the server or was set bogus */
+ if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
+ direntry->d_inode->i_nlink = 2;
+
+ mode &= ~current->fs->umask;
+ /* must turn on setgid bit if parent dir has it */
+ if (inode->i_mode & S_ISGID)
+ mode |= S_ISGID;
+
+ if (pTcon->unix_ext) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = (__u64)current->fsuid;
+ if (inode->i_mode & S_ISGID)
+ args.gid = (__u64)inode->i_gid;
+ else
+ args.gid = (__u64)current->fsgid;
+ } else {
+ args.uid = NO_CHANGE_64;
+ args.gid = NO_CHANGE_64;
+ }
+ CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else {
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
+ (mode & S_IWUGO) == 0) {
+ FILE_BASIC_INFO pInfo;
+ struct cifsInodeInfo *cifsInode;
+ u32 dosattrs;
+
+ memset(&pInfo, 0, sizeof(pInfo));
+ cifsInode = CIFS_I(newinode);
+ dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
+ pInfo.Attributes = cpu_to_le32(dosattrs);
+ tmprc = CIFSSMBSetPathInfo(xid, pTcon,
+ full_path, &pInfo,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (tmprc == 0)
+ cifsInode->cifsAttrs = dosattrs;
+ }
+ if (direntry->d_inode) {
+ if (cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_DYNPERM)
+ direntry->d_inode->i_mode =
+ (mode | S_IFDIR);
+
+ if (cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_SET_UID) {
+ direntry->d_inode->i_uid =
+ current->fsuid;
+ if (inode->i_mode & S_ISGID)
+ direntry->d_inode->i_gid =
+ inode->i_gid;
+ else
+ direntry->d_inode->i_gid =
+ current->fsgid;
+ }
+ }
+ }
+ }
+mkdir_out:
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_rmdir(struct inode *inode, struct dentry *direntry)
+{
+ int rc = 0;
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ struct cifsInodeInfo *cifsInode;
+
+ cFYI(1, ("cifs_rmdir, inode = 0x%p", inode));
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (!rc) {
+ drop_nlink(inode);
+ spin_lock(&direntry->d_inode->i_lock);
+ i_size_write(direntry->d_inode, 0);
+ clear_nlink(direntry->d_inode);
+ spin_unlock(&direntry->d_inode->i_lock);
+ }
+
+ cifsInode = CIFS_I(direntry->d_inode);
+ cifsInode->time = 0; /* force revalidate to go get info when
+ needed */
+ direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
+ current_fs_time(inode->i_sb);
+
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+static int
+cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
+ struct dentry *to_dentry, const char *toPath)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
+ struct cifsTconInfo *pTcon = cifs_sb->tcon;
+ __u16 srcfid;
+ int oplock, rc;
+
+ /* try path-based rename first */
+ rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ /*
+ * don't bother with rename by filehandle unless file is busy and
+ * source Note that cross directory moves do not work with
+ * rename by filehandle to various Windows servers.
+ */
+ if (rc == 0 || rc != -ETXTBSY)
+ return rc;
+
+ /* open the file to be renamed -- we need DELETE perms */
+ rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
+ CREATE_NOT_DIR, &srcfid, &oplock, NULL,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (rc == 0) {
+ rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid,
+ (const char *) to_dentry->d_name.name,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ CIFSSMBClose(xid, pTcon, srcfid);
+ }
+
+ return rc;
+}
+
+int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
+ struct inode *target_dir, struct dentry *target_dentry)
+{
+ char *fromName = NULL;
+ char *toName = NULL;
+ struct cifs_sb_info *cifs_sb_source;
+ struct cifs_sb_info *cifs_sb_target;
+ struct cifsTconInfo *tcon;
+ FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
+ FILE_UNIX_BASIC_INFO *info_buf_target;
+ int xid, rc, tmprc;
+
+ cifs_sb_target = CIFS_SB(target_dir->i_sb);
+ cifs_sb_source = CIFS_SB(source_dir->i_sb);
+ tcon = cifs_sb_source->tcon;
+
+ xid = GetXid();
+
+ /*
+ * BB: this might be allowed if same server, but different share.
+ * Consider adding support for this
+ */
+ if (tcon != cifs_sb_target->tcon) {
+ rc = -EXDEV;
+ goto cifs_rename_exit;
+ }
+
+ /*
+ * we already have the rename sem so we do not need to
+ * grab it again here to protect the path integrity
+ */
+ fromName = build_path_from_dentry(source_dentry);
+ if (fromName == NULL) {
+ rc = -ENOMEM;
+ goto cifs_rename_exit;
+ }
+
+ toName = build_path_from_dentry(target_dentry);
+ if (toName == NULL) {
+ rc = -ENOMEM;
+ goto cifs_rename_exit;
+ }
+
+ rc = cifs_do_rename(xid, source_dentry, fromName,
+ target_dentry, toName);
+
+ if (rc == -EEXIST && tcon->unix_ext) {
+ /*
+ * Are src and dst hardlinks of same inode? We can
+ * only tell with unix extensions enabled
+ */
+ info_buf_source =
+ kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
+ GFP_KERNEL);
+ if (info_buf_source == NULL) {
+ rc = -ENOMEM;
+ goto cifs_rename_exit;
+ }
+
+ info_buf_target = info_buf_source + 1;
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
+ info_buf_source,
+ cifs_sb_source->local_nls,
+ cifs_sb_source->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (tmprc != 0)
+ goto unlink_target;
+
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon,
+ toName, info_buf_target,
+ cifs_sb_target->local_nls,
+ /* remap based on source sb */
+ cifs_sb_source->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (tmprc == 0 && (info_buf_source->UniqueId ==
+ info_buf_target->UniqueId)) {
+ /* same file, POSIX says that this is a noop */
+ rc = 0;
+ goto cifs_rename_exit;
+ }
+ } /* else ... BB we could add the same check for Windows by
+ checking the UniqueId via FILE_INTERNAL_INFO */
+
+unlink_target:
+ if ((rc == -EACCES) || (rc == -EEXIST)) {
+ tmprc = cifs_unlink(target_dir, target_dentry);
+ if (tmprc)
+ goto cifs_rename_exit;
+
+ rc = cifs_do_rename(xid, source_dentry, fromName,
+ target_dentry, toName);
+ }
+
+cifs_rename_exit:
+ kfree(info_buf_source);
+ kfree(fromName);
+ kfree(toName);
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_revalidate(struct dentry *direntry)
+{
+ int xid;
+ int rc = 0, wbrc = 0;
+ char *full_path;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsInodeInfo *cifsInode;
+ loff_t local_size;
+ struct timespec local_mtime;
+ bool invalidate_inode = false;
+
+ if (direntry->d_inode == NULL)
+ return -ENOENT;
+
+ cifsInode = CIFS_I(direntry->d_inode);
+
+ if (cifsInode == NULL)
+ return -ENOENT;
+
+ /* no sense revalidating inode info on file that no one can write */
+ if (CIFS_I(direntry->d_inode)->clientCanCacheRead)
+ return rc;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(direntry->d_sb);
+
+ /* can not safely grab the rename sem here if rename calls revalidate
+ since that would deadlock */
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld "
+ "jiffies %ld", full_path, direntry->d_inode,
+ direntry->d_inode->i_count.counter, direntry,
+ direntry->d_time, jiffies));
+
+ if (cifsInode->time == 0) {
+ /* was set to zero previously to force revalidate */
+ } else if (time_before(jiffies, cifsInode->time + HZ) &&
+ lookupCacheEnabled) {
+ if ((S_ISREG(direntry->d_inode->i_mode) == 0) ||
+ (direntry->d_inode->i_nlink == 1)) {
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+ } else {
+ cFYI(1, ("Have to revalidate file due to hardlinks"));
+ }
+ }
+
+ /* save mtime and size */
+ local_mtime = direntry->d_inode->i_mtime;
+ local_size = direntry->d_inode->i_size;
+
+ if (cifs_sb->tcon->unix_ext) {
+ rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path,
+ direntry->d_sb, xid);
+ if (rc) {
+ cFYI(1, ("error on getting revalidate info %d", rc));
+/* if (rc != -ENOENT)
+ rc = 0; */ /* BB should we cache info on
+ certain errors? */
+ }
+ } else {
+ rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL,
+ direntry->d_sb, xid, NULL);
+ if (rc) {
+ cFYI(1, ("error on getting revalidate info %d", rc));
+/* if (rc != -ENOENT)
+ rc = 0; */ /* BB should we cache info on
+ certain errors? */
+ }
+ }
+ /* should we remap certain errors, access denied?, to zero */
+
+ /* if not oplocked, we invalidate inode pages if mtime or file size
+ had changed on server */
+
+ if (timespec_equal(&local_mtime, &direntry->d_inode->i_mtime) &&
+ (local_size == direntry->d_inode->i_size)) {
+ cFYI(1, ("cifs_revalidate - inode unchanged"));
+ } else {
+ /* file may have changed on server */
+ if (cifsInode->clientCanCacheRead) {
+ /* no need to invalidate inode pages since we were the
+ only ones who could have modified the file and the
+ server copy is staler than ours */
+ } else {
+ invalidate_inode = true;
+ }
+ }
+
+ /* can not grab this sem since kernel filesys locking documentation
+ indicates i_mutex may be taken by the kernel on lookup and rename
+ which could deadlock if we grab the i_mutex here as well */
+/* mutex_lock(&direntry->d_inode->i_mutex);*/
+ /* need to write out dirty pages here */
+ if (direntry->d_inode->i_mapping) {
+ /* do we need to lock inode until after invalidate completes
+ below? */
+ wbrc = filemap_fdatawrite(direntry->d_inode->i_mapping);
+ if (wbrc)
+ CIFS_I(direntry->d_inode)->write_behind_rc = wbrc;
+ }
+ if (invalidate_inode) {
+ /* shrink_dcache not necessary now that cifs dentry ops
+ are exported for negative dentries */
+/* if (S_ISDIR(direntry->d_inode->i_mode))
+ shrink_dcache_parent(direntry); */
+ if (S_ISREG(direntry->d_inode->i_mode)) {
+ if (direntry->d_inode->i_mapping) {
+ wbrc = filemap_fdatawait(direntry->d_inode->i_mapping);
+ if (wbrc)
+ CIFS_I(direntry->d_inode)->write_behind_rc = wbrc;
+ }
+ /* may eventually have to do this for open files too */
+ if (list_empty(&(cifsInode->openFileList))) {
+ /* changed on server - flush read ahead pages */
+ cFYI(1, ("Invalidating read ahead data on "
+ "closed file"));
+ invalidate_remote_inode(direntry->d_inode);
+ }
+ }
+ }
+/* mutex_unlock(&direntry->d_inode->i_mutex); */
+
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat)
+{
+ int err = cifs_revalidate(dentry);
+ if (!err) {
+ generic_fillattr(dentry->d_inode, stat);
+ stat->blksize = CIFS_MAX_MSGSIZE;
+ }
+ return err;
+}
+
+static int cifs_truncate_page(struct address_space *mapping, loff_t from)
+{
+ pgoff_t index = from >> PAGE_CACHE_SHIFT;
+ unsigned offset = from & (PAGE_CACHE_SIZE - 1);
+ struct page *page;
+ int rc = 0;
+
+ page = grab_cache_page(mapping, index);
+ if (!page)
+ return -ENOMEM;
+
+ zero_user_segment(page, offset, PAGE_CACHE_SIZE);
+ unlock_page(page);
+ page_cache_release(page);
+ return rc;
+}
+
+static int cifs_vmtruncate(struct inode *inode, loff_t offset)
+{
+ struct address_space *mapping = inode->i_mapping;
+ unsigned long limit;
+
+ spin_lock(&inode->i_lock);
+ if (inode->i_size < offset)
+ goto do_expand;
+ /*
+ * truncation of in-use swapfiles is disallowed - it would cause
+ * subsequent swapout to scribble on the now-freed blocks.
+ */
+ if (IS_SWAPFILE(inode)) {
+ spin_unlock(&inode->i_lock);
+ goto out_busy;
+ }
+ i_size_write(inode, offset);
+ spin_unlock(&inode->i_lock);
+ /*
+ * unmap_mapping_range is called twice, first simply for efficiency
+ * so that truncate_inode_pages does fewer single-page unmaps. However
+ * after this first call, and before truncate_inode_pages finishes,
+ * it is possible for private pages to be COWed, which remain after
+ * truncate_inode_pages finishes, hence the second unmap_mapping_range
+ * call must be made for correctness.
+ */
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+ truncate_inode_pages(mapping, offset);
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+ goto out_truncate;
+
+do_expand:
+ limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+ if (limit != RLIM_INFINITY && offset > limit) {
+ spin_unlock(&inode->i_lock);
+ goto out_sig;
+ }
+ if (offset > inode->i_sb->s_maxbytes) {
+ spin_unlock(&inode->i_lock);
+ goto out_big;
+ }
+ i_size_write(inode, offset);
+ spin_unlock(&inode->i_lock);
+out_truncate:
+ if (inode->i_op && inode->i_op->truncate)
+ inode->i_op->truncate(inode);
+ return 0;
+out_sig:
+ send_sig(SIGXFSZ, current, 0);
+out_big:
+ return -EFBIG;
+out_busy:
+ return -ETXTBSY;
+}
+
+static int
+cifs_set_file_size(struct inode *inode, struct iattr *attrs,
+ int xid, char *full_path)
+{
+ int rc;
+ struct cifsFileInfo *open_file;
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsTconInfo *pTcon = cifs_sb->tcon;
+
+ /*
+ * To avoid spurious oplock breaks from server, in the case of
+ * inodes that we already have open, avoid doing path based
+ * setting of file size if we can do it by handle.
+ * This keeps our caching token (oplock) and avoids timeouts
+ * when the local oplock break takes longer to flush
+ * writebehind data than the SMB timeout for the SetPathInfo
+ * request would allow
+ */
+ open_file = find_writable_file(cifsInode);
+ if (open_file) {
+ __u16 nfid = open_file->netfid;
+ __u32 npid = open_file->pid;
+ rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
+ npid, false);
+ atomic_dec(&open_file->wrtPending);
+ cFYI(1, ("SetFSize for attrs rc = %d", rc));
+ if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
+ unsigned int bytes_written;
+ rc = CIFSSMBWrite(xid, pTcon, nfid, 0, attrs->ia_size,
+ &bytes_written, NULL, NULL, 1);
+ cFYI(1, ("Wrt seteof rc %d", rc));
+ }
+ } else
+ rc = -EINVAL;
+
+ if (rc != 0) {
+ /* Set file size by pathname rather than by handle
+ either because no valid, writeable file handle for
+ it was found or because there was an error setting
+ it by handle */
+ rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size,
+ false, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc));
+ if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
+ __u16 netfid;
+ int oplock = 0;
+
+ rc = SMBLegacyOpen(xid, pTcon, full_path,
+ FILE_OPEN, GENERIC_WRITE,
+ CREATE_NOT_DIR, &netfid, &oplock, NULL,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == 0) {
+ unsigned int bytes_written;
+ rc = CIFSSMBWrite(xid, pTcon, netfid, 0,
+ attrs->ia_size,
+ &bytes_written, NULL,
+ NULL, 1);
+ cFYI(1, ("wrt seteof rc %d", rc));
+ CIFSSMBClose(xid, pTcon, netfid);
+ }
+ }
+ }
+
+ if (rc == 0) {
+ rc = cifs_vmtruncate(inode, attrs->ia_size);
+ cifs_truncate_page(inode->i_mapping, inode->i_size);
+ }
+
+ return rc;
+}
+
+static int
+cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
+{
+ int rc;
+ int xid;
+ char *full_path = NULL;
+ struct inode *inode = direntry->d_inode;
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsTconInfo *pTcon = cifs_sb->tcon;
+ struct cifs_unix_set_info_args *args = NULL;
+
+ cFYI(1, ("setattr_unix on file %s attrs->ia_valid=0x%x",
+ direntry->d_name.name, attrs->ia_valid));
+
+ xid = GetXid();
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) {
+ /* check if we have permission to change attrs */
+ rc = inode_change_ok(inode, attrs);
+ if (rc < 0)
+ goto out;
+ else
+ rc = 0;
+ }
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) {
+ /*
+ Flush data before changing file size or changing the last
+ write time of the file on the server. If the
+ flush returns error, store it to report later and continue.
+ BB: This should be smarter. Why bother flushing pages that
+ will be truncated anyway? Also, should we error out here if
+ the flush returns error?
+ */
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (rc != 0) {
+ cifsInode->write_behind_rc = rc;
+ rc = 0;
+ }
+ }
+
+ if (attrs->ia_valid & ATTR_SIZE) {
+ rc = cifs_set_file_size(inode, attrs, xid, full_path);
+ if (rc != 0)
+ goto out;
+ }
+
+ /* skip mode change if it's just for clearing setuid/setgid */
+ if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ attrs->ia_valid &= ~ATTR_MODE;
+
+ args = kmalloc(sizeof(*args), GFP_KERNEL);
+ if (args == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* set up the struct */
+ if (attrs->ia_valid & ATTR_MODE)
+ args->mode = attrs->ia_mode;
+ else
+ args->mode = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_UID)
+ args->uid = attrs->ia_uid;
+ else
+ args->uid = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_GID)
+ args->gid = attrs->ia_gid;
+ else
+ args->gid = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_ATIME)
+ args->atime = cifs_UnixTimeToNT(attrs->ia_atime);
+ else
+ args->atime = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_MTIME)
+ args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime);
+ else
+ args->mtime = NO_CHANGE_64;
+
+ if (attrs->ia_valid & ATTR_CTIME)
+ args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime);
+ else
+ args->ctime = NO_CHANGE_64;
+
+ args->device = 0;
+ rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path, args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (!rc)
+ rc = inode_setattr(inode, attrs);
+out:
+ kfree(args);
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+static int
+cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
+{
+ int xid;
+ struct inode *inode = direntry->d_inode;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ char *full_path = NULL;
+ int rc = -EACCES;
+ __u32 dosattr = 0;
+ __u64 mode = NO_CHANGE_64;
+
+ xid = GetXid();
+
+ cFYI(1, ("setattr on file %s attrs->iavalid 0x%x",
+ direntry->d_name.name, attrs->ia_valid));
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) {
+ /* check if we have permission to change attrs */
+ rc = inode_change_ok(inode, attrs);
+ if (rc < 0) {
+ FreeXid(xid);
+ return rc;
+ } else
+ rc = 0;
+ }
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) {
+ /*
+ Flush data before changing file size or changing the last
+ write time of the file on the server. If the
+ flush returns error, store it to report later and continue.
+ BB: This should be smarter. Why bother flushing pages that
+ will be truncated anyway? Also, should we error out here if
+ the flush returns error?
+ */
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (rc != 0) {
+ cifsInode->write_behind_rc = rc;
+ rc = 0;
+ }
+ }
+
+ if (attrs->ia_valid & ATTR_SIZE) {
+ rc = cifs_set_file_size(inode, attrs, xid, full_path);
+ if (rc != 0)
+ goto cifs_setattr_exit;
+ }
+
+ /*
+ * Without unix extensions we can't send ownership changes to the
+ * server, so silently ignore them. This is consistent with how
+ * local DOS/Windows filesystems behave (VFAT, NTFS, etc). With
+ * CIFSACL support + proper Windows to Unix idmapping, we may be
+ * able to support this in the future.
+ */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID))
+ attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
+
+ /* skip mode change if it's just for clearing setuid/setgid */
+ if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ attrs->ia_valid &= ~ATTR_MODE;
+
+ if (attrs->ia_valid & ATTR_MODE) {
+ cFYI(1, ("Mode changed to 0%o", attrs->ia_mode));
+ mode = attrs->ia_mode;
+ }
+
+ if (attrs->ia_valid & ATTR_MODE) {
+ rc = 0;
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
+ rc = mode_to_acl(inode, full_path, mode);
+ else
+#endif
+ if (((mode & S_IWUGO) == 0) &&
+ (cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
+
+ dosattr = cifsInode->cifsAttrs | ATTR_READONLY;
+
+ /* fix up mode if we're not using dynperm */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ attrs->ia_mode = inode->i_mode & ~S_IWUGO;
+ } else if ((mode & S_IWUGO) &&
+ (cifsInode->cifsAttrs & ATTR_READONLY)) {
+
+ dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY;
+ /* Attributes of 0 are ignored */
+ if (dosattr == 0)
+ dosattr |= ATTR_NORMAL;
+
+ /* reset local inode permissions to normal */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
+ attrs->ia_mode &= ~(S_IALLUGO);
+ if (S_ISDIR(inode->i_mode))
+ attrs->ia_mode |=
+ cifs_sb->mnt_dir_mode;
+ else
+ attrs->ia_mode |=
+ cifs_sb->mnt_file_mode;
+ }
+ } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
+ /* ignore mode change - ATTR_READONLY hasn't changed */
+ attrs->ia_valid &= ~ATTR_MODE;
+ }
+ }
+
+ if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) ||
+ ((attrs->ia_valid & ATTR_MODE) && dosattr)) {
+ rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
+ /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */
+
+ /* Even if error on time set, no sense failing the call if
+ the server would set the time to a reasonable value anyway,
+ and this check ensures that we are not being called from
+ sys_utimes in which case we ought to fail the call back to
+ the user when the server rejects the call */
+ if ((rc) && (attrs->ia_valid &
+ (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
+ rc = 0;
+ }
+
+ /* do not need local check to inode_check_ok since the server does
+ that */
+ if (!rc)
+ rc = inode_setattr(inode, attrs);
+cifs_setattr_exit:
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+int
+cifs_setattr(struct dentry *direntry, struct iattr *attrs)
+{
+ struct inode *inode = direntry->d_inode;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsTconInfo *pTcon = cifs_sb->tcon;
+
+ if (pTcon->unix_ext)
+ return cifs_setattr_unix(direntry, attrs);
+
+ return cifs_setattr_nounix(direntry, attrs);
+
+ /* BB: add cifs_setattr_legacy for really old servers */
+}
+
+#if 0
+void cifs_delete_inode(struct inode *inode)
+{
+ cFYI(1, ("In cifs_delete_inode, inode = 0x%p", inode));
+ /* may have to add back in if and when safe distributed caching of
+ directories added e.g. via FindNotify */
+}
+#endif
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
new file mode 100644
index 0000000..0088a5b
--- /dev/null
+++ b/fs/cifs/ioctl.c
@@ -0,0 +1,110 @@
+/*
+ * fs/cifs/ioctl.c
+ *
+ * vfs operations that deal with io control
+ *
+ * Copyright (C) International Business Machines Corp., 2005,2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+
+#define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
+
+long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
+{
+ struct inode *inode = filep->f_dentry->d_inode;
+ int rc = -ENOTTY; /* strange error - but the precedent */
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+#ifdef CONFIG_CIFS_POSIX
+ __u64 ExtAttrBits = 0;
+ __u64 ExtAttrMask = 0;
+ __u64 caps;
+ struct cifsTconInfo *tcon;
+ struct cifsFileInfo *pSMBFile =
+ (struct cifsFileInfo *)filep->private_data;
+#endif /* CONFIG_CIFS_POSIX */
+
+ xid = GetXid();
+
+ cFYI(1, ("ioctl file %p cmd %u arg %lu", filep, command, arg));
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+
+#ifdef CONFIG_CIFS_POSIX
+ tcon = cifs_sb->tcon;
+ if (tcon)
+ caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ else {
+ rc = -EIO;
+ FreeXid(xid);
+ return -EIO;
+ }
+#endif /* CONFIG_CIFS_POSIX */
+
+ switch (command) {
+ case CIFS_IOC_CHECKUMOUNT:
+ cFYI(1, ("User unmount attempted"));
+ if (cifs_sb->mnt_uid == current->uid)
+ rc = 0;
+ else {
+ rc = -EACCES;
+ cFYI(1, ("uids do not match"));
+ }
+ break;
+#ifdef CONFIG_CIFS_POSIX
+ case FS_IOC_GETFLAGS:
+ if (CIFS_UNIX_EXTATTR_CAP & caps) {
+ if (pSMBFile == NULL)
+ break;
+ rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid,
+ &ExtAttrBits, &ExtAttrMask);
+ if (rc == 0)
+ rc = put_user(ExtAttrBits &
+ FS_FL_USER_VISIBLE,
+ (int __user *)arg);
+ }
+ break;
+
+ case FS_IOC_SETFLAGS:
+ if (CIFS_UNIX_EXTATTR_CAP & caps) {
+ if (get_user(ExtAttrBits, (int __user *)arg)) {
+ rc = -EFAULT;
+ break;
+ }
+ if (pSMBFile == NULL)
+ break;
+ /* rc= CIFSGetExtAttr(xid,tcon,pSMBFile->netfid,
+ extAttrBits, &ExtAttrMask);*/
+ }
+ cFYI(1, ("set flags not implemented yet"));
+ break;
+#endif /* CONFIG_CIFS_POSIX */
+ default:
+ cFYI(1, ("unsupported ioctl"));
+ break;
+ }
+
+ FreeXid(xid);
+ return rc;
+}
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
new file mode 100644
index 0000000..63f6440
--- /dev/null
+++ b/fs/cifs/link.c
@@ -0,0 +1,324 @@
+/*
+ * fs/cifs/link.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/namei.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+
+int
+cifs_hardlink(struct dentry *old_file, struct inode *inode,
+ struct dentry *direntry)
+{
+ int rc = -EACCES;
+ int xid;
+ char *fromName = NULL;
+ char *toName = NULL;
+ struct cifs_sb_info *cifs_sb_target;
+ struct cifsTconInfo *pTcon;
+ struct cifsInodeInfo *cifsInode;
+
+ xid = GetXid();
+
+ cifs_sb_target = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb_target->tcon;
+
+/* No need to check for cross device links since server will do that
+ BB note DFS case in future though (when we may have to check) */
+
+ fromName = build_path_from_dentry(old_file);
+ toName = build_path_from_dentry(direntry);
+ if ((fromName == NULL) || (toName == NULL)) {
+ rc = -ENOMEM;
+ goto cifs_hl_exit;
+ }
+
+/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
+ if (pTcon->unix_ext)
+ rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
+ cifs_sb_target->local_nls,
+ cifs_sb_target->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ else {
+ rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
+ cifs_sb_target->local_nls,
+ cifs_sb_target->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if ((rc == -EIO) || (rc == -EINVAL))
+ rc = -EOPNOTSUPP;
+ }
+
+ d_drop(direntry); /* force new lookup from server of target */
+
+ /* if source file is cached (oplocked) revalidate will not go to server
+ until the file is closed or oplock broken so update nlinks locally */
+ if (old_file->d_inode) {
+ cifsInode = CIFS_I(old_file->d_inode);
+ if (rc == 0) {
+ old_file->d_inode->i_nlink++;
+/* BB should we make this contingent on superblock flag NOATIME? */
+/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
+ /* parent dir timestamps will update from srv
+ within a second, would it really be worth it
+ to set the parent dir cifs inode time to zero
+ to force revalidate (faster) for it too? */
+ }
+ /* if not oplocked will force revalidate to get info
+ on source file from srv */
+ cifsInode->time = 0;
+
+ /* Will update parent dir timestamps from srv within a second.
+ Would it really be worth it to set the parent dir (cifs
+ inode) time field to zero to force revalidate on parent
+ directory faster ie
+ CIFS_I(inode)->time = 0; */
+ }
+
+cifs_hl_exit:
+ kfree(fromName);
+ kfree(toName);
+ FreeXid(xid);
+ return rc;
+}
+
+void *
+cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
+{
+ struct inode *inode = direntry->d_inode;
+ int rc = -EACCES;
+ int xid;
+ char *full_path = NULL;
+ char *target_path = ERR_PTR(-ENOMEM);
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+
+ xid = GetXid();
+
+ full_path = build_path_from_dentry(direntry);
+
+ if (!full_path)
+ goto out_no_free;
+
+ cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode));
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+ target_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!target_path) {
+ target_path = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ /* We could change this to:
+ if (pTcon->unix_ext)
+ but there does not seem any point in refusing to
+ get symlink info if we can, even if unix extensions
+ turned off for this mount */
+
+ if (pTcon->ses->capabilities & CAP_UNIX)
+ rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
+ target_path,
+ PATH_MAX-1,
+ cifs_sb->local_nls);
+ else {
+ /* BB add read reparse point symlink code here */
+ /* rc = CIFSSMBQueryReparseLinkInfo */
+ /* BB Add code to Query ReparsePoint info */
+ /* BB Add MAC style xsymlink check here if enabled */
+ }
+
+ if (rc == 0) {
+
+/* BB Add special case check for Samba DFS symlinks */
+
+ target_path[PATH_MAX-1] = 0;
+ } else {
+ kfree(target_path);
+ target_path = ERR_PTR(rc);
+ }
+
+out:
+ kfree(full_path);
+out_no_free:
+ FreeXid(xid);
+ nd_set_link(nd, target_path);
+ return NULL; /* No cookie */
+}
+
+int
+cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
+{
+ int rc = -EOPNOTSUPP;
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ struct inode *newinode = NULL;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ cFYI(1, ("Full path: %s", full_path));
+ cFYI(1, ("symname is %s", symname));
+
+ /* BB what if DFS and this volume is on different share? BB */
+ if (pTcon->unix_ext)
+ rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
+ cifs_sb->local_nls);
+ /* else
+ rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
+ cifs_sb_target->local_nls); */
+
+ if (rc == 0) {
+ if (pTcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
+ inode->i_sb, xid);
+ else
+ rc = cifs_get_inode_info(&newinode, full_path, NULL,
+ inode->i_sb, xid, NULL);
+
+ if (rc != 0) {
+ cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d",
+ rc));
+ } else {
+ if (pTcon->nocase)
+ direntry->d_op = &cifs_ci_dentry_ops;
+ else
+ direntry->d_op = &cifs_dentry_ops;
+ d_instantiate(direntry, newinode);
+ }
+ }
+
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+int
+cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
+{
+ struct inode *inode = direntry->d_inode;
+ int rc = -EACCES;
+ int xid;
+ int oplock = 0;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ char *full_path = NULL;
+ char *tmpbuffer;
+ int len;
+ __u16 fid;
+
+ xid = GetXid();
+ cifs_sb = CIFS_SB(inode->i_sb);
+ pTcon = cifs_sb->tcon;
+
+/* BB would it be safe against deadlock to grab this sem
+ even though rename itself grabs the sem and calls lookup? */
+/* mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/
+ full_path = build_path_from_dentry(direntry);
+/* mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/
+
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+ cFYI(1,
+ ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d",
+ full_path, inode, pBuffer, buflen));
+ if (buflen > PATH_MAX)
+ len = PATH_MAX;
+ else
+ len = buflen;
+ tmpbuffer = kmalloc(len, GFP_KERNEL);
+ if (tmpbuffer == NULL) {
+ kfree(full_path);
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+
+/* BB add read reparse point symlink code and
+ Unix extensions symlink code here BB */
+/* We could disable this based on pTcon->unix_ext flag instead ... but why? */
+ if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
+ rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
+ tmpbuffer,
+ len - 1,
+ cifs_sb->local_nls);
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ cERROR(1, ("SFU style symlinks not implemented yet"));
+ /* add open and read as in fs/cifs/inode.c */
+ } else {
+ rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ,
+ OPEN_REPARSE_POINT, &fid, &oplock, NULL,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (!rc) {
+ rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path,
+ tmpbuffer,
+ len - 1,
+ fid,
+ cifs_sb->local_nls);
+ if (CIFSSMBClose(xid, pTcon, fid)) {
+ cFYI(1, ("Error closing junction point "
+ "(open for ioctl)"));
+ }
+ /* If it is a DFS junction earlier we would have gotten
+ PATH_NOT_COVERED returned from server so we do
+ not need to request the DFS info here */
+ }
+ }
+ /* BB Anything else to do to handle recursive links? */
+ /* BB Should we be using page ops here? */
+
+ /* BB null terminate returned string in pBuffer? BB */
+ if (rc == 0) {
+ rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer);
+ cFYI(1,
+ ("vfs_readlink called from cifs_readlink returned %d",
+ rc));
+ }
+
+ kfree(tmpbuffer);
+ kfree(full_path);
+ FreeXid(xid);
+ return rc;
+}
+
+void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
+{
+ char *p = nd_get_link(nd);
+ if (!IS_ERR(p))
+ kfree(p);
+}
diff --git a/fs/cifs/md4.c b/fs/cifs/md4.c
new file mode 100644
index 0000000..a725c26
--- /dev/null
+++ b/fs/cifs/md4.c
@@ -0,0 +1,205 @@
+/*
+ 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
new file mode 100644
index 0000000..462bbfe
--- /dev/null
+++ b/fs/cifs/md5.c
@@ -0,0 +1,366 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code slightly modified to fit into Samba by
+ abartlet@samba.org Jun 2001
+ and to fit the cifs vfs by
+ Steve French sfrench@us.ibm.com */
+
+#include <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
+MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ register __u32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((__u32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memmove(p, buf, len);
+ return;
+ }
+ memmove(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (__u32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memmove(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (__u32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned int count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (__u32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((__u32 *) ctx->in)[14] = ctx->bits[0];
+ ((__u32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (__u32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memmove(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ (w += f(x, y, z) + data, w = w<<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. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(__u32 buf[4], __u32 const in[16])
+{
+ register __u32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#if 0 /* currently unused */
+/***********************************************************************
+ the rfc 2104 version of hmac_md5 initialisation.
+***********************************************************************/
+static void
+hmac_md5_init_rfc2104(unsigned char *key, int key_len,
+ struct HMACMD5Context *ctx)
+{
+ int i;
+
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64) {
+ unsigned char tk[16];
+ struct MD5Context tctx;
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /* start out by storing key in pads */
+ memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad));
+ memset(ctx->k_opad, 0, sizeof(ctx->k_opad));
+ memcpy(ctx->k_ipad, key, key_len);
+ memcpy(ctx->k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < 64; i++) {
+ ctx->k_ipad[i] ^= 0x36;
+ ctx->k_opad[i] ^= 0x5c;
+ }
+
+ MD5Init(&ctx->ctx);
+ MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+}
+#endif
+
+/***********************************************************************
+ the microsoft version of hmac_md5 initialisation.
+***********************************************************************/
+void
+hmac_md5_init_limK_to_64(const unsigned char *key, int key_len,
+ struct HMACMD5Context *ctx)
+{
+ int i;
+
+ /* if key is longer than 64 bytes truncate it */
+ if (key_len > 64)
+ key_len = 64;
+
+ /* start out by storing key in pads */
+ memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad));
+ memset(ctx->k_opad, 0, sizeof(ctx->k_opad));
+ memcpy(ctx->k_ipad, key, key_len);
+ memcpy(ctx->k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < 64; i++) {
+ ctx->k_ipad[i] ^= 0x36;
+ ctx->k_opad[i] ^= 0x5c;
+ }
+
+ MD5Init(&ctx->ctx);
+ MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+}
+
+/***********************************************************************
+ update hmac_md5 "inner" buffer
+***********************************************************************/
+void
+hmac_md5_update(const unsigned char *text, int text_len,
+ struct HMACMD5Context *ctx)
+{
+ MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */
+}
+
+/***********************************************************************
+ finish off hmac_md5 "inner" buffer and generate outer one.
+***********************************************************************/
+void
+hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx)
+{
+ struct MD5Context ctx_o;
+
+ MD5Final(digest, &ctx->ctx);
+
+ MD5Init(&ctx_o);
+ MD5Update(&ctx_o, ctx->k_opad, 64);
+ MD5Update(&ctx_o, digest, 16);
+ MD5Final(digest, &ctx_o);
+}
+
+/***********************************************************
+ single function to calculate an HMAC MD5 digest from data.
+ use the microsoft hmacmd5 init method because the key is 16 bytes.
+************************************************************/
+#if 0 /* currently unused */
+static void
+hmac_md5(unsigned char key[16], unsigned char *data, int data_len,
+ unsigned char *digest)
+{
+ struct HMACMD5Context ctx;
+ hmac_md5_init_limK_to_64(key, 16, &ctx);
+ if (data_len != 0)
+ hmac_md5_update(data, data_len, &ctx);
+
+ hmac_md5_final(digest, &ctx);
+}
+#endif
diff --git a/fs/cifs/md5.h b/fs/cifs/md5.h
new file mode 100644
index 0000000..f7d4f41
--- /dev/null
+++ b/fs/cifs/md5.h
@@ -0,0 +1,38 @@
+#ifndef MD5_H
+#define MD5_H
+#ifndef HEADER_MD5_H
+/* Try to avoid clashes with OpenSSL */
+#define HEADER_MD5_H
+#endif
+
+struct MD5Context {
+ __u32 buf[4];
+ __u32 bits[2];
+ unsigned char in[64];
+};
+#endif /* !MD5_H */
+
+#ifndef _HMAC_MD5_H
+struct HMACMD5Context {
+ struct MD5Context ctx;
+ unsigned char k_ipad[65];
+ unsigned char k_opad[65];
+};
+#endif /* _HMAC_MD5_H */
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+/* The following definitions come from lib/hmacmd5.c */
+
+/* void hmac_md5_init_rfc2104(unsigned char *key, int key_len,
+ struct HMACMD5Context *ctx);*/
+void hmac_md5_init_limK_to_64(const unsigned char *key, int key_len,
+ struct HMACMD5Context *ctx);
+void hmac_md5_update(const unsigned char *text, int text_len,
+ struct HMACMD5Context *ctx);
+void hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx);
+/* void hmac_md5(unsigned char key[16], unsigned char *data, int data_len,
+ unsigned char *digest);*/
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
new file mode 100644
index 0000000..9ee3f68
--- /dev/null
+++ b/fs/cifs/misc.c
@@ -0,0 +1,771 @@
+/*
+ * fs/cifs/misc.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/mempool.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "smberr.h"
+#include "nterr.h"
+#include "cifs_unicode.h"
+
+extern mempool_t *cifs_sm_req_poolp;
+extern mempool_t *cifs_req_poolp;
+extern struct task_struct *oplockThread;
+
+/* The xid serves as a useful identifier for each incoming vfs request,
+ in a similar way to the mid which is useful to track each sent smb,
+ and CurrentXid can also provide a running counter (although it
+ will eventually wrap past zero) of the total vfs operations handled
+ since the cifs fs was mounted */
+
+unsigned int
+_GetXid(void)
+{
+ unsigned int xid;
+
+ spin_lock(&GlobalMid_Lock);
+ GlobalTotalActiveXid++;
+
+ /* keep high water mark for number of simultaneous ops in filesystem */
+ if (GlobalTotalActiveXid > GlobalMaxActiveXid)
+ GlobalMaxActiveXid = GlobalTotalActiveXid;
+ if (GlobalTotalActiveXid > 65000)
+ cFYI(1, ("warning: more than 65000 requests active"));
+ xid = GlobalCurrentXid++;
+ spin_unlock(&GlobalMid_Lock);
+ return xid;
+}
+
+void
+_FreeXid(unsigned int xid)
+{
+ spin_lock(&GlobalMid_Lock);
+ /* if (GlobalTotalActiveXid == 0)
+ BUG(); */
+ GlobalTotalActiveXid--;
+ spin_unlock(&GlobalMid_Lock);
+}
+
+struct cifsSesInfo *
+sesInfoAlloc(void)
+{
+ struct cifsSesInfo *ret_buf;
+
+ ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
+ if (ret_buf) {
+ atomic_inc(&sesInfoAllocCount);
+ ret_buf->status = CifsNew;
+ ++ret_buf->ses_count;
+ INIT_LIST_HEAD(&ret_buf->smb_ses_list);
+ INIT_LIST_HEAD(&ret_buf->tcon_list);
+ init_MUTEX(&ret_buf->sesSem);
+ }
+ return ret_buf;
+}
+
+void
+sesInfoFree(struct cifsSesInfo *buf_to_free)
+{
+ if (buf_to_free == NULL) {
+ cFYI(1, ("Null buffer passed to sesInfoFree"));
+ return;
+ }
+
+ atomic_dec(&sesInfoAllocCount);
+ kfree(buf_to_free->serverOS);
+ kfree(buf_to_free->serverDomain);
+ kfree(buf_to_free->serverNOS);
+ kfree(buf_to_free->password);
+ kfree(buf_to_free->domainName);
+ kfree(buf_to_free);
+}
+
+struct cifsTconInfo *
+tconInfoAlloc(void)
+{
+ struct cifsTconInfo *ret_buf;
+ ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL);
+ if (ret_buf) {
+ atomic_inc(&tconInfoAllocCount);
+ ret_buf->tidStatus = CifsNew;
+ ++ret_buf->tc_count;
+ INIT_LIST_HEAD(&ret_buf->openFileList);
+ INIT_LIST_HEAD(&ret_buf->tcon_list);
+#ifdef CONFIG_CIFS_STATS
+ spin_lock_init(&ret_buf->stat_lock);
+#endif
+ }
+ return ret_buf;
+}
+
+void
+tconInfoFree(struct cifsTconInfo *buf_to_free)
+{
+ if (buf_to_free == NULL) {
+ cFYI(1, ("Null buffer passed to tconInfoFree"));
+ return;
+ }
+ atomic_dec(&tconInfoAllocCount);
+ kfree(buf_to_free->nativeFileSystem);
+ kfree(buf_to_free);
+}
+
+struct smb_hdr *
+cifs_buf_get(void)
+{
+ struct smb_hdr *ret_buf = NULL;
+
+/* We could use negotiated size instead of max_msgsize -
+ but it may be more efficient to always alloc same size
+ albeit slightly larger than necessary and maxbuffersize
+ defaults to this and can not be bigger */
+ ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);
+
+ /* clear the first few header bytes */
+ /* for most paths, more is cleared in header_assemble */
+ if (ret_buf) {
+ memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
+ atomic_inc(&bufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&totBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
+ }
+
+ return ret_buf;
+}
+
+void
+cifs_buf_release(void *buf_to_free)
+{
+ if (buf_to_free == NULL) {
+ /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/
+ return;
+ }
+ mempool_free(buf_to_free, cifs_req_poolp);
+
+ atomic_dec(&bufAllocCount);
+ return;
+}
+
+struct smb_hdr *
+cifs_small_buf_get(void)
+{
+ struct smb_hdr *ret_buf = NULL;
+
+/* We could use negotiated size instead of max_msgsize -
+ but it may be more efficient to always alloc same size
+ albeit slightly larger than necessary and maxbuffersize
+ defaults to this and can not be bigger */
+ ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS);
+ if (ret_buf) {
+ /* No need to clear memory here, cleared in header assemble */
+ /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
+ atomic_inc(&smBufAllocCount);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&totSmBufAllocCount);
+#endif /* CONFIG_CIFS_STATS2 */
+
+ }
+ return ret_buf;
+}
+
+void
+cifs_small_buf_release(void *buf_to_free)
+{
+
+ if (buf_to_free == NULL) {
+ cFYI(1, ("Null buffer passed to cifs_small_buf_release"));
+ return;
+ }
+ mempool_free(buf_to_free, cifs_sm_req_poolp);
+
+ atomic_dec(&smBufAllocCount);
+ return;
+}
+
+/*
+ Find a free multiplex id (SMB mid). Otherwise there could be
+ mid collisions which might cause problems, demultiplexing the
+ wrong response to this request. Multiplex ids could collide if
+ one of a series requests takes much longer than the others, or
+ if a very large number of long lived requests (byte range
+ locks or FindNotify requests) are pending. No more than
+ 64K-1 requests can be outstanding at one time. If no
+ mids are available, return zero. A future optimization
+ could make the combination of mids and uid the key we use
+ to demultiplex on (rather than mid alone).
+ In addition to the above check, the cifs demultiplex
+ code already used the command code as a secondary
+ check of the frame and if signing is negotiated the
+ response would be discarded if the mid were the same
+ but the signature was wrong. Since the mid is not put in the
+ pending queue until later (when it is about to be dispatched)
+ we do have to limit the number of outstanding requests
+ to somewhat less than 64K-1 although it is hard to imagine
+ so many threads being in the vfs at one time.
+*/
+__u16 GetNextMid(struct TCP_Server_Info *server)
+{
+ __u16 mid = 0;
+ __u16 last_mid;
+ int collision;
+
+ if (server == NULL)
+ return mid;
+
+ spin_lock(&GlobalMid_Lock);
+ last_mid = server->CurrentMid; /* we do not want to loop forever */
+ server->CurrentMid++;
+ /* This nested loop looks more expensive than it is.
+ In practice the list of pending requests is short,
+ fewer than 50, and the mids are likely to be unique
+ on the first pass through the loop unless some request
+ takes longer than the 64 thousand requests before it
+ (and it would also have to have been a request that
+ did not time out) */
+ while (server->CurrentMid != last_mid) {
+ struct list_head *tmp;
+ struct mid_q_entry *mid_entry;
+
+ collision = 0;
+ if (server->CurrentMid == 0)
+ server->CurrentMid++;
+
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+
+ if ((mid_entry->mid == server->CurrentMid) &&
+ (mid_entry->midState == MID_REQUEST_SUBMITTED)) {
+ /* This mid is in use, try a different one */
+ collision = 1;
+ break;
+ }
+ }
+ if (collision == 0) {
+ mid = server->CurrentMid;
+ break;
+ }
+ server->CurrentMid++;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+}
+
+/* NB: MID can not be set if treeCon not passed in, in that
+ case it is responsbility of caller to set the mid */
+void
+header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
+ const struct cifsTconInfo *treeCon, int word_count
+ /* length of fixed section (word count) in two byte units */)
+{
+ struct list_head *temp_item;
+ struct cifsSesInfo *ses;
+ char *temp = (char *) buffer;
+
+ memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
+
+ buffer->smb_buf_length =
+ (2 * word_count) + sizeof(struct smb_hdr) -
+ 4 /* RFC 1001 length field does not count */ +
+ 2 /* for bcc field itself */ ;
+ /* Note that this is the only network field that has to be converted
+ to big endian and it is done just before we send it */
+
+ buffer->Protocol[0] = 0xFF;
+ buffer->Protocol[1] = 'S';
+ buffer->Protocol[2] = 'M';
+ buffer->Protocol[3] = 'B';
+ buffer->Command = smb_command;
+ buffer->Flags = 0x00; /* case sensitive */
+ buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
+ buffer->Pid = cpu_to_le16((__u16)current->tgid);
+ buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
+ if (treeCon) {
+ buffer->Tid = treeCon->tid;
+ if (treeCon->ses) {
+ if (treeCon->ses->capabilities & CAP_UNICODE)
+ buffer->Flags2 |= SMBFLG2_UNICODE;
+ if (treeCon->ses->capabilities & CAP_STATUS32)
+ buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+
+ /* Uid is not converted */
+ buffer->Uid = treeCon->ses->Suid;
+ buffer->Mid = GetNextMid(treeCon->ses->server);
+ if (multiuser_mount != 0) {
+ /* For the multiuser case, there are few obvious technically */
+ /* possible mechanisms to match the local linux user (uid) */
+ /* to a valid remote smb user (smb_uid): */
+ /* 1) Query Winbind (or other local pam/nss daemon */
+ /* for userid/password/logon_domain or credential */
+ /* 2) Query Winbind for uid to sid to username mapping */
+ /* and see if we have a matching password for existing*/
+ /* session for that user perhas getting password by */
+ /* adding a new pam_cifs module that stores passwords */
+ /* so that the cifs vfs can get at that for all logged*/
+ /* on users */
+ /* 3) (Which is the mechanism we have chosen) */
+ /* Search through sessions to the same server for a */
+ /* a match on the uid that was passed in on mount */
+ /* with the current processes uid (or euid?) and use */
+ /* that smb uid. If no existing smb session for */
+ /* that uid found, use the default smb session ie */
+ /* the smb session for the volume mounted which is */
+ /* the same as would be used if the multiuser mount */
+ /* flag were disabled. */
+
+ /* BB Add support for establishing new tCon and SMB Session */
+ /* with userid/password pairs found on the smb session */
+ /* for other target tcp/ip addresses BB */
+ if (current->fsuid != treeCon->ses->linux_uid) {
+ cFYI(1, ("Multiuser mode and UID "
+ "did not match tcon uid"));
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
+ ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
+ if (ses->linux_uid == current->fsuid) {
+ if (ses->server == treeCon->ses->server) {
+ cFYI(1, ("found matching uid substitute right smb_uid"));
+ buffer->Uid = ses->Suid;
+ break;
+ } else {
+ /* BB eventually call cifs_setup_session here */
+ cFYI(1, ("local UID found but no smb sess with this server exists"));
+ }
+ }
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ }
+ }
+ }
+ if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
+ buffer->Flags2 |= SMBFLG2_DFS;
+ if (treeCon->nocase)
+ buffer->Flags |= SMBFLG_CASELESS;
+ if ((treeCon->ses) && (treeCon->ses->server))
+ if (treeCon->ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+ }
+
+/* endian conversion of flags is now done just before sending */
+ buffer->WordCount = (char) word_count;
+ return;
+}
+
+static int
+checkSMBhdr(struct smb_hdr *smb, __u16 mid)
+{
+ /* Make sure that this really is an SMB, that it is a response,
+ and that the message ids match */
+ if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) &&
+ (mid == smb->Mid)) {
+ if (smb->Flags & SMBFLG_RESPONSE)
+ return 0;
+ else {
+ /* only one valid case where server sends us request */
+ if (smb->Command == SMB_COM_LOCKING_ANDX)
+ return 0;
+ else
+ cERROR(1, ("Received Request not response"));
+ }
+ } else { /* bad signature or mid */
+ if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff))
+ cERROR(1,
+ ("Bad protocol string signature header %x",
+ *(unsigned int *) smb->Protocol));
+ if (mid != smb->Mid)
+ cERROR(1, ("Mids do not match"));
+ }
+ cERROR(1, ("bad smb detected. The Mid=%d", smb->Mid));
+ return 1;
+}
+
+int
+checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)
+{
+ __u32 len = smb->smb_buf_length;
+ __u32 clc_len; /* calculated length */
+ cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len));
+
+ if (length < 2 + sizeof(struct smb_hdr)) {
+ if ((length >= sizeof(struct smb_hdr) - 1)
+ && (smb->Status.CifsError != 0)) {
+ smb->WordCount = 0;
+ /* some error cases do not return wct and bcc */
+ return 0;
+ } else if ((length == sizeof(struct smb_hdr) + 1) &&
+ (smb->WordCount == 0)) {
+ char *tmp = (char *)smb;
+ /* Need to work around a bug in two servers here */
+ /* First, check if the part of bcc they sent was zero */
+ if (tmp[sizeof(struct smb_hdr)] == 0) {
+ /* some servers return only half of bcc
+ * on simple responses (wct, bcc both zero)
+ * in particular have seen this on
+ * ulogoffX and FindClose. This leaves
+ * one byte of bcc potentially unitialized
+ */
+ /* zero rest of bcc */
+ tmp[sizeof(struct smb_hdr)+1] = 0;
+ return 0;
+ }
+ cERROR(1, ("rcvd invalid byte count (bcc)"));
+ } else {
+ cERROR(1, ("Length less than smb header size"));
+ }
+ return 1;
+ }
+ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cERROR(1, ("smb length greater than MaxBufSize, mid=%d",
+ smb->Mid));
+ return 1;
+ }
+
+ if (checkSMBhdr(smb, mid))
+ return 1;
+ clc_len = smbCalcSize_LE(smb);
+
+ if (4 + len != length) {
+ cERROR(1, ("Length read does not match RFC1001 length %d",
+ len));
+ return 1;
+ }
+
+ if (4 + len != clc_len) {
+ /* check if bcc wrapped around for large read responses */
+ if ((len > 64 * 1024) && (len > clc_len)) {
+ /* check if lengths match mod 64K */
+ if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF))
+ return 0; /* bcc wrapped */
+ }
+ cFYI(1, ("Calculated size %d vs length %d mismatch for mid %d",
+ clc_len, 4 + len, smb->Mid));
+ /* Windows XP can return a few bytes too much, presumably
+ an illegal pad, at the end of byte range lock responses
+ so we allow for that three byte pad, as long as actual
+ received length is as long or longer than calculated length */
+ /* We have now had to extend this more, since there is a
+ case in which it needs to be bigger still to handle a
+ malformed response to transact2 findfirst from WinXP when
+ access denied is returned and thus bcc and wct are zero
+ but server says length is 0x21 bytes too long as if the server
+ forget to reset the smb rfc1001 length when it reset the
+ wct and bcc to minimum size and drop the t2 parms and data */
+ if ((4+len > clc_len) && (len <= clc_len + 512))
+ return 0;
+ else {
+ cERROR(1, ("RFC1001 size %d bigger than SMB for Mid=%d",
+ len, smb->Mid));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+bool
+is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
+{
+ struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+ struct cifsInodeInfo *pCifsInode;
+ struct cifsFileInfo *netfile;
+
+ cFYI(1, ("Checking for oplock break or dnotify response"));
+ if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
+ (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
+ struct smb_com_transaction_change_notify_rsp *pSMBr =
+ (struct smb_com_transaction_change_notify_rsp *)buf;
+ struct file_notify_information *pnotify;
+ __u32 data_offset = 0;
+ if (pSMBr->ByteCount > sizeof(struct file_notify_information)) {
+ data_offset = le32_to_cpu(pSMBr->DataOffset);
+
+ pnotify = (struct file_notify_information *)
+ ((char *)&pSMBr->hdr.Protocol + data_offset);
+ cFYI(1, ("dnotify on %s Action: 0x%x",
+ pnotify->FileName, pnotify->Action));
+ /* cifs_dump_mem("Rcvd notify Data: ",buf,
+ sizeof(struct smb_hdr)+60); */
+ return true;
+ }
+ if (pSMBr->hdr.Status.CifsError) {
+ cFYI(1, ("notify err 0x%d",
+ pSMBr->hdr.Status.CifsError));
+ return true;
+ }
+ return false;
+ }
+ if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
+ return false;
+ if (pSMB->hdr.Flags & SMBFLG_RESPONSE) {
+ /* no sense logging error on invalid handle on oplock
+ break - harmless race between close request and oplock
+ break response is expected from time to time writing out
+ large dirty files cached on the client */
+ if ((NT_STATUS_INVALID_HANDLE) ==
+ le32_to_cpu(pSMB->hdr.Status.CifsError)) {
+ cFYI(1, ("invalid handle on oplock break"));
+ return true;
+ } else if (ERRbadfid ==
+ le16_to_cpu(pSMB->hdr.Status.DosError.Error)) {
+ return true;
+ } else {
+ return false; /* on valid oplock brk we get "request" */
+ }
+ }
+ if (pSMB->hdr.WordCount != 8)
+ return false;
+
+ cFYI(1, ("oplock type 0x%d level 0x%d",
+ pSMB->LockType, pSMB->OplockLevel));
+ if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
+ return false;
+
+ /* look up tcon based on tid & uid */
+ read_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &srv->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+ list_for_each(tmp1, &ses->tcon_list) {
+ tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list);
+ if (tcon->tid != buf->Tid)
+ continue;
+
+ cifs_stats_inc(&tcon->num_oplock_brks);
+ write_lock(&GlobalSMBSeslock);
+ list_for_each(tmp2, &tcon->openFileList) {
+ netfile = list_entry(tmp2, struct cifsFileInfo,
+ tlist);
+ if (pSMB->Fid != netfile->netfid)
+ continue;
+
+ write_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, ("file id match, oplock break"));
+ pCifsInode = CIFS_I(netfile->pInode);
+ pCifsInode->clientCanCacheAll = false;
+ if (pSMB->OplockLevel == 0)
+ pCifsInode->clientCanCacheRead = false;
+ pCifsInode->oplockPending = true;
+ AllocOplockQEntry(netfile->pInode,
+ netfile->netfid, tcon);
+ cFYI(1, ("about to wake up oplock thread"));
+ if (oplockThread)
+ wake_up_process(oplockThread);
+
+ return true;
+ }
+ write_unlock(&GlobalSMBSeslock);
+ read_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, ("No matching file for oplock break"));
+ return true;
+ }
+ }
+ read_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, ("Can not process oplock break for non-existent connection"));
+ return true;
+}
+
+void
+dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
+{
+ int i, j;
+ char debug_line[17];
+ unsigned char *buffer;
+
+ if (traceSMB == 0)
+ return;
+
+ buffer = (unsigned char *) smb_buf;
+ for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
+ if (i % 8 == 0) {
+ /* have reached the beginning of line */
+ printk(KERN_DEBUG "| ");
+ j = 0;
+ }
+ printk("%0#4x ", buffer[i]);
+ debug_line[2 * j] = ' ';
+ if (isprint(buffer[i]))
+ debug_line[1 + (2 * j)] = buffer[i];
+ else
+ debug_line[1 + (2 * j)] = '_';
+
+ if (i % 8 == 7) {
+ /* reached end of line, time to print ascii */
+ debug_line[16] = 0;
+ printk(" | %s\n", debug_line);
+ }
+ }
+ for (; j < 8; j++) {
+ printk(" ");
+ debug_line[2 * j] = ' ';
+ debug_line[1 + (2 * j)] = ' ';
+ }
+ printk(" | %s\n", debug_line);
+ return;
+}
+
+/* Windows maps these to the user defined 16 bit Unicode range since they are
+ reserved symbols (along with \ and /), otherwise illegal to store
+ in filenames in NTFS */
+#define UNI_ASTERIK (__u16) ('*' + 0xF000)
+#define UNI_QUESTION (__u16) ('?' + 0xF000)
+#define UNI_COLON (__u16) (':' + 0xF000)
+#define UNI_GRTRTHAN (__u16) ('>' + 0xF000)
+#define UNI_LESSTHAN (__u16) ('<' + 0xF000)
+#define UNI_PIPE (__u16) ('|' + 0xF000)
+#define UNI_SLASH (__u16) ('\\' + 0xF000)
+
+/* Convert 16 bit Unicode pathname from wire format to string in current code
+ page. Conversion may involve remapping up the seven characters that are
+ only legal in POSIX-like OS (if they are present in the string). Path
+ names are little endian 16 bit Unicode on the wire */
+int
+cifs_convertUCSpath(char *target, const __le16 *source, int maxlen,
+ const struct nls_table *cp)
+{
+ int i, j, len;
+ __u16 src_char;
+
+ for (i = 0, j = 0; i < maxlen; i++) {
+ src_char = le16_to_cpu(source[i]);
+ switch (src_char) {
+ case 0:
+ goto cUCS_out; /* BB check this BB */
+ case UNI_COLON:
+ target[j] = ':';
+ break;
+ case UNI_ASTERIK:
+ target[j] = '*';
+ break;
+ case UNI_QUESTION:
+ target[j] = '?';
+ break;
+ /* BB We can not handle remapping slash until
+ all the calls to build_path_from_dentry
+ are modified, as they use slash as separator BB */
+ /* case UNI_SLASH:
+ target[j] = '\\';
+ break;*/
+ case UNI_PIPE:
+ target[j] = '|';
+ break;
+ case UNI_GRTRTHAN:
+ target[j] = '>';
+ break;
+ case UNI_LESSTHAN:
+ target[j] = '<';
+ break;
+ default:
+ len = cp->uni2char(src_char, &target[j],
+ NLS_MAX_CHARSET_SIZE);
+ if (len > 0) {
+ j += len;
+ continue;
+ } else {
+ target[j] = '?';
+ }
+ }
+ j++;
+ /* make sure we do not overrun callers allocated temp buffer */
+ if (j >= (2 * NAME_MAX))
+ break;
+ }
+cUCS_out:
+ target[j] = 0;
+ return j;
+}
+
+/* Convert 16 bit Unicode pathname to wire format from string in current code
+ page. Conversion may involve remapping up the seven characters that are
+ only legal in POSIX-like OS (if they are present in the string). Path
+ names are little endian 16 bit Unicode on the wire */
+int
+cifsConvertToUCS(__le16 *target, const char *source, int maxlen,
+ const struct nls_table *cp, int mapChars)
+{
+ int i, j, charlen;
+ int len_remaining = maxlen;
+ char src_char;
+ __u16 temp;
+
+ if (!mapChars)
+ return cifs_strtoUCS(target, source, PATH_MAX, cp);
+
+ for (i = 0, j = 0; i < maxlen; j++) {
+ src_char = source[i];
+ switch (src_char) {
+ case 0:
+ target[j] = 0;
+ goto ctoUCS_out;
+ case ':':
+ target[j] = cpu_to_le16(UNI_COLON);
+ break;
+ case '*':
+ target[j] = cpu_to_le16(UNI_ASTERIK);
+ break;
+ case '?':
+ target[j] = cpu_to_le16(UNI_QUESTION);
+ break;
+ case '<':
+ target[j] = cpu_to_le16(UNI_LESSTHAN);
+ break;
+ case '>':
+ target[j] = cpu_to_le16(UNI_GRTRTHAN);
+ break;
+ case '|':
+ target[j] = cpu_to_le16(UNI_PIPE);
+ break;
+ /* BB We can not handle remapping slash until
+ all the calls to build_path_from_dentry
+ are modified, as they use slash as separator BB */
+ /* case '\\':
+ target[j] = cpu_to_le16(UNI_SLASH);
+ break;*/
+ default:
+ charlen = cp->char2uni(source+i,
+ len_remaining, &temp);
+ /* if no match, use question mark, which
+ at least in some cases servers as wild card */
+ if (charlen < 1) {
+ target[j] = cpu_to_le16(0x003f);
+ charlen = 1;
+ } else
+ target[j] = cpu_to_le16(temp);
+ len_remaining -= charlen;
+ /* character may take more than one byte in the
+ the source string, but will take exactly two
+ bytes in the target string */
+ i += charlen;
+ continue;
+ }
+ i++; /* move to next char in source string */
+ len_remaining--;
+ }
+
+ctoUCS_out:
+ return i;
+}
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
new file mode 100644
index 0000000..8703d68
--- /dev/null
+++ b/fs/cifs/netmisc.c
@@ -0,0 +1,940 @@
+/*
+ * fs/cifs/netmisc.c
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * Error mapping routines from Samba libsmb/errormap.c
+ * Copyright (C) Andrew Tridgell 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/net.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <asm/div64.h>
+#include <asm/byteorder.h>
+#include <linux/inet.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "smberr.h"
+#include "cifs_debug.h"
+#include "nterr.h"
+
+struct smb_to_posix_error {
+ __u16 smb_err;
+ int posix_code;
+};
+
+static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
+ {ERRbadfunc, -EINVAL},
+ {ERRbadfile, -ENOENT},
+ {ERRbadpath, -ENOTDIR},
+ {ERRnofids, -EMFILE},
+ {ERRnoaccess, -EACCES},
+ {ERRbadfid, -EBADF},
+ {ERRbadmcb, -EIO},
+ {ERRnomem, -ENOMEM},
+ {ERRbadmem, -EFAULT},
+ {ERRbadenv, -EFAULT},
+ {ERRbadformat, -EINVAL},
+ {ERRbadaccess, -EACCES},
+ {ERRbaddata, -EIO},
+ {ERRbaddrive, -ENXIO},
+ {ERRremcd, -EACCES},
+ {ERRdiffdevice, -EXDEV},
+ {ERRnofiles, -ENOENT},
+ {ERRbadshare, -ETXTBSY},
+ {ERRlock, -EACCES},
+ {ERRunsup, -EINVAL},
+ {ERRnosuchshare, -ENXIO},
+ {ERRfilexists, -EEXIST},
+ {ERRinvparm, -EINVAL},
+ {ERRdiskfull, -ENOSPC},
+ {ERRinvname, -ENOENT},
+ {ERRinvlevel, -EOPNOTSUPP},
+ {ERRdirnotempty, -ENOTEMPTY},
+ {ERRnotlocked, -ENOLCK},
+ {ERRcancelviolation, -ENOLCK},
+ {ERRalreadyexists, -EEXIST},
+ {ERRmoredata, -EOVERFLOW},
+ {ERReasnotsupported, -EOPNOTSUPP},
+ {ErrQuota, -EDQUOT},
+ {ErrNotALink, -ENOLINK},
+ {ERRnetlogonNotStarted, -ENOPROTOOPT},
+ {ErrTooManyLinks, -EMLINK},
+ {0, 0}
+};
+
+static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
+ {ERRerror, -EIO},
+ {ERRbadpw, -EACCES}, /* was EPERM */
+ {ERRbadtype, -EREMOTE},
+ {ERRaccess, -EACCES},
+ {ERRinvtid, -ENXIO},
+ {ERRinvnetname, -ENXIO},
+ {ERRinvdevice, -ENXIO},
+ {ERRqfull, -ENOSPC},
+ {ERRqtoobig, -ENOSPC},
+ {ERRqeof, -EIO},
+ {ERRinvpfid, -EBADF},
+ {ERRsmbcmd, -EBADRQC},
+ {ERRsrverror, -EIO},
+ {ERRbadBID, -EIO},
+ {ERRfilespecs, -EINVAL},
+ {ERRbadLink, -EIO},
+ {ERRbadpermits, -EINVAL},
+ {ERRbadPID, -ESRCH},
+ {ERRsetattrmode, -EINVAL},
+ {ERRpaused, -EHOSTDOWN},
+ {ERRmsgoff, -EHOSTDOWN},
+ {ERRnoroom, -ENOSPC},
+ {ERRrmuns, -EUSERS},
+ {ERRtimeout, -ETIME},
+ {ERRnoresource, -ENOBUFS},
+ {ERRtoomanyuids, -EUSERS},
+ {ERRbaduid, -EACCES},
+ {ERRusempx, -EIO},
+ {ERRusestd, -EIO},
+ {ERR_NOTIFY_ENUM_DIR, -ENOBUFS},
+ {ERRnoSuchUser, -EACCES},
+/* {ERRaccountexpired, -EACCES},
+ {ERRbadclient, -EACCES},
+ {ERRbadLogonTime, -EACCES},
+ {ERRpasswordExpired, -EACCES},*/
+ {ERRaccountexpired, -EKEYEXPIRED},
+ {ERRbadclient, -EACCES},
+ {ERRbadLogonTime, -EACCES},
+ {ERRpasswordExpired, -EKEYEXPIRED},
+
+ {ERRnosupport, -EINVAL},
+ {0, 0}
+};
+
+static const struct smb_to_posix_error mapping_table_ERRHRD[] = {
+ {0, 0}
+};
+
+/* Convert string containing dotted ip address to binary form */
+/* returns 0 if invalid address */
+
+int
+cifs_inet_pton(const int address_family, const char *cp, void *dst)
+{
+ int ret = 0;
+
+ /* calculate length by finding first slash or NULL */
+ if (address_family == AF_INET)
+ ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL);
+ else if (address_family == AF_INET6)
+ ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL);
+
+ cFYI(DBG2, ("address conversion returned %d for %s", ret, cp));
+ if (ret > 0)
+ ret = 1;
+ return ret;
+}
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+/* NT status -> dos error map */
+static const struct {
+ __u8 dos_class;
+ __u16 dos_code;
+ __u32 ntstatus;
+} ntstatus_to_dos_map[] = {
+ {
+ ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, {
+ ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, {
+ ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, {
+ ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, {
+ ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, {
+ ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, {
+ ERRDOS, 87, NT_STATUS_INVALID_CID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, {
+ ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, {
+ ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, {
+ ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, {
+ ERRDOS, 38, NT_STATUS_END_OF_FILE}, {
+ ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, {
+ ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, {
+ ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
+ during the session setup } */
+ {
+ ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, {
+ ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, {
+ ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, {
+ ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, {
+ ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, {
+ ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, {
+ ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, {
+ ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, {
+ ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, {
+ ERRDOS, 158, NT_STATUS_NOT_LOCKED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, {
+ ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, {
+ ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, {
+ /* mapping changed since shell does lookup on * expects FileNotFound */
+ ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, {
+ ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, {
+ ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, {
+ ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, {
+ ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, {
+ ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, {
+ ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, {
+ ERRDOS, 23, NT_STATUS_DATA_ERROR}, {
+ ERRDOS, 23, NT_STATUS_CRC_ERROR}, {
+ ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, {
+ ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, {
+ ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, {
+ ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, {
+ ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, {
+ ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, {
+ ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, {
+ ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, {
+ ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, {
+ ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, {
+ ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, {
+ ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, {
+ ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, {
+ ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, {
+ ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, {
+ ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, {
+ ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, {
+ ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, {
+ ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
+ during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */
+ ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
+ during the session setup } */
+ {
+ ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, {
+ ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, {
+ ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, {
+ ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, {
+ ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, {
+ ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, {
+ ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, {
+ ERRDOS, 112, NT_STATUS_DISK_FULL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, {
+ ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, {
+ ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, {
+ ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, {
+ ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, {
+ ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, {
+ ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, {
+ ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_INSUFFICIENT_RESOURCES to
+ NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
+ {
+ ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, {
+ ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
+ ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, {
+ ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, {
+ ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, {
+ ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, {
+ ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, {
+ ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, {
+ ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, {
+ ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, {
+ ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, {
+ ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, {
+ ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, {
+ ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, {
+ ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, {
+ ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, {
+ ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, {
+ ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, {
+ ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, {
+ ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, {
+ ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, {
+ ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, {
+ ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, {
+ ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, {
+ ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, {
+ ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, {
+ ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, {
+ ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, {
+ ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, {
+ ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, {
+ ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, {
+ ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, {
+ ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, {
+ ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, {
+ ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, {
+ ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, {
+ ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, {
+ ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, {
+ ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, {
+ ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, {
+ ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, {
+ ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, {
+ ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, {
+ ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, {
+ ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, {
+ ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, {
+ ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, {
+ ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, {
+ ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, {
+ ERRDOS, 203, 0xc0000100}, {
+ ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, {
+ ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, {
+ ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, {
+ ERRDOS, 2401, NT_STATUS_FILES_OPEN}, {
+ ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, {
+ ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, {
+ ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, {
+ ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, {
+ ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, {
+ ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, {
+ ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, {
+ ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, {
+ ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, {
+ ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, {
+ ERRDOS, 59, NT_STATUS_LINK_FAILED}, {
+ ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, {
+ ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, {
+ ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, {
+ ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, {
+ ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, {
+ ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, {
+ ERRHRD, ERRgeneral, 0xc000016e}, {
+ ERRHRD, ERRgeneral, 0xc000016f}, {
+ ERRHRD, ERRgeneral, 0xc0000170}, {
+ ERRHRD, ERRgeneral, 0xc0000171}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, {
+ ERRHRD, ERRgeneral, 0xc0000179}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, {
+ ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, {
+ ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, {
+ ERRDOS, 19, NT_STATUS_TOO_LATE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_TRUST_SAM_ACCOUNT to
+ NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, {
+ ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, {
+ ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
+ during the session setup } */
+ {
+ ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, {
+ ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, {
+ ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, {
+ ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, {
+ ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, {
+ ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, {
+ ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, {
+ ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, {
+ ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, {
+ ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, {
+ ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, {
+ ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, {
+ ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_RETRY}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, {
+ ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, {
+ ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, {
+ ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, {
+ ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, {
+ ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, {
+ ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, {
+ ERRHRD, ERRgeneral, 0xc000024a}, {
+ ERRHRD, ERRgeneral, 0xc000024b}, {
+ ERRHRD, ERRgeneral, 0xc000024c}, {
+ ERRHRD, ERRgeneral, 0xc000024d}, {
+ ERRHRD, ERRgeneral, 0xc000024e}, {
+ ERRHRD, ERRgeneral, 0xc000024f}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, {
+ ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, {
+ ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, {
+ ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, {
+ ERRHRD, ERRgeneral, 0xc000025d}, {
+ ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, {
+ ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, {
+ ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, {
+ ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, {
+ ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, {
+ ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, {
+ ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, {
+ ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, {
+ ERRDOS, 21, 0xc000026e}, {
+ ERRDOS, 161, 0xc0000281}, {
+ ERRDOS, ERRnoaccess, 0xc000028a}, {
+ ERRDOS, ERRnoaccess, 0xc000028b}, {
+ ERRHRD, ERRgeneral, 0xc000028c}, {
+ ERRDOS, ERRnoaccess, 0xc000028d}, {
+ ERRDOS, ERRnoaccess, 0xc000028e}, {
+ ERRDOS, ERRnoaccess, 0xc000028f}, {
+ ERRDOS, ERRnoaccess, 0xc0000290}, {
+ ERRDOS, ERRbadfunc, 0xc000029c}, {
+ ERRDOS, ERRinvlevel, 0x007c0001}, };
+
+/*****************************************************************************
+ Print an error message from the status code
+ *****************************************************************************/
+static void
+cifs_print_status(__u32 status_code)
+{
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) ==
+ (status_code & 0xFFFFFF)) {
+ printk(KERN_NOTICE "Status code returned 0x%08x %s\n",
+ status_code, nt_errs[idx].nt_errstr);
+ }
+ idx++;
+ }
+ return;
+}
+
+
+static void
+ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)
+{
+ int i;
+ if (ntstatus == 0) {
+ *eclass = 0;
+ *ecode = 0;
+ return;
+ }
+ for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) {
+ if (ntstatus == ntstatus_to_dos_map[i].ntstatus) {
+ *eclass = ntstatus_to_dos_map[i].dos_class;
+ *ecode = ntstatus_to_dos_map[i].dos_code;
+ return;
+ }
+ }
+ *eclass = ERRHRD;
+ *ecode = ERRgeneral;
+}
+
+int
+map_smb_to_linux_error(struct smb_hdr *smb, int logErr)
+{
+ unsigned int i;
+ int rc = -EIO; /* if transport error smb error may not be set */
+ __u8 smberrclass;
+ __u16 smberrcode;
+
+ /* BB if NT Status codes - map NT BB */
+
+ /* old style smb error codes */
+ if (smb->Status.CifsError == 0)
+ return 0;
+
+ if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
+ /* translate the newer STATUS codes to old style SMB errors
+ * and then to POSIX errors */
+ __u32 err = le32_to_cpu(smb->Status.CifsError);
+ if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED)))
+ cifs_print_status(err);
+ else if (cifsFYI & CIFS_RC)
+ cifs_print_status(err);
+ ntstatus_to_dos(err, &smberrclass, &smberrcode);
+ } else {
+ smberrclass = smb->Status.DosError.ErrorClass;
+ smberrcode = le16_to_cpu(smb->Status.DosError.Error);
+ }
+
+ /* old style errors */
+
+ /* DOS class smb error codes - map DOS */
+ if (smberrclass == ERRDOS) {
+ /* 1 byte field no need to byte reverse */
+ for (i = 0;
+ i <
+ sizeof(mapping_table_ERRDOS) /
+ sizeof(struct smb_to_posix_error); i++) {
+ if (mapping_table_ERRDOS[i].smb_err == 0)
+ break;
+ else if (mapping_table_ERRDOS[i].smb_err ==
+ smberrcode) {
+ rc = mapping_table_ERRDOS[i].posix_code;
+ break;
+ }
+ /* else try next error mapping one to see if match */
+ }
+ } else if (smberrclass == ERRSRV) {
+ /* server class of error codes */
+ for (i = 0;
+ i <
+ sizeof(mapping_table_ERRSRV) /
+ sizeof(struct smb_to_posix_error); i++) {
+ if (mapping_table_ERRSRV[i].smb_err == 0)
+ break;
+ else if (mapping_table_ERRSRV[i].smb_err ==
+ smberrcode) {
+ rc = mapping_table_ERRSRV[i].posix_code;
+ break;
+ }
+ /* else try next error mapping to see if match */
+ }
+ }
+ /* else ERRHRD class errors or junk - return EIO */
+
+ cFYI(1, ("Mapping smb error code %d to POSIX err %d",
+ smberrcode, rc));
+
+ /* generic corrective action e.g. reconnect SMB session on
+ * ERRbaduid could be added */
+
+ return rc;
+}
+
+/*
+ * calculate the size of the SMB message based on the fixed header
+ * portion, the number of word parameters and the data portion of the message
+ */
+unsigned int
+smbCalcSize(struct smb_hdr *ptr)
+{
+ return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
+ 2 /* size of the bcc field */ + BCC(ptr));
+}
+
+unsigned int
+smbCalcSize_LE(struct smb_hdr *ptr)
+{
+ return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
+ 2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr)));
+}
+
+/* The following are taken from fs/ntfs/util.c */
+
+#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
+
+ /*
+ * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
+ * into Unix UTC (based 1970-01-01, in seconds).
+ */
+struct timespec
+cifs_NTtimeToUnix(u64 ntutc)
+{
+ struct timespec ts;
+ /* BB what about the timezone? BB */
+
+ /* Subtract the NTFS time offset, then convert to 1s intervals. */
+ u64 t;
+
+ t = ntutc - NTFS_TIME_OFFSET;
+ ts.tv_nsec = do_div(t, 10000000) * 100;
+ ts.tv_sec = t;
+ return ts;
+}
+
+/* Convert the Unix UTC into NT UTC. */
+u64
+cifs_UnixTimeToNT(struct timespec t)
+{
+ /* Convert to 100ns intervals and then add the NTFS time offset. */
+ return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;
+}
+
+static int total_days_of_prev_months[] =
+{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+
+__le64 cnvrtDosCifsTm(__u16 date, __u16 time)
+{
+ return cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(date, time)));
+}
+
+struct timespec cnvrtDosUnixTm(__u16 date, __u16 time)
+{
+ struct timespec ts;
+ int sec, min, days, month, year;
+ SMB_TIME *st = (SMB_TIME *)&time;
+ SMB_DATE *sd = (SMB_DATE *)&date;
+
+ cFYI(1, ("date %d time %d", date, time));
+
+ sec = 2 * st->TwoSeconds;
+ min = st->Minutes;
+ if ((sec > 59) || (min > 59))
+ cERROR(1, ("illegal time min %d sec %d", min, sec));
+ sec += (min * 60);
+ sec += 60 * 60 * st->Hours;
+ if (st->Hours > 24)
+ cERROR(1, ("illegal hours %d", st->Hours));
+ days = sd->Day;
+ month = sd->Month;
+ if ((days > 31) || (month > 12)) {
+ cERROR(1, ("illegal date, month %d day: %d", month, days));
+ if (month > 12)
+ month = 12;
+ }
+ month -= 1;
+ days += total_days_of_prev_months[month];
+ days += 3652; /* account for difference in days between 1980 and 1970 */
+ year = sd->Year;
+ days += year * 365;
+ days += (year/4); /* leap year */
+ /* generalized leap year calculation is more complex, ie no leap year
+ for years/100 except for years/400, but since the maximum number for DOS
+ year is 2**7, the last year is 1980+127, which means we need only
+ consider 2 special case years, ie the years 2000 and 2100, and only
+ adjust for the lack of leap year for the year 2100, as 2000 was a
+ leap year (divisable by 400) */
+ if (year >= 120) /* the year 2100 */
+ days = days - 1; /* do not count leap year for the year 2100 */
+
+ /* adjust for leap year where we are still before leap day */
+ if (year != 120)
+ days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0);
+ sec += 24 * 60 * 60 * days;
+
+ ts.tv_sec = sec;
+
+ /* cFYI(1,("sec after cnvrt dos to unix time %d",sec)); */
+
+ ts.tv_nsec = 0;
+ return ts;
+}
diff --git a/fs/cifs/nterr.c b/fs/cifs/nterr.c
new file mode 100644
index 0000000..819fd99
--- /dev/null
+++ b/fs/cifs/nterr.c
@@ -0,0 +1,687 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * RPC Pipe client / server routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* NT error codes - see nterr.h */
+#include <linux/types.h>
+#include <linux/fs.h>
+#include "nterr.h"
+
+const struct nt_err_code_struct nt_errs[] = {
+ {"NT_STATUS_OK", NT_STATUS_OK},
+ {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL},
+ {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED},
+ {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS},
+ {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH},
+ {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION},
+ {"STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW},
+ {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR},
+ {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA},
+ {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE},
+ {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK},
+ {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC},
+ {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID},
+ {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED},
+ {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER},
+ {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE},
+ {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE},
+ {"NT_STATUS_INVALID_DEVICE_REQUEST",
+ NT_STATUS_INVALID_DEVICE_REQUEST},
+ {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE},
+ {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME},
+ {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA},
+ {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR},
+ {"NT_STATUS_MORE_PROCESSING_REQUIRED",
+ NT_STATUS_MORE_PROCESSING_REQUIRED},
+ {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY},
+ {"NT_STATUS_CONFLICTING_ADDRESSES",
+ NT_STATUS_CONFLICTING_ADDRESSES},
+ {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW},
+ {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM},
+ {"NT_STATUS_UNABLE_TO_DELETE_SECTION",
+ NT_STATUS_UNABLE_TO_DELETE_SECTION},
+ {"NT_STATUS_INVALID_SYSTEM_SERVICE",
+ NT_STATUS_INVALID_SYSTEM_SERVICE},
+ {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION},
+ {"NT_STATUS_INVALID_LOCK_SEQUENCE",
+ NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE},
+ {"NT_STATUS_INVALID_FILE_FOR_SECTION",
+ NT_STATUS_INVALID_FILE_FOR_SECTION},
+ {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED},
+ {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED},
+ {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL},
+ {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH},
+ {"NT_STATUS_NONCONTINUABLE_EXCEPTION",
+ NT_STATUS_NONCONTINUABLE_EXCEPTION},
+ {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION},
+ {"NT_STATUS_UNWIND", NT_STATUS_UNWIND},
+ {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK},
+ {"NT_STATUS_INVALID_UNWIND_TARGET",
+ NT_STATUS_INVALID_UNWIND_TARGET},
+ {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED},
+ {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR},
+ {"NT_STATUS_UNABLE_TO_DECOMMIT_VM",
+ NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+ {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED},
+ {"NT_STATUS_INVALID_PORT_ATTRIBUTES",
+ NT_STATUS_INVALID_PORT_ATTRIBUTES},
+ {"NT_STATUS_PORT_MESSAGE_TOO_LONG",
+ NT_STATUS_PORT_MESSAGE_TOO_LONG},
+ {"NT_STATUS_INVALID_PARAMETER_MIX",
+ NT_STATUS_INVALID_PARAMETER_MIX},
+ {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER},
+ {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR},
+ {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID},
+ {"NT_STATUS_OBJECT_NAME_NOT_FOUND",
+ NT_STATUS_OBJECT_NAME_NOT_FOUND},
+ {"NT_STATUS_OBJECT_NAME_COLLISION",
+ NT_STATUS_OBJECT_NAME_COLLISION},
+ {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE},
+ {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED},
+ {"NT_STATUS_DEVICE_ALREADY_ATTACHED",
+ NT_STATUS_DEVICE_ALREADY_ATTACHED},
+ {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID},
+ {"NT_STATUS_OBJECT_PATH_NOT_FOUND",
+ NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD",
+ NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+ {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN},
+ {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR},
+ {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR},
+ {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR},
+ {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG},
+ {"NT_STATUS_PORT_CONNECTION_REFUSED",
+ NT_STATUS_PORT_CONNECTION_REFUSED},
+ {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE},
+ {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION},
+ {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED},
+ {"NT_STATUS_INVALID_PAGE_PROTECTION",
+ NT_STATUS_INVALID_PAGE_PROTECTION},
+ {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED},
+ {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED",
+ NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET},
+ {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE},
+ {"NT_STATUS_SUSPEND_COUNT_EXCEEDED",
+ NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {"NT_STATUS_THREAD_IS_TERMINATING",
+ NT_STATUS_THREAD_IS_TERMINATING},
+ {"NT_STATUS_BAD_WORKING_SET_LIMIT",
+ NT_STATUS_BAD_WORKING_SET_LIMIT},
+ {"NT_STATUS_INCOMPATIBLE_FILE_MAP",
+ NT_STATUS_INCOMPATIBLE_FILE_MAP},
+ {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION},
+ {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED},
+ {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE},
+ {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE},
+ {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR},
+ {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT},
+ {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED},
+ {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING},
+ {"NT_STATUS_CTL_FILE_NOT_SUPPORTED",
+ NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION},
+ {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH},
+ {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER},
+ {"NT_STATUS_INVALID_PRIMARY_GROUP",
+ NT_STATUS_INVALID_PRIMARY_GROUP},
+ {"NT_STATUS_NO_IMPERSONATION_TOKEN",
+ NT_STATUS_NO_IMPERSONATION_TOKEN},
+ {"NT_STATUS_CANT_DISABLE_MANDATORY",
+ NT_STATUS_CANT_DISABLE_MANDATORY},
+ {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS},
+ {"NT_STATUS_NO_SUCH_LOGON_SESSION",
+ NT_STATUS_NO_SUCH_LOGON_SESSION},
+ {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE},
+ {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD},
+ {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME},
+ {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS},
+ {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER},
+ {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS},
+ {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP},
+ {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP},
+ {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP},
+ {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN},
+ {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD},
+ {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD},
+ {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION},
+ {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE},
+ {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION},
+ {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS},
+ {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION},
+ {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED},
+ {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED},
+ {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED},
+ {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED",
+ NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+ {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED},
+ {"NT_STATUS_INVALID_SUB_AUTHORITY",
+ NT_STATUS_INVALID_SUB_AUTHORITY},
+ {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL},
+ {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID},
+ {"NT_STATUS_INVALID_SECURITY_DESCR",
+ NT_STATUS_INVALID_SECURITY_DESCR},
+ {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND},
+ {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT},
+ {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN},
+ {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL},
+ {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED},
+ {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL},
+ {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED},
+ {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED},
+ {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED",
+ NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED},
+ {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY},
+ {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED},
+ {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL},
+ {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED},
+ {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA},
+ {"NT_STATUS_RESOURCE_DATA_NOT_FOUND",
+ NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+ {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND",
+ NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+ {"NT_STATUS_RESOURCE_NAME_NOT_FOUND",
+ NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+ {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED",
+ NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+ {"NT_STATUS_FLOAT_DENORMAL_OPERAND",
+ NT_STATUS_FLOAT_DENORMAL_OPERAND},
+ {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+ {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT},
+ {"NT_STATUS_FLOAT_INVALID_OPERATION",
+ NT_STATUS_FLOAT_INVALID_OPERATION},
+ {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW},
+ {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK},
+ {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW},
+ {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO",
+ NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+ {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW},
+ {"NT_STATUS_PRIVILEGED_INSTRUCTION",
+ NT_STATUS_PRIVILEGED_INSTRUCTION},
+ {"NT_STATUS_TOO_MANY_PAGING_FILES",
+ NT_STATUS_TOO_MANY_PAGING_FILES},
+ {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID},
+ {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED",
+ NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+ {"NT_STATUS_INSUFFICIENT_RESOURCES",
+ NT_STATUS_INSUFFICIENT_RESOURCES},
+ {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND},
+ {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR},
+ {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED},
+ {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE},
+ {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE},
+ {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED},
+ {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA},
+ {"NT_STATUS_MEDIA_WRITE_PROTECTED",
+ NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY},
+ {"NT_STATUS_INVALID_GROUP_ATTRIBUTES",
+ NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+ {"NT_STATUS_BAD_IMPERSONATION_LEVEL",
+ NT_STATUS_BAD_IMPERSONATION_LEVEL},
+ {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS},
+ {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS},
+ {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE},
+ {"NT_STATUS_BAD_MASTER_BOOT_RECORD",
+ NT_STATUS_BAD_MASTER_BOOT_RECORD},
+ {"NT_STATUS_INSTRUCTION_MISALIGNMENT",
+ NT_STATUS_INSTRUCTION_MISALIGNMENT},
+ {"NT_STATUS_INSTANCE_NOT_AVAILABLE",
+ NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE},
+ {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE},
+ {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY},
+ {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION},
+ {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED},
+ {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING},
+ {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED},
+ {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING},
+ {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE},
+ {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT},
+ {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED},
+ {"NT_STATUS_PROFILING_NOT_STARTED",
+ NT_STATUS_PROFILING_NOT_STARTED},
+ {"NT_STATUS_PROFILING_NOT_STOPPED",
+ NT_STATUS_PROFILING_NOT_STOPPED},
+ {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET},
+ {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY},
+ {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED},
+ {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING},
+ {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME},
+ {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH},
+ {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY},
+ {"NT_STATUS_DEVICE_DOES_NOT_EXIST",
+ NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS},
+ {"NT_STATUS_ADAPTER_HARDWARE_ERROR",
+ NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {"NT_STATUS_INVALID_NETWORK_RESPONSE",
+ NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {"NT_STATUS_UNEXPECTED_NETWORK_ERROR",
+ NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER},
+ {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL},
+ {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE},
+ {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED},
+ {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED},
+ {"NT_STATUS_NETWORK_ACCESS_DENIED",
+ NT_STATUS_NETWORK_ACCESS_DENIED},
+ {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE},
+ {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME},
+ {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES},
+ {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS},
+ {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED},
+ {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED},
+ {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT},
+ {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT},
+ {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE},
+ {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED},
+ {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED",
+ NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {"NT_STATUS_NO_SECURITY_ON_OBJECT",
+ NT_STATUS_NO_SECURITY_ON_OBJECT},
+ {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT},
+ {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY},
+ {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO",
+ NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+ {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF},
+ {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE},
+ {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE},
+ {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE},
+ {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN},
+ {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS},
+ {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED",
+ NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+ {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED},
+ {"NT_STATUS_INVALID_OPLOCK_PROTOCOL",
+ NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {"NT_STATUS_INTERNAL_DB_CORRUPTION",
+ NT_STATUS_INTERNAL_DB_CORRUPTION},
+ {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR},
+ {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED},
+ {"NT_STATUS_BAD_DESCRIPTOR_FORMAT",
+ NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+ {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER},
+ {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR},
+ {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR",
+ NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+ {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR",
+ NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+ {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR",
+ NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+ {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS},
+ {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS},
+ {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1},
+ {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2},
+ {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3},
+ {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4},
+ {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5},
+ {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6},
+ {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7},
+ {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8},
+ {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9},
+ {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10},
+ {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11},
+ {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12},
+ {"NT_STATUS_REDIRECTOR_NOT_STARTED",
+ NT_STATUS_REDIRECTOR_NOT_STARTED},
+ {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED},
+ {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW},
+ {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE},
+ {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE},
+ {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR},
+ {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY},
+ {"NT_STATUS_BAD_LOGON_SESSION_STATE",
+ NT_STATUS_BAD_LOGON_SESSION_STATE},
+ {"NT_STATUS_LOGON_SESSION_COLLISION",
+ NT_STATUS_LOGON_SESSION_COLLISION},
+ {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG},
+ {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN},
+ {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE},
+ {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND},
+ {"NT_STATUS_PROCESS_IS_TERMINATING",
+ NT_STATUS_PROCESS_IS_TERMINATING},
+ {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE},
+ {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION},
+ {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE},
+ {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED},
+ {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT},
+ {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST},
+ {"NT_STATUS_ABIOS_LID_ALREADY_OWNED",
+ NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+ {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER},
+ {"NT_STATUS_ABIOS_INVALID_COMMAND",
+ NT_STATUS_ABIOS_INVALID_COMMAND},
+ {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID},
+ {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE",
+ NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+ {"NT_STATUS_ABIOS_INVALID_SELECTOR",
+ NT_STATUS_ABIOS_INVALID_SELECTOR},
+ {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT},
+ {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE},
+ {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET},
+ {"NT_STATUS_INVALID_LDT_DESCRIPTOR",
+ NT_STATUS_INVALID_LDT_DESCRIPTOR},
+ {"NT_STATUS_INVALID_IMAGE_NE_FORMAT",
+ NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+ {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE},
+ {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE},
+ {"NT_STATUS_MAPPED_FILE_SIZE_ZERO",
+ NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+ {"NT_STATUS_TOO_MANY_OPENED_FILES",
+ NT_STATUS_TOO_MANY_OPENED_FILES},
+ {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED},
+ {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE},
+ {"NT_STATUS_INVALID_COMPUTER_NAME",
+ NT_STATUS_INVALID_COMPUTER_NAME},
+ {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED},
+ {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT},
+ {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP},
+ {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER},
+ {"NT_STATUS_MEMBERS_PRIMARY_GROUP",
+ NT_STATUS_MEMBERS_PRIMARY_GROUP},
+ {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED},
+ {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS},
+ {"NT_STATUS_THREAD_NOT_IN_PROCESS",
+ NT_STATUS_THREAD_NOT_IN_PROCESS},
+ {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE},
+ {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED",
+ NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+ {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT},
+ {"NT_STATUS_INVALID_IMAGE_LE_FORMAT",
+ NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+ {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ},
+ {"NT_STATUS_INVALID_IMAGE_PROTECT",
+ NT_STATUS_INVALID_IMAGE_PROTECT},
+ {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16},
+ {"NT_STATUS_LOGON_SERVER_CONFLICT",
+ NT_STATUS_LOGON_SERVER_CONFLICT},
+ {"NT_STATUS_TIME_DIFFERENCE_AT_DC",
+ NT_STATUS_TIME_DIFFERENCE_AT_DC},
+ {"NT_STATUS_SYNCHRONIZATION_REQUIRED",
+ NT_STATUS_SYNCHRONIZATION_REQUIRED},
+ {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND},
+ {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED},
+ {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED},
+ {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND},
+ {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND},
+ {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT},
+ {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT},
+ {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT},
+ {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES},
+ {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED},
+ {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT},
+ {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION},
+ {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS},
+ {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED},
+ {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE},
+ {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION},
+ {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE},
+ {"NT_STATUS_PAGEFILE_CREATE_FAILED",
+ NT_STATUS_PAGEFILE_CREATE_FAILED},
+ {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE},
+ {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL},
+ {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE},
+ {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT",
+ NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+ {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN},
+ {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT},
+ {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED},
+ {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR},
+ {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME},
+ {"NT_STATUS_SERIAL_NO_DEVICE_INITED",
+ NT_STATUS_SERIAL_NO_DEVICE_INITED},
+ {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS},
+ {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS},
+ {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS},
+ {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS},
+ {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED},
+ {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS},
+ {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG},
+ {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR},
+ {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE},
+ {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS},
+ {"NT_STATUS_LOGON_TYPE_NOT_GRANTED",
+ NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+ {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE},
+ {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED",
+ NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+ {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR",
+ NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+ {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER},
+ {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY",
+ NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+ {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER},
+ {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER},
+ {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER},
+ {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME},
+ {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND",
+ NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+ {"NT_STATUS_FLOPPY_WRONG_CYLINDER",
+ NT_STATUS_FLOPPY_WRONG_CYLINDER},
+ {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+ {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS},
+ {"NT_STATUS_DISK_RECALIBRATE_FAILED",
+ NT_STATUS_DISK_RECALIBRATE_FAILED},
+ {"NT_STATUS_DISK_OPERATION_FAILED",
+ NT_STATUS_DISK_OPERATION_FAILED},
+ {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED},
+ {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY},
+ {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING},
+ {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE},
+ {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH},
+ {"NT_STATUS_DEVICE_NOT_PARTITIONED",
+ NT_STATUS_DEVICE_NOT_PARTITIONED},
+ {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+ {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA",
+ NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+ {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW},
+ {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA},
+ {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER},
+ {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER},
+ {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED},
+ {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE},
+ {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS},
+ {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED",
+ NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+ {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN},
+ {"NT_STATUS_CHILD_MUST_BE_VOLATILE",
+ NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {"NT_STATUS_DEVICE_CONFIGURATION_ERROR",
+ NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+ {"NT_STATUS_DRIVER_INTERNAL_ERROR",
+ NT_STATUS_DRIVER_INTERNAL_ERROR},
+ {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE},
+ {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR},
+ {"NT_STATUS_DEVICE_PROTOCOL_ERROR",
+ NT_STATUS_DEVICE_PROTOCOL_ERROR},
+ {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER},
+ {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL},
+ {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE},
+ {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET},
+ {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+ {"NT_STATUS_TRUSTED_DOMAIN_FAILURE",
+ NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+ {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE",
+ NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+ {"NT_STATUS_EVENTLOG_FILE_CORRUPT",
+ NT_STATUS_EVENTLOG_FILE_CORRUPT},
+ {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START},
+ {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE},
+ {"NT_STATUS_MUTANT_LIMIT_EXCEEDED",
+ NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+ {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED},
+ {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED},
+ {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK},
+ {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT",
+ NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+ {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT},
+ {"NT_STATUS_EVENTLOG_FILE_CHANGED",
+ NT_STATUS_EVENTLOG_FILE_CHANGED},
+ {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
+ NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+ {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
+ NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+ {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
+ NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+ {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT",
+ NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+ {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED},
+ {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY},
+ {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED},
+ {"NT_STATUS_RESOURCE_LANG_NOT_FOUND",
+ NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+ {"NT_STATUS_INSUFF_SERVER_RESOURCES",
+ NT_STATUS_INSUFF_SERVER_RESOURCES},
+ {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE},
+ {"NT_STATUS_INVALID_ADDRESS_COMPONENT",
+ NT_STATUS_INVALID_ADDRESS_COMPONENT},
+ {"NT_STATUS_INVALID_ADDRESS_WILDCARD",
+ NT_STATUS_INVALID_ADDRESS_WILDCARD},
+ {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES},
+ {"NT_STATUS_ADDRESS_ALREADY_EXISTS",
+ NT_STATUS_ADDRESS_ALREADY_EXISTS},
+ {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED},
+ {"NT_STATUS_CONNECTION_DISCONNECTED",
+ NT_STATUS_CONNECTION_DISCONNECTED},
+ {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET},
+ {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES},
+ {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED},
+ {"NT_STATUS_TRANSACTION_TIMED_OUT",
+ NT_STATUS_TRANSACTION_TIMED_OUT},
+ {"NT_STATUS_TRANSACTION_NO_RELEASE",
+ NT_STATUS_TRANSACTION_NO_RELEASE},
+ {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH},
+ {"NT_STATUS_TRANSACTION_RESPONDED",
+ NT_STATUS_TRANSACTION_RESPONDED},
+ {"NT_STATUS_TRANSACTION_INVALID_ID",
+ NT_STATUS_TRANSACTION_INVALID_ID},
+ {"NT_STATUS_TRANSACTION_INVALID_TYPE",
+ NT_STATUS_TRANSACTION_INVALID_TYPE},
+ {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION},
+ {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION},
+ {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE",
+ NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+ {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED},
+ {"NT_STATUS_SYSTEM_PROCESS_TERMINATED",
+ NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+ {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED},
+ {"NT_STATUS_NO_BROWSER_SERVERS_FOUND",
+ NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+ {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR},
+ {"NT_STATUS_DRIVER_CANCEL_TIMEOUT",
+ NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+ {"NT_STATUS_REPLY_MESSAGE_MISMATCH",
+ NT_STATUS_REPLY_MESSAGE_MISMATCH},
+ {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT},
+ {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH",
+ NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+ {"NT_STATUS_LOST_WRITEBEHIND_DATA",
+ NT_STATUS_LOST_WRITEBEHIND_DATA},
+ {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID",
+ NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+ {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE},
+ {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND},
+ {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM},
+ {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE},
+ {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ},
+ {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK},
+ {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID},
+ {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS},
+ {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE},
+ {"NT_STATUS_RETRY", NT_STATUS_RETRY},
+ {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE},
+ {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET},
+ {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND},
+ {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW},
+ {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT},
+ {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+ {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT},
+ {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE},
+ {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED},
+ {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT},
+ {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED",
+ NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+ {"NT_STATUS_ADDRESS_NOT_ASSOCIATED",
+ NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+ {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID},
+ {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE},
+ {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE},
+ {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE},
+ {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE},
+ {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE},
+ {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED},
+ {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED},
+ {"NT_STATUS_BAD_COMPRESSION_BUFFER",
+ NT_STATUS_BAD_COMPRESSION_BUFFER},
+ {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE},
+ {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED},
+ {"NT_STATUS_TIMER_RESOLUTION_NOT_SET",
+ NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+ {"NT_STATUS_CONNECTION_COUNT_LIMIT",
+ NT_STATUS_CONNECTION_COUNT_LIMIT},
+ {"NT_STATUS_LOGIN_TIME_RESTRICTION",
+ NT_STATUS_LOGIN_TIME_RESTRICTION},
+ {"NT_STATUS_LOGIN_WKSTA_RESTRICTION",
+ NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+ {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH},
+ {"NT_STATUS_INSUFFICIENT_LOGON_INFO",
+ NT_STATUS_INSUFFICIENT_LOGON_INFO},
+ {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT},
+ {"NT_STATUS_BAD_SERVICE_ENTRYPOINT",
+ NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+ {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST},
+ {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1},
+ {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2},
+ {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT},
+ {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED},
+ {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE},
+ {"NT_STATUS_LICENSE_QUOTA_EXCEEDED",
+ NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+ {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT},
+ {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT},
+ {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT},
+ {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE},
+ {"NT_STATUS_UNSUPPORTED_COMPRESSION",
+ NT_STATUS_UNSUPPORTED_COMPRESSION},
+ {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE},
+ {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH",
+ NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+ {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND",
+ NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+ {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND",
+ NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+ {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED},
+ {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS},
+ {"NT_STATUS_QUOTA_LIST_INCONSISTENT",
+ NT_STATUS_QUOTA_LIST_INCONSISTENT},
+ {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE},
+ {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
+ {"STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES},
+ {"STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED},
+ {NULL, 0}
+};
diff --git a/fs/cifs/nterr.h b/fs/cifs/nterr.h
new file mode 100644
index 0000000..588abbb
--- /dev/null
+++ b/fs/cifs/nterr.h
@@ -0,0 +1,556 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NT error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+
+#ifndef _NTERR_H
+#define _NTERR_H
+
+struct nt_err_code_struct {
+ char *nt_errstr;
+ __u32 nt_errcode;
+};
+
+extern const struct nt_err_code_struct nt_errs[];
+
+/* Win32 Status codes. */
+
+#define STATUS_BUFFER_OVERFLOW 0x80000005
+#define STATUS_MORE_ENTRIES 0x0105
+#define ERROR_INVALID_PARAMETER 0x0057
+#define ERROR_INSUFFICIENT_BUFFER 0x007a
+#define STATUS_1804 0x070c
+#define STATUS_NOTIFY_ENUM_DIR 0x010c
+
+/* Win32 Error codes extracted using a loop in smbclient then printing a
+ netmon sniff to a file. */
+
+#define NT_STATUS_OK 0x0000
+#define STATUS_SOME_UNMAPPED 0x0107
+#define STATUS_BUFFER_OVERFLOW 0x80000005
+#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a
+#define NT_STATUS_UNSUCCESSFUL 0xC0000000 | 0x0001
+#define NT_STATUS_NOT_IMPLEMENTED 0xC0000000 | 0x0002
+#define NT_STATUS_INVALID_INFO_CLASS 0xC0000000 | 0x0003
+#define NT_STATUS_INFO_LENGTH_MISMATCH 0xC0000000 | 0x0004
+#define NT_STATUS_ACCESS_VIOLATION 0xC0000000 | 0x0005
+#define NT_STATUS_IN_PAGE_ERROR 0xC0000000 | 0x0006
+#define NT_STATUS_PAGEFILE_QUOTA 0xC0000000 | 0x0007
+#define NT_STATUS_INVALID_HANDLE 0xC0000000 | 0x0008
+#define NT_STATUS_BAD_INITIAL_STACK 0xC0000000 | 0x0009
+#define NT_STATUS_BAD_INITIAL_PC 0xC0000000 | 0x000a
+#define NT_STATUS_INVALID_CID 0xC0000000 | 0x000b
+#define NT_STATUS_TIMER_NOT_CANCELED 0xC0000000 | 0x000c
+#define NT_STATUS_INVALID_PARAMETER 0xC0000000 | 0x000d
+#define NT_STATUS_NO_SUCH_DEVICE 0xC0000000 | 0x000e
+#define NT_STATUS_NO_SUCH_FILE 0xC0000000 | 0x000f
+#define NT_STATUS_INVALID_DEVICE_REQUEST 0xC0000000 | 0x0010
+#define NT_STATUS_END_OF_FILE 0xC0000000 | 0x0011
+#define NT_STATUS_WRONG_VOLUME 0xC0000000 | 0x0012
+#define NT_STATUS_NO_MEDIA_IN_DEVICE 0xC0000000 | 0x0013
+#define NT_STATUS_UNRECOGNIZED_MEDIA 0xC0000000 | 0x0014
+#define NT_STATUS_NONEXISTENT_SECTOR 0xC0000000 | 0x0015
+#define NT_STATUS_MORE_PROCESSING_REQUIRED 0xC0000000 | 0x0016
+#define NT_STATUS_NO_MEMORY 0xC0000000 | 0x0017
+#define NT_STATUS_CONFLICTING_ADDRESSES 0xC0000000 | 0x0018
+#define NT_STATUS_NOT_MAPPED_VIEW 0xC0000000 | 0x0019
+#define NT_STATUS_UNABLE_TO_FREE_VM 0x80000000 | 0x001a
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0xC0000000 | 0x001b
+#define NT_STATUS_INVALID_SYSTEM_SERVICE 0xC0000000 | 0x001c
+#define NT_STATUS_ILLEGAL_INSTRUCTION 0xC0000000 | 0x001d
+#define NT_STATUS_INVALID_LOCK_SEQUENCE 0xC0000000 | 0x001e
+#define NT_STATUS_INVALID_VIEW_SIZE 0xC0000000 | 0x001f
+#define NT_STATUS_INVALID_FILE_FOR_SECTION 0xC0000000 | 0x0020
+#define NT_STATUS_ALREADY_COMMITTED 0xC0000000 | 0x0021
+#define NT_STATUS_ACCESS_DENIED 0xC0000000 | 0x0022
+#define NT_STATUS_BUFFER_TOO_SMALL 0xC0000000 | 0x0023
+#define NT_STATUS_OBJECT_TYPE_MISMATCH 0xC0000000 | 0x0024
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0xC0000000 | 0x0025
+#define NT_STATUS_INVALID_DISPOSITION 0xC0000000 | 0x0026
+#define NT_STATUS_UNWIND 0xC0000000 | 0x0027
+#define NT_STATUS_BAD_STACK 0xC0000000 | 0x0028
+#define NT_STATUS_INVALID_UNWIND_TARGET 0xC0000000 | 0x0029
+#define NT_STATUS_NOT_LOCKED 0xC0000000 | 0x002a
+#define NT_STATUS_PARITY_ERROR 0xC0000000 | 0x002b
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0xC0000000 | 0x002c
+#define NT_STATUS_NOT_COMMITTED 0xC0000000 | 0x002d
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0xC0000000 | 0x002e
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0xC0000000 | 0x002f
+#define NT_STATUS_INVALID_PARAMETER_MIX 0xC0000000 | 0x0030
+#define NT_STATUS_INVALID_QUOTA_LOWER 0xC0000000 | 0x0031
+#define NT_STATUS_DISK_CORRUPT_ERROR 0xC0000000 | 0x0032
+#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000000 | 0x0033
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000000 | 0x0034
+#define NT_STATUS_OBJECT_NAME_COLLISION 0xC0000000 | 0x0035
+#define NT_STATUS_HANDLE_NOT_WAITABLE 0xC0000000 | 0x0036
+#define NT_STATUS_PORT_DISCONNECTED 0xC0000000 | 0x0037
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0xC0000000 | 0x0038
+#define NT_STATUS_OBJECT_PATH_INVALID 0xC0000000 | 0x0039
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0xC0000000 | 0x003a
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0xC0000000 | 0x003b
+#define NT_STATUS_DATA_OVERRUN 0xC0000000 | 0x003c
+#define NT_STATUS_DATA_LATE_ERROR 0xC0000000 | 0x003d
+#define NT_STATUS_DATA_ERROR 0xC0000000 | 0x003e
+#define NT_STATUS_CRC_ERROR 0xC0000000 | 0x003f
+#define NT_STATUS_SECTION_TOO_BIG 0xC0000000 | 0x0040
+#define NT_STATUS_PORT_CONNECTION_REFUSED 0xC0000000 | 0x0041
+#define NT_STATUS_INVALID_PORT_HANDLE 0xC0000000 | 0x0042
+#define NT_STATUS_SHARING_VIOLATION 0xC0000000 | 0x0043
+#define NT_STATUS_QUOTA_EXCEEDED 0xC0000000 | 0x0044
+#define NT_STATUS_INVALID_PAGE_PROTECTION 0xC0000000 | 0x0045
+#define NT_STATUS_MUTANT_NOT_OWNED 0xC0000000 | 0x0046
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000000 | 0x0047
+#define NT_STATUS_PORT_ALREADY_SET 0xC0000000 | 0x0048
+#define NT_STATUS_SECTION_NOT_IMAGE 0xC0000000 | 0x0049
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0xC0000000 | 0x004a
+#define NT_STATUS_THREAD_IS_TERMINATING 0xC0000000 | 0x004b
+#define NT_STATUS_BAD_WORKING_SET_LIMIT 0xC0000000 | 0x004c
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0xC0000000 | 0x004d
+#define NT_STATUS_SECTION_PROTECTION 0xC0000000 | 0x004e
+#define NT_STATUS_EAS_NOT_SUPPORTED 0xC0000000 | 0x004f
+#define NT_STATUS_EA_TOO_LARGE 0xC0000000 | 0x0050
+#define NT_STATUS_NONEXISTENT_EA_ENTRY 0xC0000000 | 0x0051
+#define NT_STATUS_NO_EAS_ON_FILE 0xC0000000 | 0x0052
+#define NT_STATUS_EA_CORRUPT_ERROR 0xC0000000 | 0x0053
+#define NT_STATUS_FILE_LOCK_CONFLICT 0xC0000000 | 0x0054
+#define NT_STATUS_LOCK_NOT_GRANTED 0xC0000000 | 0x0055
+#define NT_STATUS_DELETE_PENDING 0xC0000000 | 0x0056
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0xC0000000 | 0x0057
+#define NT_STATUS_UNKNOWN_REVISION 0xC0000000 | 0x0058
+#define NT_STATUS_REVISION_MISMATCH 0xC0000000 | 0x0059
+#define NT_STATUS_INVALID_OWNER 0xC0000000 | 0x005a
+#define NT_STATUS_INVALID_PRIMARY_GROUP 0xC0000000 | 0x005b
+#define NT_STATUS_NO_IMPERSONATION_TOKEN 0xC0000000 | 0x005c
+#define NT_STATUS_CANT_DISABLE_MANDATORY 0xC0000000 | 0x005d
+#define NT_STATUS_NO_LOGON_SERVERS 0xC0000000 | 0x005e
+#define NT_STATUS_NO_SUCH_LOGON_SESSION 0xC0000000 | 0x005f
+#define NT_STATUS_NO_SUCH_PRIVILEGE 0xC0000000 | 0x0060
+#define NT_STATUS_PRIVILEGE_NOT_HELD 0xC0000000 | 0x0061
+#define NT_STATUS_INVALID_ACCOUNT_NAME 0xC0000000 | 0x0062
+#define NT_STATUS_USER_EXISTS 0xC0000000 | 0x0063
+#define NT_STATUS_NO_SUCH_USER 0xC0000000 | 0x0064
+#define NT_STATUS_GROUP_EXISTS 0xC0000000 | 0x0065
+#define NT_STATUS_NO_SUCH_GROUP 0xC0000000 | 0x0066
+#define NT_STATUS_MEMBER_IN_GROUP 0xC0000000 | 0x0067
+#define NT_STATUS_MEMBER_NOT_IN_GROUP 0xC0000000 | 0x0068
+#define NT_STATUS_LAST_ADMIN 0xC0000000 | 0x0069
+#define NT_STATUS_WRONG_PASSWORD 0xC0000000 | 0x006a
+#define NT_STATUS_ILL_FORMED_PASSWORD 0xC0000000 | 0x006b
+#define NT_STATUS_PASSWORD_RESTRICTION 0xC0000000 | 0x006c
+#define NT_STATUS_LOGON_FAILURE 0xC0000000 | 0x006d
+#define NT_STATUS_ACCOUNT_RESTRICTION 0xC0000000 | 0x006e
+#define NT_STATUS_INVALID_LOGON_HOURS 0xC0000000 | 0x006f
+#define NT_STATUS_INVALID_WORKSTATION 0xC0000000 | 0x0070
+#define NT_STATUS_PASSWORD_EXPIRED 0xC0000000 | 0x0071
+#define NT_STATUS_ACCOUNT_DISABLED 0xC0000000 | 0x0072
+#define NT_STATUS_NONE_MAPPED 0xC0000000 | 0x0073
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0xC0000000 | 0x0074
+#define NT_STATUS_LUIDS_EXHAUSTED 0xC0000000 | 0x0075
+#define NT_STATUS_INVALID_SUB_AUTHORITY 0xC0000000 | 0x0076
+#define NT_STATUS_INVALID_ACL 0xC0000000 | 0x0077
+#define NT_STATUS_INVALID_SID 0xC0000000 | 0x0078
+#define NT_STATUS_INVALID_SECURITY_DESCR 0xC0000000 | 0x0079
+#define NT_STATUS_PROCEDURE_NOT_FOUND 0xC0000000 | 0x007a
+#define NT_STATUS_INVALID_IMAGE_FORMAT 0xC0000000 | 0x007b
+#define NT_STATUS_NO_TOKEN 0xC0000000 | 0x007c
+#define NT_STATUS_BAD_INHERITANCE_ACL 0xC0000000 | 0x007d
+#define NT_STATUS_RANGE_NOT_LOCKED 0xC0000000 | 0x007e
+#define NT_STATUS_DISK_FULL 0xC0000000 | 0x007f
+#define NT_STATUS_SERVER_DISABLED 0xC0000000 | 0x0080
+#define NT_STATUS_SERVER_NOT_DISABLED 0xC0000000 | 0x0081
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0xC0000000 | 0x0082
+#define NT_STATUS_GUIDS_EXHAUSTED 0xC0000000 | 0x0083
+#define NT_STATUS_INVALID_ID_AUTHORITY 0xC0000000 | 0x0084
+#define NT_STATUS_AGENTS_EXHAUSTED 0xC0000000 | 0x0085
+#define NT_STATUS_INVALID_VOLUME_LABEL 0xC0000000 | 0x0086
+#define NT_STATUS_SECTION_NOT_EXTENDED 0xC0000000 | 0x0087
+#define NT_STATUS_NOT_MAPPED_DATA 0xC0000000 | 0x0088
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0xC0000000 | 0x0089
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0xC0000000 | 0x008a
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0xC0000000 | 0x008b
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0xC0000000 | 0x008c
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0xC0000000 | 0x008d
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0xC0000000 | 0x008e
+#define NT_STATUS_FLOAT_INEXACT_RESULT 0xC0000000 | 0x008f
+#define NT_STATUS_FLOAT_INVALID_OPERATION 0xC0000000 | 0x0090
+#define NT_STATUS_FLOAT_OVERFLOW 0xC0000000 | 0x0091
+#define NT_STATUS_FLOAT_STACK_CHECK 0xC0000000 | 0x0092
+#define NT_STATUS_FLOAT_UNDERFLOW 0xC0000000 | 0x0093
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000000 | 0x0094
+#define NT_STATUS_INTEGER_OVERFLOW 0xC0000000 | 0x0095
+#define NT_STATUS_PRIVILEGED_INSTRUCTION 0xC0000000 | 0x0096
+#define NT_STATUS_TOO_MANY_PAGING_FILES 0xC0000000 | 0x0097
+#define NT_STATUS_FILE_INVALID 0xC0000000 | 0x0098
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0xC0000000 | 0x0099
+#define NT_STATUS_INSUFFICIENT_RESOURCES 0xC0000000 | 0x009a
+#define NT_STATUS_DFS_EXIT_PATH_FOUND 0xC0000000 | 0x009b
+#define NT_STATUS_DEVICE_DATA_ERROR 0xC0000000 | 0x009c
+#define NT_STATUS_DEVICE_NOT_CONNECTED 0xC0000000 | 0x009d
+#define NT_STATUS_DEVICE_POWER_FAILURE 0xC0000000 | 0x009e
+#define NT_STATUS_FREE_VM_NOT_AT_BASE 0xC0000000 | 0x009f
+#define NT_STATUS_MEMORY_NOT_ALLOCATED 0xC0000000 | 0x00a0
+#define NT_STATUS_WORKING_SET_QUOTA 0xC0000000 | 0x00a1
+#define NT_STATUS_MEDIA_WRITE_PROTECTED 0xC0000000 | 0x00a2
+#define NT_STATUS_DEVICE_NOT_READY 0xC0000000 | 0x00a3
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0xC0000000 | 0x00a4
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0xC0000000 | 0x00a5
+#define NT_STATUS_CANT_OPEN_ANONYMOUS 0xC0000000 | 0x00a6
+#define NT_STATUS_BAD_VALIDATION_CLASS 0xC0000000 | 0x00a7
+#define NT_STATUS_BAD_TOKEN_TYPE 0xC0000000 | 0x00a8
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0xC0000000 | 0x00a9
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0xC0000000 | 0x00aa
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0xC0000000 | 0x00ab
+#define NT_STATUS_PIPE_NOT_AVAILABLE 0xC0000000 | 0x00ac
+#define NT_STATUS_INVALID_PIPE_STATE 0xC0000000 | 0x00ad
+#define NT_STATUS_PIPE_BUSY 0xC0000000 | 0x00ae
+#define NT_STATUS_ILLEGAL_FUNCTION 0xC0000000 | 0x00af
+#define NT_STATUS_PIPE_DISCONNECTED 0xC0000000 | 0x00b0
+#define NT_STATUS_PIPE_CLOSING 0xC0000000 | 0x00b1
+#define NT_STATUS_PIPE_CONNECTED 0xC0000000 | 0x00b2
+#define NT_STATUS_PIPE_LISTENING 0xC0000000 | 0x00b3
+#define NT_STATUS_INVALID_READ_MODE 0xC0000000 | 0x00b4
+#define NT_STATUS_IO_TIMEOUT 0xC0000000 | 0x00b5
+#define NT_STATUS_FILE_FORCED_CLOSED 0xC0000000 | 0x00b6
+#define NT_STATUS_PROFILING_NOT_STARTED 0xC0000000 | 0x00b7
+#define NT_STATUS_PROFILING_NOT_STOPPED 0xC0000000 | 0x00b8
+#define NT_STATUS_COULD_NOT_INTERPRET 0xC0000000 | 0x00b9
+#define NT_STATUS_FILE_IS_A_DIRECTORY 0xC0000000 | 0x00ba
+#define NT_STATUS_NOT_SUPPORTED 0xC0000000 | 0x00bb
+#define NT_STATUS_REMOTE_NOT_LISTENING 0xC0000000 | 0x00bc
+#define NT_STATUS_DUPLICATE_NAME 0xC0000000 | 0x00bd
+#define NT_STATUS_BAD_NETWORK_PATH 0xC0000000 | 0x00be
+#define NT_STATUS_NETWORK_BUSY 0xC0000000 | 0x00bf
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0xC0000000 | 0x00c0
+#define NT_STATUS_TOO_MANY_COMMANDS 0xC0000000 | 0x00c1
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0xC0000000 | 0x00c2
+#define NT_STATUS_INVALID_NETWORK_RESPONSE 0xC0000000 | 0x00c3
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0xC0000000 | 0x00c4
+#define NT_STATUS_BAD_REMOTE_ADAPTER 0xC0000000 | 0x00c5
+#define NT_STATUS_PRINT_QUEUE_FULL 0xC0000000 | 0x00c6
+#define NT_STATUS_NO_SPOOL_SPACE 0xC0000000 | 0x00c7
+#define NT_STATUS_PRINT_CANCELLED 0xC0000000 | 0x00c8
+#define NT_STATUS_NETWORK_NAME_DELETED 0xC0000000 | 0x00c9
+#define NT_STATUS_NETWORK_ACCESS_DENIED 0xC0000000 | 0x00ca
+#define NT_STATUS_BAD_DEVICE_TYPE 0xC0000000 | 0x00cb
+#define NT_STATUS_BAD_NETWORK_NAME 0xC0000000 | 0x00cc
+#define NT_STATUS_TOO_MANY_NAMES 0xC0000000 | 0x00cd
+#define NT_STATUS_TOO_MANY_SESSIONS 0xC0000000 | 0x00ce
+#define NT_STATUS_SHARING_PAUSED 0xC0000000 | 0x00cf
+#define NT_STATUS_REQUEST_NOT_ACCEPTED 0xC0000000 | 0x00d0
+#define NT_STATUS_REDIRECTOR_PAUSED 0xC0000000 | 0x00d1
+#define NT_STATUS_NET_WRITE_FAULT 0xC0000000 | 0x00d2
+#define NT_STATUS_PROFILING_AT_LIMIT 0xC0000000 | 0x00d3
+#define NT_STATUS_NOT_SAME_DEVICE 0xC0000000 | 0x00d4
+#define NT_STATUS_FILE_RENAMED 0xC0000000 | 0x00d5
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0xC0000000 | 0x00d6
+#define NT_STATUS_NO_SECURITY_ON_OBJECT 0xC0000000 | 0x00d7
+#define NT_STATUS_CANT_WAIT 0xC0000000 | 0x00d8
+#define NT_STATUS_PIPE_EMPTY 0xC0000000 | 0x00d9
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0xC0000000 | 0x00da
+#define NT_STATUS_CANT_TERMINATE_SELF 0xC0000000 | 0x00db
+#define NT_STATUS_INVALID_SERVER_STATE 0xC0000000 | 0x00dc
+#define NT_STATUS_INVALID_DOMAIN_STATE 0xC0000000 | 0x00dd
+#define NT_STATUS_INVALID_DOMAIN_ROLE 0xC0000000 | 0x00de
+#define NT_STATUS_NO_SUCH_DOMAIN 0xC0000000 | 0x00df
+#define NT_STATUS_DOMAIN_EXISTS 0xC0000000 | 0x00e0
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0xC0000000 | 0x00e1
+#define NT_STATUS_OPLOCK_NOT_GRANTED 0xC0000000 | 0x00e2
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0xC0000000 | 0x00e3
+#define NT_STATUS_INTERNAL_DB_CORRUPTION 0xC0000000 | 0x00e4
+#define NT_STATUS_INTERNAL_ERROR 0xC0000000 | 0x00e5
+#define NT_STATUS_GENERIC_NOT_MAPPED 0xC0000000 | 0x00e6
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0xC0000000 | 0x00e7
+#define NT_STATUS_INVALID_USER_BUFFER 0xC0000000 | 0x00e8
+#define NT_STATUS_UNEXPECTED_IO_ERROR 0xC0000000 | 0x00e9
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0xC0000000 | 0x00ea
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0xC0000000 | 0x00eb
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0xC0000000 | 0x00ec
+#define NT_STATUS_NOT_LOGON_PROCESS 0xC0000000 | 0x00ed
+#define NT_STATUS_LOGON_SESSION_EXISTS 0xC0000000 | 0x00ee
+#define NT_STATUS_INVALID_PARAMETER_1 0xC0000000 | 0x00ef
+#define NT_STATUS_INVALID_PARAMETER_2 0xC0000000 | 0x00f0
+#define NT_STATUS_INVALID_PARAMETER_3 0xC0000000 | 0x00f1
+#define NT_STATUS_INVALID_PARAMETER_4 0xC0000000 | 0x00f2
+#define NT_STATUS_INVALID_PARAMETER_5 0xC0000000 | 0x00f3
+#define NT_STATUS_INVALID_PARAMETER_6 0xC0000000 | 0x00f4
+#define NT_STATUS_INVALID_PARAMETER_7 0xC0000000 | 0x00f5
+#define NT_STATUS_INVALID_PARAMETER_8 0xC0000000 | 0x00f6
+#define NT_STATUS_INVALID_PARAMETER_9 0xC0000000 | 0x00f7
+#define NT_STATUS_INVALID_PARAMETER_10 0xC0000000 | 0x00f8
+#define NT_STATUS_INVALID_PARAMETER_11 0xC0000000 | 0x00f9
+#define NT_STATUS_INVALID_PARAMETER_12 0xC0000000 | 0x00fa
+#define NT_STATUS_REDIRECTOR_NOT_STARTED 0xC0000000 | 0x00fb
+#define NT_STATUS_REDIRECTOR_STARTED 0xC0000000 | 0x00fc
+#define NT_STATUS_STACK_OVERFLOW 0xC0000000 | 0x00fd
+#define NT_STATUS_NO_SUCH_PACKAGE 0xC0000000 | 0x00fe
+#define NT_STATUS_BAD_FUNCTION_TABLE 0xC0000000 | 0x00ff
+#define NT_STATUS_DIRECTORY_NOT_EMPTY 0xC0000000 | 0x0101
+#define NT_STATUS_FILE_CORRUPT_ERROR 0xC0000000 | 0x0102
+#define NT_STATUS_NOT_A_DIRECTORY 0xC0000000 | 0x0103
+#define NT_STATUS_BAD_LOGON_SESSION_STATE 0xC0000000 | 0x0104
+#define NT_STATUS_LOGON_SESSION_COLLISION 0xC0000000 | 0x0105
+#define NT_STATUS_NAME_TOO_LONG 0xC0000000 | 0x0106
+#define NT_STATUS_FILES_OPEN 0xC0000000 | 0x0107
+#define NT_STATUS_CONNECTION_IN_USE 0xC0000000 | 0x0108
+#define NT_STATUS_MESSAGE_NOT_FOUND 0xC0000000 | 0x0109
+#define NT_STATUS_PROCESS_IS_TERMINATING 0xC0000000 | 0x010a
+#define NT_STATUS_INVALID_LOGON_TYPE 0xC0000000 | 0x010b
+#define NT_STATUS_NO_GUID_TRANSLATION 0xC0000000 | 0x010c
+#define NT_STATUS_CANNOT_IMPERSONATE 0xC0000000 | 0x010d
+#define NT_STATUS_IMAGE_ALREADY_LOADED 0xC0000000 | 0x010e
+#define NT_STATUS_ABIOS_NOT_PRESENT 0xC0000000 | 0x010f
+#define NT_STATUS_ABIOS_LID_NOT_EXIST 0xC0000000 | 0x0110
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0xC0000000 | 0x0111
+#define NT_STATUS_ABIOS_NOT_LID_OWNER 0xC0000000 | 0x0112
+#define NT_STATUS_ABIOS_INVALID_COMMAND 0xC0000000 | 0x0113
+#define NT_STATUS_ABIOS_INVALID_LID 0xC0000000 | 0x0114
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0xC0000000 | 0x0115
+#define NT_STATUS_ABIOS_INVALID_SELECTOR 0xC0000000 | 0x0116
+#define NT_STATUS_NO_LDT 0xC0000000 | 0x0117
+#define NT_STATUS_INVALID_LDT_SIZE 0xC0000000 | 0x0118
+#define NT_STATUS_INVALID_LDT_OFFSET 0xC0000000 | 0x0119
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0xC0000000 | 0x011a
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0xC0000000 | 0x011b
+#define NT_STATUS_RXACT_INVALID_STATE 0xC0000000 | 0x011c
+#define NT_STATUS_RXACT_COMMIT_FAILURE 0xC0000000 | 0x011d
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0xC0000000 | 0x011e
+#define NT_STATUS_TOO_MANY_OPENED_FILES 0xC0000000 | 0x011f
+#define NT_STATUS_CANCELLED 0xC0000000 | 0x0120
+#define NT_STATUS_CANNOT_DELETE 0xC0000000 | 0x0121
+#define NT_STATUS_INVALID_COMPUTER_NAME 0xC0000000 | 0x0122
+#define NT_STATUS_FILE_DELETED 0xC0000000 | 0x0123
+#define NT_STATUS_SPECIAL_ACCOUNT 0xC0000000 | 0x0124
+#define NT_STATUS_SPECIAL_GROUP 0xC0000000 | 0x0125
+#define NT_STATUS_SPECIAL_USER 0xC0000000 | 0x0126
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0xC0000000 | 0x0127
+#define NT_STATUS_FILE_CLOSED 0xC0000000 | 0x0128
+#define NT_STATUS_TOO_MANY_THREADS 0xC0000000 | 0x0129
+#define NT_STATUS_THREAD_NOT_IN_PROCESS 0xC0000000 | 0x012a
+#define NT_STATUS_TOKEN_ALREADY_IN_USE 0xC0000000 | 0x012b
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0xC0000000 | 0x012c
+#define NT_STATUS_COMMITMENT_LIMIT 0xC0000000 | 0x012d
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0xC0000000 | 0x012e
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0xC0000000 | 0x012f
+#define NT_STATUS_INVALID_IMAGE_PROTECT 0xC0000000 | 0x0130
+#define NT_STATUS_INVALID_IMAGE_WIN_16 0xC0000000 | 0x0131
+#define NT_STATUS_LOGON_SERVER_CONFLICT 0xC0000000 | 0x0132
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0xC0000000 | 0x0133
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0xC0000000 | 0x0134
+#define NT_STATUS_DLL_NOT_FOUND 0xC0000000 | 0x0135
+#define NT_STATUS_OPEN_FAILED 0xC0000000 | 0x0136
+#define NT_STATUS_IO_PRIVILEGE_FAILED 0xC0000000 | 0x0137
+#define NT_STATUS_ORDINAL_NOT_FOUND 0xC0000000 | 0x0138
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0139
+#define NT_STATUS_CONTROL_C_EXIT 0xC0000000 | 0x013a
+#define NT_STATUS_LOCAL_DISCONNECT 0xC0000000 | 0x013b
+#define NT_STATUS_REMOTE_DISCONNECT 0xC0000000 | 0x013c
+#define NT_STATUS_REMOTE_RESOURCES 0xC0000000 | 0x013d
+#define NT_STATUS_LINK_FAILED 0xC0000000 | 0x013e
+#define NT_STATUS_LINK_TIMEOUT 0xC0000000 | 0x013f
+#define NT_STATUS_INVALID_CONNECTION 0xC0000000 | 0x0140
+#define NT_STATUS_INVALID_ADDRESS 0xC0000000 | 0x0141
+#define NT_STATUS_DLL_INIT_FAILED 0xC0000000 | 0x0142
+#define NT_STATUS_MISSING_SYSTEMFILE 0xC0000000 | 0x0143
+#define NT_STATUS_UNHANDLED_EXCEPTION 0xC0000000 | 0x0144
+#define NT_STATUS_APP_INIT_FAILURE 0xC0000000 | 0x0145
+#define NT_STATUS_PAGEFILE_CREATE_FAILED 0xC0000000 | 0x0146
+#define NT_STATUS_NO_PAGEFILE 0xC0000000 | 0x0147
+#define NT_STATUS_INVALID_LEVEL 0xC0000000 | 0x0148
+#define NT_STATUS_WRONG_PASSWORD_CORE 0xC0000000 | 0x0149
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0xC0000000 | 0x014a
+#define NT_STATUS_PIPE_BROKEN 0xC0000000 | 0x014b
+#define NT_STATUS_REGISTRY_CORRUPT 0xC0000000 | 0x014c
+#define NT_STATUS_REGISTRY_IO_FAILED 0xC0000000 | 0x014d
+#define NT_STATUS_NO_EVENT_PAIR 0xC0000000 | 0x014e
+#define NT_STATUS_UNRECOGNIZED_VOLUME 0xC0000000 | 0x014f
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0xC0000000 | 0x0150
+#define NT_STATUS_NO_SUCH_ALIAS 0xC0000000 | 0x0151
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0xC0000000 | 0x0152
+#define NT_STATUS_MEMBER_IN_ALIAS 0xC0000000 | 0x0153
+#define NT_STATUS_ALIAS_EXISTS 0xC0000000 | 0x0154
+#define NT_STATUS_LOGON_NOT_GRANTED 0xC0000000 | 0x0155
+#define NT_STATUS_TOO_MANY_SECRETS 0xC0000000 | 0x0156
+#define NT_STATUS_SECRET_TOO_LONG 0xC0000000 | 0x0157
+#define NT_STATUS_INTERNAL_DB_ERROR 0xC0000000 | 0x0158
+#define NT_STATUS_FULLSCREEN_MODE 0xC0000000 | 0x0159
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0xC0000000 | 0x015a
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0xC0000000 | 0x015b
+#define NT_STATUS_NOT_REGISTRY_FILE 0xC0000000 | 0x015c
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x015d
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0xC0000000 | 0x015e
+#define NT_STATUS_FT_MISSING_MEMBER 0xC0000000 | 0x015f
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0xC0000000 | 0x0160
+#define NT_STATUS_ILLEGAL_CHARACTER 0xC0000000 | 0x0161
+#define NT_STATUS_UNMAPPABLE_CHARACTER 0xC0000000 | 0x0162
+#define NT_STATUS_UNDEFINED_CHARACTER 0xC0000000 | 0x0163
+#define NT_STATUS_FLOPPY_VOLUME 0xC0000000 | 0x0164
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0xC0000000 | 0x0165
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0xC0000000 | 0x0166
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0xC0000000 | 0x0167
+#define NT_STATUS_FLOPPY_BAD_REGISTERS 0xC0000000 | 0x0168
+#define NT_STATUS_DISK_RECALIBRATE_FAILED 0xC0000000 | 0x0169
+#define NT_STATUS_DISK_OPERATION_FAILED 0xC0000000 | 0x016a
+#define NT_STATUS_DISK_RESET_FAILED 0xC0000000 | 0x016b
+#define NT_STATUS_SHARED_IRQ_BUSY 0xC0000000 | 0x016c
+#define NT_STATUS_FT_ORPHANING 0xC0000000 | 0x016d
+#define NT_STATUS_PARTITION_FAILURE 0xC0000000 | 0x0172
+#define NT_STATUS_INVALID_BLOCK_LENGTH 0xC0000000 | 0x0173
+#define NT_STATUS_DEVICE_NOT_PARTITIONED 0xC0000000 | 0x0174
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0xC0000000 | 0x0175
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0xC0000000 | 0x0176
+#define NT_STATUS_EOM_OVERFLOW 0xC0000000 | 0x0177
+#define NT_STATUS_NO_MEDIA 0xC0000000 | 0x0178
+#define NT_STATUS_NO_SUCH_MEMBER 0xC0000000 | 0x017a
+#define NT_STATUS_INVALID_MEMBER 0xC0000000 | 0x017b
+#define NT_STATUS_KEY_DELETED 0xC0000000 | 0x017c
+#define NT_STATUS_NO_LOG_SPACE 0xC0000000 | 0x017d
+#define NT_STATUS_TOO_MANY_SIDS 0xC0000000 | 0x017e
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x017f
+#define NT_STATUS_KEY_HAS_CHILDREN 0xC0000000 | 0x0180
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0xC0000000 | 0x0181
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0xC0000000 | 0x0182
+#define NT_STATUS_DRIVER_INTERNAL_ERROR 0xC0000000 | 0x0183
+#define NT_STATUS_INVALID_DEVICE_STATE 0xC0000000 | 0x0184
+#define NT_STATUS_IO_DEVICE_ERROR 0xC0000000 | 0x0185
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0xC0000000 | 0x0186
+#define NT_STATUS_BACKUP_CONTROLLER 0xC0000000 | 0x0187
+#define NT_STATUS_LOG_FILE_FULL 0xC0000000 | 0x0188
+#define NT_STATUS_TOO_LATE 0xC0000000 | 0x0189
+#define NT_STATUS_NO_TRUST_LSA_SECRET 0xC0000000 | 0x018a
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0xC0000000 | 0x018b
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0xC0000000 | 0x018c
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0xC0000000 | 0x018d
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0xC0000000 | 0x018e
+#define NT_STATUS_EVENTLOG_CANT_START 0xC0000000 | 0x018f
+#define NT_STATUS_TRUST_FAILURE 0xC0000000 | 0x0190
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0xC0000000 | 0x0191
+#define NT_STATUS_NETLOGON_NOT_STARTED 0xC0000000 | 0x0192
+#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000000 | 0x0193
+#define NT_STATUS_POSSIBLE_DEADLOCK 0xC0000000 | 0x0194
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0xC0000000 | 0x0195
+#define NT_STATUS_REMOTE_SESSION_LIMIT 0xC0000000 | 0x0196
+#define NT_STATUS_EVENTLOG_FILE_CHANGED 0xC0000000 | 0x0197
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0xC0000000 | 0x0198
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0xC0000000 | 0x0199
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC0000000 | 0x019a
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC0000000 | 0x019b
+#define NT_STATUS_FS_DRIVER_REQUIRED 0xC0000000 | 0x019c
+#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000000 | 0x0202
+#define NT_STATUS_USER_SESSION_DELETED 0xC0000000 | 0x0203
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000000 | 0x0204
+#define NT_STATUS_INSUFF_SERVER_RESOURCES 0xC0000000 | 0x0205
+#define NT_STATUS_INVALID_BUFFER_SIZE 0xC0000000 | 0x0206
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0xC0000000 | 0x0207
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0xC0000000 | 0x0208
+#define NT_STATUS_TOO_MANY_ADDRESSES 0xC0000000 | 0x0209
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0xC0000000 | 0x020a
+#define NT_STATUS_ADDRESS_CLOSED 0xC0000000 | 0x020b
+#define NT_STATUS_CONNECTION_DISCONNECTED 0xC0000000 | 0x020c
+#define NT_STATUS_CONNECTION_RESET 0xC0000000 | 0x020d
+#define NT_STATUS_TOO_MANY_NODES 0xC0000000 | 0x020e
+#define NT_STATUS_TRANSACTION_ABORTED 0xC0000000 | 0x020f
+#define NT_STATUS_TRANSACTION_TIMED_OUT 0xC0000000 | 0x0210
+#define NT_STATUS_TRANSACTION_NO_RELEASE 0xC0000000 | 0x0211
+#define NT_STATUS_TRANSACTION_NO_MATCH 0xC0000000 | 0x0212
+#define NT_STATUS_TRANSACTION_RESPONDED 0xC0000000 | 0x0213
+#define NT_STATUS_TRANSACTION_INVALID_ID 0xC0000000 | 0x0214
+#define NT_STATUS_TRANSACTION_INVALID_TYPE 0xC0000000 | 0x0215
+#define NT_STATUS_NOT_SERVER_SESSION 0xC0000000 | 0x0216
+#define NT_STATUS_NOT_CLIENT_SESSION 0xC0000000 | 0x0217
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0xC0000000 | 0x0218
+#define NT_STATUS_DEBUG_ATTACH_FAILED 0xC0000000 | 0x0219
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0xC0000000 | 0x021a
+#define NT_STATUS_DATA_NOT_ACCEPTED 0xC0000000 | 0x021b
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0xC0000000 | 0x021c
+#define NT_STATUS_VDM_HARD_ERROR 0xC0000000 | 0x021d
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0xC0000000 | 0x021e
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0xC0000000 | 0x021f
+#define NT_STATUS_MAPPED_ALIGNMENT 0xC0000000 | 0x0220
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0xC0000000 | 0x0221
+#define NT_STATUS_LOST_WRITEBEHIND_DATA 0xC0000000 | 0x0222
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0xC0000000 | 0x0223
+#define NT_STATUS_PASSWORD_MUST_CHANGE 0xC0000000 | 0x0224
+#define NT_STATUS_NOT_FOUND 0xC0000000 | 0x0225
+#define NT_STATUS_NOT_TINY_STREAM 0xC0000000 | 0x0226
+#define NT_STATUS_RECOVERY_FAILURE 0xC0000000 | 0x0227
+#define NT_STATUS_STACK_OVERFLOW_READ 0xC0000000 | 0x0228
+#define NT_STATUS_FAIL_CHECK 0xC0000000 | 0x0229
+#define NT_STATUS_DUPLICATE_OBJECTID 0xC0000000 | 0x022a
+#define NT_STATUS_OBJECTID_EXISTS 0xC0000000 | 0x022b
+#define NT_STATUS_CONVERT_TO_LARGE 0xC0000000 | 0x022c
+#define NT_STATUS_RETRY 0xC0000000 | 0x022d
+#define NT_STATUS_FOUND_OUT_OF_SCOPE 0xC0000000 | 0x022e
+#define NT_STATUS_ALLOCATE_BUCKET 0xC0000000 | 0x022f
+#define NT_STATUS_PROPSET_NOT_FOUND 0xC0000000 | 0x0230
+#define NT_STATUS_MARSHALL_OVERFLOW 0xC0000000 | 0x0231
+#define NT_STATUS_INVALID_VARIANT 0xC0000000 | 0x0232
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0xC0000000 | 0x0233
+#define NT_STATUS_ACCOUNT_LOCKED_OUT 0xC0000000 | 0x0234
+#define NT_STATUS_HANDLE_NOT_CLOSABLE 0xC0000000 | 0x0235
+#define NT_STATUS_CONNECTION_REFUSED 0xC0000000 | 0x0236
+#define NT_STATUS_GRACEFUL_DISCONNECT 0xC0000000 | 0x0237
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0xC0000000 | 0x0238
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0xC0000000 | 0x0239
+#define NT_STATUS_CONNECTION_INVALID 0xC0000000 | 0x023a
+#define NT_STATUS_CONNECTION_ACTIVE 0xC0000000 | 0x023b
+#define NT_STATUS_NETWORK_UNREACHABLE 0xC0000000 | 0x023c
+#define NT_STATUS_HOST_UNREACHABLE 0xC0000000 | 0x023d
+#define NT_STATUS_PROTOCOL_UNREACHABLE 0xC0000000 | 0x023e
+#define NT_STATUS_PORT_UNREACHABLE 0xC0000000 | 0x023f
+#define NT_STATUS_REQUEST_ABORTED 0xC0000000 | 0x0240
+#define NT_STATUS_CONNECTION_ABORTED 0xC0000000 | 0x0241
+#define NT_STATUS_BAD_COMPRESSION_BUFFER 0xC0000000 | 0x0242
+#define NT_STATUS_USER_MAPPED_FILE 0xC0000000 | 0x0243
+#define NT_STATUS_AUDIT_FAILED 0xC0000000 | 0x0244
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000000 | 0x0245
+#define NT_STATUS_CONNECTION_COUNT_LIMIT 0xC0000000 | 0x0246
+#define NT_STATUS_LOGIN_TIME_RESTRICTION 0xC0000000 | 0x0247
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0xC0000000 | 0x0248
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0xC0000000 | 0x0249
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0xC0000000 | 0x0250
+#define NT_STATUS_BAD_DLL_ENTRYPOINT 0xC0000000 | 0x0251
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0xC0000000 | 0x0252
+#define NT_STATUS_LPC_REPLY_LOST 0xC0000000 | 0x0253
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 0xC0000000 | 0x0254
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 0xC0000000 | 0x0255
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0xC0000000 | 0x0256
+#define NT_STATUS_PATH_NOT_COVERED 0xC0000000 | 0x0257
+#define NT_STATUS_NO_CALLBACK_ACTIVE 0xC0000000 | 0x0258
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0xC0000000 | 0x0259
+#define NT_STATUS_PWD_TOO_SHORT 0xC0000000 | 0x025a
+#define NT_STATUS_PWD_TOO_RECENT 0xC0000000 | 0x025b
+#define NT_STATUS_PWD_HISTORY_CONFLICT 0xC0000000 | 0x025c
+#define NT_STATUS_PLUGPLAY_NO_DEVICE 0xC0000000 | 0x025e
+#define NT_STATUS_UNSUPPORTED_COMPRESSION 0xC0000000 | 0x025f
+#define NT_STATUS_INVALID_HW_PROFILE 0xC0000000 | 0x0260
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0xC0000000 | 0x0261
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0xC0000000 | 0x0262
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0263
+#define NT_STATUS_RESOURCE_NOT_OWNED 0xC0000000 | 0x0264
+#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266
+#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267
+#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */
+
+#endif /* _NTERR_H */
diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h
new file mode 100644
index 0000000..c377d80
--- /dev/null
+++ b/fs/cifs/ntlmssp.h
@@ -0,0 +1,97 @@
+/*
+ * fs/cifs/ntlmssp.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NTLMSSP_SIGNATURE "NTLMSSP"
+/* Message Types */
+#define NtLmNegotiate cpu_to_le32(1)
+#define NtLmChallenge cpu_to_le32(2)
+#define NtLmAuthenticate cpu_to_le32(3)
+#define UnknownMessage cpu_to_le32(8)
+
+/* Negotiate Flags */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are in unicode */
+#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */
+#define NTLMSSP_REQUEST_TARGET 0x04 /* Server return its auth realm */
+#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signature capability */
+#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */
+#define NTLMSSP_NEGOTIATE_DGRAM 0x0040
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Sign/seal use LM session key */
+#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000
+#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server on same machine */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign for all security levels */
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
+#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
+#define NTLMSSP_TARGET_TYPE_SHARE 0x40000
+#define NTLMSSP_NEGOTIATE_NTLMV2 0x80000
+#define NTLMSSP_REQUEST_INIT_RESP 0x100000
+#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000
+#define NTLMSSP_REQUEST_NOT_NT_KEY 0x400000
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000
+#define NTLMSSP_NEGOTIATE_128 0x20000000
+#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
+#define NTLMSSP_NEGOTIATE_56 0x80000000
+
+/* Although typedefs are not commonly used for structure definitions */
+/* in the Linux kernel, in this particular case they are useful */
+/* to more closely match the standards document for NTLMSSP from */
+/* OpenGroup and to make the code more closely match the standard in */
+/* appearance */
+
+typedef struct _SECURITY_BUFFER {
+ __le16 Length;
+ __le16 MaximumLength;
+ __le32 Buffer; /* offset to buffer */
+} __attribute__((packed)) SECURITY_BUFFER;
+
+typedef struct _NEGOTIATE_MESSAGE {
+ __u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
+ __le32 MessageType; /* 1 */
+ __le32 NegotiateFlags;
+ SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */
+ SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */
+ char DomainString[0];
+ /* followed by WorkstationString */
+} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+
+typedef struct _CHALLENGE_MESSAGE {
+ __u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
+ __le32 MessageType; /* 2 */
+ SECURITY_BUFFER TargetName;
+ __le32 NegotiateFlags;
+ __u8 Challenge[CIFS_CRYPTO_KEY_SIZE];
+ __u8 Reserved[8];
+ SECURITY_BUFFER TargetInfoArray;
+} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+
+typedef struct _AUTHENTICATE_MESSAGE {
+ __u8 Signature[sizeof (NTLMSSP_SIGNATURE)];
+ __le32 MessageType; /* 3 */
+ SECURITY_BUFFER LmChallengeResponse;
+ SECURITY_BUFFER NtChallengeResponse;
+ SECURITY_BUFFER DomainName;
+ SECURITY_BUFFER UserName;
+ SECURITY_BUFFER WorkstationName;
+ SECURITY_BUFFER SessionKey;
+ __le32 NegotiateFlags;
+ char UserString[0];
+} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
new file mode 100644
index 0000000..9f51f9b
--- /dev/null
+++ b/fs/cifs/readdir.c
@@ -0,0 +1,1117 @@
+/*
+ * fs/cifs/readdir.c
+ *
+ * Directory search handling
+ *
+ * Copyright (C) International Business Machines Corp., 2004, 2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/stat.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifsfs.h"
+
+#ifdef CONFIG_CIFS_DEBUG2
+static void dump_cifs_file_struct(struct file *file, char *label)
+{
+ struct cifsFileInfo *cf;
+
+ if (file) {
+ cf = file->private_data;
+ if (cf == NULL) {
+ cFYI(1, ("empty cifs private file data"));
+ return;
+ }
+ if (cf->invalidHandle)
+ cFYI(1, ("invalid handle"));
+ if (cf->srch_inf.endOfSearch)
+ cFYI(1, ("end of search"));
+ if (cf->srch_inf.emptyDir)
+ cFYI(1, ("empty dir"));
+ }
+}
+#else
+static inline void dump_cifs_file_struct(struct file *file, char *label)
+{
+}
+#endif /* DEBUG2 */
+
+/* Returns one if new inode created (which therefore needs to be hashed) */
+/* Might check in the future if inode number changed so we can rehash inode */
+static int construct_dentry(struct qstr *qstring, struct file *file,
+ struct inode **ptmp_inode, struct dentry **pnew_dentry)
+{
+ struct dentry *tmp_dentry;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ int rc = 0;
+
+ cFYI(1, ("For %s", qstring->name));
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+
+ qstring->hash = full_name_hash(qstring->name, qstring->len);
+ tmp_dentry = d_lookup(file->f_path.dentry, qstring);
+ if (tmp_dentry) {
+ cFYI(0, ("existing dentry with inode 0x%p",
+ tmp_dentry->d_inode));
+ *ptmp_inode = tmp_dentry->d_inode;
+/* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/
+ if (*ptmp_inode == NULL) {
+ *ptmp_inode = new_inode(file->f_path.dentry->d_sb);
+ if (*ptmp_inode == NULL)
+ return rc;
+ rc = 1;
+ }
+ if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME)
+ (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME;
+ } else {
+ tmp_dentry = d_alloc(file->f_path.dentry, qstring);
+ if (tmp_dentry == NULL) {
+ cERROR(1, ("Failed allocating dentry"));
+ *ptmp_inode = NULL;
+ return rc;
+ }
+
+ *ptmp_inode = new_inode(file->f_path.dentry->d_sb);
+ if (pTcon->nocase)
+ tmp_dentry->d_op = &cifs_ci_dentry_ops;
+ else
+ tmp_dentry->d_op = &cifs_dentry_ops;
+ if (*ptmp_inode == NULL)
+ return rc;
+ if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME)
+ (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME;
+ rc = 2;
+ }
+
+ tmp_dentry->d_time = jiffies;
+ *pnew_dentry = tmp_dentry;
+ return rc;
+}
+
+static void AdjustForTZ(struct cifsTconInfo *tcon, struct inode *inode)
+{
+ if ((tcon) && (tcon->ses) && (tcon->ses->server)) {
+ inode->i_ctime.tv_sec += tcon->ses->server->timeAdj;
+ inode->i_mtime.tv_sec += tcon->ses->server->timeAdj;
+ inode->i_atime.tv_sec += tcon->ses->server->timeAdj;
+ }
+ return;
+}
+
+
+static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
+ char *buf, unsigned int *pobject_type, int isNewInode)
+{
+ loff_t local_size;
+ struct timespec local_mtime;
+
+ struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
+ __u32 attr;
+ __u64 allocation_size;
+ __u64 end_of_file;
+ umode_t default_mode;
+
+ /* save mtime and size */
+ local_mtime = tmp_inode->i_mtime;
+ local_size = tmp_inode->i_size;
+
+ if (new_buf_type) {
+ FILE_DIRECTORY_INFO *pfindData = (FILE_DIRECTORY_INFO *)buf;
+
+ attr = le32_to_cpu(pfindData->ExtFileAttributes);
+ allocation_size = le64_to_cpu(pfindData->AllocationSize);
+ end_of_file = le64_to_cpu(pfindData->EndOfFile);
+ tmp_inode->i_atime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
+ tmp_inode->i_mtime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
+ tmp_inode->i_ctime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
+ } else { /* legacy, OS2 and DOS style */
+/* struct timespec ts;*/
+ FIND_FILE_STANDARD_INFO *pfindData =
+ (FIND_FILE_STANDARD_INFO *)buf;
+
+ tmp_inode->i_mtime = cnvrtDosUnixTm(
+ le16_to_cpu(pfindData->LastWriteDate),
+ le16_to_cpu(pfindData->LastWriteTime));
+ tmp_inode->i_atime = cnvrtDosUnixTm(
+ le16_to_cpu(pfindData->LastAccessDate),
+ le16_to_cpu(pfindData->LastAccessTime));
+ tmp_inode->i_ctime = cnvrtDosUnixTm(
+ le16_to_cpu(pfindData->LastWriteDate),
+ le16_to_cpu(pfindData->LastWriteTime));
+ AdjustForTZ(cifs_sb->tcon, tmp_inode);
+ attr = le16_to_cpu(pfindData->Attributes);
+ allocation_size = le32_to_cpu(pfindData->AllocationSize);
+ end_of_file = le32_to_cpu(pfindData->DataSize);
+ }
+
+ /* Linux can not store file creation time unfortunately so ignore it */
+
+ cifsInfo->cifsAttrs = attr;
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ /* get more accurate mode via ACL - so force inode refresh */
+ cifsInfo->time = 0;
+ } else
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+ cifsInfo->time = jiffies;
+
+ /* treat dos attribute of read-only as read-only mode bit e.g. 555? */
+ /* 2767 perms - indicate mandatory locking */
+ /* BB fill in uid and gid here? with help from winbind?
+ or retrieve from NTFS stream extended attribute */
+ if (atomic_read(&cifsInfo->inUse) == 0) {
+ tmp_inode->i_uid = cifs_sb->mnt_uid;
+ tmp_inode->i_gid = cifs_sb->mnt_gid;
+ }
+
+ if (attr & ATTR_DIRECTORY)
+ default_mode = cifs_sb->mnt_dir_mode;
+ else
+ default_mode = cifs_sb->mnt_file_mode;
+
+ /* set initial permissions */
+ if ((atomic_read(&cifsInfo->inUse) == 0) ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ tmp_inode->i_mode = default_mode;
+ else {
+ /* just reenable write bits if !ATTR_READONLY */
+ if ((tmp_inode->i_mode & S_IWUGO) == 0 &&
+ (attr & ATTR_READONLY) == 0)
+ tmp_inode->i_mode |= (S_IWUGO & default_mode);
+
+ tmp_inode->i_mode &= ~S_IFMT;
+ }
+
+ /* clear write bits if ATTR_READONLY is set */
+ if (attr & ATTR_READONLY)
+ tmp_inode->i_mode &= ~S_IWUGO;
+
+ /* set inode type */
+ if ((attr & ATTR_SYSTEM) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
+ if (end_of_file == 0) {
+ tmp_inode->i_mode |= S_IFIFO;
+ *pobject_type = DT_FIFO;
+ } else {
+ /*
+ * trying to get the type can be slow, so just call
+ * this a regular file for now, and mark for reval
+ */
+ tmp_inode->i_mode |= S_IFREG;
+ *pobject_type = DT_REG;
+ cifsInfo->time = 0;
+ }
+ } else {
+ if (attr & ATTR_DIRECTORY) {
+ tmp_inode->i_mode |= S_IFDIR;
+ *pobject_type = DT_DIR;
+ } else {
+ tmp_inode->i_mode |= S_IFREG;
+ *pobject_type = DT_REG;
+ }
+ }
+
+ /* can not fill in nlink here as in qpathinfo version and Unx search */
+ if (atomic_read(&cifsInfo->inUse) == 0)
+ atomic_set(&cifsInfo->inUse, 1);
+
+ spin_lock(&tmp_inode->i_lock);
+ if (is_size_safe_to_change(cifsInfo, end_of_file)) {
+ /* can not safely change the file size here if the
+ client is writing to it due to potential races */
+ i_size_write(tmp_inode, end_of_file);
+
+ /* 512 bytes (2**9) is the fake blocksize that must be used */
+ /* for this calculation, even though the reported blocksize is larger */
+ tmp_inode->i_blocks = (512 - 1 + allocation_size) >> 9;
+ }
+ spin_unlock(&tmp_inode->i_lock);
+
+ if (allocation_size < end_of_file)
+ cFYI(1, ("May be sparse file, allocation less than file size"));
+ cFYI(1, ("File Size %ld and blocks %llu",
+ (unsigned long)tmp_inode->i_size,
+ (unsigned long long)tmp_inode->i_blocks));
+ if (S_ISREG(tmp_inode->i_mode)) {
+ cFYI(1, ("File inode"));
+ tmp_inode->i_op = &cifs_file_inode_ops;
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
+ else
+ tmp_inode->i_fop = &cifs_file_direct_ops;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ tmp_inode->i_fop = &cifs_file_nobrl_ops;
+ else
+ tmp_inode->i_fop = &cifs_file_ops;
+
+ if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
+ (cifs_sb->tcon->ses->server->maxBuf <
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
+ tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ tmp_inode->i_data.a_ops = &cifs_addr_ops;
+
+ if (isNewInode)
+ return; /* No sense invalidating pages for new inode
+ since have not started caching readahead file
+ data yet */
+
+ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
+ (local_size == tmp_inode->i_size)) {
+ cFYI(1, ("inode exists but unchanged"));
+ } else {
+ /* file may have changed on server */
+ cFYI(1, ("invalidate inode, readdir detected change"));
+ invalidate_remote_inode(tmp_inode);
+ }
+ } else if (S_ISDIR(tmp_inode->i_mode)) {
+ cFYI(1, ("Directory inode"));
+ tmp_inode->i_op = &cifs_dir_inode_ops;
+ tmp_inode->i_fop = &cifs_dir_ops;
+ } else if (S_ISLNK(tmp_inode->i_mode)) {
+ cFYI(1, ("Symbolic Link inode"));
+ tmp_inode->i_op = &cifs_symlink_inode_ops;
+ } else {
+ cFYI(1, ("Init special inode"));
+ init_special_inode(tmp_inode, tmp_inode->i_mode,
+ tmp_inode->i_rdev);
+ }
+}
+
+static void unix_fill_in_inode(struct inode *tmp_inode,
+ FILE_UNIX_INFO *pfindData, unsigned int *pobject_type, int isNewInode)
+{
+ loff_t local_size;
+ struct timespec local_mtime;
+
+ struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
+
+ __u32 type = le32_to_cpu(pfindData->Type);
+ __u64 num_of_bytes = le64_to_cpu(pfindData->NumOfBytes);
+ __u64 end_of_file = le64_to_cpu(pfindData->EndOfFile);
+ cifsInfo->time = jiffies;
+ atomic_inc(&cifsInfo->inUse);
+
+ /* save mtime and size */
+ local_mtime = tmp_inode->i_mtime;
+ local_size = tmp_inode->i_size;
+
+ tmp_inode->i_atime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
+ tmp_inode->i_mtime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastModificationTime));
+ tmp_inode->i_ctime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastStatusChange));
+
+ tmp_inode->i_mode = le64_to_cpu(pfindData->Permissions);
+ /* since we set the inode type below we need to mask off type
+ to avoid strange results if bits above were corrupt */
+ tmp_inode->i_mode &= ~S_IFMT;
+ if (type == UNIX_FILE) {
+ *pobject_type = DT_REG;
+ tmp_inode->i_mode |= S_IFREG;
+ } else if (type == UNIX_SYMLINK) {
+ *pobject_type = DT_LNK;
+ tmp_inode->i_mode |= S_IFLNK;
+ } else if (type == UNIX_DIR) {
+ *pobject_type = DT_DIR;
+ tmp_inode->i_mode |= S_IFDIR;
+ } else if (type == UNIX_CHARDEV) {
+ *pobject_type = DT_CHR;
+ tmp_inode->i_mode |= S_IFCHR;
+ tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor),
+ le64_to_cpu(pfindData->DevMinor) & MINORMASK);
+ } else if (type == UNIX_BLOCKDEV) {
+ *pobject_type = DT_BLK;
+ tmp_inode->i_mode |= S_IFBLK;
+ tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor),
+ le64_to_cpu(pfindData->DevMinor) & MINORMASK);
+ } else if (type == UNIX_FIFO) {
+ *pobject_type = DT_FIFO;
+ tmp_inode->i_mode |= S_IFIFO;
+ } else if (type == UNIX_SOCKET) {
+ *pobject_type = DT_SOCK;
+ tmp_inode->i_mode |= S_IFSOCK;
+ } else {
+ /* safest to just call it a file */
+ *pobject_type = DT_REG;
+ tmp_inode->i_mode |= S_IFREG;
+ cFYI(1, ("unknown inode type %d", type));
+ }
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
+ tmp_inode->i_uid = cifs_sb->mnt_uid;
+ else
+ tmp_inode->i_uid = le64_to_cpu(pfindData->Uid);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
+ tmp_inode->i_gid = cifs_sb->mnt_gid;
+ else
+ tmp_inode->i_gid = le64_to_cpu(pfindData->Gid);
+ tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks);
+
+ spin_lock(&tmp_inode->i_lock);
+ if (is_size_safe_to_change(cifsInfo, end_of_file)) {
+ /* can not safely change the file size here if the
+ client is writing to it due to potential races */
+ i_size_write(tmp_inode, end_of_file);
+
+ /* 512 bytes (2**9) is the fake blocksize that must be used */
+ /* for this calculation, not the real blocksize */
+ tmp_inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
+ }
+ spin_unlock(&tmp_inode->i_lock);
+
+ if (S_ISREG(tmp_inode->i_mode)) {
+ cFYI(1, ("File inode"));
+ tmp_inode->i_op = &cifs_file_inode_ops;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
+ else
+ tmp_inode->i_fop = &cifs_file_direct_ops;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
+ tmp_inode->i_fop = &cifs_file_nobrl_ops;
+ else
+ tmp_inode->i_fop = &cifs_file_ops;
+
+ if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
+ (cifs_sb->tcon->ses->server->maxBuf <
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
+ tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ tmp_inode->i_data.a_ops = &cifs_addr_ops;
+
+ if (isNewInode)
+ return; /* No sense invalidating pages for new inode
+ since we have not started caching readahead
+ file data for it yet */
+
+ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
+ (local_size == tmp_inode->i_size)) {
+ cFYI(1, ("inode exists but unchanged"));
+ } else {
+ /* file may have changed on server */
+ cFYI(1, ("invalidate inode, readdir detected change"));
+ invalidate_remote_inode(tmp_inode);
+ }
+ } else if (S_ISDIR(tmp_inode->i_mode)) {
+ cFYI(1, ("Directory inode"));
+ tmp_inode->i_op = &cifs_dir_inode_ops;
+ tmp_inode->i_fop = &cifs_dir_ops;
+ } else if (S_ISLNK(tmp_inode->i_mode)) {
+ cFYI(1, ("Symbolic Link inode"));
+ tmp_inode->i_op = &cifs_symlink_inode_ops;
+/* tmp_inode->i_fop = *//* do not need to set to anything */
+ } else {
+ cFYI(1, ("Special inode"));
+ init_special_inode(tmp_inode, tmp_inode->i_mode,
+ tmp_inode->i_rdev);
+ }
+}
+
+static int initiate_cifs_search(const int xid, struct file *file)
+{
+ int rc = 0;
+ char *full_path;
+ struct cifsFileInfo *cifsFile;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+
+ if (file->private_data == NULL) {
+ file->private_data =
+ kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+ }
+
+ if (file->private_data == NULL)
+ return -ENOMEM;
+ cifsFile = file->private_data;
+ cifsFile->invalidHandle = true;
+ cifsFile->srch_inf.endOfSearch = false;
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ if (cifs_sb == NULL)
+ return -EINVAL;
+
+ pTcon = cifs_sb->tcon;
+ if (pTcon == NULL)
+ return -EINVAL;
+
+ full_path = build_path_from_dentry(file->f_path.dentry);
+
+ if (full_path == NULL)
+ return -ENOMEM;
+
+ cFYI(1, ("Full path: %s start at: %lld", full_path, file->f_pos));
+
+ffirst_retry:
+ /* test for Unix extensions */
+ /* but now check for them on the share/mount not on the SMB session */
+/* if (pTcon->ses->capabilities & CAP_UNIX) { */
+ if (pTcon->unix_ext)
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
+ else if ((pTcon->ses->capabilities &
+ (CAP_NT_SMBS | CAP_NT_FIND)) == 0) {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
+ } else /* not srvinos - BB fixme add check for backlevel? */ {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
+ }
+
+ rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls,
+ &cifsFile->netfid, &cifsFile->srch_inf,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
+ if (rc == 0)
+ cifsFile->invalidHandle = false;
+ if ((rc == -EOPNOTSUPP) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
+ cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
+ goto ffirst_retry;
+ }
+ kfree(full_path);
+ return rc;
+}
+
+/* return length of unicode string in bytes */
+static int cifs_unicode_bytelen(char *str)
+{
+ int len;
+ __le16 *ustr = (__le16 *)str;
+
+ for (len = 0; len <= PATH_MAX; len++) {
+ if (ustr[len] == 0)
+ return len << 1;
+ }
+ cFYI(1, ("Unicode string longer than PATH_MAX found"));
+ return len << 1;
+}
+
+static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
+{
+ char *new_entry;
+ FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
+
+ if (level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO *pfData;
+ pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
+
+ new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
+ pfData->FileNameLength;
+ } else
+ new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
+ cFYI(1, ("new entry %p old entry %p", new_entry, old_entry));
+ /* validate that new_entry is not past end of SMB */
+ if (new_entry >= end_of_smb) {
+ cERROR(1,
+ ("search entry %p began after end of SMB %p old entry %p",
+ new_entry, end_of_smb, old_entry));
+ return NULL;
+ } else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
+ (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
+ || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
+ (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) {
+ cERROR(1, ("search entry %p extends after end of SMB %p",
+ new_entry, end_of_smb));
+ return NULL;
+ } else
+ return new_entry;
+
+}
+
+#define UNICODE_DOT cpu_to_le16(0x2e)
+
+/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
+static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
+{
+ int rc = 0;
+ char *filename = NULL;
+ int len = 0;
+
+ if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
+ FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ if (cfile->srch_inf.unicode) {
+ len = cifs_unicode_bytelen(filename);
+ } else {
+ /* BB should we make this strnlen of PATH_MAX? */
+ len = strnlen(filename, 5);
+ }
+ } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
+ FILE_DIRECTORY_INFO *pFindData =
+ (FILE_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (cfile->srch_inf.info_level ==
+ SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
+ FILE_FULL_DIRECTORY_INFO *pFindData =
+ (FILE_FULL_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (cfile->srch_inf.info_level ==
+ SMB_FIND_FILE_ID_FULL_DIR_INFO) {
+ SEARCH_ID_FULL_DIR_INFO *pFindData =
+ (SEARCH_ID_FULL_DIR_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (cfile->srch_inf.info_level ==
+ SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+ FILE_BOTH_DIRECTORY_INFO *pFindData =
+ (FILE_BOTH_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO *pFindData =
+ (FIND_FILE_STANDARD_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = pFindData->FileNameLength;
+ } else {
+ cFYI(1, ("Unknown findfirst level %d",
+ cfile->srch_inf.info_level));
+ }
+
+ if (filename) {
+ if (cfile->srch_inf.unicode) {
+ __le16 *ufilename = (__le16 *)filename;
+ if (len == 2) {
+ /* check for . */
+ if (ufilename[0] == UNICODE_DOT)
+ rc = 1;
+ } else if (len == 4) {
+ /* check for .. */
+ if ((ufilename[0] == UNICODE_DOT)
+ && (ufilename[1] == UNICODE_DOT))
+ rc = 2;
+ }
+ } else /* ASCII */ {
+ if (len == 1) {
+ if (filename[0] == '.')
+ rc = 1;
+ } else if (len == 2) {
+ if ((filename[0] == '.') && (filename[1] == '.'))
+ rc = 2;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/* Check if directory that we are searching has changed so we can decide
+ whether we can use the cached search results from the previous search */
+static int is_dir_changed(struct file *file)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
+
+ if (cifsInfo->time == 0)
+ return 1; /* directory was changed, perhaps due to unlink */
+ else
+ return 0;
+
+}
+
+static int cifs_save_resume_key(const char *current_entry,
+ struct cifsFileInfo *cifsFile)
+{
+ int rc = 0;
+ unsigned int len = 0;
+ __u16 level;
+ char *filename;
+
+ if ((cifsFile == NULL) || (current_entry == NULL))
+ return -EINVAL;
+
+ level = cifsFile->srch_inf.info_level;
+
+ if (level == SMB_FIND_FILE_UNIX) {
+ FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
+
+ filename = &pFindData->FileName[0];
+ if (cifsFile->srch_inf.unicode) {
+ len = cifs_unicode_bytelen(filename);
+ } else {
+ /* BB should we make this strnlen of PATH_MAX? */
+ len = strnlen(filename, PATH_MAX);
+ }
+ cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
+ } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
+ FILE_DIRECTORY_INFO *pFindData =
+ (FILE_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+ } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
+ FILE_FULL_DIRECTORY_INFO *pFindData =
+ (FILE_FULL_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+ } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
+ SEARCH_ID_FULL_DIR_INFO *pFindData =
+ (SEARCH_ID_FULL_DIR_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+ } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+ FILE_BOTH_DIRECTORY_INFO *pFindData =
+ (FILE_BOTH_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+ } else if (level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO *pFindData =
+ (FIND_FILE_STANDARD_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ /* one byte length, no name conversion */
+ len = (unsigned int)pFindData->FileNameLength;
+ cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
+ } else {
+ cFYI(1, ("Unknown findfirst level %d", level));
+ return -EINVAL;
+ }
+ cifsFile->srch_inf.resume_name_len = len;
+ cifsFile->srch_inf.presume_name = filename;
+ return rc;
+}
+
+/* find the corresponding entry in the search */
+/* Note that the SMB server returns search entries for . and .. which
+ complicates logic here if we choose to parse for them and we do not
+ assume that they are located in the findfirst return buffer.*/
+/* We start counting in the buffer with entry 2 and increment for every
+ entry (do not increment for . or .. entry) */
+static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
+ struct file *file, char **ppCurrentEntry, int *num_to_ret)
+{
+ int rc = 0;
+ int pos_in_buf = 0;
+ loff_t first_entry_in_buffer;
+ loff_t index_to_find = file->f_pos;
+ struct cifsFileInfo *cifsFile = file->private_data;
+ /* check if index in the buffer */
+
+ if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
+ (num_to_ret == NULL))
+ return -ENOENT;
+
+ *ppCurrentEntry = NULL;
+ first_entry_in_buffer =
+ cifsFile->srch_inf.index_of_last_entry -
+ cifsFile->srch_inf.entries_in_buffer;
+
+ /* if first entry in buf is zero then is first buffer
+ in search response data which means it is likely . and ..
+ will be in this buffer, although some servers do not return
+ . and .. for the root of a drive and for those we need
+ to start two entries earlier */
+
+ dump_cifs_file_struct(file, "In fce ");
+ if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
+ is_dir_changed(file)) ||
+ (index_to_find < first_entry_in_buffer)) {
+ /* close and restart search */
+ cFYI(1, ("search backing up - close and restart search"));
+ write_lock(&GlobalSMBSeslock);
+ if (!cifsFile->srch_inf.endOfSearch &&
+ !cifsFile->invalidHandle) {
+ cifsFile->invalidHandle = true;
+ write_unlock(&GlobalSMBSeslock);
+ CIFSFindClose(xid, pTcon, cifsFile->netfid);
+ } else
+ write_unlock(&GlobalSMBSeslock);
+ if (cifsFile->srch_inf.ntwrk_buf_start) {
+ cFYI(1, ("freeing SMB ff cache buf on search rewind"));
+ if (cifsFile->srch_inf.smallBuf)
+ cifs_small_buf_release(cifsFile->srch_inf.
+ ntwrk_buf_start);
+ else
+ cifs_buf_release(cifsFile->srch_inf.
+ ntwrk_buf_start);
+ cifsFile->srch_inf.ntwrk_buf_start = NULL;
+ }
+ rc = initiate_cifs_search(xid, file);
+ if (rc) {
+ cFYI(1, ("error %d reinitiating a search on rewind",
+ rc));
+ return rc;
+ }
+ cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
+ }
+
+ while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
+ (rc == 0) && !cifsFile->srch_inf.endOfSearch) {
+ cFYI(1, ("calling findnext2"));
+ rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
+ &cifsFile->srch_inf);
+ cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
+ if (rc)
+ return -ENOENT;
+ }
+ if (index_to_find < cifsFile->srch_inf.index_of_last_entry) {
+ /* we found the buffer that contains the entry */
+ /* scan and find it */
+ int i;
+ char *current_entry;
+ char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
+ smbCalcSize((struct smb_hdr *)
+ cifsFile->srch_inf.ntwrk_buf_start);
+
+ current_entry = cifsFile->srch_inf.srch_entries_start;
+ first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
+ - cifsFile->srch_inf.entries_in_buffer;
+ pos_in_buf = index_to_find - first_entry_in_buffer;
+ cFYI(1, ("found entry - pos_in_buf %d", pos_in_buf));
+
+ for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) {
+ /* go entry by entry figuring out which is first */
+ current_entry = nxt_dir_entry(current_entry, end_of_smb,
+ cifsFile->srch_inf.info_level);
+ }
+ if ((current_entry == NULL) && (i < pos_in_buf)) {
+ /* BB fixme - check if we should flag this error */
+ cERROR(1, ("reached end of buf searching for pos in buf"
+ " %d index to find %lld rc %d",
+ pos_in_buf, index_to_find, rc));
+ }
+ rc = 0;
+ *ppCurrentEntry = current_entry;
+ } else {
+ cFYI(1, ("index not in buffer - could not findnext into it"));
+ return 0;
+ }
+
+ if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
+ cFYI(1, ("can not return entries pos_in_buf beyond last"));
+ *num_to_ret = 0;
+ } else
+ *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;
+
+ return rc;
+}
+
+/* inode num, inode type and filename returned */
+static int cifs_get_name_from_search_buf(struct qstr *pqst,
+ char *current_entry, __u16 level, unsigned int unicode,
+ struct cifs_sb_info *cifs_sb, int max_len, ino_t *pinum)
+{
+ int rc = 0;
+ unsigned int len = 0;
+ char *filename;
+ struct nls_table *nlt = cifs_sb->local_nls;
+
+ *pinum = 0;
+
+ if (level == SMB_FIND_FILE_UNIX) {
+ FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
+
+ filename = &pFindData->FileName[0];
+ if (unicode) {
+ len = cifs_unicode_bytelen(filename);
+ } else {
+ /* BB should we make this strnlen of PATH_MAX? */
+ len = strnlen(filename, PATH_MAX);
+ }
+
+ /* BB fixme - hash low and high 32 bits if not 64 bit arch BB */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
+ *pinum = pFindData->UniqueId;
+ } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
+ FILE_DIRECTORY_INFO *pFindData =
+ (FILE_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
+ FILE_FULL_DIRECTORY_INFO *pFindData =
+ (FILE_FULL_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
+ SEARCH_ID_FULL_DIR_INFO *pFindData =
+ (SEARCH_ID_FULL_DIR_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ *pinum = pFindData->UniqueId;
+ } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+ FILE_BOTH_DIRECTORY_INFO *pFindData =
+ (FILE_BOTH_DIRECTORY_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
+ } else if (level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO *pFindData =
+ (FIND_FILE_STANDARD_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ /* one byte length, no name conversion */
+ len = (unsigned int)pFindData->FileNameLength;
+ } else {
+ cFYI(1, ("Unknown findfirst level %d", level));
+ return -EINVAL;
+ }
+
+ if (len > max_len) {
+ cERROR(1, ("bad search response length %d past smb end", len));
+ return -EINVAL;
+ }
+
+ if (unicode) {
+ /* BB fixme - test with long names */
+ /* Note converted filename can be longer than in unicode */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
+ pqst->len = cifs_convertUCSpath((char *)pqst->name,
+ (__le16 *)filename, len/2, nlt);
+ else
+ pqst->len = cifs_strfromUCS_le((char *)pqst->name,
+ (__le16 *)filename, len/2, nlt);
+ } else {
+ pqst->name = filename;
+ pqst->len = len;
+ }
+ pqst->hash = full_name_hash(pqst->name, pqst->len);
+/* cFYI(1, ("filldir on %s",pqst->name)); */
+ return rc;
+}
+
+static int cifs_filldir(char *pfindEntry, struct file *file,
+ filldir_t filldir, void *direntry, char *scratch_buf, int max_len)
+{
+ int rc = 0;
+ struct qstr qstring;
+ struct cifsFileInfo *pCifsF;
+ unsigned int obj_type;
+ ino_t inum;
+ struct cifs_sb_info *cifs_sb;
+ struct inode *tmp_inode;
+ struct dentry *tmp_dentry;
+
+ /* get filename and len into qstring */
+ /* get dentry */
+ /* decide whether to create and populate ionde */
+ if ((direntry == NULL) || (file == NULL))
+ return -EINVAL;
+
+ pCifsF = file->private_data;
+
+ if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
+ return -ENOENT;
+
+ rc = cifs_entry_is_dot(pfindEntry, pCifsF);
+ /* skip . and .. since we added them first */
+ if (rc != 0)
+ return 0;
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+
+ qstring.name = scratch_buf;
+ rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
+ pCifsF->srch_inf.info_level,
+ pCifsF->srch_inf.unicode, cifs_sb,
+ max_len,
+ &inum /* returned */);
+
+ if (rc)
+ return rc;
+
+ rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry);
+ if ((tmp_inode == NULL) || (tmp_dentry == NULL))
+ return -ENOMEM;
+
+ if (rc) {
+ /* inode created, we need to hash it with right inode number */
+ if (inum != 0) {
+ /* BB fixme - hash the 2 32 quantities bits together if
+ * necessary BB */
+ tmp_inode->i_ino = inum;
+ }
+ insert_inode_hash(tmp_inode);
+ }
+
+ /* we pass in rc below, indicating whether it is a new inode,
+ so we can figure out whether to invalidate the inode cached
+ data if the file has changed */
+ if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
+ unix_fill_in_inode(tmp_inode,
+ (FILE_UNIX_INFO *)pfindEntry,
+ &obj_type, rc);
+ else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
+ fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */,
+ pfindEntry, &obj_type, rc);
+ else
+ fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc);
+
+ if (rc) /* new inode - needs to be tied to dentry */ {
+ d_instantiate(tmp_dentry, tmp_inode);
+ if (rc == 2)
+ d_rehash(tmp_dentry);
+ }
+
+
+ rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
+ tmp_inode->i_ino, obj_type);
+ if (rc) {
+ cFYI(1, ("filldir rc = %d", rc));
+ /* we can not return filldir errors to the caller
+ since they are "normal" when the stat blocksize
+ is too small - we return remapped error instead */
+ rc = -EOVERFLOW;
+ }
+
+ dput(tmp_dentry);
+ return rc;
+}
+
+
+int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
+{
+ int rc = 0;
+ int xid, i;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct cifsFileInfo *cifsFile = NULL;
+ char *current_entry;
+ int num_to_fill = 0;
+ char *tmp_buf = NULL;
+ char *end_of_smb;
+ int max_len;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ pTcon = cifs_sb->tcon;
+ if (pTcon == NULL)
+ return -EINVAL;
+
+ switch ((int) file->f_pos) {
+ case 0:
+ if (filldir(direntry, ".", 1, file->f_pos,
+ file->f_path.dentry->d_inode->i_ino, DT_DIR) < 0) {
+ cERROR(1, ("Filldir for current dir failed"));
+ rc = -ENOMEM;
+ break;
+ }
+ file->f_pos++;
+ case 1:
+ if (filldir(direntry, "..", 2, file->f_pos,
+ file->f_path.dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
+ cERROR(1, ("Filldir for parent dir failed"));
+ rc = -ENOMEM;
+ break;
+ }
+ file->f_pos++;
+ default:
+ /* 1) If search is active,
+ is in current search buffer?
+ if it before then restart search
+ if after then keep searching till find it */
+
+ if (file->private_data == NULL) {
+ rc = initiate_cifs_search(xid, file);
+ cFYI(1, ("initiate cifs search rc %d", rc));
+ if (rc) {
+ FreeXid(xid);
+ return rc;
+ }
+ }
+ if (file->private_data == NULL) {
+ rc = -EINVAL;
+ FreeXid(xid);
+ return rc;
+ }
+ cifsFile = file->private_data;
+ if (cifsFile->srch_inf.endOfSearch) {
+ if (cifsFile->srch_inf.emptyDir) {
+ cFYI(1, ("End of search, empty dir"));
+ rc = 0;
+ break;
+ }
+ } /* else {
+ cifsFile->invalidHandle = true;
+ CIFSFindClose(xid, pTcon, cifsFile->netfid);
+ } */
+
+ rc = find_cifs_entry(xid, pTcon, file,
+ &current_entry, &num_to_fill);
+ if (rc) {
+ cFYI(1, ("fce error %d", rc));
+ goto rddir2_exit;
+ } else if (current_entry != NULL) {
+ cFYI(1, ("entry %lld found", file->f_pos));
+ } else {
+ cFYI(1, ("could not find entry"));
+ goto rddir2_exit;
+ }
+ cFYI(1, ("loop through %d times filling dir for net buf %p",
+ num_to_fill, cifsFile->srch_inf.ntwrk_buf_start));
+ max_len = smbCalcSize((struct smb_hdr *)
+ cifsFile->srch_inf.ntwrk_buf_start);
+ end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
+
+ /* To be safe - for UCS to UTF-8 with strings loaded
+ with the rare long characters alloc more to account for
+ such multibyte target UTF-8 characters. cifs_unicode.c,
+ which actually does the conversion, has the same limit */
+ tmp_buf = kmalloc((2 * NAME_MAX) + 4, GFP_KERNEL);
+ for (i = 0; (i < num_to_fill) && (rc == 0); i++) {
+ if (current_entry == NULL) {
+ /* evaluate whether this case is an error */
+ cERROR(1, ("past SMB end, num to fill %d i %d",
+ num_to_fill, i));
+ break;
+ }
+ /* if buggy server returns . and .. late do
+ we want to check for that here? */
+ rc = cifs_filldir(current_entry, file,
+ filldir, direntry, tmp_buf, max_len);
+ if (rc == -EOVERFLOW) {
+ rc = 0;
+ break;
+ }
+
+ file->f_pos++;
+ if (file->f_pos ==
+ cifsFile->srch_inf.index_of_last_entry) {
+ cFYI(1, ("last entry in buf at pos %lld %s",
+ file->f_pos, tmp_buf));
+ cifs_save_resume_key(current_entry, cifsFile);
+ break;
+ } else
+ current_entry =
+ nxt_dir_entry(current_entry, end_of_smb,
+ cifsFile->srch_inf.info_level);
+ }
+ kfree(tmp_buf);
+ break;
+ } /* end switch */
+
+rddir2_exit:
+ FreeXid(xid);
+ return rc;
+}
diff --git a/fs/cifs/rfc1002pdu.h b/fs/cifs/rfc1002pdu.h
new file mode 100644
index 0000000..8b69fcc
--- /dev/null
+++ b/fs/cifs/rfc1002pdu.h
@@ -0,0 +1,74 @@
+/*
+ * fs/cifs/rfc1002pdu.h
+ *
+ * Protocol Data Unit definitions for RFC 1001/1002 support
+ *
+ * Copyright (c) International Business Machines Corp., 2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */
+
+ /* RFC 1002 session packet types */
+#define RFC1002_SESSION_MESSAGE 0x00
+#define RFC1002_SESSION_REQUEST 0x81
+#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82
+#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83
+#define RFC1002_RETARGET_SESSION_RESPONSE 0x84
+#define RFC1002_SESSION_KEEP_ALIVE 0x85
+
+ /* RFC 1002 flags (only one defined */
+#define RFC1002_LENGTH_EXTEND 0x80 /* high order bit of length (ie +64K) */
+
+struct rfc1002_session_packet {
+ __u8 type;
+ __u8 flags;
+ __u16 length;
+ union {
+ struct {
+ __u8 called_len;
+ __u8 called_name[32];
+ __u8 scope1; /* null */
+ __u8 calling_len;
+ __u8 calling_name[32];
+ __u8 scope2; /* null */
+ } __attribute__((packed)) session_req;
+ struct {
+ __u32 retarget_ip_addr;
+ __u16 port;
+ } __attribute__((packed)) retarget_resp;
+ __u8 neg_ses_resp_error_code;
+ /* POSITIVE_SESSION_RESPONSE packet does not include trailer.
+ SESSION_KEEP_ALIVE packet also does not include a trailer.
+ Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */
+ } __attribute__((packed)) trailer;
+} __attribute__((packed));
+
+/* Negative Session Response error codes */
+#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */
+#define RFC1002_NOT_LISTENING_CALLING 0x81 /* not listening on calling name */
+#define RFC1002_NOT_PRESENT 0x82 /* called name not present */
+#define RFC1002_INSUFFICIENT_RESOURCE 0x83
+#define RFC1002_UNSPECIFIED_ERROR 0x8F
+
+/* RFC 1002 Datagram service packets are not defined here as they
+are not needed for the network filesystem client unless we plan on
+implementing broadcast resolution of the server ip address (from
+server netbios name). Currently server names are resolved only via DNS
+(tcp name) or ip address or an /etc/hosts equivalent mapping to ip address.*/
+
+#define DEFAULT_CIFS_CALLED_NAME "*SMBSERVER "
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
new file mode 100644
index 0000000..3cfa452
--- /dev/null
+++ b/fs/cifs/sess.c
@@ -0,0 +1,639 @@
+/*
+ * fs/cifs/sess.c
+ *
+ * SMB/CIFS session setup handling routines
+ *
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "ntlmssp.h"
+#include "nterr.h"
+#include <linux/utsname.h>
+#include "cifs_spnego.h"
+
+extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
+ unsigned char *p24);
+
+static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
+{
+ __u32 capabilities = 0;
+
+ /* init fields common to all four types of SessSetup */
+ /* note that header is initialized to zero in header_assemble */
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
+
+ /* BB verify whether signing required on neg or just on auth frame
+ (and NTLM case) */
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+
+ if (ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ if (ses->capabilities & CAP_UNIX)
+ capabilities |= CAP_UNIX;
+
+ /* BB check whether to init vcnum BB */
+ return capabilities;
+}
+
+static void
+unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* Copy OS version */
+ bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32,
+ nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release,
+ 32, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* trailing null */
+
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 32, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* trailing null */
+
+ *pbcc_area = bcc_ptr;
+}
+
+static void unicode_domain_string(char **pbcc_area, struct cifsSesInfo *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* copy domain */
+ if (ses->domainName == NULL) {
+ /* Sending null domain better than using a bogus domain name (as
+ we did briefly in 2.6.18) since server will use its default */
+ *bcc_ptr = 0;
+ *(bcc_ptr+1) = 0;
+ bytes_ret = 0;
+ } else
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName,
+ 256, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* account for null terminator */
+
+ *pbcc_area = bcc_ptr;
+}
+
+
+static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* BB FIXME add check that strings total less
+ than 335 or will need to send them as arrays */
+
+ /* unicode strings, must be word aligned before the call */
+/* if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ } */
+ /* copy user */
+ if (ses->userName == NULL) {
+ /* null user mount */
+ *bcc_ptr = 0;
+ *(bcc_ptr+1) = 0;
+ } else { /* 300 should be long enough for any conceivable user name */
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName,
+ 300, nls_cp);
+ }
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* account for null termination */
+
+ unicode_domain_string(&bcc_ptr, ses, nls_cp);
+ unicode_oslm_strings(&bcc_ptr, nls_cp);
+
+ *pbcc_area = bcc_ptr;
+}
+
+static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+
+ /* copy user */
+ /* BB what about null user mounts - check that we do this BB */
+ /* copy user */
+ if (ses->userName == NULL) {
+ /* BB what about null user mounts - check that we do this BB */
+ } else { /* 300 should be long enough for any conceivable user name */
+ strncpy(bcc_ptr, ses->userName, 300);
+ }
+ /* BB improve check for overflow */
+ bcc_ptr += strnlen(ses->userName, 300);
+ *bcc_ptr = 0;
+ bcc_ptr++; /* account for null termination */
+
+ /* copy domain */
+
+ if (ses->domainName != NULL) {
+ strncpy(bcc_ptr, ses->domainName, 256);
+ bcc_ptr += strnlen(ses->domainName, 256);
+ } /* else we will send a null domain name
+ so the server will default to its own domain */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+
+ /* BB check for overflow here */
+
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, init_utsname()->release);
+ bcc_ptr += strlen(init_utsname()->release) + 1;
+
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+
+ *pbcc_area = bcc_ptr;
+}
+
+static int decode_unicode_ssetup(char **pbcc_area, int bleft,
+ struct cifsSesInfo *ses,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ int words_left, len;
+ char *data = *pbcc_area;
+
+
+
+ cFYI(1, ("bleft %d", bleft));
+
+
+ /* SMB header is unaligned, so cifs servers word align start of
+ Unicode strings */
+ data++;
+ bleft--; /* Windows servers do not always double null terminate
+ their final Unicode string - in which case we
+ now will not attempt to decode the byte of junk
+ which follows it */
+
+ words_left = bleft / 2;
+
+ /* save off server operating system */
+ len = UniStrnlen((wchar_t *) data, words_left);
+
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ if (len >= words_left)
+ return rc;
+
+ kfree(ses->serverOS);
+ /* UTF-8 string will not grow more than four times as big as UCS-16 */
+ ses->serverOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL);
+ if (ses->serverOS != NULL)
+ cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp);
+ data += 2 * (len + 1);
+ words_left -= len + 1;
+
+ /* save off server network operating system */
+ len = UniStrnlen((wchar_t *) data, words_left);
+
+ if (len >= words_left)
+ return rc;
+
+ kfree(ses->serverNOS);
+ ses->serverNOS = kzalloc((4 * len) + 2 /* trailing null */, GFP_KERNEL);
+ if (ses->serverNOS != NULL) {
+ cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len,
+ nls_cp);
+ if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) {
+ cFYI(1, ("NT4 server"));
+ ses->flags |= CIFS_SES_NT4;
+ }
+ }
+ data += 2 * (len + 1);
+ words_left -= len + 1;
+
+ /* save off server domain */
+ len = UniStrnlen((wchar_t *) data, words_left);
+
+ if (len > words_left)
+ return rc;
+
+ kfree(ses->serverDomain);
+ ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */
+ if (ses->serverDomain != NULL) {
+ cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len,
+ nls_cp);
+ ses->serverDomain[2*len] = 0;
+ ses->serverDomain[(2*len) + 1] = 0;
+ }
+ data += 2 * (len + 1);
+ words_left -= len + 1;
+
+ cFYI(1, ("words left: %d", words_left));
+
+ return rc;
+}
+
+static int decode_ascii_ssetup(char **pbcc_area, int bleft,
+ struct cifsSesInfo *ses,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ int len;
+ char *bcc_ptr = *pbcc_area;
+
+ cFYI(1, ("decode sessetup ascii. bleft %d", bleft));
+
+ len = strnlen(bcc_ptr, bleft);
+ if (len >= bleft)
+ return rc;
+
+ kfree(ses->serverOS);
+
+ ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
+ if (ses->serverOS)
+ strncpy(ses->serverOS, bcc_ptr, len);
+ if (strncmp(ses->serverOS, "OS/2", 4) == 0) {
+ cFYI(1, ("OS/2 server"));
+ ses->flags |= CIFS_SES_OS2;
+ }
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ len = strnlen(bcc_ptr, bleft);
+ if (len >= bleft)
+ return rc;
+
+ kfree(ses->serverNOS);
+
+ ses->serverNOS = kzalloc(len + 1, GFP_KERNEL);
+ if (ses->serverNOS)
+ strncpy(ses->serverNOS, bcc_ptr, len);
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ len = strnlen(bcc_ptr, bleft);
+ if (len > bleft)
+ return rc;
+
+ /* No domain field in LANMAN case. Domain is
+ returned by old servers in the SMB negprot response */
+ /* BB For newer servers which do not support Unicode,
+ but thus do return domain here we could add parsing
+ for it later, but it is not very important */
+ cFYI(1, ("ascii: bytes left %d", bleft));
+
+ return rc;
+}
+
+int
+CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ int wct;
+ struct smb_hdr *smb_buf;
+ char *bcc_ptr;
+ char *str_area;
+ SESSION_SETUP_ANDX *pSMB;
+ __u32 capabilities;
+ int count;
+ int resp_buf_type;
+ struct kvec iov[3];
+ enum securityEnum type;
+ __u16 action;
+ int bytes_remaining;
+ struct key *spnego_key = NULL;
+
+ if (ses == NULL)
+ return -EINVAL;
+
+ type = ses->server->secType;
+
+ cFYI(1, ("sess setup type %d", type));
+ if (type == LANMAN) {
+#ifndef CONFIG_CIFS_WEAK_PW_HASH
+ /* LANMAN and plaintext are less secure and off by default.
+ So we make this explicitly be turned on in kconfig (in the
+ build) and turned on at runtime (changed from the default)
+ in proc/fs/cifs or via mount parm. Unfortunately this is
+ needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
+ return -EOPNOTSUPP;
+#endif
+ wct = 10; /* lanman 2 style sessionsetup */
+ } else if ((type == NTLM) || (type == NTLMv2)) {
+ /* For NTLMv2 failures eventually may need to retry NTLM */
+ wct = 13; /* old style NTLM sessionsetup */
+ } else /* same size: negotiate or auth, NTLMSSP or extended security */
+ wct = 12;
+
+ rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
+ (void **)&smb_buf);
+ if (rc)
+ return rc;
+
+ pSMB = (SESSION_SETUP_ANDX *)smb_buf;
+
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+ /* we will send the SMB in three pieces:
+ a fixed length beginning part, an optional
+ SPNEGO blob (which can be zero length), and a
+ last part which will include the strings
+ and rest of bcc area. This allows us to avoid
+ a large buffer 17K allocation */
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = smb_buf->smb_buf_length + 4;
+
+ /* setting this here allows the code at the end of the function
+ to free the request buffer if there's an error */
+ resp_buf_type = CIFS_SMALL_BUFFER;
+
+ /* 2000 big enough to fit max user, domain, NOS name etc. */
+ str_area = kmalloc(2000, GFP_KERNEL);
+ if (str_area == NULL) {
+ rc = -ENOMEM;
+ goto ssetup_exit;
+ }
+ bcc_ptr = str_area;
+
+ ses->flags &= ~CIFS_SES_LANMAN;
+
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+
+ if (type == LANMAN) {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ char lnm_session_key[CIFS_SESS_KEY_SIZE];
+
+ pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
+
+ /* no capabilities flags in old lanman negotiation */
+
+ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ /* BB calculate hash with password */
+ /* and copy into bcc */
+
+ calc_lanman_hash(ses, lnm_session_key);
+ ses->flags |= CIFS_SES_LANMAN;
+ memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+
+ /* can not sign if LANMAN negotiated so no need
+ to calculate signing key? but what if server
+ changed to do higher than lanman dialect and
+ we reconnected would we ever calc signing_key? */
+
+ cFYI(1, ("Negotiating LANMAN setting up strings"));
+ /* Unicode not allowed for LANMAN dialects */
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+#endif
+ } else if (type == NTLM) {
+ char ntlm_session_key[CIFS_SESS_KEY_SIZE];
+
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+ pSMB->req_no_secext.CaseInsensitivePasswordLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+
+ /* calculate session key */
+ SMBNTencrypt(ses->password, ses->server->cryptKey,
+ ntlm_session_key);
+
+ if (first_time) /* should this be moved into common code
+ with similar ntlmv2 path? */
+ cifs_calculate_mac_key(&ses->server->mac_signing_key,
+ ntlm_session_key, ses->password);
+ /* copy session key */
+
+ memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ if (ses->capabilities & CAP_UNICODE) {
+ /* unicode strings must be word aligned */
+ if (iov[0].iov_len % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else if (type == NTLMv2) {
+ char *v2_sess_key =
+ kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL);
+
+ /* BB FIXME change all users of v2_sess_key to
+ struct ntlmv2_resp */
+
+ if (v2_sess_key == NULL) {
+ rc = -ENOMEM;
+ goto ssetup_exit;
+ }
+
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+
+ /* LM2 password would be here if we supported it */
+ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
+ /* cpu_to_le16(LM2_SESS_KEY_SIZE); */
+
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(sizeof(struct ntlmv2_resp));
+
+ /* calculate session key */
+ setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
+ if (first_time) /* should this be moved into common code
+ with similar ntlmv2 path? */
+ /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key,
+ response BB FIXME, v2_sess_key); */
+
+ /* copy session key */
+
+ /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE);
+ bcc_ptr += LM2_SESS_KEY_SIZE; */
+ memcpy(bcc_ptr, (char *)v2_sess_key,
+ sizeof(struct ntlmv2_resp));
+ bcc_ptr += sizeof(struct ntlmv2_resp);
+ kfree(v2_sess_key);
+ if (ses->capabilities & CAP_UNICODE) {
+ if (iov[0].iov_len % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else if (type == Kerberos || type == MSKerberos) {
+#ifdef CONFIG_CIFS_UPCALL
+ struct cifs_spnego_msg *msg;
+ spnego_key = cifs_get_spnego_key(ses);
+ if (IS_ERR(spnego_key)) {
+ rc = PTR_ERR(spnego_key);
+ spnego_key = NULL;
+ goto ssetup_exit;
+ }
+
+ msg = spnego_key->payload.data;
+ /* check version field to make sure that cifs.upcall is
+ sending us a response in an expected form */
+ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
+ cERROR(1, ("incorrect version of cifs.upcall (expected"
+ " %d but got %d)",
+ CIFS_SPNEGO_UPCALL_VERSION, msg->version));
+ rc = -EKEYREJECTED;
+ goto ssetup_exit;
+ }
+ /* bail out if key is too long */
+ if (msg->sesskey_len >
+ sizeof(ses->server->mac_signing_key.data.krb5)) {
+ cERROR(1, ("Kerberos signing key too long (%u bytes)",
+ msg->sesskey_len));
+ rc = -EOVERFLOW;
+ goto ssetup_exit;
+ }
+ if (first_time) {
+ ses->server->mac_signing_key.len = msg->sesskey_len;
+ memcpy(ses->server->mac_signing_key.data.krb5,
+ msg->data, msg->sesskey_len);
+ }
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+ iov[1].iov_base = msg->data + msg->sesskey_len;
+ iov[1].iov_len = msg->secblob_len;
+ pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len);
+
+ if (ses->capabilities & CAP_UNICODE) {
+ /* unicode strings must be word aligned */
+ if ((iov[0].iov_len + iov[1].iov_len) % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_oslm_strings(&bcc_ptr, nls_cp);
+ unicode_domain_string(&bcc_ptr, ses, nls_cp);
+ } else
+ /* BB: is this right? */
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+#else /* ! CONFIG_CIFS_UPCALL */
+ cERROR(1, ("Kerberos negotiated but upcall support disabled!"));
+ rc = -ENOSYS;
+ goto ssetup_exit;
+#endif /* CONFIG_CIFS_UPCALL */
+ } else {
+ cERROR(1, ("secType %d not supported!", type));
+ rc = -ENOSYS;
+ goto ssetup_exit;
+ }
+
+ iov[2].iov_base = str_area;
+ iov[2].iov_len = (long) bcc_ptr - (long) str_area;
+
+ count = iov[1].iov_len + iov[2].iov_len;
+ smb_buf->smb_buf_length += count;
+
+ BCC_LE(smb_buf) = cpu_to_le16(count);
+
+ rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
+ CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR);
+ /* SMB request buf freed in SendReceive2 */
+
+ cFYI(1, ("ssetup rc from sendrecv2 is %d", rc));
+ if (rc)
+ goto ssetup_exit;
+
+ pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)iov[0].iov_base;
+
+ if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {
+ rc = -EIO;
+ cERROR(1, ("bad word count %d", smb_buf->WordCount));
+ goto ssetup_exit;
+ }
+ action = le16_to_cpu(pSMB->resp.Action);
+ if (action & GUEST_LOGIN)
+ cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cFYI(1, ("UID = %d ", ses->Suid));
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+ /* and lanman response is 3 */
+ bytes_remaining = BCC(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ if (smb_buf->WordCount == 4) {
+ __u16 blob_len;
+ blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+ bcc_ptr += blob_len;
+ if (blob_len > bytes_remaining) {
+ cERROR(1, ("bad security blob length %d", blob_len));
+ rc = -EINVAL;
+ goto ssetup_exit;
+ }
+ bytes_remaining -= blob_len;
+ }
+
+ /* BB check if Unicode and decode strings */
+ if (smb_buf->Flags2 & SMBFLG2_UNICODE)
+ rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining,
+ ses, nls_cp);
+ else
+ rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining,
+ ses, nls_cp);
+
+ssetup_exit:
+ if (spnego_key) {
+ key_revoke(spnego_key);
+ key_put(spnego_key);
+ }
+ kfree(str_area);
+ if (resp_buf_type == CIFS_SMALL_BUFFER) {
+ cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
+ cifs_small_buf_release(iov[0].iov_base);
+ } else if (resp_buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(iov[0].iov_base);
+
+ return rc;
+}
diff --git a/fs/cifs/smbdes.c b/fs/cifs/smbdes.c
new file mode 100644
index 0000000..04943c9
--- /dev/null
+++ b/fs/cifs/smbdes.c
@@ -0,0 +1,418 @@
+/*
+ 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, 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, unsigned char *c8, unsigned char *p24)
+{
+ smbhash(p24, c8, p21, 1);
+ smbhash(p24 + 8, c8, p21 + 7, 1);
+ smbhash(p24 + 16, c8, p21 + 14, 1);
+}
+
+#if 0 /* currently unsued */
+static void
+D_P16(unsigned char *p14, unsigned char *in, unsigned char *out)
+{
+ smbhash(out, in, p14, 0);
+ smbhash(out + 8, in + 8, p14 + 7, 0);
+}
+
+static void
+E_old_pw_hash(unsigned char *p14, unsigned char *in, unsigned char *out)
+{
+ smbhash(out, in, p14, 1);
+ smbhash(out + 8, in + 8, p14 + 7, 1);
+}
+/* these routines are currently unneeded, but may be
+ needed later */
+void
+cred_hash1(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ unsigned char buf[8];
+
+ smbhash(buf, in, key, 1);
+ smbhash(out, buf, key + 9, 1);
+}
+
+void
+cred_hash2(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ unsigned char buf[8];
+ static unsigned char key2[8];
+
+ smbhash(buf, in, key, 1);
+ key2[0] = key[7];
+ smbhash(out, buf, key2, 1);
+}
+
+void
+cred_hash3(unsigned char *out, unsigned char *in, unsigned char *key, int forw)
+{
+ static unsigned char key2[8];
+
+ smbhash(out, in, key, forw);
+ key2[0] = key[7];
+ smbhash(out + 8, in + 8, key2, forw);
+}
+#endif /* unneeded routines */
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
new file mode 100644
index 0000000..ff3232f
--- /dev/null
+++ b/fs/cifs/smbencrypt.c
@@ -0,0 +1,289 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Modified by Jeremy Allison 1995.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+ Modified by Steve French (sfrench@us.ibm.com) 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include "cifs_unicode.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "md5.h"
+#include "cifs_debug.h"
+#include "cifsencrypt.h"
+
+#ifndef false
+#define false 0
+#endif
+#ifndef true
+#define true 1
+#endif
+
+/* following came from the other byteorder.h to avoid include conflicts */
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val)))
+
+/*The following definitions come from libsmb/smbencrypt.c */
+
+void SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
+void E_md4hash(const unsigned char *passwd, unsigned char *p16);
+static void SMBOWFencrypt(unsigned char passwd[16], 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, unsigned char *c8, unsigned char *p24)
+{
+ unsigned char p14[15], p21[21];
+
+ memset(p21, '\0', 21);
+ memset(p14, '\0', 14);
+ strncpy((char *) p14, (char *) passwd, 14);
+
+/* strupper((char *)p14); *//* BB at least uppercase the easy range */
+ E_P16(p14, p21);
+
+ SMBOWFencrypt(p21, c8, p24);
+
+ memset(p14, 0, 15);
+ memset(p21, 0, 21);
+}
+
+/* Routines for Windows NT MD4 Hash functions. */
+static int
+_my_wcslen(__u16 *str)
+{
+ int len = 0;
+ while (*str++ != 0)
+ len++;
+ return len;
+}
+
+/*
+ * Convert a string into an NT UNICODE string.
+ * Note that regardless of processor type
+ * this must be in intel (little-endian)
+ * format.
+ */
+
+static int
+_my_mbstowcs(__u16 *dst, const unsigned char *src, int len)
+{ /* BB not a very good conversion routine - change/fix */
+ int i;
+ __u16 val;
+
+ for (i = 0; i < len; i++) {
+ val = *src;
+ SSVAL(dst, 0, val);
+ dst++;
+ src++;
+ if (val == 0)
+ break;
+ }
+ return i;
+}
+
+/*
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ */
+
+void
+E_md4hash(const unsigned char *passwd, unsigned char *p16)
+{
+ int len;
+ __u16 wpwd[129];
+
+ /* Password cannot be longer than 128 characters */
+ if (passwd) {
+ len = strlen((char *) passwd);
+ if (len > 128)
+ len = 128;
+
+ /* Password must be converted to NT unicode */
+ _my_mbstowcs(wpwd, passwd, len);
+ } else
+ len = 0;
+
+ wpwd[len] = 0; /* Ensure string is null terminated */
+ /* Calculate length in bytes */
+ len = _my_wcslen(wpwd) * sizeof(__u16);
+
+ mdfour(p16, (unsigned char *) wpwd, len);
+ memset(wpwd, 0, 129 * 2);
+}
+
+#if 0 /* currently unused */
+/* Does both the NT and LM owfs of a user's password */
+static void
+nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16])
+{
+ char passwd[514];
+
+ memset(passwd, '\0', 514);
+ if (strlen(pwd) < 513)
+ strcpy(passwd, pwd);
+ else
+ memcpy(passwd, pwd, 512);
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash(passwd, nt_p16);
+
+ /* Mangle the passwords into Lanman format */
+ passwd[14] = '\0';
+/* strupper(passwd); */
+
+ /* Calculate the SMB (lanman) hash functions of the password */
+
+ memset(p16, '\0', 16);
+ E_P16((unsigned char *) passwd, (unsigned char *) p16);
+
+ /* clear out local copy of user's password (just being paranoid). */
+ memset(passwd, '\0', sizeof(passwd));
+}
+#endif
+
+/* Does the NTLMv2 owfs of a user's password */
+#if 0 /* function not needed yet - but will be soon */
+static void
+ntv2_owf_gen(const unsigned char owf[16], const char *user_n,
+ const char *domain_n, unsigned char kr_buf[16],
+ const struct nls_table *nls_codepage)
+{
+ wchar_t *user_u;
+ wchar_t *dom_u;
+ int user_l, domain_l;
+ struct HMACMD5Context ctx;
+
+ /* might as well do one alloc to hold both (user_u and dom_u) */
+ user_u = kmalloc(2048 * sizeof(wchar_t), GFP_KERNEL);
+ if (user_u == NULL)
+ return;
+ dom_u = user_u + 1024;
+
+ /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2,
+ STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER);
+ push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2,
+ STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */
+
+ /* BB user and domain may need to be uppercased */
+ user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage);
+ domain_l = cifs_strtoUCS(dom_u, domain_n, 511, nls_codepage);
+
+ user_l++; /* trailing null */
+ domain_l++;
+
+ hmac_md5_init_limK_to_64(owf, 16, &ctx);
+ hmac_md5_update((const unsigned char *) user_u, user_l * 2, &ctx);
+ hmac_md5_update((const unsigned char *) dom_u, domain_l * 2, &ctx);
+ hmac_md5_final(kr_buf, &ctx);
+
+ kfree(user_u);
+}
+#endif
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+static void
+SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8,
+ unsigned char p24[24])
+{
+ unsigned char p21[21];
+
+ memset(p21, '\0', 21);
+
+ memcpy(p21, passwd, 16);
+ E_P24(p21, c8, p24);
+}
+
+/* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */
+#if 0 /* currently unused */
+static void
+NTLMSSPOWFencrypt(unsigned char passwd[8],
+ unsigned char *ntlmchalresp, unsigned char p24[24])
+{
+ unsigned char p21[21];
+
+ memset(p21, '\0', 21);
+ memcpy(p21, passwd, 8);
+ memset(p21 + 8, 0xbd, 8);
+
+ E_P24(p21, ntlmchalresp, p24);
+}
+#endif
+
+/* Does the NT MD4 hash then des encryption. */
+
+void
+SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
+{
+ unsigned char p21[21];
+
+ memset(p21, '\0', 21);
+
+ E_md4hash(passwd, p21);
+ SMBOWFencrypt(p21, c8, p24);
+}
+
+
+/* Does the md5 encryption from the NT hash for NTLMv2. */
+/* These routines will be needed later */
+#if 0
+static void
+SMBOWFencrypt_ntv2(const unsigned char kr[16],
+ const struct data_blob *srv_chal,
+ const struct data_blob *cli_chal, unsigned char resp_buf[16])
+{
+ struct HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(srv_chal->data, srv_chal->length, &ctx);
+ hmac_md5_update(cli_chal->data, cli_chal->length, &ctx);
+ hmac_md5_final(resp_buf, &ctx);
+}
+
+static void
+SMBsesskeygen_ntv2(const unsigned char kr[16],
+ const unsigned char *nt_resp, __u8 sess_key[16])
+{
+ struct HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(nt_resp, 16, &ctx);
+ hmac_md5_final((unsigned char *) sess_key, &ctx);
+}
+
+static void
+SMBsesskeygen_ntv1(const unsigned char kr[16],
+ const unsigned char *nt_resp, __u8 sess_key[16])
+{
+ mdfour((unsigned char *) sess_key, (unsigned char *) kr, 16);
+}
+#endif
diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h
new file mode 100644
index 0000000..7f50e85
--- /dev/null
+++ b/fs/cifs/smberr.h
@@ -0,0 +1,182 @@
+/*
+ * fs/cifs/smberr.h
+ *
+ * Copyright (c) International Business Machines Corp., 2002,2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * See Error Codes section of the SNIA CIFS Specification
+ * for more information
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define SUCCESS 0x00 /* The request was successful. */
+#define ERRDOS 0x01 /* Error is from the core DOS operating system set */
+#define ERRSRV 0x02 /* Error is generated by the file server daemon */
+#define ERRHRD 0x03 /* Error is a hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/* The following error codes may be generated with the SUCCESS error class.*/
+
+/*#define SUCCESS 0 The request was successful. */
+
+/* The following error codes may be generated with the ERRDOS error class.*/
+
+#define ERRbadfunc 1 /* Invalid function. The server did not
+ recognize or could not perform a
+ system call generated by the server,
+ e.g., set the DIRECTORY attribute on
+ a data file, invalid seek mode. */
+#define ERRbadfile 2 /* File not found. The last component
+ of a file's pathname could not be
+ found. */
+#define ERRbadpath 3 /* Directory invalid. A directory
+ component in a pathname could not be
+ found. */
+#define ERRnofids 4 /* Too many open files. The server has
+ no file handles available. */
+#define ERRnoaccess 5 /* Access denied, the client's context
+ does not permit the requested
+ function. This includes the
+ following conditions: invalid rename
+ command, write to Fid open for read
+ only, read on Fid open for write
+ only, attempt to delete a non-empty
+ directory */
+#define ERRbadfid 6 /* Invalid file handle. The file handle
+ specified was not recognized by the
+ server. */
+#define ERRbadmcb 7 /* Memory control blocks destroyed. */
+#define ERRnomem 8 /* Insufficient server memory to
+ perform the requested function. */
+#define ERRbadmem 9 /* Invalid memory block address. */
+#define ERRbadenv 10 /* Invalid environment. */
+#define ERRbadformat 11 /* Invalid format. */
+#define ERRbadaccess 12 /* Invalid open mode. */
+#define ERRbaddata 13 /* Invalid data (generated only by
+ IOCTL calls within the server). */
+#define ERRbaddrive 15 /* Invalid drive specified. */
+#define ERRremcd 16 /* A Delete Directory request attempted
+ to remove the server's current
+ directory. */
+#define ERRdiffdevice 17 /* Not same device (e.g., a cross
+ volume rename was attempted */
+#define ERRnofiles 18 /* A File Search command can find no
+ more files matching the specified
+ criteria. */
+#define ERRgeneral 31
+#define ERRbadshare 32 /* The sharing mode specified for an
+ Open conflicts with existing FIDs on
+ the file. */
+#define ERRlock 33 /* A Lock request conflicted with an
+ existing lock or specified an
+ invalid mode, or an Unlock requested
+ attempted to remove a lock held by
+ another process. */
+#define ERRunsup 50
+#define ERRnosuchshare 67
+#define ERRfilexists 80 /* The file named in the request
+ already exists. */
+#define ERRinvparm 87
+#define ERRdiskfull 112
+#define ERRinvname 123
+#define ERRinvlevel 124
+#define ERRdirnotempty 145
+#define ERRnotlocked 158
+#define ERRcancelviolation 173
+#define ERRalreadyexists 183
+#define ERRbadpipe 230
+#define ERRpipebusy 231
+#define ERRpipeclosing 232
+#define ERRnotconnected 233
+#define ERRmoredata 234
+#define ERReasnotsupported 282
+#define ErrQuota 0x200 /* The operation would cause a quota
+ limit to be exceeded. */
+#define ErrNotALink 0x201 /* A link operation was performed on a
+ pathname that was not a link. */
+
+/* Below errors are used internally (do not come over the wire) for passthrough
+ from STATUS codes to POSIX only */
+#define ErrTooManyLinks 0xFFFE
+
+/* Following error codes may be generated with the ERRSRV error class.*/
+
+#define ERRerror 1 /* Non-specific error code. It is
+ returned under the following
+ conditions: resource other than disk
+ space exhausted (e.g. TIDs), first
+ SMB command was not negotiate,
+ multiple negotiates attempted, and
+ internal server error. */
+#define ERRbadpw 2 /* Bad password - name/password pair in
+ a TreeConnect or Session Setup are
+ invalid. */
+#define ERRbadtype 3 /* used for indicating DFS referral
+ needed */
+#define ERRaccess 4 /* The client does not have the
+ necessary access rights within the
+ specified context for requested
+ function. */
+#define ERRinvtid 5 /* The Tid specified in a command was
+ invalid. */
+#define ERRinvnetname 6 /* Invalid network name in tree
+ connect. */
+#define ERRinvdevice 7 /* Invalid device - printer request
+ made to non-printer connection or
+ non-printer request made to printer
+ connection. */
+#define ERRqfull 49 /* Print queue full (files) -- returned
+ by open print file. */
+#define ERRqtoobig 50 /* Print queue full -- no space. */
+#define ERRqeof 51 /* EOF on print queue dump */
+#define ERRinvpfid 52 /* Invalid print file FID. */
+#define ERRsmbcmd 64 /* The server did not recognize the
+ command received. */
+#define ERRsrverror 65 /* The server encountered an internal
+ error, e.g., system file
+ unavailable. */
+#define ERRbadBID 66 /* (obsolete) */
+#define ERRfilespecs 67 /* The Fid and pathname parameters
+ contained an invalid combination of
+ values. */
+#define ERRbadLink 68 /* (obsolete) */
+#define ERRbadpermits 69 /* The access permissions specified for
+ a file or directory are not a valid
+ combination. */
+#define ERRbadPID 70
+#define ERRsetattrmode 71 /* attribute (mode) is invalid */
+#define ERRpaused 81 /* Server is paused */
+#define ERRmsgoff 82 /* reserved - messaging off */
+#define ERRnoroom 83 /* reserved - no room for message */
+#define ERRrmuns 87 /* reserved - too many remote names */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource 89 /* No resources available for request
+ */
+#define ERRtoomanyuids 90 /* Too many UIDs active on this session
+ */
+#define ERRbaduid 91 /* The UID is not known as a valid user
+ */
+#define ERRusempx 250 /* temporarily unable to use raw */
+#define ERRusestd 251 /* temporarily unable to use either raw
+ or mpx */
+#define ERR_NOTIFY_ENUM_DIR 1024
+#define ERRnoSuchUser 2238 /* user account does not exist */
+#define ERRaccountexpired 2239
+#define ERRbadclient 2240 /* can not logon from this client */
+#define ERRbadLogonTime 2241 /* logon hours do not allow this */
+#define ERRpasswordExpired 2242
+#define ERRnetlogonNotStarted 2455
+#define ERRnosupport 0xFFFF
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
new file mode 100644
index 0000000..ff8243a
--- /dev/null
+++ b/fs/cifs/transport.c
@@ -0,0 +1,1097 @@
+/*
+ * fs/cifs/transport.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org) 2006.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/net.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <linux/mempool.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+extern mempool_t *cifs_mid_poolp;
+extern struct kmem_cache *cifs_oplock_cachep;
+
+static struct mid_q_entry *
+AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
+{
+ struct mid_q_entry *temp;
+
+ if (ses == NULL) {
+ cERROR(1, ("Null session passed in to AllocMidQEntry"));
+ return NULL;
+ }
+ if (ses->server == NULL) {
+ cERROR(1, ("Null TCP session in AllocMidQEntry"));
+ return NULL;
+ }
+
+ temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
+ if (temp == NULL)
+ return temp;
+ else {
+ memset(temp, 0, sizeof(struct mid_q_entry));
+ temp->mid = smb_buffer->Mid; /* always LE */
+ temp->pid = current->pid;
+ temp->command = smb_buffer->Command;
+ cFYI(1, ("For smb_command %d", temp->command));
+ /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
+ /* when mid allocated can be before when sent */
+ temp->when_alloc = jiffies;
+ temp->ses = ses;
+ temp->tsk = current;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
+ atomic_inc(&midCount);
+ temp->midState = MID_REQUEST_ALLOCATED;
+ spin_unlock(&GlobalMid_Lock);
+ return temp;
+}
+
+static void
+DeleteMidQEntry(struct mid_q_entry *midEntry)
+{
+#ifdef CONFIG_CIFS_STATS2
+ unsigned long now;
+#endif
+ spin_lock(&GlobalMid_Lock);
+ midEntry->midState = MID_FREE;
+ list_del(&midEntry->qhead);
+ atomic_dec(&midCount);
+ spin_unlock(&GlobalMid_Lock);
+ if (midEntry->largeBuf)
+ cifs_buf_release(midEntry->resp_buf);
+ else
+ cifs_small_buf_release(midEntry->resp_buf);
+#ifdef CONFIG_CIFS_STATS2
+ now = jiffies;
+ /* commands taking longer than one second are indications that
+ something is wrong, unless it is quite a slow link or server */
+ if ((now - midEntry->when_alloc) > HZ) {
+ if ((cifsFYI & CIFS_TIMER) &&
+ (midEntry->command != SMB_COM_LOCKING_ANDX)) {
+ printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d",
+ midEntry->command, midEntry->mid);
+ printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
+ now - midEntry->when_alloc,
+ now - midEntry->when_sent,
+ now - midEntry->when_received);
+ }
+ }
+#endif
+ mempool_free(midEntry, cifs_mid_poolp);
+}
+
+struct oplock_q_entry *
+AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon)
+{
+ struct oplock_q_entry *temp;
+ if ((pinode == NULL) || (tcon == NULL)) {
+ cERROR(1, ("Null parms passed to AllocOplockQEntry"));
+ return NULL;
+ }
+ temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
+ GFP_KERNEL);
+ if (temp == NULL)
+ return temp;
+ else {
+ temp->pinode = pinode;
+ temp->tcon = tcon;
+ temp->netfid = fid;
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&temp->qhead, &GlobalOplock_Q);
+ spin_unlock(&GlobalMid_Lock);
+ }
+ return temp;
+
+}
+
+void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry)
+{
+ spin_lock(&GlobalMid_Lock);
+ /* should we check if list empty first? */
+ list_del(&oplockEntry->qhead);
+ spin_unlock(&GlobalMid_Lock);
+ kmem_cache_free(cifs_oplock_cachep, oplockEntry);
+}
+
+
+void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
+{
+ struct oplock_q_entry *temp;
+
+ if (tcon == NULL)
+ return;
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_entry(temp, &GlobalOplock_Q, qhead) {
+ if ((temp->tcon) && (temp->tcon == tcon)) {
+ list_del(&temp->qhead);
+ kmem_cache_free(cifs_oplock_cachep, temp);
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+}
+
+int
+smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
+ unsigned int smb_buf_length, struct sockaddr *sin, bool noblocksnd)
+{
+ int rc = 0;
+ int i = 0;
+ struct msghdr smb_msg;
+ struct kvec iov;
+ unsigned len = smb_buf_length + 4;
+
+ if (ssocket == NULL)
+ return -ENOTSOCK; /* BB eventually add reconnect code here */
+ iov.iov_base = smb_buffer;
+ iov.iov_len = len;
+
+ smb_msg.msg_name = sin;
+ smb_msg.msg_namelen = sizeof(struct sockaddr);
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+ if (noblocksnd)
+ smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
+ else
+ smb_msg.msg_flags = MSG_NOSIGNAL;
+
+ /* smb header is converted in header_assemble. bcc and rest of SMB word
+ area, and byte area if necessary, is converted to littleendian in
+ cifssmb.c and RFC1001 len is converted to bigendian in smb_send
+ Flags2 is converted in SendReceive */
+
+ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
+ cFYI(1, ("Sending smb of length %d", smb_buf_length));
+ dump_smb(smb_buffer, len);
+
+ while (len > 0) {
+ rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
+ if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
+ i++;
+ /* smaller timeout here than send2 since smaller size */
+ /* Although it may not be required, this also is smaller
+ oplock break time */
+ if (i > 12) {
+ cERROR(1,
+ ("sends on sock %p stuck for 7 seconds",
+ ssocket));
+ rc = -EAGAIN;
+ break;
+ }
+ msleep(1 << i);
+ continue;
+ }
+ if (rc < 0)
+ break;
+ else
+ i = 0; /* reset i after each successful send */
+ iov.iov_base += rc;
+ iov.iov_len -= rc;
+ len -= rc;
+ }
+
+ if (rc < 0) {
+ cERROR(1, ("Error %d sending data on socket to server", rc));
+ } else {
+ rc = 0;
+ }
+
+ /* Don't want to modify the buffer as a
+ side effect of this call. */
+ smb_buffer->smb_buf_length = smb_buf_length;
+
+ return rc;
+}
+
+static int
+smb_send2(struct TCP_Server_Info *server, struct kvec *iov, int n_vec,
+ struct sockaddr *sin, bool noblocksnd)
+{
+ int rc = 0;
+ int i = 0;
+ struct msghdr smb_msg;
+ struct smb_hdr *smb_buffer = iov[0].iov_base;
+ unsigned int len = iov[0].iov_len;
+ unsigned int total_len;
+ int first_vec = 0;
+ unsigned int smb_buf_length = smb_buffer->smb_buf_length;
+ struct socket *ssocket = server->ssocket;
+
+ if (ssocket == NULL)
+ return -ENOTSOCK; /* BB eventually add reconnect code here */
+
+ smb_msg.msg_name = sin;
+ smb_msg.msg_namelen = sizeof(struct sockaddr);
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+ if (noblocksnd)
+ smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
+ else
+ smb_msg.msg_flags = MSG_NOSIGNAL;
+
+ /* smb header is converted in header_assemble. bcc and rest of SMB word
+ area, and byte area if necessary, is converted to littleendian in
+ cifssmb.c and RFC1001 len is converted to bigendian in smb_send
+ Flags2 is converted in SendReceive */
+
+
+ total_len = 0;
+ for (i = 0; i < n_vec; i++)
+ total_len += iov[i].iov_len;
+
+ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
+ cFYI(1, ("Sending smb: total_len %d", total_len));
+ dump_smb(smb_buffer, len);
+
+ i = 0;
+ while (total_len) {
+ rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
+ n_vec - first_vec, total_len);
+ if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
+ i++;
+ if (i >= 14) {
+ cERROR(1,
+ ("sends on sock %p stuck for 15 seconds",
+ ssocket));
+ rc = -EAGAIN;
+ break;
+ }
+ msleep(1 << i);
+ continue;
+ }
+ if (rc < 0)
+ break;
+
+ if (rc == total_len) {
+ total_len = 0;
+ break;
+ } else if (rc > total_len) {
+ cERROR(1, ("sent %d requested %d", rc, total_len));
+ break;
+ }
+ if (rc == 0) {
+ /* should never happen, letting socket clear before
+ retrying is our only obvious option here */
+ cERROR(1, ("tcp sent no data"));
+ msleep(500);
+ continue;
+ }
+ total_len -= rc;
+ /* the line below resets i */
+ for (i = first_vec; i < n_vec; i++) {
+ if (iov[i].iov_len) {
+ if (rc > iov[i].iov_len) {
+ rc -= iov[i].iov_len;
+ iov[i].iov_len = 0;
+ } else {
+ iov[i].iov_base += rc;
+ iov[i].iov_len -= rc;
+ first_vec = i;
+ break;
+ }
+ }
+ }
+ i = 0; /* in case we get ENOSPC on the next send */
+ }
+
+ if ((total_len > 0) && (total_len != smb_buf_length + 4)) {
+ cFYI(1, ("partial send (%d remaining), terminating session",
+ total_len));
+ /* If we have only sent part of an SMB then the next SMB
+ could be taken as the remainder of this one. We need
+ to kill the socket so the server throws away the partial
+ SMB */
+ server->tcpStatus = CifsNeedReconnect;
+ }
+
+ if (rc < 0) {
+ cERROR(1, ("Error %d sending data on socket to server", rc));
+ } else
+ rc = 0;
+
+ /* Don't want to modify the buffer as a
+ side effect of this call. */
+ smb_buffer->smb_buf_length = smb_buf_length;
+
+ return rc;
+}
+
+static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
+{
+ if (long_op == CIFS_ASYNC_OP) {
+ /* oplock breaks must not be held up */
+ atomic_inc(&ses->server->inFlight);
+ } else {
+ spin_lock(&GlobalMid_Lock);
+ while (1) {
+ if (atomic_read(&ses->server->inFlight) >=
+ cifs_max_pending){
+ spin_unlock(&GlobalMid_Lock);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&ses->server->num_waiters);
+#endif
+ wait_event(ses->server->request_q,
+ atomic_read(&ses->server->inFlight)
+ < cifs_max_pending);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_dec(&ses->server->num_waiters);
+#endif
+ spin_lock(&GlobalMid_Lock);
+ } else {
+ if (ses->server->tcpStatus == CifsExiting) {
+ spin_unlock(&GlobalMid_Lock);
+ return -ENOENT;
+ }
+
+ /* can not count locking commands against total
+ as they are allowed to block on server */
+
+ /* update # of requests on the wire to server */
+ if (long_op != CIFS_BLOCKING_OP)
+ atomic_inc(&ses->server->inFlight);
+ spin_unlock(&GlobalMid_Lock);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,
+ struct mid_q_entry **ppmidQ)
+{
+ if (ses->server->tcpStatus == CifsExiting) {
+ return -ENOENT;
+ } else if (ses->server->tcpStatus == CifsNeedReconnect) {
+ cFYI(1, ("tcp session dead - return to caller to retry"));
+ return -EAGAIN;
+ } else if (ses->status != CifsGood) {
+ /* check if SMB session is bad because we are setting it up */
+ if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
+ (in_buf->Command != SMB_COM_NEGOTIATE))
+ return -EAGAIN;
+ /* else ok - we are setting up session */
+ }
+ *ppmidQ = AllocMidQEntry(in_buf, ses);
+ if (*ppmidQ == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static int wait_for_response(struct cifsSesInfo *ses,
+ struct mid_q_entry *midQ,
+ unsigned long timeout,
+ unsigned long time_to_wait)
+{
+ unsigned long curr_timeout;
+
+ for (;;) {
+ curr_timeout = timeout + jiffies;
+ wait_event(ses->server->response_q,
+ (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+ time_after(jiffies, curr_timeout) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ if (time_after(jiffies, curr_timeout) &&
+ (midQ->midState == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ unsigned long lrt;
+
+ /* We timed out. Is the server still
+ sending replies ? */
+ spin_lock(&GlobalMid_Lock);
+ lrt = ses->server->lstrp;
+ spin_unlock(&GlobalMid_Lock);
+
+ /* Calculate time_to_wait past last receive time.
+ Although we prefer not to time out if the
+ server is still responding - we will time
+ out if the server takes more than 15 (or 45
+ or 180) seconds to respond to this request
+ and has not responded to any request from
+ other threads on the client within 10 seconds */
+ lrt += time_to_wait;
+ if (time_after(jiffies, lrt)) {
+ /* No replies for time_to_wait. */
+ cERROR(1, ("server not responding"));
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+/*
+ *
+ * Send an SMB Request. No response info (other than return code)
+ * needs to be parsed.
+ *
+ * flags indicate the type of request buffer and how long to wait
+ * and whether to log NT STATUS code (error) before mapping it to POSIX error
+ *
+ */
+int
+SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses,
+ struct smb_hdr *in_buf, int flags)
+{
+ int rc;
+ struct kvec iov[1];
+ int resp_buf_type;
+
+ iov[0].iov_base = (char *)in_buf;
+ iov[0].iov_len = in_buf->smb_buf_length + 4;
+ flags |= CIFS_NO_RESP;
+ rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
+ cFYI(DBG2, ("SendRcvNoRsp flags %d rc %d", flags, rc));
+
+ return rc;
+}
+
+int
+SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
+ struct kvec *iov, int n_vec, int *pRespBufType /* ret */,
+ const int flags)
+{
+ int rc = 0;
+ int long_op;
+ unsigned int receive_len;
+ unsigned long timeout;
+ struct mid_q_entry *midQ;
+ struct smb_hdr *in_buf = iov[0].iov_base;
+
+ long_op = flags & CIFS_TIMEOUT_MASK;
+
+ *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
+
+ if ((ses == NULL) || (ses->server == NULL)) {
+ cifs_small_buf_release(in_buf);
+ cERROR(1, ("Null session"));
+ return -EIO;
+ }
+
+ if (ses->server->tcpStatus == CifsExiting) {
+ cifs_small_buf_release(in_buf);
+ return -ENOENT;
+ }
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, long_op);
+ if (rc) {
+ cifs_small_buf_release(in_buf);
+ return rc;
+ }
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ cifs_small_buf_release(in_buf);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
+ }
+ rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
+
+ midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&ses->server->inSend);
+#endif
+ rc = smb_send2(ses->server, iov, n_vec,
+ (struct sockaddr *) &(ses->server->addr.sockAddr),
+ ses->server->noblocksnd);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_dec(&ses->server->inSend);
+ midQ->when_sent = jiffies;
+#endif
+
+ up(&ses->server->tcpSem);
+ cifs_small_buf_release(in_buf);
+
+ if (rc < 0)
+ goto out;
+
+ if (long_op == CIFS_STD_OP)
+ timeout = 15 * HZ;
+ else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */
+ timeout = 180 * HZ;
+ else if (long_op == CIFS_LONG_OP)
+ timeout = 45 * HZ; /* should be greater than
+ servers oplock break timeout (about 43 seconds) */
+ else if (long_op == CIFS_ASYNC_OP)
+ goto out;
+ else if (long_op == CIFS_BLOCKING_OP)
+ timeout = 0x7FFFFFFF; /* large, but not so large as to wrap */
+ else {
+ cERROR(1, ("unknown timeout flag %d", long_op));
+ rc = -EIO;
+ goto out;
+ }
+
+ /* wait for 15 seconds or until woken up due to response arriving or
+ due to last connection to this server being unmounted */
+ if (signal_pending(current)) {
+ /* if signal pending do not hold up user for full smb timeout
+ but we still give response a chance to complete */
+ timeout = 2 * HZ;
+ }
+
+ /* No user interrupts in wait - wreaks havoc with performance */
+ wait_for_response(ses, midQ, timeout, 10 * HZ);
+
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->resp_buf) {
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+ } else {
+ cERROR(1, ("No response to cmd %d mid %d",
+ midQ->command, midQ->mid));
+ if (midQ->midState == MID_REQUEST_SUBMITTED) {
+ if (ses->server->tcpStatus == CifsExiting)
+ rc = -EHOSTDOWN;
+ else {
+ ses->server->tcpStatus = CifsNeedReconnect;
+ midQ->midState = MID_RETRY_NEEDED;
+ }
+ }
+
+ if (rc != -EHOSTDOWN) {
+ if (midQ->midState == MID_RETRY_NEEDED) {
+ rc = -EAGAIN;
+ cFYI(1, ("marking request for retry"));
+ } else {
+ rc = -EIO;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ DeleteMidQEntry(midQ);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
+ }
+
+ if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cERROR(1, ("Frame too large received. Length: %d Xid: %d",
+ receive_len, xid));
+ rc = -EIO;
+ } else { /* rcvd frame is ok */
+ if (midQ->resp_buf &&
+ (midQ->midState == MID_RESPONSE_RECEIVED)) {
+
+ iov[0].iov_base = (char *)midQ->resp_buf;
+ if (midQ->largeBuf)
+ *pRespBufType = CIFS_LARGE_BUFFER;
+ else
+ *pRespBufType = CIFS_SMALL_BUFFER;
+ iov[0].iov_len = receive_len + 4;
+
+ dump_smb(midQ->resp_buf, 80);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(midQ->resp_buf,
+ &ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
+ }
+ }
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(midQ->resp_buf,
+ flags & CIFS_LOG_ERROR);
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
+ BCC(midQ->resp_buf) =
+ le16_to_cpu(BCC_LE(midQ->resp_buf));
+ if ((flags & CIFS_NO_RESP) == 0)
+ midQ->resp_buf = NULL; /* mark it so buf will
+ not be freed by
+ DeleteMidQEntry */
+ } else {
+ rc = -EIO;
+ cFYI(1, ("Bad MID state?"));
+ }
+ }
+
+out:
+ DeleteMidQEntry(midQ);
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+
+ return rc;
+}
+
+int
+SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+ int *pbytes_returned, const int long_op)
+{
+ int rc = 0;
+ unsigned int receive_len;
+ unsigned long timeout;
+ struct mid_q_entry *midQ;
+
+ if (ses == NULL) {
+ cERROR(1, ("Null smb session"));
+ return -EIO;
+ }
+ if (ses->server == NULL) {
+ cERROR(1, ("Null tcp session"));
+ return -EIO;
+ }
+
+ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, long_op);
+ if (rc)
+ return rc;
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
+ }
+
+ 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));
+ DeleteMidQEntry(midQ);
+ up(&ses->server->tcpSem);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return -EIO;
+ }
+
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+
+ midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&ses->server->inSend);
+#endif
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr),
+ ses->server->noblocksnd);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_dec(&ses->server->inSend);
+ midQ->when_sent = jiffies;
+#endif
+ up(&ses->server->tcpSem);
+
+ if (rc < 0)
+ goto out;
+
+ if (long_op == CIFS_STD_OP)
+ timeout = 15 * HZ;
+ /* wait for 15 seconds or until woken up due to response arriving or
+ due to last connection to this server being unmounted */
+ else if (long_op == CIFS_ASYNC_OP)
+ goto out;
+ else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */
+ timeout = 180 * HZ;
+ else if (long_op == CIFS_LONG_OP)
+ timeout = 45 * HZ; /* should be greater than
+ servers oplock break timeout (about 43 seconds) */
+ else if (long_op == CIFS_BLOCKING_OP)
+ timeout = 0x7FFFFFFF; /* large but no so large as to wrap */
+ else {
+ cERROR(1, ("unknown timeout flag %d", long_op));
+ rc = -EIO;
+ goto out;
+ }
+
+ if (signal_pending(current)) {
+ /* if signal pending do not hold up user for full smb timeout
+ but we still give response a chance to complete */
+ timeout = 2 * HZ;
+ }
+
+ /* No user interrupts in wait - wreaks havoc with performance */
+ wait_for_response(ses, midQ, timeout, 10 * HZ);
+
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->resp_buf) {
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+ } else {
+ cERROR(1, ("No response for cmd %d mid %d",
+ midQ->command, midQ->mid));
+ if (midQ->midState == MID_REQUEST_SUBMITTED) {
+ if (ses->server->tcpStatus == CifsExiting)
+ rc = -EHOSTDOWN;
+ else {
+ ses->server->tcpStatus = CifsNeedReconnect;
+ midQ->midState = MID_RETRY_NEEDED;
+ }
+ }
+
+ if (rc != -EHOSTDOWN) {
+ if (midQ->midState == MID_RETRY_NEEDED) {
+ rc = -EAGAIN;
+ cFYI(1, ("marking request for retry"));
+ } else {
+ rc = -EIO;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ DeleteMidQEntry(midQ);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
+ }
+
+ if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cERROR(1, ("Frame too large received. Length: %d Xid: %d",
+ receive_len, xid));
+ rc = -EIO;
+ } else { /* rcvd frame is ok */
+
+ if (midQ->resp_buf && out_buf
+ && (midQ->midState == MID_RESPONSE_RECEIVED)) {
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
+ &ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
+ }
+ }
+
+ *pbytes_returned = out_buf->smb_buf_length;
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+ } else {
+ rc = -EIO;
+ cERROR(1, ("Bad MID state?"));
+ }
+ }
+
+out:
+ DeleteMidQEntry(midQ);
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+
+ return rc;
+}
+
+/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */
+
+static int
+send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf,
+ struct mid_q_entry *midQ)
+{
+ int rc = 0;
+ struct cifsSesInfo *ses = tcon->ses;
+ __u16 mid = in_buf->Mid;
+
+ header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0);
+ in_buf->Mid = mid;
+ down(&ses->server->tcpSem);
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ return rc;
+ }
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr),
+ ses->server->noblocksnd);
+ up(&ses->server->tcpSem);
+ return rc;
+}
+
+/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
+ blocking lock to return. */
+
+static int
+send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon,
+ struct smb_hdr *in_buf,
+ struct smb_hdr *out_buf)
+{
+ int bytes_returned;
+ struct cifsSesInfo *ses = tcon->ses;
+ LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+
+ /* We just modify the current in_buf to change
+ the type of lock from LOCKING_ANDX_SHARED_LOCK
+ or LOCKING_ANDX_EXCLUSIVE_LOCK to
+ LOCKING_ANDX_CANCEL_LOCK. */
+
+ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+ pSMB->Timeout = 0;
+ pSMB->hdr.Mid = GetNextMid(ses->server);
+
+ return SendReceive(xid, ses, in_buf, out_buf,
+ &bytes_returned, CIFS_STD_OP);
+}
+
+int
+SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+ int *pbytes_returned)
+{
+ int rc = 0;
+ int rstart = 0;
+ unsigned int receive_len;
+ struct mid_q_entry *midQ;
+ struct cifsSesInfo *ses;
+
+ if (tcon == NULL || tcon->ses == NULL) {
+ cERROR(1, ("Null smb session"));
+ return -EIO;
+ }
+ ses = tcon->ses;
+
+ if (ses->server == NULL) {
+ cERROR(1, ("Null tcp session"));
+ return -EIO;
+ }
+
+ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, CIFS_BLOCKING_OP);
+ if (rc)
+ return rc;
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ return rc;
+ }
+
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ up(&ses->server->tcpSem);
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ DeleteMidQEntry(midQ);
+ return -EIO;
+ }
+
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+
+ midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&ses->server->inSend);
+#endif
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr),
+ ses->server->noblocksnd);
+#ifdef CONFIG_CIFS_STATS2
+ atomic_dec(&ses->server->inSend);
+ midQ->when_sent = jiffies;
+#endif
+ up(&ses->server->tcpSem);
+
+ if (rc < 0) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+
+ /* Wait for a reply - allow signals to interrupt. */
+ rc = wait_event_interruptible(ses->server->response_q,
+ (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ /* Were we interrupted by a signal ? */
+ if ((rc == -ERESTARTSYS) &&
+ (midQ->midState == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ if (in_buf->Command == SMB_COM_TRANSACTION2) {
+ /* POSIX lock. We send a NT_CANCEL SMB to cause the
+ blocking lock to return. */
+
+ rc = send_nt_cancel(tcon, in_buf, midQ);
+ if (rc) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+ } else {
+ /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
+ to cause the blocking lock to return. */
+
+ rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
+
+ /* If we get -ENOLCK back the lock may have
+ already been removed. Don't exit in this case. */
+ if (rc && rc != -ENOLCK) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+ }
+
+ /* Wait 5 seconds for the response. */
+ if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) {
+ /* We got the response - restart system call. */
+ rstart = 1;
+ }
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->resp_buf) {
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+ } else {
+ cERROR(1, ("No response for cmd %d mid %d",
+ midQ->command, midQ->mid));
+ if (midQ->midState == MID_REQUEST_SUBMITTED) {
+ if (ses->server->tcpStatus == CifsExiting)
+ rc = -EHOSTDOWN;
+ else {
+ ses->server->tcpStatus = CifsNeedReconnect;
+ midQ->midState = MID_RETRY_NEEDED;
+ }
+ }
+
+ if (rc != -EHOSTDOWN) {
+ if (midQ->midState == MID_RETRY_NEEDED) {
+ rc = -EAGAIN;
+ cFYI(1, ("marking request for retry"));
+ } else {
+ rc = -EIO;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+
+ if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cERROR(1, ("Frame too large received. Length: %d Xid: %d",
+ receive_len, xid));
+ rc = -EIO;
+ } else { /* rcvd frame is ok */
+
+ if (midQ->resp_buf && out_buf
+ && (midQ->midState == MID_RESPONSE_RECEIVED)) {
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
+ &ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
+ }
+ }
+
+ *pbytes_returned = out_buf->smb_buf_length;
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+ } else {
+ rc = -EIO;
+ cERROR(1, ("Bad MID state?"));
+ }
+ }
+ DeleteMidQEntry(midQ);
+ if (rstart && rc == -EACCES)
+ return -ERESTARTSYS;
+ return rc;
+}
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
new file mode 100644
index 0000000..e9527ee
--- /dev/null
+++ b/fs/cifs/xattr.c
@@ -0,0 +1,372 @@
+/*
+ * fs/cifs/xattr.c
+ *
+ * Copyright (c) International Business Machines Corp., 2003, 2007
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/posix_acl_xattr.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+
+#define MAX_EA_VALUE_SIZE 65535
+#define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
+#define CIFS_XATTR_USER_PREFIX "user."
+#define CIFS_XATTR_SYSTEM_PREFIX "system."
+#define CIFS_XATTR_OS2_PREFIX "os2."
+#define CIFS_XATTR_SECURITY_PREFIX ".security"
+#define CIFS_XATTR_TRUSTED_PREFIX "trusted."
+#define XATTR_TRUSTED_PREFIX_LEN 8
+#define XATTR_SECURITY_PREFIX_LEN 9
+/* BB need to add server (Samba e.g) support for security and trusted prefix */
+
+
+
+int cifs_removexattr(struct dentry *direntry, const char *ea_name)
+{
+ int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block *sb;
+ char *full_path;
+
+ if (direntry == NULL)
+ return -EIO;
+ if (direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if (sb == NULL)
+ return -EIO;
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ if (ea_name == NULL) {
+ cFYI(1, ("Null xattr names not supported"));
+ } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5)
+ && (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) {
+ cFYI(1,
+ ("illegal xattr request %s (only user namespace supported)",
+ ea_name));
+ /* BB what if no namespace prefix? */
+ /* Should we just pass them to server, except for
+ system and perhaps security prefixes? */
+ } else {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto remove_ea_exit;
+
+ ea_name += 5; /* skip past user. prefix */
+ rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
+ (__u16)0, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ }
+remove_ea_exit:
+ kfree(full_path);
+ FreeXid(xid);
+#endif
+ return rc;
+}
+
+int cifs_setxattr(struct dentry *direntry, const char *ea_name,
+ const void *ea_value, size_t value_size, int flags)
+{
+ int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block *sb;
+ char *full_path;
+
+ if (direntry == NULL)
+ return -EIO;
+ if (direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if (sb == NULL)
+ return -EIO;
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+
+ /* if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+ if (value_size > MAX_EA_VALUE_SIZE) {
+ cFYI(1, ("size of EA value too large"));
+ kfree(full_path);
+ FreeXid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ if (ea_name == NULL) {
+ cFYI(1, ("Null xattr names not supported"));
+ } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto set_ea_exit;
+ if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
+ cFYI(1, ("attempt to set cifs inode metadata"));
+
+ ea_name += 5; /* skip past user. prefix */
+ rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
+ (__u16)value_size, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto set_ea_exit;
+
+ ea_name += 4; /* skip past os2. prefix */
+ rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
+ (__u16)value_size, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else {
+ int temp;
+ temp = strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
+ strlen(POSIX_ACL_XATTR_ACCESS));
+ if (temp == 0) {
+#ifdef CONFIG_CIFS_POSIX
+ if (sb->s_flags & MS_POSIXACL)
+ rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
+ ea_value, (const int)value_size,
+ ACL_TYPE_ACCESS, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ cFYI(1, ("set POSIX ACL rc %d", rc));
+#else
+ cFYI(1, ("set POSIX ACL not supported"));
+#endif
+ } else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
+ strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
+#ifdef CONFIG_CIFS_POSIX
+ if (sb->s_flags & MS_POSIXACL)
+ rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
+ ea_value, (const int)value_size,
+ ACL_TYPE_DEFAULT, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ cFYI(1, ("set POSIX default ACL rc %d", rc));
+#else
+ cFYI(1, ("set default POSIX ACL not supported"));
+#endif
+ } else {
+ cFYI(1, ("illegal xattr request %s (only user namespace"
+ " supported)", ea_name));
+ /* BB what if no namespace prefix? */
+ /* Should we just pass them to server, except for
+ system and perhaps security prefixes? */
+ }
+ }
+
+set_ea_exit:
+ kfree(full_path);
+ FreeXid(xid);
+#endif
+ return rc;
+}
+
+ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
+ void *ea_value, size_t buf_size)
+{
+ ssize_t rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block *sb;
+ char *full_path;
+
+ if (direntry == NULL)
+ return -EIO;
+ if (direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if (sb == NULL)
+ return -EIO;
+
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+ if (ea_name == NULL) {
+ cFYI(1, ("Null xattr names not supported"));
+ } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto get_ea_exit;
+
+ if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) {
+ cFYI(1, ("attempt to query cifs inode metadata"));
+ /* revalidate/getattr then populate from inode */
+ } /* BB add else when above is implemented */
+ ea_name += 5; /* skip past user. prefix */
+ rc = CIFSSMBQueryEA(xid, pTcon, full_path, ea_name, ea_value,
+ buf_size, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ goto get_ea_exit;
+
+ ea_name += 4; /* skip past os2. prefix */
+ rc = CIFSSMBQueryEA(xid, pTcon, full_path, ea_name, ea_value,
+ buf_size, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
+ strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
+#ifdef CONFIG_CIFS_POSIX
+ if (sb->s_flags & MS_POSIXACL)
+ rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
+ ea_value, buf_size, ACL_TYPE_ACCESS,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+ else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
+ __u16 fid;
+ int oplock = 0;
+ struct cifs_ntsd *pacl = NULL;
+ __u32 buflen = 0;
+ if (experimEnabled)
+ rc = CIFSSMBOpen(xid, pTcon, full_path,
+ FILE_OPEN, GENERIC_READ, 0, &fid,
+ &oplock, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ /* else rc is EOPNOTSUPP from above */
+
+ if (rc == 0) {
+ rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl,
+ &buflen);
+ CIFSSMBClose(xid, pTcon, fid);
+ }
+ }
+#endif /* EXPERIMENTAL */
+#else
+ cFYI(1, ("query POSIX ACL not supported yet"));
+#endif /* CONFIG_CIFS_POSIX */
+ } else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
+ strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
+#ifdef CONFIG_CIFS_POSIX
+ if (sb->s_flags & MS_POSIXACL)
+ rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
+ ea_value, buf_size, ACL_TYPE_DEFAULT,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+#else
+ cFYI(1, ("query POSIX default ACL not supported yet"));
+#endif
+ } else if (strncmp(ea_name,
+ CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
+ cFYI(1, ("Trusted xattr namespace not supported yet"));
+ } else if (strncmp(ea_name,
+ CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
+ cFYI(1, ("Security xattr namespace not supported yet"));
+ } else
+ cFYI(1,
+ ("illegal xattr request %s (only user namespace supported)",
+ ea_name));
+
+ /* We could add an additional check for streams ie
+ if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+
+ if (rc == -EINVAL)
+ rc = -EOPNOTSUPP;
+
+get_ea_exit:
+ kfree(full_path);
+ FreeXid(xid);
+#endif
+ return rc;
+}
+
+ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
+{
+ ssize_t rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block *sb;
+ char *full_path;
+
+ if (direntry == NULL)
+ return -EIO;
+ if (direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if (sb == NULL)
+ return -EIO;
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ return -EOPNOTSUPP;
+
+ xid = GetXid();
+
+ full_path = build_path_from_dentry(direntry);
+ if (full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+
+ /* if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+ rc = CIFSSMBQAllEAs(xid, pTcon, full_path, data, buf_size,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ kfree(full_path);
+ FreeXid(xid);
+#endif
+ return rc;
+}
OpenPOWER on IntegriCloud