diff options
30 files changed, 3220 insertions, 1629 deletions
diff --git a/examples/VFS/Makefile.in b/examples/VFS/Makefile.in index 79873c3857..caf8f030aa 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 e6af475da6..a02bf3c146 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 14fa2276e1..5996b29806 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 8faf3877ce..9531ae2903 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 ed6fce9a6d..f68a1e240f 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 1d5dfe3678..92c5a2e963 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 cde2039d1a..fb99d82412 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 33810c301f..e08b386a6a 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 bfc5eb2a8d..87f15b8759 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 443f515665..80deb3a332 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 8296f7e94c..082da67bb8 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 f6b5af068a..b3caa0a80c 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 52e4b1d04c..4ff74ca464 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 5078515b3e..b95bb895cc 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 0ecc90c794..0b3f625d03 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 07246474f3..e7075c57a6 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 0ae48f4818..309f6d15ae 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 e0c6c0686f..af4293a11e 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 805e45f6ea..6b47d0466b 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 427b6ae214..1b5a5f39c7 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 796eb44332..24d64ecfc7 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 40d26f7672..cdeccab5e8 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 ce41266a1c..040f7710fd 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 ba31827eb3..0880778f55 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 4f5039e86c..19463403ac 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 bbb7b5bb30..55bf146c20 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 07e18caa5c..9a6327b33b 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 831fa367eb..b3aaf67f41 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 fc7dfea02c..945afb3a21 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 c334fe6982..bc26be1ec9 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) { |