diff options
author | Gerald Carter <jerry@samba.org> | 2004-07-22 13:08:58 +0000 |
---|---|---|
committer | Gerald Carter <jerry@samba.org> | 2004-07-22 13:08:58 +0000 |
commit | f36859b9e485d72d9a2ccc385a7b8f6bef67263b (patch) | |
tree | 9858a70ac781bee761bb88860011b4e127075cc3 | |
parent | f5fb1079810ed08364095aac7120cee072aa661a (diff) | |
download | samba-f36859b9e485d72d9a2ccc385a7b8f6bef67263b.tar.gz |
r1563: checking in changes for 3.0.5
-rw-r--r-- | jerry/WHATSNEW.txt | 1968 | ||||
-rw-r--r-- | jerry/source/VERSION | 93 | ||||
-rw-r--r-- | jerry/source/include/mangle.h | 14 | ||||
-rw-r--r-- | jerry/source/lib/util_str.c | 2066 | ||||
-rw-r--r-- | jerry/source/smbd/filename.c | 497 | ||||
-rw-r--r-- | jerry/source/smbd/mangle.c | 124 | ||||
-rw-r--r-- | jerry/source/smbd/mangle_hash.c | 783 | ||||
-rw-r--r-- | jerry/source/smbd/mangle_hash2.c | 707 | ||||
-rw-r--r-- | jerry/source/smbd/reply.c | 4974 |
9 files changed, 11226 insertions, 0 deletions
diff --git a/jerry/WHATSNEW.txt b/jerry/WHATSNEW.txt new file mode 100644 index 00000000000..2b19a8aab42 --- /dev/null +++ b/jerry/WHATSNEW.txt @@ -0,0 +1,1968 @@ + ============================= + Release Notes for Samba 3.0.5 + July 20, 2004 + ============================= + +######################## SECURITY RELEASE ######################## + +Summary: Multiple Potential Buffer Overruns in Samba 3.0.x +CVE ID: CAN-2004-0600, CAN-2004-0686 + (http://cve.mitre.org/) + + +This is the latest stable release of Samba. This is the version +that production Samba servers should be running for all current +bug-fixes. + +It has been confirmed that versions of Samba 3 prior to v3.0.4 +are vulnerable to two potential buffer overruns. The individual +details are given below. + + +------------- +CAN-2004-0600 +------------- + +Affected Versions: Samba 3.0.2 and later + +The internal routine used by the Samba Web Administration +Tool (SWAT v3.0.2 and later) to decode the base64 data +during HTTP basic authentication is subject to a buffer +overrun caused by an invalid base64 character. It is +recommended that all Samba v3.0.2 or later installations +running SWAT either (a) upgrade to v3.0.5, or (b) disable +the swat administration service as a temporary workaround. + +This same code is used internally to decode the +sambaMungedDial attribute value when using the ldapsam +passdb backend. While we do not believe that the base64 +decoding routines used by the ldapsam passdb backend can +be exploited, sites using an LDAP directory service with +Samba are strongly encouraged to verify that the DIT only +allows write access to sambaSamAccount attributes by a +sufficiently authorized user. + +The Samba Team would like to heartily thank Evgeny Demidov +for analyzing and reporting this bug. + + +------------- +CAN-2004-0686 +------------- + +Affected Versions: Samba 3.0.0 and later + +A buffer overrun has been located in the code used to support +the 'mangling method = hash' smb.conf option. Please be aware +that the default setting for this parameter is 'mangling method += hash2' and therefore not vulnerable. + +Affected Samba 3 installations can avoid this possible security +bug by using the default hash2 mangling method. Server +installations requiring the hash mangling method are encouraged +to upgrade to Samba 3.0.5. + + +################################################################## + + +Changes for older versions follow below: + + -------------------------------------------------- + ============================= + Release Notes for Samba 3.0.4 + May 8, 2004 + ============================= + + +Common bugs fixed in Samba 3.0.4 include: + + o Password changing after applying the patch described in + the Microsoft KB828741 article to Windows clients. + o Crashes in smbd. + o Managing print jobs via Windows on Big-Endian servers. + o Several memory leaks in winbindd and smbd. + o Compile issues on AIX and *BSD. + +Changes since 3.0.3 +-------------------- + +commits +------- + +o Jeremy Allison <jra@samba.org> + * Fix path processing for DeletePrinterDriverEx(). + * BUG 1303: Fix for Microsoft hotfix MS04-011 password change + breakage. + + +o Andrew Bartlett <abartlet@samba.org> + * Fix alignment bug in GetDomPwInfo(). + + +o Alexander Bokovoy <ab@samba.org> + * Fix utime[s]() issues in smbwrapper on systems + that can boot both the 2.4 and 2.6 Linux kernels. + + +o Gerald Carter <jerry@samba.org> + * Fedora packaging fixes. + * BUG 1302: Fix seg fault by not trying to optimize a list of + invalid gids using the wrong array size. + * BUG 1309: fix seg fault caused by trying to strdup(NULL) + seen when 'security = share'. + * Fix problems when using IBM's compiler on AIX. + * Link Developer's Guide, Example Guide, and multi-page HOWTO + into SWAT's welcome page. + * BUG 1293: fix double free in printer publishing code. + + +o Wim Delvaux <wim.delvaux@adaptiveplanet.com> + * Fix for handling timeouts in socket connections. + + +o Michel Gravey <michel.gravey@optogone.com> + * BUG 483: patch from to fix password hash creation in SWAT. + + +o Volker Lendecke <vl@samba.org> + * Close the open NT pipes before the tdis. + * Fix AFS related build issues. + * Handle error conditions when base64 encoding a blob of 0 bytes. + + +o Herb Lewis <herb@samba.org> + * Added 'acls' debug class. + +o kawasa_r@itg.hitachi.co.jp + * Multiple variable initialization and memory leak fixes. + + +o Stephan Kulow <coolo@suse.de> + * Fix string length bug in libsmbclient that caused KDE's + Konqueror to crash. + * BUG 429: More libsmbclient fixes. + + +o Jim McDonough <jmcd@us.ibm.com> + * BUG 1007, 1279: Store the print job using a little-endian key. + + +o Eric Mertens + o Compile fix for OpenBSD (ENOTSUP not supported). + + +o Stefan Metzmacher <metze@samba.org> + * Correct bug in disks quota views from explorer. + + +o Tim Potter <tpot@samba.org> + BUG 1305: Correct debug output. + + +o Richard Sharpe <rsharpe@samba.org> + * Fix incorrect error code mapping. + + +o Jelmer Vernooij <jelmer@samba.org> + * Add additional NT_STATUS errorm mappings. + + + -------------------------------------------------- + + ============================= + Release Notes for Samba 3.0.3 + April 29, 2004 + ============================= + + +Common bugs fixed in Samba 3.0.3 include: + + o Crash bugs and change notify issues in Samba's printing code. + o Honoring secondary group membership on domain member servers. + o TDB scalability issue surrounding the TDB_CLEAR_IF_FIRST flag. + o Substitution errors for %[UuGg] in smb.conf. + o winbindd crashes when using ADS security mode. + o SMB signing errors. + o Delays in winbindd startup caused by unnecessary + connections to trusted domain controllers. + o Various small memory leaks. + o Winbindd failing due to expired Kerberos tickets. + +New features introduced in Samba 3.0.3 include: + + o Improved support for i18n character sets. + o Support for account lockout policy based on + bad password attempts. + o Improved support for long password changes (>14 + characters) and strong password enforcement. + o Support for Windows aliases (i.e. nested groups). + o Experimental support for storing DOS attribute on files + and folders in Extended Attributes. + o Support for local nested groups via winbindd. + o Specifying options to be passed directly to the CUPS libraries. + +Please be aware that the Samba source code repository was +migrated from CVS to Subversion on April 4, 2004. Details on +accessing the Samba source tree via anonymous svn can be found +at http://svn.samba.org/samba/subversion.html. + + +Changes since 3.0.2a +-------------------- +smb.conf changes +---------------- + + Parameter Name Action + -------------- ------ + cups options New + ea support New + only user Deprecated + store dos attributes New + unicode Removed + winbind nested groups New + + +commits +------- + +o Jeremy Allison <jra@samba.org> + * Ensure that Kerberos mutex is always properly unlocked. + * Removed Heimdal "in-memory keytab" support. + * Fixup the 'multiple-vuids' bugs in our server code. + * Correct return code from lsa_lookup_sids() on unmapped + sids (based on work by vl@samba.org). + * Fix the "too many fcntl locks" scalability problem + raised by tridge. + * Fixup correct (as per W2K3) returns for lookupsids + as well as lookupnames. + * Fixups for delete-on-close semantics as per Win2k3 behavior. + * Make SMB_FILE_ACCESS_INFORMATION call work correctly. + * Fix "unable to initialize" bug when smbd hasn't been run with + new system and a user is being added via pdbedit/smbpasswd. + * Added NTrename SMB (0xA5). + * Fixup correct timeout values for blocking lock timeouts. + * Fix various bugs reported by 'gentest'. + * More locking fixes in the case where we own the lock. + * Fix up regression in IS_NAME_VALID and renames. + * Don't set allocation size on directories. + * Return correct error code on fail if file exists and target + is a directory. + * Added client "hardlink" comment to test doing NT rename with + hard links. Added hardlink_internals() code - UNIX extensions + now use this as well. + * Use a common function to parse all pathnames from the wire for + much closer emulation of Win2k3 error return codes. + * Implement check_path_syntax() and rewrite string sub + functions for better multibyte support. + * Ensure msdfs referrals are multibyte safe. + * Allow msdfs symlink syntax to be more forgiving. + eg. sym_link -> msdfs://server/share/path/in/share + or sym_link -> msdfs:\\server\share\path\in\share. + * Cleanup multibyte netbios name support in nmbd ( based on patch + by MORIYAMA Masayuki <moriyama@miraclelinux.com>). + * Fix check_path_syntax() for multibyte encodings which have + no '\' as second byte (based on work by ab@samba.org. + * Fix the "dfs self-referrals as anonymous user" problem + (based on patch from vl@samba.org). + * BUG 1064: Ensure truncate attribute checking is done correctly + on "hidden" dot files. + * Fix bug in anonymous dfs self-referrals again. + * Fix get/set of EA's in client library + * Added support for OS/2 EA's in smbd server. + * Added 'ea support' parameter to smb.conf. + * Added 'store dos attributes' parameter to smb.conf. + * Fix wildcard identical rename. + * Fix reply_ctemp - make compatible with w2k3. + * Fix wildcard unlink. + * Fix wildcard src with wildcard dest renames. + * BUG 1139: Fix based on suggestion by jdev@panix.com. + swap lookups for user and group - group will do an + algorithmic lookup if it fails, user won't. + * Make EA's lookups case independent. + * Fix SETPATHINFO in 'unix extensions' support. + * Make 3.x pass the Samba 4.x RAW-SEARCH tests - except for + the UNIX info levels, and the short case preserve names. + + +o Timur Bakeyev <timur@com.bat.ru> + * BUG 1144: only set --with-fhs when the argument is 'yes' + * BUG 1152: Allow python modules to build despite libraries added + to LDFLAGS instead of LDPATH. + * BUG 1141: Fix nss*.so names on FreeBSD 5.x. + + +o Craig Barratt <cbarratt@users.sourceforge.net> + * BUG 389: Allow multiple exclude arguments with smbclient + tar -Xr options (better support for Amanda backup client). + + +o Andrew Bartlett <abartlet@samba.org> + * Include support for linking with cracklib for enforcing strong + password changes. + * Add support for >14 character password changes from Windows + clients. + * Add 'admin set password' capability to 'net rpc'. + * Allow 'net rpc samdump' to work with any joined domain + regardless of smb.conf settings. + * Use an allocated buffer for count_chars. + * Add sanity checks for changes in the domain SID in an + LDAP DIT. + * Implement python unit tests for Samba's multibyte string + support. + * Remove 'unicode' smb.conf option. + * BUG 1138: Fix support for 'optional' SMB signing and other + signing bugs. + * BUG 169: Fix NTLMv2-only behavior. + * Ensure 'net' honors the 'netbios name' in the smb.conf by + default. + * Support SMB signing on connections using only the LANMAN + password and generate the correct the 'session key' for these + connections. + * Implement --required-membership-of=, an ntlm_auth option + that restricts all authentication to members of this particular + group. + * Improve our fall back code for password changes. + * Only send the ntlm_auth 'ntlm-server-1' helper client a '.' + after the server had said something (such as an error). + * Add 'ntlm-server-1' helper protocol to ntlm_auth. + + +o Alexander Bokovoy <ab@samba.org> + * Fix incorrect size calculation of the directory name + in recycle.so. + * Fix problems with very long filenames in both smbd and smbclient + caused by truncating paths during character conversions. + * Fix smbfs problem with Tree Disconnect issued before smbfs + starts its work. + + +o Gerald Carter <jerry@samba.org> + * BUG 850: Fix 'make installmodules' bug on True64. + * BUG 66: mark 'only user' deprecated. + * Remove corrupt tdb and shutdown (only for printing tdbs, + connections, sessionid & locking). + * decrement smbd counter in connections.tdb in smb_panic(). + * RedHat specfile updates. + * Fix xattr.h build issue on Debian testing and SuSE 8.2. + * BUG 1147; bad pointer case in get_stored_queue_info() + causing seg fault. + * BUG 761: read the config file before initialized default + values for printing options; don't default to bsd printing + Linux. + * Allow the 'printing' parameter to be set on a per share basis. + * BUG 503: RedHat/Fedora packaging fixes regarding logrotate. + * BUG 848: don't create winbind local users/groups that already + exist in the tdb. + * BUG 1080: fix declaration of SMB_BIG_UINT (broke compile on + LynxOS/ppc). + * BUG 488: fix the 'show client in col 1' button and correctly + enumerate active connections. + * BUG 1007 (partial): Fix abort in smbd caused by byte ordering + problem when storing the updating pid for the lpq cache. + * BUG 1007 (partial): Fix print change notify bugs. + * BUG 1165, 1126: Fix bug with secondary groups (security = ads) + and winbind use default domain = yes. Also ensures that + * BUG 1151: Ensure that winbindd users are passed through + the username map. + * Fix client rpc binds for ASU derived servers (pc netlink, + etc...). + * BUG 417, 1128: Ensure that the current_user_info is set + consistently so that %[UuGg] is expanded correctly. + * BUG 1195: Fix crash in winbindd when the ADS server is + unavailable. + * BUG 1185: Set reconnect time to be the same as the + 'winbind cache time'. + * Ensure that we return the sec_desc in smb_io_printer_info_2. + * Change Samba printers Win32 attribute to PRINTER_ATTRIBUTE_LOCAL. + * BUG 1095: Honor the '-l' option in smbclient. + * BUG 1023: surround get_group_from_gid() with become_unbecome_root() + block. + * Ensure server schannel uses the auth level requested by the + client. + * Removed --with-cracklib option due to potential crash issue. + * Fix -lcrypto linking problem with wbinfo. + * BUG 761: allow printing parameter to set defaults on a per + share basis. + * Add 'cups options' parameter to allow raw printing without + changing /etc/cups/cupsd.conf. + * BUG 1081, 1183: Added remove_duplicate_gids() to smbd and + winbindd. + * BUG 1246: Fix typo in Fedora /etc/init.d/winbind. + * BUG 1288: resolve any machine netbios name (0x00) and not just + servers (0x20). + * BUG 1199: Fix potential symlink issue in + examples/printing/smbprint. + + +o Robert Dahlem <Robert.Dahlem@gmx.net> + * BUG 1048: Don't return short names when when 'mangled names = no' + + +o Guenther Deschner <gd@suse.com> + * Remove hard coded attribute name in the ads ranged retrieval + code. + * Add --with-libdir and --with-mandir to autoconf script. + + +o Bostjan Golob <golob@gimb.org> + * BUG 1046: Fix getpwent_list() so that the username is not + overwritten by other fields. + + +o Landon Fuller <landonf@opendarwin.org> + * BUG 1232: patch from landonf@opendarwin.org (Landon Fuller) + to fix user/group enumeration on systems whose libc does not + call setgrent() before trying to enumerate users (i.e. + FreeBSD 5.2). + + +o Steve French <sfrench@us.ibm.com> + * Update mount.cifs to version 1.1. + * Disable dev (MS_NODEV) on user mounts from cifs vfs. + * Fixes to minor security bug in the mount helper. + * Fix credential file mounting for cifs vfs. + * Fix free of incremented pointer in cifsvfs mount helper. + * Fix path canonicalization of the mount target path and help + text display in the cifs mount helper. + * Add missing guest mount option for mount.cifs. + + +o SATOH Fumiyasu <fumiya@miraclelinux.com> + * BUG 1055; formatting fixes for 'net share'. + * BUG 692: correct truncation of share names and workgroup + names in smbclient. + * BUG 1088: use strchr_m() for query_host (smbclient -L). + * Patch from to internally count characters correctly. + + +o Paul Green <paulg@samba.org> + * Update VOS _POSIX_C_SOURCE macro to 200112L. + * Fix bug in configure.ion by moving the first use of + AC_CHECK_HEADERS so it is always executed. + * Fix configure.in to only use $BLDSHARED to select whether to + build static or shared libraries. + + +o Pat Haywarrd <Pat.Hayward@propero.net> + * Make the session_users list dynamic (max of 128K). + + +o Cal Heldenbrand <calzplace@yahoo.com> + * Fix for for 'pam_smbpass migrate' functionality. + + +o Chris Hertel <crh@samba.org> + * fix enumeration of shares 12 characters in length via + smbclient. + + +o Ulrich Holeschak <ulrich@holeschak.de> + * BUG 932: fix local password change using pam_smbpass + + +o Krischan Jodies <kj@sernet.de> + * Implement 'net rpc group delete' + + +o John Klinger <john.klinger@lmco.com> + * Return NSS_SUCCESS once the max number of gids possible + has been found in initgroups() on Solaris. + * BUG 1182: Re-enable the -n 'no cache' option for winbindd. + + +o Volker Lendecke <vl@samba.org> + * Fix success message for net groupmap modify. + * Fix errors when enumerating members of groups in 'net rpc'. + * Match Windows behavior in samr_lookup_names() by returning + ALIAS(4) when you search in BUILTIN. + * Fix server SAMR code to be able to set alias info for + builtin as well. + * Fix duplication of logic when creating groups via smbd. + * Ensure that the HWM values are set correctly after running + 'net idmap'. + * Add 'net rpc group add'. + * Implement 'net groupmap set' and 'net groupmap cleanup'. + * Add 'net rpc group [add|del]mem' for domain groups and aliases. + * Fix wb_delgrpmem (wbinfo -o). + * As a DC we should not reply to lsalookupnames on DCNAME\\user. + * Fix sambaUserWorkstations on a Samba DC. + * Implement wbinfo -k: Have winbind generate an AFS token after + authenticating the user. + * Add expand_msdfs VFS module for providing referrals based on the + the client's IP address. + * Implement client side NETLOGON GetDCName function. + * Fix caching of name->sid lookups. + * Add support in winbindd for expanding nested local groups. + * Fix memleak in winbindd. + * Fix msdfs proxy. + * Don't list domain groups from BUILTIN. + * Fix memleak in policy handle utility functions. + * Decrease winbindd startup time by only contacting trusted + domains as necessary. + * Allow winbindd to ask the DC for its domain for a trusted + DC. + * Fix Netscape DS schema based on comments from + <thomas.mueller@christ-wasser.de>. + * Correct case where adding a domain user to a XP local group + did a lsalookupname on the user without domain prefix, and + failed. + * Fix segfault in winbindd caused by 'wbinfo -a'. + + +o Herb Lewis <herb@samba.org> + * Fix typo for tag in proto file. + * Add missing #ifdef HAVE_BICONV stuff. + * Truncate Samba's netbios name at the first '.' (not + right to left). + + +o Derrell Lipman <Derrell.Lipman@UnwiredUniverse.com> + * Bug fixes and enhancements to libsmbclient library. + + +o Jianliang Lu <j.lu@tiesse.com> + * Enforce the 'user must change password at next login' flag. + * Decode meaning of 'fields present' flags (improves support + for usrmgr.exe). + * NTLMv2 fixes. + * Don't force an upper case domain name in the ntlmssp code. + + +o L. Lucius <ib@digicron.com>. + * type fixes. + + +o Jim McDonough <jmcd@us.ibm.com> + * Add versioning support to tdbsam. + * Update the IBM Directory Server schema with the OpenLDAP + file. + * Various decoding fixes to improve usrmgr.exe support. + * Fix statfs redeclaration of statfs struct on ppc + * Implement support for password lockout of Samba domain + controllers and standalone servers. + * Get MungedDial attribute actually working with full TS + strings in it for pdb_ldap. + * BUG 1208 (partial): Improvements for working with expired krb5 + tickets in winbindd. + * Use timegm, or our already existing replacement instead of + timezone (spotted by Andrzej Tobola <san@iem.pw.edu.pl>). + * Remove modifyTimestamp from list of our attributes. + * Fix lsalookupnames to check for domain users as well as local + users. + * Merge struct uuid replacement for GUID from trunk. + * BUG 1208: Finish support for handling expired tickets in + winbindd (in conjunction with Guenther Deschner <gd@suse.de>). + + +o Stefan Metzmacher <metze@samba.org> + * Implement new VERSION schema based on subversion revision + numbers. + * Add shadow_copy vfs module. + * Fix segault in login_cache support. + + +o Heinrich Mislik <Heinrich.Mislik@univie.ac.at> + o BUG 979 -- Fix quota display on AIX. + + +o James Peach <jpeach@sgi.com> + * Correct check for printf() format when using the SGI MIPSPro + compiler. + * BUG 1038: support backtrace for 'panic action' on IRIX. + * BUG 768: Accept profileing arg to IRIX init script. + * BUG 748: Relax arg parsing to sambalp script (IRIX). + * BUG 758: Fix pdma build. + * Search IRIX ABI paths for libiconv. Based on initial fix from + Jason Mader. + + +o Kurt Pfeifle <kpfeifle@danka.de> + * Add example shell script for migrating drivers and printers + from a Windows print server to a Samba print server using + smbclient/rpcclient (examples/printing/VamireDriversFunctions). + + +o Tim Potter <tpot@samba.org> + * Fix logic bug in tdb non-blocking lock routines when + errno == EAGAIN. + * BUG 1025: Include sys/acl.h in check for broken nisplus + include files. + * BUG 1066: s/printf/d_printf/g in SWAT. + * BUG 1098: rename internal msleep() function to fix build + problems on AIX. + * BUG 1112: Fix for writable printerdata problem in python bindings. + * BUG 1154: Remove reference to <sys/mman.h> in tdbdump.c. + * BUG 1155: enclose use of fchown() with guards. + * Relicense tdb python module as LGPL. + + +o Richard Sharpe <rsharpe@samba.org> + * Add support to smbclient for multiple logins on the same + session (based on work by abartlet@samba.org). + * Correct blocking condition in smbd's use of accept() on IRIX. + * Add support for printing out the MAC address on nmblookup. + + +o Simo Source <idra@samba.org> + * Replace unknown_3 with fields_present in SAMR code. + * More length checks in strlcat(). + + +o Andrew Tridgell <tridge@samba.org> + * Rewrote the AIX UESS backend for winbindd. + * Fixed compilation with --enable-dmalloc. + * Change tdb license to LGPL (see source/tdb/tdb.c). + * Force winbindd to use schannel in clients connections to + DC's if possible. + + +o Jelmer Vernooij <jelmer@samba.org> + * Fix ETA Calculation when resuming downloads in smbget. + * Add -O (for writing downloaded files to standard out) + based on patch by Bas van Sisseren <bas@dnd.utwente.nl>. + * Fix syntax error in example mysql table + + +o TAKEDA yasuma <yasuma@miraclelinux.com> + * BUG 900: fix token processing in cmd_symlink, cmd_link, + cmd_chown, cmd_chmod smbclient functions. + + +o Shiro Yamada <shiro@miraclelinux.com> + * BUG 1129: install image files for SWAT. + + + -------------------------------------------------- + + ============================== + Release Notes for Samba 3.0.2a + February 13, 2004 + ============================== + +Samba 3.0.2a is a minor patch release for the 3.0.2 code base +to address, in particular, a problem when using pdbedit to +sanitize (--force-initialized-passwords) Samba's tdbsam +backend. This is the latest stable release of Samba. This +is the version that all production Samba servers should be +running for all current bug-fixes. + +******************* Attention! Achtung! Kree! ********************* + +Beginning with Samba 3.0.2, passwords for accounts with a last +change time (LCT-XXX in smbpasswd, sambaPwdLastSet attribute in +ldapsam, etc...) of zero (0) will be regarded as uninitialized +strings. This will cause authentication to fail for such +accounts. If you have valid passwords that meet this criteria, +you must update the last change time to a non-zero value. If you +do not, then 'pdbedit --force-initialized-passwords' will disable +these accounts and reset the password hashes to a string of X's. + +******************* Attention! Achtung! Kree! ********************* + + +Changes since 3.0.2 +------------------- + +commits +------- + +Please refer to the CVS log for the SAMBA_3_0 branch for complete +details. The list of changes per contributor are as follows: + + +o Jeremy Allison <jra@samba.org> + * Added paranoia checks in parsing code. + + +o Andrew Bartlett <abartlet@samba.org> + * Ensure that changes to uninitialized passwords in ldapsam + are written to the DIT. + + +o Gerald (Jerry) Carter <jerry@samba.org> + * Fixed iterator in tdbsam. + * Fix bug that disabled accounts with a valid NT password + hash, but no LanMan hash. + + +o Steve French <sfrench@us.ibm.com> + * Added missing nosetuid and noexec options. + + +o Bostjan Golob <golob@gimb.org> + * BUG 1046: Don't overwrite usernames of entries returned + by getpwent_list(). + + +o Sebastian Krahmer <krahmer@suse.de> + * Fixed potential crash bug in NTLMSSP parsing code. + + +o Tim Potter <tpot@samba.org> + * Fixed logic in tdb_brlock error checking. + + +o Urban Widmark <urban@teststation.com> + * Set nosuid,nodev flags in smbmnt by default. + + + -------------------------------------------------- + + ============================= + Release Notes for Samba 3.0.2 + February 9, 2004 + ============================= + +It has been confirmed that previous versions of Samba 3.0 are +susceptible to a password initialization bug that could grant an +attacker unauthorized access to a user account created by the +mksmbpasswd.sh shell script. + +The Common Vulnerabilities and Exposures project (cve.mitre.org) +has assigned the name CAN-2004-0082 to this issue. + +Samba administrators not wishing to upgrade to the current +version should download the 3.0.2 release, build the pdbedit +tool, and run + + root# pdbedit-3.0.2 --force-initialized-passwords + +This will disable all accounts not possessing a valid password +(e.g. the password field has been set a string of X's). + +Samba servers running 3.0.2 are not vulnerable to this bug +regardless of whether or not pdbedit has been used to sanitize +the passdb backend. + +Some of the more visible bugs in 3.0.1 addressed in the 3.0.2 +release include: + + o Joining a Samba domain from Pre-SP2 Windows 2000 clients. + o Logging onto a Samba domain from Windows XP clients. + o Problems with the %U and %u smb.conf variables in relation to + Windows 9x/ME clients. + o Kerberos failures due to an invalid in memory keytab detection + test. + o Updates to the ntlm_auth tool. + o Fixes for various SMB signing errors. + o Better separation of WINS and DNS queries for domain controllers. + o Issues with nss_winbind FreeBSD and Solaris. + o Several crash bugs in smbd and winbindd. + o Output formatting fixes for smbclient for better compatibility + with scripts based on the 2.2 version. + + +Changes since 3.0.1 +------------------- + +smb.conf changes +---------------- + + Parameter Name Action + -------------- ------ + ldap replication sleep New + read size removed (unused) + source environment removed (unused) + + +commits +------- + +Please refer to the CVS log for the SAMBA_3_0 branch for complete +details. The list of changes per contributor are as follows: + +o Jeremy Allison <jra@samba.org> + * Revert change that broke Exchange clear text samlogons. + * Fix gcc 3.4 warning in MS-DFS code. + * Tidy up of NTLMSSP code. + * Fixes for SMB signing errors + * BUG 815: Workaround NT4 bug to support plaintext + password logins and UNICODE. + * Fix SMB signing bug when copying large files. + * Correct error logic in mkdir_internals() (caused a panic + when combined with --enable-developer). + * BUG 830: Protect against crashes due to bad character + conversions. + + +o Petri Asikainen <paca@sci.fi> + * BUG 330, 387:Fix single valued attribute updates when + working with Novell NDS. + + +o Andrew Bartlett <abartlet@samba.org> + * Correctly handle per-pipe NTLMSSP inside a NULL session. + * Fix segfault in gencache + * Fix early free() of encrypted_session_key. + * Change DC lookup routines to more carefully separate + DNS names (realms) from NetBIOS domain names. + * Add new sid_to_dn() function for internal winbindd use. + * Refactor cli_ds_enum_domain_trusts(). + * BUG 707: Implement range retrieval of ADS attributes (based + on work from Volker <vl@samba.org> and Guenther Deschner + <gd@suse.com>). + * Automatically initialize the signing engine if a session key + is available. + * BUG 916: Do not perform a + -> ' ' substitution for squid URL + encoded strings, only form input in SWAT. + * Resets the NTLMSSP state for new negotiate packets. + * Add 2-byte alignments in net_samlogon() queries to parse + odd-length plain text passwords. + * Allow Windows groups with no members in winbindd. + * Allow normal authentication in the absence of a server + generated session key. + * More optimizations for looking up UNIX group lists. + * Clean up error codes and return values for pam_winbindd + and winbindd PAM interface. + * Fix string return values in ntlm_auth tool. + * Fix segfault when 'security = ads' but no realm is defined. + * BUG 722: Allow winbindd to map machine accounts to uids. + * More cleanups for winbindd's find_our_domain(). + * More clearly detect whether a domain controller is an NT4 + or mixed-mode AD DC (additional bug fixes by jerry & jmcd). + * Increase separation between DNS queries for hosts and queries + for AD domain controllers. + * Include additional NT_STATUS to PAM error mappings. + * Password initialization fixes. + + +o Justin Baugh <justin.baugh@request.com> + * BUG 948: Implement missing functions required for FreeBSD + nss_winbind support. + + +o Alexander Bokovoy <ab@samba.org> + * BUG 922: Make sure enable fast path for strlower_m() and + strupper_m(). + + +o Luca Bolcioni <Luca.Bolcioni@yacme.com> + * Fix crash when using 'security = server' and 'encrypt + passwords = no' by always initializing the session key. + + +o Dmitry Butskoj <buc@odusz.elektra.ru> + * Fix for special files being hidden from admins. + + +o Gerald (Jerry) Carter <jerry@samba.org> + * Fix bug in the lanman session key generation. Caused + "decode_pw: incorrect password length" error messages. + * Save the right case for the located user name in + fill_sam_account(). Fixes %U/%u expansion for win9x clients. + * BUG 897: Add well known rid for pre win2k compatible access + group. + * BUG 887: Correct typo in delete user script example. + * Use short lived TALLOC_CTX* for allocating printer objects + from the print handle cache. + * BUG 912: Fix check for HAVE_MEMORY_KEYTAB. + * Fix several warnings reported by the SUN Forte C compiler. + * Fully control DNS queries for AD DC's using 'name resolve order'. + * BUG 770: Send the SMBjobid for UNIX jobs back to the client. + * BUG 972: Fix segfault in cli_ds_getprimarydominfo(). + * BUG 936: fix bind credentials for schannel binds in smbd. + * BUG 446: Fix output of smbclient for better compatibility + with scripts based on the 2.2 version (including Amanda). + * BUG 891, 949: Fedora packaging fixes. + * Fix bug that caused rpcclient to incorrectly retrieve + the SID for a server (this causing all calls that required + this information to fail). + * BUG 977: Don't create a homes share for a user if a static + share already exists by the same name. + * Removed unused smb.conf options. + * Password initialization fixes. + * Set the disable flag for template accounts created by + mksmbpasswd.sh. + * Disable any account has no passwords and does not have the + ACB_PWNOTREQ bit set. + + +o Guenther Deschner <gd@suse.com> + * Install smbwrapper.so should be put into the $(libdir) + and not $(bindir). + * Add the capability to specify the new user password + for "net ads password" on the command line. + * Correctly detect AFS headers on SuSE. + + +o James Flemer <jflemer@uvm.edu> + * Fix AIX compile bug by linking HAVE_ATTR_LIST to + HAVE_SYS_ATTRIBUTES_H. + + +o Luke Howard <lukeh@PADL.COM> + * Fix segfault in session setup reply caused by a early free(). + + +o Stoian Ivanov <sdr@bultra.com> + * Implement grepable output for smbclient -L. + + +o LaMont Jones <lamont@debian.org> + * BUG 225328 (Debian): Correct false failure LFS test that resulted + in _GNU_SOURCE not being defined (thus resulting in strndup() + not being defined). + + +o Volker Lendecke <vl@samba.org> + * BUG 583: Ensure that user names always contain the short + version of the domain name. + * Fix our parsing of the LDAP uri. + * Don't show the 'afs username map' in the SWAT basic view. + * Fix SMB signing issues in relation to failed NTLMSSP logins. + * BUG 924: Fix return codes in smbtorture harness. + * Always lower-case usernames before handing it to AFS code. + * Add a German translation for SWAT. + * Fix a segfaults in winbindd. + * Fix the user's domain passed to register_vuid() from + reply_spnego_kerberos(). + * Add NSS example code in nss_winbind to convert UNIX + id's <-> Windows SIDs. + * Display more descriptive error messages for login via 'net'. + * Fix compiler warning in the net tool. + * Fix length bug when decoding base64 strings. + * Ensure we don't call getpwnam() inside a loop that is iterating + over users with getpwent(). This broke on glibc 2.3.2. + + +o Herb Lewis <herb@samba.org> + * Fix bit rot in psec. + + +o Jianliang Lu <j.lu@tiesse.com> + * Ensure we delete the group mapping before calling the delete + group script. + * Define well known RID for managing the "Power Users" group. + * BUG 381: check builtin (not local) group SID when updating + group membership. + * BUG 101: set the SV_TYPE_PRINTQ_SERVER flag in host announcement + packet. + + +o John Klinger <john.klinger@lmco.com> + * Implement initgroups() call in nss_winbind on Solaris. + + +o Jim McDonough <jmcd@us.ibm.com> + * Fix regression in net rpc join caused by recent changes + to cli_lsa_query_info_policy(). + * BUG 964: Fix crash bug in 'net rpc join' using a preexisting + machine account. + + +o MORIYAMA Masayuki <moriyama@miraclelinux.com> + * BUG 570: Ensure that configure honors the LDFLAGS variable. + + +o Stefan Metzmacher <metze@samba.org> + * Implement LDAP rebind sleep patch. + * Revert to 2.2 quota code because of so many broken quota files + out there. + * Fix XFS quotas: HAVE_XFS_QUOTA -> HAVE_XFS_QUOTAS + XFS_USER_QUOTA -> USRQUOTA + XFS_GROUP_QUOTA -> GRPQUOTA + * Fix disk_free calculation with group quotas. + * Add debug class 'quota' and a lot of DEBUG()'s + to the quota code. + * Fix sys_chown() when no chown() is present. + * Add SIGABRT to fault handling in order to catch got a + backtrace if an error occurs the OpenLDAP client libs. + + +o <ndb@theghet.to> + * Allow an existing LDAP machine account to be re-used when + joining an AD domain. + + +o James Peach <jpeach@sgi.com> + * BUG 889: Change smbd to use pread/pwrite on platforms that + support these calls. Can lead to a significant speed increase. + + +o Tim Potter <tpot@samba.org> + * BUG 905: Remove POBAD_CC to fix Solaris Forte compiles. + * BUG 924: Fix typo in RW2 torture test. + + +o Richard Sharpe <shape@samba.org> + * Small fixes to torture.c to cleanup the error handling + and prevent crashes. + + +o J. Tournier <jerome.tournier@IDEALX.com> + * Small fixes for the smbldap-tool scripts. + + +o Andrew Tridgell <tridge@samba.org> + * Fix src len check in pull_usc2(). + + +o Jelmer Vernooij <jelmer@samba.org> + * Put functions for generating SQL queries in pdb_sql.c + * Add pgSQL backend (based on patch by Hamish Friedlander) + * BUG 908: Fix -s option to smbcontrol. + * Add smbget utility - a wget-clone for the SMB/CIFS protocol. + * Fix for libnss_wins on IRIX platforms. + * Fix swatdir for --with-fhs. + + + -------------------------------------------------- + + ============================= + Release Notes for Samba 3.0.1 + December 15, 2003 + ============================= + +Some of the more common bugs in 3.0.0 addressed in the release +include: + + o Substitution problems with smb.conf variables. + o Errors in return codes which caused some applications + to fail to open files. + o General Protection Faults on Windows 2000/XP clients + using Samba point-n-print features. + o Several miscellaneous crash bugs. + o Access problems when enumerating group mappings are + stored in an LDAP Directory. + o Several common SWAT bugs when writing changes to + smb.conf. + o Internal inconsistencies when 'winbind use default + domain = yes' + + + +Changes since 3.0.0 +---------------------- + + Parameter Name Action + -------------- ------ + hide local users Removed + mangled map Deprecated + mangled stack Removed + passwd chat timeout New + + +commits +------- + +o Change the interface for init_unistr2 to not take a length + but a flags field. We were assuming that + 2*strlen(mb_string) == length of ucs2-le string. (bug 480). +o Allow d_printf() to handle strings with escaped quotation + marks since the msg file includes the escape character (bug 489). +o Fix bad html table row termination in SWAT wizard code (bug 413). +o Fix to parse the level-2 strings. +o Fix for "valid users = %S" in [homes]. Fix read/write + list as well. +o Change AC_CHECK_LIB_EXT to prepend libraries instead of append. + This is the same way AC_CHECK_LIB works (bug 508). +o Testparm output fixes for clarity. +o Fix broken wins hook functionality -- i18n bug (bug 528). +o Take care of condition where DOS and NT error codes must differ. +o Default to using only built-in charsets when a working iconv + implementation cannot be located. +o Wrap internals of sys_setgroups() so the sys_XX() call can + be done unconditionally (bug 550). +o Remove duplicate smbspool link on SWAT's front page (bug 541). +o Save and restore CFLAGS before/after AC_PROG_CC. Ensures that + --enable-debug=[yes|no] works correctly. +o Allow ^C to interrupt smbpasswd if using our getpass + (e.g. smbpasswd command). +o Support signing only on RPC's (bug 167). +o Correct bug that prevented Excel 2000 clients from opening + files marked as read-only. +o Portability fix bugs 546 - 549). +o Explicitly initialize the value of AR for vendor makes that don't + do this (e.g. HPUX 11). (bug 552). +o More i18n fixes for SWAT (bug 413). +o Change the cwd before the postexec script to ensure that a + umount will succeed. +o Correct double free that caused winbindd to crash when a DC + is rebooted (bug 437). +o Fix incorrect mode sum (bug 562). +o Canonicalize SMB_INFO_ALLOCATION in the same was as + SMB_FS_FULL_SIZE_INFORMATION (bug 564). +o Add script to generate *msg files. +o Add Dutch SWAT translation file. +o Make sure to call get_user_groups() with the full winbindd + name for a user if he/she has one (bug 406). +o Fix up error code returns from Samba4 tester. Ensure invalid + paths are validated the same way. +o Allow Samba3 to pass the Samba4 RAW-READ tests. +o Refuse to configure if --with-expsam=$BACKEND was used but no + libraries were found for $BACKEND. +o Move sysquotas autoconf tests to a separate file. +o Match W2K w.r.t. writelock and writeclose. Samba4 torture + tester +o Make sure that the files that contain the static_init_$subsystem; + macro get recompiled after configure by removing the object + files. +o Ensure canceling a blocking lock returns the correct error + message. +o Match Samba 2.2 behavior; make ACB_NORMAL the default ACB value. +o Updated Japanese welcome file in SWAT. +o Fix to nt-time <-> unix-time functions reversible. +o Ensure that winbindd uses the the escaped DN when querying + an AD ldap server. +o Fix portability issues when compiling (bug 505, 550) +o Compile fix for tdbbackup when Samba needs to override + non-C99 compliant implementations of snprintf(). +o Use @PICSUFFIX@ instead of .po in Makefile.in (bug 574). +o Make sure we break out of samsync loop on error. +o Ensure error code path doesn't free unmalloc()'d memory + (bug 628). +o Add configure test for krb5_keytab_entry keyblock vs key + member (bug 636). +o Fixed spinlocks. +o Modified testparm so that all output so all debug output goes + to stderr, and all file processing goes to stdout. +o Fix error return code for BUFFER_TOO_SMALL in smbcacls + and smbcquotas. +o Fix "NULL dest in safe_strcpy()" log message by ensuring that + we have a devmode before copying a string to the devicename. +o Support mapping REALM.COM\user to a local user account (without + running winbindd) for compatibility with 2.2.x release. +o Ensure we don't use mmap() on blacklisted systems. +o fixed a number of bugs and memory leaks in the AIX + winbindd shim +o Call initgroups() in SWAT before becomming the user so that + secondary group permissions can be used when writing to + smb.conf. +o Fix signing problems when reverse connecting back to a + client for printer notify +o Fix signing problems caused by a miss-sequence bug. +o Missing map in errormap for ERROR_MORE_DATA -> ERRDOS, ERRmoredata. + Fixes NEXUS tools running on Win9x clients (bug 64). +o Don't leave the domain field uninitialized in cli_lsa.c if some + SID could not be mapped. +o Fix segfault in mount.cifs helper when there is no options + specified during mount. +o Change the \n after the password prompt to go to tty instead + of stdout (bug 668). +o Stop net -P from prompting for machine account password (bug 451). +o Change in behavior to Not only change the effective uid but also + the real uid when becoming unprivileged. +o Cope with Exchange 5.5 cleartext pop password auth. +o New files for support of initshutdown pipe. Win2k doesn't + respond properly to all requests on the winreg pipe, so we need + to handle this new pipe (bug 534). +o Added more va_copy() checks in configure.in. +o Include fixes for libsmbclient build problems. +o Missing UNIX -> DOS codepage conversion in lanman.c. +o Allow DFMS-S filenames can now have arbitrary case (bug 667). +o Parameterize the listen backlog in smbd and make it larger by + default. A backlog of 5 is way too small these days. +o Check for an invalid fid before dereferencing the fsp pointer + (bug 696). +o Remove invalid memory frees and return codes in pdb_ldap.c. +o Prompt for password when invoking --set-auth-user and no + password is given. +o Bind the nmbd sending socket to the 'socket address'. +o Re-order link command for smbd, rpcclient and smbpasswd to ensure + $LDFLAGS occurs before any library specification (bug 661). +o Fix large number of printf() calls for 64-bit size_t. +o Fix AC_CHECK_MEMBER so that SLES8 does correctly finds the + keyblock in the krb5 structs. +o Remove #include <compat.h> in hopes to avoid problems with + apache header files. +o Correct winbindd build problems on HP-UX 11. +o Lowercase netgroups lookups (bug 703). +o Use the actual size of the buffer in strftime instead of a made + up value which just happens to be less than sizeof(fstring). + (bug 713). +o Add ldaplibs to pdbedit link line (bug 651). +o Fix crash bug in smbclient completion (bug 659). +o Fix packet length for browse list reply (bug 771). +o Fix coredump in cli_get_backup_list(). +o Make sure that we expand %N (bug 612). +o Allow rpcclient adddriver command to specify printer driver + version (bug 514). +o Compile tdbdump by default. +o Apply patches to fix iconv detection for FreeBSD. +o Do not allow the 'guest account' to be added to a passdb backend + using smbpasswd or pdbedit (bug 624). +o Save LDFLAGS during iconv detection (bug 57). +o Run krb5 logins through the username map if the winbindd + lookup fails (bug 698). +o Add const for lp_set_name_resolve_order() to avoid compiler + warnings (bug 471). +o Add support for the %i macro in smb.conf to stand in for the for + the local IP address to which a client connected. +o Allow winbindd to match local accounts to domain SID when + 'winbind trusted domains only = yes' (bug 680). +o Remove code in idmap_ldap that searches the user suffix and group + suffix. It's not needed and provides inconsistent functionality + from the tdb backend. +o Patch to handle munged dial string for Windows 2000 TSE. + Thanks to Gaz de France, Direction de la Recherche, Service + Informatique Métier for their supporting this work by Aurelien + Degrémont <adegremont@idealx.com>. +o Correct the "smbldap_open: cannot access when not root error" + messages when looking up group information (bug 281). +o Skip over the winbind separator when looking up a user. + This fixes the bug that prevented local users from + matching an AD user when not running winbindd (bug 698). +o Fix a problem with configure on *BSD systems. Make sure + we add -liconv etc to LDFLAGS. +o Fix core dump bug when "security = server" and the authentication + server goes away. +o Correct crash bug due to an empty munged dial string. +o Show files locked by a specific user (smbstatus -u 'user') + (bug 590). +o Fix bug preventing print jobs from display in the queue + monitor used by Windows NT and later clients (bug 660). +o Fix several reported problems with point-n-print from + Windows 2000/XP clients due to a bug in the EnumPrinterDataEx() + reply (bug 338, 527 & 643). +o Fix a handful of potential memory leaks in the LDAP code used + by ldapsam[_compat] and the LDAP idmap backend. +o Fix for pdbedit error code returns (bug 763). +o Make sure we only enumerate group mapping entries (not + /etc/group) even when doing local aliases. +o Relax check on the pipe name in a dce/rpc bind response to work + around issues with establishing trusts to a Windows 2003 domain. +o Ensure we mangle names ending in '.' in hash2 mangling method. +o Correct parsing issues with munged dial string. +o Fix bugs in quota support for XFS. +o Add a cleaner method for applications that need to provide + name->SID mappings to do this via NSS rather than having to + know the winbindd pipe protocol. +o Adds a variant of the winbindd_getgroups() call called + winbindd_getusersids() that provides direct SID->SIDs listing of + a users supplementary groups. This is enough to allow non-Samba + applications to do ACL checking. +o Make sure we don't append the 'ldap suffix' when writing out the + 'ldap XXX suffix' values in SWAT (bug 328). +o Fix renames across file systems. +o Ensure that items in a list of strings containing whitespace are + written out surrounded by single quotes. This means that both + double and single quotes are now used to surround strings in + smb.conf (bug 481). +o Enable SWAT to correctly determine if winbindd is running (bug + 398). +o Include WWW-Authenticate field in 401 response for bad auth + attempt (bug 629). +o Add support for NTLM2 (NTLMv2 session security). +o Add support for variable-length session keys. +o More privilege fixes for group enumeration in LDAP (bug 281). +o Use the dns name (or IP) as the originating client name when + using CUPS (bug 467). +o Fix various SMB signing bugs. +o Fix ACL propagation on a DFS root (bug 263). +o Disable NTLM2 for RPC pipes. +o Allow the client to specify the NTLM2 flags got NTLMSSP + authentication. +o Change the name of the job passed off to cups from "Test Page" + to "smbprn.00000033 Test Page" so that we can get the smb + jobid back. This allow users to delete jobs with cups printing + backend (partial work on bug 770). +o Fix build of winbindd with static pdb modules. +o Retrieve the correct ACL group bits if the file has an ACL + (bug 802). +o Implement "net rpc group members": Get members of a domain group + in human-readable format. +o Add MacOSX (Darwin) specific charset module code. +o Use samr_dispinfo(level == 1) for enumerating domain users so we + can include the full name in gecos field (bug 587). +o Add support for winbind's NSS library on FeeeBSD 5.1 (bug 797). +o Implement 'net rpc group list [global|local|builtin]*' for a + select listing of the respective user databases. +o Don't automatically set NT status code flag unless client tells + us it can cope. +o Add 'net status [sessions|shares] [parseable]'. +o Don't mistake pre-existing UNIX jobs for smb jobs (remainder of + bug 770). +o Add 'Replicator' and 'RAS Servers' to list of builtin SIDs + (bug 608). +o Fix inverted logic in hosts allow/deny checks caused by + s/strcmp/strequal/ (bug 846). +o Implement correct version SamrRemoveSidForeignDomain() (bug 252). +o Fix typo in 'hash' mangling algorithm. +o Support munged dial for ldapsam (bug 800). +o Fix process_incoming_data() to return the number of bytes handled + this call whether we have a complete PDU or not; fixes bug + with multiple PDU request rpc's broken over SMBwriteX calls + each. +o Fix incorrect smb flags2 for connections to pre-NT servers + (causes smbclient to fail to OS2 for example) (bug 821). +o Update version string in smbldap-tools Makefile to 0.8.2. +o Correct a problem with "net rpc vampire" mis-parsing the + alias member info reply. +o Ensure the ${libdir} is created by the installclientlib script. +o Fix detection of Windows 2003 client architecture in the smb.conf + %a variable. +o Ensure that smbd calls the add user script for a missing UNIX + user on kerberos auth call (bug 445). +o Fix bugs in hosts allow/deny when using a mismatched + network/netmask pair. +o Protect alloc_sub_basic() from crashing when the source string + is NULL (partial work on bug 687). +o Fix spinlocks on IRIX. +o Corrected some bad destination paths when running "configure + --with-fhs". +o Add packaging files for Fedora Core 1. +o Correct bug in SWAT install script for non-english languages. +o Support character set ISO-8859-1 internally (bug 558). +o Fixed more LDAP access errors when looking up group mappings + (bug 281). +o Fix UNISTR2 length bug in LsaQueryInfo(3) that caused SID + resolution to fail on local files on on domain members + (bug 875). +o Fix uninitialized variable in passdb.c. +o Fix formal parameter type in get_static() in nsswitch/wins.c. +o Fix problem mounting directories when mount.cifs is installed + with the setuid bit on. +o Fix bug that prevent --mandir from overriding the defaults + given in the --with-fhs macro. +o Fix bug in in-memory Kerberos keytab detection routines + in configure.in + + + +###################################################################### + + The original 3.0.0 release notes follow + ======================================= + WHATS NEW IN Samba 3.0.0 + September 24, 2003 + ======================================= + + +Major new features: +------------------- + +1) Active Directory support. Samba 3.0 is now able to + join a ADS realm as a member server and authenticate + users using LDAP/Kerberos. + +2) Unicode support. Samba will now negotiate UNICODE on the wire + and internally there is now a much better infrastructure for + multi-byte and UNICODE character sets. + +3) New authentication system. The internal authentication system + has been almost completely rewritten. Most of the changes are + internal, but the new auth system is also very configurable. + +4) New default filename mangling system. + +5) A new "net" command has been added. It is somewhat similar to + the "net" command in windows. Eventually we plan to replace + numerous other utilities (such as smbpasswd) with subcommands + in "net". + +6) Samba now negotiates NT-style status32 codes on the wire. This + improves error handling a lot. + +7) Better Windows 2000/XP/2003 printing support including publishing + printer attributes in active directory. + +8) New loadable module support for passdb backends and character + sets. + +9) New default dual-daemon winbindd support for better performance. + +10) Support for migrating from a Windows NT 4.0 domain to a Samba + domain and maintaining user, group and domain SIDs. + +11) Support for establishing trust relationships with Windows NT 4.0 + domain controllers. + +12) Initial support for a distributed Winbind architecture using + an LDAP directory for storing SID to uid/gid mappings. + +13) Major updates to the Samba documentation tree. + +14) Full support for client and server SMB signing to ensure + compatibility with default Windows 2003 security settings. + +15) Improvement of ACL mapping features based on code donated by + Andreas Grünbacher. + + +Plus lots of other improvements! + + +Additional Documentation +------------------------ + +Please refer to Samba documentation tree (included in the docs/ +subdirectory) for extensive explanations of installing, configuring +and maintaining Samba 3.0 servers and clients. It is advised to +begin with the Samba-HOWTO-Collection for overviews and specific +tasks (the current book is up to approximately 400 pages) and to +refer to the various man pages for information on individual options. + +We are very glad to be able to include the second edition of +"Using Samba" by Jay Ts, Robert Eckstein, and David Collier-Brown +(O'Reilly & Associates) in this release. The book is available +on-line at http://samba.org/samba/docs/ and is included with +the Samba Web Administration Tool (SWAT). Thanks to the authors and +publisher for making "Using Samba" under the GNU Free Documentation +License. + + +###################################################################### +Upgrading from a previous Samba 3.0 beta +######################################## + +Beginning with Samba 3.0.0beta3, the RID allocation functions +have been moved into winbindd. Previously these were handled +by each passdb backend. This means that winbindd must be running +to automatically allocate RIDs for users and/or groups. Otherwise, +smbd will use the 2.2 algorithm for generating new RIDs. + +If you are using 'passdb backend = tdbsam' with a previous Samba +3.0 beta release (or possibly alpha), it may be necessary to +move the RID_COUNTER entry from /usr/local/samba/private/passdb.tdb +to winbindd_idmap.tdb. To do this: + +1) Ensure that winbindd_idmap.tdb exists (launch winbindd at least + once) +2) build tdbtool by executing 'make tdbtool' in the source/tdb/ + directory +3) run: (note that 'tdb>' is the tool's prompt for input) + + root# ./tdbtool /usr/local/samba/private/passdb.tdb + tdb> show RID_COUNTER + key 12 bytes + RID_COUNTER + data 4 bytes + [000] 0A 52 00 00 .R. + + tdb> move RID_COUNTER /usr/local/samba/var/locks/winbindd_idmap.tdb + .... + record moved + +If you are using 'passdb backend = ldapsam', it will be necessary to +store idmap entries in the LDAP directory as well (i.e. idmap backend += ldap). Refer to the 'net idmap' command for more information on +migrating SID<->UNIX id mappings from one backend to another. + +If the RID_COUNTER record does not exist, then these instructions are +unneccessary and the new RID_COUNTER record will be correctly generated +if needed. + + + +######################## +Upgrading from Samba 2.2 +######################## + +This section is provided to help administrators understand the details +involved with upgrading a Samba 2.2 server to Samba 3.0. + + +Building +-------- + +Many of the options to the GNU autoconf script have been modified +in the 3.0 release. The most noticeable are: + + * removal of --with-tdbsam (is now included by default; see section + on passdb backends and authentication for more details) + + * --with-ldapsam is now on used to provided backward compatible + parameters for LDAP enabled Samba 2.2 servers. Refer to the passdb + backend and authentication section for more details + + * inclusion of non-standard passdb modules may be enabled using + --with-expsam. This includes an XML backend and a mysql backend. + + * removal of --with-msdfs (is now enabled by default) + + * removal of --with-ssl (no longer supported) + + * --with-utmp now defaults to 'yes' on supported systems + + * --with-sendfile-support is now enabled by default on supported + systems + + +Parameters +---------- + +This section contains a brief listing of changes to smb.conf options +in the 3.0.0 release. Please refer to the smb.conf(5) man page for +complete descriptions of new or modified parameters. + +Removed Parameters (order alphabetically): + + * admin log + * alternate permissions + * character set + * client codepage + * code page directory + * coding system + * domain admin group + * domain guest group + * force unknown acl user + * hide local users + * mangled stack + * nt smb support + * postscript + * printer driver + * printer driver file + * printer driver location + * read size + * source environment + * status + * strip dot + * total print jobs + * use rhosts + * valid chars + * vfs options + +New Parameters (new parameters have been grouped by function): + + Remote management + ----------------- + * abort shutdown script + * shutdown script + + User and Group Account Management + --------------------------------- + * add group script + * add machine script + * add user to group script + * algorithmic rid base + * delete group script + * delete user from group script + * passdb backend + * set primary group script + + Authentication + -------------- + * auth methods + * realm + * passwd chat timeout + + Protocol Options + ---------------- + * client lanman auth + * client NTLMv2 auth + * client schannel + * client signing + * client use spnego + * disable netbios + * ntlm auth + * paranoid server security + * server schannel + * server signing + * smb ports + * use spnego + + File Service + ------------ + * get quota command + * hide special files + * hide unwriteable files + * hostname lookups + * kernel change notify + * mangle prefix + * map acl inherit + * msdfs proxy + * set quota command + * use sendfile + * vfs objects + + Printing + -------- + * max reported print jobs + + UNICODE and Character Sets + -------------------------- + * display charset + * dos charset + * unicode + * unix charset + + SID to uid/gid Mappings + ----------------------- + * idmap backend + * idmap gid + * idmap uid + * winbind enable local accounts + * winbind trusted domains only + * template primary group + * enable rid algorithm + + LDAP + ---- + * ldap delete dn + * ldap group suffix + * ldap idmap suffix + * ldap machine suffix + * ldap passwd sync + * ldap replication sleep + * ldap user suffix + + General Configuration + --------------------- + * preload modules + * private dir + +Modified Parameters (changes in behavior): + + * encrypt passwords (enabled by default) + * mangling method (set to 'hash2' by default) + * passwd chat + * passwd program + * restrict anonymous (integer value) + * security (new 'ads' value) + * strict locking (enabled by default) + * unix extensions (enabled by default) + * winbind cache time (increased to 5 minutes) + * winbind uid (deprecated in favor of 'idmap uid') + * winbind gid (deprecated in favor of 'idmap gid') + + +Databases +--------- + +This section contains brief descriptions of any new databases +introduced in Samba 3.0. Please remember to backup your existing +${lock directory}/*tdb before upgrading to Samba 3.0. Samba will +upgrade databases as they are opened (if necessary), but downgrading +from 3.0 to 2.2 is an unsupported path. + +Name Description Backup? +---- ----------- ------- +account_policy User policy settings yes +gencache Generic caching db no +group_mapping Mapping table from Windows yes + groups/SID to unix groups +winbindd_idmap ID map table from SIDS to UNIX yes + uids/gids. +namecache Name resolution cache entries no +netsamlogon_cache Cache of NET_USER_INFO_3 structure no + returned as part of a successful + net_sam_logon request +printing/*.tdb Cached output from 'lpq no + command' created on a per print + service basis +registry Read-only samba registry skeleton no + that provides support for exporting + various db tables via the winreg RPCs + + +Changes in Behavior +------------------- + +The following issues are known changes in behavior between Samba 2.2 and +Samba 3.0 that may affect certain installations of Samba. + + 1) When operating as a member of a Windows domain, Samba 2.2 would + map any users authenticated by the remote DC to the 'guest account' + if a uid could not be obtained via the getpwnam() call. Samba 3.0 + rejects the connection as NT_STATUS_LOGON_FAILURE. There is no + current work around to re-establish the 2.2 behavior. + + 2) When adding machines to a Samba 2.2 controlled domain, the + 'add user script' was used to create the UNIX identity of the + machine trust account. Samba 3.0 introduces a new 'add machine + script' that must be specified for this purpose. Samba 3.0 will + not fall back to using the 'add user script' in the absence of + an 'add machine script' + + +###################################################################### +Passdb Backends and Authentication +################################## + +There have been a few new changes that Samba administrators should be +aware of when moving to Samba 3.0. + + 1) encrypted passwords have been enabled by default in order to + inter-operate better with out-of-the-box Windows client + installations. This does mean that either (a) a samba account + must be created for each user, or (b) 'encrypt passwords = no' + must be explicitly defined in smb.conf. + + 2) Inclusion of new 'security = ads' option for integration + with an Active Directory domain using the native Windows + Kerberos 5 and LDAP protocols. + + MIT kerberos 1.3.1 supports the ARCFOUR-HMAC-MD5 encryption + type which is neccessary for servers on which the + administrator password has not been changed, or kerberos-enabled + SMB connections to servers that require Kerberos SMB signing. + Besides this one difference, either MIT or Heimdal Kerberos + distributions are usable by Samba 3.0. + + +Samba 3.0 also includes the possibility of setting up chains +of authentication methods (auth methods) and account storage +backends (passdb backend). Please refer to the smb.conf(5) +man page for details. While both parameters assume sane default +values, it is likely that you will need to understand what the +values actually mean in order to ensure Samba operates correctly. + +The recommended passdb backends at this time are + + * smbpasswd - 2.2 compatible flat file format + * tdbsam - attribute rich database intended as an smbpasswd + replacement for stand alone servers + * ldapsam - attribute rich account storage and retrieval + backend utilizing an LDAP directory. + * ldapsam_compat - a 2.2 backward compatible LDAP account + backend + +Certain functions of the smbpasswd(8) tool have been split between the +new smbpasswd(8) utility, the net(8) tool, and the new pdbedit(8) +utility. See the respective man pages for details. + + +###################################################################### +LDAP +#### + +This section outlines the new features affecting Samba / LDAP +integration. + +New Schema +---------- + +A new object class (sambaSamAccount) has been introduced to replace +the old sambaAccount. This change aids us in the renaming of +attributes to prevent clashes with attributes from other vendors. +There is a conversion script (examples/LDAP/convertSambaAccount) to +modify and LDIF file to the new schema. + +Example: + + $ ldapsearch .... -b "ou=people,dc=..." > sambaAcct.ldif + $ convertSambaAccount --sid=<Domain SID> \ + --input=sambaAcct.ldif --output=sambaSamAcct.ldif \ + --changetype=[modify|add] + +The <DOM SID> can be obtained by running 'net getlocalsid +<DOMAINNAME>' on the Samba PDC as root. The changetype determines +the format of the generated LDIF output--either create new entries +or modify existing entries. + +The old sambaAccount schema may still be used by specifying the +"ldapsam_compat" passdb backend. However, the sambaAccount and +associated attributes have been moved to the historical section of +the schema file and must be uncommented before use if needed. +The 2.2 object class declaration for a sambaAccount has not changed +in the 3.0 samba.schema file. + +Other new object classes and their uses include: + + * sambaDomain - domain information used to allocate rids + for users and groups as necessary. The attributes are added + in 'ldap suffix' directory entry automatically if + an idmap uid/gid range has been set and the 'ldapsam' + passdb backend has been selected. + + * sambaGroupMapping - an object representing the + relationship between a posixGroup and a Windows + group/SID. These entries are stored in the 'ldap + group suffix' and managed by the 'net groupmap' command. + + * sambaUnixIdPool - created in the 'ldap idmap suffix' entry + automatically and contains the next available 'idmap uid' and + 'idmap gid' + + * sambaIdmapEntry - object storing a mapping between a + SID and a UNIX uid/gid. These objects are created by the + idmap_ldap module as needed. + + * sambaSidEntry - object representing a SID alone, as a Structural + class on which to build the sambaIdmapEntry. + + +New Suffix for Searching +------------------------ + +The following new smb.conf parameters have been added to aid in directing +certain LDAP queries when 'passdb backend = ldapsam://...' has been +specified. + + * ldap suffix - used to search for user and computer accounts + * ldap user suffix - used to store user accounts + * ldap machine suffix - used to store machine trust accounts + * ldap group suffix - location of posixGroup/sambaGroupMapping entries + * ldap idmap suffix - location of sambaIdmapEntry objects + +If an 'ldap suffix' is defined, it will be appended to all of the +remaining sub-suffix parameters. In this case, the order of the suffix +listings in smb.conf is important. Always place the 'ldap suffix' first +in the list. + +Due to a limitation in Samba's smb.conf parsing, you should not surround +the DN's with quotation marks. + + +IdMap LDAP support +------------------ + +Samba 3.0 supports an ldap backend for the idmap subsystem. The +following options would inform Samba that the idmap table should be +stored on the directory server onterose in the "ou=idmap,dc=plainjoe, +dc=org" partition. + + [global] + ... + idmap backend = ldap:ldap://onterose/ + ldap idmap suffix = ou=idmap,dc=plainjoe,dc=org + idmap uid = 40000-50000 + idmap gid = 40000-50000 + +This configuration allows winbind installations on multiple servers to +share a uid/gid number space, thus avoiding the interoperability problems +with NFS that were present in Samba 2.2. + + + +###################################################################### +Trust Relationships and a Samba Domain +###################################### + +Samba 3.0.0beta2 is able to utilize winbindd as the means of +allocating uids and gids to trusted users and groups. More +information regarding Samba's support for establishing trust +relationships can be found in the Samba-HOWTO-Collection included +in the docs/ directory of this release. + +First create your Samba PDC and ensure that everything is +working correctly before moving on the trusts. + +To establish Samba as the trusting domain (named SAMBA) from a Windows NT +4.0 domain named WINDOWS: + + 1) create the trust account for SAMBA in "User Manager for Domains" + 2) connect the trust from the Samba domain using + 'net rpc trustdom establish GLASS' + +To create a trustlationship with SAMBA as the trusted domain: + + 1) create the initial trust account for GLASS using + 'smbpasswd -a -i GLASS'. You may need to create a UNIX + account for GLASS$ prior to this step (depending on your + local configuration). + 2) connect the trust from a WINDOWS DC using "User Manager + for Domains" + +Now join winbindd on the Samba PDC to the SAMBA domain using +the normal steps for adding a Samba server to an NT4 domain: +(note that smbd & nmbd must be running at this point) + + root# net rpc join -U root + Password: <enter root password from smbpasswd file here> + +Start winbindd and test the join with 'wbinfo -t'. + +Now test the trust relationship by connecting to the SAMBA DC +(e.g. POGO) as a user from the WINDOWS domain: + + $ smbclient //pogo/netlogon -U Administrator -W WINDOWS + Password: + +Now connect to the WINDOWS DC (e.g. CRYSTAL) as a Samba user: + + $ smbclient //crystal/netlogon -U root -W WINDOWS + Password: + +###################################################################### +Changes in Winbind +################## + +Beginning with Samba3.0.0beta3, winbindd has been given new account +manage functionality equivalent to the 'add user script' family of +smb.conf parameters. The idmap design has also been changed to +centralize control of foreign SID lookups and matching to UNIX +uids and gids. + + +Brief Description of Changes +---------------------------- + +1) The sid_to_uid() family of functions (smbd/uid.c) have been + reverted to the 2.2.x design. This means that when resolving a + SID to a UID or similar mapping: + + a) First consult winbindd + b) perform a local lookup only if winbindd fails to + return a successful answer + + There are some variations to this, but these two rules generally + apply. + +2) All idmap lookups have been moved into winbindd. This means that + a server must run winbindd (and support NSS) in order to achieve + any mappings of SID to dynamically allocated UNIX ids. This was + a conscious design choice. + +3) New functions have been added to winbindd to emulate the 'add user + script' family of smbd functions without requiring that external + scripts be defined. This functionality is controlled by the 'winbind + enable local accounts' smb.conf parameter (enabled by default). + + However, this account management functionality is only supported + in a local tdb (winbindd_idmap.tdb). If these new UNIX accounts + must be shared among multiple Samba servers (such as a PDC and BDCs), + it will be necessary to define your own 'add user script', et. al. + programs that place the accounts/groups in some form of directory + such as NIS or LDAP. This requirement was deemed beyond the scope + of winbind's account management functions. Solutions for + distributing UNIX system information have been deployed and tested + for many years. We saw no need to reinvent the wheel. + +4) A member of a Samba controlled domain running winbindd is now able + to map domain users directly onto existing UNIX accounts while still + automatically creating accounts for trusted users and groups. This + behavior is controlled by the 'winbind trusted domains only' smb.conf + parameter (disabled by default to provide 2.2.x winbind behavior). + +5) Group mapping support is wrapped in the local_XX_to_XX() functions + in smbd/uid.c. The reason that group mappings are not included + in winbindd is because the purpose of Samba's group map is to + match any Windows SID with an existing UNIX group. These UNIX + groups can be created by winbindd (see next section), but the + SID<->gid mapping is retreived by smbd, not winbindd. + + +Examples +-------- + +* security = server running winbindd to allocate accounts on demand + +* Samba PDC running winbindd to handle the automatic creation of UNIX + identities for machine trust accounts + +* Automtically creating UNIX user and groups when migrating a Windows NT + 4.0 PDC to a Samba PDC. Winbindd must be running when executing + 'net rpc vampire' for this to work. + + +###################################################################### +Known Issues +############ + +* There are several bugs currently logged against the 3.0 codebase + that affect the use of NT 4.0 GUI domain management tools when run + against a Samba 3.0 PDC. This bugs should be released in an early + 3.0.x release. + +Please refer to https://bugzilla.samba.org/ for a current list of bugs +filed against the Samba 3.0 codebase. + + +###################################################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical IRC channel on irc.freenode.net. + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. + +A new bugzilla installation has been established to help support the +Samba 3.0 community of users. This server, located at +https://bugzilla.samba.org/, has replaced the older jitterbug server +previously located at http://bugs.samba.org/. + diff --git a/jerry/source/VERSION b/jerry/source/VERSION new file mode 100644 index 00000000000..33844ab36e3 --- /dev/null +++ b/jerry/source/VERSION @@ -0,0 +1,93 @@ +######################################################## +# SAMBA Version # +# # +# script/mkversion.sh # +# will use this file to create # +# include/version.h # +# # +######################################################## + +######################################################## +# This are the main SAMBA version numbers # +# # +# <MAJOR>.<MINOR>.<RELEASE> # +# # +# e.g. SAMBA_VERSION_MAJOR=3 # +# SAMBA_VERSION_MINOR=0 # +# SAMBA_VERSION_RELEASE=0 # +# -> "3.0.0" # +######################################################## +SAMBA_VERSION_MAJOR=3 +SAMBA_VERSION_MINOR=0 +SAMBA_VERSION_RELEASE=5 + +######################################################## +# If a official release has a serious bug # +# a security release will have 'a' sufffix # +# # +# so SAMBA's version will be # +# <MAJOR>.<MINOR>.<RELEASE><REVISION> # +# # +# e.g. SAMBA_VERSION_PRE_RELEASE=a # +# -> "2.2.8a" # +######################################################## +SAMBA_VERSION_REVISION= + +######################################################## +# For 'pre' releases the version will be # +# # +# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> # +# # +# e.g. SAMBA_VERSION_PRE_RELEASE=1 # +# -> "2.2.9pre1" # +######################################################## +SAMBA_VERSION_PRE_RELEASE= + +######################################################## +# For 'rc' releases the version will be # +# # +# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> # +# # +# e.g. SAMBA_VERSION_RC_RELEASE=1 # +# -> "3.0.0rc1" # +######################################################## +SAMBA_VERSION_RC_RELEASE= + +######################################################## +# To mark SVN snapshots this should be set to 'yes' # +# in the development BRANCH, and set to 'no' only in # +# the SAMBA_X_X_RELEASE BRANCH # +# # +# <MAJOR>.<MINOR>.<RELEASE>[...]cvs # +# # +# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes # +# -> "3.0.0-SVN-build-199" # +######################################################## +SAMBA_VERSION_IS_SVN_SNAPSHOT=no + +######################################################## +# This can be set by vendors if they want... # +# This can be a string constant or a function which # +# returns a string (const char *) # +# # +# <MAJOR>.<MINOR>.<RELEASE>[...]-<VENDOR_SUFFIX> # +# # +# Note the '-' is automaticaly added # +# # +# e.g. SAMBA_VERSION_VENDOR_SUFFIX="VendorVersion" # +# -> "CVS 3.0.0rc2-VendorVersion" # +# # +# Note: If you want to use a function, # +# then patch lib/version.c and add this function # +# there, because the symbol must be available in # +# binary. # +# # +# const char *vendor_version(void) # +# { # +# return "VendorVersion"; # +# } # +# # +# e.g. SAMBA_VERSION_VENDOR_SUFFIX=vendor_version() # +# -> "CVS 3.0.0rc2-VendorVersion" # +######################################################## +SAMBA_VERSION_VENDOR_SUFFIX= diff --git a/jerry/source/include/mangle.h b/jerry/source/include/mangle.h new file mode 100644 index 00000000000..d86cfd6ef1a --- /dev/null +++ b/jerry/source/include/mangle.h @@ -0,0 +1,14 @@ +#ifndef _MANGLE_H_ +#define _MANGLE_H_ +/* + header for 8.3 name mangling interface +*/ + +struct mangle_fns { + BOOL (*is_mangled)(const char *s); + BOOL (*is_8_3)(const char *fname, BOOL check_case, BOOL allow_wildcards); + void (*reset)(void); + BOOL (*check_cache)(char *s, size_t maxlen); + void (*name_map)(char *OutName, BOOL need83, BOOL cache83); +}; +#endif /* _MANGLE_H_ */ diff --git a/jerry/source/lib/util_str.c b/jerry/source/lib/util_str.c new file mode 100644 index 00000000000..410ed3d96b6 --- /dev/null +++ b/jerry/source/lib/util_str.c @@ -0,0 +1,2066 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Martin Pool 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 "includes.h" + +/** + * @file + * @brief String utilities. + **/ + +/** + * Get the next token from a string, return False if none found. + * Handles double-quotes. + * + * Based on a routine by GJC@VILLAGE.COM. + * Extensively modified by Andrew.Tridgell@anu.edu.au + **/ +BOOL next_token(const char **ptr,char *buff, const char *sep, size_t bufsize) +{ + char *s; + char *pbuf; + BOOL quoted; + size_t len=1; + + if (!ptr) + return(False); + + s = (char *)*ptr; + + /* default to simple separators */ + if (!sep) + sep = " \t\n\r"; + + /* find the first non sep char */ + while (*s && strchr_m(sep,*s)) + s++; + + /* nothing left? */ + if (! *s) + return(False); + + /* copy over the token */ + pbuf = buff; + for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) { + if (*s == '\"' || *s == '\'') { + quoted = !quoted; + } else { + len++; + *pbuf++ = *s; + } + } + + *ptr = (*s) ? s+1 : s; + *pbuf = 0; + + return(True); +} + +/** +This is like next_token but is not re-entrant and "remembers" the first +parameter so you can pass NULL. This is useful for user interface code +but beware the fact that it is not re-entrant! +**/ + +static const char *last_ptr=NULL; + +BOOL next_token_nr(const char **ptr,char *buff, const char *sep, size_t bufsize) +{ + BOOL ret; + if (!ptr) + ptr = &last_ptr; + + ret = next_token(ptr, buff, sep, bufsize); + last_ptr = *ptr; + return ret; +} + +static uint16 tmpbuf[sizeof(pstring)]; + +void set_first_token(char *ptr) +{ + last_ptr = ptr; +} + +/** + Convert list of tokens to array; dependent on above routine. + Uses last_ptr from above - bit of a hack. +**/ + +char **toktocliplist(int *ctok, const char *sep) +{ + char *s=(char *)last_ptr; + int ictok=0; + char **ret, **iret; + + if (!sep) + sep = " \t\n\r"; + + while(*s && strchr_m(sep,*s)) + s++; + + /* nothing left? */ + if (!*s) + return(NULL); + + do { + ictok++; + while(*s && (!strchr_m(sep,*s))) + s++; + while(*s && strchr_m(sep,*s)) + *s++=0; + } while(*s); + + *ctok=ictok; + s=(char *)last_ptr; + + if (!(ret=iret=malloc(ictok*sizeof(char *)))) + return NULL; + + while(ictok--) { + *iret++=s; + while(*s++) + ; + while(!*s) + s++; + } + + return ret; +} + +/** + * Case insensitive string compararison. + * + * iconv does not directly give us a way to compare strings in + * arbitrary unix character sets -- all we can is convert and then + * compare. This is expensive. + * + * As an optimization, we do a first pass that considers only the + * prefix of the strings that is entirely 7-bit. Within this, we + * check whether they have the same value. + * + * Hopefully this will often give the answer without needing to copy. + * In particular it should speed comparisons to literal ascii strings + * or comparisons of strings that are "obviously" different. + * + * If we find a non-ascii character we fall back to converting via + * iconv. + * + * This should never be slower than convering the whole thing, and + * often faster. + * + * A different optimization would be to compare for bitwise equality + * in the binary encoding. (It would be possible thought hairy to do + * both simultaneously.) But in that case if they turn out to be + * different, we'd need to restart the whole thing. + * + * Even better is to implement strcasecmp for each encoding and use a + * function pointer. + **/ +int StrCaseCmp(const char *s, const char *t) +{ + + const char * ps, * pt; + size_t size; + smb_ucs2_t *buffer_s, *buffer_t; + int ret; + + for (ps = s, pt = t; ; ps++, pt++) { + char us, ut; + + if (!*ps && !*pt) + return 0; /* both ended */ + else if (!*ps) + return -1; /* s is a prefix */ + else if (!*pt) + return +1; /* t is a prefix */ + else if ((*ps & 0x80) || (*pt & 0x80)) + /* not ascii anymore, do it the hard way from here on in */ + break; + + us = toupper(*ps); + ut = toupper(*pt); + if (us == ut) + continue; + else if (us < ut) + return -1; + else if (us > ut) + return +1; + } + + size = push_ucs2_allocate(&buffer_s, s); + if (size == (size_t)-1) { + return strcmp(s, t); + /* Not quite the right answer, but finding the right one + under this failure case is expensive, and it's pretty close */ + } + + size = push_ucs2_allocate(&buffer_t, t); + if (size == (size_t)-1) { + SAFE_FREE(buffer_s); + return strcmp(s, t); + /* Not quite the right answer, but finding the right one + under this failure case is expensive, and it's pretty close */ + } + + ret = strcasecmp_w(buffer_s, buffer_t); + SAFE_FREE(buffer_s); + SAFE_FREE(buffer_t); + return ret; +} + + +/** + Case insensitive string compararison, length limited. +**/ +int StrnCaseCmp(const char *s, const char *t, size_t n) +{ + pstring buf1, buf2; + unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1)); + unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2)); + return strncmp(buf1,buf2,n); +} + +/** + * Compare 2 strings. + * + * @note The comparison is case-insensitive. + **/ +BOOL strequal(const char *s1, const char *s2) +{ + if (s1 == s2) + return(True); + if (!s1 || !s2) + return(False); + + return(StrCaseCmp(s1,s2)==0); +} + +/** + * Compare 2 strings up to and including the nth char. + * + * @note The comparison is case-insensitive. + **/ +BOOL strnequal(const char *s1,const char *s2,size_t n) +{ + if (s1 == s2) + return(True); + if (!s1 || !s2 || !n) + return(False); + + return(StrnCaseCmp(s1,s2,n)==0); +} + +/** + Compare 2 strings (case sensitive). +**/ + +BOOL strcsequal(const char *s1,const char *s2) +{ + if (s1 == s2) + return(True); + if (!s1 || !s2) + return(False); + + return(strcmp(s1,s2)==0); +} + +/** +Do a case-insensitive, whitespace-ignoring string compare. +**/ + +int strwicmp(const char *psz1, const char *psz2) +{ + /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */ + /* appropriate value. */ + if (psz1 == psz2) + return (0); + else if (psz1 == NULL) + return (-1); + else if (psz2 == NULL) + return (1); + + /* sync the strings on first non-whitespace */ + while (1) { + while (isspace((int)*psz1)) + psz1++; + while (isspace((int)*psz2)) + psz2++; + if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' + || *psz2 == '\0') + break; + psz1++; + psz2++; + } + return (*psz1 - *psz2); +} + + +/** + Convert a string to upper case, but don't modify it. +**/ + +char *strupper_static(const char *s) +{ + static pstring str; + + pstrcpy(str, s); + strupper_m(str); + + return str; +} + +/** + Convert a string to "normal" form. +**/ + +void strnorm(char *s) +{ + extern int case_default; + if (case_default == CASE_UPPER) + strupper_m(s); + else + strlower_m(s); +} + +/** + Check if a string is in "normal" case. +**/ + +BOOL strisnormal(const char *s) +{ + extern int case_default; + if (case_default == CASE_UPPER) + return(!strhaslower(s)); + + return(!strhasupper(s)); +} + + +/** + String replace. + NOTE: oldc and newc must be 7 bit characters +**/ + +void string_replace(pstring s,char oldc,char newc) +{ + unsigned char *p; + + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + for (p = (unsigned char *)s; *p; p++) { + if (*p & 0x80) /* mb string - slow path. */ + break; + if (*p == oldc) + *p = newc; + } + + if (!*p) + return; + + /* Slow (mb) path. */ +#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS + /* With compose characters we must restart from the beginning. JRA. */ + p = s; +#endif + push_ucs2(NULL, tmpbuf, p, sizeof(tmpbuf), STR_TERMINATE); + string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc)); + pull_ucs2(NULL, p, tmpbuf, -1, sizeof(tmpbuf), STR_TERMINATE); +} + +/** + Skip past some strings in a buffer. +**/ + +char *skip_string(char *buf,size_t n) +{ + while (n--) + buf += strlen(buf) + 1; + return(buf); +} + +/** + Count the number of characters in a string. Normally this will + be the same as the number of bytes in a string for single byte strings, + but will be different for multibyte. +**/ + +size_t str_charnum(const char *s) +{ + uint16 tmpbuf2[sizeof(pstring)]; + push_ucs2(NULL, tmpbuf2,s, sizeof(tmpbuf2), STR_TERMINATE); + return strlen_w(tmpbuf2); +} + +/** + Count the number of characters in a string. Normally this will + be the same as the number of bytes in a string for single byte strings, + but will be different for multibyte. +**/ + +size_t str_ascii_charnum(const char *s) +{ + pstring tmpbuf2; + push_ascii(tmpbuf2, s, sizeof(tmpbuf2), STR_TERMINATE); + return strlen(tmpbuf2); +} + +BOOL trim_char(char *s,char cfront,char cback) +{ + BOOL ret = False; + char *ep; + char *fp = s; + + /* Ignore null or empty strings. */ + if (!s || (s[0] == '\0')) + return False; + + if (cfront) { + while (*fp && *fp == cfront) + fp++; + if (!*fp) { + /* We ate the string. */ + s[0] = '\0'; + return True; + } + if (fp != s) + ret = True; + } + + ep = fp + strlen(fp) - 1; + if (cback) { + /* Attempt ascii only. Bail for mb strings. */ + while ((ep >= fp) && (*ep == cback)) { + ret = True; + if ((ep > fp) && (((unsigned char)ep[-1]) & 0x80)) { + /* Could be mb... bail back to tim_string. */ + char fs[2], bs[2]; + if (cfront) { + fs[0] = cfront; + fs[1] = '\0'; + } + bs[0] = cback; + bs[1] = '\0'; + return trim_string(s, cfront ? fs : NULL, bs); + } else { + ep--; + } + } + if (ep < fp) { + /* We ate the string. */ + s[0] = '\0'; + return True; + } + } + + ep[1] = '\0'; + memmove(s, fp, ep-fp+2); + return ret; +} + +/** + Trim the specified elements off the front and back of a string. +**/ + +BOOL trim_string(char *s,const char *front,const char *back) +{ + BOOL ret = False; + size_t front_len; + size_t back_len; + size_t len; + + /* Ignore null or empty strings. */ + if (!s || (s[0] == '\0')) + return False; + + front_len = front? strlen(front) : 0; + back_len = back? strlen(back) : 0; + + len = strlen(s); + + if (front_len) { + while (len && strncmp(s, front, front_len)==0) { + /* Must use memmove here as src & dest can + * easily overlap. Found by valgrind. JRA. */ + memmove(s, s+front_len, (len-front_len)+1); + len -= front_len; + ret=True; + } + } + + if (back_len) { + while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) { + s[len-back_len]='\0'; + len -= back_len; + ret=True; + } + } + return ret; +} + +/** + Does a string have any uppercase chars in it? +**/ + +BOOL strhasupper(const char *s) +{ + smb_ucs2_t *ptr; + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(ptr=tmpbuf;*ptr;ptr++) + if(isupper_w(*ptr)) + return True; + return(False); +} + +/** + Does a string have any lowercase chars in it? +**/ + +BOOL strhaslower(const char *s) +{ + smb_ucs2_t *ptr; + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(ptr=tmpbuf;*ptr;ptr++) + if(islower_w(*ptr)) + return True; + return(False); +} + +/** + Find the number of 'c' chars in a string +**/ + +size_t count_chars(const char *s,char c) +{ + smb_ucs2_t *ptr; + int count; + smb_ucs2_t *alloc_tmpbuf = NULL; + + if (push_ucs2_allocate(&alloc_tmpbuf, s) == (size_t)-1) { + return 0; + } + + for(count=0,ptr=alloc_tmpbuf;*ptr;ptr++) + if(*ptr==UCS2_CHAR(c)) + count++; + + SAFE_FREE(alloc_tmpbuf); + return(count); +} + +/** + Safe string copy into a known length string. maxlength does not + include the terminating zero. +**/ + +char *safe_strcpy_fn(const char *fn, int line, char *dest,const char *src, size_t maxlength) +{ + size_t len; + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in safe_strcpy, called from [%s][%d]\n", fn, line)); + return NULL; + } + +#ifdef DEVELOPER + clobber_region(fn,line,dest, maxlength+1); +#endif + + if (!src) { + *dest = 0; + return dest; + } + + len = strnlen(src, maxlength+1); + + if (len > maxlength) { + DEBUG(0,("ERROR: string overflow by %lu (%lu - %lu) in safe_strcpy [%.50s]\n", + (unsigned long)(len-maxlength), (unsigned long)len, + (unsigned long)maxlength, src)); + len = maxlength; + } + + memmove(dest, src, len); + dest[len] = 0; + return dest; +} + +/** + Safe string cat into a string. maxlength does not + include the terminating zero. +**/ +char *safe_strcat_fn(const char *fn, int line, char *dest, const char *src, size_t maxlength) +{ + size_t src_len, dest_len; + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in safe_strcat, called from [%s][%d]\n", fn, line)); + return NULL; + } + + if (!src) + return dest; + + src_len = strnlen(src, maxlength + 1); + dest_len = strnlen(dest, maxlength + 1); + +#ifdef DEVELOPER + clobber_region(fn, line, dest + dest_len, maxlength + 1 - dest_len); +#endif + + if (src_len + dest_len > maxlength) { + DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n", + (int)(src_len + dest_len - maxlength), src)); + if (maxlength > dest_len) { + memcpy(&dest[dest_len], src, maxlength - dest_len); + } + dest[maxlength] = 0; + return NULL; + } + + memcpy(&dest[dest_len], src, src_len); + dest[dest_len + src_len] = 0; + return dest; +} + +/** + Paranoid strcpy into a buffer of given length (includes terminating + zero. Strips out all but 'a-Z0-9' and the character in other_safe_chars + and replaces with '_'. Deliberately does *NOT* check for multibyte + characters. Don't change it ! +**/ +char *alpha_strcpy_fn(const char *fn, int line, char *dest, const char *src, const char *other_safe_chars, size_t maxlength) +{ + size_t len, i; + +#ifdef DEVELOPER + clobber_region(fn, line, dest, maxlength); +#endif + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in alpha_strcpy, called from [%s][%d]\n", fn, line)); + return NULL; + } + + if (!src) { + *dest = 0; + return dest; + } + + len = strlen(src); + if (len >= maxlength) + len = maxlength - 1; + + if (!other_safe_chars) + other_safe_chars = ""; + + for(i = 0; i < len; i++) { + int val = (src[i] & 0xff); + if (isupper(val) || islower(val) || isdigit(val) || strchr_m(other_safe_chars, val)) + dest[i] = src[i]; + else + dest[i] = '_'; + } + + dest[i] = '\0'; + + return dest; +} + +/** + Like strncpy but always null terminates. Make sure there is room! + The variable n should always be one less than the available size. +**/ +char *StrnCpy_fn(const char *fn, int line,char *dest,const char *src,size_t n) +{ + char *d = dest; + +#ifdef DEVELOPER + clobber_region(fn, line, dest, n+1); +#endif + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in StrnCpy, called from [%s][%d]\n", fn, line)); + return(NULL); + } + + if (!src) { + *dest = 0; + return(dest); + } + + while (n-- && (*d = *src)) { + d++; + src++; + } + + *d = 0; + return(dest); +} + +#if 0 +/** + Like strncpy but copies up to the character marker. always null terminates. + returns a pointer to the character marker in the source string (src). +**/ + +static char *strncpyn(char *dest, const char *src, size_t n, char c) +{ + char *p; + size_t str_len; + +#ifdef DEVELOPER + clobber_region(dest, n+1); +#endif + p = strchr_m(src, c); + if (p == NULL) { + DEBUG(5, ("strncpyn: separator character (%c) not found\n", c)); + return NULL; + } + + str_len = PTR_DIFF(p, src); + strncpy(dest, src, MIN(n, str_len)); + dest[str_len] = '\0'; + + return p; +} +#endif + +/** + Routine to get hex characters and turn them into a 16 byte array. + the array can be variable length, and any non-hex-numeric + characters are skipped. "0xnn" or "0Xnn" is specially catered + for. + + valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n" + +**/ + +size_t strhex_to_str(char *p, size_t len, const char *strhex) +{ + size_t i; + size_t num_chars = 0; + unsigned char lonybble, hinybble; + const char *hexchars = "0123456789ABCDEF"; + char *p1 = NULL, *p2 = NULL; + + for (i = 0; i < len && strhex[i] != 0; i++) { + if (strnequal(hexchars, "0x", 2)) { + i++; /* skip two chars */ + continue; + } + + if (!(p1 = strchr_m(hexchars, toupper(strhex[i])))) + break; + + i++; /* next hex digit */ + + if (!(p2 = strchr_m(hexchars, toupper(strhex[i])))) + break; + + /* get the two nybbles */ + hinybble = PTR_DIFF(p1, hexchars); + lonybble = PTR_DIFF(p2, hexchars); + + p[num_chars] = (hinybble << 4) | lonybble; + num_chars++; + + p1 = NULL; + p2 = NULL; + } + return num_chars; +} + +DATA_BLOB strhex_to_data_blob(const char *strhex) +{ + DATA_BLOB ret_blob = data_blob(NULL, strlen(strhex)/2+1); + + ret_blob.length = strhex_to_str(ret_blob.data, + strlen(strhex), + strhex); + + return ret_blob; +} + +/** + * Routine to print a buffer as HEX digits, into an allocated string. + */ + +void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer) +{ + int i; + char *hex_buffer; + + *out_hex_buffer = smb_xmalloc((len*2)+1); + hex_buffer = *out_hex_buffer; + + for (i = 0; i < len; i++) + slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]); +} + +/** + Check if a string is part of a list. +**/ + +BOOL in_list(char *s,char *list,BOOL casesensitive) +{ + pstring tok; + const char *p=list; + + if (!list) + return(False); + + while (next_token(&p,tok,LIST_SEP,sizeof(tok))) { + if (casesensitive) { + if (strcmp(tok,s) == 0) + return(True); + } else { + if (StrCaseCmp(tok,s) == 0) + return(True); + } + } + return(False); +} + +/* this is used to prevent lots of mallocs of size 1 */ +static char *null_string = NULL; + +/** + Set a string value, allocing the space for the string +**/ + +static BOOL string_init(char **dest,const char *src) +{ + size_t l; + if (!src) + src = ""; + + l = strlen(src); + + if (l == 0) { + if (!null_string) { + if((null_string = (char *)malloc(1)) == NULL) { + DEBUG(0,("string_init: malloc fail for null_string.\n")); + return False; + } + *null_string = 0; + } + *dest = null_string; + } else { + (*dest) = strdup(src); + if ((*dest) == NULL) { + DEBUG(0,("Out of memory in string_init\n")); + return False; + } + } + return(True); +} + +/** + Free a string value. +**/ + +void string_free(char **s) +{ + if (!s || !(*s)) + return; + if (*s == null_string) + *s = NULL; + SAFE_FREE(*s); +} + +/** + Set a string value, deallocating any existing space, and allocing the space + for the string +**/ + +BOOL string_set(char **dest,const char *src) +{ + string_free(dest); + return(string_init(dest,src)); +} + +/** + Substitute a string for a pattern in another string. Make sure there is + enough room! + + This routine looks for pattern in s and replaces it with + insert. It may do multiple replacements. + + Any of " ; ' $ or ` in the insert string are replaced with _ + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ + +void string_sub(char *s,const char *pattern, const char *insert, size_t len) +{ + char *p; + ssize_t ls,lp,li, i; + + if (!insert || !pattern || !*pattern || !s) + return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (len == 0) + len = ls + 1; /* len is number of *bytes* */ + + while (lp <= ls && (p = strstr_m(s,pattern))) { + if (ls + (li-lp) >= len) { + DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n", + (int)(ls + (li-lp) - len), + pattern, (int)len)); + break; + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + for (i=0;i<li;i++) { + switch (insert[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + p[i] = '_'; + break; + default: + p[i] = insert[i]; + } + } + s = p + li; + ls += (li-lp); + } +} + +void fstring_sub(char *s,const char *pattern,const char *insert) +{ + string_sub(s, pattern, insert, sizeof(fstring)); +} + +void pstring_sub(char *s,const char *pattern,const char *insert) +{ + string_sub(s, pattern, insert, sizeof(pstring)); +} + +/** + Similar to string_sub, but it will accept only allocated strings + and may realloc them so pay attention at what you pass on no + pointers inside strings, no pstrings or const may be passed + as string. +**/ + +char *realloc_string_sub(char *string, const char *pattern, const char *insert) +{ + char *p, *in; + char *s; + ssize_t ls,lp,li,ld, i; + + if (!insert || !pattern || !*pattern || !string || !*string) + return NULL; + + s = string; + + in = strdup(insert); + if (!in) { + DEBUG(0, ("realloc_string_sub: out of memory!\n")); + return NULL; + } + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + ld = li - lp; + for (i=0;i<li;i++) { + switch (in[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + in[i] = '_'; + default: + /* ok */ + break; + } + } + + while ((p = strstr_m(s,pattern))) { + if (ld > 0) { + int offset = PTR_DIFF(s,string); + char *t = Realloc(string, ls + ld + 1); + if (!t) { + DEBUG(0, ("realloc_string_sub: out of memory!\n")); + SAFE_FREE(in); + return NULL; + } + string = t; + p = t + offset + (p - s); + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + memcpy(p, in, li); + s = p + li; + ls += ld; + } + SAFE_FREE(in); + return string; +} + +/** + Similar to string_sub() but allows for any character to be substituted. + Use with caution! + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ + +void all_string_sub(char *s,const char *pattern,const char *insert, size_t len) +{ + char *p; + ssize_t ls,lp,li; + + if (!insert || !pattern || !s) + return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (!*pattern) + return; + + if (len == 0) + len = ls + 1; /* len is number of *bytes* */ + + while (lp <= ls && (p = strstr_m(s,pattern))) { + if (ls + (li-lp) >= len) { + DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n", + (int)(ls + (li-lp) - len), + pattern, (int)len)); + break; + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + memcpy(p, insert, li); + s = p + li; + ls += (li-lp); + } +} + +/** + Similar to all_string_sub but for unicode strings. + Return a new allocated unicode string. + similar to string_sub() but allows for any character to be substituted. + Use with caution! +**/ + +static smb_ucs2_t *all_string_sub_w(const smb_ucs2_t *s, const smb_ucs2_t *pattern, + const smb_ucs2_t *insert) +{ + smb_ucs2_t *r, *rp; + const smb_ucs2_t *sp; + size_t lr, lp, li, lt; + + if (!insert || !pattern || !*pattern || !s) + return NULL; + + lt = (size_t)strlen_w(s); + lp = (size_t)strlen_w(pattern); + li = (size_t)strlen_w(insert); + + if (li > lp) { + const smb_ucs2_t *st = s; + int ld = li - lp; + while ((sp = strstr_w(st, pattern))) { + st = sp + lp; + lt += ld; + } + } + + r = rp = (smb_ucs2_t *)malloc((lt + 1)*(sizeof(smb_ucs2_t))); + if (!r) { + DEBUG(0, ("all_string_sub_w: out of memory!\n")); + return NULL; + } + + while ((sp = strstr_w(s, pattern))) { + memcpy(rp, s, (sp - s)); + rp += ((sp - s) / sizeof(smb_ucs2_t)); + memcpy(rp, insert, (li * sizeof(smb_ucs2_t))); + s = sp + lp; + rp += li; + } + lr = ((rp - r) / sizeof(smb_ucs2_t)); + if (lr < lt) { + memcpy(rp, s, ((lt - lr) * sizeof(smb_ucs2_t))); + rp += (lt - lr); + } + *rp = 0; + + return r; +} + +smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern, + const char *insert) +{ + wpstring p, i; + + if (!insert || !pattern || !s) + return NULL; + push_ucs2(NULL, p, pattern, sizeof(wpstring) - 1, STR_TERMINATE); + push_ucs2(NULL, i, insert, sizeof(wpstring) - 1, STR_TERMINATE); + return all_string_sub_w(s, p, i); +} + +#if 0 +/** + Splits out the front and back at a separator. +**/ + +static void split_at_last_component(char *path, char *front, char sep, char *back) +{ + char *p = strrchr_m(path, sep); + + if (p != NULL) + *p = 0; + + if (front != NULL) + pstrcpy(front, path); + + if (p != NULL) { + if (back != NULL) + pstrcpy(back, p+1); + *p = '\\'; + } else { + if (back != NULL) + back[0] = 0; + } +} +#endif + +/** + Write an octal as a string. +**/ + +const char *octal_string(int i) +{ + static char ret[64]; + if (i == -1) + return "-1"; + slprintf(ret, sizeof(ret)-1, "0%o", i); + return ret; +} + + +/** + Truncate a string at a specified length. +**/ + +char *string_truncate(char *s, unsigned int length) +{ + if (s && strlen(s) > length) + s[length] = 0; + return s; +} + +/** + Strchr and strrchr_m are very hard to do on general multi-byte strings. + We convert via ucs2 for now. +**/ + +char *strchr_m(const char *src, char c) +{ + wpstring ws; + pstring s2; + smb_ucs2_t *p; + const char *s; + + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) { + if (*s == c) + return (char *)s; + } + + if (!*s) + return NULL; + +#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS + /* With compose characters we must restart from the beginning. JRA. */ + s = src; +#endif + + push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE); + p = strchr_w(ws, UCS2_CHAR(c)); + if (!p) + return NULL; + *p = 0; + pull_ucs2_pstring(s2, ws); + return (char *)(s+strlen(s2)); +} + +char *strrchr_m(const char *s, char c) +{ + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars). Also, in Samba + we only search for ascii characters in 'c' and that + in all mb character sets with a compound character + containing c, if 'c' is not a match at position + p, then p[-1] > 0x7f. JRA. */ + + { + size_t len = strlen(s); + const char *cp = s; + BOOL got_mb = False; + + if (len == 0) + return NULL; + cp += (len - 1); + do { + if (c == *cp) { + /* Could be a match. Part of a multibyte ? */ + if ((cp > s) && (((unsigned char)cp[-1]) & 0x80)) { + /* Yep - go slow :-( */ + got_mb = True; + break; + } + /* No - we have a match ! */ + return (char *)cp; + } + } while (cp-- != s); + if (!got_mb) + return NULL; + } + + /* String contained a non-ascii char. Slow path. */ + { + wpstring ws; + pstring s2; + smb_ucs2_t *p; + + push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE); + p = strrchr_w(ws, UCS2_CHAR(c)); + if (!p) + return NULL; + *p = 0; + pull_ucs2_pstring(s2, ws); + return (char *)(s+strlen(s2)); + } +} + +/*********************************************************************** + Return the equivalent of doing strrchr 'n' times - always going + backwards. +***********************************************************************/ + +char *strnrchr_m(const char *s, char c, unsigned int n) +{ + wpstring ws; + pstring s2; + smb_ucs2_t *p; + + push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE); + p = strnrchr_w(ws, UCS2_CHAR(c), n); + if (!p) + return NULL; + *p = 0; + pull_ucs2_pstring(s2, ws); + return (char *)(s+strlen(s2)); +} + +/*********************************************************************** + strstr_m - We convert via ucs2 for now. +***********************************************************************/ + +char *strstr_m(const char *src, const char *findstr) +{ + smb_ucs2_t *p; + smb_ucs2_t *src_w, *find_w; + const char *s; + char *s2; + char *retp; + + size_t findstr_len = 0; + + /* for correctness */ + if (!findstr[0]) { + return src; + } + + /* Samba does single character findstr calls a *lot*. */ + if (findstr[1] == '\0') + return strchr_m(src, *findstr); + + /* We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) { + if (*s == *findstr) { + if (!findstr_len) + findstr_len = strlen(findstr); + + if (strncmp(s, findstr, findstr_len) == 0) { + return (char *)s; + } + } + } + + if (!*s) + return NULL; + +#if 1 /* def BROKEN_UNICODE_COMPOSE_CHARACTERS */ + /* 'make check' fails unless we do this */ + + /* With compose characters we must restart from the beginning. JRA. */ + s = src; +#endif + + if (push_ucs2_allocate(&src_w, src) == (size_t)-1) { + DEBUG(0,("strstr_m: src malloc fail\n")); + return NULL; + } + + if (push_ucs2_allocate(&find_w, findstr) == (size_t)-1) { + SAFE_FREE(src_w); + DEBUG(0,("strstr_m: find malloc fail\n")); + return NULL; + } + + p = strstr_w(src_w, find_w); + + if (!p) { + SAFE_FREE(src_w); + SAFE_FREE(find_w); + return NULL; + } + + *p = 0; + if (pull_ucs2_allocate(&s2, src_w) == (size_t)-1) { + SAFE_FREE(src_w); + SAFE_FREE(find_w); + DEBUG(0,("strstr_m: dest malloc fail\n")); + return NULL; + } + retp = (char *)(s+strlen(s2)); + SAFE_FREE(src_w); + SAFE_FREE(find_w); + SAFE_FREE(s2); + return retp; +} + +/** + Convert a string to lower case. +**/ + +void strlower_m(char *s) +{ + size_t len; + + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + while (*s && !(((unsigned char)s[0]) & 0x80)) { + *s = tolower((unsigned char)*s); + s++; + } + + if (!*s) + return; + + /* I assume that lowercased string takes the same number of bytes + * as source string even in UTF-8 encoding. (VIV) */ + len = strlen(s) + 1; + errno = 0; + unix_strlower(s,len,s,len); + /* Catch mb conversion errors that may not terminate. */ + if (errno) + s[len-1] = '\0'; +} + +/** + Convert a string to upper case. +**/ + +void strupper_m(char *s) +{ + size_t len; + + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + while (*s && !(((unsigned char)s[0]) & 0x80)) { + *s = toupper((unsigned char)*s); + s++; + } + + if (!*s) + return; + + /* I assume that lowercased string takes the same number of bytes + * as source string even in multibyte encoding. (VIV) */ + len = strlen(s) + 1; + errno = 0; + unix_strupper(s,len,s,len); + /* Catch mb conversion errors that may not terminate. */ + if (errno) + s[len-1] = '\0'; +} + +/** + Return a RFC2254 binary string representation of a buffer. + Used in LDAP filters. + Caller must free. +**/ + +char *binary_string(char *buf, int len) +{ + char *s; + int i, j; + const char *hex = "0123456789ABCDEF"; + s = malloc(len * 3 + 1); + if (!s) + return NULL; + for (j=i=0;i<len;i++) { + s[j] = '\\'; + s[j+1] = hex[((unsigned char)buf[i]) >> 4]; + s[j+2] = hex[((unsigned char)buf[i]) & 0xF]; + j += 3; + } + s[j] = 0; + return s; +} + +/** + Just a typesafety wrapper for snprintf into a pstring. +**/ + + int pstr_sprintf(pstring s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(s, PSTRING_LEN, fmt, ap); + va_end(ap); + return ret; +} + + +/** + Just a typesafety wrapper for snprintf into a fstring. +**/ + +int fstr_sprintf(fstring s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(s, FSTRING_LEN, fmt, ap); + va_end(ap); + return ret; +} + + +#ifndef HAVE_STRNDUP +/** + Some platforms don't have strndup. +**/ + + char *strndup(const char *s, size_t n) +{ + char *ret; + + n = strnlen(s, n); + ret = malloc(n+1); + if (!ret) + return NULL; + memcpy(ret, s, n); + ret[n] = 0; + + return ret; +} +#endif + +#ifndef HAVE_STRNLEN +/** + Some platforms don't have strnlen +**/ + + size_t strnlen(const char *s, size_t n) +{ + int i; + for (i=0; s[i] && i<n; i++) + /* noop */ ; + return i; +} +#endif + +/** + List of Strings manipulation functions +**/ + +#define S_LIST_ABS 16 /* List Allocation Block Size */ + +char **str_list_make(const char *string, const char *sep) +{ + char **list, **rlist; + const char *str; + char *s; + int num, lsize; + pstring tok; + + if (!string || !*string) + return NULL; + s = strdup(string); + if (!s) { + DEBUG(0,("str_list_make: Unable to allocate memory")); + return NULL; + } + if (!sep) sep = LIST_SEP; + + num = lsize = 0; + list = NULL; + + str = s; + while (next_token(&str, tok, sep, sizeof(tok))) { + if (num == lsize) { + lsize += S_LIST_ABS; + rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1))); + if (!rlist) { + DEBUG(0,("str_list_make: Unable to allocate memory")); + str_list_free(&list); + SAFE_FREE(s); + return NULL; + } else + list = rlist; + memset (&list[num], 0, ((sizeof(char**)) * (S_LIST_ABS +1))); + } + + list[num] = strdup(tok); + if (!list[num]) { + DEBUG(0,("str_list_make: Unable to allocate memory")); + str_list_free(&list); + SAFE_FREE(s); + return NULL; + } + + num++; + } + + SAFE_FREE(s); + return list; +} + +BOOL str_list_copy(char ***dest, const char **src) +{ + char **list, **rlist; + int num, lsize; + + *dest = NULL; + if (!src) + return False; + + num = lsize = 0; + list = NULL; + + while (src[num]) { + if (num == lsize) { + lsize += S_LIST_ABS; + rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1))); + if (!rlist) { + DEBUG(0,("str_list_copy: Unable to re-allocate memory")); + str_list_free(&list); + return False; + } else + list = rlist; + memset (&list[num], 0, ((sizeof(char **)) * (S_LIST_ABS +1))); + } + + list[num] = strdup(src[num]); + if (!list[num]) { + DEBUG(0,("str_list_copy: Unable to allocate memory")); + str_list_free(&list); + return False; + } + + num++; + } + + *dest = list; + return True; +} + +/** + * Return true if all the elements of the list match exactly. + **/ +BOOL str_list_compare(char **list1, char **list2) +{ + int num; + + if (!list1 || !list2) + return (list1 == list2); + + for (num = 0; list1[num]; num++) { + if (!list2[num]) + return False; + if (!strcsequal(list1[num], list2[num])) + return False; + } + if (list2[num]) + return False; /* if list2 has more elements than list1 fail */ + + return True; +} + +void str_list_free(char ***list) +{ + char **tlist; + + if (!list || !*list) + return; + tlist = *list; + for(; *tlist; tlist++) + SAFE_FREE(*tlist); + SAFE_FREE(*list); +} + +/****************************************************************************** + version of standard_sub_basic() for string lists; uses alloc_sub_basic() + for the work + *****************************************************************************/ + +BOOL str_list_sub_basic( char **list, const char *smb_name ) +{ + char *s, *tmpstr; + + while ( *list ) { + s = *list; + tmpstr = alloc_sub_basic(smb_name, s); + if ( !tmpstr ) { + DEBUG(0,("str_list_sub_basic: alloc_sub_basic() return NULL!\n")); + return False; + } + + *list = tmpstr; + + list++; + } + + return True; +} + +/****************************************************************************** + substritute a specific pattern in a string list + *****************************************************************************/ + +BOOL str_list_substitute(char **list, const char *pattern, const char *insert) +{ + char *p, *s, *t; + ssize_t ls, lp, li, ld, i, d; + + if (!list) + return False; + if (!pattern) + return False; + if (!insert) + return False; + + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + ld = li -lp; + + while (*list) { + s = *list; + ls = (ssize_t)strlen(s); + + while ((p = strstr_m(s, pattern))) { + t = *list; + d = p -t; + if (ld) { + t = (char *) malloc(ls +ld +1); + if (!t) { + DEBUG(0,("str_list_substitute: Unable to allocate memory")); + return False; + } + memcpy(t, *list, d); + memcpy(t +d +li, p +lp, ls -d -lp +1); + SAFE_FREE(*list); + *list = t; + ls += ld; + s = t +d +li; + } + + for (i = 0; i < li; i++) { + switch (insert[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + t[d +i] = '_'; + break; + default: + t[d +i] = insert[i]; + } + } + } + + + list++; + } + + return True; +} + + +#define IPSTR_LIST_SEP "," +#define IPSTR_LIST_CHAR ',' + +/** + * Add ip string representation to ipstr list. Used also + * as part of @function ipstr_list_make + * + * @param ipstr_list pointer to string containing ip list; + * MUST BE already allocated and IS reallocated if necessary + * @param ipstr_size pointer to current size of ipstr_list (might be changed + * as a result of reallocation) + * @param ip IP address which is to be added to list + * @return pointer to string appended with new ip and possibly + * reallocated to new length + **/ + +char* ipstr_list_add(char** ipstr_list, const struct ip_service *service) +{ + char* new_ipstr = NULL; + + /* arguments checking */ + if (!ipstr_list || !service) return NULL; + + /* attempt to convert ip to a string and append colon separator to it */ + if (*ipstr_list) { + asprintf(&new_ipstr, "%s%s%s:%d", *ipstr_list, IPSTR_LIST_SEP, + inet_ntoa(service->ip), service->port); + SAFE_FREE(*ipstr_list); + } else { + asprintf(&new_ipstr, "%s:%d", inet_ntoa(service->ip), service->port); + } + *ipstr_list = new_ipstr; + return *ipstr_list; +} + + +/** + * Allocate and initialise an ipstr list using ip adresses + * passed as arguments. + * + * @param ipstr_list pointer to string meant to be allocated and set + * @param ip_list array of ip addresses to place in the list + * @param ip_count number of addresses stored in ip_list + * @return pointer to allocated ip string + **/ + +char* ipstr_list_make(char** ipstr_list, const struct ip_service* ip_list, int ip_count) +{ + int i; + + /* arguments checking */ + if (!ip_list && !ipstr_list) return 0; + + *ipstr_list = NULL; + + /* process ip addresses given as arguments */ + for (i = 0; i < ip_count; i++) + *ipstr_list = ipstr_list_add(ipstr_list, &ip_list[i]); + + return (*ipstr_list); +} + + +/** + * Parse given ip string list into array of ip addresses + * (as ip_service structures) + * e.g. 192.168.1.100:389,192.168.1.78, ... + * + * @param ipstr ip string list to be parsed + * @param ip_list pointer to array of ip addresses which is + * allocated by this function and must be freed by caller + * @return number of succesfully parsed addresses + **/ + +int ipstr_list_parse(const char* ipstr_list, struct ip_service **ip_list) +{ + fstring token_str; + size_t count; + int i; + + if (!ipstr_list || !ip_list) + return 0; + + count = count_chars(ipstr_list, IPSTR_LIST_CHAR) + 1; + if ( (*ip_list = (struct ip_service*)malloc(count * sizeof(struct ip_service))) == NULL ) { + DEBUG(0,("ipstr_list_parse: malloc failed for %lu entries\n", (unsigned long)count)); + return 0; + } + + for ( i=0; + next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN) && i<count; + i++ ) + { + struct in_addr addr; + unsigned port = 0; + char *p = strchr(token_str, ':'); + + if (p) { + *p = 0; + port = atoi(p+1); + } + + /* convert single token to ip address */ + if ( (addr.s_addr = inet_addr(token_str)) == INADDR_NONE ) + break; + + (*ip_list)[i].ip = addr; + (*ip_list)[i].port = port; + } + + return count; +} + + +/** + * Safely free ip string list + * + * @param ipstr_list ip string list to be freed + **/ + +void ipstr_list_free(char* ipstr_list) +{ + SAFE_FREE(ipstr_list); +} + + +/** + Unescape a URL encoded string, in place. +**/ + +void rfc1738_unescape(char *buf) +{ + char *p=buf; + + while (p && *p && (p=strchr_m(p,'%'))) { + int c1 = p[1]; + int c2 = p[2]; + + if (c1 >= '0' && c1 <= '9') + c1 = c1 - '0'; + else if (c1 >= 'A' && c1 <= 'F') + c1 = 10 + c1 - 'A'; + else if (c1 >= 'a' && c1 <= 'f') + c1 = 10 + c1 - 'a'; + else {p++; continue;} + + if (c2 >= '0' && c2 <= '9') + c2 = c2 - '0'; + else if (c2 >= 'A' && c2 <= 'F') + c2 = 10 + c2 - 'A'; + else if (c2 >= 'a' && c2 <= 'f') + c2 = 10 + c2 - 'a'; + else {p++; continue;} + + *p = (c1<<4) | c2; + + memmove(p+1, p+3, strlen(p+3)+1); + p++; + } +} + +static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Decode a base64 string into a DATA_BLOB - simple and slow algorithm + **/ +DATA_BLOB base64_decode_data_blob(const char *s) +{ + int bit_offset, byte_offset, idx, i, n; + DATA_BLOB decoded = data_blob(s, strlen(s)+1); + unsigned char *d = decoded.data; + char *p; + + n=i=0; + + while (*s && (p=strchr_m(b64,*s))) { + idx = (int)(p - b64); + byte_offset = (i*6)/8; + bit_offset = (i*6)%8; + d[byte_offset] &= ~((1<<(8-bit_offset))-1); + if (bit_offset < 3) { + d[byte_offset] |= (idx << (2-bit_offset)); + n = byte_offset+1; + } else { + d[byte_offset] |= (idx >> (bit_offset-2)); + d[byte_offset+1] = 0; + d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF; + n = byte_offset+2; + } + s++; i++; + } + + if ((n > 0) && (*s == '=')) { + n -= 1; + } + + /* fix up length */ + decoded.length = n; + return decoded; +} + +/** + * Decode a base64 string in-place - wrapper for the above + **/ +void base64_decode_inplace(char *s) +{ + DATA_BLOB decoded = base64_decode_data_blob(s); + + if ( decoded.length != 0 ) { + memcpy(s, decoded.data, decoded.length); + + /* null terminate */ + s[decoded.length] = '\0'; + } else { + *s = '\0'; + } + + data_blob_free(&decoded); +} + +/** + * Encode a base64 string into a malloc()ed string caller to free. + * + *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments + **/ +char * base64_encode_data_blob(DATA_BLOB data) +{ + int bits = 0; + int char_count = 0; + size_t out_cnt = 0; + size_t len = data.length; + size_t output_len = data.length * 2; + char *result = malloc(output_len); /* get us plenty of space */ + + while (len-- && out_cnt < (data.length * 2) - 5) { + int c = (unsigned char) *(data.data++); + bits += c; + char_count++; + if (char_count == 3) { + result[out_cnt++] = b64[bits >> 18]; + result[out_cnt++] = b64[(bits >> 12) & 0x3f]; + result[out_cnt++] = b64[(bits >> 6) & 0x3f]; + result[out_cnt++] = b64[bits & 0x3f]; + bits = 0; + char_count = 0; + } else { + bits <<= 8; + } + } + if (char_count != 0) { + bits <<= 16 - (8 * char_count); + result[out_cnt++] = b64[bits >> 18]; + result[out_cnt++] = b64[(bits >> 12) & 0x3f]; + if (char_count == 1) { + result[out_cnt++] = '='; + result[out_cnt++] = '='; + } else { + result[out_cnt++] = b64[(bits >> 6) & 0x3f]; + result[out_cnt++] = '='; + } + } + result[out_cnt] = '\0'; /* terminate */ + return result; +} + +/* read a SMB_BIG_UINT from a string */ +SMB_BIG_UINT STR_TO_SMB_BIG_UINT(const char *nptr, const char **entptr) +{ + + SMB_BIG_UINT val = -1; + const char *p = nptr; + + while (p && *p && isspace(*p)) + p++; +#ifdef LARGE_SMB_OFF_T + sscanf(p,"%llu",&val); +#else /* LARGE_SMB_OFF_T */ + sscanf(p,"%lu",&val); +#endif /* LARGE_SMB_OFF_T */ + if (entptr) { + while (p && *p && isdigit(*p)) + p++; + *entptr = p; + } + + return val; +} + +void string_append(char **left, const char *right) +{ + int new_len = strlen(right) + 1; + + if (*left == NULL) { + *left = malloc(new_len); + *left[0] = '\0'; + } else { + new_len += strlen(*left); + *left = Realloc(*left, new_len); + } + + if (*left == NULL) + return; + + safe_strcat(*left, right, new_len-1); +} diff --git a/jerry/source/smbd/filename.c b/jerry/source/smbd/filename.c new file mode 100644 index 00000000000..60a22942e66 --- /dev/null +++ b/jerry/source/smbd/filename.c @@ -0,0 +1,497 @@ +/* + Unix SMB/CIFS implementation. + filename handling routines + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1999-2004 + Copyright (C) Ying Chen 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. +*/ + +/* + * New hash table stat cache code added by Ying Chen. + */ + +#include "includes.h" + +extern BOOL case_sensitive; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern BOOL use_mangled_map; + +static BOOL scan_directory(const char *path, char *name,size_t maxlength, + connection_struct *conn,BOOL docache); + +/**************************************************************************** + Check if two filenames are equal. + This needs to be careful about whether we are case sensitive. +****************************************************************************/ + +static BOOL fname_equal(const char *name1, const char *name2) +{ + /* Normal filename handling */ + if (case_sensitive) + return(strcmp(name1,name2) == 0); + + return(strequal(name1,name2)); +} + +/**************************************************************************** + Mangle the 2nd name and check if it is then equal to the first name. +****************************************************************************/ + +static BOOL mangled_equal(const char *name1, const char *name2, int snum) +{ + pstring tmpname; + + pstrcpy(tmpname, name2); + mangle_map(tmpname, True, False, snum); + return strequal(name1, tmpname); +} + +/**************************************************************************** +This routine is called to convert names from the dos namespace to unix +namespace. It needs to handle any case conversions, mangling, format +changes etc. + +We assume that we have already done a chdir() to the right "root" directory +for this service. + +The function will return False if some part of the name except for the last +part cannot be resolved + +If the saved_last_component != 0, then the unmodified last component +of the pathname is returned there. This is used in an exceptional +case in reply_mv (so far). If saved_last_component == 0 then nothing +is returned there. + +The bad_path arg is set to True if the filename walk failed. This is +used to pick the correct error code to return between ENOENT and ENOTDIR +as Windows applications depend on ERRbadpath being returned if a component +of a pathname does not exist. + +On exit from unix_convert, if *pst was not null, then the file stat +struct will be returned if the file exists and was found, if not this +stat struct will be filled with zeros (and this can be detected by checking +for nlinks = 0, which can never be true for any file). +****************************************************************************/ + +BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_component, + BOOL *bad_path, SMB_STRUCT_STAT *pst) +{ + SMB_STRUCT_STAT st; + char *start, *end; + pstring dirpath; + pstring orig_path; + BOOL component_was_mangled = False; + BOOL name_has_wildcard = False; + + ZERO_STRUCTP(pst); + + *dirpath = 0; + *bad_path = False; + if(saved_last_component) + *saved_last_component = 0; + + if (conn->printer) { + /* we don't ever use the filenames on a printer share as a + filename - so don't convert them */ + return True; + } + + DEBUG(5, ("unix_convert called on file \"%s\"\n", name)); + + /* + * Conversion to basic unix format is already done in check_path_syntax(). + */ + + /* + * Names must be relative to the root of the service - any leading /. + * and trailing /'s should have been trimmed by check_path_syntax(). + */ + +#ifdef DEVELOPER + SMB_ASSERT(*name != '/'); +#endif + + /* + * If we trimmed down to a single '\0' character + * then we should use the "." directory to avoid + * searching the cache, but not if we are in a + * printing share. + * As we know this is valid we can return true here. + */ + + if (!*name) { + name[0] = '.'; + name[1] = '\0'; + return(True); + } + + /* + * Ensure saved_last_component is valid even if file exists. + */ + + if(saved_last_component) { + end = strrchr_m(name, '/'); + if(end) + pstrcpy(saved_last_component, end + 1); + else + pstrcpy(saved_last_component, name); + } + + if (!case_sensitive && (!case_preserve || (mangle_is_8_3(name, False) && !short_case_preserve))) + strnorm(name); + + start = name; + pstrcpy(orig_path, name); + + if(!case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) { + *pst = st; + return True; + } + + /* + * stat the name - if it exists then we are all done! + */ + + if (SMB_VFS_STAT(conn,name,&st) == 0) { + stat_cache_add(orig_path, name); + DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); + *pst = st; + return(True); + } + + DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start)); + + /* + * A special case - if we don't have any mangling chars and are case + * sensitive then searching won't help. + */ + + if (case_sensitive && !mangle_is_mangled(name) && !use_mangled_map) + return(False); + + name_has_wildcard = ms_has_wild(start); + + /* + * is_mangled() was changed to look at an entire pathname, not + * just a component. JRA. + */ + + if (mangle_is_mangled(start)) + component_was_mangled = True; + + /* + * Now we need to recursively match the name against the real + * directory structure. + */ + + /* + * Match each part of the path name separately, trying the names + * as is first, then trying to scan the directory for matching names. + */ + + for (; start ; start = (end?end+1:(char *)NULL)) { + /* + * Pinpoint the end of this section of the filename. + */ + end = strchr_m(start, '/'); + + /* + * Chop the name at this point. + */ + if (end) + *end = 0; + + if(saved_last_component != 0) + pstrcpy(saved_last_component, end ? end + 1 : start); + + /* + * Check if the name exists up to this point. + */ + + if (SMB_VFS_STAT(conn,name, &st) == 0) { + /* + * It exists. it must either be a directory or this must be + * the last part of the path for it to be OK. + */ + if (end && !(st.st_mode & S_IFDIR)) { + /* + * An intermediate part of the name isn't a directory. + */ + DEBUG(5,("Not a dir %s\n",start)); + *end = '/'; + return(False); + } + + if (!end) { + /* + * We just scanned for, and found the end of the path. + * We must return the valid stat struct. + * JRA. + */ + + *pst = st; + } + + } else { + pstring rest; + + /* Stat failed - ensure we don't use it. */ + ZERO_STRUCT(st); + *rest = 0; + + /* + * Remember the rest of the pathname so it can be restored + * later. + */ + + if (end) + pstrcpy(rest,end+1); + + /* + * Try to find this part of the path in the directory. + */ + + if (ms_has_wild(start) || + !scan_directory(dirpath, start, + sizeof(pstring) - 1 - (start - name), + conn, + end?True:False)) { + if (end) { + /* + * An intermediate part of the name can't be found. + */ + DEBUG(5,("Intermediate not found %s\n",start)); + *end = '/'; + + /* + * We need to return the fact that the intermediate + * name resolution failed. This is used to return an + * error of ERRbadpath rather than ERRbadfile. Some + * Windows applications depend on the difference between + * these two errors. + */ + *bad_path = True; + return(False); + } + + /* + * Just the last part of the name doesn't exist. + * We may need to strupper() or strlower() it in case + * this conversion is being used for file creation + * purposes. If the filename is of mixed case then + * don't normalise it. + */ + + if (!case_preserve && (!strhasupper(start) || !strhaslower(start))) + strnorm(start); + + /* + * check on the mangled stack to see if we can recover the + * base of the filename. + */ + + if (mangle_is_mangled(start)) { + mangle_check_cache( start, sizeof(pstring) - 1 - (start - name) ); + } + + DEBUG(5,("New file %s\n",start)); + return(True); + } + + /* + * Restore the rest of the string. If the string was mangled the size + * may have changed. + */ + if (end) { + end = start + strlen(start); + if (!safe_strcat(start, "/", sizeof(pstring) - 1 - (start - name)) || + !safe_strcat(start, rest, sizeof(pstring) - 1 - (start - name))) { + return False; + } + *end = '\0'; + } else { + /* + * We just scanned for, and found the end of the path. + * We must return a valid stat struct if it exists. + * JRA. + */ + + if (SMB_VFS_STAT(conn,name, &st) == 0) { + *pst = st; + } else { + ZERO_STRUCT(st); + } + } + } /* end else */ + + /* + * Add to the dirpath that we have resolved so far. + */ + if (*dirpath) + pstrcat(dirpath,"/"); + + pstrcat(dirpath,start); + + /* + * Don't cache a name with mangled or wildcard components + * as this can change the size. + */ + + if(!component_was_mangled && !name_has_wildcard) + stat_cache_add(orig_path, dirpath); + + /* + * Restore the / that we wiped out earlier. + */ + if (end) + *end = '/'; + } + + /* + * Don't cache a name with mangled or wildcard components + * as this can change the size. + */ + + if(!component_was_mangled && !name_has_wildcard) + stat_cache_add(orig_path, name); + + /* + * The name has been resolved. + */ + + DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); + return(True); +} + +/**************************************************************************** + Check a filename - possibly caling reducename. + This is called by every routine before it allows an operation on a filename. + It does any final confirmation necessary to ensure that the filename is + a valid one for the user to access. +****************************************************************************/ + +BOOL check_name(pstring name,connection_struct *conn) +{ + BOOL ret = True; + + errno = 0; + + if (IS_VETO_PATH(conn, name)) { + /* Is it not dot or dot dot. */ + if (!((name[0] == '.') && (!name[1] || (name[1] == '.' && !name[2])))) { + DEBUG(5,("file path name %s vetoed\n",name)); + return False; + } + } + + if (!lp_widelinks(SNUM(conn))) { + ret = reduce_name(conn,name,conn->connectpath); + } + + /* Check if we are allowing users to follow symlinks */ + /* Patch from David Clerc <David.Clerc@cui.unige.ch> + University of Geneva */ + +#ifdef S_ISLNK + if (!lp_symlinks(SNUM(conn))) { + SMB_STRUCT_STAT statbuf; + if ( (SMB_VFS_LSTAT(conn,name,&statbuf) != -1) && + (S_ISLNK(statbuf.st_mode)) ) { + DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name)); + ret = False; + } + } +#endif + + if (!ret) + DEBUG(5,("check_name on %s failed\n",name)); + + return(ret); +} + +/**************************************************************************** + Scan a directory to find a filename, matching without case sensitivity. + If the name looks like a mangled name then try via the mangling functions +****************************************************************************/ + +static BOOL scan_directory(const char *path, char *name, size_t maxlength, + connection_struct *conn,BOOL docache) +{ + void *cur_dir; + const char *dname; + BOOL mangled; + + mangled = mangle_is_mangled(name); + + /* handle null paths */ + if (*path == 0) + path = "."; + + if (docache && (dname = DirCacheCheck(path,name,SNUM(conn)))) { + safe_strcpy(name, dname, maxlength); + return(True); + } + + /* + * The incoming name can be mangled, and if we de-mangle it + * here it will not compare correctly against the filename (name2) + * read from the directory and then mangled by the mangle_map() + * call. We need to mangle both names or neither. + * (JRA). + */ + if (mangled) + mangled = !mangle_check_cache( name, maxlength ); + + /* open the directory */ + if (!(cur_dir = OpenDir(conn, path, True))) { + DEBUG(3,("scan dir didn't open dir [%s]\n",path)); + return(False); + } + + /* now scan for matching names */ + while ((dname = ReadDirName(cur_dir))) { + + /* Is it dot or dot dot. */ + if ((dname[0] == '.') && (!dname[1] || (dname[1] == '.' && !dname[2]))) { + continue; + } + + /* + * At this point dname is the unmangled name. + * name is either mangled or not, depending on the state of the "mangled" + * variable. JRA. + */ + + /* + * Check mangled name against mangled name, or unmangled name + * against unmangled name. + */ + + if ((mangled && mangled_equal(name,dname,SNUM(conn))) || fname_equal(name, dname)) { + /* we've found the file, change it's name and return */ + if (docache) + DirCacheAdd(path,name,dname,SNUM(conn)); + safe_strcpy(name, dname, maxlength); + CloseDir(cur_dir); + return(True); + } + } + + CloseDir(cur_dir); + return(False); +} diff --git a/jerry/source/smbd/mangle.c b/jerry/source/smbd/mangle.c new file mode 100644 index 00000000000..98388182d8f --- /dev/null +++ b/jerry/source/smbd/mangle.c @@ -0,0 +1,124 @@ +/* + Unix SMB/CIFS implementation. + Name mangling interface + Copyright (C) Andrew Tridgell 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +static struct mangle_fns *mangle_fns; + +/* this allows us to add more mangling backends */ +static const struct { + const char *name; + struct mangle_fns *(*init_fn)(void); +} mangle_backends[] = { + { "hash", mangle_hash_init }, + { "hash2", mangle_hash2_init }, + /*{ "tdb", mangle_tdb_init }, */ + { NULL, NULL } +}; + +/* + initialise the mangling subsystem +*/ +static void mangle_init(void) +{ + int i; + char *method; + + if (mangle_fns) + return; + + method = lp_mangling_method(); + + /* find the first mangling method that manages to initialise and + matches the "mangling method" parameter */ + for (i=0; mangle_backends[i].name && !mangle_fns; i++) { + if (!method || !*method || strcmp(method, mangle_backends[i].name) == 0) { + mangle_fns = mangle_backends[i].init_fn(); + } + } + + if (!mangle_fns) { + DEBUG(0,("Failed to initialise mangling system '%s'\n", method)); + exit_server("mangling init failed"); + } +} + + +/* + reset the cache. This is called when smb.conf has been reloaded +*/ +void mangle_reset_cache(void) +{ + mangle_init(); + + mangle_fns->reset(); +} + +/* + see if a filename has come out of our mangling code +*/ +BOOL mangle_is_mangled(const char *s) +{ + return mangle_fns->is_mangled(s); +} + +/* + see if a filename matches the rules of a 8.3 filename +*/ +BOOL mangle_is_8_3(const char *fname, BOOL check_case) +{ + return mangle_fns->is_8_3(fname, check_case, False); +} + +BOOL mangle_is_8_3_wildcards(const char *fname, BOOL check_case) +{ + return mangle_fns->is_8_3(fname, check_case, True); +} + +/* + try to reverse map a 8.3 name to the original filename. This doesn't have to + always succeed, as the directory handling code in smbd will scan the directory + looking for a matching name if it doesn't. It should succeed most of the time + or there will be a huge performance penalty +*/ +BOOL mangle_check_cache(char *s, size_t maxlen) +{ + return mangle_fns->check_cache(s, maxlen); +} + +/* + map a long filename to a 8.3 name. + */ + +void mangle_map(pstring OutName, BOOL need83, BOOL cache83, int snum) +{ + /* name mangling can be disabled for speed, in which case + we just truncate the string */ + if (!lp_manglednames(snum)) { + if (need83) { + string_truncate(OutName, 12); + } + return; + } + + /* invoke the inane "mangled map" code */ + mangle_map_filename(OutName, snum); + mangle_fns->name_map(OutName, need83, cache83); +} diff --git a/jerry/source/smbd/mangle_hash.c b/jerry/source/smbd/mangle_hash.c new file mode 100644 index 00000000000..968a700b42c --- /dev/null +++ b/jerry/source/smbd/mangle_hash.c @@ -0,0 +1,783 @@ +/* + Unix SMB/CIFS implementation. + Name mangling + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Simo Sorce 2001 + Copyright (C) Andrew Bartlett 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +/* -------------------------------------------------------------------------- ** + * Notable problems... + * + * March/April 1998 CRH + * - Many of the functions in this module overwrite string buffers passed to + * them. This causes a variety of problems and is, generally speaking, + * dangerous and scarry. See the kludge notes in name_map() + * below. + * - It seems that something is calling name_map() twice. The + * first call is probably some sort of test. Names which contain + * illegal characters are being doubly mangled. I'm not sure, but + * I'm guessing the problem is in server.c. + * + * -------------------------------------------------------------------------- ** + */ + +/* -------------------------------------------------------------------------- ** + * History... + * + * March/April 1998 CRH + * Updated a bit. Rewrote is_mangled() to be a bit more selective. + * Rewrote the mangled name cache. Added comments here and there. + * &c. + * -------------------------------------------------------------------------- ** + */ + +#include "includes.h" + + +/* -------------------------------------------------------------------------- ** + * External Variables... + */ + +extern int case_default; /* Are conforming 8.3 names all upper or lower? */ +extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */ + +/* -------------------------------------------------------------------------- ** + * Other stuff... + * + * magic_char - This is the magic char used for mangling. It's + * global. There is a call to lp_magicchar() in server.c + * that is used to override the initial value. + * + * MANGLE_BASE - This is the number of characters we use for name mangling. + * + * basechars - The set characters used for name mangling. This + * is static (scope is this file only). + * + * mangle() - Macro used to select a character from basechars (i.e., + * mangle(n) will return the nth digit, modulo MANGLE_BASE). + * + * chartest - array 0..255. The index range is the set of all possible + * values of a byte. For each byte value, the content is a + * two nibble pair. See BASECHAR_MASK and ILLEGAL_MASK, + * below. + * + * ct_initialized - False until the chartest array has been initialized via + * a call to init_chartest(). + * + * BASECHAR_MASK - Masks the upper nibble of a one-byte value. + * + * ILLEGAL_MASK - Masks the lower nibble of a one-byte value. + * + * isbasecahr() - Given a character, check the chartest array to see + * if that character is in the basechars set. This is + * faster than using strchr_m(). + * + * isillegal() - Given a character, check the chartest array to see + * if that character is in the illegal characters set. + * This is faster than using strchr_m(). + * + * mangled_cache - Cache header used for storing mangled -> original + * reverse maps. + * + * mc_initialized - False until the mangled_cache structure has been + * initialized via a call to reset_mangled_cache(). + * + * MANGLED_CACHE_MAX_ENTRIES - Default maximum number of entries for the + * cache. A value of 0 indicates "infinite". + * + * MANGLED_CACHE_MAX_MEMORY - Default maximum amount of memory for the + * cache. When the cache was kept as an array of 256 + * byte strings, the default cache size was 50 entries. + * This required a fixed 12.5Kbytes of memory. The + * mangled stack parameter is no longer used (though + * this might change). We're now using a fixed 16Kbyte + * maximum cache size. This will probably be much more + * than 50 entries. + */ + +char magic_char = '~'; + +static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1) + +static unsigned char chartest[256] = { 0 }; +static BOOL ct_initialized = False; + +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) +#define BASECHAR_MASK 0xf0 +#define ILLEGAL_MASK 0x0f +#define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK ) +#define isillegal(C) ( (chartest[ ((C) & 0xff) ]) & ILLEGAL_MASK ) + +static ubi_cacheRoot mangled_cache[1] = { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0 } }; +static BOOL mc_initialized = False; +#define MANGLED_CACHE_MAX_ENTRIES 1024 +#define MANGLED_CACHE_MAX_MEMORY 0 + +/* -------------------------------------------------------------------------- ** + * External Variables... + */ + +extern int case_default; /* Are conforming 8.3 names all upper or lower? */ +extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */ + +/* -------------------------------------------------------------------- */ + +static NTSTATUS has_valid_83_chars(const smb_ucs2_t *s, BOOL allow_wildcards) +{ + if (!s || !*s) + return NT_STATUS_INVALID_PARAMETER; + + /* CHECK: this should not be necessary if the ms wild chars + are not valid in valid.dat --- simo */ + if (!allow_wildcards && ms_has_wild_w(s)) + return NT_STATUS_UNSUCCESSFUL; + + while (*s) { + if(!isvalid83_w(*s)) + return NT_STATUS_UNSUCCESSFUL; + s++; + } + + return NT_STATUS_OK; +} + +/* return False if something fail and + * return 2 alloced unicode strings that contain prefix and extension + */ + +static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix, + smb_ucs2_t **extension, BOOL allow_wildcards) +{ + size_t ext_len; + smb_ucs2_t *p; + + *extension = 0; + *prefix = strdup_w(ucs2_string); + if (!*prefix) { + return NT_STATUS_NO_MEMORY; + } + if ((p = strrchr_w(*prefix, UCS2_CHAR('.')))) { + ext_len = strlen_w(p+1); + if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) && + (NT_STATUS_IS_OK(has_valid_83_chars(p+1,allow_wildcards)))) /* check extension */ { + *p = 0; + *extension = strdup_w(p+1); + if (!*extension) { + SAFE_FREE(*prefix); + return NT_STATUS_NO_MEMORY; + } + } + } + return NT_STATUS_OK; +} + +/* ************************************************************************** ** + * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name. + * + * Input: fname - String containing the name to be tested. + * + * Output: NT_STATUS_UNSUCCESSFUL, if the name matches one of the list of reserved names. + * + * Notes: This is a static function called by is_8_3(), below. + * + * ************************************************************************** ** + */ + +static NTSTATUS is_valid_name(const smb_ucs2_t *fname, BOOL allow_wildcards, BOOL only_8_3) +{ + smb_ucs2_t *str, *p; + NTSTATUS ret = NT_STATUS_OK; + + if (!fname || !*fname) + return NT_STATUS_INVALID_PARAMETER; + + /* . and .. are valid names. */ + if (strcmp_wa(fname, ".")==0 || strcmp_wa(fname, "..")==0) + return NT_STATUS_OK; + + /* Name cannot start with '.' */ + if (*fname == UCS2_CHAR('.')) + return NT_STATUS_UNSUCCESSFUL; + + if (only_8_3) { + ret = has_valid_83_chars(fname, allow_wildcards); + if (!NT_STATUS_IS_OK(ret)) + return ret; + } + + str = strdup_w(fname); + p = strchr_w(str, UCS2_CHAR('.')); + if (p && p[1] == UCS2_CHAR(0)) { + /* Name cannot end in '.' */ + SAFE_FREE(str); + return NT_STATUS_UNSUCCESSFUL; + } + if (p) + *p = 0; + strupper_w(str); + p = &(str[1]); + + switch(str[0]) + { + case UCS2_CHAR('A'): + if(strcmp_wa(p, "UX") == 0) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('C'): + if((strcmp_wa(p, "LOCK$") == 0) + || (strcmp_wa(p, "ON") == 0) + || (strcmp_wa(p, "OM1") == 0) + || (strcmp_wa(p, "OM2") == 0) + || (strcmp_wa(p, "OM3") == 0) + || (strcmp_wa(p, "OM4") == 0) + ) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('L'): + if((strcmp_wa(p, "PT1") == 0) + || (strcmp_wa(p, "PT2") == 0) + || (strcmp_wa(p, "PT3") == 0) + ) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('N'): + if(strcmp_wa(p, "UL") == 0) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('P'): + if(strcmp_wa(p, "RN") == 0) + ret = NT_STATUS_UNSUCCESSFUL; + break; + default: + break; + } + + SAFE_FREE(str); + return ret; +} + +static NTSTATUS is_8_3_w(const smb_ucs2_t *fname, BOOL allow_wildcards) +{ + smb_ucs2_t *pref = 0, *ext = 0; + size_t plen; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if (!fname || !*fname) + return NT_STATUS_INVALID_PARAMETER; + + if (strlen_w(fname) > 12) + return NT_STATUS_UNSUCCESSFUL; + + if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0) + return NT_STATUS_OK; + + if (!NT_STATUS_IS_OK(is_valid_name(fname, allow_wildcards, True))) + goto done; + + if (!NT_STATUS_IS_OK(mangle_get_prefix(fname, &pref, &ext, allow_wildcards))) + goto done; + plen = strlen_w(pref); + + if (strchr_wa(pref, '.')) + goto done; + if (plen < 1 || plen > 8) + goto done; + if (ext && (strlen_w(ext) > 3)) + goto done; + + ret = NT_STATUS_OK; + +done: + SAFE_FREE(pref); + SAFE_FREE(ext); + return ret; +} + +static BOOL is_8_3(const char *fname, BOOL check_case, BOOL allow_wildcards) +{ + const char *f; + smb_ucs2_t *ucs2name; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + size_t size; + + if (!fname || !*fname) + return False; + if ((f = strrchr(fname, '/')) == NULL) + f = fname; + else + f++; + + if (strlen(f) > 12) + return False; + + size = push_ucs2_allocate(&ucs2name, f); + if (size == (size_t)-1) { + DEBUG(0,("is_8_3: internal error push_ucs2_allocate() failed!\n")); + goto done; + } + + ret = is_8_3_w(ucs2name, allow_wildcards); + +done: + SAFE_FREE(ucs2name); + + if (!NT_STATUS_IS_OK(ret)) { + return False; + } + + return True; +} + + + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + +/* ************************************************************************** ** + * Initialize the static character test array. + * + * Input: none + * + * Output: none + * + * Notes: This function changes (loads) the contents of the <chartest> + * array. The scope of <chartest> is this file. + * + * ************************************************************************** ** + */ +static void init_chartest( void ) +{ + const char *illegalchars = "*\\/?<>|\":"; + const unsigned char *s; + + memset( (char *)chartest, '\0', 256 ); + + for( s = (const unsigned char *)illegalchars; *s; s++ ) + chartest[*s] = ILLEGAL_MASK; + + for( s = (const unsigned char *)basechars; *s; s++ ) + chartest[*s] |= BASECHAR_MASK; + + ct_initialized = True; +} + +/* ************************************************************************** ** + * Return True if the name *could be* a mangled name. + * + * Input: s - A path name - in UNIX pathname format. + * + * Output: True if the name matches the pattern described below in the + * notes, else False. + * + * Notes: The input name is *not* tested for 8.3 compliance. This must be + * done separately. This function returns true if the name contains + * a magic character followed by excactly two characters from the + * basechars list (above), which in turn are followed either by the + * nul (end of string) byte or a dot (extension) or by a '/' (end of + * a directory name). + * + * ************************************************************************** ** + */ +static BOOL is_mangled(const char *s) +{ + char *magic; + + if( !ct_initialized ) + init_chartest(); + + magic = strchr_m( s, magic_char ); + while( magic && magic[1] && magic[2] ) { /* 3 chars, 1st is magic. */ + if( ('.' == magic[3] || '/' == magic[3] || !(magic[3])) /* Ends with '.' or nul or '/' ? */ + && isbasechar( toupper(magic[1]) ) /* is 2nd char basechar? */ + && isbasechar( toupper(magic[2]) ) ) /* is 3rd char basechar? */ + return( True ); /* If all above, then true, */ + magic = strchr_m( magic+1, magic_char ); /* else seek next magic. */ + } + return( False ); +} + +/* ************************************************************************** ** + * Compare two cache keys and return a value indicating their ordinal + * relationship. + * + * Input: ItemPtr - Pointer to a comparison key. In this case, this will + * be a mangled name string. + * NodePtr - Pointer to a node in the cache. The node structure + * will be followed in memory by a mangled name string. + * + * Output: A signed integer, as follows: + * (x < 0) <==> Key1 less than Key2 + * (x == 0) <==> Key1 equals Key2 + * (x > 0) <==> Key1 greater than Key2 + * + * Notes: This is a ubiqx-style comparison routine. See ubi_BinTree for + * more info. + * + * ************************************************************************** ** + */ +static signed int cache_compare( ubi_btItemPtr ItemPtr, ubi_btNodePtr NodePtr ) +{ + char *Key1 = (char *)ItemPtr; + char *Key2 = (char *)(((ubi_cacheEntryPtr)NodePtr) + 1); + + return( StrCaseCmp( Key1, Key2 ) ); +} + +/* ************************************************************************** ** + * Free a cache entry. + * + * Input: WarrenZevon - Pointer to the entry that is to be returned to + * Nirvana. + * Output: none. + * + * Notes: This function gets around the possibility that the standard + * free() function may be implemented as a macro, or other evil + * subversions (oh, so much fun). + * + * ************************************************************************** ** + */ +static void cache_free_entry( ubi_trNodePtr WarrenZevon ) +{ + ZERO_STRUCTP(WarrenZevon); + SAFE_FREE( WarrenZevon ); +} + +/* ************************************************************************** ** + * Initializes or clears the mangled cache. + * + * Input: none. + * Output: none. + * + * Notes: There is a section below that is commented out. It shows how + * one might use lp_ calls to set the maximum memory and entry size + * of the cache. You might also want to remove the constants used + * in ubi_cacheInit() and replace them with lp_ calls. If so, then + * the calls to ubi_cacheSetMax*() would be moved into the else + * clause. Another option would be to pass in the max_entries and + * max_memory values as parameters. crh 09-Apr-1998. + * + * ************************************************************************** ** + */ + +static void mangle_reset( void ) +{ + if( !mc_initialized ) { + (void)ubi_cacheInit( mangled_cache, + cache_compare, + cache_free_entry, + MANGLED_CACHE_MAX_ENTRIES, + MANGLED_CACHE_MAX_MEMORY ); + mc_initialized = True; + } else { + (void)ubi_cacheClear( mangled_cache ); + } + + /* + (void)ubi_cacheSetMaxEntries( mangled_cache, lp_mangled_cache_entries() ); + (void)ubi_cacheSetMaxMemory( mangled_cache, lp_mangled_cache_memory() ); + */ +} + +/* ************************************************************************** ** + * Add a mangled name into the cache. + * + * Notes: If the mangled cache has not been initialized, then the + * function will simply fail. It could initialize the cache, + * but that's not the way it was done before I changed the + * cache mechanism, so I'm sticking with the old method. + * + * If the extension of the raw name maps directly to the + * extension of the mangled name, then we'll store both names + * *without* extensions. That way, we can provide consistent + * reverse mangling for all names that match. The test here is + * a bit more careful than the one done in earlier versions of + * mangle.c: + * + * - the extension must exist on the raw name, + * - it must be all lower case + * - it must match the mangled extension (to prove that no + * mangling occurred). + * + * crh 07-Apr-1998 + * + * ************************************************************************** ** + */ +static void cache_mangled_name( char *mangled_name, char *raw_name ) +{ + ubi_cacheEntryPtr new_entry; + char *s1; + char *s2; + size_t mangled_len; + size_t raw_len; + size_t i; + + /* If the cache isn't initialized, give up. */ + if( !mc_initialized ) + return; + + /* Init the string lengths. */ + mangled_len = strlen( mangled_name ); + raw_len = strlen( raw_name ); + + /* See if the extensions are unmangled. If so, store the entry + * without the extension, thus creating a "group" reverse map. + */ + s1 = strrchr( mangled_name, '.' ); + if( s1 && (s2 = strrchr( raw_name, '.' )) ) { + i = 1; + while( s1[i] && (tolower( s1[i] ) == s2[i]) ) + i++; + if( !s1[i] && !s2[i] ) { + mangled_len -= i; + raw_len -= i; + } + } + + /* Allocate a new cache entry. If the allocation fails, just return. */ + i = sizeof( ubi_cacheEntry ) + mangled_len + raw_len + 2; + new_entry = malloc( i ); + if( !new_entry ) + return; + + /* Fill the new cache entry, and add it to the cache. */ + s1 = (char *)(new_entry + 1); + s2 = (char *)&(s1[mangled_len + 1]); + safe_strcpy( s1, mangled_name, mangled_len ); + safe_strcpy( s2, raw_name, raw_len ); + ubi_cachePut( mangled_cache, i, new_entry, s1 ); +} + +/* ************************************************************************** ** + * Check for a name on the mangled name stack + * + * Input: s - Input *and* output string buffer. + * maxlen - space in i/o string buffer. + * Output: True if the name was found in the cache, else False. + * + * Notes: If a reverse map is found, the function will overwrite the string + * space indicated by the input pointer <s>. This is frightening. + * It should be rewritten to return NULL if the long name was not + * found, and a pointer to the long name if it was found. + * + * ************************************************************************** ** + */ + +static BOOL check_cache( char *s, size_t maxlen ) +{ + ubi_cacheEntryPtr FoundPtr; + char *ext_start = NULL; + char *found_name; + char *saved_ext = NULL; + + /* If the cache isn't initialized, give up. */ + if( !mc_initialized ) + return( False ); + + FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s ); + + /* If we didn't find the name *with* the extension, try without. */ + if( !FoundPtr ) { + ext_start = strrchr( s, '.' ); + if( ext_start ) { + if((saved_ext = strdup(ext_start)) == NULL) + return False; + + *ext_start = '\0'; + FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s ); + /* + * At this point s is the name without the + * extension. We re-add the extension if saved_ext + * is not null, before freeing saved_ext. + */ + } + } + + /* Okay, if we haven't found it we're done. */ + if( !FoundPtr ) { + if(saved_ext) { + /* Replace the saved_ext as it was truncated. */ + (void)safe_strcat( s, saved_ext, maxlen ); + SAFE_FREE(saved_ext); + } + return( False ); + } + + /* If we *did* find it, we need to copy it into the string buffer. */ + found_name = (char *)(FoundPtr + 1); + found_name += (strlen( found_name ) + 1); + + (void)safe_strcpy( s, found_name, maxlen ); + if( saved_ext ) { + /* Replace the saved_ext as it was truncated. */ + (void)safe_strcat( s, saved_ext, maxlen ); + SAFE_FREE(saved_ext); + } + + return( True ); +} + +/***************************************************************************** + * do the actual mangling to 8.3 format + * the buffer must be able to hold 13 characters (including the null) + ***************************************************************************** + */ +static void to_8_3(char *s) +{ + int csum; + char *p; + char extension[4]; + char base[9]; + int baselen = 0; + int extlen = 0; + + extension[0] = 0; + base[0] = 0; + + p = strrchr(s,'.'); + if( p && (strlen(p+1) < (size_t)4) ) { + BOOL all_normal = ( strisnormal(p+1) ); /* XXXXXXXXX */ + + if( all_normal && p[1] != 0 ) { + *p = 0; + csum = str_checksum( s ); + *p = '.'; + } else + csum = str_checksum(s); + } else + csum = str_checksum(s); + + strupper_m( s ); + + if( p ) { + if( p == s ) + safe_strcpy( extension, "___", 3 ); + else { + *p++ = 0; + while( *p && extlen < 3 ) { + if ( *p != '.') { + extension[extlen++] = p[0]; + } + p++; + } + extension[extlen] = 0; + } + } + + p = s; + + while( *p && baselen < 5 ) { + if (*p != '.') { + base[baselen++] = p[0]; + } + p++; + } + base[baselen] = 0; + + csum = csum % (MANGLE_BASE*MANGLE_BASE); + + (void)slprintf(s, 12, "%s%c%c%c", + base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) ); + + if( *extension ) { + (void)pstrcat( s, "." ); + (void)pstrcat( s, extension ); + } +} + +/***************************************************************************** + * Convert a filename to DOS format. Return True if successful. + * + * Input: OutName - Source *and* destination buffer. + * + * NOTE that OutName must point to a memory space that + * is at least 13 bytes in size! + * + * need83 - If False, name mangling will be skipped unless the + * name contains illegal characters. Mapping will still + * be done, if appropriate. This is probably used to + * signal that a client does not require name mangling, + * thus skipping the name mangling even on shares which + * have name-mangling turned on. + * cache83 - If False, the mangled name cache will not be updated. + * This is usually used to prevent that we overwrite + * a conflicting cache entry prematurely, i.e. before + * we know whether the client is really interested in the + * current name. (See PR#13758). UKD. + * + * Output: Returns False only if the name wanted mangling but the share does + * not have name mangling turned on. + * + * **************************************************************************** + */ + +static void name_map(char *OutName, BOOL need83, BOOL cache83) +{ + smb_ucs2_t *OutName_ucs2; + DEBUG(5,("name_map( %s, need83 = %s, cache83 = %s)\n", OutName, + need83 ? "True" : "False", cache83 ? "True" : "False")); + + if (push_ucs2_allocate(&OutName_ucs2, OutName) == (size_t)-1) { + DEBUG(0, ("push_ucs2_allocate failed!\n")); + return; + } + + if( !need83 && !NT_STATUS_IS_OK(is_valid_name(OutName_ucs2, False, False))) + need83 = True; + + /* check if it's already in 8.3 format */ + if (need83 && !NT_STATUS_IS_OK(is_8_3_w(OutName_ucs2, False))) { + char *tmp = NULL; + + /* mangle it into 8.3 */ + if (cache83) + tmp = strdup(OutName); + + to_8_3(OutName); + + if(tmp != NULL) { + cache_mangled_name(OutName, tmp); + SAFE_FREE(tmp); + } + } + + DEBUG(5,("name_map() ==> [%s]\n", OutName)); + SAFE_FREE(OutName_ucs2); +} + +/* + the following provides the abstraction layer to make it easier + to drop in an alternative mangling implementation +*/ +static struct mangle_fns mangle_fns = { + is_mangled, + is_8_3, + mangle_reset, + check_cache, + name_map +}; + +/* return the methods for this mangling implementation */ +struct mangle_fns *mangle_hash_init(void) +{ + mangle_reset(); + + return &mangle_fns; +} diff --git a/jerry/source/smbd/mangle_hash2.c b/jerry/source/smbd/mangle_hash2.c new file mode 100644 index 00000000000..d4365af1452 --- /dev/null +++ b/jerry/source/smbd/mangle_hash2.c @@ -0,0 +1,707 @@ +/* + Unix SMB/CIFS implementation. + new hash based name mangling implementation + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Simo Sorce 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + this mangling scheme uses the following format + + Annnn~n.AAA + + where nnnnn is a base 36 hash, and A represents characters from the original string + + The hash is taken of the leading part of the long filename, in uppercase + + for simplicity, we only allow ascii characters in 8.3 names + */ + + /* hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce). + * see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a + * discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors + */ + +/* + =============================================================================== + NOTE NOTE NOTE!!! + + This file deliberately uses non-multibyte string functions in many places. This + is *not* a mistake. This code is multi-byte safe, but it gets this property + through some very subtle knowledge of the way multi-byte strings are encoded + and the fact that this mangling algorithm only supports ascii characters in + 8.3 names. + + please don't convert this file to use the *_m() functions!! + =============================================================================== +*/ + + +#include "includes.h" + +#if 1 +#define M_DEBUG(level, x) DEBUG(level, x) +#else +#define M_DEBUG(level, x) +#endif + +/* these flags are used to mark characters in as having particular + properties */ +#define FLAG_BASECHAR 1 +#define FLAG_ASCII 2 +#define FLAG_ILLEGAL 4 +#define FLAG_WILDCARD 8 + +/* the "possible" flags are used as a fast way to find possible DOS + reserved filenames */ +#define FLAG_POSSIBLE1 16 +#define FLAG_POSSIBLE2 32 +#define FLAG_POSSIBLE3 64 +#define FLAG_POSSIBLE4 128 + +/* by default have a max of 4096 entries in the cache. */ +#ifndef MANGLE_CACHE_SIZE +#define MANGLE_CACHE_SIZE 4096 +#endif + +#define FNV1_PRIME 0x01000193 +/*the following number is a fnv1 of the string: idra@samba.org 2002 */ +#define FNV1_INIT 0xa6b93095 + +/* these tables are used to provide fast tests for characters */ +static unsigned char char_flags[256]; + +#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag)) + +/* + this determines how many characters are used from the original filename + in the 8.3 mangled name. A larger value leads to a weaker hash and more collisions. + The largest possible value is 6. +*/ +static unsigned mangle_prefix; + +/* we will use a very simple direct mapped prefix cache. The big + advantage of this cache structure is speed and low memory usage + + The cache is indexed by the low-order bits of the hash, and confirmed by + hashing the resulting cache entry to match the known hash +*/ +static char **prefix_cache; +static u32 *prefix_cache_hashes; + +/* these are the characters we use in the 8.3 hash. Must be 36 chars long */ +static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static unsigned char base_reverse[256]; +#define base_forward(v) basechars[v] + +/* the list of reserved dos names - all of these are illegal */ +static const char *reserved_names[] = +{ "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4", + "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL }; + +/* + hash a string of the specified length. The string does not need to be + null terminated + + this hash needs to be fast with a low collision rate (what hash doesn't?) +*/ +static u32 mangle_hash(const char *key, unsigned length) +{ + u32 value; + u32 i; + fstring str; + + /* we have to uppercase here to ensure that the mangled name + doesn't depend on the case of the long name. Note that this + is the only place where we need to use a multi-byte string + function */ + strncpy(str, key, length); + str[length] = 0; + strupper_m(str); + + /* the length of a multi-byte string can change after a strupper_m */ + length = strlen(str); + + /* Set the initial value from the key size. */ + for (value = FNV1_INIT, i=0; i < length; i++) { + value *= (u32)FNV1_PRIME; + value ^= (u32)(str[i]); + } + + /* note that we force it to a 31 bit hash, to keep within the limits + of the 36^6 mangle space */ + return value & ~0x80000000; +} + +/* + initialise (ie. allocate) the prefix cache + */ +static BOOL cache_init(void) +{ + if (prefix_cache) return True; + + prefix_cache = calloc(MANGLE_CACHE_SIZE, sizeof(char *)); + if (!prefix_cache) return False; + + prefix_cache_hashes = calloc(MANGLE_CACHE_SIZE, sizeof(u32)); + if (!prefix_cache_hashes) return False; + + return True; +} + +/* + insert an entry into the prefix cache. The string might not be null + terminated */ +static void cache_insert(const char *prefix, int length, u32 hash) +{ + int i = hash % MANGLE_CACHE_SIZE; + + if (prefix_cache[i]) { + free(prefix_cache[i]); + } + + prefix_cache[i] = strndup(prefix, length); + prefix_cache_hashes[i] = hash; +} + +/* + lookup an entry in the prefix cache. Return NULL if not found. +*/ +static const char *cache_lookup(u32 hash) +{ + int i = hash % MANGLE_CACHE_SIZE; + + if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) { + return NULL; + } + + /* yep, it matched */ + return prefix_cache[i]; +} + + +/* + determine if a string is possibly in a mangled format, ignoring + case + + In this algorithm, mangled names use only pure ascii characters (no + multi-byte) so we can avoid doing a UCS2 conversion + */ +static BOOL is_mangled_component(const char *name, size_t len) +{ + unsigned int i; + + M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len)); + + /* check the length */ + if (len > 12 || len < 8) + return False; + + /* the best distinguishing characteristic is the ~ */ + if (name[6] != '~') + return False; + + /* check extension */ + if (len > 8) { + if (name[8] != '.') + return False; + for (i=9; name[i] && i < len; i++) { + if (! FLAG_CHECK(name[i], FLAG_ASCII)) { + return False; + } + } + } + + /* check lead characters */ + for (i=0;i<mangle_prefix;i++) { + if (! FLAG_CHECK(name[i], FLAG_ASCII)) { + return False; + } + } + + /* check rest of hash */ + if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) { + return False; + } + for (i=mangle_prefix;i<6;i++) { + if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) { + return False; + } + } + + M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len)); + + return True; +} + + + +/* + determine if a string is possibly in a mangled format, ignoring + case + + In this algorithm, mangled names use only pure ascii characters (no + multi-byte) so we can avoid doing a UCS2 conversion + + NOTE! This interface must be able to handle a path with unix + directory separators. It should return true if any component is + mangled + */ +static BOOL is_mangled(const char *name) +{ + const char *p; + const char *s; + + M_DEBUG(10,("is_mangled %s ?\n", name)); + + for (s=name; (p=strchr(s, '/')); s=p+1) { + if (is_mangled_component(s, PTR_DIFF(p, s))) { + return True; + } + } + + /* and the last part ... */ + return is_mangled_component(s,strlen(s)); +} + + +/* + see if a filename is an allowable 8.3 name. + + we are only going to allow ascii characters in 8.3 names, as this + simplifies things greatly (it means that we know the string won't + get larger when converted from UNIX to DOS formats) +*/ +static BOOL is_8_3(const char *name, BOOL check_case, BOOL allow_wildcards) +{ + int len, i; + char *dot_p; + + /* as a special case, the names '.' and '..' are allowable 8.3 names */ + if (name[0] == '.') { + if (!name[1] || (name[1] == '.' && !name[2])) { + return True; + } + } + + /* the simplest test is on the overall length of the + filename. Note that we deliberately use the ascii string + length (not the multi-byte one) as it is faster, and gives us + the result we need in this case. Using strlen_m would not + only be slower, it would be incorrect */ + len = strlen(name); + if (len > 12) + return False; + + /* find the '.'. Note that once again we use the non-multibyte + function */ + dot_p = strchr(name, '.'); + + if (!dot_p) { + /* if the name doesn't contain a '.' then its length + must be less than 8 */ + if (len > 8) { + return False; + } + } else { + int prefix_len, suffix_len; + + /* if it does contain a dot then the prefix must be <= + 8 and the suffix <= 3 in length */ + prefix_len = PTR_DIFF(dot_p, name); + suffix_len = len - (prefix_len+1); + + if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) { + return False; + } + + /* a 8.3 name cannot contain more than 1 '.' */ + if (strchr(dot_p+1, '.')) { + return False; + } + } + + /* the length are all OK. Now check to see if the characters themselves are OK */ + for (i=0; name[i]; i++) { + /* note that we may allow wildcard petterns! */ + if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) && name[i] != '.') { + return False; + } + } + + /* it is a good 8.3 name */ + return True; +} + + +/* + reset the mangling cache on a smb.conf reload. This only really makes sense for + mangling backends that have parameters in smb.conf, and as this backend doesn't + this is a NULL operation +*/ +static void mangle_reset(void) +{ + /* noop */ +} + + +/* + try to find a 8.3 name in the cache, and if found then + replace the string with the original long name. +*/ +static BOOL check_cache(char *name, size_t maxlen) +{ + u32 hash, multiplier; + unsigned int i; + const char *prefix; + char extension[4]; + + /* make sure that this is a mangled name from this cache */ + if (!is_mangled(name)) { + M_DEBUG(10,("check_cache: %s -> not mangled\n", name)); + return False; + } + + /* we need to extract the hash from the 8.3 name */ + hash = base_reverse[(unsigned char)name[7]]; + for (multiplier=36, i=5;i>=mangle_prefix;i--) { + u32 v = base_reverse[(unsigned char)name[i]]; + hash += multiplier * v; + multiplier *= 36; + } + + /* now look in the prefix cache for that hash */ + prefix = cache_lookup(hash); + if (!prefix) { + M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash)); + return False; + } + + /* we found it - construct the full name */ + if (name[8] == '.') { + strncpy(extension, name+9, 3); + extension[3] = 0; + } else { + extension[0] = 0; + } + + if (extension[0]) { + M_DEBUG(10,("check_cache: %s -> %s.%s\n", name, prefix, extension)); + slprintf(name, maxlen, "%s.%s", prefix, extension); + } else { + M_DEBUG(10,("check_cache: %s -> %s\n", name, prefix)); + safe_strcpy(name, prefix, maxlen); + } + + return True; +} + + +/* + look for a DOS reserved name +*/ +static BOOL is_reserved_name(const char *name) +{ + if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) && + FLAG_CHECK(name[1], FLAG_POSSIBLE2) && + FLAG_CHECK(name[2], FLAG_POSSIBLE3) && + FLAG_CHECK(name[3], FLAG_POSSIBLE4)) { + /* a likely match, scan the lot */ + int i; + for (i=0; reserved_names[i]; i++) { + int len = strlen(reserved_names[i]); + /* note that we match on COM1 as well as COM1.foo */ + if (strnequal(name, reserved_names[i], len) && + (name[len] == '.' || name[len] == 0)) { + return True; + } + } + } + + return False; +} + +/* + See if a filename is a legal long filename. + A filename ending in a '.' is not legal unless it's "." or "..". JRA. +*/ + +static BOOL is_legal_name(const char *name) +{ + const char *dot_pos = NULL; + BOOL alldots = True; + size_t numdots = 0; + + while (*name) { + if (((unsigned int)name[0]) > 128 && (name[1] != 0)) { + /* Possible start of mb character. */ + char mbc[2]; + /* + * Note that if CH_UNIX is utf8 a string may be 3 + * bytes, but this is ok as mb utf8 characters don't + * contain embedded ascii bytes. We are really checking + * for mb UNIX asian characters like Japanese (SJIS) here. + * JRA. + */ + if (convert_string(CH_UNIX, CH_UCS2, name, 2, mbc, 2, False) == 2) { + /* Was a good mb string. */ + name += 2; + continue; + } + } + + if (FLAG_CHECK(name[0], FLAG_ILLEGAL)) { + return False; + } + if (name[0] == '.') { + dot_pos = name; + numdots++; + } else { + alldots = False; + } + name++; + } + + if (dot_pos) { + if (alldots && (numdots == 1 || numdots == 2)) + return True; /* . or .. is a valid name */ + + /* A valid long name cannot end in '.' */ + if (dot_pos[1] == '\0') + return False; + } + + return True; +} + +/* + the main forward mapping function, which converts a long filename to + a 8.3 name + + if need83 is not set then we only do the mangling if the name is illegal + as a long name + + if cache83 is not set then we don't cache the result + + the name parameter must be able to hold 13 bytes +*/ +static void name_map(fstring name, BOOL need83, BOOL cache83) +{ + char *dot_p; + char lead_chars[7]; + char extension[4]; + unsigned int extension_length, i; + unsigned int prefix_len; + u32 hash, v; + char new_name[13]; + + /* reserved names are handled specially */ + if (!is_reserved_name(name)) { + /* if the name is already a valid 8.3 name then we don't need to + do anything */ + if (is_8_3(name, False, False)) { + return; + } + + /* if the caller doesn't strictly need 8.3 then just check for illegal + filenames */ + if (!need83 && is_legal_name(name)) { + return; + } + } + + /* find the '.' if any */ + dot_p = strrchr(name, '.'); + + if (dot_p) { + /* if the extension contains any illegal characters or + is too long or zero length then we treat it as part + of the prefix */ + for (i=0; i<4 && dot_p[i+1]; i++) { + if (! FLAG_CHECK(dot_p[i+1], FLAG_ASCII)) { + dot_p = NULL; + break; + } + } + if (i == 0 || i == 4) dot_p = NULL; + } + + /* the leading characters in the mangled name is taken from + the first characters of the name, if they are ascii otherwise + '_' is used + */ + for (i=0;i<mangle_prefix && name[i];i++) { + lead_chars[i] = name[i]; + if (! FLAG_CHECK(lead_chars[i], FLAG_ASCII)) { + lead_chars[i] = '_'; + } + lead_chars[i] = toupper(lead_chars[i]); + } + for (;i<mangle_prefix;i++) { + lead_chars[i] = '_'; + } + + /* the prefix is anything up to the first dot */ + if (dot_p) { + prefix_len = PTR_DIFF(dot_p, name); + } else { + prefix_len = strlen(name); + } + + /* the extension of the mangled name is taken from the first 3 + ascii chars after the dot */ + extension_length = 0; + if (dot_p) { + for (i=1; extension_length < 3 && dot_p[i]; i++) { + char c = dot_p[i]; + if (FLAG_CHECK(c, FLAG_ASCII)) { + extension[extension_length++] = toupper(c); + } + } + } + + /* find the hash for this prefix */ + v = hash = mangle_hash(name, prefix_len); + + /* now form the mangled name. */ + for (i=0;i<mangle_prefix;i++) { + new_name[i] = lead_chars[i]; + } + new_name[7] = base_forward(v % 36); + new_name[6] = '~'; + for (i=5; i>=mangle_prefix; i--) { + v = v / 36; + new_name[i] = base_forward(v % 36); + } + + /* add the extension */ + if (extension_length) { + new_name[8] = '.'; + memcpy(&new_name[9], extension, extension_length); + new_name[9+extension_length] = 0; + } else { + new_name[8] = 0; + } + + if (cache83) { + /* put it in the cache */ + cache_insert(name, prefix_len, hash); + } + + M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n", + name, hash, new_name, cache83)); + + /* and overwrite the old name */ + fstrcpy(name, new_name); + + /* all done, we've managed to mangle it */ +} + + +/* initialise the flags table + + we allow only a very restricted set of characters as 'ascii' in this + mangling backend. This isn't a significant problem as modern clients + use the 'long' filenames anyway, and those don't have these + restrictions. +*/ +static void init_tables(void) +{ + int i; + + memset(char_flags, 0, sizeof(char_flags)); + + for (i=1;i<128;i++) { + if ((i >= '0' && i <= '9') || + (i >= 'a' && i <= 'z') || + (i >= 'A' && i <= 'Z')) { + char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR); + } + if (strchr("_-$~", i)) { + char_flags[i] |= FLAG_ASCII; + } + + if (strchr("*\\/?<>|\":", i)) { + char_flags[i] |= FLAG_ILLEGAL; + } + + if (strchr("*?\"<>", i)) { + char_flags[i] |= FLAG_WILDCARD; + } + } + + memset(base_reverse, 0, sizeof(base_reverse)); + for (i=0;i<36;i++) { + base_reverse[(unsigned char)base_forward(i)] = i; + } + + /* fill in the reserved names flags. These are used as a very + fast filter for finding possible DOS reserved filenames */ + for (i=0; reserved_names[i]; i++) { + unsigned char c1, c2, c3, c4; + + c1 = (unsigned char)reserved_names[i][0]; + c2 = (unsigned char)reserved_names[i][1]; + c3 = (unsigned char)reserved_names[i][2]; + c4 = (unsigned char)reserved_names[i][3]; + + char_flags[c1] |= FLAG_POSSIBLE1; + char_flags[c2] |= FLAG_POSSIBLE2; + char_flags[c3] |= FLAG_POSSIBLE3; + char_flags[c4] |= FLAG_POSSIBLE4; + char_flags[tolower(c1)] |= FLAG_POSSIBLE1; + char_flags[tolower(c2)] |= FLAG_POSSIBLE2; + char_flags[tolower(c3)] |= FLAG_POSSIBLE3; + char_flags[tolower(c4)] |= FLAG_POSSIBLE4; + + char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4; + } +} + + +/* + the following provides the abstraction layer to make it easier + to drop in an alternative mangling implementation */ +static struct mangle_fns mangle_fns = { + is_mangled, + is_8_3, + mangle_reset, + check_cache, + name_map +}; + +/* return the methods for this mangling implementation */ +struct mangle_fns *mangle_hash2_init(void) +{ + /* the mangle prefix can only be in the mange 1 to 6 */ + mangle_prefix = lp_mangle_prefix(); + if (mangle_prefix > 6) { + mangle_prefix = 6; + } + if (mangle_prefix < 1) { + mangle_prefix = 1; + } + + init_tables(); + mangle_reset(); + + if (!cache_init()) { + return NULL; + } + + return &mangle_fns; +} diff --git a/jerry/source/smbd/reply.c b/jerry/source/smbd/reply.c new file mode 100644 index 00000000000..1956b829e30 --- /dev/null +++ b/jerry/source/smbd/reply.c @@ -0,0 +1,4974 @@ +/* + Unix SMB/CIFS implementation. + Main SMB reply routines + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jeremy Allison 1992-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. +*/ +/* + This file handles most of the reply_ calls that the server + makes to handle specific protocols +*/ + +#include "includes.h" + +/* look in server.c for some explanation of these variables */ +extern int Protocol; +extern int max_send; +extern int max_recv; +extern char magic_char; +extern BOOL case_sensitive; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern int global_oplock_break; +unsigned int smb_echo_count = 0; + +extern BOOL global_encrypted_passwords_negotiated; + +/**************************************************************************** + Ensure we check the path in *exactly* the same way as W2K. + We're assuming here that '/' is not the second byte in any multibyte char + set (a safe assumption). '\\' *may* be the second byte in a multibyte char + set. +****************************************************************************/ + +NTSTATUS check_path_syntax(pstring destname, const pstring srcname) +{ + char *d = destname; + const char *s = srcname; + NTSTATUS ret = NT_STATUS_OK; + + while (*s) { + if (IS_DIRECTORY_SEP(*s)) { + /* + * Safe to assume is not the second part of a mb char as this is handled below. + */ + /* Eat multiple '/' or '\\' */ + while (IS_DIRECTORY_SEP(*s)) { + s++; + } + if ((s[0] == '.') && (s[1] == '\0')) { + ret = NT_STATUS_OBJECT_NAME_INVALID; + break; + } + if ((d != destname) && (*s != '\0')) { + /* We only care about non-leading or trailing '/' or '\\' */ + *d++ = '/'; + } + } else if ((s[0] == '.') && (s[1] == '.') && (IS_DIRECTORY_SEP(s[2]) || s[2] == '\0')) { + /* Uh oh - "../" or "..\\" or "..\0" ! */ + + /* + * No mb char starts with '.' so we're safe checking the directory separator here. + */ + + /* If we just added a '/', delete it. */ + + if ((d > destname) && (*(d-1) == '/')) { + *(d-1) = '\0'; + if (d == (destname + 1)) { + d--; + } else { + d -= 2; + } + } + /* Are we at the start ? Can't go back further if so. */ + if (d == destname) { + ret = NT_STATUS_OBJECT_PATH_SYNTAX_BAD; + break; + } + /* Go back one level... */ + /* We know this is safe as '/' cannot be part of a mb sequence. */ + /* NOTE - if this assumption is invalid we are not in good shape... */ + while (d > destname) { + if (*d == '/') + break; + d--; + } + s += 3; + } else if ((s[0] == '.') && (IS_DIRECTORY_SEP(s[1]) || (s[1] == '\0'))) { + + /* + * No mb char starts with '.' so we're safe checking the directory separator here. + */ + + /* "./" or ".\\" fails with a different error depending on where it is... */ + + if (s == srcname) { + ret = NT_STATUS_OBJECT_NAME_INVALID; + break; + } else { + if (s[1] != '\0' && s[2] == '\0') { + ret = NT_STATUS_INVALID_PARAMETER; + break; + } + ret = NT_STATUS_OBJECT_PATH_NOT_FOUND; + break; + } + s++; + } else { + if (!(*s & 0x80)) { + *d++ = *s++; + } else { + switch(next_mb_char_size(s)) { + case 4: + *d++ = *s++; + case 3: + *d++ = *s++; + case 2: + *d++ = *s++; + case 1: + *d++ = *s++; + break; + default: + DEBUG(0,("check_path_syntax: character length assumptions invalid !\n")); + *d = '\0'; + return NT_STATUS_INVALID_PARAMETER; + } + } + } + } + *d = '\0'; + return ret; +} + +/**************************************************************************** + Pull a string and check the path - provide for error return. +****************************************************************************/ + +size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err) +{ + pstring tmppath; + char *tmppath_ptr = tmppath; + size_t ret; +#ifdef DEVELOPER + SMB_ASSERT(dest_len == sizeof(pstring)); +#endif + + if (src_len == 0) { + ret = srvstr_pull_buf( inbuf, tmppath_ptr, src, dest_len, flags); + } else { + ret = srvstr_pull( inbuf, tmppath_ptr, src, dest_len, src_len, flags); + } + *err = check_path_syntax(dest, tmppath); + return ret; +} + +/**************************************************************************** + Reply to a special message. +****************************************************************************/ + +int reply_special(char *inbuf,char *outbuf) +{ + int outsize = 4; + int msg_type = CVAL(inbuf,0); + int msg_flags = CVAL(inbuf,1); + fstring name1,name2; + char name_type = 0; + + static BOOL already_got_session = False; + + *name1 = *name2 = 0; + + memset(outbuf,'\0',smb_size); + + smb_setlen(outbuf,0); + + switch (msg_type) { + case 0x81: /* session request */ + + if (already_got_session) { + exit_server("multiple session request not permitted"); + } + + SCVAL(outbuf,0,0x82); + SCVAL(outbuf,3,0); + if (name_len(inbuf+4) > 50 || + name_len(inbuf+4 + name_len(inbuf + 4)) > 50) { + DEBUG(0,("Invalid name length in session request\n")); + return(0); + } + name_extract(inbuf,4,name1); + name_type = name_extract(inbuf,4 + name_len(inbuf + 4),name2); + DEBUG(2,("netbios connect: name1=%s name2=%s\n", + name1,name2)); + + set_local_machine_name(name1, True); + set_remote_machine_name(name2, True); + + DEBUG(2,("netbios connect: local=%s remote=%s, name type = %x\n", + get_local_machine_name(), get_remote_machine_name(), + name_type)); + + if (name_type == 'R') { + /* We are being asked for a pathworks session --- + no thanks! */ + SCVAL(outbuf, 0,0x83); + break; + } + + /* only add the client's machine name to the list + of possibly valid usernames if we are operating + in share mode security */ + if (lp_security() == SEC_SHARE) { + add_session_user(get_remote_machine_name()); + } + + reload_services(True); + reopen_logs(); + + claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD); + + already_got_session = True; + break; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + SCVAL(outbuf,0,SMBkeepalive); + SCVAL(outbuf,3,0); + break; + + case 0x82: /* positive session response */ + case 0x83: /* negative session response */ + case 0x84: /* retarget session response */ + DEBUG(0,("Unexpected session response\n")); + break; + + case SMBkeepalive: /* session keepalive */ + default: + return(0); + } + + DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n", + msg_type, msg_flags)); + + return(outsize); +} + +/**************************************************************************** + Reply to a tcon. +****************************************************************************/ + +int reply_tcon(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + const char *service; + pstring service_buf; + pstring password; + pstring dev; + int outsize = 0; + uint16 vuid = SVAL(inbuf,smb_uid); + int pwlen=0; + NTSTATUS nt_status; + char *p; + DATA_BLOB password_blob; + + START_PROFILE(SMBtcon); + + *service_buf = *password = *dev = 0; + + p = smb_buf(inbuf)+1; + p += srvstr_pull_buf(inbuf, service_buf, p, sizeof(service_buf), STR_TERMINATE) + 1; + pwlen = srvstr_pull_buf(inbuf, password, p, sizeof(password), STR_TERMINATE) + 1; + p += pwlen; + p += srvstr_pull_buf(inbuf, dev, p, sizeof(dev), STR_TERMINATE) + 1; + + p = strrchr_m(service_buf,'\\'); + if (p) { + service = p+1; + } else { + service = service_buf; + } + + password_blob = data_blob(password, pwlen+1); + + conn = make_connection(service,password_blob,dev,vuid,&nt_status); + + data_blob_clear_free(&password_blob); + + if (!conn) { + END_PROFILE(SMBtcon); + return ERROR_NT(nt_status); + } + + outsize = set_message(outbuf,2,0,True); + SSVAL(outbuf,smb_vwv0,max_recv); + SSVAL(outbuf,smb_vwv1,conn->cnum); + SSVAL(outbuf,smb_tid,conn->cnum); + + DEBUG(3,("tcon service=%s cnum=%d\n", + service, conn->cnum)); + + END_PROFILE(SMBtcon); + return(outsize); +} + +/**************************************************************************** + Reply to a tcon and X. +****************************************************************************/ + +int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + fstring service; + DATA_BLOB password; + + /* what the cleint thinks the device is */ + fstring client_devicetype; + /* what the server tells the client the share represents */ + const char *server_devicetype; + NTSTATUS nt_status; + uint16 vuid = SVAL(inbuf,smb_uid); + int passlen = SVAL(inbuf,smb_vwv3); + pstring path; + char *p, *q; + extern BOOL global_encrypted_passwords_negotiated; + + START_PROFILE(SMBtconX); + + *service = *client_devicetype = 0; + + /* we might have to close an old one */ + if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) { + close_cnum(conn,vuid); + } + + if (passlen > MAX_PASS_LEN) { + return ERROR_DOS(ERRDOS,ERRbuftoosmall); + } + + if (global_encrypted_passwords_negotiated) { + password = data_blob(smb_buf(inbuf),passlen); + } else { + password = data_blob(smb_buf(inbuf),passlen+1); + /* Ensure correct termination */ + password.data[passlen]=0; + } + + p = smb_buf(inbuf) + passlen; + p += srvstr_pull_buf(inbuf, path, p, sizeof(path), STR_TERMINATE); + + /* + * the service name can be either: \\server\share + * or share directly like on the DELL PowerVault 705 + */ + if (*path=='\\') { + q = strchr_m(path+2,'\\'); + if (!q) { + END_PROFILE(SMBtconX); + return(ERROR_DOS(ERRDOS,ERRnosuchshare)); + } + fstrcpy(service,q+1); + } + else + fstrcpy(service,path); + + p += srvstr_pull(inbuf, client_devicetype, p, sizeof(client_devicetype), 6, STR_ASCII); + + DEBUG(4,("Client requested device type [%s] for share [%s]\n", client_devicetype, service)); + + conn = make_connection(service,password,client_devicetype,vuid,&nt_status); + + data_blob_clear_free(&password); + + if (!conn) { + END_PROFILE(SMBtconX); + return ERROR_NT(nt_status); + } + + if ( IS_IPC(conn) ) + server_devicetype = "IPC"; + else if ( IS_PRINT(conn) ) + server_devicetype = "LPT1:"; + else + server_devicetype = "A:"; + + if (Protocol < PROTOCOL_NT1) { + set_message(outbuf,2,0,True); + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, server_devicetype, -1, + STR_TERMINATE|STR_ASCII); + set_message_end(outbuf,p); + } else { + /* NT sets the fstype of IPC$ to the null string */ + const char *fstype = IS_IPC(conn) ? "" : lp_fstype(SNUM(conn)); + + set_message(outbuf,3,0,True); + + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, server_devicetype, -1, + STR_TERMINATE|STR_ASCII); + p += srvstr_push(outbuf, p, fstype, -1, + STR_TERMINATE); + + set_message_end(outbuf,p); + + /* what does setting this bit do? It is set by NT4 and + may affect the ability to autorun mounted cdroms */ + SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS| + (lp_csc_policy(SNUM(conn)) << 2)); + + init_dfsroot(conn, inbuf, outbuf); + } + + + DEBUG(3,("tconX service=%s \n", + service)); + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(inbuf,smb_tid,conn->cnum); + SSVAL(outbuf,smb_tid,conn->cnum); + + END_PROFILE(SMBtconX); + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + Reply to an unknown type. +****************************************************************************/ + +int reply_unknown(char *inbuf,char *outbuf) +{ + int type; + type = CVAL(inbuf,smb_com); + + DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n", + smb_fn_name(type), type, type)); + + return(ERROR_DOS(ERRSRV,ERRunknownsmb)); +} + +/**************************************************************************** + Reply to an ioctl. +****************************************************************************/ + +int reply_ioctl(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + uint16 device = SVAL(inbuf,smb_vwv1); + uint16 function = SVAL(inbuf,smb_vwv2); + uint32 ioctl_code = (device << 16) + function; + int replysize, outsize; + char *p; + START_PROFILE(SMBioctl); + + DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code)); + + switch (ioctl_code) { + case IOCTL_QUERY_JOB_INFO: + replysize = 32; + break; + default: + END_PROFILE(SMBioctl); + return(ERROR_DOS(ERRSRV,ERRnosupport)); + } + + outsize = set_message(outbuf,8,replysize+1,True); + SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */ + SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */ + SSVAL(outbuf,smb_vwv6,52); /* Offset to data */ + p = smb_buf(outbuf) + 1; /* Allow for alignment */ + + switch (ioctl_code) { + case IOCTL_QUERY_JOB_INFO: + { + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + if (!fsp) { + END_PROFILE(SMBioctl); + return(UNIXERROR(ERRDOS,ERRbadfid)); + } + SSVAL(p,0,fsp->rap_print_jobid); /* Job number */ + srvstr_push(outbuf, p+2, global_myname(), 15, STR_TERMINATE|STR_ASCII); + srvstr_push(outbuf, p+18, lp_servicename(SNUM(conn)), 13, STR_TERMINATE|STR_ASCII); + break; + } + } + + END_PROFILE(SMBioctl); + return outsize; +} + +/**************************************************************************** + Reply to a chkpth. +****************************************************************************/ + +int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = 0; + int mode; + pstring name; + BOOL ok = False; + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + NTSTATUS status; + + START_PROFILE(SMBchkpth); + + srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBchkpth); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + + unix_convert(name,conn,0,&bad_path,&sbuf); + + mode = SVAL(inbuf,smb_vwv0); + + if (check_name(name,conn)) { + if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,name,&sbuf) == 0) + if (!(ok = S_ISDIR(sbuf.st_mode))) { + END_PROFILE(SMBchkpth); + return ERROR_BOTH(NT_STATUS_NOT_A_DIRECTORY,ERRDOS,ERRbadpath); + } + } + + if (!ok) { + /* We special case this - as when a Windows machine + is parsing a path is steps through the components + one at a time - if a component fails it expects + ERRbadpath, not ERRbadfile. + */ + if(errno == ENOENT) { + /* + * Windows returns different error codes if + * the parent directory is valid but not the + * last component - it returns NT_STATUS_OBJECT_NAME_NOT_FOUND + * for that case and NT_STATUS_OBJECT_PATH_NOT_FOUND + * if the path is invalid. + */ + if (bad_path) { + END_PROFILE(SMBchkpth); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } else { + END_PROFILE(SMBchkpth); + return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND); + } + } else if (errno == ENOTDIR) { + END_PROFILE(SMBchkpth); + return ERROR_NT(NT_STATUS_NOT_A_DIRECTORY); + } + + END_PROFILE(SMBchkpth); + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("chkpth %s mode=%d\n", name, mode)); + + END_PROFILE(SMBchkpth); + return(outsize); +} + +/**************************************************************************** + Reply to a getatr. +****************************************************************************/ + +int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring fname; + int outsize = 0; + SMB_STRUCT_STAT sbuf; + BOOL ok = False; + int mode=0; + SMB_OFF_T size=0; + time_t mtime=0; + BOOL bad_path = False; + char *p; + NTSTATUS status; + + START_PROFILE(SMBgetatr); + + p = smb_buf(inbuf) + 1; + p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBgetatr); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + /* dos smetimes asks for a stat of "" - it returns a "hidden directory" + under WfWg - weird! */ + if (! (*fname)) { + mode = aHIDDEN | aDIR; + if (!CAN_WRITE(conn)) + mode |= aRONLY; + size = 0; + mtime = 0; + ok = True; + } else { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (check_name(fname,conn)) { + if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,fname,&sbuf) == 0) { + mode = dos_mode(conn,fname,&sbuf); + size = sbuf.st_size; + mtime = sbuf.st_mtime; + if (mode & aDIR) + size = 0; + ok = True; + } else { + DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno))); + } + } + } + + if (!ok) { + END_PROFILE(SMBgetatr); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadfile); + } + + outsize = set_message(outbuf,10,0,True); + + SSVAL(outbuf,smb_vwv0,mode); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv1,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv1,mtime); + SIVAL(outbuf,smb_vwv3,(uint32)size); + + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME); + + DEBUG( 3, ( "getatr name=%s mode=%d size=%d\n", fname, mode, (uint32)size ) ); + + END_PROFILE(SMBgetatr); + return(outsize); +} + +/**************************************************************************** + Reply to a setatr. +****************************************************************************/ + +int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring fname; + int outsize = 0; + BOOL ok=False; + int mode; + time_t mtime; + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + char *p; + NTSTATUS status; + + START_PROFILE(SMBsetatr); + + p = smb_buf(inbuf) + 1; + p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBsetatr); + return ERROR_NT(status); + } + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + mode = SVAL(inbuf,smb_vwv0); + mtime = make_unix_date3(inbuf+smb_vwv1); + + if (mode != FILE_ATTRIBUTE_NORMAL) { + if (VALID_STAT_OF_DIR(sbuf)) + mode |= aDIR; + else + mode &= ~aDIR; + + if (check_name(fname,conn)) { + ok = (file_set_dosmode(conn,fname,mode,NULL) == 0); + } + } else { + ok = True; + } + + if (ok) + ok = set_filetime(conn,fname,mtime); + + if (!ok) { + END_PROFILE(SMBsetatr); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); + } + + outsize = set_message(outbuf,0,0,True); + + DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) ); + + END_PROFILE(SMBsetatr); + return(outsize); +} + +/**************************************************************************** + Reply to a dskattr. +****************************************************************************/ + +int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = 0; + SMB_BIG_UINT dfree,dsize,bsize; + START_PROFILE(SMBdskattr); + + SMB_VFS_DISK_FREE(conn,".",True,&bsize,&dfree,&dsize); + + outsize = set_message(outbuf,5,0,True); + + if (Protocol <= PROTOCOL_LANMAN2) { + double total_space, free_space; + /* we need to scale this to a number that DOS6 can handle. We + use floating point so we can handle large drives on systems + that don't have 64 bit integers + + we end up displaying a maximum of 2G to DOS systems + */ + total_space = dsize * (double)bsize; + free_space = dfree * (double)bsize; + + dsize = (total_space+63*512) / (64*512); + dfree = (free_space+63*512) / (64*512); + + if (dsize > 0xFFFF) dsize = 0xFFFF; + if (dfree > 0xFFFF) dfree = 0xFFFF; + + SSVAL(outbuf,smb_vwv0,dsize); + SSVAL(outbuf,smb_vwv1,64); /* this must be 64 for dos systems */ + SSVAL(outbuf,smb_vwv2,512); /* and this must be 512 */ + SSVAL(outbuf,smb_vwv3,dfree); + } else { + SSVAL(outbuf,smb_vwv0,dsize); + SSVAL(outbuf,smb_vwv1,bsize/512); + SSVAL(outbuf,smb_vwv2,512); + SSVAL(outbuf,smb_vwv3,dfree); + } + + DEBUG(3,("dskattr dfree=%d\n", (unsigned int)dfree)); + + END_PROFILE(SMBdskattr); + return(outsize); +} + +/**************************************************************************** + Reply to a search. + Can be called from SMBsearch, SMBffirst or SMBfunique. +****************************************************************************/ + +int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring mask; + pstring directory; + pstring fname; + SMB_OFF_T size; + int mode; + time_t date; + int dirtype; + int outsize = 0; + unsigned int numentries = 0; + unsigned int maxentries = 0; + BOOL finished = False; + char *p; + BOOL ok = False; + int status_len; + pstring path; + char status[21]; + int dptr_num= -1; + BOOL check_descend = False; + BOOL expect_close = False; + BOOL can_open = True; + BOOL bad_path = False; + NTSTATUS nt_status; + START_PROFILE(SMBsearch); + + *mask = *directory = *fname = 0; + + /* If we were called as SMBffirst then we must expect close. */ + if(CVAL(inbuf,smb_com) == SMBffirst) + expect_close = True; + + outsize = set_message(outbuf,1,3,True); + maxentries = SVAL(inbuf,smb_vwv0); + dirtype = SVAL(inbuf,smb_vwv1); + p = smb_buf(inbuf) + 1; + p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + END_PROFILE(SMBsearch); + return ERROR_NT(nt_status); + } + p++; + status_len = SVAL(p, 0); + p += 2; + + /* dirtype &= ~aDIR; */ + + if (status_len == 0) { + SMB_STRUCT_STAT sbuf; + pstring dir2; + + pstrcpy(directory,path); + pstrcpy(dir2,path); + unix_convert(directory,conn,0,&bad_path,&sbuf); + unix_format(dir2); + + if (!check_name(directory,conn)) + can_open = False; + + p = strrchr_m(dir2,'/'); + if (p == NULL) { + pstrcpy(mask,dir2); + *dir2 = 0; + } else { + *p = 0; + pstrcpy(mask,p+1); + } + + p = strrchr_m(directory,'/'); + if (!p) + *directory = 0; + else + *p = 0; + + if (strlen(directory) == 0) + pstrcpy(directory,"."); + memset((char *)status,'\0',21); + SCVAL(status,0,(dirtype & 0x1F)); + } else { + int status_dirtype; + + memcpy(status,p,21); + status_dirtype = CVAL(status,0) & 0x1F; + if (status_dirtype != (dirtype & 0x1F)) + dirtype = status_dirtype; + + conn->dirptr = dptr_fetch(status+12,&dptr_num); + if (!conn->dirptr) + goto SearchEmpty; + string_set(&conn->dirpath,dptr_path(dptr_num)); + pstrcpy(mask, dptr_wcard(dptr_num)); + } + + if (can_open) { + p = smb_buf(outbuf) + 3; + ok = True; + + if (status_len == 0) { + dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) { + if(dptr_num == -2) { + END_PROFILE(SMBsearch); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnofids); + } + END_PROFILE(SMBsearch); + return ERROR_DOS(ERRDOS,ERRnofids); + } + dptr_set_wcard(dptr_num, strdup(mask)); + dptr_set_attr(dptr_num, dirtype); + } else { + dirtype = dptr_attr(dptr_num); + } + + DEBUG(4,("dptr_num is %d\n",dptr_num)); + + if (ok) { + if ((dirtype&0x1F) == aVOLID) { + memcpy(p,status,21); + make_dir_struct(p,"???????????",volume_label(SNUM(conn)),0,aVOLID,0); + dptr_fill(p+12,dptr_num); + if (dptr_zero(p+12) && (status_len==0)) + numentries = 1; + else + numentries = 0; + p += DIR_STRUCT_SIZE; + } else { + unsigned int i; + maxentries = MIN(maxentries, ((BUFFER_SIZE - (p - outbuf))/DIR_STRUCT_SIZE)); + + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", + conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True)) + check_descend = True; + + for (i=numentries;(i<maxentries) && !finished;i++) { + finished = !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend); + if (!finished) { + memcpy(p,status,21); + make_dir_struct(p,mask,fname,size,mode,date); + dptr_fill(p+12,dptr_num); + numentries++; + } + p += DIR_STRUCT_SIZE; + } + } + } /* if (ok ) */ + } + + + SearchEmpty: + + /* If we were called as SMBffirst with smb_search_id == NULL + and no entries were found then return error and close dirptr + (X/Open spec) */ + + if(ok && expect_close && numentries == 0 && status_len == 0) { + if (Protocol < PROTOCOL_NT1) { + SCVAL(outbuf,smb_rcls,ERRDOS); + SSVAL(outbuf,smb_err,ERRnofiles); + } + /* Also close the dptr - we know it's gone */ + dptr_close(&dptr_num); + } else if (numentries == 0 || !ok) { + if (Protocol < PROTOCOL_NT1) { + SCVAL(outbuf,smb_rcls,ERRDOS); + SSVAL(outbuf,smb_err,ERRnofiles); + } + dptr_close(&dptr_num); + } + + /* If we were called as SMBfunique, then we can close the dirptr now ! */ + if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique) + dptr_close(&dptr_num); + + SSVAL(outbuf,smb_vwv0,numentries); + SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE); + SCVAL(smb_buf(outbuf),0,5); + SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE); + + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME); + + outsize += DIR_STRUCT_SIZE*numentries; + smb_setlen(outbuf,outsize - 4); + + if ((! *directory) && dptr_path(dptr_num)) + slprintf(directory, sizeof(directory)-1, "(%s)",dptr_path(dptr_num)); + + DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%u of %u\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries, maxentries ) ); + + END_PROFILE(SMBsearch); + return(outsize); +} + +/**************************************************************************** + Reply to a fclose (stop directory search). +****************************************************************************/ + +int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = 0; + int status_len; + pstring path; + char status[21]; + int dptr_num= -2; + char *p; + NTSTATUS err; + + START_PROFILE(SMBfclose); + + outsize = set_message(outbuf,1,0,True); + p = smb_buf(inbuf) + 1; + p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err); + if (!NT_STATUS_IS_OK(err)) { + END_PROFILE(SMBfclose); + return ERROR_NT(err); + } + p++; + status_len = SVAL(p,0); + p += 2; + + if (status_len == 0) { + END_PROFILE(SMBfclose); + return ERROR_DOS(ERRSRV,ERRsrverror); + } + + memcpy(status,p,21); + + if(dptr_fetch(status+12,&dptr_num)) { + /* Close the dptr - we know it's gone */ + dptr_close(&dptr_num); + } + + SSVAL(outbuf,smb_vwv0,0); + + DEBUG(3,("search close\n")); + + END_PROFILE(SMBfclose); + return(outsize); +} + +/**************************************************************************** + Reply to an open. +****************************************************************************/ + +int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring fname; + int outsize = 0; + int fmode=0; + int share_mode; + SMB_OFF_T size = 0; + time_t mtime=0; + int rmode=0; + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + uint16 dos_attr = SVAL(inbuf,smb_vwv1); + NTSTATUS status; + START_PROFILE(SMBopen); + + share_mode = SVAL(inbuf,smb_vwv0); + + srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBopen); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + (uint32)dos_attr, oplock_request,&rmode,NULL); + + if (!fsp) { + END_PROFILE(SMBopen); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); + } + + size = sbuf.st_size; + fmode = dos_mode(conn,fname,&sbuf); + mtime = sbuf.st_mtime; + + if (fmode & aDIR) { + DEBUG(3,("attempt to open a directory %s\n",fname)); + close_file(fsp,False); + END_PROFILE(SMBopen); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + outsize = set_message(outbuf,7,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); + SSVAL(outbuf,smb_vwv1,fmode); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv2,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv2,mtime); + SIVAL(outbuf,smb_vwv4,(uint32)size); + SSVAL(outbuf,smb_vwv6,rmode); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + END_PROFILE(SMBopen); + return(outsize); +} + +/**************************************************************************** + Reply to an open and X. +****************************************************************************/ + +int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring fname; + int smb_mode = SVAL(inbuf,smb_vwv3); + int smb_attr = SVAL(inbuf,smb_vwv5); + /* Breakout the oplock request bits so we can set the + reply bits separately. */ + BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf); + BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf); + BOOL oplock_request = ex_oplock_request | core_oplock_request; +#if 0 + int open_flags = SVAL(inbuf,smb_vwv2); + int smb_sattr = SVAL(inbuf,smb_vwv4); + uint32 smb_time = make_unix_date3(inbuf+smb_vwv6); +#endif + int smb_ofun = SVAL(inbuf,smb_vwv8); + SMB_OFF_T size=0; + int fmode=0,mtime=0,rmode=0; + SMB_STRUCT_STAT sbuf; + int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp; + NTSTATUS status; + START_PROFILE(SMBopenX); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + if (lp_nt_pipe_support()) { + END_PROFILE(SMBopenX); + return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize); + } else { + END_PROFILE(SMBopenX); + return ERROR_DOS(ERRSRV,ERRaccess); + } + } + + /* XXXX we need to handle passed times, sattr and flags */ + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBopenX); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,(uint32)smb_attr, + oplock_request, &rmode,&smb_action); + + if (!fsp) { + END_PROFILE(SMBopenX); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); + } + + size = sbuf.st_size; + fmode = dos_mode(conn,fname,&sbuf); + mtime = sbuf.st_mtime; + if (fmode & aDIR) { + close_file(fsp,False); + END_PROFILE(SMBopenX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + /* If the caller set the extended oplock request bit + and we granted one (by whatever means) - set the + correct bit for extended oplock reply. + */ + + if (ex_oplock_request && lp_fake_oplocks(SNUM(conn))) + smb_action |= EXTENDED_OPLOCK_GRANTED; + + if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + smb_action |= EXTENDED_OPLOCK_GRANTED; + + /* If the caller set the core oplock request bit + and we granted one (by whatever means) - set the + correct bit for core oplock reply. + */ + + if (core_oplock_request && lp_fake_oplocks(SNUM(conn))) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + set_message(outbuf,15,0,True); + SSVAL(outbuf,smb_vwv2,fsp->fnum); + SSVAL(outbuf,smb_vwv3,fmode); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv4,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv4,mtime); + SIVAL(outbuf,smb_vwv6,(uint32)size); + SSVAL(outbuf,smb_vwv8,rmode); + SSVAL(outbuf,smb_vwv11,smb_action); + + END_PROFILE(SMBopenX); + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + Reply to a SMBulogoffX. +****************************************************************************/ + +int reply_ulogoffX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + uint16 vuid = SVAL(inbuf,smb_uid); + user_struct *vuser = get_valid_user_struct(vuid); + START_PROFILE(SMBulogoffX); + + if(vuser == 0) + DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid)); + + /* in user level security we are supposed to close any files + open by this user */ + if ((vuser != 0) && (lp_security() != SEC_SHARE)) + file_close_user(vuid); + + invalidate_vuid(vuid); + + set_message(outbuf,2,0,True); + + DEBUG( 3, ( "ulogoffX vuid=%d\n", vuid ) ); + + END_PROFILE(SMBulogoffX); + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ + +int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring fname; + int com; + int outsize = 0; + int createmode; + int ofun = 0; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + SMB_STRUCT_STAT sbuf; + NTSTATUS status; + START_PROFILE(SMBcreate); + + com = SVAL(inbuf,smb_com); + + createmode = SVAL(inbuf,smb_vwv0); + srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcreate); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + if (createmode & aVOLID) + DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); + + if(com == SMBmknew) { + /* We should fail if file exists. */ + ofun = FILE_CREATE_IF_NOT_EXIST; + } else { + /* SMBcreate - Create if file doesn't exist, truncate if it does. */ + ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE; + } + + /* Open file in dos compatibility share mode. */ + fsp = open_file_shared(conn,fname,&sbuf,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + ofun, (uint32)createmode, oplock_request, NULL, NULL); + + if (!fsp) { + END_PROFILE(SMBcreate); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); + } + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + DEBUG( 2, ( "new file %s\n", fname ) ); + DEBUG( 3, ( "mknew %s fd=%d dmode=%d\n", fname, fsp->fd, createmode ) ); + + END_PROFILE(SMBcreate); + return(outsize); +} + +/**************************************************************************** + Reply to a create temporary file. +****************************************************************************/ + +int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring fname; + int outsize = 0; + int createattr; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + int tmpfd; + SMB_STRUCT_STAT sbuf; + char *p, *s; + NTSTATUS status; + unsigned int namelen; + + START_PROFILE(SMBctemp); + + createattr = SVAL(inbuf,smb_vwv0); + srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBctemp); + return ERROR_NT(status); + } + if (*fname) { + pstrcat(fname,"/TMXXXXXX"); + } else { + pstrcat(fname,"TMXXXXXX"); + } + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + tmpfd = smb_mkstemp(fname); + if (tmpfd == -1) { + END_PROFILE(SMBctemp); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + SMB_VFS_STAT(conn,fname,&sbuf); + + /* Open file in dos compatibility share mode. */ + /* We should fail if file does not exist. */ + fsp = open_file_shared(conn,fname,&sbuf, + SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + FILE_EXISTS_OPEN|FILE_FAIL_IF_NOT_EXIST, + (uint32)createattr, oplock_request, NULL, NULL); + + /* close fd from smb_mkstemp() */ + close(tmpfd); + + if (!fsp) { + END_PROFILE(SMBctemp); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); + } + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + /* the returned filename is relative to the directory */ + s = strrchr_m(fname, '/'); + if (!s) + s = fname; + else + s++; + + p = smb_buf(outbuf); +#if 0 + /* Tested vs W2K3 - this doesn't seem to be here - null terminated filename is the only + thing in the byte section. JRA */ + SSVALS(p, 0, -1); /* what is this? not in spec */ +#endif + namelen = srvstr_push(outbuf, p, s, -1, STR_ASCII|STR_TERMINATE); + p += namelen; + outsize = set_message_end(outbuf, p); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + DEBUG( 2, ( "created temp file %s\n", fname ) ); + DEBUG( 3, ( "ctemp %s fd=%d umode=%o\n", + fname, fsp->fd, sbuf.st_mode ) ); + + END_PROFILE(SMBctemp); + return(outsize); +} + +/******************************************************************* + Check if a user is allowed to rename a file. +********************************************************************/ + +static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst) +{ + int smb_action; + int access_mode; + files_struct *fsp; + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + if (S_ISDIR(pst->st_mode)) + return NT_STATUS_OK; + + /* We need a better way to return NT status codes from open... */ + unix_ERR_class = 0; + unix_ERR_code = 0; + + fsp = open_file_shared1(conn, fname, pst, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action); + + if (!fsp) { + NTSTATUS ret = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + ret = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + unix_ERR_ntstatus = NT_STATUS_OK; + return ret; + } + close_file(fsp,False); + return NT_STATUS_OK; +} + +/******************************************************************* + Check if a user is allowed to delete a file. +********************************************************************/ + +static NTSTATUS can_delete(char *fname,connection_struct *conn, int dirtype, BOOL bad_path) +{ + SMB_STRUCT_STAT sbuf; + int fmode; + int smb_action; + int access_mode; + files_struct *fsp; + + DEBUG(10,("can_delete: %s, dirtype = %d\n", + fname, dirtype )); + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + if (SMB_VFS_LSTAT(conn,fname,&sbuf) != 0) { + if(errno == ENOENT) { + if (bad_path) + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + else + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return map_nt_error_from_unix(errno); + } + + fmode = dos_mode(conn,fname,&sbuf); + + /* Can't delete a directory. */ + if (fmode & aDIR) + return NT_STATUS_FILE_IS_A_DIRECTORY; +#if 0 /* JRATEST */ + else if (dirtype & aDIR) /* Asked for a directory and it isn't. */ + return NT_STATUS_OBJECT_NAME_INVALID; +#endif /* JRATEST */ + + if (!lp_delete_readonly(SNUM(conn))) { + if (fmode & aRONLY) + return NT_STATUS_CANNOT_DELETE; + } + if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) + return NT_STATUS_NO_SUCH_FILE; + + /* We need a better way to return NT status codes from open... */ + unix_ERR_class = 0; + unix_ERR_code = 0; + + fsp = open_file_shared1(conn, fname, &sbuf, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action); + + if (!fsp) { + NTSTATUS ret = NT_STATUS_ACCESS_DENIED; + if (!NT_STATUS_IS_OK(unix_ERR_ntstatus)) + ret = unix_ERR_ntstatus; + else if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + ret = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + unix_ERR_ntstatus = NT_STATUS_OK; + return ret; + } + close_file(fsp,False); + return NT_STATUS_OK; +} + +/**************************************************************************** + The guts of the unlink command, split out so it may be called by the NT SMB + code. +****************************************************************************/ + +NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) +{ + pstring directory; + pstring mask; + char *p; + int count=0; + NTSTATUS error = NT_STATUS_OK; + BOOL has_wild; + BOOL bad_path = False; + BOOL rc = True; + SMB_STRUCT_STAT sbuf; + + *directory = *mask = 0; + + /* We must check for wildcards in the name given + * directly by the client - before any unmangling. + * This prevents an unmangling of a UNIX name containing + * a DOS wildcard like '*' or '?' from unmangling into + * a wildcard delete which was not intended. + * FIX for #226. JRA. + */ + + has_wild = ms_has_wild(name); + + rc = unix_convert(name,conn,0,&bad_path,&sbuf); + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"."); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + } + + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask, sizeof(pstring)-1 ); + + if (!has_wild) { + pstrcat(directory,"/"); + pstrcat(directory,mask); + error = can_delete(directory,conn,dirtype,bad_path); + if (!NT_STATUS_IS_OK(error)) + return error; + + if (SMB_VFS_UNLINK(conn,directory) == 0) { + count++; + } + } else { + void *dirptr = NULL; + const char *dname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then + the pattern matches against the long name, otherwise the short name + We don't implement this yet XXXX + */ + + if (dirptr) { + error = NT_STATUS_NO_SUCH_FILE; + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + BOOL sys_direntry = False; + pstrcpy(fname,dname); + + /* Quick check for "." and ".." */ + if (fname[0] == '.') { + if (!fname[1] || (fname[1] == '.' && !fname[2])) { + if ((dirtype & aDIR)) { + sys_direntry = True; + } else { + continue; + } + } + } + + if(!mask_match(fname, mask, case_sensitive)) + continue; + + if (sys_direntry) { + error = NT_STATUS_OBJECT_NAME_INVALID; + break; + } + + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + error = can_delete(fname,conn,dirtype,bad_path); + if (!NT_STATUS_IS_OK(error)) { + continue; + } + if (SMB_VFS_UNLINK(conn,fname) == 0) + count++; + DEBUG(3,("unlink_internals: succesful unlink [%s]\n",fname)); + } + CloseDir(dirptr); + } + } + + if (count == 0 && NT_STATUS_IS_OK(error)) { + error = map_nt_error_from_unix(errno); + } + + return error; +} + +/**************************************************************************** + Reply to a unlink +****************************************************************************/ + +int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + int outsize = 0; + pstring name; + int dirtype; + NTSTATUS status; + START_PROFILE(SMBunlink); + + dirtype = SVAL(inbuf,smb_vwv0); + + srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBunlink); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + + DEBUG(3,("reply_unlink : %s\n",name)); + + status = unlink_internals(conn, dirtype, name); + if (!NT_STATUS_IS_OK(status)) + return ERROR_NT(status); + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + + outsize = set_message(outbuf,0,0,True); + + END_PROFILE(SMBunlink); + return outsize; +} + +/**************************************************************************** + Fail for readbraw. +****************************************************************************/ + +void fail_readraw(void) +{ + pstring errstr; + slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)", + strerror(errno) ); + exit_server(errstr); +} + +/**************************************************************************** + Use sendfile in readbraw. +****************************************************************************/ + +void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T startpos, size_t nread, + ssize_t mincount, char *outbuf) +{ + ssize_t ret=0; + +#if defined(WITH_SENDFILE) + /* + * We can only use sendfile on a non-chained packet and on a file + * that is exclusively oplocked. reply_readbraw has already checked the length. + */ + + if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) && + EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && lp_use_sendfile(SNUM(conn)) ) { + DATA_BLOB header; + + _smb_setlen(outbuf,nread); + header.data = outbuf; + header.length = 4; + header.free = NULL; + + if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, nread) == -1) { + /* + * Special hack for broken Linux with no 64 bit clean sendfile. If we + * return ENOSYS then pretend we just got a normal read. + */ + if (errno == ENOSYS) + goto normal_read; + + DEBUG(0,("send_file_readbraw: sendfile failed for file %s (%s). Terminating\n", + fsp->fsp_name, strerror(errno) )); + exit_server("send_file_readbraw sendfile failed"); + } + + } + + normal_read: +#endif + + if (nread > 0) { + ret = read_file(fsp,outbuf+4,startpos,nread); +#if 0 /* mincount appears to be ignored in a W2K server. JRA. */ + if (ret < mincount) + ret = 0; +#else + if (ret < nread) + ret = 0; +#endif + } + + _smb_setlen(outbuf,ret); + if (write_data(smbd_server_fd(),outbuf,4+ret) != 4+ret) + fail_readraw(); +} + +/**************************************************************************** + Reply to a readbraw (core+ protocol). +****************************************************************************/ + +int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize) +{ + extern struct current_user current_user; + ssize_t maxcount,mincount; + size_t nread = 0; + SMB_OFF_T startpos; + char *header = outbuf; + files_struct *fsp; + START_PROFILE(SMBreadbraw); + + if (srv_is_signing_active()) { + exit_server("reply_readbraw: SMB signing is active - raw reads/writes are disallowed."); + } + + /* + * Special check if an oplock break has been issued + * and the readraw request croses on the wire, we must + * return a zero length response here. + */ + + if(global_oplock_break) { + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + DEBUG(5,("readbraw - oplock break finished\n")); + END_PROFILE(SMBreadbraw); + return -1; + } + + fsp = file_fsp(inbuf,smb_vwv0); + + if (!FNUM_OK(fsp,conn) || !fsp->can_read) { + /* + * fsp could be NULL here so use the value from the packet. JRA. + */ + DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0))); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } + + CHECK_FSP(fsp,conn); + + flush_write_cache(fsp, READRAW_FLUSH); + + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1); + if(CVAL(inbuf,smb_wct) == 10) { + /* + * This is a large offset (64 bit) read. + */ +#ifdef LARGE_SMB_OFF_T + + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv8) != 0) { + DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) )); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } + +#endif /* LARGE_SMB_OFF_T */ + + if(startpos < 0) { + DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos )); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } + } + maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF); + mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF); + + /* ensure we don't overrun the packet size */ + maxcount = MIN(65535,maxcount); + + if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + SMB_OFF_T size = fsp->size; + SMB_OFF_T sizeneeded = startpos + maxcount; + + if (size < sizeneeded) { + SMB_STRUCT_STAT st; + if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0) + size = st.st_size; + if (!fsp->can_write) + fsp->size = size; + } + + if (startpos >= size) + nread = 0; + else + nread = MIN(maxcount,(size - startpos)); + } + +#if 0 /* mincount appears to be ignored in a W2K server. JRA. */ + if (nread < mincount) + nread = 0; +#endif + + DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos, + (int)maxcount, (int)mincount, (int)nread ) ); + + send_file_readbraw(conn, fsp, startpos, nread, mincount, outbuf); + + DEBUG(5,("readbraw finished\n")); + END_PROFILE(SMBreadbraw); + return -1; +} + +/**************************************************************************** + Reply to a lockread (core+ protocol). +****************************************************************************/ + +int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length, int dum_buffsiz) +{ + ssize_t nread = -1; + char *data; + int outsize = 0; + SMB_OFF_T startpos; + size_t numtoread; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + BOOL my_lock_ctx = False; + START_PROFILE(SMBlockread); + + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); + + release_level_2_oplocks_on_change(fsp); + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); + + outsize = set_message(outbuf,5,3,True); + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + data = smb_buf(outbuf) + 3; + + /* + * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+ + * protocol request that predates the read/write lock concept. + * Thus instead of asking for a read lock here we need to ask + * for a write lock. JRA. + * Note that the requested lock size is unaffected by max_recv. + */ + + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK, &my_lock_ctx); + + if (NT_STATUS_V(status)) { +#if 0 + /* + * We used to make lockread a blocking lock. It turns out + * that this isn't on W2k. Found by the Samba 4 RAW-READ torture + * tester. JRA. + */ + + if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)startpos, + (SMB_BIG_UINT)numtoread)) { + END_PROFILE(SMBlockread); + return -1; + } + } +#endif + END_PROFILE(SMBlockread); + return ERROR_NT(status); + } + + /* + * However the requested READ size IS affected by max_recv. Insanity.... JRA. + */ + + if (numtoread > max_recv) { + DEBUG(0,("reply_lockread: requested read size (%u) is greater than maximum allowed (%u). \ +Returning short read of maximum allowed for compatibility with Windows 2000.\n", + (unsigned int)numtoread, (unsigned int)max_recv )); + numtoread = MIN(numtoread,max_recv); + } + nread = read_file(fsp,data,startpos,numtoread); + + if (nread < 0) { + END_PROFILE(SMBlockread); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + SSVAL(smb_buf(outbuf),1,nread); + + DEBUG(3,("lockread fnum=%d num=%d nread=%d\n", + fsp->fnum, (int)numtoread, (int)nread)); + + END_PROFILE(SMBlockread); + return(outsize); +} + +/**************************************************************************** + Reply to a read. +****************************************************************************/ + +int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + size_t numtoread; + ssize_t nread = 0; + char *data; + SMB_OFF_T startpos; + int outsize = 0; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBread); + + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); + + outsize = set_message(outbuf,5,3,True); + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + /* + * The requested read size cannot be greater than max_recv. JRA. + */ + if (numtoread > max_recv) { + DEBUG(0,("reply_read: requested read size (%u) is greater than maximum allowed (%u). \ +Returning short read of maximum allowed for compatibility with Windows 2000.\n", + (unsigned int)numtoread, (unsigned int)max_recv )); + numtoread = MIN(numtoread,max_recv); + } + + data = smb_buf(outbuf) + 3; + + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBread); + return ERROR_DOS(ERRDOS,ERRlock); + } + + if (numtoread > 0) + nread = read_file(fsp,data,startpos,numtoread); + + if (nread < 0) { + END_PROFILE(SMBread); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + SCVAL(smb_buf(outbuf),0,1); + SSVAL(smb_buf(outbuf),1,nread); + + DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n", + fsp->fnum, (int)numtoread, (int)nread ) ); + + END_PROFILE(SMBread); + return(outsize); +} + +/**************************************************************************** + Reply to a read and X - possibly using sendfile. +****************************************************************************/ + +int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length, + files_struct *fsp, SMB_OFF_T startpos, size_t smb_maxcnt) +{ + ssize_t nread = -1; + char *data = smb_buf(outbuf); + +#if defined(WITH_SENDFILE) + /* + * We can only use sendfile on a non-chained packet and on a file + * that is exclusively oplocked. + */ + + if ((CVAL(inbuf,smb_vwv0) == 0xFF) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && + lp_use_sendfile(SNUM(conn)) && (lp_write_cache_size(SNUM(conn)) == 0) ) { + SMB_STRUCT_STAT sbuf; + DATA_BLOB header; + + if(SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf) == -1) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (startpos > sbuf.st_size) + goto normal_read; + + if (smb_maxcnt > (sbuf.st_size - startpos)) + smb_maxcnt = (sbuf.st_size - startpos); + + if (smb_maxcnt == 0) + goto normal_read; + + /* + * Set up the packet header before send. We + * assume here the sendfile will work (get the + * correct amount of data). + */ + + SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */ + SSVAL(outbuf,smb_vwv5,smb_maxcnt); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); + SSVAL(smb_buf(outbuf),-2,smb_maxcnt); + SCVAL(outbuf,smb_vwv0,0xFF); + set_message(outbuf,12,smb_maxcnt,False); + header.data = outbuf; + header.length = data - outbuf; + header.free = NULL; + + if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, smb_maxcnt) == -1) { + /* + * Special hack for broken Linux with no 64 bit clean sendfile. If we + * return ENOSYS then pretend we just got a normal read. + */ + if (errno == ENOSYS) + goto normal_read; + + DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n", + fsp->fsp_name, strerror(errno) )); + exit_server("send_file_readX sendfile failed"); + } + + DEBUG( 3, ( "send_file_readX: sendfile fnum=%d max=%d nread=%d\n", + fsp->fnum, (int)smb_maxcnt, (int)nread ) ); + return -1; + } + + normal_read: + +#endif + + nread = read_file(fsp,data,startpos,smb_maxcnt); + + if (nread < 0) { + END_PROFILE(SMBreadX); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */ + SSVAL(outbuf,smb_vwv5,nread); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); + SSVAL(smb_buf(outbuf),-2,nread); + + DEBUG( 3, ( "send_file_readX fnum=%d max=%d nread=%d\n", + fsp->fnum, (int)smb_maxcnt, (int)nread ) ); + + return nread; +} + +/**************************************************************************** + Reply to a read and X. +****************************************************************************/ + +int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); + ssize_t nread = -1; + size_t smb_maxcnt = SVAL(inbuf,smb_vwv5); +#if 0 + size_t smb_mincnt = SVAL(inbuf,smb_vwv6); +#endif + + START_PROFILE(SMBreadX); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBreadX); + return reply_pipe_read_and_X(inbuf,outbuf,length,bufsize); + } + + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); + + set_message(outbuf,12,0,True); + + if(CVAL(inbuf,smb_wct) == 12) { +#ifdef LARGE_SMB_OFF_T + /* + * This is a large offset (64 bit) read. + */ + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv10) != 0) { + DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) )); + END_PROFILE(SMBreadX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + +#endif /* LARGE_SMB_OFF_T */ + + } + + if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBreadX); + return ERROR_DOS(ERRDOS,ERRlock); + } + + nread = send_file_readX(conn, inbuf, outbuf, length, fsp, startpos, smb_maxcnt); + if (nread != -1) + nread = chain_reply(inbuf,outbuf,length,bufsize); + + END_PROFILE(SMBreadX); + return nread; +} + +/**************************************************************************** + Reply to a writebraw (core+ or LANMAN1.0 protocol). +****************************************************************************/ + +int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + ssize_t nwritten=0; + ssize_t total_written=0; + size_t numtowrite=0; + size_t tcount; + SMB_OFF_T startpos; + char *data=NULL; + BOOL write_through; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwritebraw); + + if (srv_is_signing_active()) { + exit_server("reply_writebraw: SMB signing is active - raw reads/writes are disallowed."); + } + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + tcount = IVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); + write_through = BITSETW(inbuf+smb_vwv7,0); + + /* We have to deal with slightly different formats depending + on whether we are using the core+ or lanman1.0 protocol */ + + if(Protocol <= PROTOCOL_COREPLUS) { + numtowrite = SVAL(smb_buf(inbuf),-2); + data = smb_buf(inbuf); + } else { + numtowrite = SVAL(inbuf,smb_vwv10); + data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11); + } + + /* force the error type */ + SCVAL(inbuf,smb_com,SMBwritec); + SCVAL(outbuf,smb_com,SMBwritec); + + if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwritebraw); + return(ERROR_DOS(ERRDOS,ERRlock)); + } + + if (numtowrite>0) + nwritten = write_file(fsp,data,startpos,numtowrite); + + DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n", + fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through)); + + if (nwritten < (ssize_t)numtowrite) { + END_PROFILE(SMBwritebraw); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + total_written = nwritten; + + /* Return a message to the redirector to tell it to send more bytes */ + SCVAL(outbuf,smb_com,SMBwritebraw); + SSVALS(outbuf,smb_vwv0,-1); + outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_writebraw: send_smb failed."); + + /* Now read the raw data into the buffer and write it */ + if (read_smb_length(smbd_server_fd(),inbuf,SMB_SECONDARY_WAIT) == -1) { + exit_server("secondary writebraw failed"); + } + + /* Even though this is not an smb message, smb_len returns the generic length of an smb message */ + numtowrite = smb_len(inbuf); + + /* Set up outbuf to return the correct return */ + outsize = set_message(outbuf,1,0,True); + SCVAL(outbuf,smb_com,SMBwritec); + SSVAL(outbuf,smb_vwv0,total_written); + + if (numtowrite != 0) { + + if (numtowrite > BUFFER_SIZE) { + DEBUG(0,("reply_writebraw: Oversize secondary write raw requested (%u). Terminating\n", + (unsigned int)numtowrite )); + exit_server("secondary writebraw failed"); + } + + if (tcount > nwritten+numtowrite) { + DEBUG(3,("Client overestimated the write %d %d %d\n", + (int)tcount,(int)nwritten,(int)numtowrite)); + } + + if (read_data( smbd_server_fd(), inbuf+4, numtowrite) != numtowrite ) { + DEBUG(0,("reply_writebraw: Oversize secondary write raw read failed (%s). Terminating\n", + strerror(errno) )); + exit_server("secondary writebraw failed"); + } + + nwritten = write_file(fsp,inbuf+4,startpos+nwritten,numtowrite); + + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + if (nwritten > 0) + total_written += nwritten; + } + + if ((lp_syncalways(SNUM(conn)) || write_through) && lp_strict_sync(SNUM(conn))) + sync_file(conn,fsp); + + DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n", + fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written)); + + /* we won't return a status if write through is not selected - this follows what WfWg does */ + END_PROFILE(SMBwritebraw); + if (!write_through && total_written==tcount) { + +#if RABBIT_PELLET_FIX + /* + * Fix for "rabbit pellet" mode, trigger an early TCP ack by + * sending a SMBkeepalive. Thanks to DaveCB at Sun for this. JRA. + */ + if (!send_keepalive(smbd_server_fd())) + exit_server("reply_writebraw: send of keepalive failed"); +#endif + return(-1); + } + + return(outsize); +} + +/**************************************************************************** + Reply to a writeunlock (core+). +****************************************************************************/ + +int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, + int size, int dum_buffsize) +{ + ssize_t nwritten = -1; + size_t numtowrite; + SMB_OFF_T startpos; + char *data; + NTSTATUS status = NT_STATUS_OK; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwriteunlock); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; + + if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, + WRITE_LOCK,False)) { + END_PROFILE(SMBwriteunlock); + return ERROR_DOS(ERRDOS,ERRlock); + } + + /* The special X/Open SMB protocol handling of + zero length writes is *NOT* done for + this call */ + if(numtowrite == 0) + nwritten = 0; + else + nwritten = write_file(fsp,data,startpos,numtowrite); + + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteunlock); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + if (numtowrite) { + status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite, + (SMB_BIG_UINT)startpos); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBwriteunlock); + return ERROR_NT(status); + } + } + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten)); + + END_PROFILE(SMBwriteunlock); + return outsize; +} + +/**************************************************************************** + Reply to a write. +****************************************************************************/ + +int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize) +{ + size_t numtowrite; + ssize_t nwritten = -1; + SMB_OFF_T startpos; + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwrite); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBwrite); + return reply_pipe_write(inbuf,outbuf,size,dum_buffsize); + } + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; + + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwrite); + return ERROR_DOS(ERRDOS,ERRlock); + } + + /* + * X/Open SMB protocol says that if smb_vwv1 is + * zero then the file size should be extended or + * truncated to the size given in smb_vwv[2-3]. + */ + + if(numtowrite == 0) { + /* + * This is actually an allocate call, and set EOF. JRA. + */ + nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos); + if (nwritten < 0) { + END_PROFILE(SMBwrite); + return ERROR_NT(NT_STATUS_DISK_FULL); + } + nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos); + if (nwritten < 0) { + END_PROFILE(SMBwrite); + return ERROR_NT(NT_STATUS_DISK_FULL); + } + } else + nwritten = write_file(fsp,data,startpos,numtowrite); + + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwrite); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten)); + + END_PROFILE(SMBwrite); + return(outsize); +} + +/**************************************************************************** + Reply to a write and X. +****************************************************************************/ + +int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); + size_t numtowrite = SVAL(inbuf,smb_vwv10); + BOOL write_through = BITSETW(inbuf+smb_vwv7,0); + ssize_t nwritten = -1; + unsigned int smb_doff = SVAL(inbuf,smb_vwv11); + unsigned int smblen = smb_len(inbuf); + char *data; + BOOL large_writeX = ((CVAL(inbuf,smb_wct) == 14) && (smblen > 0xFFFF)); + START_PROFILE(SMBwriteX); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBwriteX); + return reply_pipe_write_and_X(inbuf,outbuf,length,bufsize); + } + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + /* Deal with possible LARGE_WRITEX */ + if (large_writeX) + numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16); + + if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) { + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRbadmem); + } + + data = smb_base(inbuf) + smb_doff; + + if(CVAL(inbuf,smb_wct) == 14) { +#ifdef LARGE_SMB_OFF_T + /* + * This is a large offset (64 bit) write. + */ + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv12) != 0) { + DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) )); + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + +#endif /* LARGE_SMB_OFF_T */ + } + + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRlock); + } + + /* X/Open SMB protocol says that, unlike SMBwrite + if the length is zero then NO truncation is + done, just a write of zero. To truncate a file, + use SMBwrite. */ + + if(numtowrite == 0) + nwritten = 0; + else + nwritten = write_file(fsp,data,startpos,numtowrite); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteX); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + set_message(outbuf,6,0,True); + + SSVAL(outbuf,smb_vwv2,nwritten); + if (large_writeX) + SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1); + + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten)); + + if (lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); + + END_PROFILE(SMBwriteX); + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + Reply to a lseek. +****************************************************************************/ + +int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + SMB_OFF_T startpos; + SMB_OFF_T res= -1; + int mode,umode; + int outsize = 0; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlseek); + + CHECK_FSP(fsp,conn); + + flush_write_cache(fsp, SEEK_FLUSH); + + mode = SVAL(inbuf,smb_vwv1) & 3; + /* NB. This doesn't use IVAL_TO_SMB_OFF_T as startpos can be signed in this case. */ + startpos = (SMB_OFF_T)IVALS(inbuf,smb_vwv2); + + switch (mode) { + case 0: + umode = SEEK_SET; + res = startpos; + break; + case 1: + umode = SEEK_CUR; + res = fsp->pos + startpos; + break; + case 2: + umode = SEEK_END; + break; + default: + umode = SEEK_SET; + res = startpos; + break; + } + + if (umode == SEEK_END) { + if((res = SMB_VFS_LSEEK(fsp,fsp->fd,startpos,umode)) == -1) { + if(errno == EINVAL) { + SMB_OFF_T current_pos = startpos; + SMB_STRUCT_STAT sbuf; + + if(SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf) == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + current_pos += sbuf.st_size; + if(current_pos < 0) + res = SMB_VFS_LSEEK(fsp,fsp->fd,0,SEEK_SET); + } + } + + if(res == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + + fsp->pos = res; + + outsize = set_message(outbuf,2,0,True); + SIVAL(outbuf,smb_vwv0,res); + + DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n", + fsp->fnum, (double)startpos, (double)res, mode)); + + END_PROFILE(SMBlseek); + return(outsize); +} + +/**************************************************************************** + Reply to a flush. +****************************************************************************/ + +int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + uint16 fnum = SVAL(inbuf,smb_vwv0); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBflush); + + if (fnum != 0xFFFF) + CHECK_FSP(fsp,conn); + + if (!fsp) { + file_sync_all(conn); + } else { + sync_file(conn,fsp); + } + + DEBUG(3,("flush\n")); + END_PROFILE(SMBflush); + return(outsize); +} + +/**************************************************************************** + Reply to a exit. +****************************************************************************/ + +int reply_exit(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize; + START_PROFILE(SMBexit); + + file_close_pid(SVAL(inbuf,smb_pid)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("exit\n")); + + END_PROFILE(SMBexit); + return(outsize); +} + +/**************************************************************************** + Reply to a close - has to deal with closing a directory opened by NT SMB's. +****************************************************************************/ + +int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) +{ + extern struct current_user current_user; + int outsize = 0; + time_t mtime; + int32 eclass = 0, err = 0; + files_struct *fsp = NULL; + START_PROFILE(SMBclose); + + outsize = set_message(outbuf,0,0,True); + + /* If it's an IPC, pass off to the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBclose); + return reply_pipe_close(conn, inbuf,outbuf); + } + + fsp = file_fsp(inbuf,smb_vwv0); + + /* + * We can only use CHECK_FSP if we know it's not a directory. + */ + + if(!fsp || (fsp->conn != conn) || (fsp->vuid != current_user.vuid)) { + END_PROFILE(SMBclose); + return ERROR_DOS(ERRDOS,ERRbadfid); + } + + if(fsp->is_directory) { + /* + * Special case - close NT SMB directory handle. + */ + DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum)); + close_file(fsp,True); + } else { + /* + * Close ordinary file. + */ + int close_err; + pstring file_name; + + /* Save the name for time set in close. */ + pstrcpy( file_name, fsp->fsp_name); + + DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n", + fsp->fd, fsp->fnum, + conn->num_files_open)); + + /* + * close_file() returns the unix errno if an error + * was detected on close - normally this is due to + * a disk full error. If not then it was probably an I/O error. + */ + + if((close_err = close_file(fsp,True)) != 0) { + errno = close_err; + END_PROFILE(SMBclose); + return (UNIXERROR(ERRHRD,ERRgeneral)); + } + + /* + * Now take care of any time sent in the close. + */ + + mtime = make_unix_date3(inbuf+smb_vwv1); + + /* try and set the date */ + set_filetime(conn, file_name, mtime); + + } + + /* We have a cached error */ + if(eclass || err) { + END_PROFILE(SMBclose); + return ERROR_DOS(eclass,err); + } + + END_PROFILE(SMBclose); + return(outsize); +} + +/**************************************************************************** + Reply to a writeclose (Core+ protocol). +****************************************************************************/ + +int reply_writeclose(connection_struct *conn, + char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + int close_err = 0; + SMB_OFF_T startpos; + char *data; + time_t mtime; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteclose); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); + mtime = make_unix_date3(inbuf+smb_vwv4); + data = smb_buf(inbuf) + 1; + + if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwriteclose); + return ERROR_DOS(ERRDOS,ERRlock); + } + + nwritten = write_file(fsp,data,startpos,numtowrite); + + set_filetime(conn, fsp->fsp_name,mtime); + + /* + * More insanity. W2K only closes the file if writelen > 0. + * JRA. + */ + + if (numtowrite) { + DEBUG(3,("reply_writeclose: zero length write doesn't close file %s\n", + fsp->fsp_name )); + close_err = close_file(fsp,True); + } + + DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n", + fsp->fnum, (int)numtowrite, (int)nwritten, + conn->num_files_open)); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteclose); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + if(close_err != 0) { + errno = close_err; + END_PROFILE(SMBwriteclose); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + END_PROFILE(SMBwriteclose); + return(outsize); +} + +/**************************************************************************** + Reply to a lock. +****************************************************************************/ + +int reply_lock(connection_struct *conn, + char *inbuf,char *outbuf, int length, int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + SMB_BIG_UINT count,offset; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + BOOL my_lock_ctx = False; + + START_PROFILE(SMBlock); + + CHECK_FSP(fsp,conn); + + release_level_2_oplocks_on_change(fsp); + + count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); + offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); + + DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n", + fsp->fd, fsp->fnum, (double)offset, (double)count)); + + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx); + if (NT_STATUS_V(status)) { +#if 0 + /* Tests using Samba4 against W2K show this call never creates a blocking lock. */ + if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), offset, count)) { + END_PROFILE(SMBlock); + return -1; + } + } +#endif + END_PROFILE(SMBlock); + return ERROR_NT(status); + } + + END_PROFILE(SMBlock); + return(outsize); +} + +/**************************************************************************** + Reply to a unlock. +****************************************************************************/ + +int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + SMB_BIG_UINT count,offset; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBunlock); + + CHECK_FSP(fsp,conn); + + count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); + offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); + + status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBunlock); + return ERROR_NT(status); + } + + DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n", + fsp->fd, fsp->fnum, (double)offset, (double)count ) ); + + END_PROFILE(SMBunlock); + return(outsize); +} + +/**************************************************************************** + Reply to a tdis. +****************************************************************************/ + +int reply_tdis(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + uint16 vuid; + START_PROFILE(SMBtdis); + + vuid = SVAL(inbuf,smb_uid); + + if (!conn) { + DEBUG(4,("Invalid connection in tdis\n")); + END_PROFILE(SMBtdis); + return ERROR_DOS(ERRSRV,ERRinvnid); + } + + conn->used = False; + + close_cnum(conn,vuid); + + END_PROFILE(SMBtdis); + return outsize; +} + +/**************************************************************************** + Reply to a echo. +****************************************************************************/ + +int reply_echo(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int smb_reverb = SVAL(inbuf,smb_vwv0); + int seq_num; + unsigned int data_len = smb_buflen(inbuf); + int outsize = set_message(outbuf,1,data_len,True); + START_PROFILE(SMBecho); + + if (data_len > BUFFER_SIZE) { + DEBUG(0,("reply_echo: data_len too large.\n")); + END_PROFILE(SMBecho); + return -1; + } + + /* copy any incoming data back out */ + if (data_len > 0) + memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len); + + if (smb_reverb > 100) { + DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb)); + smb_reverb = 100; + } + + for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) { + SSVAL(outbuf,smb_vwv0,seq_num); + + smb_setlen(outbuf,outsize - 4); + + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_echo: send_smb failed."); + } + + DEBUG(3,("echo %d times\n", smb_reverb)); + + smb_echo_count++; + + END_PROFILE(SMBecho); + return -1; +} + +/**************************************************************************** + Reply to a printopen. +****************************************************************************/ + +int reply_printopen(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = 0; + files_struct *fsp; + START_PROFILE(SMBsplopen); + + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplopen); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + /* Open for exclusive use, write only. */ + fsp = print_fsp_open(conn, NULL); + + if (!fsp) { + END_PROFILE(SMBsplopen); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + DEBUG(3,("openprint fd=%d fnum=%d\n", + fsp->fd, fsp->fnum)); + + END_PROFILE(SMBsplopen); + return(outsize); +} + +/**************************************************************************** + Reply to a printclose. +****************************************************************************/ + +int reply_printclose(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int close_err = 0; + START_PROFILE(SMBsplclose); + + CHECK_FSP(fsp,conn); + + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplclose); + return ERROR_NT(NT_STATUS_UNSUCCESSFUL); + } + + DEBUG(3,("printclose fd=%d fnum=%d\n", + fsp->fd,fsp->fnum)); + + close_err = close_file(fsp,True); + + if(close_err != 0) { + errno = close_err; + END_PROFILE(SMBsplclose); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + END_PROFILE(SMBsplclose); + return(outsize); +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ + +int reply_printqueue(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = set_message(outbuf,2,3,True); + int max_count = SVAL(inbuf,smb_vwv0); + int start_index = SVAL(inbuf,smb_vwv1); + START_PROFILE(SMBsplretq); + + /* we used to allow the client to get the cnum wrong, but that + is really quite gross and only worked when there was only + one printer - I think we should now only accept it if they + get it right (tridge) */ + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplretq); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + SSVAL(outbuf,smb_vwv0,0); + SSVAL(outbuf,smb_vwv1,0); + SCVAL(smb_buf(outbuf),0,1); + SSVAL(smb_buf(outbuf),1,0); + + DEBUG(3,("printqueue start_index=%d max_count=%d\n", + start_index, max_count)); + + { + print_queue_struct *queue = NULL; + print_status_struct status; + char *p = smb_buf(outbuf) + 3; + int count = print_queue_status(SNUM(conn), &queue, &status); + int num_to_get = ABS(max_count); + int first = (max_count>0?start_index:start_index+max_count+1); + int i; + + if (first >= count) + num_to_get = 0; + else + num_to_get = MIN(num_to_get,count-first); + + + for (i=first;i<first+num_to_get;i++) { + put_dos_date2(p,0,queue[i].time); + SCVAL(p,4,(queue[i].status==LPQ_PRINTING?2:3)); + SSVAL(p,5, queue[i].job); + SIVAL(p,7,queue[i].size); + SCVAL(p,11,0); + srvstr_push(outbuf, p+12, queue[i].fs_user, 16, STR_ASCII); + p += 28; + } + + if (count > 0) { + outsize = set_message(outbuf,2,28*count+3,False); + SSVAL(outbuf,smb_vwv0,count); + SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1)); + SCVAL(smb_buf(outbuf),0,1); + SSVAL(smb_buf(outbuf),1,28*count); + } + + SAFE_FREE(queue); + + DEBUG(3,("%d entries returned in queue\n",count)); + } + + END_PROFILE(SMBsplretq); + return(outsize); +} + +/**************************************************************************** + Reply to a printwrite. +****************************************************************************/ + +int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int numtowrite; + int outsize = set_message(outbuf,0,0,True); + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + + START_PROFILE(SMBsplwr); + + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplwr); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + numtowrite = SVAL(smb_buf(inbuf),1); + data = smb_buf(inbuf) + 3; + + if (write_file(fsp,data,-1,numtowrite) != numtowrite) { + END_PROFILE(SMBsplwr); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) ); + + END_PROFILE(SMBsplwr); + return(outsize); +} + +/**************************************************************************** + The guts of the mkdir command, split out so it may be called by the NT SMB + code. +****************************************************************************/ + +NTSTATUS mkdir_internal(connection_struct *conn, pstring directory) +{ + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + int ret= -1; + + unix_convert(directory,conn,0,&bad_path,&sbuf); + + if( strchr_m(directory, ':')) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + if (ms_has_wild(directory)) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + + if (check_name(directory, conn)) + ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory)); + + if (ret == -1) { + if(errno == ENOENT) { + if (bad_path) + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + else + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + Reply to a mkdir. +****************************************************************************/ + +int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring directory; + int outsize; + NTSTATUS status; + START_PROFILE(SMBmkdir); + + srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmkdir); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(directory, conn, inbuf, outbuf); + + status = mkdir_internal(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmkdir); + return ERROR_NT(status); + } + + outsize = set_message(outbuf,0,0,True); + + DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) ); + + END_PROFILE(SMBmkdir); + return(outsize); +} + +/**************************************************************************** + Static function used by reply_rmdir to delete an entire directory + tree recursively. Return False on ok, True on fail. +****************************************************************************/ + +static BOOL recursive_rmdir(connection_struct *conn, char *directory) +{ + const char *dname = NULL; + BOOL ret = False; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr == NULL) + return True; + + while((dname = ReadDirName(dirptr))) { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { + errno = ENOMEM; + ret = True; + break; + } + + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(SMB_VFS_LSTAT(conn,fullname, &st) != 0) { + ret = True; + break; + } + + if(st.st_mode & S_IFDIR) { + if(recursive_rmdir(conn, fullname)!=0) { + ret = True; + break; + } + if(SMB_VFS_RMDIR(conn,fullname) != 0) { + ret = True; + break; + } + } else if(SMB_VFS_UNLINK(conn,fullname) != 0) { + ret = True; + break; + } + } + CloseDir(dirptr); + return ret; +} + +/**************************************************************************** + The internals of the rmdir code - called elsewhere. +****************************************************************************/ + +BOOL rmdir_internals(connection_struct *conn, char *directory) +{ + BOOL ok; + + ok = (SMB_VFS_RMDIR(conn,directory) == 0); + if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { + /* + * Check to see if the only thing in this directory are + * vetoed files/directories. If so then delete them and + * retry. If we fail to delete any of them (and we *don't* + * do a recursive delete) then fail the rmdir. + */ + BOOL all_veto_files = True; + const char *dname; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr != NULL) { + int dirpos = TellDir(dirptr); + while ((dname = ReadDirName(dirptr))) { + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + if(!IS_VETO_PATH(conn, dname)) { + all_veto_files = False; + break; + } + } + + if(all_veto_files) { + SeekDir(dirptr,dirpos); + while ((dname = ReadDirName(dirptr))) { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { + errno = ENOMEM; + break; + } + + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(SMB_VFS_LSTAT(conn,fullname, &st) != 0) + break; + if(st.st_mode & S_IFDIR) { + if(lp_recursive_veto_delete(SNUM(conn))) { + if(recursive_rmdir(conn, fullname) != 0) + break; + } + if(SMB_VFS_RMDIR(conn,fullname) != 0) + break; + } else if(SMB_VFS_UNLINK(conn,fullname) != 0) + break; + } + CloseDir(dirptr); + /* Retry the rmdir */ + ok = (SMB_VFS_RMDIR(conn,directory) == 0); + } else { + CloseDir(dirptr); + } + } else { + errno = ENOTEMPTY; + } + } + + if (!ok) + DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno))); + + return ok; +} + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ + +int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring directory; + int outsize = 0; + BOOL ok = False; + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + NTSTATUS status; + START_PROFILE(SMBrmdir); + + srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBrmdir); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(directory, conn, inbuf, outbuf) + + unix_convert(directory,conn, NULL,&bad_path,&sbuf); + + if (check_name(directory,conn)) { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = rmdir_internals(conn, directory); + } + + if (!ok) { + END_PROFILE(SMBrmdir); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRbadpath); + } + + outsize = set_message(outbuf,0,0,True); + + DEBUG( 3, ( "rmdir %s\n", directory ) ); + + END_PROFILE(SMBrmdir); + return(outsize); +} + +/******************************************************************* + Resolve wildcards in a filename rename. + Note that name is in UNIX charset and thus potentially can be more + than fstring buffer (255 bytes) especially in default UTF-8 case. + Therefore, we use pstring inside and all calls should ensure that + name2 is at least pstring-long (they do already) +********************************************************************/ + +static BOOL resolve_wildcards(const char *name1, char *name2) +{ + pstring root1,root2; + pstring ext1,ext2; + char *p,*p2, *pname1, *pname2; + int available_space, actual_space; + + + pname1 = strrchr_m(name1,'/'); + pname2 = strrchr_m(name2,'/'); + + if (!pname1 || !pname2) + return(False); + + pstrcpy(root1,pname1); + pstrcpy(root2,pname2); + p = strrchr_m(root1,'.'); + if (p) { + *p = 0; + pstrcpy(ext1,p+1); + } else { + pstrcpy(ext1,""); + } + p = strrchr_m(root2,'.'); + if (p) { + *p = 0; + pstrcpy(ext2,p+1); + } else { + pstrcpy(ext2,""); + } + + p = root1; + p2 = root2; + while (*p2) { + if (*p2 == '?') { + *p2 = *p; + p2++; + } else if (*p2 == '*') { + pstrcpy(p2, p); + break; + } else { + p2++; + } + if (*p) + p++; + } + + p = ext1; + p2 = ext2; + while (*p2) { + if (*p2 == '?') { + *p2 = *p; + p2++; + } else if (*p2 == '*') { + pstrcpy(p2, p); + break; + } else { + p2++; + } + if (*p) + p++; + } + + available_space = sizeof(pstring) - PTR_DIFF(pname2, name2); + + if (ext2[0]) { + actual_space = snprintf(pname2, available_space - 1, "%s.%s", root2, ext2); + if (actual_space >= available_space - 1) { + DEBUG(1,("resolve_wildcards: can't fit resolved name into specified buffer (overrun by %d bytes)\n", + actual_space - available_space)); + } + } else { + pstrcpy_base(pname2, root2, name2); + } + + return(True); +} + +/**************************************************************************** + Ensure open files have their names updates. +****************************************************************************/ + +static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, char *newname) +{ + files_struct *fsp; + BOOL did_rename = False; + + for(fsp = file_find_di_first(dev, inode); fsp; fsp = file_find_di_next(fsp)) { + DEBUG(10,("rename_open_files: renaming file fnum %d (dev = %x, inode = %.0f) from %s -> %s\n", + fsp->fnum, (unsigned int)fsp->dev, (double)fsp->inode, + fsp->fsp_name, newname )); + string_set(&fsp->fsp_name, newname); + did_rename = True; + } + + if (!did_rename) + DEBUG(10,("rename_open_files: no open files on dev %x, inode %.0f for %s\n", + (unsigned int)dev, (double)inode, newname )); +} + +/**************************************************************************** + Rename an open file - given an fsp. +****************************************************************************/ + +NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, BOOL replace_if_exists) +{ + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + pstring newname_last_component; + NTSTATUS error = NT_STATUS_OK; + BOOL dest_exists; + BOOL rcdest = True; + + ZERO_STRUCT(sbuf); + rcdest = unix_convert(newname,conn,newname_last_component,&bad_path,&sbuf); + + /* Quick check for "." and ".." */ + if (!bad_path && newname_last_component[0] == '.') { + if (!newname_last_component[1] || (newname_last_component[1] == '.' && !newname_last_component[2])) { + return NT_STATUS_ACCESS_DENIED; + } + } + if (!rcdest && bad_path) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Ensure newname contains a '/' */ + if(strrchr_m(newname,'/') == 0) { + pstring tmpstr; + + pstrcpy(tmpstr, "./"); + pstrcat(tmpstr, newname); + pstrcpy(newname, tmpstr); + } + + /* + * Check for special case with case preserving and not + * case sensitive. If the old last component differs from the original + * last component only by case, then we should allow + * the rename (user is trying to change the case of the + * filename). + */ + + if((case_sensitive == False) && (case_preserve == True) && + strequal(newname, fsp->fsp_name)) { + char *p; + pstring newname_modified_last_component; + + /* + * Get the last component of the modified name. + * Note that we guarantee that newname contains a '/' + * character above. + */ + p = strrchr_m(newname,'/'); + pstrcpy(newname_modified_last_component,p+1); + + if(strcsequal(newname_modified_last_component, + newname_last_component) == False) { + /* + * Replace the modified last component with + * the original. + */ + pstrcpy(p+1, newname_last_component); + } + } + + /* + * If the src and dest names are identical - including case, + * don't do the rename, just return success. + */ + + if (strcsequal(fsp->fsp_name, newname)) { + DEBUG(3,("rename_internals_fsp: identical names in rename %s - returning success\n", + newname)); + return NT_STATUS_OK; + } + + dest_exists = vfs_object_exist(conn,newname,NULL); + + if(!replace_if_exists && dest_exists) { + DEBUG(3,("rename_internals_fsp: dest exists doing rename %s -> %s\n", + fsp->fsp_name,newname)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + error = can_rename(newname,conn,&sbuf); + + if (dest_exists && !NT_STATUS_IS_OK(error)) { + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), fsp->fsp_name,newname)); + if (NT_STATUS_EQUAL(error,NT_STATUS_SHARING_VIOLATION)) + error = NT_STATUS_ACCESS_DENIED; + return error; + } + + if(SMB_VFS_RENAME(conn,fsp->fsp_name, newname) == 0) { + DEBUG(3,("rename_internals_fsp: succeeded doing rename on %s -> %s\n", + fsp->fsp_name,newname)); + rename_open_files(conn, fsp->dev, fsp->inode, newname); + return NT_STATUS_OK; + } + + if (errno == ENOTDIR || errno == EISDIR) + error = NT_STATUS_OBJECT_NAME_COLLISION; + else + error = map_nt_error_from_unix(errno); + + DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n", + nt_errstr(error), fsp->fsp_name,newname)); + + return error; +} + +/**************************************************************************** + The guts of the rename command, split out so it may be called by the NT SMB + code. +****************************************************************************/ + +NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, uint16 attrs, BOOL replace_if_exists) +{ + pstring directory; + pstring mask; + pstring last_component_src; + pstring last_component_dest; + char *p; + BOOL has_wild; + BOOL bad_path_src = False; + BOOL bad_path_dest = False; + int count=0; + NTSTATUS error = NT_STATUS_OK; + BOOL rc = True; + BOOL rcdest = True; + SMB_STRUCT_STAT sbuf1, sbuf2; + + *directory = *mask = 0; + + ZERO_STRUCT(sbuf1); + ZERO_STRUCT(sbuf2); + + rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1); + if (!rc && bad_path_src) { + if (ms_has_wild(last_component_src)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Quick check for "." and ".." */ + if (last_component_src[0] == '.') { + if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2); + + /* Quick check for "." and ".." */ + if (last_component_dest[0] == '.') { + if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + /* + * Split the old name into directory and last component + * strings. Note that unix_convert may have stripped off a + * leading ./ from both name and newname if the rename is + * at the root of the share. We need to make sure either both + * name and newname contain a / character or neither of them do + * as this is checked in resolve_wildcards(). + */ + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"."); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + *p = '/'; /* Replace needed for exceptional test below. */ + } + + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask, sizeof(pstring)-1 ); + + has_wild = ms_has_wild(mask); + + if (!has_wild) { + /* + * No wildcards - just process the one file. + */ + BOOL is_short_name = mangle_is_8_3(name, True); + + /* Add a terminating '/' to the directory name. */ + pstrcat(directory,"/"); + pstrcat(directory,mask); + + /* Ensure newname contains a '/' also */ + if(strrchr_m(newname,'/') == 0) { + pstring tmpstr; + + pstrcpy(tmpstr, "./"); + pstrcat(tmpstr, newname); + pstrcpy(newname, tmpstr); + } + + DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \ +directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n", + case_sensitive, case_preserve, short_case_preserve, directory, + newname, last_component_dest, is_short_name)); + + /* + * Check for special case with case preserving and not + * case sensitive, if directory and newname are identical, + * and the old last component differs from the original + * last component only by case, then we should allow + * the rename (user is trying to change the case of the + * filename). + */ + if((case_sensitive == False) && + (((case_preserve == True) && + (is_short_name == False)) || + ((short_case_preserve == True) && + (is_short_name == True))) && + strcsequal(directory, newname)) { + pstring modified_last_component; + + /* + * Get the last component of the modified name. + * Note that we guarantee that newname contains a '/' + * character above. + */ + p = strrchr_m(newname,'/'); + pstrcpy(modified_last_component,p+1); + + if(strcsequal(modified_last_component, + last_component_dest) == False) { + /* + * Replace the modified last component with + * the original. + */ + pstrcpy(p+1, last_component_dest); + } + } + + resolve_wildcards(directory,newname); + + /* + * The source object must exist. + */ + + if (!vfs_object_exist(conn, directory, &sbuf1)) { + DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n", + directory,newname)); + + if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) { + /* + * Must return different errors depending on whether the parent + * directory existed or not. + */ + + p = strrchr_m(directory, '/'); + if (!p) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + *p = '\0'; + if (vfs_object_exist(conn, directory, NULL)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + error = map_nt_error_from_unix(errno); + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + + return error; + } + + if (!rcdest && bad_path_dest) { + if (ms_has_wild(last_component_dest)) + return NT_STATUS_OBJECT_NAME_INVALID; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + error = can_rename(directory,conn,&sbuf1); + + if (!NT_STATUS_IS_OK(error)) { + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + return error; + } + + /* + * If the src and dest names are identical - including case, + * don't do the rename, just return success. + */ + + if (strcsequal(directory, newname)) { + rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname); + DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory)); + return NT_STATUS_OK; + } + + if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) { + DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n", + directory,newname)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + if(SMB_VFS_RENAME(conn,directory, newname) == 0) { + DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n", + directory,newname)); + rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname); + return NT_STATUS_OK; + } + + if (errno == ENOTDIR || errno == EISDIR) + error = NT_STATUS_OBJECT_NAME_COLLISION; + else + error = map_nt_error_from_unix(errno); + + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + + return error; + } else { + /* + * Wildcards - process each file that matches. + */ + void *dirptr = NULL; + const char *dname; + pstring destname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + if (dirptr) { + error = NT_STATUS_NO_SUCH_FILE; +/* Was error = NT_STATUS_OBJECT_NAME_NOT_FOUND; - gentest fix. JRA */ + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + BOOL sysdir_entry = False; + + pstrcpy(fname,dname); + + /* Quick check for "." and ".." */ + if (fname[0] == '.') { + if (!fname[1] || (fname[1] == '.' && !fname[2])) { + if (attrs & aDIR) { + sysdir_entry = True; + } else { + continue; + } + } + } + + if(!mask_match(fname, mask, case_sensitive)) + continue; + + if (sysdir_entry) { + error = NT_STATUS_OBJECT_NAME_INVALID; + break; + } + + error = NT_STATUS_ACCESS_DENIED; + slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname); + if (!vfs_object_exist(conn, fname, &sbuf1)) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error))); + continue; + } + error = can_rename(fname,conn,&sbuf1); + if (!NT_STATUS_IS_OK(error)) { + DEBUG(6,("rename %s refused\n", fname)); + continue; + } + pstrcpy(destname,newname); + + if (!resolve_wildcards(fname,destname)) { + DEBUG(6,("resolve_wildcards %s %s failed\n", + fname, destname)); + continue; + } + + if (strcsequal(fname,destname)) { + rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname); + DEBUG(3,("rename_internals: identical names in wildcard rename %s - success\n", fname)); + count++; + error = NT_STATUS_OK; + continue; + } + + if (!replace_if_exists && + vfs_file_exist(conn,destname, NULL)) { + DEBUG(6,("file_exist %s\n", destname)); + error = NT_STATUS_OBJECT_NAME_COLLISION; + continue; + } + + if (!SMB_VFS_RENAME(conn,fname,destname)) { + rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname); + count++; + error = NT_STATUS_OK; + } + DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + + if (!NT_STATUS_EQUAL(error,NT_STATUS_NO_SUCH_FILE)) { + if (!rcdest && bad_path_dest) { + if (ms_has_wild(last_component_dest)) + return NT_STATUS_OBJECT_NAME_INVALID; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + } + } + + if (count == 0 && NT_STATUS_IS_OK(error)) { + error = map_nt_error_from_unix(errno); + } + + return error; +} + +/**************************************************************************** + Reply to a mv. +****************************************************************************/ + +int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + int outsize = 0; + pstring name; + pstring newname; + char *p; + uint16 attrs = SVAL(inbuf,smb_vwv0); + NTSTATUS status; + + START_PROFILE(SMBmv); + + p = smb_buf(inbuf) + 1; + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmv); + return ERROR_NT(status); + } + p++; + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmv); + return ERROR_NT(status); + } + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); + + DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); + + status = rename_internals(conn, name, newname, attrs, False); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmv); + return ERROR_NT(status); + } + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + outsize = set_message(outbuf,0,0,True); + + END_PROFILE(SMBmv); + return(outsize); +} + +/******************************************************************* + Copy a file as part of a reply_copy. +******************************************************************/ + +static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, + int count,BOOL target_is_directory, int *err_ret) +{ + int Access,action; + SMB_STRUCT_STAT src_sbuf, sbuf2; + SMB_OFF_T ret=-1; + files_struct *fsp1,*fsp2; + pstring dest; + uint32 dosattrs; + + *err_ret = 0; + + pstrcpy(dest,dest1); + if (target_is_directory) { + char *p = strrchr_m(src,'/'); + if (p) + p++; + else + p = src; + pstrcat(dest,"/"); + pstrcat(dest,p); + } + + if (!vfs_file_exist(conn,src,&src_sbuf)) + return(False); + + fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action); + + if (!fsp1) + return(False); + + if (!target_is_directory && count) + ofun = FILE_EXISTS_OPEN; + + dosattrs = dos_mode(conn, src, &src_sbuf); + if (SMB_VFS_STAT(conn,dest,&sbuf2) == -1) + ZERO_STRUCTP(&sbuf2); + + fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), + ofun,dosattrs,0,&Access,&action); + + if (!fsp2) { + close_file(fsp1,False); + return(False); + } + + if ((ofun&3) == 1) { + if(SMB_VFS_LSEEK(fsp2,fsp2->fd,0,SEEK_END) == -1) { + DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) )); + /* + * Stop the copy from occurring. + */ + ret = -1; + src_sbuf.st_size = 0; + } + } + + if (src_sbuf.st_size) + ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size); + + close_file(fsp1,False); + + /* Ensure the modtime is set correctly on the destination file. */ + fsp2->pending_modtime = src_sbuf.st_mtime; + + /* + * As we are opening fsp1 read-only we only expect + * an error on close on fsp2 if we are out of space. + * Thus we don't look at the error return from the + * close of fsp1. + */ + *err_ret = close_file(fsp2,False); + + return(ret == (SMB_OFF_T)src_sbuf.st_size); +} + +/**************************************************************************** + Reply to a file copy. +****************************************************************************/ + +int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int outsize = 0; + pstring name; + pstring directory; + pstring mask,newname; + char *p; + int count=0; + int error = ERRnoaccess; + int err = 0; + BOOL has_wild; + BOOL exists=False; + int tid2 = SVAL(inbuf,smb_vwv0); + int ofun = SVAL(inbuf,smb_vwv1); + int flags = SVAL(inbuf,smb_vwv2); + BOOL target_is_directory=False; + BOOL bad_path1 = False; + BOOL bad_path2 = False; + BOOL rc = True; + SMB_STRUCT_STAT sbuf1, sbuf2; + NTSTATUS status; + + START_PROFILE(SMBcopy); + + *directory = *mask = 0; + + p = smb_buf(inbuf); + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcopy); + return ERROR_NT(status); + } + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcopy); + return ERROR_NT(status); + } + + DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); + + if (tid2 != conn->cnum) { + /* can't currently handle inter share copies XXXX */ + DEBUG(3,("Rejecting inter-share copy\n")); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRSRV,ERRinvdevice); + } + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); + + rc = unix_convert(name,conn,0,&bad_path1,&sbuf1); + unix_convert(newname,conn,0,&bad_path2,&sbuf2); + + target_is_directory = VALID_STAT_OF_DIR(sbuf2); + + if ((flags&1) && target_is_directory) { + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,ERRbadfile); + } + + if ((flags&2) && !target_is_directory) { + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,ERRbadpath); + } + + if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) { + /* wants a tree copy! XXXX */ + DEBUG(3,("Rejecting tree copy\n")); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRSRV,ERRerror); + } + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"./"); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + } + + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask, sizeof(pstring)-1 ); + + has_wild = ms_has_wild(mask); + + if (!has_wild) { + pstrcat(directory,"/"); + pstrcat(directory,mask); + if (resolve_wildcards(directory,newname) && + copy_file(directory,newname,conn,ofun, count,target_is_directory,&err)) + count++; + if(!count && err) { + errno = err; + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + if (!count) { + exists = vfs_file_exist(conn,directory,NULL); + } + } else { + void *dirptr = NULL; + const char *dname; + pstring destname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + if (dirptr) { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + pstrcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive)) + continue; + + error = ERRnoaccess; + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + pstrcpy(destname,newname); + if (resolve_wildcards(fname,destname) && + copy_file(fname,destname,conn,ofun, + count,target_is_directory,&err)) + count++; + DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if(err) { + /* Error on close... */ + errno = err; + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + if (exists) { + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,error); + } else { + if((errno == ENOENT) && (bad_path1 || bad_path2)) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRDOS,error)); + } + } + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,count); + + END_PROFILE(SMBcopy); + return(outsize); +} + +/**************************************************************************** + Reply to a setdir. +****************************************************************************/ + +int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + int snum; + int outsize = 0; + BOOL ok = False; + pstring newdir; + NTSTATUS status; + + START_PROFILE(pathworks_setdir); + + snum = SNUM(conn); + if (!CAN_SETDIR(snum)) { + END_PROFILE(pathworks_setdir); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(pathworks_setdir); + return ERROR_NT(status); + } + + if (strlen(newdir) == 0) { + ok = True; + } else { + ok = vfs_directory_exist(conn,newdir,NULL); + if (ok) + string_set(&conn->connectpath,newdir); + } + + if (!ok) { + END_PROFILE(pathworks_setdir); + return ERROR_DOS(ERRDOS,ERRbadpath); + } + + outsize = set_message(outbuf,0,0,True); + SCVAL(outbuf,smb_reh,CVAL(inbuf,smb_reh)); + + DEBUG(3,("setdir %s\n", newdir)); + + END_PROFILE(pathworks_setdir); + return(outsize); +} + +/**************************************************************************** + Get a lock pid, dealing with large count requests. +****************************************************************************/ + +uint16 get_lock_pid( char *data, int data_offset, BOOL large_file_format) +{ + if(!large_file_format) + return SVAL(data,SMB_LPID_OFFSET(data_offset)); + else + return SVAL(data,SMB_LARGE_LPID_OFFSET(data_offset)); +} + +/**************************************************************************** + Get a lock count, dealing with large count requests. +****************************************************************************/ + +SMB_BIG_UINT get_lock_count( char *data, int data_offset, BOOL large_file_format) +{ + SMB_BIG_UINT count = 0; + + if(!large_file_format) { + count = (SMB_BIG_UINT)IVAL(data,SMB_LKLEN_OFFSET(data_offset)); + } else { + +#if defined(HAVE_LONGLONG) + count = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset))); +#else /* HAVE_LONGLONG */ + + /* + * NT4.x seems to be broken in that it sends large file (64 bit) + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large unsigned ints truncate the + * lock count by dropping the top 32 bits. + */ + + if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) { + DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n", + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)), + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) )); + SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0); + } + + count = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)); +#endif /* HAVE_LONGLONG */ + } + + return count; +} + +#if !defined(HAVE_LONGLONG) +/**************************************************************************** + Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-). +****************************************************************************/ + +static uint32 map_lock_offset(uint32 high, uint32 low) +{ + unsigned int i; + uint32 mask = 0; + uint32 highcopy = high; + + /* + * Try and find out how many significant bits there are in high. + */ + + for(i = 0; highcopy; i++) + highcopy >>= 1; + + /* + * We use 31 bits not 32 here as POSIX + * lock offsets may not be negative. + */ + + mask = (~0) << (31 - i); + + if(low & mask) + return 0; /* Fail. */ + + high <<= (31 - i); + + return (high|low); +} +#endif /* !defined(HAVE_LONGLONG) */ + +/**************************************************************************** + Get a lock offset, dealing with large offset requests. +****************************************************************************/ + +SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err) +{ + SMB_BIG_UINT offset = 0; + + *err = False; + + if(!large_file_format) { + offset = (SMB_BIG_UINT)IVAL(data,SMB_LKOFF_OFFSET(data_offset)); + } else { + +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset))); +#else /* HAVE_LONGLONG */ + + /* + * NT4.x seems to be broken in that it sends large file (64 bit) + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large unsigned ints mangle the + * lock offset by mapping the top 32 bits onto the lower 32. + */ + + if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) { + uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); + uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)); + uint32 new_low = 0; + + if((new_low = map_lock_offset(high, low)) == 0) { + *err = True; + return (SMB_BIG_UINT)-1; + } + + DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n", + (unsigned int)high, (unsigned int)low, (unsigned int)new_low )); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low); + } + + offset = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); +#endif /* HAVE_LONGLONG */ + } + + return offset; +} + +/**************************************************************************** + Reply to a lockingX request. +****************************************************************************/ + +int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + unsigned char locktype = CVAL(inbuf,smb_vwv3); + unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + uint16 num_locks = SVAL(inbuf,smb_vwv7); + SMB_BIG_UINT count = 0, offset = 0; + uint16 lock_pid; + int32 lock_timeout = IVAL(inbuf,smb_vwv4); + int i; + char *data; + BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False; + BOOL err; + BOOL my_lock_ctx = False; + NTSTATUS status; + + START_PROFILE(SMBlockingX); + + CHECK_FSP(fsp,conn); + + data = smb_buf(inbuf); + + if (locktype & (LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_CHANGE_LOCKTYPE)) { + /* we don't support these - and CANCEL_LOCK makes w2k + and XP reboot so I don't really want to be + compatible! (tridge) */ + return ERROR_NT(NT_STATUS_NOT_SUPPORTED); + } + + /* Check if this is an oplock break on a file + we have granted an oplock on. + */ + if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) { + /* Client can insist on breaking to none. */ + BOOL break_to_none = (oplocklevel == 0); + + DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n", + (unsigned int)oplocklevel, fsp->fnum )); + + /* + * Make sure we have granted an exclusive or batch oplock on this file. + */ + + if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ +no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) { + END_PROFILE(SMBlockingX); + return -1; + } else { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRlock); + } + } + + if (remove_oplock(fsp, break_to_none) == False) { + DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n", + fsp->fsp_name )); + } + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) { + /* Sanity check - ensure a pure oplock break is not a + chained request. */ + if(CVAL(inbuf,smb_vwv0) != 0xff) + DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n", + (unsigned int)CVAL(inbuf,smb_vwv0) )); + END_PROFILE(SMBlockingX); + return -1; + } + } + + /* + * We do this check *after* we have checked this is not a oplock break + * response message. JRA. + */ + + release_level_2_oplocks_on_change(fsp); + + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n", + (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name )); + + status = do_unlock(fsp,conn,lock_pid,count,offset); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBlockingX); + return ERROR_NT(status); + } + } + + /* Setup the timeout in seconds. */ + + lock_timeout = ((lock_timeout == -1) ? -1 : (lock_timeout+999)/1000); + + /* Now do any requested locks */ + data += ((large_file_format ? 20 : 10)*num_ulocks); + + /* Data now points at the beginning of the list + of smb_lkrng structs */ + + for(i = 0; i < (int)num_locks; i++) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n", + (double)offset, (double)count, (unsigned int)lock_pid, + fsp->fsp_name, (int)lock_timeout )); + + status = do_lock_spin(fsp,conn,lock_pid, count,offset, + ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx); + if (NT_STATUS_V(status)) { + /* + * Interesting fact found by IFSTEST /t LockOverlappedTest... + * Even if it's our own lock context, we need to wait here as + * there may be an unlock on the way. + * So I removed a "&& !my_lock_ctx" from the following + * if statement. JRA. + */ + if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, lock_timeout, i, lock_pid, offset, count)) { + END_PROFILE(SMBlockingX); + return -1; + } + } + break; + } + } + + /* If any of the above locks failed, then we must unlock + all of the previous locks (X/Open spec). */ + if (i != num_locks && num_locks != 0) { + /* + * Ensure we don't do a remove on the lock that just failed, + * as under POSIX rules, if we have a lock already there, we + * will delete it (and we shouldn't) ..... + */ + for(i--; i >= 0; i--) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + do_unlock(fsp,conn,lock_pid,count,offset); + } + END_PROFILE(SMBlockingX); + return ERROR_NT(status); + } + + set_message(outbuf,2,0,True); + + DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n", + fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) ); + + END_PROFILE(SMBlockingX); + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + Reply to a SMBreadbmpx (read block multiplex) request. +****************************************************************************/ + +int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + ssize_t nread = -1; + ssize_t total_read; + char *data; + SMB_OFF_T startpos; + int outsize; + size_t maxcount; + int max_per_packet; + size_t tcount; + int pad; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBreadBmpx); + + /* this function doesn't seem to work - disable by default */ + if (!lp_readbmpx()) { + END_PROFILE(SMBreadBmpx); + return ERROR_DOS(ERRSRV,ERRuseSTD); + } + + outsize = set_message(outbuf,8,0,True); + + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); + + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); + + data = smb_buf(outbuf); + pad = ((long)data)%4; + if (pad) + pad = 4 - pad; + data += pad; + + max_per_packet = bufsize-(outsize+pad); + tcount = maxcount; + total_read = 0; + + if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBreadBmpx); + return ERROR_DOS(ERRDOS,ERRlock); + } + + do { + size_t N = MIN(max_per_packet,tcount-total_read); + + nread = read_file(fsp,data,startpos,N); + + if (nread <= 0) + nread = 0; + + if (nread < (ssize_t)N) + tcount = total_read + nread; + + set_message(outbuf,8,nread,False); + SIVAL(outbuf,smb_vwv0,startpos); + SSVAL(outbuf,smb_vwv2,tcount); + SSVAL(outbuf,smb_vwv6,nread); + SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf)); + + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_readbmpx: send_smb failed."); + + total_read += nread; + startpos += nread; + } while (total_read < (ssize_t)tcount); + + END_PROFILE(SMBreadBmpx); + return(-1); +} + +/**************************************************************************** + Reply to a SMBsetattrE. +****************************************************************************/ + +int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + struct utimbuf unix_times; + int outsize = 0; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBsetattrE); + + outsize = set_message(outbuf,0,0,True); + + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBgetattrE); + return ERROR_DOS(ERRDOS,ERRbadfid); + } + + /* + * Convert the DOS times into unix times. Ignore create + * time as UNIX can't set this. + */ + + unix_times.actime = make_unix_date2(inbuf+smb_vwv3); + unix_times.modtime = make_unix_date2(inbuf+smb_vwv5); + + /* + * Patch from Ray Frush <frush@engr.colostate.edu> + * Sometimes times are sent as zero - ignore them. + */ + + if ((unix_times.actime == 0) && (unix_times.modtime == 0)) { + /* Ignore request */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "reply_setattrE fnum=%d ", fsp->fnum); + dbgtext( "ignoring zero request - not setting timestamps of 0\n" ); + } + END_PROFILE(SMBsetattrE); + return(outsize); + } else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) { + /* set modify time = to access time if modify time was 0 */ + unix_times.modtime = unix_times.actime; + } + + /* Set the date on this file */ + if(file_utime(conn, fsp->fsp_name, &unix_times)) { + END_PROFILE(SMBsetattrE); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n", + fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) ); + + END_PROFILE(SMBsetattrE); + return(outsize); +} + + +/* Back from the dead for OS/2..... JRA. */ + +/**************************************************************************** + Reply to a SMBwritebmpx (write block multiplex primary) request. +****************************************************************************/ + +int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + SMB_OFF_T startpos; + size_t tcount; + BOOL write_through; + int smb_doff; + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteBmpx); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + CHECK_ERROR(fsp); + + tcount = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); + write_through = BITSETW(inbuf+smb_vwv7,0); + numtowrite = SVAL(inbuf,smb_vwv10); + smb_doff = SVAL(inbuf,smb_vwv11); + + data = smb_base(inbuf) + smb_doff; + + /* If this fails we need to send an SMBwriteC response, + not an SMBwritebmpx - set this up now so we don't forget */ + SCVAL(outbuf,smb_com,SMBwritec); + + if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK,False)) { + END_PROFILE(SMBwriteBmpx); + return(ERROR_DOS(ERRDOS,ERRlock)); + } + + nwritten = write_file(fsp,data,startpos,numtowrite); + + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); + + if(nwritten < (ssize_t)numtowrite) { + END_PROFILE(SMBwriteBmpx); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } + + /* If the maximum to be written to this file + is greater than what we just wrote then set + up a secondary struct to be attached to this + fd, we will use this to cache error messages etc. */ + + if((ssize_t)tcount > nwritten) { + write_bmpx_struct *wbms; + if(fsp->wbmpx_ptr != NULL) + wbms = fsp->wbmpx_ptr; /* Use an existing struct */ + else + wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct)); + if(!wbms) { + DEBUG(0,("Out of memory in reply_readmpx\n")); + END_PROFILE(SMBwriteBmpx); + return(ERROR_DOS(ERRSRV,ERRnoresource)); + } + wbms->wr_mode = write_through; + wbms->wr_discard = False; /* No errors yet */ + wbms->wr_total_written = nwritten; + wbms->wr_errclass = 0; + wbms->wr_error = 0; + fsp->wbmpx_ptr = wbms; + } + + /* We are returning successfully, set the message type back to + SMBwritebmpx */ + SCVAL(outbuf,smb_com,SMBwriteBmpx); + + outsize = set_message(outbuf,1,0,True); + + SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */ + + DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten ) ); + + if (write_through && tcount==nwritten) { + /* We need to send both a primary and a secondary response */ + smb_setlen(outbuf,outsize - 4); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_writebmpx: send_smb failed."); + + /* Now the secondary */ + outsize = set_message(outbuf,1,0,True); + SCVAL(outbuf,smb_com,SMBwritec); + SSVAL(outbuf,smb_vwv0,nwritten); + } + + END_PROFILE(SMBwriteBmpx); + return(outsize); +} + +/**************************************************************************** + Reply to a SMBwritebs (write block multiplex secondary) request. +****************************************************************************/ + +int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + SMB_OFF_T startpos; + size_t tcount; + BOOL write_through; + int smb_doff; + char *data; + write_bmpx_struct *wbms; + BOOL send_response = False; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteBs); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + tcount = SVAL(inbuf,smb_vwv1); + startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); + numtowrite = SVAL(inbuf,smb_vwv6); + smb_doff = SVAL(inbuf,smb_vwv7); + + data = smb_base(inbuf) + smb_doff; + + /* We need to send an SMBwriteC response, not an SMBwritebs */ + SCVAL(outbuf,smb_com,SMBwritec); + + /* This fd should have an auxiliary struct attached, + check that it does */ + wbms = fsp->wbmpx_ptr; + if(!wbms) { + END_PROFILE(SMBwriteBs); + return(-1); + } + + /* If write through is set we can return errors, else we must cache them */ + write_through = wbms->wr_mode; + + /* Check for an earlier error */ + if(wbms->wr_discard) { + END_PROFILE(SMBwriteBs); + return -1; /* Just discard the packet */ + } + + nwritten = write_file(fsp,data,startpos,numtowrite); + + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); + + if (nwritten < (ssize_t)numtowrite) { + if(write_through) { + /* We are returning an error - we can delete the aux struct */ + if (wbms) + free((char *)wbms); + fsp->wbmpx_ptr = NULL; + END_PROFILE(SMBwriteBs); + return(ERROR_DOS(ERRHRD,ERRdiskfull)); + } + END_PROFILE(SMBwriteBs); + return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull)); + } + + /* Increment the total written, if this matches tcount + we can discard the auxiliary struct (hurrah !) and return a writeC */ + wbms->wr_total_written += nwritten; + if(wbms->wr_total_written >= tcount) { + if (write_through) { + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,wbms->wr_total_written); + send_response = True; + } + + free((char *)wbms); + fsp->wbmpx_ptr = NULL; + } + + if(send_response) { + END_PROFILE(SMBwriteBs); + return(outsize); + } + + END_PROFILE(SMBwriteBs); + return(-1); +} + +/**************************************************************************** + Reply to a SMBgetattrE. +****************************************************************************/ + +int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + SMB_STRUCT_STAT sbuf; + int outsize = 0; + int mode; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBgetattrE); + + outsize = set_message(outbuf,11,0,True); + + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBgetattrE); + return ERROR_DOS(ERRDOS,ERRbadfid); + } + + /* Do an fstat on this file */ + if(fsp_stat(fsp, &sbuf)) { + END_PROFILE(SMBgetattrE); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + mode = dos_mode(conn,fsp->fsp_name,&sbuf); + + /* + * Convert the times into dos times. Set create + * date to be last modify date as UNIX doesn't save + * this. + */ + + put_dos_date2(outbuf,smb_vwv0,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); + put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime); + put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime); + + if (mode & aDIR) { + SIVAL(outbuf,smb_vwv6,0); + SIVAL(outbuf,smb_vwv8,0); + } else { + uint32 allocation_size = get_allocation_size(fsp, &sbuf); + SIVAL(outbuf,smb_vwv6,(uint32)sbuf.st_size); + SIVAL(outbuf,smb_vwv8,allocation_size); + } + SSVAL(outbuf,smb_vwv10, mode); + + DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum)); + + END_PROFILE(SMBgetattrE); + return(outsize); +} |