diff options
author | Jeremy Allison <jra@samba.org> | 2006-04-10 15:33:04 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:15:57 -0500 |
commit | 22dbd67708f1651a2341d70ce576fac360affccf (patch) | |
tree | 83b1bb601be996716bd4e8d3ae79e94df056df2e | |
parent | 0f985dcb1978afd4b383116f01b3acf65c39ecd7 (diff) | |
download | samba-22dbd67708f1651a2341d70ce576fac360affccf.tar.gz samba-22dbd67708f1651a2341d70ce576fac360affccf.tar.xz samba-22dbd67708f1651a2341d70ce576fac360affccf.zip |
r15018: Merge Volker's ipc/trans2/nttrans changes over
into 3.0. Also merge the new POSIX lock code - this
is not enabled unless -DDEVELOPER is defined.
This doesn't yet map onto underlying system POSIX
locks. Updates vfs to allow lock queries.
Jeremy.
(This used to be commit 08e52ead03304ff04229e1bfe544ff40e2564fc7)
30 files changed, 3220 insertions, 1629 deletions
diff --git a/examples/VFS/Makefile.in b/examples/VFS/Makefile.in index 79873c38576..caf8f030aa4 100644 --- a/examples/VFS/Makefile.in +++ b/examples/VFS/Makefile.in @@ -7,7 +7,7 @@ INSTALLCMD = @INSTALL@ SAMBA_SOURCE = @SAMBA_SOURCE@ SHLIBEXT = @SHLIBEXT@ OBJEXT = @OBJEXT@ -FLAGS = $(CFLAGS) -Iinclude -I$(SAMBA_SOURCE)/include -I$(SAMBA_SOURCE)/popt -I$(SAMBA_SOURCE)/ubiqx -I$(SAMBA_SOURCE)/smbwrapper -I. $(CPPFLAGS) -I$(SAMBA_SOURCE) -fPIC +FLAGS = $(CFLAGS) -Iinclude -I$(SAMBA_SOURCE)/include -I$(SAMBA_SOURCE)/popt -I$(SAMBA_SOURCE)/smbwrapper -I. $(CPPFLAGS) -I$(SAMBA_SOURCE) -fPIC prefix = @prefix@ diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c index e6af475da6e..a02bf3c146b 100644 --- a/examples/VFS/skel_opaque.c +++ b/examples/VFS/skel_opaque.c @@ -226,6 +226,11 @@ static BOOL skel_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int return vfswrap_lock(NULL, fsp, fd, op, offset, count, type); } +static BOOL skel_getlock(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid) +{ + return vfswrap_getlock(NULL, fsp, fd, poffset, pcount, ptype, ppid); +} + static int skel_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath) { return vfswrap_symlink(NULL, conn, oldpath, newpath); @@ -576,6 +581,7 @@ static vfs_op_tuple skel_op_tuples[] = { {SMB_VFS_OP(skel_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(skel_ftruncate), SMB_VFS_OP_FTRUNCATE, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(skel_lock), SMB_VFS_OP_LOCK, SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(skel_getlock), SMB_VFS_OP_GETLOCK, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(skel_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(skel_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(skel_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_OPAQUE}, diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c index 14fa2276e1f..5996b298060 100644 --- a/examples/VFS/skel_transparent.c +++ b/examples/VFS/skel_transparent.c @@ -225,6 +225,11 @@ static BOOL skel_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int return SMB_VFS_NEXT_LOCK(handle, fsp, fd, op, offset, count, type); } +static BOOL skel_getlock(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid) +{ + return SMB_VFS_NEXT_GETLOCK(handle, fsp, fd, poffset, pcount, ptype, ppid); +} + static int skel_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath) { return SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath); @@ -543,6 +548,7 @@ static vfs_op_tuple skel_op_tuples[] = { {SMB_VFS_OP(skel_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(skel_ftruncate), SMB_VFS_OP_FTRUNCATE, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(skel_lock), SMB_VFS_OP_LOCK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(skel_getlock), SMB_VFS_OP_GETLOCK, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(skel_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(skel_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(skel_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT}, diff --git a/source3/include/smb.h b/source3/include/smb.h index 8faf3877ce9..9531ae2903c 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -486,6 +486,36 @@ typedef struct { BOOL is_wild; } name_compare_entry; +struct trans_state { + struct trans_state *next, *prev; + uint16 vuid; + uint16 mid; + + uint32 max_param_return; + uint32 max_data_return; + uint32 max_setup_return; + + uint8 cmd; /* SMBtrans or SMBtrans2 */ + + fstring name; /* for trans requests */ + uint16 call; /* for trans2 and nttrans requests */ + + BOOL close_on_completion; + BOOL one_way; + + unsigned int setup_count; + uint16 *setup; + + size_t received_data; + size_t received_param; + + size_t total_param; + char *param; + + size_t total_data; + char *data; +}; + /* Include VFS stuff */ #include "smb_acls.h" @@ -550,6 +580,7 @@ typedef struct connection_struct { name_compare_entry *veto_oplock_list; /* Per-share list of files to refuse oplocks on. */ name_compare_entry *aio_write_behind_list; /* Per-share list of files to use aio write behind on. */ struct dfree_cached_info *dfree_info; + struct trans_state *pending_trans; } connection_struct; struct current_user { @@ -799,17 +830,29 @@ struct parm_struct { #define FLAG_HIDE 0x2000 /* options that should be hidden in SWAT */ #define FLAG_DOS_STRING 0x4000 /* convert from UNIX to DOS codepage when reading this string. */ -/* passed to br lock code */ -enum brl_type {READ_LOCK, WRITE_LOCK, PENDING_LOCK}; +/* passed to br lock code - the UNLOCK_LOCK should never be stored into the tdb + and is used in calculating POSIX unlock ranges only. */ + +enum brl_type {READ_LOCK, WRITE_LOCK, PENDING_LOCK, UNLOCK_LOCK}; +enum brl_flavour {WINDOWS_LOCK = 0, POSIX_LOCK = 1}; + +struct byte_range_lock { + files_struct *fsp; + unsigned int num_locks; + BOOL modified; + void *lock_data; +}; #define BRLOCK_FN_CAST() \ void (*)(SMB_DEV_T dev, SMB_INO_T ino, struct process_id pid, \ enum brl_type lock_type, \ + enum brl_flavour lock_flav, \ br_off start, br_off size) #define BRLOCK_FN(fn) \ void (*fn)(SMB_DEV_T dev, SMB_INO_T ino, struct process_id pid, \ enum brl_type lock_type, \ + enum brl_flavour lock_flav, \ br_off start, br_off size) struct bitmap { diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index ed6fce9a6d6..f68a1e240f2 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -110,6 +110,8 @@ struct profile_stats { unsigned syscall_ftruncate_time; unsigned syscall_fcntl_lock_count; unsigned syscall_fcntl_lock_time; + unsigned syscall_fcntl_getlock_count; + unsigned syscall_fcntl_getlock_time; unsigned syscall_readlink_count; unsigned syscall_readlink_time; unsigned syscall_symlink_count; diff --git a/source3/include/trans2.h b/source3/include/trans2.h index 1d5dfe3678e..92c5a2e963f 100644 --- a/source3/include/trans2.h +++ b/source3/include/trans2.h @@ -441,7 +441,9 @@ Offset Size Name #define SMB_QUERY_ATTR_FLAGS 0x206 /* chflags, chattr */ #define SMB_SET_ATTR_FLAGS 0x206 #define SMB_QUERY_POSIX_PERMISSION 0x207 +/* Only valid for qfileinfo */ #define SMB_QUERY_POSIX_LOCK 0x208 +/* Only valid for setfileinfo */ #define SMB_SET_POSIX_LOCK 0x208 /* Transact 2 Find First levels */ @@ -576,4 +578,28 @@ number of entries sent will be zero. #define SMB_POSIX_ACL_ENTRY_SIZE 10 #define SMB_POSIX_IGNORE_ACE_ENTRIES 0xFFFF + +/* Definition of SMB_SET_POSIX_LOCK */ +/* + [2 bytes] lock_type - 0 = Read, 1 = Write, 2 = Unlock + [2 bytes] lock_flags - 1 = Wait (only valid for setlock) + [4 bytes] pid = locking context. + [8 bytes] start = unsigned 64 bits. + [8 bytes] length = unsigned 64 bits. +*/ + +#define POSIX_LOCK_TYPE_OFFSET 0 +#define POSIX_LOCK_FLAGS_OFFSET 2 +#define POSIX_LOCK_PID_OFFSET 4 +#define POSIX_LOCK_START_OFFSET 8 +#define POSIX_LOCK_LEN_OFFSET 16 +#define POSIX_LOCK_DATA_SIZE 24 + +#define POSIX_LOCK_FLAG_NOWAIT 0 +#define POSIX_LOCK_FLAG_WAIT 1 + +#define POSIX_LOCK_TYPE_READ 0 +#define POSIX_LOCK_TYPE_WRITE 1 +#define POSIX_LOCK_TYPE_UNLOCK 2 + #endif diff --git a/source3/include/vfs.h b/source3/include/vfs.h index cde2039d1a1..fb99d824125 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -59,9 +59,10 @@ /* Changed to version 12 to add mask and attributes to opendir(). JRA Also include aio calls. JRA. */ /* Changed to version 13 as the internal structure of files_struct has changed. JRA */ -/* Changed to version 14 as the we had to change DIR to SMB_STRUCT_DIR. JRA */ -/* Changed to version 15 as the we added the statvfs call. JRA */ -#define SMB_VFS_INTERFACE_VERSION 15 +/* Changed to version 14 as we had to change DIR to SMB_STRUCT_DIR. JRA */ +/* Changed to version 15 as we added the statvfs call. JRA */ +/* Changed to version 16 as we added the getlock call. JRA */ +#define SMB_VFS_INTERFACE_VERSION 16 /* to bug old modules which are trying to compile with the old functions */ @@ -141,6 +142,7 @@ typedef enum _vfs_op_type { SMB_VFS_OP_UTIME, SMB_VFS_OP_FTRUNCATE, SMB_VFS_OP_LOCK, + SMB_VFS_OP_GETLOCK, SMB_VFS_OP_SYMLINK, SMB_VFS_OP_READLINK, SMB_VFS_OP_LINK, @@ -262,6 +264,7 @@ struct vfs_ops { int (*utime)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, struct utimbuf *times); int (*ftruncate)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_OFF_T offset); BOOL (*lock)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type); + BOOL (*getlock)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid); int (*symlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *oldpath, const char *newpath); int (*readlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, char *buf, size_t bufsiz); int (*link)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *oldpath, const char *newpath); @@ -375,6 +378,7 @@ struct vfs_ops { struct vfs_handle_struct *utime; struct vfs_handle_struct *ftruncate; struct vfs_handle_struct *lock; + struct vfs_handle_struct *getlock; struct vfs_handle_struct *symlink; struct vfs_handle_struct *readlink; struct vfs_handle_struct *link; diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h index 33810c301f9..e08b386a6ac 100644 --- a/source3/include/vfs_macros.h +++ b/source3/include/vfs_macros.h @@ -70,6 +70,7 @@ #define SMB_VFS_UTIME(conn, path, times) ((conn)->vfs.ops.utime((conn)->vfs.handles.utime, (conn), (path), (times))) #define SMB_VFS_FTRUNCATE(fsp, fd, offset) ((fsp)->conn->vfs.ops.ftruncate((fsp)->conn->vfs.handles.ftruncate, (fsp), (fd), (offset))) #define SMB_VFS_LOCK(fsp, fd, op, offset, count, type) ((fsp)->conn->vfs.ops.lock((fsp)->conn->vfs.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type))) +#define SMB_VFS_GETLOCK(fsp, fd, poffset, pcount, ptype, ppid) ((fsp)->conn->vfs.ops.getlock((fsp)->conn->vfs.handles.getlock, (fsp), (fd) ,(poffset), (pcount), (ptype), (ppid))) #define SMB_VFS_SYMLINK(conn, oldpath, newpath) ((conn)->vfs.ops.symlink((conn)->vfs.handles.symlink, (conn), (oldpath), (newpath))) #define SMB_VFS_READLINK(conn, path, buf, bufsiz) ((conn)->vfs.ops.readlink((conn)->vfs.handles.readlink, (conn), (path), (buf), (bufsiz))) #define SMB_VFS_LINK(conn, oldpath, newpath) ((conn)->vfs.ops.link((conn)->vfs.handles.link, (conn), (oldpath), (newpath))) @@ -181,6 +182,7 @@ #define SMB_VFS_OPAQUE_UTIME(conn, path, times) ((conn)->vfs_opaque.ops.utime((conn)->vfs_opaque.handles.utime, (conn), (path), (times))) #define SMB_VFS_OPAQUE_FTRUNCATE(fsp, fd, offset) ((fsp)->conn->vfs_opaque.ops.ftruncate((fsp)->conn->vfs_opaque.handles.ftruncate, (fsp), (fd), (offset))) #define SMB_VFS_OPAQUE_LOCK(fsp, fd, op, offset, count, type) ((fsp)->conn->vfs_opaque.ops.lock((fsp)->conn->vfs_opaque.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type))) +#define SMB_VFS_OPAQUE_GETLOCK(fsp, fd, poffset, pcount, ptype, ppid) ((fsp)->conn->vfs_opaque.ops.getlock((fsp)->conn->vfs_opaque.handles.getlock, (fsp), (fd), (poffset), (pcount), (ptype), (ppid))) #define SMB_VFS_OPAQUE_SYMLINK(conn, oldpath, newpath) ((conn)->vfs_opaque.ops.symlink((conn)->vfs_opaque.handles.symlink, (conn), (oldpath), (newpath))) #define SMB_VFS_OPAQUE_READLINK(conn, path, buf, bufsiz) ((conn)->vfs_opaque.ops.readlink((conn)->vfs_opaque.handles.readlink, (conn), (path), (buf), (bufsiz))) #define SMB_VFS_OPAQUE_LINK(conn, oldpath, newpath) ((conn)->vfs_opaque.ops.link((conn)->vfs_opaque.handles.link, (conn), (oldpath), (newpath))) @@ -293,6 +295,7 @@ #define SMB_VFS_NEXT_UTIME(handle, conn, path, times) ((handle)->vfs_next.ops.utime((handle)->vfs_next.handles.utime, (conn), (path), (times))) #define SMB_VFS_NEXT_FTRUNCATE(handle, fsp, fd, offset) ((handle)->vfs_next.ops.ftruncate((handle)->vfs_next.handles.ftruncate, (fsp), (fd), (offset))) #define SMB_VFS_NEXT_LOCK(handle, fsp, fd, op, offset, count, type) ((handle)->vfs_next.ops.lock((handle)->vfs_next.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type))) +#define SMB_VFS_NEXT_GETLOCK(handle, fsp, fd, poffset, pcount, ptype, ppid) ((handle)->vfs_next.ops.getlock((handle)->vfs_next.handles.getlock, (fsp), (fd), (poffset), (pcount), (ptype), (ppid))) #define SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath) ((handle)->vfs_next.ops.symlink((handle)->vfs_next.handles.symlink, (conn), (oldpath), (newpath))) #define SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz) ((handle)->vfs_next.ops.readlink((handle)->vfs_next.handles.readlink, (conn), (path), (buf), (bufsiz))) #define SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath) ((handle)->vfs_next.ops.link((handle)->vfs_next.handles.link, (conn), (oldpath), (newpath))) diff --git a/source3/lib/util.c b/source3/lib/util.c index bfc5eb2a8da..87f15b8759d 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -1876,6 +1876,7 @@ void free_namearray(name_compare_entry *name_array) /**************************************************************************** Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping is dealt with in posix.c + Returns True if the lock was granted, False otherwise. ****************************************************************************/ BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) @@ -1893,34 +1894,54 @@ BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) ret = sys_fcntl_ptr(fd,op,&lock); - if (ret == -1 && errno != 0) - DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno))); - - /* a lock query - return True if this region is locked, False if not locked. */ - if (op == SMB_F_GETLK) { - if ((ret != -1) && - (lock.l_type != F_UNLCK) && - (lock.l_pid != 0) && - (lock.l_pid != sys_getpid())) { - DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid)); - return(True); - } - - /* it must be not locked or locked by me */ - return(False); - } - - /* a lock set or unset */ if (ret == -1) { DEBUG(3,("fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n", (double)offset,(double)count,op,type,strerror(errno))); - return(False); + return False; } /* everything went OK */ DEBUG(8,("fcntl_lock: Lock call successful\n")); - return(True); + return True; +} + +/**************************************************************************** + Simple routine to query existing file locks. Cruft in NFS and 64->32 bit mapping + is dealt with in posix.c + Returns True if we have information regarding this lock region (and returns + F_UNLCK in *ptype if the region is unlocked). False if the call failed. +****************************************************************************/ + +BOOL fcntl_getlock(int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid) +{ + SMB_STRUCT_FLOCK lock; + int ret; + + DEBUG(8,("fcntl_getlock %d %.0f %.0f %d\n",fd,(double)*poffset,(double)*pcount,*ptype)); + + lock.l_type = *ptype; + lock.l_whence = SEEK_SET; + lock.l_start = *poffset; + lock.l_len = *pcount; + lock.l_pid = 0; + + ret = sys_fcntl_ptr(fd,SMB_F_GETLK,&lock); + + if (ret == -1) { + DEBUG(3,("fcntl_getlock: lock request failed at offset %.0f count %.0f type %d (%s)\n", + (double)*poffset,(double)*pcount,*ptype,strerror(errno))); + return False; + } + + *ptype = lock.l_type; + *poffset = lock.l_start; + *pcount = lock.l_len; + *ppid = lock.l_pid; + + DEBUG(3,("fcntl_getlock: fd %d is returned info %d pid %u\n", + fd, (int)lock.l_type, (unsigned int)lock.l_pid)); + return True; } #undef DBGC_CLASS diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 443f5156653..80deb3a3320 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -816,6 +816,7 @@ BOOL cli_close(struct cli_state *cli, int fnum) send a lock with a specified locktype this is used for testing LOCKING_ANDX_CANCEL_LOCK ****************************************************************************/ + NTSTATUS cli_locktype(struct cli_state *cli, int fnum, uint32 offset, uint32 len, int timeout, unsigned char locktype) { @@ -863,11 +864,11 @@ NTSTATUS cli_locktype(struct cli_state *cli, int fnum, return cli_nt_error(cli); } - /**************************************************************************** Lock a file. note that timeout is in units of 2 milliseconds ****************************************************************************/ + BOOL cli_lock(struct cli_state *cli, int fnum, uint32 offset, uint32 len, int timeout, enum brl_type lock_type) { @@ -1068,6 +1069,108 @@ BOOL cli_unlock64(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_ return True; } +/**************************************************************************** + Get/unlock a POSIX lock on a file - internal function. +****************************************************************************/ + +static BOOL cli_posix_lock_internal(struct cli_state *cli, int fnum, + SMB_BIG_UINT offset, SMB_BIG_UINT len, BOOL wait_lock, enum brl_type lock_type) +{ + unsigned int param_len = 4; + unsigned int data_len = POSIX_LOCK_DATA_SIZE; + uint16 setup = TRANSACT2_SETFILEINFO; + char param[4]; + unsigned char data[POSIX_LOCK_DATA_SIZE]; + char *rparam=NULL, *rdata=NULL; + int saved_timeout = cli->timeout; + + SSVAL(param,0,fnum); + SSVAL(param,2,SMB_SET_POSIX_LOCK); + + switch (lock_type) { + case READ_LOCK: + SSVAL(data, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_READ); + break; + case WRITE_LOCK: + SSVAL(data, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_WRITE); + break; + case UNLOCK_LOCK: + SSVAL(data, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_UNLOCK); + break; + default: + return False; + } + + if (wait_lock) { + SSVAL(data, POSIX_LOCK_FLAGS_OFFSET, POSIX_LOCK_FLAG_WAIT); + cli->timeout = 0x7FFFFFFF; + } else { + SSVAL(data, POSIX_LOCK_FLAGS_OFFSET, POSIX_LOCK_FLAG_NOWAIT); + } + + SIVAL(data, POSIX_LOCK_PID_OFFSET, cli->pid); + SOFF_T(data, POSIX_LOCK_START_OFFSET, offset); + SOFF_T(data, POSIX_LOCK_LEN_OFFSET, len); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + (char *)&data, data_len, cli->max_xmit /* data, length, max */ + )) { + cli->timeout = saved_timeout; + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + cli->timeout = saved_timeout; + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return False; + } + + cli->timeout = saved_timeout; + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + return True; +} + +/**************************************************************************** + POSIX Lock a file. +****************************************************************************/ + +BOOL cli_posix_lock(struct cli_state *cli, int fnum, + SMB_BIG_UINT offset, SMB_BIG_UINT len, + BOOL wait_lock, enum brl_type lock_type) +{ + if (lock_type != READ_LOCK || lock_type != WRITE_LOCK) { + return False; + } + return cli_posix_lock_internal(cli, fnum, offset, len, wait_lock, lock_type); +} + +/**************************************************************************** + POSIX Unlock a file. +****************************************************************************/ + +BOOL cli_posix_unlock(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_UINT len) +{ + return cli_posix_lock_internal(cli, fnum, offset, len, False, UNLOCK_LOCK); +} + +/**************************************************************************** + POSIX Get any lock covering a file. +****************************************************************************/ + +BOOL cli_posix_getlock(struct cli_state *cli, int fnum, SMB_BIG_UINT *poffset, SMB_BIG_UINT *plen) +{ + return True; +} /**************************************************************************** Do a SMBgetattrE call. diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c index 8296f7e94c1..082da67bb8b 100644 --- a/source3/libsmb/clitrans.c +++ b/source3/libsmb/clitrans.c @@ -153,7 +153,6 @@ BOOL cli_send_trans(struct cli_state *cli, int trans, /* Note we're in a trans state. Save the sequence * numbers for replies. */ - cli_signing_trans_start(cli, mid); return(True); } @@ -173,7 +172,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, *data_len = *param_len = 0; if (!cli_receive_smb(cli)) { - cli_signing_trans_stop(cli); return False; } @@ -184,7 +182,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(cli->inbuf,smb_com))); - cli_signing_trans_stop(cli); return(False); } @@ -197,7 +194,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, status = cli_nt_error(cli); if (NT_STATUS_IS_ERR(status) || NT_STATUS_EQUAL(status,STATUS_NO_MORE_FILES)) { - cli_signing_trans_stop(cli); return False; } @@ -210,7 +206,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, *data = SMB_REALLOC(*data,total_data); if (!(*data)) { DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n")); - cli_signing_trans_stop(cli); return False; } } @@ -219,7 +214,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, *param = SMB_REALLOC(*param,total_param); if (!(*param)) { DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n")); - cli_signing_trans_stop(cli); return False; } } @@ -231,7 +225,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, if (this_data + *data_len > total_data || this_param + *param_len > total_param) { DEBUG(1,("Data overflow in cli_receive_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -240,7 +233,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, this_param + *param_len < this_param || this_param + *param_len < *param_len) { DEBUG(1,("Data overflow in cli_receive_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -253,7 +245,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, data_offset_out + this_data < data_offset_out || data_offset_out + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_trans\n")); - cli_signing_trans_stop(cli); return False; } if (data_offset_in > cli->bufsize || @@ -261,7 +252,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, data_offset_in + this_data < data_offset_in || data_offset_in + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -276,7 +266,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, param_offset_out + this_param < param_offset_out || param_offset_out + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_trans\n")); - cli_signing_trans_stop(cli); return False; } if (param_offset_in > cli->bufsize || @@ -284,7 +273,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, param_offset_in + this_param < param_offset_in || param_offset_in + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -297,7 +285,6 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, break; if (!cli_receive_smb(cli)) { - cli_signing_trans_stop(cli); return False; } @@ -308,11 +295,9 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(cli->inbuf,smb_com))); - cli_signing_trans_stop(cli); return(False); } if (NT_STATUS_IS_ERR(cli_nt_error(cli))) { - cli_signing_trans_stop(cli); return(False); } @@ -326,8 +311,7 @@ BOOL cli_receive_trans(struct cli_state *cli,int trans, break; } - - cli_signing_trans_stop(cli); + return(True); } @@ -453,7 +437,6 @@ BOOL cli_send_nt_trans(struct cli_state *cli, /* Note we're in a trans state. Save the sequence * numbers for replies. */ - cli_signing_trans_start(cli, mid); return(True); } @@ -474,7 +457,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, *data_len = *param_len = 0; if (!cli_receive_smb(cli)) { - cli_signing_trans_stop(cli); return False; } @@ -484,7 +466,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", CVAL(cli->inbuf,smb_com))); - cli_signing_trans_stop(cli); return(False); } @@ -496,7 +477,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (cli_is_dos_error(cli)) { cli_dos_error(cli, &eclass, &ecode); if (!(eclass == ERRDOS && ecode == ERRmoredata)) { - cli_signing_trans_stop(cli); return(False); } } @@ -507,7 +487,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (cli_is_nt_error(cli)) { if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_BUFFER_TOO_SMALL)) { - cli_signing_trans_stop(cli); return(False); } } @@ -521,7 +500,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, *data = SMB_REALLOC(*data,total_data); if (!(*data)) { DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data)); - cli_signing_trans_stop(cli); return False; } } @@ -530,7 +508,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, *param = SMB_REALLOC(*param,total_param); if (!(*param)) { DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param)); - cli_signing_trans_stop(cli); return False; } } @@ -542,7 +519,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (this_data + *data_len > total_data || this_param + *param_len > total_param) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -551,7 +527,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, this_param + *param_len < this_param || this_param + *param_len < *param_len) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -564,7 +539,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, data_offset_out + this_data < data_offset_out || data_offset_out + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); - cli_signing_trans_stop(cli); return False; } if (data_offset_in > cli->bufsize || @@ -572,7 +546,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, data_offset_in + this_data < data_offset_in || data_offset_in + this_data < this_data) { DEBUG(1,("Data overflow in cli_receive_nt_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -588,7 +561,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, param_offset_out + this_param < param_offset_out || param_offset_out + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_nt_trans\n")); - cli_signing_trans_stop(cli); return False; } if (param_offset_in > cli->bufsize || @@ -596,7 +568,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, param_offset_in + this_param < param_offset_in || param_offset_in + this_param < this_param) { DEBUG(1,("Param overflow in cli_receive_nt_trans\n")); - cli_signing_trans_stop(cli); return False; } @@ -610,7 +581,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, break; if (!cli_receive_smb(cli)) { - cli_signing_trans_stop(cli); return False; } @@ -620,13 +590,11 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", CVAL(cli->inbuf,smb_com))); - cli_signing_trans_stop(cli); return(False); } if (cli_is_dos_error(cli)) { cli_dos_error(cli, &eclass, &ecode); if(!(eclass == ERRDOS && ecode == ERRmoredata)) { - cli_signing_trans_stop(cli); return(False); } } @@ -636,7 +604,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (cli_is_nt_error(cli)) { if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_BUFFER_TOO_SMALL)) { - cli_signing_trans_stop(cli); return(False); } } @@ -650,7 +617,6 @@ BOOL cli_receive_nt_trans(struct cli_state *cli, if (total_data <= *data_len && total_param <= *param_len) break; } - - cli_signing_trans_stop(cli); + return(True); } diff --git a/source3/libsmb/errormap.c b/source3/libsmb/errormap.c index f6b5af068a5..b3caa0a80ce 100644 --- a/source3/libsmb/errormap.c +++ b/source3/libsmb/errormap.c @@ -407,7 +407,7 @@ static const struct { {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, - {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, ERRunknownlevel, NT_STATUS_INVALID_LEVEL}, {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, @@ -680,7 +680,7 @@ static const struct { {ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, {ERRDOS, 122, NT_STATUS_BUFFER_TOO_SMALL}, {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID}, - {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, ERRunknownlevel, NT_STATUS_INVALID_LEVEL}, {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, diff --git a/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c index 52e4b1d04c5..4ff74ca464c 100644 --- a/source3/libsmb/smb_signing.c +++ b/source3/libsmb/smb_signing.c @@ -28,17 +28,9 @@ struct outstanding_packet_lookup { struct outstanding_packet_lookup *prev, *next; }; -/* Store the data for an ongoing trans/trans2/nttrans operation. */ -struct trans_info_context { - uint16 mid; - uint32 send_seq_num; - uint32 reply_seq_num; -}; - struct smb_basic_signing_context { DATA_BLOB mac_key; uint32 send_seq_num; - struct trans_info_context *trans_info; struct outstanding_packet_lookup *outstanding_packet_list; }; @@ -315,7 +307,6 @@ static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) { unsigned char calc_md5_mac[16]; struct smb_basic_signing_context *data = si->signing_context; - uint32 send_seq_num; if (!si->doing_signing) return; @@ -330,12 +321,8 @@ static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) /* mark the packet as signed - BEFORE we sign it...*/ mark_packet_signed(outbuf); - if (data->trans_info) - send_seq_num = data->trans_info->send_seq_num; - else - send_seq_num = data->send_seq_num; - - simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_num, calc_md5_mac); + simple_packet_signature(data, (const unsigned char *)outbuf, + data->send_seq_num, calc_md5_mac); DEBUG(10, ("client_sign_outgoing_message: sent SMB signature of\n")); dump_data(10, (const char *)calc_md5_mac, 8); @@ -345,13 +332,7 @@ static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) /* cli->outbuf[smb_ss_field+2]=0; Uncomment this to test if the remote server actually verifies signatures...*/ - if (data->trans_info) - return; - - data->send_seq_num++; - store_sequence_for_reply(&data->outstanding_packet_list, - SVAL(outbuf,smb_mid), data->send_seq_num); - data->send_seq_num++; + data->send_seq_num += 2; } /*********************************************************** @@ -362,7 +343,6 @@ static BOOL client_check_incoming_message(char *inbuf, struct smb_sign_info *si, { BOOL good; uint32 reply_seq_number; - uint32 saved_seq; unsigned char calc_md5_mac[16]; unsigned char *server_sent_mac; @@ -376,17 +356,9 @@ static BOOL client_check_incoming_message(char *inbuf, struct smb_sign_info *si, return False; } - if (data->trans_info) { - reply_seq_number = data->trans_info->reply_seq_num; - } else if (!get_sequence_for_reply(&data->outstanding_packet_list, - SVAL(inbuf, smb_mid), &reply_seq_number)) { - DEBUG(1, ("client_check_incoming_message: failed to get sequence number %u for reply.\n", - (unsigned int) SVAL(inbuf, smb_mid) )); - return False; - } - - saved_seq = reply_seq_number; - simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac); + reply_seq_number = data->send_seq_num - 1; + simple_packet_signature(data, (const unsigned char *)inbuf, + reply_seq_number, calc_md5_mac); server_sent_mac = (unsigned char *)&inbuf[smb_ss_field]; good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0); @@ -400,12 +372,11 @@ static BOOL client_check_incoming_message(char *inbuf, struct smb_sign_info *si, #if 1 /* JRATEST */ { int i; - reply_seq_number -= 5; - for (i = 0; i < 10; i++, reply_seq_number++) { - simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac); + for (i = -5; i < 5; i++) { + simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number+i, calc_md5_mac); if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) { DEBUG(0,("client_check_incoming_message: out of seq. seq num %u matches. \ -We were expecting seq %u\n", reply_seq_number, saved_seq )); +We were expecting seq %u\n", reply_seq_number+i, reply_seq_number )); break; } } @@ -416,7 +387,7 @@ We were expecting seq %u\n", reply_seq_number, saved_seq )); DEBUG(10, ("client_check_incoming_message: seq %u: got good SMB signature of\n", (unsigned int)reply_seq_number)); dump_data(10, (const char *)server_sent_mac, 8); } - return signing_good(inbuf, si, good, saved_seq, must_be_ok); + return signing_good(inbuf, si, good, reply_seq_number, must_be_ok); } /*********************************************************** @@ -437,10 +408,6 @@ static void simple_free_signing_context(struct smb_sign_info *si) data_blob_free(&data->mac_key); - if (data->trans_info) { - SAFE_FREE(data->trans_info); - } - SAFE_FREE(si->signing_context); return; @@ -503,65 +470,6 @@ BOOL cli_simple_set_signing(struct cli_state *cli, } /*********************************************************** - Tell client code we are in a multiple trans reply state. - We call this after the last outgoing trans2 packet (which - has incremented the sequence numbers), so we must save the - current mid and sequence number -2. -************************************************************/ - -void cli_signing_trans_start(struct cli_state *cli, uint16 mid) -{ - struct smb_basic_signing_context *data = cli->sign_info.signing_context; - uint32 reply_seq_num; - - if (!cli->sign_info.doing_signing || !data) - return; - - data->trans_info = SMB_XMALLOC_P(struct trans_info_context); - ZERO_STRUCTP(data->trans_info); - - /* This ensures the sequence is pulled off the outstanding packet list */ - if (!get_sequence_for_reply(&data->outstanding_packet_list, - mid, &reply_seq_num)) { - DEBUG(1, ("get_sequence_for_reply failed - did we enter the trans signing state without sending a packet?\n")); - return; - } - - data->trans_info->send_seq_num = reply_seq_num - 1; - data->trans_info->mid = mid; - data->trans_info->reply_seq_num = reply_seq_num; - - DEBUG(10,("cli_signing_trans_start: storing mid = %u, reply_seq_num = %u, send_seq_num = %u \ -data->send_seq_num = %u\n", - (unsigned int)data->trans_info->mid, - (unsigned int)data->trans_info->reply_seq_num, - (unsigned int)data->trans_info->send_seq_num, - (unsigned int)data->send_seq_num )); -} - -/*********************************************************** - Tell client code we are out of a multiple trans reply state. -************************************************************/ - -void cli_signing_trans_stop(struct cli_state *cli) -{ - struct smb_basic_signing_context *data = cli->sign_info.signing_context; - - if (!cli->sign_info.doing_signing || !data) - return; - - DEBUG(10,("cli_signing_trans_stop: freeing mid = %u, reply_seq_num = %u, send_seq_num = %u \ -data->send_seq_num = %u\n", - (unsigned int)data->trans_info->mid, - (unsigned int)data->trans_info->reply_seq_num, - (unsigned int)data->trans_info->send_seq_num, - (unsigned int)data->send_seq_num )); - - SAFE_FREE(data->trans_info); - data->trans_info = NULL; -} - -/*********************************************************** SMB signing - TEMP implementation - calculate a MAC to send. ************************************************************/ @@ -659,8 +567,7 @@ static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) { unsigned char calc_md5_mac[16]; struct smb_basic_signing_context *data = si->signing_context; - uint32 send_seq_number = data->send_seq_num; - BOOL was_deferred_packet = False; + uint32 send_seq_number = data->send_seq_num-1; uint16 mid; if (!si->doing_signing) { @@ -680,13 +587,7 @@ static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) mid = SVAL(outbuf, smb_mid); /* See if this is a reply for a deferred packet. */ - was_deferred_packet = get_sequence_for_reply(&data->outstanding_packet_list, mid, &send_seq_number); - - if (data->trans_info && (data->trans_info->mid == mid)) { - /* This is a reply in a trans stream. Use the sequence - * number associated with the stream mid. */ - send_seq_number = data->trans_info->send_seq_num; - } + get_sequence_for_reply(&data->outstanding_packet_list, mid, &send_seq_number); simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_number, calc_md5_mac); @@ -697,36 +598,6 @@ static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) /* cli->outbuf[smb_ss_field+2]=0; Uncomment this to test if the remote client actually verifies signatures...*/ - - /* Don't mess with the sequence number for a deferred packet. */ - if (was_deferred_packet) { - return; - } - - if (!data->trans_info) { - /* Always increment if not in a trans stream. */ - data->send_seq_num++; - } else if ((data->trans_info->send_seq_num == data->send_seq_num) || (data->trans_info->mid != mid)) { - /* Increment if this is the first reply in a trans stream or a - * packet that doesn't belong to this stream (different mid). */ - data->send_seq_num++; - } -} - -/*********************************************************** - Is an incoming packet an oplock break reply ? -************************************************************/ - -static BOOL is_oplock_break(char *inbuf) -{ - if (CVAL(inbuf,smb_com) != SMBlockingX) - return False; - - if (!(CVAL(inbuf,smb_vwv3) & LOCKING_ANDX_OPLOCK_RELEASE)) - return False; - - DEBUG(10,("is_oplock_break: Packet is oplock break\n")); - return True; } /*********************************************************** @@ -753,23 +624,8 @@ static BOOL srv_check_incoming_message(char *inbuf, struct smb_sign_info *si, BO mid = SVAL(inbuf, smb_mid); - /* Is this part of a trans stream ? */ - if (data->trans_info && (data->trans_info->mid == mid)) { - /* If so we don't increment the sequence. */ - reply_seq_number = data->trans_info->reply_seq_num; - } else { - /* We always increment the sequence number. */ - data->send_seq_num++; - - /* If we get an asynchronous oplock break reply and there - * isn't a reply pending we need to re-sync the sequence - * number. - */ - if (is_oplock_break(inbuf)) { - DEBUG(10,("srv_check_incoming_message: oplock break at seq num %u\n", data->send_seq_num)); - data->send_seq_num++; - } - } + /* We always increment the sequence number. */ + data->send_seq_num += 2; saved_seq = reply_seq_number; simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac); @@ -885,9 +741,8 @@ void srv_defer_sign_response(uint16 mid) * Ensure we only store this mid reply once... */ - if (store_sequence_for_reply(&data->outstanding_packet_list, mid, data->send_seq_num)) { - data->send_seq_num++; - } + store_sequence_for_reply(&data->outstanding_packet_list, mid, + data->send_seq_num-1); } /*********************************************************** @@ -974,63 +829,6 @@ BOOL srv_signing_started(void) return True; } - -/*********************************************************** - Tell server code we are in a multiple trans reply state. -************************************************************/ - -void srv_signing_trans_start(uint16 mid) -{ - struct smb_basic_signing_context *data; - - if (!srv_sign_info.doing_signing) - return; - - data = (struct smb_basic_signing_context *)srv_sign_info.signing_context; - if (!data) - return; - - data->trans_info = SMB_XMALLOC_P(struct trans_info_context); - ZERO_STRUCTP(data->trans_info); - - data->trans_info->reply_seq_num = data->send_seq_num-1; - data->trans_info->mid = mid; - data->trans_info->send_seq_num = data->send_seq_num; - - DEBUG(10,("srv_signing_trans_start: storing mid = %u, reply_seq_num = %u, send_seq_num = %u \ -data->send_seq_num = %u\n", - (unsigned int)mid, - (unsigned int)data->trans_info->reply_seq_num, - (unsigned int)data->trans_info->send_seq_num, - (unsigned int)data->send_seq_num )); -} - -/*********************************************************** - Tell server code we are out of a multiple trans reply state. -************************************************************/ - -void srv_signing_trans_stop(void) -{ - struct smb_basic_signing_context *data; - - if (!srv_sign_info.doing_signing) - return; - - data = (struct smb_basic_signing_context *)srv_sign_info.signing_context; - if (!data || !data->trans_info) - return; - - DEBUG(10,("srv_signing_trans_stop: removing mid = %u, reply_seq_num = %u, send_seq_num = %u \ -data->send_seq_num = %u\n", - (unsigned int)data->trans_info->mid, - (unsigned int)data->trans_info->reply_seq_num, - (unsigned int)data->trans_info->send_seq_num, - (unsigned int)data->send_seq_num )); - - SAFE_FREE(data->trans_info); - data->trans_info = NULL; -} - /*********************************************************** Turn on signing from this packet onwards. ************************************************************/ diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 5078515b3e9..b95bb895cc3 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -52,6 +52,7 @@ struct lock_struct { br_off size; int fnum; enum brl_type lock_type; + enum brl_flavour lock_flav; }; /* The key used in the brlock database. */ @@ -66,6 +67,26 @@ struct lock_key { static TDB_CONTEXT *tdb; /**************************************************************************** + Debug info at level 10 for lock struct. +****************************************************************************/ + +static void print_lock_struct(unsigned int i, struct lock_struct *pls) +{ + DEBUG(10,("[%u]: smbpid = %u, tid = %u, pid = %u, ", + i, + (unsigned int)pls->context.smbpid, + (unsigned int)pls->context.tid, + (unsigned int)procid_to_pid(&pls->context.pid) )); + + DEBUG(10,("start = %.0f, size = %.0f, fnum = %d, %s %s\n", + (double)pls->start, + (double)pls->size, + pls->fnum, + lock_type_name(pls->lock_type), + lock_flav_name(pls->lock_flav) )); +} + +/**************************************************************************** Create a locking key - ensuring zero filled for pad purposes. ****************************************************************************/ @@ -86,8 +107,8 @@ static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode) See if two locking contexts are equal. ****************************************************************************/ -static BOOL brl_same_context(struct lock_context *ctx1, - struct lock_context *ctx2) +static BOOL brl_same_context(const struct lock_context *ctx1, + const struct lock_context *ctx2) { return (procid_equal(&ctx1->pid, &ctx2->pid) && (ctx1->smbpid == ctx2->smbpid) && @@ -98,8 +119,8 @@ static BOOL brl_same_context(struct lock_context *ctx1, See if lck1 and lck2 overlap. ****************************************************************************/ -static BOOL brl_overlap(struct lock_struct *lck1, - struct lock_struct *lck2) +static BOOL brl_overlap(const struct lock_struct *lck1, + const struct lock_struct *lck2) { /* this extra check is not redundent - it copes with locks that go beyond the end of 64 bit file space */ @@ -120,12 +141,14 @@ static BOOL brl_overlap(struct lock_struct *lck1, See if lock2 can be added when lock1 is in place. ****************************************************************************/ -static BOOL brl_conflict(struct lock_struct *lck1, - struct lock_struct *lck2) +static BOOL brl_conflict(const struct lock_struct *lck1, + const struct lock_struct *lck2) { + /* Ignore PENDING locks. */ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) return False; + /* Read locks never conflict. */ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) { return False; } @@ -138,9 +161,42 @@ static BOOL brl_conflict(struct lock_struct *lck1, return brl_overlap(lck1, lck2); } +/**************************************************************************** + See if lock2 can be added when lock1 is in place - when both locks are POSIX + flavour. POSIX locks ignore fnum - they only care about dev/ino which we + know already match. +****************************************************************************/ + +static BOOL brl_conflict_posix(const struct lock_struct *lck1, + const struct lock_struct *lck2) +{ +#if defined(DEVELOPER) + SMB_ASSERT(lck1->lock_flav == POSIX_LOCK); + SMB_ASSERT(lck2->lock_flav == POSIX_LOCK); +#endif + + /* Ignore PENDING locks. */ + if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) + return False; + + /* Read locks never conflict. */ + if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) { + return False; + } + + /* Locks on the same context con't conflict. Ignore fnum. */ + if (brl_same_context(&lck1->context, &lck2->context)) { + return False; + } + + /* One is read, the other write, or the context is different, + do they overlap ? */ + return brl_overlap(lck1, lck2); +} + #if ZERO_ZERO -static BOOL brl_conflict1(struct lock_struct *lck1, - struct lock_struct *lck2) +static BOOL brl_conflict1(const struct lock_struct *lck1, + const struct lock_struct *lck2) { if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) return False; @@ -169,10 +225,11 @@ static BOOL brl_conflict1(struct lock_struct *lck1, /**************************************************************************** Check to see if this lock conflicts, but ignore our own locks on the - same fnum only. + same fnum only. This is the read/write lock check code path. + This is never used in the POSIX lock case. ****************************************************************************/ -static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2) +static BOOL brl_conflict_other(const struct lock_struct *lck1, const struct lock_struct *lck2) { if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) return False; @@ -180,6 +237,12 @@ static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) return False; + /* POSIX flavour locks never conflict here - this is only called + in the read/write path. */ + + if (lck1->lock_flav == POSIX_LOCK && lck2->lock_flav == POSIX_LOCK) + return False; + /* * Incoming WRITE locks conflict with existing READ locks even * if the context is the same. JRA. See LOCKTEST7 in smbtorture. @@ -200,7 +263,7 @@ static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck app depends on this ? ****************************************************************************/ -static NTSTATUS brl_lock_failed(struct lock_struct *lock) +static NTSTATUS brl_lock_failed(const struct lock_struct *lock) { static struct lock_struct last_lock_failure; @@ -222,146 +285,432 @@ static NTSTATUS brl_lock_failed(struct lock_struct *lock) return NT_STATUS_LOCK_NOT_GRANTED; } -#if DONT_DO_THIS - /* doing this traversal could kill solaris machines under high load (tridge) */ - /* delete any dead locks */ - /**************************************************************************** - Delete a record if it is for a dead process, if check_self is true, then - delete any records belonging to this pid also (there shouldn't be any). + Open up the brlock.tdb database. ****************************************************************************/ -static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +void brl_init(int read_only) { - struct lock_struct *locks; - int count, i; - BOOL check_self = *(BOOL *)state; - pid_t mypid = sys_getpid(); - - tdb_chainlock(tdb, kbuf); + if (tdb) { + return; + } + tdb = tdb_open_log(lock_path("brlock.tdb"), + lp_open_files_db_hash_size(), + TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), + read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644 ); + if (!tdb) { + DEBUG(0,("Failed to open byte range locking database %s\n", + lock_path("brlock.tdb"))); + return; + } +} - locks = (struct lock_struct *)dbuf.dptr; +/**************************************************************************** + Close down the brlock.tdb database. +****************************************************************************/ - count = dbuf.dsize / sizeof(*locks); - for (i=0; i<count; i++) { - struct lock_struct *lock = &locks[i]; +void brl_shutdown(int read_only) +{ + if (!tdb) { + return; + } + tdb_close(tdb); +} - /* If check_self is true we want to remove our own records. */ - if (check_self && (mypid == lock->context.pid)) { +#if ZERO_ZERO +/**************************************************************************** + Compare two locks for sorting. +****************************************************************************/ - DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n", - (unsigned int)lock->context.pid )); +static int lock_compare(const struct lock_struct *lck1, + const struct lock_struct *lck2) +{ + if (lck1->start != lck2->start) { + return (lck1->start - lck2->start); + } + if (lck2->size != lck1->size) { + return ((int)lck1->size - (int)lck2->size); + } + return 0; +} +#endif - } else if (process_exists(&lock->context.pid)) { +/**************************************************************************** + Lock a range of bytes - Windows lock semantics. +****************************************************************************/ - DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock->context.pid )); - continue; +static NTSTATUS brl_lock_windows(struct byte_range_lock *br_lck, + const struct lock_struct *plock, + BOOL *my_lock_ctx) +{ + unsigned int i; + files_struct *fsp = br_lck->fsp; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + + for (i=0; i < br_lck->num_locks; i++) { + /* Do any Windows or POSIX locks conflict ? */ + if (brl_conflict(&locks[i], plock)) { + NTSTATUS status = brl_lock_failed(plock);; + /* Did we block ourselves ? */ + if (brl_same_context(&locks[i].context, &plock->context)) { + *my_lock_ctx = True; + } + return status; + } +#if ZERO_ZERO + if (plock->start == 0 && plock->size == 0 && + locks[i].size == 0) { + break; } +#endif + } - DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n", - (unsigned int)lock->context.pid )); + /* We can get the Windows lock, now see if it needs to + be mapped into a lower level POSIX one, and if so can + we get it ? We tell the lower lock layer about the + lock type so it can cope with the difference between + Windows "stacking" locks and POSIX "flat" ones. */ - if (count > 1 && i < count-1) { - memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); + if ((plock->lock_type != PENDING_LOCK) && lp_posix_locking(SNUM(fsp->conn))) { + if (!set_posix_lock(fsp, plock->start, plock->size, plock->lock_type, WINDOWS_LOCK)) { + if (errno == EACCES || errno == EAGAIN) { + return NT_STATUS_FILE_LOCK_CONFLICT; + } else { + return map_nt_error_from_unix(errno); + } } - count--; - i--; } - if (count == 0) { - tdb_delete(tdb, kbuf); - } else if (count < (dbuf.dsize / sizeof(*locks))) { - dbuf.dsize = count * sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + /* no conflicts - add it to the list of locks */ + locks = (struct lock_struct *)SMB_REALLOC(locks, (br_lck->num_locks + 1) * sizeof(*locks)); + if (!locks) { + return NT_STATUS_NO_MEMORY; } - tdb_chainunlock(tdb, kbuf); - return 0; + memcpy(&locks[br_lck->num_locks], plock, sizeof(struct lock_struct)); + br_lck->num_locks += 1; + br_lck->lock_data = (void *)locks; + br_lck->modified = True; + + return NT_STATUS_OK; } -#endif /**************************************************************************** - Open up the brlock.tdb database. + Cope with POSIX range splits and merges. ****************************************************************************/ -void brl_init(int read_only) +static unsigned int brlock_posix_split_merge(struct lock_struct *lck_arr, + const struct lock_struct *ex, + const struct lock_struct *plock, + BOOL *lock_was_added) { - if (tdb) - return; - tdb = tdb_open_log(lock_path("brlock.tdb"), - lp_open_files_db_hash_size(), - TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), - read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644 ); - if (!tdb) { - DEBUG(0,("Failed to open byte range locking database\n")); - return; + BOOL lock_types_differ = (ex->lock_type != plock->lock_type); + + /* We can't merge non-conflicting locks on different context - ignore fnum. */ + + if (!brl_same_context(&ex->context, &plock->context)) { + /* Just copy. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + return 1; } -#if DONT_DO_THIS - /* doing this traversal could kill solaris machines under high load (tridge) */ - /* delete any dead locks */ - if (!read_only) { - BOOL check_self = False; - tdb_traverse(tdb, delete_fn, &check_self); + /* We now know we have the same context. */ + + /* Did we overlap ? */ + +/********************************************* + +---------+ + | ex | + +---------+ + +-------+ + | plock | + +-------+ +OR.... + +---------+ + | ex | + +---------+ +**********************************************/ + + if ( (ex->start >= (plock->start + plock->size)) || + (plock->start >= (ex->start + ex->size))) { + /* No overlap with this lock - copy existing. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + return 1; } -#endif + +/********************************************* + +---------+ + | ex | + +---------+ + +---------------------------+ + | plock | -> replace with plock. + +---------------------------+ +**********************************************/ + + if ( (ex->start >= plock->start) && + (ex->start + ex->size <= plock->start + plock->size) ) { + memcpy(&lck_arr[0], plock, sizeof(struct lock_struct)); + *lock_was_added = True; + return 1; + } + +/********************************************* + +---------------+ + | ex | + +---------------+ + +---------------+ + | plock | + +---------------+ +BECOMES.... + +---------------+-------+ + | plock | ex | - different lock types. + +---------------+-------+ +OR.... + +-----------------------+ + | ex | - same lock type. + +-----------------------+ +**********************************************/ + + if ( (ex->start >= plock->start) && + (ex->start < plock->start + plock->size) && + (ex->start + ex->size > plock->start + plock->size) ) { + + *lock_was_added = True; + + /* If the lock types are the same, we merge, if different, we + add the new lock before the old. */ + + if (lock_types_differ) { + /* Add new. */ + memcpy(&lck_arr[0], plock, sizeof(struct lock_struct)); + memcpy(&lck_arr[1], ex, sizeof(struct lock_struct)); + /* Adjust existing start and size. */ + lck_arr[1].start = plock->start + plock->size; + lck_arr[1].size = (ex->start + ex->size) - (plock->start + plock->size); + return 2; + } else { + /* Merge. */ + memcpy(&lck_arr[0], plock, sizeof(struct lock_struct)); + /* Set new start and size. */ + lck_arr[0].start = plock->start; + lck_arr[0].size = (ex->start + ex->size) - plock->start; + return 1; + } + } + +/********************************************* + +---------------+ + | ex | + +---------------+ + +---------------+ + | plock | + +---------------+ +BECOMES.... + +-------+---------------+ + | ex | plock | - different lock types + +-------+---------------+ + +OR + +-----------------------+ + | ex | - same lock type. + +-----------------------+ + +**********************************************/ + + if ( (ex->start < plock->start) && + (ex->start + ex->size > plock->start) && + (ex->start + ex->size <= plock->start + plock->size) ) { + + *lock_was_added = True; + + /* If the lock types are the same, we merge, if different, we + add the new lock after the old. */ + + if (lock_types_differ) { + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + memcpy(&lck_arr[1], plock, sizeof(struct lock_struct)); + /* Adjust existing size. */ + lck_arr[0].size = plock->start - ex->start; + return 2; + } else { + /* Merge. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + /* Adjust existing size. */ + lck_arr[0].size = (plock->start + plock->size) - ex->start; + return 1; + } + } + +/********************************************* + +---------------------------+ + | ex | + +---------------------------+ + +---------+ + | plock | + +---------+ +BECOMES..... + +-------+---------+---------+ + | ex | plock | ex | - different lock types. + +-------+---------+---------+ +OR + +---------------------------+ + | ex | - same lock type. + +---------------------------+ +**********************************************/ + + if ( (ex->start < plock->start) && (ex->start + ex->size > plock->start + plock->size) ) { + *lock_was_added = True; + + if (lock_types_differ) { + + /* We have to split ex into two locks here. */ + + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + memcpy(&lck_arr[1], plock, sizeof(struct lock_struct)); + memcpy(&lck_arr[2], ex, sizeof(struct lock_struct)); + + /* Adjust first existing size. */ + lck_arr[0].size = plock->start - ex->start; + + /* Adjust second existing start and size. */ + lck_arr[2].start = plock->start + plock->size; + lck_arr[2].size = (ex->start + ex->size) - (plock->start + plock->size); + return 3; + } else { + /* Just eat plock. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + return 1; + } + } + + /* Never get here. */ + smb_panic("brlock_posix_split_merge\n"); + /* Notreached. */ + abort(); } /**************************************************************************** - Close down the brlock.tdb database. + Lock a range of bytes - POSIX lock semantics. + We must cope with range splits and merges. ****************************************************************************/ -void brl_shutdown(int read_only) +static NTSTATUS brl_lock_posix(struct byte_range_lock *br_lck, + const struct lock_struct *plock, + BOOL *my_lock_ctx) { - if (!tdb) - return; + unsigned int i, count; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + struct lock_struct *tp; + files_struct *fsp = br_lck->fsp; + BOOL lock_was_added = False; + + /* No zero-zero locks for POSIX. */ + if (plock->start == 0 && plock->size == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Don't allow 64-bit lock wrap. */ + if (plock->start + plock->size < plock->start || + plock->start + plock->size < plock->size) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* The worst case scenario here is we have to split an + existing POSIX lock range into two, and add our lock, + so we need at most 2 more entries. */ + + tp = SMB_MALLOC_ARRAY(struct lock_struct, (br_lck->num_locks + 2)); + if (!tp) { + return NT_STATUS_NO_MEMORY; + } + + count = 0; + for (i=0; i < br_lck->num_locks; i++) { + if (locks[i].lock_flav == WINDOWS_LOCK) { + /* Do any Windows flavour locks conflict ? */ + if (brl_conflict(&locks[i], plock)) { + /* Did we block ourselves ? */ + if (brl_same_context(&locks[i].context, &plock->context)) { + *my_lock_ctx = True; + } + /* No games with error messages. */ + SAFE_FREE(tp); + return NT_STATUS_FILE_LOCK_CONFLICT; + } + /* Just copy the Windows lock into the new array. */ + memcpy(&tp[count], &locks[i], sizeof(struct lock_struct)); + count++; + } else { + /* POSIX conflict semantics are different. */ + if (brl_conflict_posix(&locks[i], plock)) { + /* Can't block ourselves with POSIX locks. */ + /* No games with error messages. */ + SAFE_FREE(tp); + return NT_STATUS_FILE_LOCK_CONFLICT; + } + + /* Work out overlaps. */ + count += brlock_posix_split_merge(&tp[count], &locks[i], plock, &lock_was_added); + } + } + + /* We can get the POSIX lock, now see if it needs to + be mapped into a lower level POSIX one, and if so can + we get it ? We well the lower lock layer about the + lock type so it can cope with the difference between + Windows "stacking" locks and POSIX "flat" ones. */ + +#if 0 + /* FIXME - this call doesn't work correctly yet for POSIX locks... */ -#if DONT_DO_THIS - /* doing this traversal could kill solaris machines under high load (tridge) */ - /* delete any dead locks */ - if (!read_only) { - BOOL check_self = True; - tdb_traverse(tdb, delete_fn, &check_self); + if ((plock->lock_type != PENDING_LOCK) && lp_posix_locking(SNUM(fsp->conn))) { + + + if (!set_posix_lock(fsp, plock->start, plock->size, plock->lock_type, POSIX_LOCK)) { + if (errno == EACCES || errno == EAGAIN) { + SAFE_FREE(tp); + return NT_STATUS_FILE_LOCK_CONFLICT; + } else { + SAFE_FREE(tp); + return map_nt_error_from_unix(errno); + } + } } #endif - tdb_close(tdb); -} + if (!lock_was_added) { + memcpy(&tp[count], plock, sizeof(struct lock_struct)); + count++; + } -#if ZERO_ZERO -/**************************************************************************** -compare two locks for sorting -****************************************************************************/ -static int lock_compare(struct lock_struct *lck1, - struct lock_struct *lck2) -{ - if (lck1->start != lck2->start) return (lck1->start - lck2->start); - if (lck2->size != lck1->size) { - return ((int)lck1->size - (int)lck2->size); + /* Realloc so we don't leak entries per lock call. */ + tp = (struct lock_struct *)SMB_REALLOC(tp, count * sizeof(*locks)); + if (!tp) { + return NT_STATUS_NO_MEMORY; } - return 0; + br_lck->num_locks = count; + br_lck->lock_data = (void *)tp; + br_lck->modified = True; + return NT_STATUS_OK; } -#endif /**************************************************************************** Lock a range of bytes. ****************************************************************************/ -NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, - uint16 smbpid, struct process_id pid, uint16 tid, - br_off start, br_off size, - enum brl_type lock_type, BOOL *my_lock_ctx) +NTSTATUS brl_lock(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_type lock_type, + enum brl_flavour lock_flav, + BOOL *my_lock_ctx) { - TDB_DATA kbuf, dbuf; - int count, i; - struct lock_struct lock, *locks; - NTSTATUS status = NT_STATUS_OK; + NTSTATUS ret; + struct lock_struct lock; *my_lock_ctx = False; - kbuf = locking_key(dev,ino); - - dbuf.dptr = NULL; #if !ZERO_ZERO if (start == 0 && size == 0) { @@ -369,66 +718,27 @@ NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, } #endif - tdb_chainlock(tdb, kbuf); - dbuf = tdb_fetch(tdb, kbuf); - lock.context.smbpid = smbpid; lock.context.pid = pid; - lock.context.tid = tid; + lock.context.tid = br_lck->fsp->conn->cnum; lock.start = start; lock.size = size; - lock.fnum = fnum; + lock.fnum = br_lck->fsp->fnum; lock.lock_type = lock_type; + lock.lock_flav = lock_flav; - if (dbuf.dptr) { - /* there are existing locks - make sure they don't conflict */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); - for (i=0; i<count; i++) { - if (brl_conflict(&locks[i], &lock)) { - status = brl_lock_failed(&lock);; - /* Did we block ourselves ? */ - if (brl_same_context(&locks[i].context, &lock.context)) - *my_lock_ctx = True; - goto fail; - } -#if ZERO_ZERO - if (lock.start == 0 && lock.size == 0 && - locks[i].size == 0) { - break; - } -#endif - } + if (lock_flav == WINDOWS_LOCK) { + ret = brl_lock_windows(br_lck, &lock, my_lock_ctx); + } else { + ret = brl_lock_posix(br_lck, &lock, my_lock_ctx); } - /* no conflicts - add it to the list of locks */ - dbuf.dptr = SMB_REALLOC(dbuf.dptr, dbuf.dsize + sizeof(*locks)); - if (!dbuf.dptr) { - status = NT_STATUS_NO_MEMORY; - goto fail; - } - memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock)); - dbuf.dsize += sizeof(lock); - #if ZERO_ZERO /* sort the lock list */ - qsort(dbuf.dptr, dbuf.dsize/sizeof(lock), sizeof(lock), lock_compare); + qsort(br_lck->lock_data, (size_t)br_lck->num_locks, sizeof(lock), lock_compare); #endif - if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) { - status = NT_STATUS_INTERNAL_DB_CORRUPTION; - goto fail; - } - - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return NT_STATUS_OK; - - fail: - - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return status; + return ret; } /**************************************************************************** @@ -445,260 +755,532 @@ static BOOL brl_pending_overlap(struct lock_struct *lock, struct lock_struct *pe } /**************************************************************************** - Unlock a range of bytes. + Unlock a range of bytes - Windows semantics. ****************************************************************************/ -BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, - uint16 smbpid, struct process_id pid, uint16 tid, - br_off start, br_off size, - BOOL remove_pending_locks_only, - void (*pre_unlock_fn)(void *), - void *pre_unlock_data) +static BOOL brl_unlock_windows(struct byte_range_lock *br_lck, const struct lock_struct *plock) { - TDB_DATA kbuf, dbuf; - int count, i, j; - struct lock_struct *locks; - struct lock_context context; + unsigned int i, j; + struct lock_struct *lock = NULL; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; - kbuf = locking_key(dev,ino); +#if ZERO_ZERO + for (i = 0; i < br_lck->num_locks; i++) { + lock = &locks[i]; - dbuf.dptr = NULL; + if (lock->lock_type == WRITE_LOCK && + brl_same_context(&lock->context, &plock->context) && + lock->fnum == plock->fnum && + lock->lock_flav == WINDOWS_LOCK && + lock->start == plock->start && + lock->size == plock->size) { - tdb_chainlock(tdb, kbuf); - dbuf = tdb_fetch(tdb, kbuf); + /* found it - delete it */ + if (i < br_lck->num_locks - 1) { + memmove(&locks[i], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + } - if (!dbuf.dptr) { - DEBUG(10,("brl_unlock: tdb_fetch failed !\n")); - goto fail; + br_lck->num_locks -= 1; + br_lck->modified = True; + return True; + } } +#endif - context.smbpid = smbpid; - context.pid = pid; - context.tid = tid; + for (i = 0; i < br_lck->num_locks; i++) { + lock = &locks[i]; - /* there are existing locks - find a match */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); + /* Only remove our own locks that match in start, size, and flavour. */ + if (brl_same_context(&lock->context, &plock->context) && + lock->fnum == plock->fnum && + lock->lock_flav == WINDOWS_LOCK && + lock->start == plock->start && + lock->size == plock->size ) { + break; + } + } -#if ZERO_ZERO - for (i=0; i<count; i++) { - struct lock_struct *lock = &locks[i]; + if (i == br_lck->num_locks) { + /* we didn't find it */ + return False; + } - if (lock->lock_type == WRITE_LOCK && - brl_same_context(&lock->context, &context) && - lock->fnum == fnum && - lock->start == start && - lock->size == size) { + /* Unlock any POSIX regions. */ + if(lp_posix_locking(br_lck->fsp->conn->cnum)) { + release_posix_lock(br_lck->fsp, plock->start, plock->size); + } - if (pre_unlock_fn) - (*pre_unlock_fn)(pre_unlock_data); + /* Send unlock messages to any pending waiters that overlap. */ + for (j=0; j < br_lck->num_locks; j++) { + struct lock_struct *pend_lock = &locks[j]; - /* found it - delete it */ - if (count == 1) { - tdb_delete(tdb, kbuf); - } else { - if (i < count-1) { - memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); - } - dbuf.dsize -= sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); - } + /* Ignore non-pending locks. */ + if (pend_lock->lock_type != PENDING_LOCK) { + continue; + } - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return True; + /* We could send specific lock info here... */ + if (brl_pending_overlap(lock, pend_lock)) { + DEBUG(10,("brl_unlock: sending unlock message to pid %s\n", + procid_str_static(&pend_lock->context.pid ))); + + become_root(); + message_send_pid(pend_lock->context.pid, + MSG_SMB_UNLOCK, + NULL, 0, True); + unbecome_root(); } } -#endif - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); - for (i=0; i<count; i++) { - struct lock_struct *lock = &locks[i]; + /* Actually delete the lock. */ + if (i < br_lck->num_locks - 1) { + memmove(&locks[i], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + } - if (brl_same_context(&lock->context, &context) && - lock->fnum == fnum && - lock->start == start && - lock->size == size) { + br_lck->num_locks -= 1; + br_lck->modified = True; + return True; +} + +/**************************************************************************** + Unlock a range of bytes - POSIX semantics. +****************************************************************************/ - if (remove_pending_locks_only && lock->lock_type != PENDING_LOCK) - continue; +static BOOL brl_unlock_posix(struct byte_range_lock *br_lck, const struct lock_struct *plock) +{ + unsigned int i, j, count; + struct lock_struct *lock = NULL; + struct lock_struct *tp; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + BOOL overlap_found = False; + + /* No zero-zero locks for POSIX. */ + if (plock->start == 0 && plock->size == 0) { + return False; + } - if (lock->lock_type != PENDING_LOCK) { + /* Don't allow 64-bit lock wrap. */ + if (plock->start + plock->size < plock->start || + plock->start + plock->size < plock->size) { + DEBUG(10,("brl_unlock_posix: lock wrap\n")); + return False; + } - /* Do any POSIX unlocks needed. */ - if (pre_unlock_fn) - (*pre_unlock_fn)(pre_unlock_data); + /* The worst case scenario here is we have to split an + existing POSIX lock range into two, so we need at most + 1 more entry. */ - /* Send unlock messages to any pending waiters that overlap. */ - for (j=0; j<count; j++) { - struct lock_struct *pend_lock = &locks[j]; + tp = SMB_MALLOC_ARRAY(struct lock_struct, (br_lck->num_locks + 1)); + if (!tp) { + DEBUG(10,("brl_unlock_posix: malloc fail\n")); + return False; + } - /* Ignore non-pending locks. */ - if (pend_lock->lock_type != PENDING_LOCK) - continue; + count = 0; + for (i = 0; i < br_lck->num_locks; i++) { + struct lock_struct tmp_lock[3]; + BOOL lock_was_added = False; + unsigned int tmp_count; - /* We could send specific lock info here... */ - if (brl_pending_overlap(lock, pend_lock)) { - DEBUG(10,("brl_unlock: sending unlock message to pid %s\n", - procid_str_static(&pend_lock->context.pid ))); + lock = &locks[i]; - message_send_pid(pend_lock->context.pid, - MSG_SMB_UNLOCK, - NULL, 0, True); - } - } - } + /* Only remove our own locks - ignore fnum. */ + if (lock->lock_type == PENDING_LOCK || + !brl_same_context(&lock->context, &plock->context)) { + memcpy(&tp[count], lock, sizeof(struct lock_struct)); + count++; + continue; + } - /* found it - delete it */ - if (count == 1) { - tdb_delete(tdb, kbuf); + /* Work out overlaps. */ + tmp_count = brlock_posix_split_merge(&tmp_lock[0], &locks[i], plock, &lock_was_added); + + if (tmp_count == 1) { + /* Ether the locks didn't overlap, or the unlock completely + overlapped this lock. If it didn't overlap, then there's + no change in the locks. */ + if (tmp_lock[0].lock_type != UNLOCK_LOCK) { + SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type); + /* No change in this lock. */ + memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct)); + count++; } else { - if (i < count-1) { - memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); - } - dbuf.dsize -= sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + SMB_ASSERT(tmp_lock[0].lock_type == UNLOCK_LOCK); + overlap_found = True; } + continue; + } else if (tmp_count == 2) { + /* The unlock overlapped an existing lock. Copy the truncated + lock into the lock array. */ + if (tmp_lock[0].lock_type != UNLOCK_LOCK) { + SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type); + SMB_ASSERT(tmp_lock[1].lock_type == UNLOCK_LOCK); + memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct)); + } else { + SMB_ASSERT(tmp_lock[0].lock_type == UNLOCK_LOCK); + SMB_ASSERT(tmp_lock[1].lock_type == locks[i].lock_type); + memcpy(&tp[count], &tmp_lock[1], sizeof(struct lock_struct)); + } + count++; + overlap_found = True; + continue; + } else { + /* tmp_count == 3 - (we split a lock range in two). */ + SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type); + SMB_ASSERT(tmp_lock[1].lock_type == UNLOCK_LOCK); + SMB_ASSERT(tmp_lock[2].lock_type != locks[i].lock_type); + + memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct)); + count++; + memcpy(&tp[count], &tmp_lock[2], sizeof(struct lock_struct)); + count++; + overlap_found = True; + /* Optimisation... */ + /* We know we're finished here as we can't overlap any + more POSIX locks. Copy the rest of the lock array. */ + if (i < br_lck->num_locks - 1) { + memcpy(&tp[count], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + count += ((br_lck->num_locks-1) - i); + } + break; + } + } - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return True; + if (!overlap_found) { + /* Just ignore - no change. */ + SAFE_FREE(tp); + DEBUG(10,("brl_unlock_posix: No overlap - unlocked.\n")); + return True; + } + +#if 0 + /* FIXME - this call doesn't work correctly yet for POSIX locks... */ + + /* Unlock any POSIX regions. */ + if(lp_posix_locking(br_lck->fsp->conn->cnum)) { + release_posix_lock(br_lck->fsp, plock->start, plock->size); + } +#endif + + /* Realloc so we don't leak entries per unlock call. */ + if (count) { + tp = (struct lock_struct *)SMB_REALLOC(tp, count * sizeof(*locks)); + if (!tp) { + DEBUG(10,("brl_unlock_posix: realloc fail\n")); + return False; } + } else { + /* We deleted the last lock. */ + SAFE_FREE(tp); + tp = NULL; } - /* we didn't find it */ + br_lck->num_locks = count; + br_lck->lock_data = (void *)tp; + br_lck->modified = True; - fail: - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return False; -} + /* Send unlock messages to any pending waiters that overlap. */ + locks = tp; + for (j=0; j < br_lck->num_locks; j++) { + struct lock_struct *pend_lock = &locks[j]; + + /* Ignore non-pending locks. */ + if (pend_lock->lock_type != PENDING_LOCK) { + continue; + } + + /* We could send specific lock info here... */ + if (brl_pending_overlap(lock, pend_lock)) { + DEBUG(10,("brl_unlock: sending unlock message to pid %s\n", + procid_str_static(&pend_lock->context.pid ))); + + become_root(); + message_send_pid(pend_lock->context.pid, + MSG_SMB_UNLOCK, + NULL, 0, True); + unbecome_root(); + } + } + + return True; +} /**************************************************************************** - Test if we could add a lock if we wanted to. + Unlock a range of bytes. ****************************************************************************/ -BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum, - uint16 smbpid, struct process_id pid, uint16 tid, - br_off start, br_off size, - enum brl_type lock_type) +BOOL brl_unlock(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_flavour lock_flav) { - TDB_DATA kbuf, dbuf; - int count, i; - struct lock_struct lock, *locks; + struct lock_struct lock; - kbuf = locking_key(dev,ino); + lock.context.smbpid = smbpid; + lock.context.pid = pid; + lock.context.tid = br_lck->fsp->conn->cnum; + lock.start = start; + lock.size = size; + lock.fnum = br_lck->fsp->fnum; + lock.lock_type = UNLOCK_LOCK; + lock.lock_flav = lock_flav; + + if (lock_flav == WINDOWS_LOCK) { + return brl_unlock_windows(br_lck, &lock); + } else { + return brl_unlock_posix(br_lck, &lock); + } +} - dbuf.dptr = NULL; +/**************************************************************************** + Test if we could add a lock if we wanted to. + Returns True if the region required is currently unlocked, False if locked. +****************************************************************************/ - dbuf = tdb_fetch(tdb, kbuf); +BOOL brl_locktest(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_type lock_type, + enum brl_flavour lock_flav) +{ + BOOL ret = True; + unsigned int i; + struct lock_struct lock; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + files_struct *fsp = br_lck->fsp; lock.context.smbpid = smbpid; lock.context.pid = pid; - lock.context.tid = tid; + lock.context.tid = br_lck->fsp->conn->cnum; lock.start = start; lock.size = size; - lock.fnum = fnum; + lock.fnum = fsp->fnum; lock.lock_type = lock_type; - - if (dbuf.dptr) { - /* there are existing locks - make sure they don't conflict */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); - for (i=0; i<count; i++) { - /* - * Our own locks don't conflict. - */ - if (brl_conflict_other(&locks[i], &lock)) - goto fail; + lock.lock_flav = lock_flav; + + /* Make sure existing locks don't conflict */ + for (i=0; i < br_lck->num_locks; i++) { + /* + * Our own locks don't conflict. + */ + if (brl_conflict_other(&locks[i], &lock)) { + return False; } } + /* + * There is no lock held by an SMB daemon, check to + * see if there is a POSIX lock from a UNIX or NFS process. + * This only conflicts with Windows locks, not POSIX locks. + */ + + if(lp_posix_locking(fsp->conn->cnum) && (lock_flav == WINDOWS_LOCK)) { + ret = is_posix_locked(fsp, &start, &size, &lock_type, WINDOWS_LOCK); + + DEBUG(10,("brl_locktest: posix start=%.0f len=%.0f %s for fnum %d file %s\n", + (double)start, (double)size, ret ? "locked" : "unlocked", + fsp->fnum, fsp->fsp_name )); + + /* We need to return the inverse of is_posix_locked. */ + ret = !ret; + } + /* no conflicts - we could have added it */ - SAFE_FREE(dbuf.dptr); - return True; + return ret; +} - fail: - SAFE_FREE(dbuf.dptr); - return False; +/**************************************************************************** + Query for existing locks. +****************************************************************************/ + +NTSTATUS brl_lockquery(struct byte_range_lock *br_lck, + uint16 *psmbpid, + struct process_id pid, + br_off *pstart, + br_off *psize, + enum brl_type *plock_type, + enum brl_flavour lock_flav) +{ + unsigned int i; + struct lock_struct lock; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + files_struct *fsp = br_lck->fsp; + + lock.context.smbpid = *psmbpid; + lock.context.pid = pid; + lock.context.tid = br_lck->fsp->conn->cnum; + lock.start = *pstart; + lock.size = *psize; + lock.fnum = fsp->fnum; + lock.lock_type = *plock_type; + lock.lock_flav = lock_flav; + + /* Make sure existing locks don't conflict */ + for (i=0; i < br_lck->num_locks; i++) { + struct lock_struct *exlock = &locks[i]; + BOOL conflict = False; + + if (exlock->lock_flav == WINDOWS_LOCK) { + conflict = brl_conflict(exlock, &lock); + } else { + conflict = brl_conflict_posix(exlock, &lock); + } + + if (conflict) { + *psmbpid = exlock->context.smbpid; + *pstart = exlock->start; + *psize = exlock->size; + *plock_type = exlock->lock_type; + return NT_STATUS_LOCK_NOT_GRANTED; + } + } + + /* + * There is no lock held by an SMB daemon, check to + * see if there is a POSIX lock from a UNIX or NFS process. + */ + + if(lp_posix_locking(fsp->conn->cnum)) { + BOOL ret = is_posix_locked(fsp, pstart, psize, plock_type, POSIX_LOCK); + + DEBUG(10,("brl_lockquery: posix start=%.0f len=%.0f %s for fnum %d file %s\n", + (double)*pstart, (double)*psize, ret ? "locked" : "unlocked", + fsp->fnum, fsp->fsp_name )); + + if (ret) { + /* Hmmm. No clue what to set smbpid to - use -1. */ + *psmbpid = 0xFFFF; + return NT_STATUS_LOCK_NOT_GRANTED; + } + } + + return NT_STATUS_OK; } + /**************************************************************************** - Remove any locks associated with a open file. + Remove a particular pending lock. ****************************************************************************/ -void brl_close(SMB_DEV_T dev, SMB_INO_T ino, struct process_id pid, int tid, int fnum) +BOOL brl_remove_pending_lock(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_flavour lock_flav) { - TDB_DATA kbuf, dbuf; - int count, i, j, dcount=0; - struct lock_struct *locks; + unsigned int i; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + struct lock_context context; + + context.smbpid = smbpid; + context.pid = pid; + context.tid = br_lck->fsp->conn->cnum; + + for (i = 0; i < br_lck->num_locks; i++) { + struct lock_struct *lock = &locks[i]; + + /* For pending locks we *always* care about the fnum. */ + if (brl_same_context(&lock->context, &context) && + lock->fnum == br_lck->fsp->fnum && + lock->lock_type == PENDING_LOCK && + lock->lock_flav == lock_flav && + lock->start == start && + lock->size == size) { + break; + } + } - kbuf = locking_key(dev,ino); + if (i == br_lck->num_locks) { + /* Didn't find it. */ + return False; + } - dbuf.dptr = NULL; + if (i < br_lck->num_locks - 1) { + /* Found this particular pending lock - delete it */ + memmove(&locks[i], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + } - tdb_chainlock(tdb, kbuf); - dbuf = tdb_fetch(tdb, kbuf); + br_lck->num_locks -= 1; + br_lck->modified = True; + return True; +} - if (!dbuf.dptr) goto fail; - /* there are existing locks - remove any for this fnum */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); +/**************************************************************************** + Remove any locks associated with a open file. +****************************************************************************/ + +void brl_close_fnum(struct byte_range_lock *br_lck, struct process_id pid) +{ + files_struct *fsp = br_lck->fsp; + uint16 tid = fsp->conn->cnum; + int fnum = fsp->fnum; + unsigned int i, j, dcount=0; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + + /* Remove any existing locks for this fnum (or any fnum if they're POSIX). */ - for (i=0; i<count; i++) { + for (i=0; i < br_lck->num_locks; i++) { struct lock_struct *lock = &locks[i]; + BOOL del_this_lock = False; - if (lock->context.tid == tid && - procid_equal(&lock->context.pid, &pid) && - lock->fnum == fnum) { + if (lock->context.tid == tid && procid_equal(&lock->context.pid, &pid)) { + if ((lock->lock_flav == WINDOWS_LOCK) && (lock->fnum == fnum)) { + del_this_lock = True; + } else if (lock->lock_flav == POSIX_LOCK) { + del_this_lock = True; + } + } + if (del_this_lock) { /* Send unlock messages to any pending waiters that overlap. */ - for (j=0; j<count; j++) { + for (j=0; j < br_lck->num_locks; j++) { struct lock_struct *pend_lock = &locks[j]; /* Ignore our own or non-pending locks. */ - if (pend_lock->lock_type != PENDING_LOCK) + if (pend_lock->lock_type != PENDING_LOCK) { continue; + } + /* Optimisation - don't send to this fnum as we're + closing it. */ if (pend_lock->context.tid == tid && procid_equal(&pend_lock->context.pid, &pid) && - pend_lock->fnum == fnum) + pend_lock->fnum == fnum) { continue; + } /* We could send specific lock info here... */ - if (brl_pending_overlap(lock, pend_lock)) + if (brl_pending_overlap(lock, pend_lock)) { + become_root(); message_send_pid(pend_lock->context.pid, MSG_SMB_UNLOCK, NULL, 0, True); + unbecome_root(); + } } /* found it - delete it */ - if (count > 1 && i < count-1) { + if (br_lck->num_locks > 1 && i < br_lck->num_locks - 1) { memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); + sizeof(*locks)*((br_lck->num_locks-1) - i)); } - count--; + br_lck->num_locks--; + br_lck->modified = True; i--; dcount++; } } - - if (count == 0) { - tdb_delete(tdb, kbuf); - } else if (count < (dbuf.dsize / sizeof(*locks))) { - dbuf.dsize -= dcount * sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); - } - - /* we didn't find it */ - fail: - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); } /**************************************************************************** @@ -718,9 +1300,11 @@ static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *st key = (struct lock_key *)kbuf.dptr; for (i=0;i<dbuf.dsize/sizeof(*locks);i++) { - traverse_callback(key->device, key->inode, + traverse_callback(key->device, + key->inode, locks[i].context.pid, locks[i].lock_type, + locks[i].lock_flav, locks[i].start, locks[i].size); } @@ -733,6 +1317,92 @@ static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *st int brl_forall(BRLOCK_FN(fn)) { - if (!tdb) return 0; + if (!tdb) { + return 0; + } return tdb_traverse(tdb, traverse_fn, (void *)fn); } + +/******************************************************************* + Store a potentially modified set of byte range lock data back into + the database. + Unlock the record. +********************************************************************/ + +static int byte_range_lock_destructor(void *p) +{ + struct byte_range_lock *br_lck = + talloc_get_type_abort(p, struct byte_range_lock); + TDB_DATA key = locking_key(br_lck->fsp->dev, br_lck->fsp->inode); + + if (!br_lck->modified) { + goto done; + } + + if (br_lck->num_locks == 0) { + /* No locks - delete this entry. */ + if (tdb_delete(tdb, key) == -1) { + smb_panic("Could not delete byte range lock entry\n"); + } + } else { + TDB_DATA data; + data.dptr = br_lck->lock_data; + data.dsize = br_lck->num_locks * sizeof(struct lock_struct); + + if (tdb_store(tdb, key, data, TDB_REPLACE) == -1) { + smb_panic("Could not store byte range mode entry\n"); + } + } + + done: + + SAFE_FREE(br_lck->lock_data); + tdb_chainunlock(tdb, key); + return 0; +} + +/******************************************************************* + Fetch a set of byte range lock data from the database. + Leave the record locked. +********************************************************************/ + +struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, + files_struct *fsp) +{ + TDB_DATA key = locking_key(fsp->dev, fsp->inode); + TDB_DATA data; + struct byte_range_lock *br_lck; + + br_lck = TALLOC_P(mem_ctx, struct byte_range_lock); + if (br_lck == NULL) { + return NULL; + } + + br_lck->fsp = fsp; + br_lck->num_locks = 0; + br_lck->modified = False; + + if (tdb_chainlock(tdb, key) != 0) { + DEBUG(3, ("Could not lock byte range lock entry\n")); + TALLOC_FREE(br_lck); + return NULL; + } + + talloc_set_destructor(br_lck, byte_range_lock_destructor); + + data = tdb_fetch(tdb, key); + br_lck->lock_data = (void *)data.dptr; + br_lck->num_locks = data.dsize / sizeof(struct lock_struct); + + if (DEBUGLEVEL >= 10) { + unsigned int i; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + DEBUG(10,("brl_get_locks: %u current locks on dev=%.0f, inode=%.0f\n", + br_lck->num_locks, + (double)fsp->dev, (double)fsp->inode )); + for( i = 0; i < br_lck->num_locks; i++) { + print_lock_struct(i, &locks[i]); + } + } + return br_lck; +} diff --git a/source3/locking/locking.c b/source3/locking/locking.c index 0ecc90c794c..0b3f625d03e 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. Locking functions Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) Jeremy Allison 1992-2000 + Copyright (C) Jeremy Allison 1992-2006 Copyright (C) Volker Lendecke 2005 This program is free software; you can redistribute it and/or modify @@ -33,6 +33,7 @@ rewrtten completely to use new tdb code. Tridge, Dec '99 Added POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000. + Added Unix Extensions POSIX locking support. Jeremy Allison Mar 2006. */ #include "includes.h" @@ -45,120 +46,179 @@ uint16 global_smbpid; static TDB_CONTEXT *tdb; /**************************************************************************** - Debugging aid :-). + Debugging aids :-). ****************************************************************************/ -static const char *lock_type_name(enum brl_type lock_type) +const char *lock_type_name(enum brl_type lock_type) { - return (lock_type == READ_LOCK) ? "READ" : "WRITE"; + switch (lock_type) { + case READ_LOCK: + return "READ"; + case WRITE_LOCK: + return "WRITE"; + case PENDING_LOCK: + return "PENDING"; + default: + return "other"; + } +} + +const char *lock_flav_name(enum brl_flavour lock_flav) +{ + return (lock_flav == WINDOWS_LOCK) ? "WINDOWS_LOCK" : "POSIX_LOCK"; } /**************************************************************************** Utility function called to see if a file region is locked. + Called in the read/write codepath. ****************************************************************************/ -BOOL is_locked(files_struct *fsp,connection_struct *conn, - SMB_BIG_UINT count,SMB_BIG_UINT offset, - enum brl_type lock_type) +BOOL is_locked(files_struct *fsp, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_type lock_type) { - int snum = SNUM(conn); + int snum = SNUM(fsp->conn); int strict_locking = lp_strict_locking(snum); - BOOL ret; + enum brl_flavour lock_flav = lp_posix_cifsu_locktype(); + BOOL ret = True; - if (count == 0) - return(False); + if (count == 0) { + return False; + } - if (!lp_locking(snum) || !strict_locking) - return(False); + if (!lp_locking(snum) || !strict_locking) { + return False; + } if (strict_locking == Auto) { if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && (lock_type == READ_LOCK || lock_type == WRITE_LOCK)) { DEBUG(10,("is_locked: optimisation - exclusive oplock on file %s\n", fsp->fsp_name )); - ret = 0; + ret = False; } else if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (lock_type == READ_LOCK)) { DEBUG(10,("is_locked: optimisation - level II oplock on file %s\n", fsp->fsp_name )); - ret = 0; + ret = False; } else { - ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum, - global_smbpid, procid_self(), conn->cnum, - offset, count, lock_type); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return False; + } + ret = !brl_locktest(br_lck, + global_smbpid, + procid_self(), + offset, + count, + lock_type, + lock_flav); + TALLOC_FREE(br_lck); } } else { - ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum, - global_smbpid, procid_self(), conn->cnum, - offset, count, lock_type); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return False; + } + ret = !brl_locktest(br_lck, + global_smbpid, + procid_self(), + offset, + count, + lock_type, + lock_flav); + TALLOC_FREE(br_lck); } - DEBUG(10,("is_locked: brl start=%.0f len=%.0f %s for file %s\n", + DEBUG(10,("is_locked: flavour = %s brl start=%.0f len=%.0f %s for fnum %d file %s\n", + lock_flav_name(lock_flav), (double)offset, (double)count, ret ? "locked" : "unlocked", - fsp->fsp_name )); + fsp->fnum, fsp->fsp_name )); - /* - * There is no lock held by an SMB daemon, check to - * see if there is a POSIX lock from a UNIX or NFS process. - */ + return ret; +} - if(!ret && lp_posix_locking(snum)) { - ret = is_posix_locked(fsp, offset, count, lock_type); +/**************************************************************************** + Find out if a lock could be granted - return who is blocking us if we can't. +****************************************************************************/ + +NTSTATUS query_lock(files_struct *fsp, + uint16 *psmbpid, + SMB_BIG_UINT *pcount, + SMB_BIG_UINT *poffset, + enum brl_type *plock_type, + enum brl_flavour lock_flav) +{ + struct byte_range_lock *br_lck = NULL; + NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED; - DEBUG(10,("is_locked: posix start=%.0f len=%.0f %s for file %s\n", - (double)offset, (double)count, ret ? "locked" : "unlocked", - fsp->fsp_name )); + if (!OPEN_FSP(fsp) || !fsp->can_lock) { + return NT_STATUS_INVALID_HANDLE; } - return ret; + if (!lp_locking(SNUM(fsp->conn))) { + return NT_STATUS_OK; + } + + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } + + status = brl_lockquery(br_lck, + psmbpid, + procid_self(), + poffset, + pcount, + plock_type, + lock_flav); + + TALLOC_FREE(br_lck); + return status; } /**************************************************************************** Utility function called by locking requests. ****************************************************************************/ -static NTSTATUS do_lock(files_struct *fsp,connection_struct *conn, uint16 lock_pid, - SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx) +NTSTATUS do_lock(files_struct *fsp, + uint16 lock_pid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_type lock_type, + enum brl_flavour lock_flav, + BOOL *my_lock_ctx) { + struct byte_range_lock *br_lck = NULL; NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED; - if (!lp_locking(SNUM(conn))) + if (!OPEN_FSP(fsp) || !fsp->can_lock) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!lp_locking(SNUM(fsp->conn))) { return NT_STATUS_OK; + } /* NOTE! 0 byte long ranges ARE allowed and should be stored */ - DEBUG(10,("do_lock: lock type %s start=%.0f len=%.0f requested for file %s\n", - lock_type_name(lock_type), (double)offset, (double)count, fsp->fsp_name )); - - if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) { - status = brl_lock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, - offset, count, - lock_type, my_lock_ctx); - - if (NT_STATUS_IS_OK(status) && lp_posix_locking(SNUM(conn))) { - - /* - * Try and get a POSIX lock on this range. - * Note that this is ok if it is a read lock - * overlapping on a different fd. JRA. - */ - - if (!set_posix_lock(fsp, offset, count, lock_type)) { - if (errno == EACCES || errno == EAGAIN) - status = NT_STATUS_FILE_LOCK_CONFLICT; - else - status = map_nt_error_from_unix(errno); - - /* - * We failed to map - we must now remove the brl - * lock entry. - */ - (void)brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, - offset, count, False, - NULL, NULL); - } - } + DEBUG(10,("do_lock: lock flavour %s lock type %s start=%.0f len=%.0f requested for fnum %d file %s\n", + lock_flav_name(lock_flav), lock_type_name(lock_type), + (double)offset, (double)count, fsp->fnum, fsp->fsp_name )); + + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; } + status = brl_lock(br_lck, + lock_pid, + procid_self(), + offset, + count, + lock_type, + lock_flav, + my_lock_ctx); + + TALLOC_FREE(br_lck); return status; } @@ -169,20 +229,33 @@ static NTSTATUS do_lock(files_struct *fsp,connection_struct *conn, uint16 lock_p it, we need this. JRA. ****************************************************************************/ -NTSTATUS do_lock_spin(files_struct *fsp,connection_struct *conn, uint16 lock_pid, - SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx) +NTSTATUS do_lock_spin(files_struct *fsp, + uint16 lock_pid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_type lock_type, + enum brl_flavour lock_flav, + BOOL *my_lock_ctx) { int j, maxj = lp_lock_spin_count(); int sleeptime = lp_lock_sleep_time(); NTSTATUS status, ret; - if (maxj <= 0) + if (maxj <= 0) { maxj = 1; + } ret = NT_STATUS_OK; /* to keep dumb compilers happy */ for (j = 0; j < maxj; j++) { - status = do_lock(fsp, conn, lock_pid, count, offset, lock_type, my_lock_ctx); + status = do_lock(fsp, + lock_pid, + count, + offset, + lock_type, + lock_flav, + my_lock_ctx); + if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED) && !NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) { return status; @@ -191,72 +264,66 @@ NTSTATUS do_lock_spin(files_struct *fsp,connection_struct *conn, uint16 lock_pid if (j == 0) { ret = status; /* Don't spin if we blocked ourselves. */ - if (*my_lock_ctx) + if (*my_lock_ctx) { return ret; + } + + /* Only spin for Windows locks. */ + if (lock_flav == POSIX_LOCK) { + return ret; + } } - if (sleeptime) + + if (sleeptime) { sys_usleep(sleeptime); + } } return ret; } -/* Struct passed to brl_unlock. */ -struct posix_unlock_data_struct { - files_struct *fsp; - SMB_BIG_UINT offset; - SMB_BIG_UINT count; -}; - -/**************************************************************************** - Function passed to brl_unlock to allow POSIX unlock to be done first. -****************************************************************************/ - -static void posix_unlock(void *pre_data) -{ - struct posix_unlock_data_struct *pdata = (struct posix_unlock_data_struct *)pre_data; - - if (lp_posix_locking(SNUM(pdata->fsp->conn))) - release_posix_lock(pdata->fsp, pdata->offset, pdata->count); -} - /**************************************************************************** Utility function called by unlocking requests. ****************************************************************************/ -NTSTATUS do_unlock(files_struct *fsp,connection_struct *conn, uint16 lock_pid, - SMB_BIG_UINT count,SMB_BIG_UINT offset) +NTSTATUS do_unlock(files_struct *fsp, + uint16 lock_pid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_flavour lock_flav) { BOOL ok = False; - struct posix_unlock_data_struct posix_data; + struct byte_range_lock *br_lck = NULL; - if (!lp_locking(SNUM(conn))) + if (!lp_locking(SNUM(fsp->conn))) { return NT_STATUS_OK; + } - if (!OPEN_FSP(fsp) || !fsp->can_lock || (fsp->conn != conn)) { + if (!OPEN_FSP(fsp) || !fsp->can_lock) { return NT_STATUS_INVALID_HANDLE; } - DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n", - (double)offset, (double)count, fsp->fsp_name )); + DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for fnum %d file %s\n", + (double)offset, (double)count, fsp->fnum, fsp->fsp_name )); - /* - * Remove the existing lock record from the tdb lockdb - * before looking at POSIX locks. If this record doesn't - * match then don't bother looking to remove POSIX locks. - */ - - posix_data.fsp = fsp; - posix_data.offset = offset; - posix_data.count = count; + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } - ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, offset, count, - False, posix_unlock, (void *)&posix_data); + ok = brl_unlock(br_lck, + lock_pid, + procid_self(), + offset, + count, + lock_flav); + TALLOC_FREE(br_lck); + if (!ok) { DEBUG(10,("do_unlock: returning ERRlock.\n" )); return NT_STATUS_RANGE_NOT_LOCKED; } + return NT_STATUS_OK; } @@ -266,6 +333,7 @@ NTSTATUS do_unlock(files_struct *fsp,connection_struct *conn, uint16 lock_pid, void locking_close_file(files_struct *fsp) { + struct byte_range_lock *br_lck; struct process_id pid = procid_self(); if (!lp_locking(SNUM(fsp->conn))) @@ -275,13 +343,14 @@ void locking_close_file(files_struct *fsp) * Just release all the brl locks, no need to release individually. */ - brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum); + br_lck = brl_get_locks(NULL,fsp); + if (br_lck) { + brl_close_fnum(br_lck, pid); + TALLOC_FREE(br_lck); + } if(lp_posix_locking(SNUM(fsp->conn))) { - - /* - * Release all the POSIX locks. - */ + /* Release all the POSIX locks.*/ posix_locking_close_file(fsp); } diff --git a/source3/locking/posix.c b/source3/locking/posix.c index 07246474f3f..e7075c57a64 100644 --- a/source3/locking/posix.c +++ b/source3/locking/posix.c @@ -644,8 +644,7 @@ static BOOL posix_lock_in_range(SMB_OFF_T *offset_out, SMB_OFF_T *count_out, /**************************************************************************** Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and - broken NFS implementations. Returns True if we got the lock or the region - is unlocked in the F_GETLK case, False otherwise. + broken NFS implementations. ****************************************************************************/ static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) @@ -654,9 +653,6 @@ static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OF DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fh->fd,op,(double)offset,(double)count,type)); - /* In the F_GETLK case this returns True if the region - was locked, False if unlocked. */ - ret = SMB_VFS_LOCK(fsp,fsp->fh->fd,op,offset,count,type); if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno == EINVAL))) { @@ -686,39 +682,97 @@ static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OF } DEBUG(8,("posix_fcntl_lock: Lock call %s\n", ret ? "successful" : "failed")); + return ret; +} + +/**************************************************************************** + Actual function that gets POSIX locks. Copes with 64 -> 32 bit cruft and + broken NFS implementations. +****************************************************************************/ +static BOOL posix_fcntl_getlock(files_struct *fsp, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype) +{ + pid_t pid; + BOOL ret; + + DEBUG(8,("posix_fcntl_getlock %d %.0f %.0f %d\n", + fsp->fh->fd,(double)*poffset,(double)*pcount,*ptype)); + + ret = SMB_VFS_GETLOCK(fsp,fsp->fh->fd,poffset,pcount,ptype,&pid); + + if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno == EINVAL))) { + + DEBUG(0,("posix_fcntl_getlock: WARNING: lock request at offset %.0f, length %.0f returned\n", + (double)*poffset,(double)*pcount)); + DEBUG(0,("an %s error. This can happen when using 64 bit lock offsets\n", strerror(errno))); + DEBUG(0,("on 32 bit NFS mounted file systems.\n")); + + /* + * If the offset is > 0x7FFFFFFF then this will cause problems on + * 32 bit NFS mounted filesystems. Just ignore it. + */ + + if (*poffset & ~((SMB_OFF_T)0x7fffffff)) { + DEBUG(0,("Offset greater than 31 bits. Returning success.\n")); + return True; + } + + if (*pcount & ~((SMB_OFF_T)0x7fffffff)) { + /* 32 bit NFS file system, retry with smaller offset */ + DEBUG(0,("Count greater than 31 bits - retrying with 31 bit truncated length.\n")); + errno = 0; + *pcount &= 0x7fffffff; + ret = SMB_VFS_GETLOCK(fsp,fsp->fh->fd,poffset,pcount,ptype,&pid); + } + } + + DEBUG(8,("posix_fcntl_getlock: Lock query call %s\n", ret ? "successful" : "failed")); return ret; } + /**************************************************************************** POSIX function to see if a file region is locked. Returns True if the region is locked, False otherwise. ****************************************************************************/ -BOOL is_posix_locked(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type) +BOOL is_posix_locked(files_struct *fsp, + SMB_BIG_UINT *pu_offset, + SMB_BIG_UINT *pu_count, + enum brl_type *plock_type, + enum brl_flavour lock_flav) { SMB_OFF_T offset; SMB_OFF_T count; - int posix_lock_type = map_posix_lock_type(fsp,lock_type); + int posix_lock_type = map_posix_lock_type(fsp,*plock_type); DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n", - fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) )); + fsp->fsp_name, (double)*pu_offset, (double)*pu_count, posix_lock_type_name(*plock_type) )); /* * If the requested lock won't fit in the POSIX range, we will * never set it, so presume it is not locked. */ - if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) + if(!posix_lock_in_range(&offset, &count, *pu_offset, *pu_count)) { return False; + } - /* - * Note that most UNIX's can *test* for a write lock on - * a read-only fd, just not *set* a write lock on a read-only - * fd. So we don't need to use map_lock_type here. - */ + if (!posix_fcntl_getlock(fsp,&offset,&count,&posix_lock_type)) { + return False; + } - return posix_fcntl_lock(fsp,SMB_F_GETLK,offset,count,posix_lock_type); + if (posix_lock_type == F_UNLCK) { + return False; + } + + if (lock_flav == POSIX_LOCK) { + /* Only POSIX lock queries need to know the details. */ + *pu_offset = (SMB_BIG_UINT)offset; + *pu_count = (SMB_BIG_UINT)count; + *plock_type = (posix_lock_type == F_RDLCK) ? READ_LOCK : WRITE_LOCK; + } + return True; } /* @@ -958,9 +1012,14 @@ lock: start = %.0f, size = %.0f\n", (double)l_curr->start, (double)l_curr->size, /**************************************************************************** POSIX function to acquire a lock. Returns True if the lock could be granted, False if not. + TODO -- Fix POSIX lock flavour semantics. ****************************************************************************/ -BOOL set_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type) +BOOL set_posix_lock(files_struct *fsp, + SMB_BIG_UINT u_offset, + SMB_BIG_UINT u_count, + enum brl_type lock_type, + enum brl_flavour lock_flav) { SMB_OFF_T offset; SMB_OFF_T count; diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c index 0ae48f48186..309f6d15ae7 100644 --- a/source3/modules/vfs_full_audit.c +++ b/source3/modules/vfs_full_audit.c @@ -161,6 +161,8 @@ static int smb_full_audit_ftruncate(vfs_handle_struct *handle, files_struct *fsp int fd, SMB_OFF_T len); static BOOL smb_full_audit_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type); +static BOOL smb_full_audit_getlock(vfs_handle_struct *handle, files_struct *fsp, int fd, + SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid); static int smb_full_audit_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath); static int smb_full_audit_readlink(vfs_handle_struct *handle, connection_struct *conn, @@ -399,6 +401,8 @@ static vfs_op_tuple audit_op_tuples[] = { SMB_VFS_LAYER_LOGGER}, {SMB_VFS_OP(smb_full_audit_lock), SMB_VFS_OP_LOCK, SMB_VFS_LAYER_LOGGER}, + {SMB_VFS_OP(smb_full_audit_getlock), SMB_VFS_OP_GETLOCK, + SMB_VFS_LAYER_LOGGER}, {SMB_VFS_OP(smb_full_audit_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_LOGGER}, {SMB_VFS_OP(smb_full_audit_readlink), SMB_VFS_OP_READLINK, @@ -564,6 +568,7 @@ static struct { { SMB_VFS_OP_UTIME, "utime" }, { SMB_VFS_OP_FTRUNCATE, "ftruncate" }, { SMB_VFS_OP_LOCK, "lock" }, + { SMB_VFS_OP_GETLOCK, "getlock" }, { SMB_VFS_OP_SYMLINK, "symlink" }, { SMB_VFS_OP_READLINK, "readlink" }, { SMB_VFS_OP_LINK, "link" }, @@ -1313,6 +1318,18 @@ static BOOL smb_full_audit_lock(vfs_handle_struct *handle, files_struct *fsp, in return result; } +static BOOL smb_full_audit_getlock(vfs_handle_struct *handle, files_struct *fsp, int fd, + SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid) +{ + BOOL result; + + result = SMB_VFS_NEXT_GETLOCK(handle, fsp, fd, poffset, pcount, ptype, ppid); + + do_log(SMB_VFS_OP_GETLOCK, (result >= 0), handle, "%s", fsp->fsp_name); + + return result; +} + static int smb_full_audit_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath) { diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index e0c6c0686f1..af4293a11e8 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -5444,3 +5444,22 @@ void lp_set_posix_pathnames(void) { posix_pathnames = True; } + +/******************************************************************* + Global state for POSIX lock processing - CIFS unix extensions. +********************************************************************/ + +static enum brl_flavour posix_cifsx_locktype; /* By default 0 == WINDOWS_LOCK */ + +enum brl_flavour lp_posix_cifsu_locktype(void) +{ + return posix_cifsx_locktype; +} + +/******************************************************************* +********************************************************************/ + +void lp_set_posix_cifsx_locktype(enum brl_flavour val) +{ + posix_cifsx_locktype = val; +} diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 805e45f6ea3..6b47d0466bd 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -35,6 +35,8 @@ typedef struct _blocking_lock_record { SMB_BIG_UINT offset; SMB_BIG_UINT count; uint16 lock_pid; + enum brl_flavour lock_flav; + enum brl_type lock_type; char *inbuf; int length; } blocking_lock_record; @@ -53,25 +55,6 @@ static void free_blocking_lock_record(blocking_lock_record *blr) } /**************************************************************************** - Get the files_struct given a particular queued SMB. -*****************************************************************************/ - -static files_struct *get_fsp_from_pkt(char *inbuf) -{ - switch(CVAL(inbuf,smb_com)) { - case SMBlock: - case SMBlockread: - return file_fsp(inbuf,smb_vwv0); - case SMBlockingX: - return file_fsp(inbuf,smb_vwv2); - default: - DEBUG(0,("get_fsp_from_pkt: PANIC - unknown type on blocking lock queue - exiting.!\n")); - exit_server("PANIC - unknown type on blocking lock queue"); - } - return NULL; /* Keep compiler happy. */ -} - -/**************************************************************************** Determine if this is a secondary element of a chained SMB. **************************************************************************/ @@ -87,12 +70,19 @@ static void received_unlock_msg(int msg_type, struct process_id src, Function to push a blocking lock request onto the lock queue. ****************************************************************************/ -BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, - int lock_num, uint16 lock_pid, SMB_BIG_UINT offset, SMB_BIG_UINT count) +BOOL push_blocking_lock_request( char *inbuf, int length, + files_struct *fsp, + int lock_timeout, + int lock_num, + uint16 lock_pid, + enum brl_type lock_type, + enum brl_flavour lock_flav, + SMB_BIG_UINT offset, SMB_BIG_UINT count) { static BOOL set_lock_msg; blocking_lock_record *blr, *tmp; BOOL my_lock_ctx = False; + struct byte_range_lock *br_lck = NULL; NTSTATUS status; if(in_chained_smb() ) { @@ -110,6 +100,9 @@ BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, return False; } + blr->next = NULL; + blr->prev = NULL; + if((blr->inbuf = (char *)SMB_MALLOC(length)) == NULL) { DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" )); SAFE_FREE(blr); @@ -117,19 +110,33 @@ BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, } blr->com_type = CVAL(inbuf,smb_com); - blr->fsp = get_fsp_from_pkt(inbuf); + blr->fsp = fsp; blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout; blr->lock_num = lock_num; blr->lock_pid = lock_pid; + blr->lock_flav = lock_flav; + blr->lock_type = lock_type; blr->offset = offset; blr->count = count; memcpy(blr->inbuf, inbuf, length); blr->length = length; + br_lck = brl_get_locks(NULL, blr->fsp); + if (!br_lck) { + free_blocking_lock_record(blr); + return False; + } + /* Add a pending lock record for this. */ - status = brl_lock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - lock_pid, procid_self(), blr->fsp->conn->cnum, - offset, count, PENDING_LOCK, &my_lock_ctx); + status = brl_lock(br_lck, + lock_pid, + procid_self(), + offset, + count, + PENDING_LOCK, + blr->lock_flav, + &my_lock_ctx); + TALLOC_FREE(br_lck); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n")); @@ -227,7 +234,6 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) { char *inbuf = blr->inbuf; files_struct *fsp = blr->fsp; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); uint16 num_ulocks = SVAL(inbuf,smb_vwv6); SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT) 0; uint16 lock_pid; @@ -261,7 +267,11 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) * request would never have been queued. JRA. */ - do_unlock(fsp,conn,lock_pid,count,offset); + do_unlock(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); } generic_blocking_lock_error(blr, status); @@ -274,19 +284,41 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status) { switch(blr->com_type) { +#if 0 + /* We no longer push blocking lock requests for anything but lockingX and trans2. */ case SMBlock: case SMBlockread: generic_blocking_lock_error(blr, status); break; +#endif case SMBlockingX: reply_lockingX_error(blr, status); break; + case SMBtrans2: + case SMBtranss2: + { + char *outbuf = get_OutBuffer(); + char *inbuf = blr->inbuf; + construct_reply_common(inbuf, outbuf); + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBtranss2 which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBtrans2); + ERROR_NT(status); + if (!send_smb(smbd_server_fd(),outbuf)) { + exit_server("blocking_lock_reply_error: send_smb failed."); + } + break; + } default: DEBUG(0,("blocking_lock_reply_error: PANIC - unknown type on blocking lock queue - exiting.!\n")); exit_server("PANIC - unknown type on blocking lock queue"); } } +#if 0 +/* We no longer push blocking lock requests for anything but lockingX and trans2. */ + /**************************************************************************** Attempt to finish off getting all pending blocking locks for a lockread call. Returns True if we want to be removed from the list. @@ -302,7 +334,6 @@ static BOOL process_lockread(blocking_lock_record *blr) SMB_BIG_UINT startpos; size_t numtoread; NTSTATUS status; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); files_struct *fsp = blr->fsp; BOOL my_lock_ctx = False; @@ -312,7 +343,14 @@ static BOOL process_lockread(blocking_lock_record *blr) numtoread = MIN(BUFFER_SIZE-outsize,numtoread); data = smb_buf(outbuf) + 3; - status = do_lock_spin( fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtoread, startpos, READ_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, + startpos, + READ_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); + if (NT_STATUS_V(status)) { if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { @@ -371,7 +409,6 @@ static BOOL process_lock(blocking_lock_record *blr) int outsize; SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; NTSTATUS status; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); files_struct *fsp = blr->fsp; BOOL my_lock_ctx = False; @@ -379,7 +416,14 @@ static BOOL process_lock(blocking_lock_record *blr) offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); errno = 0; - status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + count, + offset, + WRITE_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); + if (NT_STATUS_IS_ERR(status)) { if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { @@ -412,6 +456,7 @@ static BOOL process_lock(blocking_lock_record *blr) send_blocking_reply(outbuf,outsize); return True; } +#endif /**************************************************************************** Attempt to finish off getting all pending blocking locks for a lockingX call. @@ -423,7 +468,6 @@ static BOOL process_lockingX(blocking_lock_record *blr) char *inbuf = blr->inbuf; unsigned char locktype = CVAL(inbuf,smb_vwv3); files_struct *fsp = blr->fsp; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); uint16 num_ulocks = SVAL(inbuf,smb_vwv6); uint16 num_locks = SVAL(inbuf,smb_vwv7); SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; @@ -452,9 +496,17 @@ static BOOL process_lockingX(blocking_lock_record *blr) * request would never have been queued. JRA. */ errno = 0; - status = do_lock_spin(fsp,conn,lock_pid,count,offset, - ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx); - if (NT_STATUS_IS_ERR(status)) break; + status = do_lock_spin(fsp, + lock_pid, + count, + offset, + ((locktype & 1) ? READ_LOCK : WRITE_LOCK), + WINDOWS_LOCK, + &my_lock_ctx); + + if (NT_STATUS_IS_ERR(status)) { + break; + } } if(blr->lock_num == num_locks) { @@ -491,6 +543,51 @@ Waiting....\n", } /**************************************************************************** + Attempt to get the posix lock request from a SMBtrans2 call. + Returns True if we want to be removed from the list. +*****************************************************************************/ + +static BOOL process_trans2(blocking_lock_record *blr) +{ + extern int max_send; + char *inbuf = blr->inbuf; + char *outbuf; + BOOL my_lock_ctx = False; + char params[2]; + NTSTATUS status; + + status = do_lock(blr->fsp, + blr->lock_pid, + blr->count, + blr->offset, + blr->lock_type, + blr->lock_flav, + &my_lock_ctx); + + if (!NT_STATUS_IS_OK(status)) { + if (ERROR_WAS_LOCK_DENIED(status)) { + /* Still can't get the lock, just keep waiting. */ + return False; + } + /* + * We have other than a "can't get lock" + * error. Send an error and return True so we get dequeued. + */ + blocking_lock_reply_error(blr, status); + return True; + } + + /* We finally got the lock, return success. */ + outbuf = get_OutBuffer(); + construct_reply_common(inbuf, outbuf); + SCVAL(outbuf,smb_com,SMBtrans2); + SSVAL(params,0,0); + send_trans2_replies(outbuf, max_send, params, 2, NULL, 0); + return True; +} + + +/**************************************************************************** Process a blocking lock SMB. Returns True if we want to be removed from the list. *****************************************************************************/ @@ -498,12 +595,18 @@ Waiting....\n", static BOOL blocking_lock_record_process(blocking_lock_record *blr) { switch(blr->com_type) { +#if 0 + /* We no longer push blocking lock requests for anything but lockingX and trans2. */ case SMBlock: return process_lock(blr); case SMBlockread: return process_lockread(blr); +#endif case SMBlockingX: return process_lockingX(blr); + case SMBtrans2: + case SMBtranss2: + return process_trans2(blr); default: DEBUG(0,("blocking_lock_record_process: PANIC - unknown type on blocking lock queue - exiting.!\n")); exit_server("PANIC - unknown type on blocking lock queue"); @@ -522,13 +625,21 @@ void remove_pending_lock_requests_by_fid(files_struct *fsp) for(blr = blocking_lock_queue; blr; blr = next) { next = blr->next; if(blr->fsp->fnum == fsp->fnum) { + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); - DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \ + if (br_lck) { + DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); - brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - blr->lock_pid, procid_self(), blr->fsp->conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + + } free_blocking_lock_record(blr); } @@ -547,14 +658,22 @@ void remove_pending_lock_requests_by_mid(int mid) next = blr->next; if(SVAL(blr->inbuf,smb_mid) == mid) { files_struct *fsp = blr->fsp; + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); - DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ + if (br_lck) { + DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } + blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); - brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - blr->lock_pid, procid_self(), blr->fsp->conn->cnum, - blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); } } @@ -635,16 +754,25 @@ void process_blocking_lock_queue(time_t t) fsp->fnum, fsp->fsp_name )); if((blr->expire_time != -1) && (blr->expire_time <= t)) { + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + /* * Lock expired - throw away all previously * obtained locks and return lock error. */ - DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n", - fsp->fnum, fsp->fsp_name )); - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + if (br_lck) { + DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n", + fsp->fnum, fsp->fsp_name )); + + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); free_blocking_lock_record(blr); @@ -652,32 +780,48 @@ void process_blocking_lock_queue(time_t t) } if(!change_to_user(conn,vuid)) { - DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n", - vuid )); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + /* * Remove the entry and return an error to the client. */ - blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + if (br_lck) { + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } + DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n", + vuid )); + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); free_blocking_lock_record(blr); continue; } if(!set_current_service(conn,SVAL(blr->inbuf,smb_flg),True)) { - DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + /* * Remove the entry and return an error to the client. */ - blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + if (br_lck) { + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } + DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); free_blocking_lock_record(blr); change_to_root_user(); continue; @@ -690,10 +834,17 @@ void process_blocking_lock_queue(time_t t) */ if(blocking_lock_record_process(blr)) { - - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + + if (br_lck) { + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } free_blocking_lock_record(blr); } diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index 427b6ae2144..1b5a5f39c72 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -355,259 +355,349 @@ static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *na return 0; } +static NTSTATUS handle_trans(connection_struct *conn, + struct trans_state *state, + char *outbuf, int *outsize) +{ + char *local_machine_name; + int name_offset = 0; + + DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n", + state->name,state->total_data,state->total_param, + state->setup_count)); + + /* + * WinCE wierdness.... + */ + + local_machine_name = talloc_asprintf(state, "\\%s\\", + get_local_machine_name()); + + if (local_machine_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (strnequal(state->name, local_machine_name, + strlen(local_machine_name))) { + name_offset = strlen(local_machine_name)-1; + } + + if (!strnequal(&state->name[name_offset], "\\PIPE", + strlen("\\PIPE"))) { + return NT_STATUS_NOT_SUPPORTED; + } + + name_offset += strlen("\\PIPE"); + + /* Win9x weirdness. When talking to a unicode server Win9x + only sends \PIPE instead of \PIPE\ */ + + if (state->name[name_offset] == '\\') + name_offset++; + + DEBUG(5,("calling named_pipe\n")); + *outsize = named_pipe(conn, state->vuid, outbuf, + state->name+name_offset, + state->setup,state->data, + state->param, + state->setup_count,state->total_data, + state->total_param, + state->max_setup_return, + state->max_data_return, + state->max_param_return); + + if (*outsize == 0) { + return NT_STATUS_NOT_SUPPORTED; + } + + if (state->close_on_completion) + close_cnum(conn,state->vuid); + + return NT_STATUS_OK; +} /**************************************************************************** Reply to a SMBtrans. ****************************************************************************/ -int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int bufsize) +int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, + int size, int bufsize) { - fstring name; - int name_offset = 0; - char *data=NULL,*params=NULL; - uint16 *setup=NULL; int outsize = 0; - uint16 vuid = SVAL(inbuf,smb_uid); - unsigned int tpscnt = SVAL(inbuf,smb_vwv0); - unsigned int tdscnt = SVAL(inbuf,smb_vwv1); - unsigned int mprcnt = SVAL(inbuf,smb_vwv2); - unsigned int mdrcnt = SVAL(inbuf,smb_vwv3); - unsigned int msrcnt = CVAL(inbuf,smb_vwv4); - BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0); - BOOL one_way = BITSETW(inbuf+smb_vwv5,1); - unsigned int pscnt = SVAL(inbuf,smb_vwv9); - unsigned int psoff = SVAL(inbuf,smb_vwv10); - unsigned int dscnt = SVAL(inbuf,smb_vwv11); - unsigned int dsoff = SVAL(inbuf,smb_vwv12); - unsigned int suwcnt = CVAL(inbuf,smb_vwv13); - char *local_machine_name; + unsigned int dsoff = SVAL(inbuf, smb_dsoff); + unsigned int dscnt = SVAL(inbuf, smb_dscnt); + unsigned int psoff = SVAL(inbuf, smb_psoff); + unsigned int pscnt = SVAL(inbuf, smb_pscnt); + struct trans_state *state; + NTSTATUS result; + START_PROFILE(SMBtrans); - memset(name, '\0',sizeof(name)); - srvstr_pull_buf(inbuf, name, smb_buf(inbuf), sizeof(name), STR_TERMINATE); + if (!NT_STATUS_IS_OK(allow_new_trans(conn->pending_trans, + SVAL(inbuf, smb_mid)))) { + DEBUG(2, ("Got invalid trans request: %s\n", + nt_errstr(result))); + END_PROFILE(SMBtrans); + return ERROR_NT(result); + } - if (dscnt > tdscnt || pscnt > tpscnt) + if ((state = TALLOC_P(NULL, struct trans_state)) == NULL) { + DEBUG(0, ("talloc failed\n")); + END_PROFILE(SMBtrans); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + state->cmd = SMBtrans; + + state->mid = SVAL(inbuf, smb_mid); + state->vuid = SVAL(inbuf, smb_uid); + state->setup_count = CVAL(inbuf, smb_suwcnt); + state->total_param = SVAL(inbuf, smb_tpscnt); + state->param = NULL; + state->total_data = SVAL(inbuf, smb_tdscnt); + state->data = NULL; + state->max_param_return = SVAL(inbuf, smb_mprcnt); + state->max_data_return = SVAL(inbuf, smb_mdrcnt); + state->max_setup_return = CVAL(inbuf, smb_msrcnt); + state->close_on_completion = BITSETW(inbuf+smb_vwv5,0); + state->one_way = BITSETW(inbuf+smb_vwv5,1); + + memset(state->name, '\0',sizeof(state->name)); + srvstr_pull_buf(inbuf, state->name, smb_buf(inbuf), + sizeof(state->name), STR_TERMINATE); + + if ((dscnt > state->total_data) || (pscnt > state->total_param)) goto bad_param; - - if (tdscnt) { - if((data = (char *)SMB_MALLOC(tdscnt)) == NULL) { - DEBUG(0,("reply_trans: data malloc fail for %u bytes !\n", tdscnt)); + + if (state->total_data) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->data = SMB_MALLOC(state->total_data); + if (state->data == NULL) { + DEBUG(0,("reply_trans: data malloc fail for %u " + "bytes !\n", state->total_data)); + TALLOC_FREE(state); END_PROFILE(SMBtrans); return(ERROR_DOS(ERRDOS,ERRnomem)); } if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt)) goto bad_param; if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) || - (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) + (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) goto bad_param; - memcpy(data,smb_base(inbuf)+dsoff,dscnt); + memcpy(state->data,smb_base(inbuf)+dsoff,dscnt); } - if (tpscnt) { - if((params = (char *)SMB_MALLOC(tpscnt)) == NULL) { - DEBUG(0,("reply_trans: param malloc fail for %u bytes !\n", tpscnt)); - SAFE_FREE(data); + if (state->total_param) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->param = SMB_MALLOC(state->total_param); + if (state->param == NULL) { + DEBUG(0,("reply_trans: param malloc fail for %u " + "bytes !\n", state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); END_PROFILE(SMBtrans); return(ERROR_DOS(ERRDOS,ERRnomem)); } if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt)) goto bad_param; if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) || - (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) + (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) goto bad_param; - memcpy(params,smb_base(inbuf)+psoff,pscnt); + memcpy(state->param,smb_base(inbuf)+psoff,pscnt); } - if (suwcnt) { + state->received_data = dscnt; + state->received_param = pscnt; + + if (state->setup_count) { unsigned int i; - if((setup = SMB_MALLOC_ARRAY(uint16,suwcnt)) == NULL) { - DEBUG(0,("reply_trans: setup malloc fail for %u bytes !\n", (unsigned int)(suwcnt * sizeof(uint16)))); - SAFE_FREE(data); - SAFE_FREE(params); + if((state->setup = TALLOC_ARRAY( + state, uint16, state->setup_count)) == NULL) { + DEBUG(0,("reply_trans: setup malloc fail for %u " + "bytes !\n", (unsigned int) + (state->setup_count * sizeof(uint16)))); + TALLOC_FREE(state); END_PROFILE(SMBtrans); return(ERROR_DOS(ERRDOS,ERRnomem)); } - if (inbuf+smb_vwv14+(suwcnt*SIZEOFWORD) > inbuf + size) + if (inbuf+smb_vwv14+(state->setup_count*SIZEOFWORD) > + inbuf + size) goto bad_param; - if ((smb_vwv14+(suwcnt*SIZEOFWORD) < smb_vwv14) || (smb_vwv14+(suwcnt*SIZEOFWORD) < (suwcnt*SIZEOFWORD))) + if ((smb_vwv14+(state->setup_count*SIZEOFWORD) < smb_vwv14) || + (smb_vwv14+(state->setup_count*SIZEOFWORD) < + (state->setup_count*SIZEOFWORD))) goto bad_param; - for (i=0;i<suwcnt;i++) - setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); + for (i=0;i<state->setup_count;i++) + state->setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); } + state->received_param = pscnt; - srv_signing_trans_start(SVAL(inbuf,smb_mid)); + if ((state->received_param == state->total_param) && + (state->received_data == state->total_data)) { - if (pscnt < tpscnt || dscnt < tdscnt) { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - show_msg(outbuf); - srv_signing_trans_stop(); - if (!send_smb(smbd_server_fd(),outbuf)) - exit_server("reply_trans: send_smb failed."); - } + result = handle_trans(conn, state, outbuf, &outsize); + + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); - /* receive the rest of the trans packet */ - while (pscnt < tpscnt || dscnt < tdscnt) { - BOOL ret; - unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; - - ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); - - /* - * The sequence number for the trans reply is always - * based on the last secondary received. - */ - - srv_signing_trans_start(SVAL(inbuf,smb_mid)); - - if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) { - if(ret) { - DEBUG(0,("reply_trans: Invalid secondary trans packet\n")); - } else { - DEBUG(0,("reply_trans: %s in getting secondary trans response.\n", - (smb_read_error == READ_ERROR) ? "error" : "timeout" )); - } - SAFE_FREE(params); - SAFE_FREE(data); - SAFE_FREE(setup); + if (!NT_STATUS_IS_OK(result)) { END_PROFILE(SMBtrans); - srv_signing_trans_stop(); - return(ERROR_DOS(ERRSRV,ERRerror)); + return ERROR_NT(result); } - show_msg(inbuf); - - /* Revise total_params and total_data in case they have changed downwards */ - if (SVAL(inbuf,smb_vwv0) < tpscnt) - tpscnt = SVAL(inbuf,smb_vwv0); - if (SVAL(inbuf,smb_vwv1) < tdscnt) - tdscnt = SVAL(inbuf,smb_vwv1); - - pcnt = SVAL(inbuf,smb_vwv2); - poff = SVAL(inbuf,smb_vwv3); - pdisp = SVAL(inbuf,smb_vwv4); - - dcnt = SVAL(inbuf,smb_vwv5); - doff = SVAL(inbuf,smb_vwv6); - ddisp = SVAL(inbuf,smb_vwv7); - - pscnt += pcnt; - dscnt += dcnt; - - if (dscnt > tdscnt || pscnt > tpscnt) - goto bad_param; - - if (pcnt) { - if (pdisp+pcnt > tpscnt) - goto bad_param; - if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) - goto bad_param; - if (pdisp > tpscnt) - goto bad_param; - if ((smb_base(inbuf) + poff + pcnt > inbuf + bufsize) || - (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) - goto bad_param; - if (params + pdisp < params) - goto bad_param; - - memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt); + if (outsize == 0) { + END_PROFILE(SMBtrans); + return ERROR_NT(NT_STATUS_INTERNAL_ERROR); } - if (dcnt) { - if (ddisp+dcnt > tdscnt) - goto bad_param; - if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) - goto bad_param; - if (ddisp > tdscnt) - goto bad_param; - if ((smb_base(inbuf) + doff + dcnt > inbuf + bufsize) || - (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) - goto bad_param; - if (data + ddisp < data) - goto bad_param; - - memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt); - } + END_PROFILE(SMBtrans); + return outsize; } - DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n", - name,tdscnt,tpscnt,suwcnt)); + DLIST_ADD(conn->pending_trans, state); - /* - * WinCE wierdness.... - */ + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); + END_PROFILE(SMBtrans); + return outsize; - asprintf(&local_machine_name, "\\%s\\", get_local_machine_name()); + bad_param: - if (local_machine_name == NULL) { - srv_signing_trans_stop(); - SAFE_FREE(data); - SAFE_FREE(params); - SAFE_FREE(setup); - END_PROFILE(SMBtrans); - return ERROR_NT(NT_STATUS_NO_MEMORY); + DEBUG(0,("reply_trans: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); +} + +/**************************************************************************** + Reply to a secondary SMBtrans. + ****************************************************************************/ + +int reply_transs(connection_struct *conn, char *inbuf,char *outbuf, + int size, int bufsize) +{ + int outsize = 0; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; + NTSTATUS result; + + START_PROFILE(SMBtranss); + + show_msg(inbuf); + + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == SVAL(inbuf,smb_mid)) { + break; + } } - if (strnequal(name, local_machine_name, strlen(local_machine_name))) { - name_offset = strlen(local_machine_name)-1; + if ((state == NULL) || (state->cmd != SMBtrans)) { + END_PROFILE(SMBtranss); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - SAFE_FREE(local_machine_name); + /* Revise total_params and total_data in case they have changed + * downwards */ - if (strnequal(&name[name_offset], "\\PIPE", strlen("\\PIPE"))) { - name_offset += strlen("\\PIPE"); + if (SVAL(inbuf, smb_vwv0) < state->total_param) + state->total_param = SVAL(inbuf,smb_vwv0); + if (SVAL(inbuf, smb_vwv1) < state->total_data) + state->total_data = SVAL(inbuf,smb_vwv1); - /* Win9x weirdness. When talking to a unicode server Win9x - only sends \PIPE instead of \PIPE\ */ + pcnt = SVAL(inbuf, smb_spscnt); + poff = SVAL(inbuf, smb_spsoff); + pdisp = SVAL(inbuf, smb_spsdisp); - if (name[name_offset] == '\\') - name_offset++; + dcnt = SVAL(inbuf, smb_sdscnt); + doff = SVAL(inbuf, smb_sdsoff); + ddisp = SVAL(inbuf, smb_sdsdisp); - DEBUG(5,("calling named_pipe\n")); - outsize = named_pipe(conn,vuid,outbuf, - name+name_offset,setup,data,params, - suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt); - } else { - DEBUG(3,("invalid pipe name\n")); - outsize = 0; + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; + + if (pcnt) { + if (pdisp+pcnt > state->total_param) + goto bad_param; + if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) + goto bad_param; + if (pdisp > state->total_param) + goto bad_param; + if ((smb_base(inbuf) + poff + pcnt > inbuf + size) || + (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) + goto bad_param; + if (state->param + pdisp < state->param) + goto bad_param; + + memcpy(state->param+pdisp,smb_base(inbuf)+poff, + pcnt); } - - SAFE_FREE(data); - SAFE_FREE(params); - SAFE_FREE(setup); - - srv_signing_trans_stop(); + if (dcnt) { + if (ddisp+dcnt > state->total_data) + goto bad_param; + if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) + goto bad_param; + if (ddisp > state->total_data) + goto bad_param; + if ((smb_base(inbuf) + doff + dcnt > inbuf + size) || + (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) + goto bad_param; + if (state->data + ddisp < state->data) + goto bad_param; - if (close_on_completion) - close_cnum(conn,vuid); + memcpy(state->data+ddisp, smb_base(inbuf)+doff, + dcnt); + } - if (one_way) { - END_PROFILE(SMBtrans); - return(-1); + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBtranss); + return -1; } - - if (outsize == 0) { - END_PROFILE(SMBtrans); + + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBtranss which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBtrans); + + result = handle_trans(conn, state, outbuf, &outsize); + + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + + if ((outsize == 0) || !NT_STATUS_IS_OK(result)) { + END_PROFILE(SMBtranss); return(ERROR_DOS(ERRSRV,ERRnosupport)); } - END_PROFILE(SMBtrans); + END_PROFILE(SMBtranss); return(outsize); - bad_param: - srv_signing_trans_stop(); - DEBUG(0,("reply_trans: invalid trans parameters\n")); - SAFE_FREE(data); - SAFE_FREE(params); - SAFE_FREE(setup); - END_PROFILE(SMBtrans); + DEBUG(0,("reply_transs: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtranss); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 796eb443326..24d64ecfc76 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -1812,19 +1812,6 @@ int reply_ntrename(connection_struct *conn, } /**************************************************************************** - Reply to an unsolicited SMBNTtranss - just ignore it! -****************************************************************************/ - -int reply_nttranss(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) -{ - START_PROFILE(SMBnttranss); - DEBUG(4,("Ignoring nttranss of length %d\n",length)); - END_PROFILE(SMBnttranss); - return(-1); -} - -/**************************************************************************** Reply to a notify change - queue the request and don't allow a directory to be opened. ****************************************************************************/ @@ -2719,29 +2706,120 @@ static int call_nt_transact_set_user_quota(connection_struct *conn, char *inbuf, } #endif /* HAVE_SYS_QUOTAS */ +static int handle_nttrans(connection_struct *conn, + struct trans_state *state, + char *inbuf, char *outbuf, int size, int bufsize) +{ + int outsize; + + if (Protocol >= PROTOCOL_NT1) { + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */ + } + + /* Now we must call the relevant NT_TRANS function */ + switch(state->call) { + case NT_TRANSACT_CREATE: + START_PROFILE_NESTED(NT_transact_create); + outsize = call_nt_transact_create(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE_NESTED(NT_transact_create); + break; + case NT_TRANSACT_IOCTL: + START_PROFILE_NESTED(NT_transact_ioctl); + outsize = call_nt_transact_ioctl(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_ioctl); + break; + case NT_TRANSACT_SET_SECURITY_DESC: + START_PROFILE_NESTED(NT_transact_set_security_desc); + outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_set_security_desc); + break; + case NT_TRANSACT_NOTIFY_CHANGE: + START_PROFILE_NESTED(NT_transact_notify_change); + outsize = call_nt_transact_notify_change(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_notify_change); + break; + case NT_TRANSACT_RENAME: + START_PROFILE_NESTED(NT_transact_rename); + outsize = call_nt_transact_rename(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_rename); + break; + + case NT_TRANSACT_QUERY_SECURITY_DESC: + START_PROFILE_NESTED(NT_transact_query_security_desc); + outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_query_security_desc); + break; +#ifdef HAVE_SYS_QUOTAS + case NT_TRANSACT_GET_USER_QUOTA: + START_PROFILE_NESTED(NT_transact_get_user_quota); + outsize = call_nt_transact_get_user_quota(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_get_user_quota); + break; + case NT_TRANSACT_SET_USER_QUOTA: + START_PROFILE_NESTED(NT_transact_set_user_quota); + outsize = call_nt_transact_set_user_quota(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_set_user_quota); + break; +#endif /* HAVE_SYS_QUOTAS */ + default: + /* Error in request */ + DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", + state->call)); + return ERROR_DOS(ERRSRV,ERRerror); + } + return outsize; +} + /**************************************************************************** Reply to a SMBNTtrans. ****************************************************************************/ int reply_nttrans(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) + char *inbuf,char *outbuf,int size,int bufsize) { int outsize = 0; - uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount); -#if 0 /* Not used. */ - uint16 max_setup_count = CVAL(inbuf, smb_nt_MaxSetupCount); - uint32 max_parameter_count = IVAL(inbuf, smb_nt_MaxParameterCount); -#endif /* Not used. */ - uint32 total_parameter_count = IVAL(inbuf, smb_nt_TotalParameterCount); - uint32 total_data_count = IVAL(inbuf, smb_nt_TotalDataCount); - uint32 parameter_count = IVAL(inbuf,smb_nt_ParameterCount); - uint32 parameter_offset = IVAL(inbuf,smb_nt_ParameterOffset); - uint32 data_count = IVAL(inbuf,smb_nt_DataCount); - uint32 data_offset = IVAL(inbuf,smb_nt_DataOffset); - uint16 setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); /* setup count is in *words* */ + uint32 pscnt = IVAL(inbuf,smb_nt_ParameterCount); + uint32 psoff = IVAL(inbuf,smb_nt_ParameterOffset); + uint32 dscnt = IVAL(inbuf,smb_nt_DataCount); + uint32 dsoff = IVAL(inbuf,smb_nt_DataOffset); + uint16 function_code = SVAL( inbuf, smb_nt_Function); - char *params = NULL, *data = NULL, *setup = NULL; - uint32 num_params_sofar, num_data_sofar; + NTSTATUS result; + struct trans_state *state; + START_PROFILE(SMBnttrans); if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) { @@ -2749,319 +2827,269 @@ int reply_nttrans(connection_struct *conn, return ERROR_DOS(ERRSRV,ERRaccess); } - outsize = set_message(outbuf,0,0,True); + if (!NT_STATUS_IS_OK(allow_new_trans(conn->pending_trans, + SVAL(inbuf, smb_mid)))) { + DEBUG(2, ("Got invalid nttrans request: %s\n", nt_errstr(result))); + END_PROFILE(SMBnttrans); + return ERROR_NT(result); + } + + if ((state = TALLOC_P(NULL, struct trans_state)) == NULL) { + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRSRV,ERRaccess); + } + + state->cmd = SMBnttrans; + + state->mid = SVAL(inbuf,smb_mid); + state->vuid = SVAL(inbuf,smb_uid); + state->total_data = IVAL(inbuf, smb_nt_TotalDataCount); + state->data = NULL; + state->total_param = IVAL(inbuf, smb_nt_TotalParameterCount); + state->param = NULL; + state->max_data_return = IVAL(inbuf,smb_nt_MaxDataCount); + + /* setup count is in *words* */ + state->setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); + state->call = function_code; /* - * All nttrans messages we handle have smb_wct == 19 + setup_count. - * Ensure this is so as a sanity check. + * All nttrans messages we handle have smb_wct == 19 + + * state->setup_count. Ensure this is so as a sanity check. */ - if(CVAL(inbuf, smb_wct) != 19 + (setup_count/2)) { + if(CVAL(inbuf, smb_wct) != 19 + (state->setup_count/2)) { DEBUG(2,("Invalid smb_wct %d in nttrans call (should be %d)\n", - CVAL(inbuf, smb_wct), 19 + (setup_count/2))); + CVAL(inbuf, smb_wct), 19 + (state->setup_count/2))); goto bad_param; } /* Don't allow more than 128mb for each value. */ - if ((total_parameter_count > (1024*1024*128)) || (total_data_count > (1024*1024*128))) { + if ((state->total_data > (1024*1024*128)) || + (state->total_param > (1024*1024*128))) { END_PROFILE(SMBnttrans); return ERROR_DOS(ERRDOS,ERRnomem); } - /* Allocate the space for the setup, the maximum needed parameters and data */ - - if(setup_count > 0) { - setup = (char *)SMB_MALLOC(setup_count); - } - if (total_parameter_count > 0) { - params = (char *)SMB_MALLOC(total_parameter_count); - } - if (total_data_count > 0) { - data = (char *)SMB_MALLOC(total_data_count); - } - - if ((total_parameter_count && !params) || (total_data_count && !data) || - (setup_count && !setup)) { - SAFE_FREE(setup); - SAFE_FREE(params); - SAFE_FREE(data); - DEBUG(0,("reply_nttrans : Out of memory\n")); - END_PROFILE(SMBnttrans); - return ERROR_DOS(ERRDOS,ERRnomem); - } - - /* Copy the param and data bytes sent with this request into the params buffer */ - num_params_sofar = parameter_count; - num_data_sofar = data_count; - - if (parameter_count > total_parameter_count || data_count > total_data_count) + if ((dscnt > state->total_data) || (pscnt > state->total_param)) goto bad_param; - if(setup) { - DEBUG(10,("reply_nttrans: setup_count = %d\n", setup_count)); - if ((smb_nt_SetupStart + setup_count < smb_nt_SetupStart) || - (smb_nt_SetupStart + setup_count < setup_count)) { + if (state->total_data) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + if ((state->data = SMB_MALLOC(state->total_data)) == NULL) { + DEBUG(0,("reply_nttrans: data malloc fail for %u " + "bytes !\n", state->total_data)); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt)) goto bad_param; - } - if (smb_nt_SetupStart + setup_count > length) { + if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) || + (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) goto bad_param; - } - memcpy( setup, &inbuf[smb_nt_SetupStart], setup_count); - dump_data(10, setup, setup_count); + memcpy(state->data,smb_base(inbuf)+dsoff,dscnt); } - if(params) { - DEBUG(10,("reply_nttrans: parameter_count = %d\n", parameter_count)); - if ((parameter_offset + parameter_count < parameter_offset) || - (parameter_offset + parameter_count < parameter_count)) { + + if (state->total_param) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + if ((state->param = SMB_MALLOC(state->total_param)) == NULL) { + DEBUG(0,("reply_nttrans: param malloc fail for %u " + "bytes !\n", state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt)) goto bad_param; - } - if ((smb_base(inbuf) + parameter_offset + parameter_count > inbuf + length)|| - (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf))) { + if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) || + (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) goto bad_param; - } - memcpy( params, smb_base(inbuf) + parameter_offset, parameter_count); - dump_data(10, params, parameter_count); + memcpy(state->param,smb_base(inbuf)+psoff,pscnt); } - if(data) { - DEBUG(10,("reply_nttrans: data_count = %d\n",data_count)); - if ((data_offset + data_count < data_offset) || (data_offset + data_count < data_count)) { + + state->received_data = dscnt; + state->received_param = pscnt; + + if(state->setup_count > 0) { + DEBUG(10,("reply_nttrans: state->setup_count = %d\n", + state->setup_count)); + state->setup = TALLOC(state, state->setup_count); + if (state->setup == NULL) { + DEBUG(0,("reply_nttrans : Out of memory\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRDOS,ERRnomem); + } + + if ((smb_nt_SetupStart + state->setup_count < smb_nt_SetupStart) || + (smb_nt_SetupStart + state->setup_count < state->setup_count)) { goto bad_param; } - if ((smb_base(inbuf) + data_offset + data_count > inbuf + length) || - (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf))) { + if (smb_nt_SetupStart + state->setup_count > size) { goto bad_param; } - memcpy( data, smb_base(inbuf) + data_offset, data_count); - dump_data(10, data, data_count); + memcpy( state->setup, &inbuf[smb_nt_SetupStart], state->setup_count); + dump_data(10, (char *)state->setup, state->setup_count); } - srv_signing_trans_start(SVAL(inbuf,smb_mid)); + if ((state->received_data == state->total_data) && + (state->received_param == state->total_param)) { + outsize = handle_nttrans(conn, state, inbuf, outbuf, + size, bufsize); + SAFE_FREE(state->param); + SAFE_FREE(state->data); + TALLOC_FREE(state); + END_PROFILE(SMBnttrans); + return outsize; + } - if(num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - srv_signing_trans_stop(); - show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) { - exit_server("reply_nttrans: send_smb failed."); - } + DLIST_ADD(conn->pending_trans, state); - while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) { - BOOL ret; - uint32 parameter_displacement; - uint32 data_displacement; + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); + END_PROFILE(SMBnttrans); + return outsize; - ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); + bad_param: - /* We need to re-calcuate the new length after we've read the secondary packet. */ - length = smb_len(inbuf) + 4; + DEBUG(0,("reply_nttrans: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBnttrans); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); +} + +/**************************************************************************** + Reply to a SMBnttranss + ****************************************************************************/ - /* - * The sequence number for the trans reply is always - * based on the last secondary received. - */ +int reply_nttranss(connection_struct *conn, char *inbuf,char *outbuf, + int size,int bufsize) +{ + int outsize = 0; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; - srv_signing_trans_start(SVAL(inbuf,smb_mid)); + START_PROFILE(SMBnttranss); - if((ret && (CVAL(inbuf, smb_com) != SMBnttranss)) || !ret) { - outsize = set_message(outbuf,0,0,True); - if(ret) { - DEBUG(0,("reply_nttrans: Invalid secondary nttrans packet\n")); - } else { - DEBUG(0,("reply_nttrans: %s in getting secondary nttrans response.\n", - (smb_read_error == READ_ERROR) ? "error" : "timeout" )); - } - goto bad_param; - } - - /* Revise total_params and total_data in case they have changed downwards */ - if (IVAL(inbuf, smb_nts_TotalParameterCount) < total_parameter_count) { - total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount); - } - if (IVAL(inbuf, smb_nts_TotalDataCount) < total_data_count) { - total_data_count = IVAL(inbuf, smb_nts_TotalDataCount); - } + show_msg(inbuf); - parameter_count = IVAL(inbuf,smb_nts_ParameterCount); - parameter_offset = IVAL(inbuf, smb_nts_ParameterOffset); - parameter_displacement = IVAL(inbuf, smb_nts_ParameterDisplacement); - num_params_sofar += parameter_count; + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == SVAL(inbuf,smb_mid)) { + break; + } + } - data_count = IVAL(inbuf, smb_nts_DataCount); - data_displacement = IVAL(inbuf, smb_nts_DataDisplacement); - data_offset = IVAL(inbuf, smb_nts_DataOffset); - num_data_sofar += data_count; + if ((state == NULL) || (state->cmd != SMBnttrans)) { + END_PROFILE(SMBnttranss); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } - if (num_params_sofar > total_parameter_count || num_data_sofar > total_data_count) { - DEBUG(0,("reply_nttrans2: data overflow in secondary nttrans packet")); - goto bad_param; - } + /* Revise state->total_param and state->total_data in case they have + changed downwards */ + if (IVAL(inbuf, smb_nts_TotalParameterCount) < state->total_param) { + state->total_param = IVAL(inbuf, smb_nts_TotalParameterCount); + } + if (IVAL(inbuf, smb_nts_TotalDataCount) < state->total_data) { + state->total_data = IVAL(inbuf, smb_nts_TotalDataCount); + } - if (parameter_count) { - if (parameter_displacement + parameter_count > total_parameter_count) { - goto bad_param; - } - if ((parameter_displacement + parameter_count < parameter_displacement) || - (parameter_displacement + parameter_count < parameter_count)) { - goto bad_param; - } - if (parameter_displacement > total_parameter_count) { - goto bad_param; - } - if ((smb_base(inbuf) + parameter_offset + parameter_count > inbuf + length) || - (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf))) { - goto bad_param; - } - if (parameter_displacement + params < params) { - goto bad_param; - } + pcnt = IVAL(inbuf,smb_nts_ParameterCount); + poff = IVAL(inbuf, smb_nts_ParameterOffset); + pdisp = IVAL(inbuf, smb_nts_ParameterDisplacement); - memcpy( ¶ms[parameter_displacement], smb_base(inbuf) + parameter_offset, parameter_count); - } + dcnt = IVAL(inbuf, smb_nts_DataCount); + ddisp = IVAL(inbuf, smb_nts_DataDisplacement); + doff = IVAL(inbuf, smb_nts_DataOffset); - if (data_count) { - if (data_displacement + data_count > total_data_count) { - goto bad_param; - } - if ((data_displacement + data_count < data_displacement) || - (data_displacement + data_count < data_count)) { - goto bad_param; - } - if (data_displacement > total_data_count) { - goto bad_param; - } - if ((smb_base(inbuf) + data_offset + data_count > inbuf + length) || - (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf))) { - goto bad_param; - } - if (data_displacement + data < data) { - goto bad_param; - } + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; - memcpy( &data[data_displacement], smb_base(inbuf)+ data_offset, data_count); - } - } - } + if (pcnt) { + if (pdisp+pcnt > state->total_param) + goto bad_param; + if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) + goto bad_param; + if (pdisp > state->total_param) + goto bad_param; + if ((smb_base(inbuf) + poff + pcnt > inbuf + size) || + (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) + goto bad_param; + if (state->param + pdisp < state->param) + goto bad_param; - if (Protocol >= PROTOCOL_NT1) { - SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_IS_LONG_NAME); + memcpy(state->param+pdisp,smb_base(inbuf)+poff, + pcnt); } - /* Now we must call the relevant NT_TRANS function */ - switch(function_code) { - case NT_TRANSACT_CREATE: - START_PROFILE_NESTED(NT_transact_create); - outsize = call_nt_transact_create(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_create); - break; - case NT_TRANSACT_IOCTL: - START_PROFILE_NESTED(NT_transact_ioctl); - outsize = call_nt_transact_ioctl(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_ioctl); - break; - case NT_TRANSACT_SET_SECURITY_DESC: - START_PROFILE_NESTED(NT_transact_set_security_desc); - outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_set_security_desc); - break; - case NT_TRANSACT_NOTIFY_CHANGE: - START_PROFILE_NESTED(NT_transact_notify_change); - outsize = call_nt_transact_notify_change(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_notify_change); - break; - case NT_TRANSACT_RENAME: - START_PROFILE_NESTED(NT_transact_rename); - outsize = call_nt_transact_rename(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_rename); - break; + if (dcnt) { + if (ddisp+dcnt > state->total_data) + goto bad_param; + if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) + goto bad_param; + if (ddisp > state->total_data) + goto bad_param; + if ((smb_base(inbuf) + doff + dcnt > inbuf + size) || + (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) + goto bad_param; + if (state->data + ddisp < state->data) + goto bad_param; - case NT_TRANSACT_QUERY_SECURITY_DESC: - START_PROFILE_NESTED(NT_transact_query_security_desc); - outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_query_security_desc); - break; -#ifdef HAVE_SYS_QUOTAS - case NT_TRANSACT_GET_USER_QUOTA: - START_PROFILE_NESTED(NT_transact_get_user_quota); - outsize = call_nt_transact_get_user_quota(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_get_user_quota); - break; - case NT_TRANSACT_SET_USER_QUOTA: - START_PROFILE_NESTED(NT_transact_set_user_quota); - outsize = call_nt_transact_set_user_quota(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_set_user_quota); - break; -#endif /* HAVE_SYS_QUOTAS */ - default: - /* Error in request */ - DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code)); - SAFE_FREE(setup); - SAFE_FREE(params); - SAFE_FREE(data); - END_PROFILE(SMBnttrans); - srv_signing_trans_stop(); - return ERROR_DOS(ERRSRV,ERRerror); + memcpy(state->data+ddisp, smb_base(inbuf)+doff, + dcnt); } - /* As we do not know how many data packets will need to be - returned here the various call_nt_transact_xxxx calls - must send their own. Thus a call_nt_transact_xxxx routine only - returns a value other than -1 when it wants to send - an error packet. - */ + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBnttranss); + return -1; + } - srv_signing_trans_stop(); + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBnttranss which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBnttrans); - SAFE_FREE(setup); - SAFE_FREE(params); - SAFE_FREE(data); - END_PROFILE(SMBnttrans); - return outsize; /* If a correct response was needed the call_nt_transact_xxxx - calls have already sent it. If outsize != -1 then it is - returning an error packet. */ + outsize = handle_nttrans(conn, state, inbuf, outbuf, + size, bufsize); - bad_param: + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); - srv_signing_trans_stop(); - SAFE_FREE(params); - SAFE_FREE(data); - SAFE_FREE(setup); - END_PROFILE(SMBnttrans); + if (outsize == 0) { + END_PROFILE(SMBnttranss); + return(ERROR_DOS(ERRSRV,ERRnosupport)); + } + + END_PROFILE(SMBnttranss); + return(outsize); + + bad_param: + + DEBUG(0,("reply_nttranss: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBnttranss); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 40d26f7672c..cdeccab5e81 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -505,22 +505,27 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) return receive_smb(smbd_server_fd(), buffer, 0); } -/**************************************************************************** -Get the next SMB packet, doing the local message processing automatically. -****************************************************************************/ +/* + * Only allow 5 outstanding trans requests. We're allocating memory, so + * prevent a DoS. + */ -BOOL receive_next_smb(char *inbuf, int bufsize, int timeout) +NTSTATUS allow_new_trans(struct trans_state *list, int mid) { - BOOL got_keepalive; - BOOL ret; + int count = 0; + for (; list != NULL; list = list->next) { - do { - ret = receive_message_or_smb(inbuf,bufsize,timeout); - - got_keepalive = (ret && (CVAL(inbuf,0) == SMBkeepalive)); - } while (ret && got_keepalive); + if (list->mid == mid) { + return NT_STATUS_INVALID_PARAMETER; + } + + count += 1; + } + if (count > 5) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } - return ret; + return NT_STATUS_OK; } /**************************************************************************** @@ -611,7 +616,7 @@ static const struct smb_message_struct { /* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER }, /* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER }, /* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC }, -/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC}, +/* 0x26 */ { "SMBtranss",reply_transs,AS_USER | CAN_IPC}, /* 0x27 */ { "SMBioctl",reply_ioctl,0}, /* 0x28 */ { "SMBioctls",NULL,AS_USER}, /* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE }, diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index ce41266a1c8..040f7710fdd 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -269,10 +269,13 @@ NTSTATUS check_path_syntax_wcard(pstring destname, const pstring srcname, BOOL * switch(next_mb_char_size(s)) { case 4: *d++ = *s++; + /*fall through*/ case 3: *d++ = *s++; + /*fall through*/ case 2: *d++ = *s++; + /*fall through*/ case 1: *d++ = *s++; break; @@ -374,10 +377,13 @@ NTSTATUS check_path_syntax_posix(pstring destname, const pstring srcname) switch(next_mb_char_size(s)) { case 4: *d++ = *s++; + /*fall through*/ case 3: *d++ = *s++; + /*fall through*/ case 2: *d++ = *s++; + /*fall through*/ case 1: *d++ = *s++; break; @@ -2319,7 +2325,7 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s /* 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)) { + if (!is_locked(fsp,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { SMB_STRUCT_STAT st; SMB_OFF_T size = 0; @@ -2390,8 +2396,13 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length * 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); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, + (SMB_BIG_UINT)startpos, + WRITE_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); if (NT_STATUS_V(status)) { #if 0 @@ -2407,8 +2418,15 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length * 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)) { + if(push_blocking_lock_request(inbuf, length, + fsp, + -1, + 0, + SVAL(inbuf,smb_pid), + WRITE_LOCK, + WINDOWS_LOCK, + (SMB_BIG_UINT)startpos, + (SMB_BIG_UINT)numtoread)) { END_PROFILE(SMBlockread); return -1; } @@ -2486,7 +2504,7 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", data = smb_buf(outbuf) + 3; - if (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK)) { END_PROFILE(SMBread); return ERROR_DOS(ERRDOS,ERRlock); } @@ -2694,7 +2712,7 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt } - if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK)) { END_PROFILE(SMBreadX); return ERROR_DOS(ERRDOS,ERRlock); } @@ -2757,7 +2775,7 @@ int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, 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)) { + if (is_locked(fsp,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwritebraw); return(ERROR_DOS(ERRDOS,ERRlock)); } @@ -2878,7 +2896,7 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, 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)) { + if (numtowrite && is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwriteunlock); return ERROR_DOS(ERRDOS,ERRlock); } @@ -2900,8 +2918,12 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, } if (numtowrite) { - status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite, - (SMB_BIG_UINT)startpos); + status = do_unlock(fsp, + SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtowrite, + (SMB_BIG_UINT)startpos, + WINDOWS_LOCK); + if (NT_STATUS_V(status)) { END_PROFILE(SMBwriteunlock); return ERROR_NT(status); @@ -2951,7 +2973,7 @@ int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int d 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)) { + if (is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwrite); return ERROR_DOS(ERRDOS,ERRlock); } @@ -3066,7 +3088,7 @@ int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int leng #endif /* LARGE_SMB_OFF_T */ } - if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwriteX); return ERROR_DOS(ERRDOS,ERRlock); } @@ -3340,7 +3362,7 @@ int reply_writeclose(connection_struct *conn, mtime = srv_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)) { + if (numtowrite && is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwriteclose); return ERROR_DOS(ERRDOS,ERRlock); } @@ -3410,7 +3432,13 @@ int reply_lock(connection_struct *conn, DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n", fsp->fh->fd, fsp->fnum, (double)offset, (double)count)); - status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + count, + offset, + WRITE_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); if (NT_STATUS_V(status)) { #if 0 /* Tests using Samba4 against W2K show this call never creates a blocking lock. */ @@ -3420,7 +3448,14 @@ int reply_lock(connection_struct *conn, * 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)) { + if(push_blocking_lock_request(inbuf, length, + fsp, + -1, + 0, + SVAL(inbuf,smb_pid), + WRITE_LOCK, + WINDOWS_LOCK, + offset, count)) { END_PROFILE(SMBlock); return -1; } @@ -3452,7 +3487,12 @@ int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size, 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); + status = do_unlock(fsp, + SVAL(inbuf,smb_pid), + count, + offset, + WINDOWS_LOCK); + if (NT_STATUS_V(status)) { END_PROFILE(SMBunlock); return ERROR_NT(status); @@ -5279,7 +5319,12 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, "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); + status = do_unlock(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); + if (NT_STATUS_V(status)) { END_PROFILE(SMBlockingX); return ERROR_NT(status); @@ -5297,6 +5342,7 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, of smb_lkrng structs */ for(i = 0; i < (int)num_locks; i++) { + enum brl_type lock_type = ((locktype & 1) ? READ_LOCK:WRITE_LOCK); 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); @@ -5314,9 +5360,14 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, (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); + status = do_lock_spin(fsp, + lock_pid, + count, + offset, + lock_type, + WINDOWS_LOCK, + &my_lock_ctx); + if (NT_STATUS_V(status)) { /* * Interesting fact found by IFSTEST /t @@ -5334,8 +5385,13 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, * onto the blocking lock queue. */ if(push_blocking_lock_request(inbuf, length, - lock_timeout, i, - lock_pid, offset, + fsp, + lock_timeout, + i, + lock_pid, + lock_type, + WINDOWS_LOCK, + offset, count)) { END_PROFILE(SMBlockingX); return -1; @@ -5368,7 +5424,11 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_DOS(ERRDOS,ERRnoaccess); } - do_unlock(fsp,conn,lock_pid,count,offset); + do_unlock(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); } END_PROFILE(SMBlockingX); return ERROR_NT(status); @@ -5430,7 +5490,7 @@ int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length, tcount = maxcount; total_read = 0; - if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { END_PROFILE(SMBreadBmpx); return ERROR_DOS(ERRDOS,ERRlock); } @@ -5562,7 +5622,7 @@ int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, 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)) { + if (is_locked(fsp,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK)) { END_PROFILE(SMBwriteBmpx); return(ERROR_DOS(ERRDOS,ERRlock)); } diff --git a/source3/smbd/server.c b/source3/smbd/server.c index ba31827eb3f..0880778f55f 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -898,9 +898,8 @@ void build_options(BOOL screen); * If we're interactive we want to set our own process group for * signal management. */ - if (interactive && !no_process_group) { + if (interactive && !no_process_group) setpgid( (pid_t)0, (pid_t)0); - } #endif if (!directory_exist(lp_lockdir(), NULL)) diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 4f5039e86c3..19463403aca 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -563,7 +563,7 @@ static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list * HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ -static int send_trans2_replies(char *outbuf, +int send_trans2_replies(char *outbuf, int bufsize, char *params, int paramsize, @@ -1644,11 +1644,12 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", case SMB_FIND_ID_BOTH_DIRECTORY_INFO: break; case SMB_FIND_FILE_UNIX: - if (!lp_unix_extensions()) - return(ERROR_DOS(ERRDOS,ERRunknownlevel)); + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } break; default: - return(ERROR_DOS(ERRDOS,ERRunknownlevel)); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } srvstr_get_path_wcard(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus, &mask_contains_wcard); @@ -1926,11 +1927,12 @@ resume_key = %d resume name = %s continue=%d level = %d\n", case SMB_FIND_ID_BOTH_DIRECTORY_INFO: break; case SMB_FIND_FILE_UNIX: - if (!lp_unix_extensions()) - return(ERROR_DOS(ERRDOS,ERRunknownlevel)); + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } break; default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } if (info_level == SMB_FIND_EA_LIST) { @@ -2398,13 +2400,21 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned */ case SMB_QUERY_CIFS_UNIX_INFO: - if (!lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } data_len = 12; SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION); SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION); - SBIG_UINT(pdata,4,((SMB_BIG_UINT)(CIFS_UNIX_POSIX_ACLS_CAP| - CIFS_UNIX_POSIX_PATHNAMES_CAP))); /* We have POSIX ACLs and pathname capability. */ + /* We have POSIX ACLs, pathname and locking capability. */ + SBIG_UINT(pdata,4,((SMB_BIG_UINT)( + CIFS_UNIX_POSIX_ACLS_CAP| + CIFS_UNIX_POSIX_PATHNAMES_CAP| +#if defined(DEVELOPER) /* Not quite finished yet... */ + CIFS_UNIX_FCNTL_LOCKS_CAP))); +#else + 0))); +#endif break; case SMB_QUERY_POSIX_FS_INFO: @@ -2412,9 +2422,10 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int rc; vfs_statvfs_struct svfs; - if (!lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); - + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + rc = SMB_VFS_STATVFS(conn, ".", &svfs); if (!rc) { @@ -2430,7 +2441,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_POSIX_FS_INFO succsessful\n")); #ifdef EOPNOTSUPP } else if (rc == EOPNOTSUPP) { - return ERROR_DOS(ERRDOS, ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); #endif /* EOPNOTSUPP */ } else { DEBUG(0,("vfs_statvfs() failed for service [%s]\n",lp_servicename(SNUM(conn)))); @@ -2451,7 +2462,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned } /* drop through */ default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } @@ -2495,7 +2506,7 @@ static int call_trans2setfsinfo(connection_struct *conn, char *inbuf, char *outb uint32 client_unix_cap_high; if (!lp_unix_extensions()) { - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } /* There should be 12 bytes of capabilities set. */ @@ -2515,8 +2526,15 @@ cap_low = 0x%x, cap_high = 0x%x\n", (unsigned int)client_unix_cap_high )); /* Here is where we must switch to posix pathname processing... */ - lp_set_posix_pathnames(); - mangle_change_to_posix(); + if (client_unix_cap_low & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + lp_set_posix_pathnames(); + mangle_change_to_posix(); + } +#if defined(DEVELOPER) + if (client_unix_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP) { + lp_set_posix_cifsx_locktype(POSIX_LOCK); + } +#endif break; } case SMB_FS_QUOTA_INFORMATION: @@ -2593,7 +2611,7 @@ cap_low = 0x%x, cap_high = 0x%x\n", default: DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n", info_level)); - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); break; } @@ -2768,13 +2786,14 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * char *fullpathname; char *base_name; char *p; + char *lock_data = NULL; SMB_OFF_T pos = 0; BOOL bad_path = False; BOOL delete_pending = False; int len; time_t c_time; files_struct *fsp = NULL; - TALLOC_CTX *ea_ctx = NULL; + TALLOC_CTX *data_ctx = NULL; struct ea_list *ea_list = NULL; uint32 access_mask = 0x12019F; /* Default - GENERIC_EXECUTE mapping from Windows */ @@ -2893,8 +2912,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * nlink -= 1; } - if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } DEBUG(3,("call_trans2qfilepathinfo %s (fnum = %d) level=%d call=%d total_data=%d\n", fname,fsp ? fsp->fnum : -1, info_level,tran_call,total_data)); @@ -2913,40 +2933,70 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * if (!(mode & aDIR)) file_size = get_file_size(sbuf); - /* Pull any EA list from the data portion. */ - if (info_level == SMB_INFO_QUERY_EAS_FROM_LIST) { - uint32 ea_size; + /* Pull out any data sent here before we realloc. */ + switch (info_level) { + case SMB_INFO_QUERY_EAS_FROM_LIST: + { + /* Pull any EA list from the data portion. */ + uint32 ea_size; - if (total_data < 4) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } - ea_size = IVAL(pdata,0); + if (total_data < 4) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + ea_size = IVAL(pdata,0); - if (total_data > 0 && ea_size != total_data) { - DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \ + if (total_data > 0 && ea_size != total_data) { + DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) )); - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } - if (!lp_ea_support(SNUM(conn))) { - return ERROR_DOS(ERRDOS,ERReasnotsupported); - } + if (!lp_ea_support(SNUM(conn))) { + return ERROR_DOS(ERRDOS,ERReasnotsupported); + } - if ((ea_ctx = talloc_init("ea_list")) == NULL) { - return ERROR_NT(NT_STATUS_NO_MEMORY); + if ((data_ctx = talloc_init("ea_list")) == NULL) { + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + /* Pull out the list of names. */ + ea_list = read_ea_name_list(data_ctx, pdata + 4, ea_size - 4); + if (!ea_list) { + talloc_destroy(data_ctx); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + break; } +#if defined(DEVELOPER) + case SMB_QUERY_POSIX_LOCK: + { + if (fsp == NULL || fsp->fh->fd == -1) { + return ERROR_NT(NT_STATUS_INVALID_HANDLE); + } - /* Pull out the list of names. */ - ea_list = read_ea_name_list(ea_ctx, pdata + 4, ea_size - 4); - if (!ea_list) { - talloc_destroy(ea_ctx); - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + if (total_data != POSIX_LOCK_DATA_SIZE) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + if ((data_ctx = talloc_init("lock_request")) == NULL) { + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + /* Copy the lock range data. */ + lock_data = talloc_memdup(data_ctx, pdata, total_data); + if (!lock_data) { + talloc_destroy(data_ctx); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } } +#endif + default: + break; } *pparams = SMB_REALLOC(*pparams,2); if (*pparams == NULL) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); return ERROR_NT(NT_STATUS_NO_MEMORY); } params = *pparams; @@ -2954,7 +3004,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN; *ppdata = SMB_REALLOC(*ppdata, data_size); if (*ppdata == NULL ) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); return ERROR_NT(NT_STATUS_NO_MEMORY); } pdata = *ppdata; @@ -3040,18 +3090,18 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_EAS_FROM_LIST\n")); - ea_file_list = get_ea_list_from_file(ea_ctx, conn, fsp, fname, &total_ea_len); + ea_file_list = get_ea_list_from_file(data_ctx, conn, fsp, fname, &total_ea_len); ea_list = ea_list_union(ea_list, ea_file_list, &total_ea_len); if (!ea_list || (total_ea_len > data_size)) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); data_size = 4; SIVAL(pdata,0,4); /* EA List Length must be set to 4 if no EA's. */ break; } - data_size = fill_ea_buffer(ea_ctx, pdata, data_size, conn, ea_list); - talloc_destroy(ea_ctx); + data_size = fill_ea_buffer(data_ctx, pdata, data_size, conn, ea_list); + talloc_destroy(data_ctx); break; } @@ -3062,21 +3112,21 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_ALL_EAS\n")); - ea_ctx = talloc_init("ea_ctx"); - if (!ea_ctx) { + data_ctx = talloc_init("ea_ctx"); + if (!data_ctx) { return ERROR_NT(NT_STATUS_NO_MEMORY); } - ea_list = get_ea_list_from_file(ea_ctx, conn, fsp, fname, &total_ea_len); + ea_list = get_ea_list_from_file(data_ctx, conn, fsp, fname, &total_ea_len); if (!ea_list || (total_ea_len > data_size)) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); data_size = 4; SIVAL(pdata,0,4); /* EA List Length must be set to 4 if no EA's. */ break; } - data_size = fill_ea_buffer(ea_ctx, pdata, data_size, conn, ea_list); - talloc_destroy(ea_ctx); + data_size = fill_ea_buffer(data_ctx, pdata, data_size, conn, ea_list); + talloc_destroy(data_ctx); break; } @@ -3469,8 +3519,84 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd } #endif + +#if defined(DEVELOPER) + case SMB_QUERY_POSIX_LOCK: + { + NTSTATUS status = NT_STATUS_INVALID_LEVEL; + SMB_BIG_UINT count; + SMB_BIG_UINT offset; + uint16 lock_pid; + enum brl_type lock_type; + + if (total_data != POSIX_LOCK_DATA_SIZE) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) { + case POSIX_LOCK_TYPE_READ: + lock_type = READ_LOCK; + break; + case POSIX_LOCK_TYPE_WRITE: + lock_type = WRITE_LOCK; + break; + case POSIX_LOCK_TYPE_UNLOCK: + default: + /* There's no point in asking for an unlock... */ + talloc_destroy(data_ctx); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + lock_pid = (uint16)IVAL(pdata, POSIX_LOCK_PID_OFFSET); +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_START_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_START_OFFSET)); + count = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_LEN_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_LEN_OFFSET)); +#else /* HAVE_LONGLONG */ + offset = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_START_OFFSET); + count = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_LEN_OFFSET); +#endif /* HAVE_LONGLONG */ + + status = query_lock(fsp, + &lock_pid, + &count, + &offset, + &lock_type, + POSIX_LOCK); + + if (ERROR_WAS_LOCK_DENIED(status)) { + /* Here we need to report who has it locked... */ + data_size = POSIX_LOCK_DATA_SIZE; + + SSVAL(pdata, POSIX_LOCK_TYPE_OFFSET, lock_type); + SSVAL(pdata, POSIX_LOCK_FLAGS_OFFSET, 0); + SIVAL(pdata, POSIX_LOCK_PID_OFFSET, lock_pid); +#if defined(HAVE_LONGLONG) + SIVAL(pdata, POSIX_LOCK_START_OFFSET, (uint32)(offset & 0xFFFFFFFF)); + SIVAL(pdata, POSIX_LOCK_START_OFFSET + 4, (uint32)((offset >> 32) & 0xFFFFFFFF)); + SIVAL(pdata, POSIX_LOCK_LEN_OFFSET, (uint32)(count & 0xFFFFFFFF)); + SIVAL(pdata, POSIX_LOCK_LEN_OFFSET + 4, (uint32)((count >> 32) & 0xFFFFFFFF)); +#else /* HAVE_LONGLONG */ + SIVAL(pdata, POSIX_LOCK_START_OFFSET, offset); + SIVAL(pdata, POSIX_LOCK_LEN_OFFSET, count); +#endif /* HAVE_LONGLONG */ + + } else if (NT_STATUS_IS_OK(status)) { + /* For success we just return a copy of what we sent + with the lock type set to POSIX_LOCK_TYPE_UNLOCK. */ + data_size = POSIX_LOCK_DATA_SIZE; + memcpy(pdata, lock_data, POSIX_LOCK_DATA_SIZE); + SSVAL(pdata, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_UNLOCK); + } else { + return ERROR_NT(status); + } + break; + } +#endif + default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } send_trans2_replies(outbuf, bufsize, params, param_size, *ppdata, data_size); @@ -3674,8 +3800,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char if (!CAN_WRITE(conn)) return ERROR_DOS(ERRSRV,ERRaccess); - if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } if (VALID_STAT(sbuf)) unixmode = sbuf.st_mode; @@ -3843,8 +3970,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T allocation_size |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", fname, (double)allocation_size )); @@ -3910,8 +4039,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size )); break; @@ -3964,8 +4095,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T position_information |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ DEBUG(10,("call_trans2setfilepathinfo: Set file position information for file %s to %.0f\n", fname, (double)position_information )); @@ -4021,8 +4154,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ } pdata+=24; /* ctime & st_blocks are not changed */ @@ -4329,8 +4464,109 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", } #endif +#if defined(DEVELOPER) + case SMB_SET_POSIX_LOCK: + { + SMB_BIG_UINT count; + SMB_BIG_UINT offset; + uint16 lock_pid; + BOOL lock_blocking; + enum brl_type lock_type; + BOOL my_lock_ctx; + + if (fsp == NULL || fsp->fh->fd == -1) { + return ERROR_NT(NT_STATUS_INVALID_HANDLE); + } + + if (total_data != POSIX_LOCK_DATA_SIZE) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) { + case POSIX_LOCK_TYPE_READ: + lock_type = READ_LOCK; + break; + case POSIX_LOCK_TYPE_WRITE: + /* Return the right POSIX-mappable error code for files opened read-only. */ + if (!fsp->can_write) { + return ERROR_NT(NT_STATUS_INVALID_HANDLE); + } + lock_type = WRITE_LOCK; + break; + case POSIX_LOCK_TYPE_UNLOCK: + lock_type = UNLOCK_LOCK; + break; + default: + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + if (SVAL(pdata,POSIX_LOCK_FLAGS_OFFSET) == POSIX_LOCK_FLAG_NOWAIT) { + lock_blocking = False; + } else if (SVAL(pdata,POSIX_LOCK_FLAGS_OFFSET) == POSIX_LOCK_FLAG_WAIT) { + lock_blocking = True; + } else { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + lock_pid = (uint16)IVAL(pdata, POSIX_LOCK_PID_OFFSET); +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_START_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_START_OFFSET)); + count = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_LEN_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_LEN_OFFSET)); +#else /* HAVE_LONGLONG */ + offset = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_START_OFFSET); + count = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_LEN_OFFSET); +#endif /* HAVE_LONGLONG */ + + if (lock_type == UNLOCK_LOCK) { + status = do_unlock(fsp, + lock_pid, + count, + offset, + POSIX_LOCK); + } else { + status = do_lock(fsp, + lock_pid, + count, + offset, + lock_type, + POSIX_LOCK, + &my_lock_ctx); + + /* TODO: Deal with rescheduling blocking lock fail here... */ + if (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, + fsp, + -1, /* infinite timeout. */ + 0, + lock_pid, + lock_type, + POSIX_LOCK, + offset, + count)) { + return -1; + } + } + } + + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + SSVAL(params,0,0); + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + return(-1); + } +#endif + default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } /* get some defaults (no modifications) if any info is zero or -1. */ @@ -4582,7 +4818,7 @@ static int call_trans2findnotifyfirst(connection_struct *conn, char *inbuf, char case 2: break; default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } /* Realloc the parameter and data sizes */ @@ -4759,331 +4995,418 @@ int reply_findnclose(connection_struct *conn, return(outsize); } -/**************************************************************************** - Reply to a SMBtranss2 - just ignore it! -****************************************************************************/ - -int reply_transs2(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) -{ - START_PROFILE(SMBtranss2); - DEBUG(4,("Ignoring transs2 of length %d\n",length)); - END_PROFILE(SMBtranss2); - return(-1); -} - -/**************************************************************************** - Reply to a SMBtrans2. -****************************************************************************/ - -int reply_trans2(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) +int handle_trans2(connection_struct *conn, + struct trans_state *state, + char *inbuf, char *outbuf, int size, int bufsize) { - int outsize = 0; - unsigned int total_params = SVAL(inbuf, smb_tpscnt); - unsigned int total_data =SVAL(inbuf, smb_tdscnt); - unsigned int max_data_bytes = SVAL(inbuf, smb_mdrcnt); -#if 0 - unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt); - unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt); - BOOL close_tid = BITSETW(inbuf+smb_flags,0); - BOOL no_final_response = BITSETW(inbuf+smb_flags,1); - int32 timeout = IVALS(inbuf,smb_timeout); -#endif - unsigned int suwcnt = SVAL(inbuf, smb_suwcnt); - unsigned int tran_call = SVAL(inbuf, smb_setup0); - char *params = NULL, *data = NULL; - unsigned int num_params, num_params_sofar, num_data, num_data_sofar; - START_PROFILE(SMBtrans2); - - if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) - && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) { - END_PROFILE(SMBtrans2); - return ERROR_DOS(ERRSRV,ERRaccess); - } - - outsize = set_message(outbuf,0,0,True); - - /* All trans2 messages we handle have smb_sucnt == 1 - ensure this - is so as a sanity check */ - if (suwcnt != 1) { - /* - * Need to have rc=0 for ioctl to get job id for OS/2. - * Network printing will fail if function is not successful. - * Similar function in reply.c will be used if protocol - * is LANMAN1.0 instead of LM1.2X002. - * Until DosPrintSetJobInfo with PRJINFO3 is supported, - * outbuf doesn't have to be set(only job id is used). - */ - if ( (suwcnt == 4) && (tran_call == TRANSACT2_IOCTL) && - (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) && - (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) { - DEBUG(2,("Got Trans2 DevIOctl jobid\n")); - } else { - DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",suwcnt)); - DEBUG(2,("Transaction is %d\n",tran_call)); - END_PROFILE(SMBtrans2); - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } - } - - /* Allocate the space for the maximum needed parameters and data */ - if (total_params > 0) - params = (char *)SMB_MALLOC(total_params); - if (total_data > 0) - data = (char *)SMB_MALLOC(total_data); - - if ((total_params && !params) || (total_data && !data)) { - DEBUG(2,("Out of memory in reply_trans2\n")); - SAFE_FREE(params); - SAFE_FREE(data); - END_PROFILE(SMBtrans2); - return ERROR_NT(NT_STATUS_NO_MEMORY); - } - - /* Copy the param and data bytes sent with this request into - the params buffer */ - num_params = num_params_sofar = SVAL(inbuf,smb_pscnt); - num_data = num_data_sofar = SVAL(inbuf, smb_dscnt); - - if (num_params > total_params || num_data > total_data) - exit_server("invalid params in reply_trans2"); - - if(params) { - unsigned int psoff = SVAL(inbuf, smb_psoff); - if ((psoff + num_params < psoff) || (psoff + num_params < num_params)) - goto bad_param; - if ((smb_base(inbuf) + psoff + num_params > inbuf + length) || - (smb_base(inbuf) + psoff + num_params < smb_base(inbuf))) - goto bad_param; - memcpy( params, smb_base(inbuf) + psoff, num_params); - } - if(data) { - unsigned int dsoff = SVAL(inbuf, smb_dsoff); - if ((dsoff + num_data < dsoff) || (dsoff + num_data < num_data)) - goto bad_param; - if ((smb_base(inbuf) + dsoff + num_data > inbuf + length) || - (smb_base(inbuf) + dsoff + num_data < smb_base(inbuf))) - goto bad_param; - memcpy( data, smb_base(inbuf) + dsoff, num_data); - } - - srv_signing_trans_start(SVAL(inbuf,smb_mid)); - - if(num_data_sofar < total_data || num_params_sofar < total_params) { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - srv_signing_trans_stop(); - show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) - exit_server("reply_trans2: send_smb failed."); - - while (num_data_sofar < total_data || - num_params_sofar < total_params) { - BOOL ret; - unsigned int param_disp; - unsigned int param_off; - unsigned int data_disp; - unsigned int data_off; - - ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); - - /* We need to re-calcuate the new length after we've read the secondary packet. */ - length = smb_len(inbuf) + 4; - - /* - * The sequence number for the trans reply is always - * based on the last secondary received. - */ + int outsize; - srv_signing_trans_start(SVAL(inbuf,smb_mid)); - - if ((ret && - (CVAL(inbuf, smb_com) != SMBtranss2)) || !ret) { - outsize = set_message(outbuf,0,0,True); - if(ret) - DEBUG(0,("reply_trans2: Invalid secondary trans2 packet\n")); - else - DEBUG(0,("reply_trans2: %s in getting secondary trans2 response.\n", - (smb_read_error == READ_ERROR) ? "error" : "timeout" )); - goto bad_param; - } - - /* Revise total_params and total_data in case - they have changed downwards */ - if (SVAL(inbuf, smb_tpscnt) < total_params) - total_params = SVAL(inbuf, smb_tpscnt); - if (SVAL(inbuf, smb_tdscnt) < total_data) - total_data = SVAL(inbuf, smb_tdscnt); - - num_params = SVAL(inbuf,smb_spscnt); - param_off = SVAL(inbuf, smb_spsoff); - param_disp = SVAL(inbuf, smb_spsdisp); - num_params_sofar += num_params; - - num_data = SVAL(inbuf, smb_sdscnt); - data_off = SVAL(inbuf, smb_sdsoff); - data_disp = SVAL(inbuf, smb_sdsdisp); - num_data_sofar += num_data; - - if (num_params_sofar > total_params || num_data_sofar > total_data) - goto bad_param; - - if (num_params) { - if (param_disp + num_params > total_params) - goto bad_param; - if ((param_disp + num_params < param_disp) || - (param_disp + num_params < num_params)) - goto bad_param; - if (param_disp > total_params) - goto bad_param; - if ((smb_base(inbuf) + param_off + num_params > inbuf + length) || - (smb_base(inbuf) + param_off + num_params < smb_base(inbuf))) - goto bad_param; - if (params + param_disp < params) - goto bad_param; - - memcpy( ¶ms[param_disp], smb_base(inbuf) + param_off, num_params); - } - if (num_data) { - if (data_disp + num_data > total_data) - goto bad_param; - if ((data_disp + num_data < data_disp) || - (data_disp + num_data < num_data)) - goto bad_param; - if (data_disp > total_data) - goto bad_param; - if ((smb_base(inbuf) + data_off + num_data > inbuf + length) || - (smb_base(inbuf) + data_off + num_data < smb_base(inbuf))) - goto bad_param; - if (data + data_disp < data) - goto bad_param; - - memcpy( &data[data_disp], smb_base(inbuf) + data_off, num_data); - } - } - } - if (Protocol >= PROTOCOL_NT1) { SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */ } /* Now we must call the relevant TRANS2 function */ - switch(tran_call) { + switch(state->call) { case TRANSACT2_OPEN: START_PROFILE_NESTED(Trans2_open); - outsize = call_trans2open(conn, inbuf, outbuf, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2open( + conn, inbuf, outbuf, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_open); break; case TRANSACT2_FINDFIRST: START_PROFILE_NESTED(Trans2_findfirst); - outsize = call_trans2findfirst(conn, inbuf, outbuf, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findfirst( + conn, inbuf, outbuf, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findfirst); break; case TRANSACT2_FINDNEXT: START_PROFILE_NESTED(Trans2_findnext); - outsize = call_trans2findnext(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findnext( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findnext); break; case TRANSACT2_QFSINFO: START_PROFILE_NESTED(Trans2_qfsinfo); - outsize = call_trans2qfsinfo(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2qfsinfo( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_qfsinfo); break; case TRANSACT2_SETFSINFO: START_PROFILE_NESTED(Trans2_setfsinfo); - outsize = call_trans2setfsinfo(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2setfsinfo( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_setfsinfo); break; case TRANSACT2_QPATHINFO: case TRANSACT2_QFILEINFO: START_PROFILE_NESTED(Trans2_qpathinfo); - outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, length, bufsize, tran_call, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2qfilepathinfo( + conn, inbuf, outbuf, size, bufsize, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_qpathinfo); break; case TRANSACT2_SETPATHINFO: case TRANSACT2_SETFILEINFO: START_PROFILE_NESTED(Trans2_setpathinfo); - outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, length, bufsize, tran_call, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2setfilepathinfo( + conn, inbuf, outbuf, size, bufsize, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_setpathinfo); break; case TRANSACT2_FINDNOTIFYFIRST: START_PROFILE_NESTED(Trans2_findnotifyfirst); - outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findnotifyfirst( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findnotifyfirst); break; case TRANSACT2_FINDNOTIFYNEXT: START_PROFILE_NESTED(Trans2_findnotifynext); - outsize = call_trans2findnotifynext(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findnotifynext( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findnotifynext); break; case TRANSACT2_MKDIR: START_PROFILE_NESTED(Trans2_mkdir); - outsize = call_trans2mkdir(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2mkdir( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_mkdir); break; case TRANSACT2_GET_DFS_REFERRAL: START_PROFILE_NESTED(Trans2_get_dfs_referral); - outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2getdfsreferral( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_get_dfs_referral); break; case TRANSACT2_IOCTL: START_PROFILE_NESTED(Trans2_ioctl); - outsize = call_trans2ioctl(conn,inbuf,outbuf,length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2ioctl( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_ioctl); break; default: /* Error in request */ - DEBUG(2,("Unknown request %d in trans2 call\n", tran_call)); - SAFE_FREE(params); - SAFE_FREE(data); + DEBUG(2,("Unknown request %d in trans2 call\n", state->call)); + outsize = ERROR_DOS(ERRSRV,ERRerror); + } + return outsize; +} + +/**************************************************************************** + Reply to a SMBtrans2. + ****************************************************************************/ + +int reply_trans2(connection_struct *conn, char *inbuf,char *outbuf, + int size, int bufsize) +{ + int outsize = 0; + unsigned int dsoff = SVAL(inbuf, smb_dsoff); + unsigned int dscnt = SVAL(inbuf, smb_dscnt); + unsigned int psoff = SVAL(inbuf, smb_psoff); + unsigned int pscnt = SVAL(inbuf, smb_pscnt); + unsigned int tran_call = SVAL(inbuf, smb_setup0); + struct trans_state *state; + NTSTATUS result; + + START_PROFILE(SMBtrans2); + + if (!NT_STATUS_IS_OK(allow_new_trans(conn->pending_trans, + SVAL(inbuf, smb_mid)))) { + DEBUG(2, ("Got invalid trans2 request: %s\n", + nt_errstr(result))); END_PROFILE(SMBtrans2); - srv_signing_trans_stop(); - return ERROR_DOS(ERRSRV,ERRerror); + return ERROR_NT(result); } - - /* As we do not know how many data packets will need to be - returned here the various call_trans2xxxx calls - must send their own. Thus a call_trans2xxx routine only - returns a value other than -1 when it wants to send - an error packet. - */ - - srv_signing_trans_stop(); - SAFE_FREE(params); - SAFE_FREE(data); + if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) + && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) { + END_PROFILE(SMBtrans2); + return ERROR_DOS(ERRSRV,ERRaccess); + } + + if ((state = TALLOC_P(NULL, struct trans_state)) == NULL) { + DEBUG(0, ("talloc failed\n")); + END_PROFILE(SMBtrans2); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + state->cmd = SMBtrans2; + + state->mid = SVAL(inbuf, smb_mid); + state->vuid = SVAL(inbuf, smb_uid); + state->setup_count = SVAL(inbuf, smb_suwcnt); + state->total_param = SVAL(inbuf, smb_tpscnt); + state->param = NULL; + state->total_data = SVAL(inbuf, smb_tdscnt); + state->data = NULL; + state->max_param_return = SVAL(inbuf, smb_mprcnt); + state->max_data_return = SVAL(inbuf, smb_mdrcnt); + state->max_setup_return = SVAL(inbuf, smb_msrcnt); + state->close_on_completion = BITSETW(inbuf+smb_vwv5,0); + state->one_way = BITSETW(inbuf+smb_vwv5,1); + + state->call = tran_call; + + /* All trans2 messages we handle have smb_sucnt == 1 - ensure this + is so as a sanity check */ + if (state->setup_count != 1) { + /* + * Need to have rc=0 for ioctl to get job id for OS/2. + * Network printing will fail if function is not successful. + * Similar function in reply.c will be used if protocol + * is LANMAN1.0 instead of LM1.2X002. + * Until DosPrintSetJobInfo with PRJINFO3 is supported, + * outbuf doesn't have to be set(only job id is used). + */ + if ( (state->setup_count == 4) && (tran_call == TRANSACT2_IOCTL) && + (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) && + (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) { + DEBUG(2,("Got Trans2 DevIOctl jobid\n")); + } else { + DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",state->setup_count)); + DEBUG(2,("Transaction is %d\n",tran_call)); + END_PROFILE(SMBtrans2); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + } + + if ((dscnt > state->total_data) || (pscnt > state->total_param)) + goto bad_param; + + if (state->total_data) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->data = SMB_MALLOC(state->total_data); + if (state->data == NULL) { + DEBUG(0,("reply_trans2: data malloc fail for %u " + "bytes !\n", state->total_data)); + TALLOC_FREE(state); + END_PROFILE(SMBtrans2); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt)) + goto bad_param; + if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) || + (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) + goto bad_param; + + memcpy(state->data,smb_base(inbuf)+dsoff,dscnt); + } + + if (state->total_param) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->param = SMB_MALLOC(state->total_param); + if (state->param == NULL) { + DEBUG(0,("reply_trans: param malloc fail for %u " + "bytes !\n", state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt)) + goto bad_param; + if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) || + (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) + goto bad_param; + + memcpy(state->param,smb_base(inbuf)+psoff,pscnt); + } + + state->received_data = dscnt; + state->received_param = pscnt; + + if ((state->received_param == state->total_param) && + (state->received_data == state->total_data)) { + + outsize = handle_trans2(conn, state, inbuf, outbuf, + size, bufsize); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return outsize; + } + + DLIST_ADD(conn->pending_trans, state); + + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); END_PROFILE(SMBtrans2); - return outsize; /* If a correct response was needed the - call_trans2xxx calls have already sent - it. If outsize != -1 then it is returning */ + return outsize; bad_param: - srv_signing_trans_stop(); - SAFE_FREE(params); - SAFE_FREE(data); + DEBUG(0,("reply_trans2: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); END_PROFILE(SMBtrans2); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } + + +/**************************************************************************** + Reply to a SMBtranss2 + ****************************************************************************/ + +int reply_transs2(connection_struct *conn, + char *inbuf,char *outbuf,int size,int bufsize) +{ + int outsize = 0; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; + + START_PROFILE(SMBtranss2); + + show_msg(inbuf); + + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == SVAL(inbuf,smb_mid)) { + break; + } + } + + if ((state == NULL) || (state->cmd != SMBtrans2)) { + END_PROFILE(SMBtranss2); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + /* Revise state->total_param and state->total_data in case they have + changed downwards */ + + if (SVAL(inbuf, smb_tpscnt) < state->total_param) + state->total_param = SVAL(inbuf, smb_tpscnt); + if (SVAL(inbuf, smb_tdscnt) < state->total_data) + state->total_data = SVAL(inbuf, smb_tdscnt); + + pcnt = SVAL(inbuf, smb_spscnt); + poff = SVAL(inbuf, smb_spsoff); + pdisp = SVAL(inbuf, smb_spsdisp); + + dcnt = SVAL(inbuf, smb_sdscnt); + doff = SVAL(inbuf, smb_sdsoff); + ddisp = SVAL(inbuf, smb_sdsdisp); + + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; + + if (pcnt) { + if (pdisp+pcnt > state->total_param) + goto bad_param; + if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) + goto bad_param; + if (pdisp > state->total_param) + goto bad_param; + if ((smb_base(inbuf) + poff + pcnt > inbuf + size) || + (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) + goto bad_param; + if (state->param + pdisp < state->param) + goto bad_param; + + memcpy(state->param+pdisp,smb_base(inbuf)+poff, + pcnt); + } + + if (dcnt) { + if (ddisp+dcnt > state->total_data) + goto bad_param; + if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) + goto bad_param; + if (ddisp > state->total_data) + goto bad_param; + if ((smb_base(inbuf) + doff + dcnt > inbuf + size) || + (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) + goto bad_param; + if (state->data + ddisp < state->data) + goto bad_param; + + memcpy(state->data+ddisp, smb_base(inbuf)+doff, + dcnt); + } + + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBtranss); + return -1; + } + + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBtranss2 which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBtrans2); + + outsize = handle_trans2(conn, state, inbuf, outbuf, size, bufsize); + + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + + if (outsize == 0) { + END_PROFILE(SMBtranss); + return(ERROR_DOS(ERRSRV,ERRnosupport)); + } + + END_PROFILE(SMBtranss2); + return(outsize); + + bad_param: + + DEBUG(0,("reply_transs2: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtranss2); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); +} diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c index bbb7b5bb308..55bf146c20f 100644 --- a/source3/smbd/vfs-wrap.c +++ b/source3/smbd/vfs-wrap.c @@ -759,11 +759,21 @@ BOOL vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, BOOL result; START_PROFILE(syscall_fcntl_lock); - result = fcntl_lock(fd, op, offset, count,type); + result = fcntl_lock(fd, op, offset, count, type); END_PROFILE(syscall_fcntl_lock); return result; } +BOOL vfswrap_getlock(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid) +{ + BOOL result; + + START_PROFILE(syscall_fcntl_getlock); + result = fcntl_getlock(fd, poffset, pcount, ptype, ppid); + END_PROFILE(syscall_fcntl_getlock); + return result; +} + int vfswrap_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath) { int result; diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 07e18caa5c6..9a6327b33b5 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -95,6 +95,7 @@ static struct vfs_ops default_vfs = { vfswrap_utime, vfswrap_ftruncate, vfswrap_lock, + vfswrap_getlock, vfswrap_symlink, vfswrap_readlink, vfswrap_link, diff --git a/source3/tests/os2_delete.c b/source3/tests/os2_delete.c index 831fa367eb4..b3aaf67f418 100644 --- a/source3/tests/os2_delete.c +++ b/source3/tests/os2_delete.c @@ -105,3 +105,110 @@ int main(void) return 0; } +/* + test readdir/unlink pattern that OS/2 uses + tridge@samba.org July 2005 +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> + +#define NUM_FILES 700 +#define READDIR_SIZE 100 +#define DELETE_SIZE 4 + +#define TESTDIR "test.dir" + +#define FAILED(d) (fprintf(stderr, "Failed for %s - %s\n", d, strerror(errno)), exit(1), 1) + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +static void cleanup(void) +{ + /* I'm a lazy bastard */ + system("rm -rf " TESTDIR); + mkdir(TESTDIR, 0700) == 0 || FAILED("mkdir"); +} + +static void create_files() +{ + int i; + for (i=0;i<NUM_FILES;i++) { + char fname[40]; + sprintf(fname, TESTDIR "/test%u.txt", i); + close(open(fname, O_CREAT|O_RDWR, 0600)) == 0 || FAILED("close"); + } +} + +static int os2_delete(DIR *d) +{ + off_t offsets[READDIR_SIZE]; + int i, j; + struct dirent *de; + char names[READDIR_SIZE][30]; + + /* scan, remembering offsets */ + for (i=0, de=readdir(d); + de && i < READDIR_SIZE; + de=readdir(d), i++) { + offsets[i] = telldir(d); + strcpy(names[i], de->d_name); + } + + if (i == 0) { + return 0; + } + + /* delete the first few */ + for (j=0; j<MIN(i, DELETE_SIZE); j++) { + char fname[40]; + sprintf(fname, TESTDIR "/%s", names[j]); + unlink(fname) == 0 || FAILED("unlink"); + } + + /* seek to just after the deletion */ + seekdir(d, offsets[j-1]); + + /* return number deleted */ + return j; +} + +int main(void) +{ + int total_deleted = 0; + DIR *d; + struct dirent *de; + + cleanup(); + create_files(); + + d = opendir(TESTDIR); + + /* skip past . and .. */ + de = readdir(d); + strcmp(de->d_name, ".") == 0 || FAILED("match ."); + de = readdir(d); + strcmp(de->d_name, "..") == 0 || FAILED("match .."); + + while (1) { + int n = os2_delete(d); + if (n == 0) break; + total_deleted += n; + } + closedir(d); + + printf("Deleted %d files of %d\n", total_deleted, NUM_FILES); + + rmdir(TESTDIR) == 0 || FAILED("rmdir"); + + return 0; +} diff --git a/source3/utils/net_sam.c b/source3/utils/net_sam.c index fc7dfea02c5..945afb3a210 100644 --- a/source3/utils/net_sam.c +++ b/source3/utils/net_sam.c @@ -1203,6 +1203,7 @@ failed: talloc_free(tc); return -1; } + #endif /*********************************************************** diff --git a/source3/utils/status.c b/source3/utils/status.c index c334fe6982d..bc26be1ec93 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -159,9 +159,13 @@ static void print_share_mode(const struct share_mode_entry *e, const char *share } } -static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, struct process_id pid, - enum brl_type lock_type, - br_off start, br_off size) +static void print_brl(SMB_DEV_T dev, + SMB_INO_T ino, + struct process_id pid, + enum brl_type lock_type, + enum brl_flavour lock_flav, + br_off start, + br_off size) { static int count; if (count==0) { |