summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/.cvsignore2
-rw-r--r--source3/smbd/blocking.c635
-rw-r--r--source3/smbd/build_options.c536
-rw-r--r--source3/smbd/change_trust_pw.c151
-rw-r--r--source3/smbd/chgpasswd.c1157
-rw-r--r--source3/smbd/close.c274
-rw-r--r--source3/smbd/conn.c224
-rw-r--r--source3/smbd/connection.c189
-rw-r--r--source3/smbd/dfree.c164
-rw-r--r--source3/smbd/dir.c1226
-rw-r--r--source3/smbd/dosmode.c337
-rw-r--r--source3/smbd/error.c129
-rw-r--r--source3/smbd/fileio.c659
-rw-r--r--source3/smbd/filename.c503
-rw-r--r--source3/smbd/files.c400
-rw-r--r--source3/smbd/groupname.c238
-rw-r--r--source3/smbd/ipc.c3013
-rw-r--r--source3/smbd/lanman.c3650
-rw-r--r--source3/smbd/mangle.c629
-rw-r--r--source3/smbd/mangle_hash.c775
-rw-r--r--source3/smbd/mangle_hash2.c587
-rw-r--r--source3/smbd/mangle_map.c203
-rw-r--r--source3/smbd/message.c156
-rw-r--r--source3/smbd/negprot.c509
-rw-r--r--source3/smbd/noquotas.c38
-rw-r--r--source3/smbd/notify.c220
-rw-r--r--source3/smbd/notify_hash.c225
-rw-r--r--source3/smbd/notify_kernel.c206
-rw-r--r--source3/smbd/nttrans.c1818
-rw-r--r--source3/smbd/open.c1328
-rw-r--r--source3/smbd/oplock.c1219
-rw-r--r--source3/smbd/oplock_irix.c285
-rw-r--r--source3/smbd/oplock_linux.c300
-rw-r--r--source3/smbd/password.c1703
-rw-r--r--source3/smbd/pipes.c264
-rw-r--r--source3/smbd/posix_acls.c2313
-rw-r--r--source3/smbd/process.c1301
-rw-r--r--source3/smbd/quotas.c1097
-rw-r--r--source3/smbd/reply.c5397
-rw-r--r--source3/smbd/sec_ctx.c424
-rw-r--r--source3/smbd/server.c4690
-rw-r--r--source3/smbd/service.c738
-rw-r--r--source3/smbd/session.c178
-rw-r--r--source3/smbd/sesssetup.c808
-rw-r--r--source3/smbd/smbrun.c96
-rw-r--r--source3/smbd/srvstr.c32
-rw-r--r--source3/smbd/ssl.c286
-rw-r--r--source3/smbd/statcache.c230
-rw-r--r--source3/smbd/trans2.c3352
-rw-r--r--source3/smbd/uid.c709
-rw-r--r--source3/smbd/utmp.c606
-rw-r--r--source3/smbd/vfs-wrap.c726
-rw-r--r--source3/smbd/vfs.c802
-rw-r--r--source3/smbd/vt_mode.c496
54 files changed, 34919 insertions, 13314 deletions
diff --git a/source3/smbd/.cvsignore b/source3/smbd/.cvsignore
new file mode 100644
index 0000000000..5f2a5c4cf7
--- /dev/null
+++ b/source3/smbd/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c
new file mode 100644
index 0000000000..d4a53d9a6d
--- /dev/null
+++ b/source3/smbd/blocking.c
@@ -0,0 +1,635 @@
+/*
+ Unix SMB/CIFS implementation.
+ Blocking Locking functions
+ Copyright (C) Jeremy Allison 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern char *OutBuffer;
+
+/****************************************************************************
+ This is the structure to queue to implement blocking locks.
+ notify. It consists of the requesting SMB and the expiry time.
+*****************************************************************************/
+
+typedef struct {
+ ubi_slNode msg_next;
+ int com_type;
+ files_struct *fsp;
+ time_t expire_time;
+ int lock_num;
+ char *inbuf;
+ int length;
+} blocking_lock_record;
+
+static ubi_slList blocking_lock_queue = { NULL, (ubi_slNodePtr)&blocking_lock_queue, 0};
+
+/****************************************************************************
+ Destructor for the above structure.
+****************************************************************************/
+
+static void free_blocking_lock_record(blocking_lock_record *blr)
+{
+ SAFE_FREE(blr->inbuf);
+ SAFE_FREE(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.
+ **************************************************************************/
+
+static BOOL in_chained_smb(void)
+{
+ return (chain_size != 0);
+}
+
+/****************************************************************************
+ 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)
+{
+ blocking_lock_record *blr;
+
+ if(in_chained_smb() ) {
+ DEBUG(0,("push_blocking_lock_request: cannot queue a chained request (currently).\n"));
+ return False;
+ }
+
+ /*
+ * Now queue an entry on the blocking lock queue. We setup
+ * the expiration time here.
+ */
+
+ if((blr = (blocking_lock_record *)malloc(sizeof(blocking_lock_record))) == NULL) {
+ DEBUG(0,("push_blocking_lock_request: Malloc fail !\n" ));
+ return False;
+ }
+
+ if((blr->inbuf = (char *)malloc(length)) == NULL) {
+ DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" ));
+ SAFE_FREE(blr);
+ return False;
+ }
+
+ blr->com_type = CVAL(inbuf,smb_com);
+ blr->fsp = get_fsp_from_pkt(inbuf);
+ blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout;
+ blr->lock_num = lock_num;
+ memcpy(blr->inbuf, inbuf, length);
+ blr->length = length;
+
+ ubi_slAddTail(&blocking_lock_queue, blr);
+
+
+ DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d (+%d) \
+for fnum = %d, name = %s\n", length, (int)blr->expire_time, lock_timeout,
+ blr->fsp->fnum, blr->fsp->fsp_name ));
+
+ return True;
+}
+
+/****************************************************************************
+ Return a smd with a given size.
+*****************************************************************************/
+
+static void send_blocking_reply(char *outbuf, int outsize)
+{
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_blocking_reply: send_smb failed.");
+}
+
+/****************************************************************************
+ Return a lockingX success SMB.
+*****************************************************************************/
+
+static void reply_lockingX_success(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ int bufsize = BUFFER_SIZE;
+ char *inbuf = blr->inbuf;
+ int outsize = 0;
+
+ construct_reply_common(inbuf, outbuf);
+ set_message(outbuf,2,0,True);
+
+ /*
+ * As this message is a lockingX call we must handle
+ * any following chained message correctly.
+ * This is normally handled in construct_reply(),
+ * but as that calls switch_message, we can't use
+ * that here and must set up the chain info manually.
+ */
+
+ outsize = chain_reply(inbuf,outbuf,blr->length,bufsize);
+
+ outsize += chain_size;
+
+ send_blocking_reply(outbuf,outsize);
+}
+
+/****************************************************************************
+ Return a generic lock fail error blocking call.
+*****************************************************************************/
+
+static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ construct_reply_common(inbuf, outbuf);
+
+ /* whenever a timeout is given w2k maps LOCK_NOT_GRANTED to
+ FILE_LOCK_CONFLICT! (tridge) */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ ERROR_NT(status);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("generic_blocking_lock_error: send_smb failed.");
+}
+
+/****************************************************************************
+ Return a lock fail error for a lockingX call. Undo all the locks we have
+ obtained first.
+*****************************************************************************/
+
+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;
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
+ char *data;
+ int i;
+
+ data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /*
+ * Data now points at the beginning of the list
+ * of smb_lkrng structs.
+ */
+
+ /*
+ * Ensure we don't do a remove on the lock that just failed,
+ * as under POSIX rules, if we have a lock already there, we
+ * will delete it (and we shouldn't) .....
+ */
+
+ for(i = blr->lock_num - 1; i >= 0; i--) {
+ BOOL err;
+
+ 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);
+
+ /*
+ * We know err cannot be set as if it was the lock
+ * request would never have been queued. JRA.
+ */
+
+ do_unlock(fsp,conn,lock_pid,count,offset);
+ }
+
+ generic_blocking_lock_error(blr, status);
+}
+
+/****************************************************************************
+ Return a lock fail error.
+*****************************************************************************/
+
+static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ switch(blr->com_type) {
+ case SMBlock:
+ case SMBlockread:
+ generic_blocking_lock_error(blr, status);
+ break;
+ case SMBlockingX:
+ reply_lockingX_error(blr, status);
+ 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");
+ }
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lockread call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lockread(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ ssize_t nread = -1;
+ char *data, *p;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t numtoread;
+ NTSTATUS status;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ files_struct *fsp = blr->fsp;
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+
+ 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,
+ (SMB_BIG_UINT)startpos, READ_LOCK);
+ if (NT_STATUS_V(status)) {
+ if ((errno != EACCES) && (errno != EAGAIN)) {
+ /*
+ * We have other than a "can't get lock" POSIX
+ * error. Send an error.
+ * Return True so we get dequeued.
+ */
+ generic_blocking_lock_error(blr, status);
+ return True;
+ }
+
+ /*
+ * Still waiting for lock....
+ */
+
+ DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n",
+ fsp->fsp_name));
+ return False;
+ }
+
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ generic_blocking_lock_error(blr,NT_STATUS_ACCESS_DENIED);
+ return True;
+ }
+
+ construct_reply_common(inbuf, outbuf);
+ outsize = set_message(outbuf,5,0,True);
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ p = smb_buf(outbuf);
+ *p++ = 1;
+ SSVAL(p,0,nread); p += 2;
+ set_message_end(outbuf, p+nread);
+
+ DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n",
+ fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) );
+
+ send_blocking_reply(outbuf,outsize);
+ return True;
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lock call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lock(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ int outsize;
+ SMB_OFF_T count = 0, offset = 0;
+ NTSTATUS status;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ files_struct *fsp = blr->fsp;
+
+ count = IVAL(inbuf,smb_vwv1);
+ offset = IVAL(inbuf,smb_vwv3);
+
+ errno = 0;
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)count,
+ (SMB_BIG_UINT)offset, WRITE_LOCK);
+ if (NT_STATUS_IS_ERR(status)) {
+ if((errno != EACCES) && (errno != EAGAIN)) {
+ /*
+ * We have other than a "can't get lock" POSIX
+ * error. Send an error.
+ * Return True so we get dequeued.
+ */
+
+ blocking_lock_reply_error(blr, status);
+ return True;
+ }
+ /*
+ * Still can't get the lock - keep waiting.
+ */
+ DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n",
+ fsp->fsp_name));
+ return False;
+ }
+
+ /*
+ * Success - we got the lock.
+ */
+
+ DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fsp_name, fsp->fnum, (double)offset, (double)count));
+
+ construct_reply_common(inbuf, outbuf);
+ outsize = set_message(outbuf,0,0,True);
+ send_blocking_reply(outbuf,outsize);
+ return True;
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lockingX call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+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;
+ uint16 lock_pid;
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
+ char *data;
+ NTSTATUS status = NT_STATUS_OK;
+
+ data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /*
+ * Data now points at the beginning of the list
+ * of smb_lkrng structs.
+ */
+
+ for(; blr->lock_num < num_locks; blr->lock_num++) {
+ BOOL err;
+
+ lock_pid = get_lock_pid( data, blr->lock_num, large_file_format);
+ count = get_lock_count( data, blr->lock_num, large_file_format);
+ offset = get_lock_offset( data, blr->lock_num, large_file_format, &err);
+
+ /*
+ * We know err cannot be set as if it was the lock
+ * 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));
+ if (NT_STATUS_IS_ERR(status)) break;
+ }
+
+ if(blr->lock_num == num_locks) {
+ /*
+ * Success - we got all the locks.
+ */
+
+ DEBUG(3,("process_lockingX file = %s, fnum=%d type=%d num_locks=%d\n",
+ fsp->fsp_name, fsp->fnum, (unsigned int)locktype, num_locks) );
+
+ reply_lockingX_success(blr);
+ return True;
+ } else if ((errno != EACCES) && (errno != EAGAIN)) {
+ /*
+ * We have other than a "can't get lock" POSIX
+ * error. Free any locks we had and return an error.
+ * Return True so we get dequeued.
+ */
+
+ blocking_lock_reply_error(blr, status);
+ return True;
+ }
+
+ /*
+ * Still can't get all the locks - keep waiting.
+ */
+
+ DEBUG(10,("process_lockingX: only got %d locks of %d needed for file %s, fnum = %d. \
+Waiting....\n",
+ blr->lock_num, num_locks, fsp->fsp_name, fsp->fnum));
+
+ return False;
+}
+
+/****************************************************************************
+ Process a blocking lock SMB.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL blocking_lock_record_process(blocking_lock_record *blr)
+{
+ switch(blr->com_type) {
+ case SMBlock:
+ return process_lock(blr);
+ case SMBlockread:
+ return process_lockread(blr);
+ case SMBlockingX:
+ return process_lockingX(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");
+ }
+ return False; /* Keep compiler happy. */
+}
+
+/****************************************************************************
+ Delete entries by fnum from the blocking lock pending queue.
+*****************************************************************************/
+
+void remove_pending_lock_requests_by_fid(files_struct *fsp)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ while(blr != NULL) {
+ if(blr->fsp->fnum == fsp->fnum) {
+
+ 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 ));
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
+
+/****************************************************************************
+ Delete entries by mid from the blocking lock pending queue. Always send reply.
+*****************************************************************************/
+
+void remove_pending_lock_requests_by_mid(int mid)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ while(blr != NULL) {
+ if(SVAL(blr->inbuf,smb_mid) == mid) {
+ files_struct *fsp = blr->fsp;
+
+ 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 ));
+
+ blocking_lock_reply_error(blr,NT_STATUS_CANCELLED);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
+
+/****************************************************************************
+ Return True if the blocking lock queue has entries.
+*****************************************************************************/
+
+BOOL blocking_locks_pending(void)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ return (blr == NULL ? False : True);
+}
+
+/****************************************************************************
+ Process the blocking lock queue. Note that this is only called as root.
+*****************************************************************************/
+
+void process_blocking_lock_queue(time_t t)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ if(blr == NULL)
+ return;
+
+ /*
+ * Go through the queue and see if we can get any of the locks.
+ */
+
+ while(blr != NULL) {
+ connection_struct *conn = NULL;
+ uint16 vuid;
+ files_struct *fsp = NULL;
+
+ /*
+ * Ensure we don't have any old chain_fsp values
+ * sitting around....
+ */
+ chain_size = 0;
+ file_chain_reset();
+ fsp = blr->fsp;
+
+ conn = conn_find(SVAL(blr->inbuf,smb_tid));
+ vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
+ SVAL(blr->inbuf,smb_uid);
+
+ DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n",
+ fsp->fnum, fsp->fsp_name ));
+
+ if((blr->expire_time != -1) && (blr->expire_time > t)) {
+ /*
+ * 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 ));
+
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ if(!change_to_user(conn,vuid)) {
+ DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n",
+ vuid ));
+ /*
+ * Remove the entry and return an error to the client.
+ */
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ if(!set_current_service(conn,True)) {
+ DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) ));
+ /*
+ * Remove the entry and return an error to the client.
+ */
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ change_to_root_user();
+ continue;
+ }
+
+ /*
+ * Go through the remaining locks and try and obtain them.
+ * The call returns True if all locks were obtained successfully
+ * and False if we still need to wait.
+ */
+
+ if(blocking_lock_record_process(blr)) {
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ change_to_root_user();
+ continue;
+ }
+
+ change_to_root_user();
+
+ /*
+ * Move to the next in the list.
+ */
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
diff --git a/source3/smbd/build_options.c b/source3/smbd/build_options.c
new file mode 100644
index 0000000000..1d18f534b1
--- /dev/null
+++ b/source3/smbd/build_options.c
@@ -0,0 +1,536 @@
+/*
+ Unix SMB/CIFS implementation.
+ Build Options for Samba Suite
+ Copyright (C) Vance Lankhaar <vlankhaar@hotmail.com> 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "build_env.h"
+#include "dynconfig.h"
+
+static void output(BOOL screen, char *format, ...) PRINTF_ATTRIBUTE(2,3);
+
+/*
+#define OUTPUT(x) snprintf(outstring,sizeof(outstring),x); output(screen,outstring);
+*/
+/****************************************************************************
+helper function for build_options
+****************************************************************************/
+static void output(BOOL screen, char *format, ...)
+{
+ char *ptr;
+ va_list ap;
+
+ va_start(ap, format);
+ vasprintf(&ptr,format,ap);
+ va_end(ap);
+
+ if (screen) {
+ d_printf("%s", ptr);
+ } else {
+ DEBUG(4,("%s", ptr));
+ }
+
+ SAFE_FREE(ptr);
+}
+
+/****************************************************************************
+options set at build time for the samba suite
+****************************************************************************/
+void build_options(BOOL screen)
+{
+ if ((DEBUGLEVEL < 4) && (!screen)) {
+ return;
+ }
+
+#ifdef _BUILD_ENV_H
+ /* Output information about the build environment */
+ output(screen,"Build environment:\n");
+ output(screen," Built by: %s@%s\n",BUILD_ENV_USER,BUILD_ENV_HOST);
+ output(screen," Built on: %s\n",BUILD_ENV_DATE);
+
+ output(screen," Built using: %s\n",BUILD_ENV_COMPILER);
+ output(screen," Build host: %s\n",BUILD_ENV_UNAME);
+ output(screen," SRCDIR: %s\n",BUILD_ENV_SRCDIR);
+ output(screen," BUILDDIR: %s\n",BUILD_ENV_BUILDDIR);
+
+
+#endif
+
+ /* Output various options (most correspond to --with options) */
+ output(screen,"\nBuild options:\n");
+#ifdef WITH_SMBWRAPPER
+ output(screen," WITH_SMBWRAPPER\n");
+#endif
+#ifdef WITH_AFS
+ output(screen," WITH_AFS\n");
+#endif
+#ifdef WITH_DFS
+ output(screen," WITH_DFS\n");
+#endif
+#ifdef KRB4_AUTH
+ output(screen," KRB4_AUTH");
+#endif
+#ifdef HAVE_KRB5
+ output(screen," HAVE_KRB5");
+#endif
+#ifdef WITH_AUTOMOUNT
+ output(screen," WITH_AUTOMOUNT\n");
+#endif
+#ifdef WITH_SMBMOUNT
+ output(screen," WITH_SMBMOUNT\n");
+#endif
+#ifdef WITH_PAM
+ output(screen," WITH_PAM\n");
+#endif
+#ifdef WITH_TDB_SAM
+ output(screen," WITH_TDB_SAM\n");
+#endif
+#ifdef WITH_LDAP_SAM
+ output(screen," WITH_LDAP_SAM\n");
+#endif
+#ifdef WITH_SMBPASSWD_SAM
+ output(screen," WITH_SMBPASSWD_SAM\n");
+#endif
+#ifdef WITH_NISPLUS_SAM
+ output(screen," WITH_NISPLUS_SAM\n");
+#endif
+#ifdef WITH_NISPLUS_HOME
+ output(screen," WITH_NISPLUS_HOME\n");
+#endif
+#ifdef WITH_SSL
+ output(screen," WITH_SSL\n");
+#endif
+#ifdef SSL_DIR
+ output(screen," SSL_DIR: %s\n",SSL_DIR);
+#endif
+#ifdef WITH_SYSLOG
+ output(screen," WITH_SYSLOG\n");
+#endif
+#ifdef WITH_PROFILE
+ output(screen," WITH_PROFILE\n");
+#endif
+#ifdef WITH_QUOTAS
+ output(screen," WITH_QUOTAS\n");
+#endif
+#ifdef WITH_VFS
+ output(screen," WITH_VFS\n");
+#endif
+#ifdef USE_SPINLOCKS
+ output(screen," USE_SPINLOCKS\n");
+#endif
+#ifdef SPARC_SPINLOCKS
+ output(screen," SPARC_SPINLOCKS\n");
+#endif
+#ifdef INTEL_SPINLOCKS
+ output(screen," INTEL_SPINLOCKS\n");
+#endif
+#ifdef MIPS_SPINLOCKS
+ output(screen," MIPS_SPINLOCKS\n");
+#endif
+#ifdef POWERPC_SPINLOCKS
+ output(screen," POWERPC_SPINLOCKS\n");
+#endif
+#ifdef HAVE_UNIXWARE_ACLS
+ output(screen," HAVE_UNIXWARE_ACLS\n");
+#endif
+#ifdef HAVE_SOLARIS_ACLS
+ output(screen," HAVE_SOLARIS_ACLS\n");
+#endif
+#ifdef HAVE_IRIX_ACLS
+ output(screen," HAVE_IRIX_ACLS\n");
+#endif
+#ifdef HAVE_AIX_ACLS
+ output(screen," HAVE_AIX_ACLS\n");
+#endif
+#ifdef HAVE_POSIX_ACLS
+ output(screen," HAVE_POSIX_ACLS\n");
+#endif
+#ifdef HAVE_TRU64_ACLS
+ output(screen," HAVE_TRU64_ACLS\n");
+#endif
+
+#ifdef HAVE_ACL_GET_PERM_NP
+ output(screen," HAVE_ACL_GET_PERM_NP\n");
+#endif
+#ifdef HAVE_NO_ACLS
+ output(screen," HAVE_NO_ACLS\n");
+#endif
+#ifdef HAVE_LIBREADLINE
+ output(screen," HAVE_LIBREADLINE\n");
+#endif
+#ifdef WITH_LIBICONV
+ output(screen," WITH_LIBICONV: %s\n",WITH_LIBICONV);
+#endif
+
+
+ /* Output various paths to files and directories */
+ output(screen,"\nPaths:\n");
+ output(screen," CONFIGFILE: %s\n", dyn_CONFIGFILE);
+#ifdef PRIVATE_DIR
+ output(screen," PRIVATE_DIR: %s\n",PRIVATE_DIR);
+#endif
+#ifdef LMHOSTSFILE
+ output(screen," LMHOSTSFILE: %s\n",LMHOSTSFILE);
+#endif
+ output(screen," SBINDIR: %s\n", dyn_SBINDIR);
+ output(screen," BINDIR: %s\n", dyn_BINDIR);
+ output(screen," LOCKDIR: %s\n",dyn_LOCKDIR);
+ output(screen," DRIVERFILE: %s\n", dyn_DRIVERFILE);
+ output(screen," LOGFILEBASE: %s\n", dyn_LOGFILEBASE);
+
+ /*Output various other options (most map to defines in the configure script*/
+ output(screen,"\nOther Build Options:\n");
+#ifdef HAVE_VOLATILE
+ output(screen," HAVE_VOLATILE\n");
+#endif
+#ifdef HAVE_SHADOW_H
+ output(screen," HAVE_SHADOW_H\n");
+#endif
+#ifdef HAVE_CRYPT
+ output(screen," HAVE_CRYPT\n");
+#endif
+#ifdef USE_BOTH_CRYPT_CALLS
+ output(screen," USE_BOTH_CRYPT_CALLS\n");
+#endif
+#ifdef HAVE_TRUNCATED_SALT
+ output(screen," HAVE_TRUNCATED_SALT\n");
+#endif
+#ifdef HAVE_CUPS
+ output(screen," HAVE_CUPS\n");
+#endif
+#ifdef HAVE_CUPS_CUPS_H
+ output(screen," HAVE_CUPS_CUPS_H\n");
+#endif
+#ifdef HAVE_CUPS_LANGUAGE_H
+ output(screen," HAVE_CUPS_LANGUAGE_H\n");
+#endif
+#ifdef HAVE_LIBDL
+ output(screen," HAVE_LIBDL\n");
+#endif
+#ifdef HAVE_UNIXSOCKET
+ output(screen," HAVE_UNIXSOCKET\n");
+#endif
+#ifdef HAVE_SOCKLEN_T_TYPE
+ output(screen," HAVE_SOCKLEN_T_TYPE\n");
+#endif
+#ifdef HAVE_SIG_ATOMIC_T_TYPE
+ output(screen," HAVE_SIG_ATOMIC_T_TYPE\n");
+#endif
+#ifdef HAVE_SETRESUID
+ output(screen," HAVE_SETRESUID\n");
+#endif
+#ifdef HAVE_SETRESGID
+ output(screen," HAVE_SETRESGID\n");
+#endif
+#ifdef HAVE_CONNECT
+ output(screen," HAVE_CONNECT\n");
+#endif
+#ifdef HAVE_YP_GET_DEFAULT_DOMAIN
+ output(screen," HAVE_YP_GET_DEFAULT_DOMAIN\n");
+#endif
+#ifdef HAVE_STAT64
+ output(screen," HAVE_STAT64\n");
+#endif
+#ifdef HAVE_LSTAT64
+ output(screen," HAVE_LSTAT64\n");
+#endif
+#ifdef HAVE_FSTAT64
+ output(screen," HAVE_FSTAT64\n");
+#endif
+#ifdef HAVE_STRCASECMP
+ output(screen," HAVE_STRCASECMP\n");
+#endif
+#ifdef HAVE_MEMSET
+ output(screen," HAVE_MEMSET\n");
+#endif
+#ifdef HAVE_LONGLONG
+ output(screen," HAVE_LONGLONG\n");
+#endif
+#ifdef COMPILER_SUPPORTS_LL
+ output(screen," COMPILER_SUPPORTS_LL\n");
+#endif
+#ifdef SIZEOF_OFF_T
+ output(screen," SIZEOF_OFF_T: %d\n",SIZEOF_OFF_T);
+#endif
+#ifdef HAVE_OFF64_T
+ output(screen," HAVE_OFF64_T\n");
+#endif
+#ifdef SIZEOF_INO_T
+ output(screen," SIZEOF_INO_T: %d\n",SIZEOF_INO_T);
+#endif
+#ifdef HAVE_INO64_T
+ output(screen," HAVE_INO64_T\n");
+#endif
+#ifdef HAVE_STRUCT_DIRENT64
+ output(screen," HAVE_STRUCT_DIRENT64\n");
+#endif
+#ifdef HAVE_UNSIGNED_CHAR
+ output(screen," HAVE_UNSIGNED_CHAR\n");
+#endif
+#ifdef HAVE_SOCK_SIN_LEN
+ output(screen," HAVE_SOCK_SIN_LEN\n");
+#endif
+#ifdef SEEKDIR_RETURNS_VOID
+ output(screen," SEEKDIR_RETURNS_VOID\n");
+#endif
+#ifdef HAVE_FILE_MACRO
+ output(screen," HAVE_FILE_MACRO\n");
+#endif
+#ifdef HAVE_FUNCTION_MACRO
+ output(screen," HAVE_FUNCTION_MACRO\n");
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+ output(screen," HAVE_GETTIMEOFDAY\n");
+#endif
+#ifdef HAVE_C99_VSNPRINTF
+ output(screen," HAVE_C99_VSNPRINTF\n");
+#endif
+#ifdef HAVE_BROKEN_READDIR
+ output(screen," HAVE_BROKEN_READDIR\n");
+#endif
+#ifdef HAVE_NATIVE_ICONV
+ output(screen," HAVE_NATIVE_ICONV\n");
+#endif
+#ifdef HAVE_KERNEL_OPLOCKS_LINUX
+ output(screen," HAVE_KERNEL_OPLOCKS_LINUX\n");
+#endif
+#ifdef HAVE_KERNEL_CHANGE_NOTIFY
+ output(screen," HAVE_KERNEL_CHANGE_NOTIFY\n");
+#endif
+#ifdef HAVE_KERNEL_SHARE_MODES
+ output(screen," HAVE_KERNEL_SHARE_MODES\n");
+#endif
+#ifdef HAVE_KERNEL_OPLOCKS_IRIX
+ output(screen," HAVE_KERNEL_OPLOCKS_IRIX\n");
+#endif
+#ifdef HAVE_IRIX_SPECIFIC_CAPABILITIES
+ output(screen," HAVE_IRIX_SPECIFIC_CAPABILITIES\n");
+#endif
+#ifdef HAVE_INT16_FROM_RPC_RPC_H
+ output(screen," HAVE_INT16_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_UINT16_FROM_RPC_RPC_H
+ output(screen," HAVE_UINT16_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_INT32_FROM_RPC_RPC_H
+ output(screen," HAVE_INT16_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_UINT32_FROM_RPC_RPC_H
+ output(screen," HAVE_UINT32_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_RPC_AUTH_ERROR_CONFLICT
+ output(screen," HAVE_RPC_AUTH_ERROR_CONFLICT\n");
+#endif
+#ifdef HAVE_FTRUNCATE_EXTEND
+ output(screen," HAVE_FTRUNCATE_EXTEND\n");
+#endif
+#ifdef HAVE_WORKING_AF_LOCAL
+ output(screen," HAVE_WORKING_AF_LOCAL\n");
+#endif
+#ifdef HAVE_BROKEN_GETGROUPS
+ output(screen," HAVE_BROKEN_GETGROUPS\n");
+#endif
+#ifdef REPLACE_GETPASS
+ output(screen," REPLACE_GETPASS\n");
+#endif
+#ifdef REPLACE_INET_NTOA
+ output(screen," REPLACE_INET_NTOA\n");
+#endif
+#ifdef HAVE_SECURE_MKSTEMP
+ output(screen," HAVE_SECURE_MKSTEMP\n");
+#endif
+#ifdef SYSCONF_SC_NGROUPS_MAX
+ output(screen," SYSCONF_SC_NGROUPS_MAX\n");
+#endif
+#ifdef HAVE_IFACE_AIX
+ output(screen," HAVE_IFACE_AIX\n");
+#endif
+#ifdef HAVE_IFACE_IFCONF
+ output(screen," HAVE_IFACE_IFCONF\n");
+#endif
+#ifdef HAVE_IFACE_IFREQ
+ output(screen," HAVE_IFACE_IFREQ\n");
+#endif
+#ifdef USE_SETRESUID
+ output(screen," USE_SETRESUID\n");
+#endif
+#ifdef USE_SETRESGID
+ output(screen," USE_SETREUID\n");
+#endif
+#ifdef USE_SETEUID
+ output(screen," USE_SETEUID\n");
+#endif
+#ifdef USE_SETUIDX
+ output(screen," USE_SETUIDX\n");
+#endif
+#ifdef HAVE_MMAP
+ output(screen," HAVE_MMAP\n");
+#endif
+#ifdef MMAP_BLACKLIST
+ output(screen," MMAP_BLACKLIST\n");
+#endif
+#ifdef FTRUNCATE_NEEDS_ROOT
+ output(screen," FTRUNCATE_NEEDS_ROOT\n");
+#endif
+#ifdef HAVE_FCNTL_LOCK
+ output(screen," HAVE_FCNTL_LOCK\n");
+#endif
+#ifdef HAVE_BROKEN_FCNTL64_LOCKS
+ output(screen," HAVE_BROKEN_FCNTL64_LOCKS\n");
+#endif
+#ifdef HAVE_STRUCT_FLOCK64
+ output(screen," HAVE_STRUCT_FLOCK64\n");
+#endif
+#ifdef BROKEN_NISPLUS_INCLUDE_FILES
+ output(screen," BROKEN_NISPLUS_INCLUDE_FILES\n");
+#endif
+#ifdef HAVE_LIBPAM
+ output(screen," HAVE_LIBPAM\n");
+#endif
+#ifdef STAT_STATVFS64
+ output(screen," STAT_STATVFS64\n");
+#endif
+#ifdef STAT_STATVFS
+ output(screen," STAT_STATVFS\n");
+#endif
+#ifdef STAT_STATFS3_OSF1
+ output(screen," STAT_STATFS3_OSF1\n");
+#endif
+#ifdef STAT_STATFS2_BSIZE
+ output(screen," STAT_STATFS2_BSIZE\n");
+#endif
+#ifdef STAT_STATFS4
+ output(screen," STAT_STATFS4\n");
+#endif
+#ifdef STAT_STATFS2_FSIZE
+ output(screen," STAT_STATFS2_FSIZE\n");
+#endif
+#ifdef STAT_STATFS2_FS_DATA
+ output(screen," STAT_STATFS2_FS_DATA\n");
+#endif
+#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT
+ output(screen," HAVE_EXPLICIT_LARGEFILE_SUPPORT\n");
+#endif
+
+#ifdef WITH_UTMP
+ /* Output UTMP Stuff */
+ output(screen,"\nUTMP Related:\n");
+ output(screen," WITH_UTMP\n");
+
+#ifdef HAVE_UTIMBUF
+ output(screen," HAVE_UTIMBUF\n");
+#endif
+#ifdef HAVE_UT_UT_NAME
+ output(screen," HAVE_UT_UT_NAME\n");
+#endif
+#ifdef HAVE_UT_UT_USER
+ output(screen," HAVE_UT_UT_USER\n");
+#endif
+#ifdef HAVE_UT_UT_ID
+ output(screen," HAVE_UT_UT_ID\n");
+#endif
+#ifdef HAVE_UT_UT_HOST
+ output(screen," HAVE_UT_UT_HOST\n");
+#endif
+#ifdef HAVE_UT_UT_TIME
+ output(screen," HAVE_UT_UT_TIME\n");
+#endif
+#ifdef HAVE_UT_UT_TV
+ output(screen," HAVE_UT_UT_TV\n");
+#endif
+#ifdef HAVE_UT_UT_TYPE
+ output(screen," HAVE_UT_UT_TYPE\n");
+#endif
+#ifdef HAVE_UT_UT_PID
+ output(screen," HAVE_UT_UT_PID\n");
+#endif
+#ifdef HAVE_UT_UT_EXIT
+ output(screen," HAVE_UT_UT_EXIT\n");
+#endif
+#ifdef HAVE_UT_UT_ADDR
+ output(screen," HAVE_UT_UT_ADDR\n");
+#endif
+#ifdef PUTUTLINE_RETURNS_UTMP
+ output(screen," PUTUTLINE_RETURNS_UTMP\n");
+#endif
+#ifdef HAVE_UX_UT_SYSLEN
+ output(screen," HAVE_UX_UT_SYSLEN\n");
+#endif
+#endif /* WITH_UTMP */
+
+ /* Output Build OS */
+ output(screen,"\nBuilt for host os:\n");
+#ifdef LINUX
+ output(screen," LINUX\n");
+#endif
+#ifdef SUNOS5
+ output(screen," SUNOS5\n");
+#endif
+#ifdef SUNOS4
+ output(screen," SUNOS4\n");
+#endif
+ /* BSD Isn't Defined in the configure script, but there is something about it in include/config.h.in (and I guess acconfig.h) */
+#ifdef BSD
+ output(screen," BSD\n");
+#endif
+#ifdef IRIX
+ output(screen," IRIX\n");
+#endif
+#ifdef IRIX6
+ output(screen," IRIX6\n");
+#endif
+#ifdef AIX
+ output(screen," AIX\n");
+#endif
+#ifdef HPUX
+ output(screen," HPUX\n");
+#endif
+#ifdef QNX
+ output(screen," QNX\n");
+#endif
+#ifdef OSF1
+ output(screen," OSF1\n");
+#endif
+#ifdef SCO
+ output(screen," SCO\n");
+#endif
+#ifdef UNIXWARE
+ output(screen," UNIXWARE\n");
+#endif
+#ifdef NEXT2
+ output(screen," NEXT2\n");
+#endif
+#ifdef RELIANTUNIX
+ output(screen," RELIANTUNIX\n");
+#endif
+
+ /* Output the sizes of the various types */
+ output(screen,"\nType sizes:\n");
+ output(screen," sizeof(char): %d\n",sizeof(char));
+ output(screen," sizeof(int): %d\n",sizeof(int));
+ output(screen," sizeof(long): %d\n",sizeof(long));
+ output(screen," sizeof(uint8): %d\n",sizeof(uint8));
+ output(screen," sizeof(uint16): %d\n",sizeof(uint16));
+ output(screen," sizeof(uint32): %d\n",sizeof(uint32));
+ output(screen," sizeof(short): %d\n",sizeof(short));
+ output(screen," sizeof(void*): %d\n",sizeof(void*));
+}
+
+
+
diff --git a/source3/smbd/change_trust_pw.c b/source3/smbd/change_trust_pw.c
new file mode 100644
index 0000000000..0d80d5718f
--- /dev/null
+++ b/source3/smbd/change_trust_pw.c
@@ -0,0 +1,151 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Periodic Trust account password changing.
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1998.
+ * Copyright (C) Andrew Bartlett 2001.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+
+extern pstring global_myname;
+
+/*********************************************************
+ Change the domain password on the PDC.
+**********************************************************/
+
+static NTSTATUS modify_trust_password( char *domain, char *remote_machine,
+ unsigned char orig_trust_passwd_hash[16])
+{
+ struct cli_state *cli;
+ DOM_SID domain_sid;
+ struct in_addr dest_ip;
+ NTSTATUS nt_status;
+
+ /*
+ * Ensure we have the domain SID for this domain.
+ */
+
+ if (!secrets_fetch_domain_sid(domain, &domain_sid)) {
+ DEBUG(0, ("domain_client_validate: unable to fetch domain sid.\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if(!resolve_name( remote_machine, &dest_ip, 0x20)) {
+ DEBUG(0,("modify_trust_password: Can't resolve address for %s\n", remote_machine));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(cli_full_connection(&cli, global_myname, remote_machine,
+ &dest_ip, 0,
+ "IPC$", "IPC",
+ "", "",
+ "", 0))) {
+ DEBUG(0,("modify_trust_password: Connection to %s failed!\n", remote_machine));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * Ok - we have an anonymous connection to the IPC$ share.
+ * Now start the NT Domain stuff :-).
+ */
+
+ if(cli_nt_session_open(cli, PIPE_NETLOGON) == False) {
+ DEBUG(0,("modify_trust_password: unable to open the domain client session to \
+machine %s. Error was : %s.\n", remote_machine, cli_errstr(cli)));
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ nt_status = trust_pw_change_and_store_it(cli, cli->mem_ctx,
+ orig_trust_passwd_hash);
+
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ return nt_status;
+}
+
+/************************************************************************
+ Change the trust account password for a domain.
+************************************************************************/
+
+NTSTATUS change_trust_account_password( char *domain, char *remote_machine_list)
+{
+ fstring remote_machine;
+ unsigned char old_trust_passwd_hash[16];
+ time_t lct;
+ NTSTATUS res = NT_STATUS_UNSUCCESSFUL;
+
+ if(!secrets_fetch_trust_account_password(domain, old_trust_passwd_hash, &lct)) {
+ DEBUG(0,("change_trust_account_password: unable to read the machine \
+account password for domain %s.\n", domain));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ while(remote_machine_list &&
+ next_token(&remote_machine_list, remote_machine,
+ LIST_SEP, sizeof(remote_machine))) {
+ strupper(remote_machine);
+ if(strequal(remote_machine, "*")) {
+
+ /*
+ * We have been asked to dynamcially determine the IP addresses of the PDC.
+ */
+
+ struct in_addr *ip_list = NULL;
+ int count = 0;
+ int i;
+
+ /* Use the PDC *only* for this. */
+ if(!get_dc_list(True, domain, &ip_list, &count))
+ continue;
+
+ /*
+ * Try and connect to the PDC/BDC list in turn as an IP
+ * address used as a string.
+ */
+
+ for(i = 0; i < count; i++) {
+ fstring dc_name;
+ if(!lookup_dc_name(global_myname, domain, &ip_list[i], dc_name))
+ continue;
+ if(NT_STATUS_IS_OK(res = modify_trust_password( domain, dc_name,
+ old_trust_passwd_hash)))
+ break;
+ }
+
+ SAFE_FREE(ip_list);
+
+ } else {
+ res = modify_trust_password( domain, remote_machine,
+ old_trust_passwd_hash);
+ }
+
+ }
+
+ if (!NT_STATUS_IS_OK(res)) {
+ DEBUG(0,("%s : change_trust_account_password: Failed to change password for \
+domain %s.\n", timestring(False), domain));
+ }
+
+ return res;
+}
diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c
index dc0514c1ed..eed535cf11 100644
--- a/source3/smbd/chgpasswd.c
+++ b/source3/smbd/chgpasswd.c
@@ -1,8 +1,28 @@
-/* fork a child process to exec passwd and write to its
-* tty to change a users password. This is running as the
-* user who is attempting to change the password.
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/* fork a child process to exec passwd and write to its
+ * tty to change a users password. This is running as the
+ * user who is attempting to change the password.
+ */
+
/*
* This code was copied/borrowed and stolen from various sources.
* The primary source was the poppasswd.c from the authors of POPMail. This software
@@ -27,350 +47,891 @@
*/
#include "includes.h"
-#include "loadparm.h"
-extern int DEBUGLEVEL;
+extern struct passdb_ops pdb_ops;
-#ifdef ALLOW_CHANGE_PASSWORD
+static BOOL check_oem_password(const char *user,
+ uchar * lmdata, const uchar * lmhash,
+ const uchar * ntdata, const uchar * nthash,
+ SAM_ACCOUNT **hnd, char *new_passwd,
+ int new_passwd_size);
-#define MINPASSWDLENGTH 5
-#define BUFSIZE 512
+#if ALLOW_CHANGE_PASSWORD
static int findpty(char **slave)
{
- int master;
-#ifdef SVR4
- extern char *ptsname();
-#else
- static char line[12] = "/dev/ptyXX";
- void *dirp;
- char *dpname;
-#endif
-
-#ifdef SVR4
- if ((master = open("/dev/ptmx", O_RDWR)) >= 1) {
- grantpt(master);
- unlockpt(master);
- *slave = ptsname(master);
- return (master);
- }
-#else
- dirp = OpenDir("/dev");
- if (!dirp) return(-1);
- while ((dpname = ReadDirName(dirp)) != NULL) {
- if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
- line[8] = dpname[3];
- line[9] = dpname[4];
- if ((master = open(line, O_RDWR)) >= 0) {
- line[5] = 't';
- *slave = line;
- CloseDir(dirp);
- return (master);
- }
- }
- }
- CloseDir(dirp);
-#endif
- return (-1);
+ int master;
+ static fstring line;
+ DIR *dirp;
+ char *dpname;
+
+#if defined(HAVE_GRANTPT)
+ /* Try to open /dev/ptmx. If that fails, fall through to old method. */
+ if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
+ {
+ grantpt(master);
+ unlockpt(master);
+ *slave = (char *)ptsname(master);
+ if (*slave == NULL)
+ {
+ DEBUG(0,
+ ("findpty: Unable to create master/slave pty pair.\n"));
+ /* Stop fd leak on error. */
+ close(master);
+ return -1;
+ }
+ else
+ {
+ DEBUG(10,
+ ("findpty: Allocated slave pty %s\n", *slave));
+ return (master);
+ }
+ }
+#endif /* HAVE_GRANTPT */
+
+ fstrcpy(line, "/dev/ptyXX");
+
+ dirp = opendir("/dev");
+ if (!dirp)
+ return (-1);
+ while ((dpname = readdirname(dirp)) != NULL)
+ {
+ if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
+ {
+ DEBUG(3,
+ ("pty: try to open %s, line was %s\n", dpname,
+ line));
+ line[8] = dpname[3];
+ line[9] = dpname[4];
+ if ((master = sys_open(line, O_RDWR, 0)) >= 0)
+ {
+ DEBUG(3, ("pty: opened %s\n", line));
+ line[5] = 't';
+ *slave = line;
+ closedir(dirp);
+ return (master);
+ }
+ }
+ }
+ closedir(dirp);
+ return (-1);
}
-static int dochild(int master,char *slavedev, char *name, char *passwordprogram)
+static int dochild(int master, const char *slavedev, const struct passwd *pass,
+ const char *passwordprogram, BOOL as_root)
{
- int slave;
- struct termios stermios;
- struct passwd *pass = Get_Pwnam(name,True);
- int gid = pass->pw_gid;
- int uid = pass->pw_uid;
-
-#ifdef USE_SETRES
- setresuid(0,0,0);
-#else
- setuid(0);
-#endif
-
- /* Start new session - gets rid of controlling terminal. */
- if (setsid() < 0) {
- DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
- return(False);
- }
-
- /* Open slave pty and acquire as new controlling terminal. */
- if ((slave = open(slavedev, O_RDWR)) < 0) {
- DEBUG(3,("More weirdness, could not open %s\n",
- slavedev));
- return(False);
- }
-#ifdef SVR4
- ioctl(slave, I_PUSH, "ptem");
- ioctl(slave, I_PUSH, "ldterm");
-#else
- if (ioctl(slave,TIOCSCTTY,0) <0) {
- DEBUG(3,("Error in ioctl call for slave pty\n"));
- /* return(False); */
- }
-#endif
-
- /* Close master. */
- close(master);
-
- /* Make slave stdin/out/err of child. */
-
- if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
- DEBUG(3,("Could not re-direct stdin\n"));
- return(False);
- }
- if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
- DEBUG(3,("Could not re-direct stdout\n"));
- return(False);
- }
- if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
- DEBUG(3,("Could not re-direct stderr\n"));
- return(False);
- }
- if (slave > 2) close(slave);
-
- /* Set proper terminal attributes - no echo, canonical input processing,
- no map NL to CR/NL on output. */
-
- if (tcgetattr(0, &stermios) < 0) {
- DEBUG(3,("could not read default terminal attributes on pty\n"));
- return(False);
- }
- stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
- stermios.c_lflag |= ICANON;
- stermios.c_oflag &= ~(ONLCR);
- if (tcsetattr(0, TCSANOW, &stermios) < 0) {
- DEBUG(3,("could not set attributes of pty\n"));
- return(False);
- }
-
- /* make us completely into the right uid */
-#ifdef USE_SETRES
- setresgid(0,0,0);
- setresuid(0,0,0);
- setresgid(gid,gid,gid);
- setresuid(uid,uid,uid);
-#else
- setuid(0);
- seteuid(0);
- setgid(gid);
- setegid(gid);
- setuid(uid);
- seteuid(uid);
+ int slave;
+ struct termios stermios;
+ gid_t gid;
+ uid_t uid;
+
+ if (pass == NULL)
+ {
+ DEBUG(0,
+ ("dochild: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ gid = pass->pw_gid;
+ uid = pass->pw_uid;
+
+ gain_root_privilege();
+
+ /* Start new session - gets rid of controlling terminal. */
+ if (setsid() < 0)
+ {
+ DEBUG(3,
+ ("Weirdness, couldn't let go of controlling terminal\n"));
+ return (False);
+ }
+
+ /* Open slave pty and acquire as new controlling terminal. */
+ if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
+ {
+ DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
+ return (False);
+ }
+#ifdef I_PUSH
+ ioctl(slave, I_PUSH, "ptem");
+ ioctl(slave, I_PUSH, "ldterm");
+#elif defined(TIOCSCTTY)
+ if (ioctl(slave, TIOCSCTTY, 0) < 0)
+ {
+ DEBUG(3, ("Error in ioctl call for slave pty\n"));
+ /* return(False); */
+ }
#endif
- /* execl() password-change application */
- if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
- DEBUG(3,("Bad status returned from %s\n",passwordprogram));
- return(False);
- }
- return(True);
+ /* Close master. */
+ close(master);
+
+ /* Make slave stdin/out/err of child. */
+
+ if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdin\n"));
+ return (False);
+ }
+ if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdout\n"));
+ return (False);
+ }
+ if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stderr\n"));
+ return (False);
+ }
+ if (slave > 2)
+ close(slave);
+
+ /* Set proper terminal attributes - no echo, canonical input processing,
+ no map NL to CR/NL on output. */
+
+ if (tcgetattr(0, &stermios) < 0)
+ {
+ DEBUG(3,
+ ("could not read default terminal attributes on pty\n"));
+ return (False);
+ }
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ stermios.c_lflag |= ICANON;
+ stermios.c_oflag &= ~(ONLCR);
+ if (tcsetattr(0, TCSANOW, &stermios) < 0)
+ {
+ DEBUG(3, ("could not set attributes of pty\n"));
+ return (False);
+ }
+
+ /* make us completely into the right uid */
+ if (!as_root)
+ {
+ become_user_permanently(uid, gid);
+ }
+
+ DEBUG(10,
+ ("Invoking '%s' as password change program.\n",
+ passwordprogram));
+
+ /* execl() password-change application */
+ if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
+ {
+ DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
+ return (False);
+ }
+ return (True);
}
-static int expect(int master,char *expected,char *buf)
+static int expect(int master, char *issue, char *expected)
{
- int n, m;
-
- n = 0;
- buf[0] = 0;
- while (1) {
- if (n >= BUFSIZE-1) {
- return False;
- }
-
- /* allow 4 seconds for some output to appear */
- m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000, True);
- if (m < 0)
- return False;
-
- n += m;
- buf[n] = 0;
-
- {
- pstring s1,s2;
- strcpy(s1,buf);
- strcpy(s2,expected);
- if (do_match(s1, s2, False))
- return(True);
- }
- }
+ pstring buffer;
+ int attempts, timeout, nread, len;
+ BOOL match = False;
+
+ for (attempts = 0; attempts < 2; attempts++) {
+ if (!strequal(issue, ".")) {
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: sending [%s]\n", issue));
+
+ if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
+ DEBUG(2,("expect: (short) write returned %d\n", len ));
+ return False;
+ }
+ }
+
+ if (strequal(expected, "."))
+ return True;
+
+ timeout = 2000;
+ nread = 0;
+ buffer[nread] = 0;
+
+ while ((len = read_with_timeout(master, buffer + nread, 1,
+ sizeof(buffer) - nread - 1,
+ timeout)) > 0) {
+ nread += len;
+ buffer[nread] = 0;
+
+ {
+ /* Eat leading/trailing whitespace before match. */
+ pstring str;
+ pstrcpy( str, buffer);
+ trim_string( str, " ", " ");
+
+ if ((match = (unix_wild_match(expected, str) == 0)))
+ timeout = 200;
+ }
+ }
+
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
+ expected, buffer, match ? "yes" : "no" ));
+
+ if (match)
+ break;
+
+ if (len < 0) {
+ DEBUG(2, ("expect: %s\n", strerror(errno)));
+ return False;
+ }
+ }
+
+ DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
+ return match;
}
static void pwd_sub(char *buf)
{
- string_sub(buf,"\\n","\n");
- string_sub(buf,"\\r","\r");
- string_sub(buf,"\\s"," ");
- string_sub(buf,"\\t","\t");
+ all_string_sub(buf, "\\n", "\n", 0);
+ all_string_sub(buf, "\\r", "\r", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static int talktochild(int master, char *seq)
+{
+ int count = 0;
+ fstring issue, expected;
+
+ fstrcpy(issue, ".");
+
+ while (next_token(&seq, expected, NULL, sizeof(expected)))
+ {
+ pwd_sub(expected);
+ count++;
+
+ if (!expect(master, issue, expected))
+ {
+ DEBUG(3, ("Response %d incorrect\n", count));
+ return False;
+ }
+
+ if (!next_token(&seq, issue, NULL, sizeof(issue)))
+ fstrcpy(issue, ".");
+
+ pwd_sub(issue);
+ }
+ if (!strequal(issue, ".")) {
+ /* we have one final issue to send */
+ fstrcpy(expected, ".");
+ if (!expect(master, issue, expected))
+ return False;
+ }
+
+ return (count > 0);
}
-static void writestring(int fd,char *s)
+static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
+ char *chatsequence, BOOL as_root)
{
- int l;
-
- l = strlen (s);
- write (fd, s, l);
+ char *slavedev;
+ int master;
+ pid_t pid, wpid;
+ int wstat;
+ BOOL chstat = False;
+
+ if (pass == NULL)
+ {
+ DEBUG(0,
+ ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ /* allocate a pseudo-terminal device */
+ if ((master = findpty(&slavedev)) < 0)
+ {
+ DEBUG(3,
+ ("Cannot Allocate pty for password change: %s\n",
+ pass->pw_name));
+ return (False);
+ }
+
+ /*
+ * We need to temporarily stop CatchChild from eating
+ * SIGCLD signals as it also eats the exit status code. JRA.
+ */
+
+ CatchChildLeaveStatus();
+
+ if ((pid = sys_fork()) < 0)
+ {
+ DEBUG(3,
+ ("Cannot fork() child for password change: %s\n",
+ pass->pw_name));
+ close(master);
+ CatchChild();
+ return (False);
+ }
+
+ /* we now have a pty */
+ if (pid > 0)
+ { /* This is the parent process */
+ if ((chstat = talktochild(master, chatsequence)) == False)
+ {
+ DEBUG(3,
+ ("Child failed to change password: %s\n",
+ pass->pw_name));
+ kill(pid, SIGKILL); /* be sure to end this process */
+ }
+
+ while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
+ {
+ if (errno == EINTR)
+ {
+ errno = 0;
+ continue;
+ }
+ break;
+ }
+
+ if (wpid < 0)
+ {
+ DEBUG(3, ("The process is no longer waiting!\n\n"));
+ close(master);
+ CatchChild();
+ return (False);
+ }
+
+ /*
+ * Go back to ignoring children.
+ */
+ CatchChild();
+
+ close(master);
+
+ if (pid != wpid)
+ {
+ DEBUG(3,
+ ("We were waiting for the wrong process ID\n"));
+ return (False);
+ }
+ if (WIFEXITED(wstat) == 0)
+ {
+ DEBUG(3,
+ ("The process exited while we were waiting\n"));
+ return (False);
+ }
+ if (WEXITSTATUS(wstat) != 0)
+ {
+ DEBUG(3,
+ ("The status of the process exiting was %d\n",
+ wstat));
+ return (False);
+ }
+
+ }
+ else
+ {
+ /* CHILD */
+
+ /*
+ * Lose any oplock capabilities.
+ */
+ oplock_set_capability(False, False);
+
+ /* make sure it doesn't freeze */
+ alarm(20);
+
+ if (as_root)
+ become_root();
+
+ DEBUG(3,
+ ("Dochild for user %s (uid=%d,gid=%d)\n", pass->pw_name,
+ (int)getuid(), (int)getgid()));
+ chstat =
+ dochild(master, slavedev, pass, passwordprogram,
+ as_root);
+
+ if (as_root)
+ unbecome_root();
+
+ /*
+ * The child should never return from dochild() ....
+ */
+
+ DEBUG(0,
+ ("chat_with_program: Error: dochild() returned %d\n",
+ chstat));
+ exit(1);
+ }
+
+ if (chstat)
+ DEBUG(3,
+ ("Password change %ssuccessful for user %s\n",
+ (chstat ? "" : "un"), pass->pw_name));
+ return (chstat);
}
-static int talktochild(int master, char *chatsequence)
+BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
{
- char buf[BUFSIZE];
- int count=0;
- char *ptr=chatsequence;
- fstring chatbuf;
+ pstring passwordprogram;
+ pstring chatsequence;
+ size_t i;
+ size_t len;
- *buf = 0;
- sleep(1);
+ struct passwd *pass;
- while (next_token(&ptr,chatbuf,NULL)) {
- BOOL ok=True;
- count++;
- pwd_sub(chatbuf);
- if (!strequal(chatbuf,"."))
- ok = expect(master,chatbuf,buf);
+ DEBUG(3, ("Password change for user: %s\n", name));
#if DEBUG_PASSWORD
- DEBUG(100,("chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
-#endif
-
- if (!ok) {
- DEBUG(3,("response %d incorrect\n",count));
- return(False);
- }
+ DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
+#endif
- if (!next_token(&ptr,chatbuf,NULL)) break;
- pwd_sub(chatbuf);
- if (!strequal(chatbuf,"."))
- writestring(master,chatbuf);
+ /* Take the passed information and test it for minimum criteria */
+ /* Minimum password length */
+ if (strlen(newpass) < lp_min_passwd_length()) {
+ /* too short, must be at least MINPASSWDLENGTH */
+ DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
+ name, lp_min_passwd_length()));
+ return (False); /* inform the user */
+ }
+
+ /* Password is same as old password */
+ if (strcmp(oldpass, newpass) == 0) {
+ /* don't allow same password */
+ DEBUG(2, ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
+ return (False); /* inform the user */
+ }
+
+ /*
+ * Check the old and new passwords don't contain any control
+ * characters.
+ */
+
+ len = strlen(oldpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)oldpass[i])) {
+ DEBUG(0,
+ ("chat_with_program: oldpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+ len = strlen(newpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)newpass[i])) {
+ DEBUG(0,
+ ("chat_with_program: newpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+ pass = Get_Pwnam(name);
+
+#ifdef WITH_PAM
+ if (lp_pam_password_change()) {
+ BOOL ret;
+
+ if (as_root)
+ become_root();
+
+ if (pass) {
+ ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
+ } else {
+ ret = smb_pam_passchange(name, oldpass, newpass);
+ }
+
+ if (as_root)
+ unbecome_root();
+
+ return ret;
+ }
+#endif
-#if DEBUG_PASSWORD
- DEBUG(100,("sendbuf=[%s]\n",chatbuf));
-#endif
- }
+ /* A non-PAM password change just doen't make sense without a valid local user */
+
+ if (pass == NULL)
+ {
+ DEBUG(0,
+ ("chgpasswd: user %s doesn't exist in the UNIX password database.\n",
+ name));
+ return False;
+ }
+
+ pstrcpy(passwordprogram, lp_passwd_program());
+ pstrcpy(chatsequence, lp_passwd_chat());
+
+ if (!*chatsequence) {
+ DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
+ return (False);
+ }
+
+ if (!*passwordprogram) {
+ DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
+ return (False);
+ }
+
+ if (as_root) {
+ /* The password program *must* contain the user name to work. Fail if not. */
+ if (strstr(passwordprogram, "%u") == NULL) {
+ DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
+the string %%u, and the given string %s does not.\n", passwordprogram ));
+ return False;
+ }
+ }
+
+ pstring_sub(passwordprogram, "%u", name);
+ /* note that we do NOT substitute the %o and %n in the password program
+ as this would open up a security hole where the user could use
+ a new password containing shell escape characters */
+
+ pstring_sub(chatsequence, "%u", name);
+ all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
+ all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
+ return (chat_with_program
+ (passwordprogram, pass, chatsequence, as_root));
+}
- if (count<1) return(False);
+#else /* ALLOW_CHANGE_PASSWORD */
- return (True);
+BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
+{
+ DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
+ return (False);
}
+#endif /* ALLOW_CHANGE_PASSWORD */
+/***********************************************************
+ Code to check the lanman hashed password.
+************************************************************/
-BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence)
+BOOL check_lanman_password(char *user, uchar * pass1,
+ uchar * pass2, SAM_ACCOUNT **hnd)
{
- char *slavedev;
- int master;
- pid_t pid, wpid;
- int wstat;
- BOOL chstat;
-
- /* allocate a pseudo-terminal device */
- if ((master = findpty (&slavedev)) < 0) {
- DEBUG(3,("Cannot Allocate pty for password change: %s",name));
- return(False);
- }
-
- if ((pid = fork()) < 0) {
- DEBUG(3,("Cannot fork() child for password change: %s",name));
- return(False);
- }
-
- /* we now have a pty */
- if (pid > 0){ /* This is the parent process */
- if ((chstat = talktochild(master, chatsequence)) == False) {
- DEBUG(3,("Child failed to change password: %s\n",name));
- kill(pid, SIGKILL); /* be sure to end this process */
- return(False);
- }
- if ((wpid = waitpid(pid, &wstat, 0)) < 0) {
- DEBUG(3,("The process is no longer waiting!\n\n"));
- return(False);
- }
- if (pid != wpid) {
- DEBUG(3,("We were waiting for the wrong process ID\n"));
- return(False);
- }
- if (WIFEXITED(wstat) == 0) {
- DEBUG(3,("The process exited while we were waiting\n"));
- return(False);
- }
- if (WEXITSTATUS(wstat) != 0) {
- DEBUG(3,("The status of the process exiting was %d\n", wstat));
- return(False);
- }
-
- } else {
- /* CHILD */
-
- /* make sure it doesn't freeze */
- alarm(20);
-
- DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,getuid(),getgid()));
- chstat = dochild(master, slavedev, name, passwordprogram);
- }
- DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
- return (chstat);
+ uchar unenc_new_pw[16];
+ uchar unenc_old_pw[16];
+ SAM_ACCOUNT *sampass = NULL;
+ uint16 acct_ctrl;
+ const uint8 *lanman_pw;
+ BOOL ret;
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl (sampass);
+ lanman_pw = pdb_get_lanman_passwd (sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ if (lanman_pw == NULL) {
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ /* this saves the pointer for the caller */
+ *hnd = sampass;
+ return True;
+ } else {
+ DEBUG(0, ("check_lanman_password: no lanman password !\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+ }
+
+ /* Get the new lanman hash. */
+ D_P16(lanman_pw, pass2, unenc_new_pw);
+
+ /* Use this to get the old lanman hash. */
+ D_P16(unenc_new_pw, pass1, unenc_old_pw);
+
+ /* Check that the two old passwords match. */
+ if (memcmp(lanman_pw, unenc_old_pw, 16)) {
+ DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /* this saves the pointer for the caller */
+ *hnd = sampass;
+ return True;
}
+/***********************************************************
+ Code to change the lanman hashed password.
+ It nulls out the NT hashed password as it will
+ no longer be valid.
+************************************************************/
+
+BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1,
+ uchar * pass2)
+{
+ static uchar null_pw[16];
+ uchar unenc_new_pw[16];
+ BOOL ret;
+ uint16 acct_ctrl;
+ const uint8 *pwd;
+
+ if (sampass == NULL) {
+ DEBUG(0,("change_lanman_password: no smb password entry.\n"));
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ pwd = pdb_get_lanman_passwd(sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("change_lanman_password: account %s disabled.\n",
+ pdb_get_username(sampass)));
+ return False;
+ }
+
+ if (pwd == NULL) {
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ uchar no_pw[14];
+ memset(no_pw, '\0', 14);
+ E_P16(no_pw, null_pw);
+
+ /* Get the new lanman hash. */
+ D_P16(null_pw, pass2, unenc_new_pw);
+ } else {
+ DEBUG(0,("change_lanman_password: no lanman password !\n"));
+ return False;
+ }
+ } else {
+ /* Get the new lanman hash. */
+ D_P16(pwd, pass2, unenc_new_pw);
+ }
+
+ if (!pdb_set_lanman_passwd(sampass, unenc_new_pw)) {
+ return False;
+ }
+
+ if (!pdb_set_nt_passwd (sampass, NULL)) {
+ return False; /* We lose the NT hash. Sorry. */
+ }
+
+ if (!pdb_set_pass_changed_now (sampass)) {
+ pdb_free_sam(&sampass);
+ /* Not quite sure what this one qualifies as, but this will do */
+ return False;
+ }
+
+ /* Now flush the sam_passwd struct to persistent storage */
+ become_root();
+ ret = pdb_update_sam_account (sampass);
+ unbecome_root();
+
+ return ret;
+}
-BOOL chgpasswd(char *name,char *oldpass,char *newpass)
+/***********************************************************
+ Code to check and change the OEM hashed password.
+************************************************************/
+BOOL pass_oem_change(char *user,
+ uchar * lmdata, uchar * lmhash,
+ uchar * ntdata, uchar * nthash)
{
- pstring passwordprogram;
- pstring chatsequence;
+ fstring new_passwd;
+ const char *unix_user;
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
+ &sampass, new_passwd, sizeof(new_passwd));
- strlower(name);
- DEBUG(3,("Password change for user: %s\n",name));
+ /*
+ * At this point we have the new case-sensitive plaintext
+ * password in the fstring new_passwd. If we wanted to synchronise
+ * with UNIX passwords we would call a UNIX password changing
+ * function here. However it would have to be done as root
+ * as the plaintext of the old users password is not
+ * available. JRA.
+ */
-#if DEBUG_PASSWORD
- DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass));
-#endif
+ unix_user = pdb_get_username(sampass);
- /* Take the passed information and test it for minimum criteria */
- /* Minimum password length */
- if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */
- {
- DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
- return (False); /* inform the user */
- }
-
- /* Password is same as old password */
- if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
- {
- DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
- return (False); /* inform the user */
- }
-
-#if (defined(PASSWD_PROGRAM) && defined(PASSWD_CHAT))
- strcpy(passwordprogram,PASSWD_PROGRAM);
- strcpy(chatsequence,PASSWD_CHAT);
-#else
- strcpy(passwordprogram,lp_passwd_program());
- strcpy(chatsequence,lp_passwd_chat());
-#endif
+ if ((ret) && (unix_user) && (*unix_user) && lp_unix_password_sync())
+ ret = chgpasswd(unix_user, "", new_passwd, True);
- if (!*chatsequence) {
- DEBUG(2,("Null chat sequence - no password changing\n"));
- return(False);
- }
+ if (ret)
+ ret = change_oem_password(sampass, new_passwd);
- if (!*passwordprogram) {
- DEBUG(2,("Null password program - no password changing\n"));
- return(False);
- }
+ memset(new_passwd, 0, sizeof(new_passwd));
- string_sub(passwordprogram,"%u",name);
- string_sub(passwordprogram,"%o",oldpass);
- string_sub(passwordprogram,"%n",newpass);
+ pdb_free_sam(&sampass);
- string_sub(chatsequence,"%u",name);
- string_sub(chatsequence,"%o",oldpass);
- string_sub(chatsequence,"%n",newpass);
- return(chat_with_program(passwordprogram,name,chatsequence));
+ return ret;
}
-#else
-BOOL chgpasswd(char *name,char *oldpass,char *newpass)
+/***********************************************************
+ Code to check the OEM hashed password.
+
+ this function ignores the 516 byte nt OEM hashed password
+ but does use the lm OEM password to check the nt hashed-hash.
+
+************************************************************/
+static BOOL check_oem_password(const char *user,
+ uchar * lmdata, const uchar * lmhash,
+ const uchar * ntdata, const uchar * nthash,
+ SAM_ACCOUNT **hnd, char *new_passwd,
+ int new_passwd_size)
{
- DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
- return(False);
-}
+ static uchar null_pw[16];
+ static uchar null_ntpw[16];
+ SAM_ACCOUNT *sampass = NULL;
+ const uint8 *lanman_pw, *nt_pw;
+ uint16 acct_ctrl;
+ int new_pw_len;
+ uchar new_ntp16[16];
+ uchar unenc_old_ntpw[16];
+ uchar new_p16[16];
+ uchar unenc_old_pw[16];
+ char no_pw[2];
+ BOOL ret;
+
+ BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
+ return False;
+ }
+
+ *hnd = sampass;
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
+ return False;
+ }
+
+ /* construct a null password (in case one is needed */
+ no_pw[0] = 0;
+ no_pw[1] = 0;
+ nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
+
+ /* save pointers to passwords so we don't have to keep looking them up */
+ lanman_pw = pdb_get_lanman_passwd(sampass);
+ nt_pw = pdb_get_nt_passwd (sampass);
+
+ /* check for null passwords */
+ if (lanman_pw == NULL) {
+ if (!(acct_ctrl & ACB_PWNOTREQ)) {
+ DEBUG(0,("check_oem_password: no lanman password !\n"));
+ return False;
+ }
+ }
+
+ if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
+ if (!(acct_ctrl & ACB_PWNOTREQ)) {
+ DEBUG(0,("check_oem_password: no ntlm password !\n"));
+ return False;
+ }
+ }
+
+ /*
+ * Call the hash function to get the new password.
+ */
+ SamOEMhash( lmdata, lanman_pw, 516);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+
+ new_pw_len = IVAL(lmdata, 512);
+ if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
+ DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
+ return False;
+ }
+
+ if (nt_pass_set) {
+ /*
+ * nt passwords are in unicode
+ */
+ pull_ucs2(NULL, new_passwd,
+ (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
+ new_passwd_size, new_pw_len, 0);
+ } else {
+ memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
+ new_passwd[new_pw_len] = 0;
+ }
+
+ /*
+ * To ensure we got the correct new password, hash it and
+ * use it as a key to test the passed old password.
+ */
+
+ nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
+
+ if (!nt_pass_set)
+ {
+ /*
+ * Now use new_p16 as the key to see if the old
+ * password matches.
+ */
+ D_P16(new_p16, lmhash, unenc_old_pw);
+
+ if (memcmp(lanman_pw, unenc_old_pw, 16))
+ {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ return False;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ return True;
+ }
+
+ /*
+ * Now use new_p16 as the key to see if the old
+ * password matches.
+ */
+ D_P16(new_ntp16, lmhash, unenc_old_pw);
+ D_P16(new_ntp16, nthash, unenc_old_ntpw);
+
+ if (memcmp(lanman_pw, unenc_old_pw, 16))
+ {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ return False;
+ }
+
+ if (memcmp(nt_pw, unenc_old_ntpw, 16))
+ {
+ DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
+ return False;
+ }
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
#endif
+ return True;
+}
+
+/***********************************************************
+ Code to change the oem password. Changes both the lanman
+ and NT hashes.
+************************************************************/
+
+BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd)
+{
+ BOOL ret;
+
+ if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
+ return False;
+ }
+
+ /* Now write it into the file. */
+ become_root();
+ ret = pdb_update_sam_account (hnd);
+ unbecome_root();
+
+ return ret;
+}
+
+
+
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
new file mode 100644
index 0000000000..dc52327cb4
--- /dev/null
+++ b/source3/smbd/close.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+ file closing
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+run a file if it is a magic script
+****************************************************************************/
+static void check_magic(files_struct *fsp,connection_struct *conn)
+{
+ if (!*lp_magicscript(SNUM(conn)))
+ return;
+
+ DEBUG(5,("checking magic for %s\n",fsp->fsp_name));
+
+ {
+ char *p;
+ if (!(p = strrchr_m(fsp->fsp_name,'/')))
+ p = fsp->fsp_name;
+ else
+ p++;
+
+ if (!strequal(lp_magicscript(SNUM(conn)),p))
+ return;
+ }
+
+ {
+ int ret;
+ pstring magic_output;
+ pstring fname;
+ SMB_STRUCT_STAT st;
+ int tmp_fd, outfd;
+
+ pstrcpy(fname,fsp->fsp_name);
+ if (*lp_magicoutput(SNUM(conn)))
+ pstrcpy(magic_output,lp_magicoutput(SNUM(conn)));
+ else
+ slprintf(magic_output,sizeof(fname)-1, "%s.out",fname);
+
+ chmod(fname,0755);
+ ret = smbrun(fname,&tmp_fd);
+ DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
+ unlink(fname);
+ if (ret != 0 || tmp_fd == -1) {
+ if (tmp_fd != -1)
+ close(tmp_fd);
+ return;
+ }
+ outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
+ if (outfd == -1) {
+ close(tmp_fd);
+ return;
+ }
+
+ if (sys_fstat(tmp_fd,&st) == -1) {
+ close(tmp_fd);
+ close(outfd);
+ return;
+ }
+
+ transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
+ close(tmp_fd);
+ close(outfd);
+ }
+}
+
+/****************************************************************************
+ Common code to close a file or a directory.
+****************************************************************************/
+
+static int close_filestruct(files_struct *fsp)
+{
+ connection_struct *conn = fsp->conn;
+ int ret = 0;
+
+ if (fsp->fd != -1) {
+ if(flush_write_cache(fsp, CLOSE_FLUSH) == -1)
+ ret = -1;
+
+ delete_write_cache(fsp);
+ }
+
+ fsp->is_directory = False;
+
+ conn->num_files_open--;
+ SAFE_FREE(fsp->wbmpx_ptr);
+
+ return ret;
+}
+
+/****************************************************************************
+ Close a file.
+
+ If normal_close is 1 then this came from a normal SMBclose (or equivalent)
+ operation otherwise it came as the result of some other operation such as
+ the closing of the connection. In the latter case printing and
+ magic scripts are not run.
+****************************************************************************/
+
+static int close_normal_file(files_struct *fsp, BOOL normal_close)
+{
+ share_mode_entry *share_entry = NULL;
+ size_t share_entry_count = 0;
+ BOOL delete_on_close = False;
+ connection_struct *conn = fsp->conn;
+ int err = 0;
+ int err1 = 0;
+
+ remove_pending_lock_requests_by_fid(fsp);
+
+ /*
+ * If we're flushing on a close we can get a write
+ * error here, we must remember this.
+ */
+
+ if (close_filestruct(fsp) == -1)
+ err1 = -1;
+
+ if (fsp->print_file) {
+ print_fsp_end(fsp, normal_close);
+ file_free(fsp);
+ return 0;
+ }
+
+ /*
+ * Lock the share entries, and determine if we should delete
+ * on close. If so delete whilst the lock is still in effect.
+ * This prevents race conditions with the file being created. JRA.
+ */
+
+ lock_share_entry_fsp(fsp);
+ share_entry_count = del_share_mode(fsp, &share_entry);
+
+ DEBUG(10,("close_normal_file: share_entry_count = %d for file %s\n",
+ share_entry_count, fsp->fsp_name ));
+
+ /*
+ * We delete on close if it's the last open, and the
+ * delete on close flag was set in the entry we just deleted.
+ */
+
+ if ((share_entry_count == 0) && share_entry &&
+ GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) )
+ delete_on_close = True;
+
+ SAFE_FREE(share_entry);
+
+ /*
+ * NT can set delete_on_close of the last open
+ * reference to a file.
+ */
+
+ if (normal_close && delete_on_close) {
+ DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n",
+ fsp->fsp_name));
+ if(fsp->conn->vfs_ops.unlink(conn,fsp->fsp_name) != 0) {
+ /*
+ * This call can potentially fail as another smbd may have
+ * had the file open with delete on close set and deleted
+ * it when its last reference to this file went away. Hence
+ * we log this but not at debug level zero.
+ */
+
+ DEBUG(5,("close_file: file %s. Delete on close was set and unlink failed \
+with error %s\n", fsp->fsp_name, strerror(errno) ));
+ }
+ }
+
+ unlock_share_entry_fsp(fsp);
+
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ release_file_oplock(fsp);
+
+ locking_close_file(fsp);
+
+ err = fd_close(conn, fsp);
+
+ /* check for magic scripts */
+ if (normal_close) {
+ check_magic(fsp,conn);
+ }
+
+ /*
+ * Ensure pending modtime is set after close.
+ */
+
+ if(fsp->pending_modtime) {
+ int saved_errno = errno;
+ set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
+ errno = saved_errno;
+ }
+
+ DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
+ conn->user,fsp->fsp_name,
+ conn->num_files_open, err ? strerror(err) : ""));
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+
+ if (err == -1 || err1 == -1)
+ return -1;
+ else
+ return 0;
+}
+
+/****************************************************************************
+ Close a directory opened by an NT SMB call.
+****************************************************************************/
+
+static int close_directory(files_struct *fsp, BOOL normal_close)
+{
+ remove_pending_change_notify_requests_by_fid(fsp);
+
+ /*
+ * NT can set delete_on_close of the last open
+ * reference to a directory also.
+ */
+
+ if (normal_close && fsp->directory_delete_on_close) {
+ BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name);
+ DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n",
+ fsp->fsp_name, ok ? "succeeded" : "failed" ));
+
+ /*
+ * Ensure we remove any change notify requests that would
+ * now fail as the directory has been deleted.
+ */
+
+ if(ok)
+ remove_pending_change_notify_requests_by_filename(fsp);
+ }
+
+ /*
+ * Do the code common to files and directories.
+ */
+ close_filestruct(fsp);
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+
+ return 0;
+}
+
+/****************************************************************************
+ Close a directory opened by an NT SMB call.
+****************************************************************************/
+
+int close_file(files_struct *fsp, BOOL normal_close)
+{
+ if(fsp->is_directory)
+ return close_directory(fsp, normal_close);
+ return close_normal_file(fsp, normal_close);
+}
diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c
new file mode 100644
index 0000000000..f552d4a224
--- /dev/null
+++ b/source3/smbd/conn.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ Manage connections_struct structures
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* set these to define the limits of the server. NOTE These are on a
+ per-client basis. Thus any one machine can't connect to more than
+ MAX_CONNECTIONS services, but any number of machines may connect at
+ one time. */
+#define MAX_CONNECTIONS 128
+
+static connection_struct *Connections;
+
+/* number of open connections */
+static struct bitmap *bmap;
+static int num_open;
+
+/****************************************************************************
+init the conn structures
+****************************************************************************/
+void conn_init(void)
+{
+ bmap = bitmap_allocate(MAX_CONNECTIONS);
+}
+
+/****************************************************************************
+return the number of open connections
+****************************************************************************/
+int conn_num_open(void)
+{
+ return num_open;
+}
+
+
+/****************************************************************************
+check if a snum is in use
+****************************************************************************/
+BOOL conn_snum_used(int snum)
+{
+ connection_struct *conn;
+ for (conn=Connections;conn;conn=conn->next) {
+ if (conn->service == snum) {
+ return(True);
+ }
+ }
+ return(False);
+}
+
+
+/****************************************************************************
+find a conn given a cnum
+****************************************************************************/
+connection_struct *conn_find(int cnum)
+{
+ int count=0;
+ connection_struct *conn;
+
+ for (conn=Connections;conn;conn=conn->next,count++) {
+ if (conn->cnum == cnum) {
+ if (count > 10) {
+ DLIST_PROMOTE(Connections, conn);
+ }
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
+
+/****************************************************************************
+ find first available connection slot, starting from a random position.
+The randomisation stops problems with the server dieing and clients
+thinking the server is still available.
+****************************************************************************/
+connection_struct *conn_new(void)
+{
+ connection_struct *conn;
+ int i;
+
+ i = bitmap_find(bmap, 1);
+
+ if (i == -1) {
+ DEBUG(1,("ERROR! Out of connection structures\n"));
+ return NULL;
+ }
+
+ conn = (connection_struct *)malloc(sizeof(*conn));
+ if (!conn) return NULL;
+
+ ZERO_STRUCTP(conn);
+ conn->cnum = i;
+
+ bitmap_set(bmap, i);
+
+ num_open++;
+
+ string_set(&conn->user,"");
+ string_set(&conn->dirpath,"");
+ string_set(&conn->connectpath,"");
+ string_set(&conn->origpath,"");
+
+ DLIST_ADD(Connections, conn);
+
+ return conn;
+}
+
+/****************************************************************************
+close all conn structures
+****************************************************************************/
+void conn_close_all(void)
+{
+ connection_struct *conn, *next;
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ close_cnum(conn, (uint16)-1);
+ }
+}
+
+/****************************************************************************
+idle inactive connections
+****************************************************************************/
+BOOL conn_idle_all(time_t t, int deadtime)
+{
+ BOOL allidle = True;
+ connection_struct *conn, *next;
+
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ /* close dirptrs on connections that are idle */
+ if ((t-conn->lastused) > DPTR_IDLE_TIMEOUT)
+ dptr_idlecnum(conn);
+
+ if (conn->num_files_open > 0 ||
+ (t-conn->lastused)<deadtime)
+ allidle = False;
+ }
+
+ return allidle;
+}
+
+/****************************************************************************
+ Free a conn structure.
+****************************************************************************/
+
+void conn_free(connection_struct *conn)
+{
+ /* Free vfs_connection_struct */
+
+ if (conn->dl_handle != NULL) {
+ /* Close dlopen() handle */
+ dlclose(conn->dl_handle);
+ }
+
+ DLIST_REMOVE(Connections, conn);
+
+ if (conn->ngroups && conn->groups) {
+ SAFE_FREE(conn->groups);
+ conn->ngroups = 0;
+ }
+
+ delete_nt_token(&conn->nt_user_token);
+ free_namearray(conn->veto_list);
+ free_namearray(conn->hide_list);
+ free_namearray(conn->veto_oplock_list);
+
+ string_free(&conn->user);
+ string_free(&conn->dirpath);
+ string_free(&conn->connectpath);
+ string_free(&conn->origpath);
+
+ bitmap_clear(bmap, conn->cnum);
+ num_open--;
+
+ ZERO_STRUCTP(conn);
+ SAFE_FREE(conn);
+}
+
+
+/****************************************************************************
+receive a smbcontrol message to forcibly unmount a share
+the message contains just a share name and all instances of that
+share are unmounted
+the special sharename '*' forces unmount of all shares
+****************************************************************************/
+void msg_force_tdis(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ connection_struct *conn, *next;
+ fstring sharename;
+
+ fstrcpy(sharename, buf);
+
+ if (strcmp(sharename, "*") == 0) {
+ DEBUG(1,("Forcing close of all shares\n"));
+ conn_close_all();
+ return;
+ }
+
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ if (strequal(lp_servicename(conn->service), sharename)) {
+ DEBUG(1,("Forcing close of share %s cnum=%d\n",
+ sharename, conn->cnum));
+ close_cnum(conn, (uint16)-1);
+ }
+ }
+}
diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c
new file mode 100644
index 0000000000..c9815dbf8c
--- /dev/null
+++ b/source3/smbd/connection.c
@@ -0,0 +1,189 @@
+/*
+ Unix SMB/CIFS implementation.
+ connection claim routines
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern fstring remote_machine;
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Return the connection tdb context (used for message send all).
+****************************************************************************/
+
+TDB_CONTEXT *conn_tdb_ctx(void)
+{
+ return tdb;
+}
+
+/****************************************************************************
+ Delete a connection record.
+****************************************************************************/
+
+BOOL yield_connection(connection_struct *conn,char *name)
+{
+ struct connections_key key;
+ TDB_DATA kbuf;
+
+ if (!tdb) return False;
+
+ DEBUG(3,("Yielding connection to %s\n",name));
+
+ ZERO_STRUCT(key);
+ key.pid = sys_getpid();
+ key.cnum = conn?conn->cnum:-1;
+ fstrcpy(key.name, name);
+
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+
+ if (tdb_delete(tdb, kbuf) != 0) {
+ int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0;
+ DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n",
+ name, tdb_errorstr(tdb) ));
+ return (False);
+ }
+
+ return(True);
+}
+
+struct count_stat {
+ pid_t mypid;
+ int curr_connections;
+ char *name;
+ BOOL Clear;
+};
+
+/****************************************************************************
+ Count the entries belonging to a service in the connection db.
+****************************************************************************/
+
+static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp)
+{
+ struct connections_data crec;
+ struct count_stat *cs = (struct count_stat *)udp;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) {
+ DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+ (unsigned int)crec.pid, crec.cnum, crec.name));
+ if (tdb_delete(the_tdb, kbuf) != 0)
+ DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) ));
+ return 0;
+ }
+
+ if (strequal(crec.name, cs->name))
+ cs->curr_connections++;
+
+ return 0;
+}
+
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
+
+BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear)
+{
+ struct connections_key key;
+ struct connections_data crec;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb) {
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
+ }
+ if (!tdb)
+ return False;
+
+ /*
+ * Enforce the max connections parameter.
+ */
+
+ if (max_connections > 0) {
+ struct count_stat cs;
+
+ cs.mypid = sys_getpid();
+ cs.curr_connections = 0;
+ cs.name = lp_servicename(SNUM(conn));
+ cs.Clear = Clear;
+
+ /*
+ * This has a race condition, but locking the chain before hand is worse
+ * as it leads to deadlock.
+ */
+
+ if (tdb_traverse(tdb, count_fn, &cs) == -1) {
+ DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+
+ if (cs.curr_connections >= max_connections) {
+ DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n",
+ max_connections, name ));
+ return False;
+ }
+ }
+
+ DEBUG(5,("claiming %s %d\n",name,max_connections));
+
+ ZERO_STRUCT(key);
+ key.pid = sys_getpid();
+ key.cnum = conn?conn->cnum:-1;
+ fstrcpy(key.name, name);
+
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+
+ /* fill in the crec */
+ ZERO_STRUCT(crec);
+ crec.magic = 0x280267;
+ crec.pid = sys_getpid();
+ crec.cnum = conn?conn->cnum:-1;
+ if (conn) {
+ crec.uid = conn->uid;
+ crec.gid = conn->gid;
+ StrnCpy(crec.name,
+ lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
+ }
+ crec.start = time(NULL);
+
+ StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
+ StrnCpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
+
+ dbuf.dptr = (char *)&crec;
+ dbuf.dsize = sizeof(crec);
+
+ if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ DEBUG(0,("claim_connection: tdb_store failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source3/smbd/dfree.c b/source3/smbd/dfree.c
new file mode 100644
index 0000000000..71b3f2bf77
--- /dev/null
+++ b/source3/smbd/dfree.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+ functions to calculate the free disk space
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+normalise for DOS usage
+****************************************************************************/
+static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ /* check if the disk is beyond the max disk size */
+ SMB_BIG_UINT maxdisksize = lp_maxdisksize();
+ if (maxdisksize) {
+ /* convert to blocks - and don't overflow */
+ maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
+ if (*dsize > maxdisksize) *dsize = maxdisksize;
+ if (*dfree > maxdisksize) *dfree = maxdisksize-1;
+ /* the -1 should stop applications getting div by 0
+ errors */
+ }
+
+ while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
+ *dfree /= 2;
+ *dsize /= 2;
+ *bsize *= 2;
+ if(small_query) {
+ /*
+ * Force max to fit in 16 bit fields.
+ */
+ if (*bsize > (WORDMAX*512)) {
+ *bsize = (WORDMAX*512);
+ if (*dsize > WORDMAX)
+ *dsize = WORDMAX;
+ if (*dfree > WORDMAX)
+ *dfree = WORDMAX;
+ break;
+ }
+ }
+ }
+}
+
+
+
+/****************************************************************************
+ return number of 1K blocks available on a path and total number
+****************************************************************************/
+
+static SMB_BIG_UINT disk_free(const char *path, BOOL small_query,
+ SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ int dfree_retval;
+ SMB_BIG_UINT dfree_q = 0;
+ SMB_BIG_UINT bsize_q = 0;
+ SMB_BIG_UINT dsize_q = 0;
+ char *dfree_command;
+
+ (*dfree) = (*dsize) = 0;
+ (*bsize) = 512;
+
+ /*
+ * If external disk calculation specified, use it.
+ */
+
+ dfree_command = lp_dfree_command();
+ if (dfree_command && *dfree_command) {
+ char *p;
+ char **lines;
+ pstring syscmd;
+
+ slprintf(syscmd, sizeof(syscmd)-1, "%s %s", dfree_command, path);
+ DEBUG (3, ("disk_free: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
+
+ *dsize = (SMB_BIG_UINT)strtoul(line, &p, 10);
+ while (p && *p & isspace(*p))
+ p++;
+ if (p && *p)
+ *dfree = (SMB_BIG_UINT)strtoul(p, &p, 10);
+ while (p && *p & isspace(*p))
+ p++;
+ if (p && *p)
+ *bsize = (SMB_BIG_UINT)strtoul(p, NULL, 10);
+ else
+ *bsize = 1024;
+ file_lines_free(lines);
+ DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
+ (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
+
+ if (!*dsize)
+ *dsize = 2048;
+ if (!*dfree)
+ *dfree = 1024;
+ } else {
+ DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n",
+ syscmd, strerror(errno) ));
+ sys_fsusage(path, dfree, dsize);
+ }
+ } else
+ sys_fsusage(path, dfree, dsize);
+
+ if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
+ (*bsize) = bsize_q;
+ (*dfree) = MIN(*dfree,dfree_q);
+ (*dsize) = MIN(*dsize,dsize_q);
+ }
+
+ /* FIXME : Any reason for this assumption ? */
+ if (*bsize < 256) {
+ DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
+ *bsize = 512;
+ }
+
+ if ((*dsize)<1) {
+ static int done;
+ if (!done) {
+ DEBUG(0,("WARNING: dfree is broken on this system\n"));
+ done=1;
+ }
+ *dsize = 20*1024*1024/(*bsize);
+ *dfree = MAX(1,*dfree);
+ }
+
+ disk_norm(small_query,bsize,dfree,dsize);
+
+ if ((*bsize) < 1024) {
+ dfree_retval = (*dfree)/(1024/(*bsize));
+ } else {
+ dfree_retval = ((*bsize)/1024)*(*dfree);
+ }
+
+ return(dfree_retval);
+}
+
+
+/****************************************************************************
+wrap it to get filenames right
+****************************************************************************/
+SMB_BIG_UINT sys_disk_free(const char *path, BOOL small_query,
+ SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ return disk_free(path,small_query, bsize,dfree,dsize);
+}
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index ac6f918b9d..f56e0e9ef0 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -1,8 +1,7 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
Directory handling routines
- Copyright (C) Andrew Tridgell 1992-1995
+ Copyright (C) Andrew Tridgell 1992-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,351 +19,505 @@
*/
#include "includes.h"
-#include "loadparm.h"
-
-extern int DEBUGLEVEL;
-extern connection_struct Connections[];
/*
This module implements directory related functions for Samba.
*/
-
-
-uint32 dircounter = 0;
-
-
-#define NUMDIRPTRS 256
-
-
-static struct dptr_struct
-{
- int pid;
- int cnum;
- uint32 lastused;
- void *ptr;
- BOOL valid;
- BOOL finished;
- BOOL expect_close;
- char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */
- uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */
- char *path;
-}
-dirptrs[NUMDIRPTRS];
-
+typedef struct _dptr_struct {
+ struct _dptr_struct *next, *prev;
+ int dnum;
+ uint16 spid;
+ connection_struct *conn;
+ void *ptr;
+ BOOL expect_close;
+ char *wcard; /* Field only used for trans2_ searches */
+ uint16 attr; /* Field only used for trans2_ searches */
+ char *path;
+} dptr_struct;
+
+static struct bitmap *dptr_bmap;
+static dptr_struct *dirptrs;
static int dptrs_open = 0;
+#define INVALID_DPTR_KEY (-3)
+
/****************************************************************************
-initialise the dir array
+ Initialise the dir bitmap.
****************************************************************************/
+
void init_dptrs(void)
{
static BOOL dptrs_init=False;
- int i;
- if (dptrs_init) return;
- for (i=0;i<NUMDIRPTRS;i++)
- {
- dirptrs[i].valid = False;
- dirptrs[i].wcard = NULL;
- dirptrs[i].ptr = NULL;
- string_init(&dirptrs[i].path,"");
- }
+ if (dptrs_init)
+ return;
+
+ dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES);
+
+ if (!dptr_bmap)
+ exit_server("out of memory in init_dptrs");
+
dptrs_init = True;
}
/****************************************************************************
-idle a dptr - the directory is closed but the control info is kept
+ Idle a dptr - the directory is closed but the control info is kept.
****************************************************************************/
-static void dptr_idle(int key)
+
+static void dptr_idle(dptr_struct *dptr)
{
- if (dirptrs[key].valid && dirptrs[key].ptr) {
- DEBUG(4,("Idling dptr key %d\n",key));
+ if (dptr->ptr) {
+ DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
dptrs_open--;
- CloseDir(dirptrs[key].ptr);
- dirptrs[key].ptr = NULL;
- }
+ CloseDir(dptr->ptr);
+ dptr->ptr = NULL;
+ }
}
/****************************************************************************
-idle the oldest dptr
+ Idle the oldest dptr.
****************************************************************************/
+
static void dptr_idleoldest(void)
{
- int i;
- uint32 old=dircounter+1;
- int oldi= -1;
- for (i=0;i<NUMDIRPTRS;i++)
- if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) {
- old = dirptrs[i].lastused;
- oldi = i;
+ dptr_struct *dptr;
+
+ /*
+ * Go to the end of the list.
+ */
+ for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
+ ;
+
+ if(!dptr) {
+ DEBUG(0,("No dptrs available to idle ?\n"));
+ return;
+ }
+
+ /*
+ * Idle the oldest pointer.
+ */
+
+ for(; dptr; dptr = dptr->prev) {
+ if (dptr->ptr) {
+ dptr_idle(dptr);
+ return;
}
- if (oldi != -1)
- dptr_idle(oldi);
- else
- DEBUG(0,("No dptrs available to idle??\n"));
+ }
}
/****************************************************************************
-get the dir ptr for a dir index
+ Get the dptr_struct for a dir index.
****************************************************************************/
-static void *dptr_get(int key,uint32 lastused)
-{
- if (dirptrs[key].valid) {
- if (lastused) dirptrs[key].lastused = lastused;
- if (!dirptrs[key].ptr) {
- if (dptrs_open >= MAXDIR)
- dptr_idleoldest();
- DEBUG(4,("Reopening dptr key %d\n",key));
- if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path)))
- dptrs_open++;
+
+static dptr_struct *dptr_get(int key, BOOL forclose)
+{
+ dptr_struct *dptr;
+
+ for(dptr = dirptrs; dptr; dptr = dptr->next) {
+ if(dptr->dnum == key) {
+ if (!forclose && !dptr->ptr) {
+ if (dptrs_open >= MAX_OPEN_DIRECTORIES)
+ dptr_idleoldest();
+ DEBUG(4,("Reopening dptr key %d\n",key));
+ if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True)))
+ dptrs_open++;
+ }
+ DLIST_PROMOTE(dirptrs,dptr);
+ return dptr;
}
- return(dirptrs[key].ptr);
}
return(NULL);
}
/****************************************************************************
-get the dir path for a dir index
+ Get the dptr ptr for a dir index.
+****************************************************************************/
+
+static void *dptr_ptr(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->ptr);
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dir path for a dir index.
****************************************************************************/
+
char *dptr_path(int key)
{
- if (dirptrs[key].valid)
- return(dirptrs[key].path);
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->path);
return(NULL);
}
/****************************************************************************
-get the dir wcard for a dir index (lanman2 specific)
+ Get the dir wcard for a dir index (lanman2 specific).
****************************************************************************/
+
char *dptr_wcard(int key)
{
- if (dirptrs[key].valid)
- return(dirptrs[key].wcard);
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->wcard);
return(NULL);
}
/****************************************************************************
-set the dir wcard for a dir index (lanman2 specific)
-Returns 0 on ok, 1 on fail.
+ Set the dir wcard for a dir index (lanman2 specific).
+ Returns 0 on ok, 1 on fail.
****************************************************************************/
+
BOOL dptr_set_wcard(int key, char *wcard)
{
- if (dirptrs[key].valid) {
- dirptrs[key].wcard = wcard;
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr) {
+ dptr->wcard = wcard;
return True;
}
return False;
}
/****************************************************************************
-set the dir attrib for a dir index (lanman2 specific)
-Returns 0 on ok, 1 on fail.
+ Set the dir attrib for a dir index (lanman2 specific).
+ Returns 0 on ok, 1 on fail.
****************************************************************************/
+
BOOL dptr_set_attr(int key, uint16 attr)
{
- if (dirptrs[key].valid) {
- dirptrs[key].attr = attr;
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr) {
+ dptr->attr = attr;
return True;
}
return False;
}
/****************************************************************************
-get the dir attrib for a dir index (lanman2 specific)
+ Get the dir attrib for a dir index (lanman2 specific)
****************************************************************************/
+
uint16 dptr_attr(int key)
{
- if (dirptrs[key].valid)
- return(dirptrs[key].attr);
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->attr);
return(0);
}
/****************************************************************************
-close a dptr
+ Close a dptr (internal func).
+****************************************************************************/
+
+static void dptr_close_internal(dptr_struct *dptr)
+{
+ DEBUG(4,("closing dptr key %d\n",dptr->dnum));
+
+ DLIST_REMOVE(dirptrs, dptr);
+
+ /*
+ * Free the dnum in the bitmap. Remember the dnum value is always
+ * biased by one with respect to the bitmap.
+ */
+
+ if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) {
+ DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n",
+ dptr->dnum ));
+ }
+
+ bitmap_clear(dptr_bmap, dptr->dnum - 1);
+
+ if (dptr->ptr) {
+ CloseDir(dptr->ptr);
+ dptrs_open--;
+ }
+
+ /* Lanman 2 specific code */
+ SAFE_FREE(dptr->wcard);
+ string_set(&dptr->path,"");
+ SAFE_FREE(dptr);
+}
+
+/****************************************************************************
+ Close a dptr given a key.
****************************************************************************/
-void dptr_close(int key)
+
+void dptr_close(int *key)
{
- if (dirptrs[key].valid) {
- DEBUG(4,("closing dptr key %d\n",key));
- if (dirptrs[key].ptr) {
- CloseDir(dirptrs[key].ptr);
- dptrs_open--;
+ dptr_struct *dptr;
+
+ if(*key == INVALID_DPTR_KEY)
+ return;
+
+ /* OS/2 seems to use -1 to indicate "close all directories" */
+ if (*key == -1) {
+ dptr_struct *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ dptr_close_internal(dptr);
}
- /* Lanman 2 specific code */
- if (dirptrs[key].wcard)
- free(dirptrs[key].wcard);
- dirptrs[key].valid = False;
- string_set(&dirptrs[key].path,"");
+ *key = INVALID_DPTR_KEY;
+ return;
}
+
+ dptr = dptr_get(*key, True);
+
+ if (!dptr) {
+ DEBUG(0,("Invalid key %d given to dptr_close\n", *key));
+ return;
+ }
+
+ dptr_close_internal(dptr);
+
+ *key = INVALID_DPTR_KEY;
}
/****************************************************************************
-close all dptrs for a cnum
+ Close all dptrs for a cnum.
****************************************************************************/
-void dptr_closecnum(int cnum)
+
+void dptr_closecnum(connection_struct *conn)
{
- int i;
- for (i=0;i<NUMDIRPTRS;i++)
- if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
- dptr_close(i);
+ dptr_struct *dptr, *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ if (dptr->conn == conn)
+ dptr_close_internal(dptr);
+ }
}
/****************************************************************************
-idle all dptrs for a cnum
+ Idle all dptrs for a cnum.
****************************************************************************/
-void dptr_idlecnum(int cnum)
+
+void dptr_idlecnum(connection_struct *conn)
{
- int i;
- for (i=0;i<NUMDIRPTRS;i++)
- if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
- dptr_idle(i);
+ dptr_struct *dptr;
+ for(dptr = dirptrs; dptr; dptr = dptr->next) {
+ if (dptr->conn == conn && dptr->ptr)
+ dptr_idle(dptr);
+ }
}
/****************************************************************************
-close a dptr that matches a given path, only if it matches the pid also
+ Close a dptr that matches a given path, only if it matches the spid also.
****************************************************************************/
-void dptr_closepath(char *path,int pid)
+
+void dptr_closepath(char *path,uint16 spid)
{
- int i;
- for (i=0;i<NUMDIRPTRS;i++)
- if (dirptrs[i].valid && pid == dirptrs[i].pid &&
- strequal(dirptrs[i].path,path))
- dptr_close(i);
+ dptr_struct *dptr, *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ if (spid == dptr->spid && strequal(dptr->path,path))
+ dptr_close_internal(dptr);
+ }
}
/****************************************************************************
- start a directory listing
+ Start a directory listing.
****************************************************************************/
-static BOOL start_dir(int cnum,char *directory)
+
+static BOOL start_dir(connection_struct *conn,char *directory)
{
- DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
+ DEBUG(5,("start_dir dir=%s\n",directory));
- if (!check_name(directory,cnum))
+ if (!check_name(directory,conn))
return(False);
if (! *directory)
directory = ".";
- Connections[cnum].dirptr = OpenDir(directory);
- if (Connections[cnum].dirptr) {
+ conn->dirptr = OpenDir(conn, directory, True);
+ if (conn->dirptr) {
dptrs_open++;
- string_set(&Connections[cnum].dirpath,directory);
+ string_set(&conn->dirpath,directory);
return(True);
}
return(False);
}
+/****************************************************************************
+ Try and close the oldest handle not marked for
+ expect close in the hope that the client has
+ finished with that one.
+****************************************************************************/
+
+static void dptr_close_oldest(BOOL old)
+{
+ dptr_struct *dptr;
+
+ /*
+ * Go to the end of the list.
+ */
+ for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
+ ;
+
+ if(!dptr) {
+ DEBUG(0,("No old dptrs available to close oldest ?\n"));
+ return;
+ }
+
+ /*
+ * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that
+ * does not have expect_close set. If 'old' is false, close
+ * one of the new dnum handles.
+ */
+
+ for(; dptr; dptr = dptr->prev) {
+ if ((old && (dptr->dnum < 256) && !dptr->expect_close) ||
+ (!old && (dptr->dnum > 255))) {
+ dptr_close_internal(dptr);
+ return;
+ }
+ }
+}
/****************************************************************************
-create a new dir ptr
+ Create a new dir ptr. If the flag old_handle is true then we must allocate
+ from the bitmap range 0 - 255 as old SMBsearch directory handles are only
+ one byte long. If old_handle is false we allocate from the range
+ 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure
+ a directory handle is never zero. All the above is folklore taught to
+ me at Andrew's knee.... :-) :-). JRA.
****************************************************************************/
-int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
+
+int dptr_create(connection_struct *conn,char *path, BOOL old_handle, BOOL expect_close,uint16 spid)
{
- int i;
- uint32 old;
- int oldi;
+ dptr_struct *dptr;
- if (!start_dir(cnum,path))
- return(-1);
+ if (!start_dir(conn,path))
+ return(-2); /* Code to say use a unix error return code. */
- if (dptrs_open >= MAXDIR)
+ if (dptrs_open >= MAX_OPEN_DIRECTORIES)
dptr_idleoldest();
- for (i=0;i<NUMDIRPTRS;i++)
- if (!dirptrs[i].valid)
- break;
- if (i == NUMDIRPTRS) i = -1;
+ dptr = (dptr_struct *)malloc(sizeof(dptr_struct));
+ if(!dptr) {
+ DEBUG(0,("malloc fail in dptr_create.\n"));
+ return -1;
+ }
+
+ ZERO_STRUCTP(dptr);
+ if(old_handle) {
- /* as a 2nd option, grab the oldest not marked for expect_close */
- if (i == -1) {
- old=dircounter+1;
- oldi= -1;
- for (i=0;i<NUMDIRPTRS;i++)
- if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
- old = dirptrs[i].lastused;
- oldi = i;
+ /*
+ * This is an old-style SMBsearch request. Ensure the
+ * value we return will fit in the range 1-255.
+ */
+
+ dptr->dnum = bitmap_find(dptr_bmap, 0);
+
+ if(dptr->dnum == -1 || dptr->dnum > 254) {
+
+ /*
+ * Try and close the oldest handle not marked for
+ * expect close in the hope that the client has
+ * finished with that one.
+ */
+
+ dptr_close_oldest(True);
+
+ /* Now try again... */
+ dptr->dnum = bitmap_find(dptr_bmap, 0);
+
+ if(dptr->dnum == -1 || dptr->dnum > 254) {
+ DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
+ SAFE_FREE(dptr);
+ return -1;
}
- i = oldi;
- }
+ }
+ } else {
+
+ /*
+ * This is a new-style trans2 request. Allocate from
+ * a range that will return 256 - MAX_DIRECTORY_HANDLES.
+ */
+
+ dptr->dnum = bitmap_find(dptr_bmap, 255);
+
+ if(dptr->dnum == -1 || dptr->dnum < 255) {
+
+ /*
+ * Try and close the oldest handle close in the hope that
+ * the client has finished with that one. This will only
+ * happen in the case of the Win98 client bug where it leaks
+ * directory handles.
+ */
+
+ dptr_close_oldest(False);
- /* a 3rd option - grab the oldest one */
- if (i == -1) {
- old=dircounter+1;
- oldi= -1;
- for (i=0;i<NUMDIRPTRS;i++)
- if (dirptrs[i].lastused < old) {
- old = dirptrs[i].lastused;
- oldi = i;
+ /* Now try again... */
+ dptr->dnum = bitmap_find(dptr_bmap, 255);
+
+ if(dptr->dnum == -1 || dptr->dnum < 255) {
+ DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
+ SAFE_FREE(dptr);
+ return -1;
}
- i = oldi;
+ }
}
- if (i == -1) {
- DEBUG(0,("Error - all dirptrs in use??\n"));
- return(-1);
- }
+ bitmap_set(dptr_bmap, dptr->dnum);
+
+ dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
- if (dirptrs[i].valid)
- dptr_close(i);
+ dptr->ptr = conn->dirptr;
+ string_set(&dptr->path,path);
+ dptr->conn = conn;
+ dptr->spid = spid;
+ dptr->expect_close = expect_close;
+ dptr->wcard = NULL; /* Only used in lanman2 searches */
+ dptr->attr = 0; /* Only used in lanman2 searches */
- dirptrs[i].ptr = Connections[cnum].dirptr;
- string_set(&dirptrs[i].path,path);
- dirptrs[i].lastused = dircounter++;
- dirptrs[i].finished = False;
- dirptrs[i].cnum = cnum;
- dirptrs[i].pid = pid;
- dirptrs[i].expect_close = expect_close;
- dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
- dirptrs[i].attr = 0; /* Only used in lanman2 searches */
- dirptrs[i].valid = True;
+ DLIST_ADD(dirptrs, dptr);
DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
- i,path,expect_close));
+ dptr->dnum,path,expect_close));
- return(i);
+ return(dptr->dnum);
}
-#define DPTR_MASK ((uint32)(((uint32)1)<<31))
-
/****************************************************************************
-fill the 5 byte server reserved dptr field
+ Fill the 5 byte server reserved dptr field.
****************************************************************************/
+
BOOL dptr_fill(char *buf1,unsigned int key)
{
unsigned char *buf = (unsigned char *)buf1;
- void *p = dptr_get(key,0);
+ void *p = dptr_ptr(key);
uint32 offset;
if (!p) {
DEBUG(1,("filling null dirptr %d\n",key));
return(False);
}
offset = TellDir(p);
- DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset));
+ DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
+ (long)p,(int)offset));
buf[0] = key;
SIVAL(buf,1,offset | DPTR_MASK);
return(True);
}
-
/****************************************************************************
-return True is the offset is at zero
+ Fetch the dir ptr and seek it given the 5 byte server field.
****************************************************************************/
-BOOL dptr_zero(char *buf)
-{
- return((IVAL(buf,1)&~DPTR_MASK) == 0);
-}
-/****************************************************************************
-fetch the dir ptr and seek it given the 5 byte server field
-****************************************************************************/
void *dptr_fetch(char *buf,int *num)
{
unsigned int key = *(unsigned char *)buf;
- void *p = dptr_get(key,dircounter++);
+ void *p = dptr_ptr(key);
uint32 offset;
if (!p) {
DEBUG(3,("fetched null dirptr %d\n",key));
@@ -379,101 +532,117 @@ void *dptr_fetch(char *buf,int *num)
}
/****************************************************************************
-fetch the dir ptr and seek it given the lanman2 parameter block
+ Fetch the dir ptr.
****************************************************************************/
-void *dptr_fetch_lanman2(char *params,int dptr_num)
+
+void *dptr_fetch_lanman2(int dptr_num)
{
- void *p = dptr_get(dptr_num,dircounter++);
- uint32 resume_key = SVAL(params,6);
- BOOL uses_resume_key = BITSETW(params+10,2);
- BOOL continue_bit = BITSETW(params+10,3);
+ void *p = dptr_ptr(dptr_num);
if (!p) {
DEBUG(3,("fetched null dirptr %d\n",dptr_num));
return(NULL);
}
- if(uses_resume_key && !continue_bit)
- SeekDir(p,resume_key);
DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
return(p);
}
/****************************************************************************
- get a directory entry
+ Check a filetype for being valid.
+****************************************************************************/
+
+BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype)
+{
+ if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ return False;
+ return True;
+}
+
+/****************************************************************************
+ Get an 8.3 directory entry.
****************************************************************************/
-BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
+
+BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname,
+ SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
{
char *dname;
BOOL found = False;
- struct stat sbuf;
+ SMB_STRUCT_STAT sbuf;
pstring path;
pstring pathreal;
BOOL isrootdir;
pstring filename;
- BOOL matched;
+ BOOL needslash;
*path = *pathreal = *filename = 0;
- isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
- strequal(Connections[cnum].dirpath,".") ||
- strequal(Connections[cnum].dirpath,"/"));
+ isrootdir = (strequal(conn->dirpath,"./") ||
+ strequal(conn->dirpath,".") ||
+ strequal(conn->dirpath,"/"));
- if (!Connections[cnum].dirptr)
+ needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
+
+ if (!conn->dirptr)
return(False);
-
+
while (!found)
- {
- dname = ReadDirName(Connections[cnum].dirptr);
+ {
+ dname = ReadDirName(conn->dirptr);
- DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
- Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
+ DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
+ (long)conn->dirptr,TellDir(conn->dirptr)));
- if (dname == NULL)
- return(False);
+ if (dname == NULL)
+ return(False);
- matched = False;
-
- strcpy(filename,dname);
-
- if ((strcmp(filename,mask) == 0) ||
- (name_map_mangle(filename,True,SNUM(cnum)) &&
- mask_match(filename,mask,False,False)))
- {
- if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
- continue;
-
- strcpy(fname,filename);
- *path = 0;
- strcpy(path,Connections[cnum].dirpath);
- strcat(path,"/");
- strcpy(pathreal,path);
- strcat(path,fname);
- strcat(pathreal,dname);
- if (sys_stat(pathreal,&sbuf) != 0)
- {
- DEBUG(5,("Couldn't stat 1 [%s]\n",path));
- continue;
- }
+ pstrcpy(filename,dname);
+
+ /* notice the special *.* handling. This appears to be the only difference
+ between the wildcard handling in this routine and in the trans2 routines.
+ see masktest for a demo
+ */
+ if ((strcmp(mask,"*.*") == 0) ||
+ mask_match(filename,mask,False) ||
+ (mangle_map(filename,True,False,SNUM(conn)) &&
+ mask_match(filename,mask,False)))
+ {
+ if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
+ continue;
+
+ if (!mangle_is_8_3(filename, False)) {
+ mangle_map(filename,True,False,SNUM(conn));
+ }
- if (check_descend &&
- !strequal(fname,".") && !strequal(fname,".."))
- continue;
+ pstrcpy(fname,filename);
+ *path = 0;
+ pstrcpy(path,conn->dirpath);
+ if(needslash)
+ pstrcat(path,"/");
+ pstrcpy(pathreal,path);
+ pstrcat(path,fname);
+ pstrcat(pathreal,dname);
+ if (conn->vfs_ops.stat(conn, pathreal, &sbuf) != 0)
+ {
+ DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) ));
+ continue;
+ }
- *mode = dos_mode(cnum,pathreal,&sbuf);
+ *mode = dos_mode(conn,pathreal,&sbuf);
- if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
- {
- DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
- continue;
- }
- *size = sbuf.st_size;
- *date = sbuf.st_mtime;
+ if (!dir_check_ftype(conn,*mode,&sbuf,dirtype))
+ {
+ DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+ continue;
+ }
+
+ *size = sbuf.st_size;
+ *date = sbuf.st_mtime;
- DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
+ DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
- found = True;
- }
+ found = True;
}
+ }
return(found);
}
@@ -490,27 +659,109 @@ typedef struct
} Dir;
+
+/*******************************************************************
+check to see if a user can read a file. This is only approximate,
+it is used as part of the "hide unreadable" option. Don't
+use it for anything security sensitive
+********************************************************************/
+
+static BOOL user_can_read_file(connection_struct *conn, char *name)
+{
+ extern struct current_user current_user;
+ SMB_STRUCT_STAT ste;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ files_struct *fsp;
+ int smb_action;
+ int access_mode;
+ NTSTATUS status;
+ uint32 access_granted;
+
+ ZERO_STRUCT(ste);
+
+ /* if we can't stat it does not show it */
+ if (vfs_stat(conn, name, &ste) != 0)
+ return False;
+
+ /* Pseudo-open the file (note - no fd's created). */
+
+ if(S_ISDIR(ste.st_mode))
+ fsp = open_directory(conn, name, &ste, 0, SET_DENY_MODE(DENY_NONE), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ unix_mode(conn,aRONLY|aDIR, name), &smb_action);
+ else
+ fsp = open_file_shared1(conn, name, &ste, FILE_READ_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action);
+
+ if (!fsp)
+ return False;
+
+ /* Get NT ACL -allocated in main loop talloc context. No free needed here. */
+ sd_size = conn->vfs_ops.fget_nt_acl(fsp, fsp->fd, &psd);
+ close_file(fsp, False);
+
+ /* No access if SD get failed. */
+ if (!sd_size)
+ return False;
+
+ return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA,
+ &access_granted, &status);
+}
+
/*******************************************************************
-open a directory
+ Open a directory.
********************************************************************/
-void *OpenDir(char *name)
+
+void *OpenDir(connection_struct *conn, char *name, BOOL use_veto)
{
Dir *dirp;
char *n;
- void *p = sys_opendir(name);
+ DIR *p = conn->vfs_ops.opendir(conn,name);
int used=0;
if (!p) return(NULL);
dirp = (Dir *)malloc(sizeof(Dir));
if (!dirp) {
- closedir(p);
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ conn->vfs_ops.closedir(conn,p);
return(NULL);
}
dirp->pos = dirp->numentries = dirp->mallocsize = 0;
dirp->data = dirp->current = NULL;
- while ((n = readdirname(p))) {
- int l = strlen(n)+1;
+ while (True)
+ {
+ int l;
+
+ if (used == 0) {
+ n = ".";
+ } else if (used == 2) {
+ n = "..";
+ } else {
+ n = vfs_readdirname(conn, p);
+ if (n == NULL)
+ break;
+ if ((strcmp(".",n) == 0) ||(strcmp("..",n) == 0))
+ continue;
+ }
+
+ l = strlen(n)+1;
+
+ /* If it's a vetoed file, pretend it doesn't even exist */
+ if (use_veto && conn && IS_VETO_PATH(conn, n)) continue;
+
+ /* Honour _hide unreadable_ option */
+ if (conn && lp_hideunreadable(SNUM(conn))) {
+ char *entry;
+ int ret=0;
+
+ if (asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = user_can_read_file(conn, entry);
+ SAFE_FREE(entry);
+ }
+ if (!ret) continue;
+ }
+
if (used + l > dirp->mallocsize) {
int s = MAX(used+l,used+2000);
char *r;
@@ -523,30 +774,31 @@ void *OpenDir(char *name)
dirp->mallocsize = s;
dirp->current = dirp->data;
}
- strcpy(dirp->data+used,n);
+ pstrcpy(dirp->data+used,n);
used += l;
dirp->numentries++;
}
- closedir(p);
+ conn->vfs_ops.closedir(conn,p);
return((void *)dirp);
}
/*******************************************************************
-close a directory
+ Close a directory.
********************************************************************/
+
void CloseDir(void *p)
{
- Dir *dirp = (Dir *)p;
- if (!dirp) return;
- if (dirp->data) free(dirp->data);
- free(dirp);
+ if (!p) return;
+ SAFE_FREE(((Dir *)p)->data);
+ SAFE_FREE(p);
}
/*******************************************************************
-read from a directory
+ Read from a directory.
********************************************************************/
+
char *ReadDirName(void *p)
{
char *ret;
@@ -563,8 +815,9 @@ char *ReadDirName(void *p)
/*******************************************************************
-seek a dir
+ Seek a dir.
********************************************************************/
+
BOOL SeekDir(void *p,int pos)
{
Dir *dirp = (Dir *)p;
@@ -582,8 +835,9 @@ BOOL SeekDir(void *p,int pos)
}
/*******************************************************************
-tell a dir position
+ Tell a dir position.
********************************************************************/
+
int TellDir(void *p)
{
Dir *dirp = (Dir *)p;
@@ -593,363 +847,113 @@ int TellDir(void *p)
return(dirp->pos);
}
+/*******************************************************************************
+ This section manages a global directory cache.
+ (It should probably be split into a separate module. crh)
+********************************************************************************/
+
+typedef struct {
+ ubi_dlNode node;
+ char *path;
+ char *name;
+ char *dname;
+ int snum;
+} dir_cache_entry;
+
+static ubi_dlNewList( dir_cache );
+
+/*****************************************************************************
+ Add an entry to the directory cache.
+ Input: path -
+ name -
+ dname -
+ snum -
+ Output: None.
+*****************************************************************************/
+
+void DirCacheAdd( char *path, char *name, char *dname, int snum )
+{
+ int pathlen;
+ int namelen;
+ dir_cache_entry *entry;
+
+ /* Allocate the structure & string space in one go so that it can be freed
+ * in one call to free().
+ */
+ pathlen = strlen( path ) +1; /* Bytes required to store path (with nul). */
+ namelen = strlen( name ) +1; /* Bytes required to store name (with nul). */
+ entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
+ + pathlen
+ + namelen
+ + strlen( dname ) +1 );
+ if( NULL == entry ) /* Not adding to the cache is not fatal, */
+ return; /* so just return as if nothing happened. */
+
+ /* Set pointers correctly and load values. */
+ entry->path = pstrcpy( (char *)&entry[1], path);
+ entry->name = pstrcpy( &(entry->path[pathlen]), name);
+ entry->dname = pstrcpy( &(entry->name[namelen]), dname);
+ entry->snum = snum;
+
+ /* Add the new entry to the linked list. */
+ (void)ubi_dlAddHead( dir_cache, entry );
+ DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
+
+ /* Free excess cache entries. */
+ while( DIRCACHESIZE < dir_cache->count )
+ safe_free( ubi_dlRemTail( dir_cache ) );
-static int dir_cache_size = 0;
-static struct dir_cache {
- struct dir_cache *next;
- struct dir_cache *prev;
- char *path;
- char *name;
- char *dname;
- int snum;
-} *dir_cache = NULL;
-
-/*******************************************************************
-add an entry to the directory cache
-********************************************************************/
-void DirCacheAdd(char *path,char *name,char *dname,int snum)
-{
- struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
- if (!entry) return;
- entry->path = strdup(path);
- entry->name = strdup(name);
- entry->dname = strdup(dname);
- entry->snum = snum;
- if (!entry->path || !entry->name || !entry->dname) return;
-
- entry->next = dir_cache;
- entry->prev = NULL;
- if (entry->next) entry->next->prev = entry;
- dir_cache = entry;
-
- DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
-
- if (dir_cache_size == DIRCACHESIZE) {
- for (entry=dir_cache; entry->next; entry=entry->next) ;
- free(entry->path);
- free(entry->name);
- free(entry->dname);
- if (entry->prev) entry->prev->next = entry->next;
- free(entry);
- } else {
- dir_cache_size++;
- }
}
+/*****************************************************************************
+ Search for an entry to the directory cache.
+ Input: path -
+ name -
+ snum -
+ Output: The dname string of the located entry, or NULL if the entry was
+ not found.
-/*******************************************************************
-check for an entry in the directory cache
-********************************************************************/
-char *DirCacheCheck(char *path,char *name,int snum)
+ Notes: This uses a linear search, which is is okay because of
+ the small size of the cache. Use a splay tree or hash
+ for large caches.
+*****************************************************************************/
+
+char *DirCacheCheck( char *path, char *name, int snum )
{
- struct dir_cache *entry;
+ dir_cache_entry *entry;
- for (entry=dir_cache; entry; entry=entry->next) {
- if (entry->snum == snum &&
- strcmp(path,entry->path) == 0 &&
- strcmp(name,entry->name) == 0) {
- DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
- return(entry->dname);
+ for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
+ NULL != entry;
+ entry = (dir_cache_entry *)ubi_dlNext( entry ) )
+ {
+ if( entry->snum == snum
+ && 0 == strcmp( name, entry->name )
+ && 0 == strcmp( path, entry->path ) )
+ {
+ DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
+ return( entry->dname );
+ }
}
- }
return(NULL);
}
-/*******************************************************************
-flush entries in the dir_cache
-********************************************************************/
+/*****************************************************************************
+ Remove all cache entries which have an snum that matches the input.
+ Input: snum -
+ Output: None.
+*****************************************************************************/
+
void DirCacheFlush(int snum)
{
- struct dir_cache *entry,*next;
-
- for (entry=dir_cache; entry; entry=next) {
- if (entry->snum == snum) {
- free(entry->path);
- free(entry->dname);
- free(entry->name);
- next = entry->next;
- if (entry->prev) entry->prev->next = entry->next;
- if (entry->next) entry->next->prev = entry->prev;
- if (dir_cache == entry) dir_cache = entry->next;
- free(entry);
- } else {
- next = entry->next;
- }
- }
-}
-
-
-#ifdef REPLACE_GETWD
-/* This is getcwd.c from bash. It is needed in Interactive UNIX. To
- * add support for another OS you need to determine which of the
- * conditional compilation macros you need to define. All the options
- * are defined for Interactive UNIX.
- */
-#ifdef ISC
-#define HAVE_UNISTD_H
-#define USGr3
-#define USG
-#endif
-
-#if defined (HAVE_UNISTD_H)
-# include <unistd.h>
-#endif
-
-#if defined (__STDC__)
-# define CONST const
-# define PTR void *
-#else /* !__STDC__ */
-# define CONST
-# define PTR char *
-#endif /* !__STDC__ */
-
-#if !defined (PATH_MAX)
-# if defined (MAXPATHLEN)
-# define PATH_MAX MAXPATHLEN
-# else /* !MAXPATHLEN */
-# define PATH_MAX 1024
-# endif /* !MAXPATHLEN */
-#endif /* !PATH_MAX */
-
-#if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
-# if !defined (HAVE_DIRENT)
-# define HAVE_DIRENT
-# endif /* !HAVE_DIRENT */
-#endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
-
-#if defined (HAVE_DIRENT)
-# define D_NAMLEN(d) (strlen ((d)->d_name))
-#else
-# define D_NAMLEN(d) ((d)->d_namlen)
-#endif /* ! (_POSIX_VERSION || USGr3) */
-
-#if defined (USG) || defined (USGr3)
-# define d_fileno d_ino
-#endif
-
-#if !defined (alloca)
-extern char *alloca ();
-#endif /* alloca */
-
-/* Get the pathname of the current working directory,
- and put it in SIZE bytes of BUF. Returns NULL if the
- directory couldn't be determined or SIZE was too small.
- If successful, returns BUF. In GNU, if BUF is NULL,
- an array is allocated with `malloc'; the array is SIZE
- bytes long, unless SIZE <= 0, in which case it is as
- big as necessary. */
-#if defined (__STDC__)
-char *
-getcwd (char *buf, size_t size)
-#else /* !__STDC__ */
-char *
-getcwd (buf, size)
- char *buf;
- int size;
-#endif /* !__STDC__ */
-{
- static CONST char dots[]
- = "../../../../../../../../../../../../../../../../../../../../../../../\
-../../../../../../../../../../../../../../../../../../../../../../../../../../\
-../../../../../../../../../../../../../../../../../../../../../../../../../..";
- CONST char *dotp, *dotlist;
- size_t dotsize;
- dev_t rootdev, thisdev;
- ino_t rootino, thisino;
- char path[PATH_MAX + 1];
- register char *pathp;
- char *pathbuf;
- size_t pathsize;
- struct stat st;
-
- if (buf != NULL && size == 0)
- {
- errno = EINVAL;
- return ((char *)NULL);
- }
-
- pathsize = sizeof (path);
- pathp = &path[pathsize];
- *--pathp = '\0';
- pathbuf = path;
-
- if (stat (".", &st) < 0)
- return ((char *)NULL);
- thisdev = st.st_dev;
- thisino = st.st_ino;
-
- if (stat ("/", &st) < 0)
- return ((char *)NULL);
- rootdev = st.st_dev;
- rootino = st.st_ino;
-
- dotsize = sizeof (dots) - 1;
- dotp = &dots[sizeof (dots)];
- dotlist = dots;
- while (!(thisdev == rootdev && thisino == rootino))
- {
- register DIR *dirstream;
- register struct dirent *d;
- dev_t dotdev;
- ino_t dotino;
- char mount_point;
- int namlen;
-
- /* Look at the parent directory. */
- if (dotp == dotlist)
- {
- /* My, what a deep directory tree you have, Grandma. */
- char *new;
- if (dotlist == dots)
- {
- new = malloc (dotsize * 2 + 1);
- if (new == NULL)
- goto lose;
- memcpy (new, dots, dotsize);
- }
- else
- {
- new = realloc ((PTR) dotlist, dotsize * 2 + 1);
- if (new == NULL)
- goto lose;
- }
- memcpy (&new[dotsize], new, dotsize);
- dotp = &new[dotsize];
- dotsize *= 2;
- new[dotsize] = '\0';
- dotlist = new;
+ dir_cache_entry *entry;
+ ubi_dlNodePtr next;
+
+ for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
+ NULL != entry; ) {
+ next = ubi_dlNext( entry );
+ if( entry->snum == snum )
+ safe_free( ubi_dlRemThis( dir_cache, entry ) );
+ entry = (dir_cache_entry *)next;
}
-
- dotp -= 3;
-
- /* Figure out if this directory is a mount point. */
- if (stat (dotp, &st) < 0)
- goto lose;
- dotdev = st.st_dev;
- dotino = st.st_ino;
- mount_point = dotdev != thisdev;
-
- /* Search for the last directory. */
- dirstream = opendir(dotp);
- if (dirstream == NULL)
- goto lose;
- while ((d = (struct dirent *)readdir(dirstream)) != NULL)
- {
- if (d->d_name[0] == '.' &&
- (d->d_name[1] == '\0' ||
- (d->d_name[1] == '.' && d->d_name[2] == '\0')))
- continue;
- if (mount_point || d->d_fileno == thisino)
- {
- char *name;
-
- namlen = D_NAMLEN(d);
- name = (char *)
- alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
- memcpy (name, dotp, dotlist + dotsize - dotp);
- name[dotlist + dotsize - dotp] = '/';
- memcpy (&name[dotlist + dotsize - dotp + 1],
- d->d_name, namlen + 1);
- if (lstat (name, &st) < 0)
- {
- int save = errno;
- closedir(dirstream);
- errno = save;
- goto lose;
- }
- if (st.st_dev == thisdev && st.st_ino == thisino)
- break;
- }
- }
- if (d == NULL)
- {
- int save = errno;
- closedir(dirstream);
- errno = save;
- goto lose;
- }
- else
- {
- size_t space;
-
- while ((space = pathp - pathbuf) <= namlen)
- {
- char *new;
-
- if (pathbuf == path)
- {
- new = malloc (pathsize * 2);
- if (!new)
- goto lose;
- }
- else
- {
- new = realloc ((PTR) pathbuf, (pathsize * 2));
- if (!new)
- goto lose;
- pathp = new + space;
- }
- (void) memcpy (new + pathsize + space, pathp, pathsize - space);
- pathp = new + pathsize + space;
- pathbuf = new;
- pathsize *= 2;
- }
-
- pathp -= namlen;
- (void) memcpy (pathp, d->d_name, namlen);
- *--pathp = '/';
- closedir(dirstream);
- }
-
- thisdev = dotdev;
- thisino = dotino;
- }
-
- if (pathp == &path[sizeof(path) - 1])
- *--pathp = '/';
-
- if (dotlist != dots)
- free ((PTR) dotlist);
-
- {
- size_t len = pathbuf + pathsize - pathp;
- if (buf == NULL)
- {
- if (len < (size_t) size)
- len = size;
- buf = (char *) malloc (len);
- if (buf == NULL)
- goto lose2;
- }
- else if ((size_t) size < len)
- {
- errno = ERANGE;
- goto lose2;
- }
- (void) memcpy((PTR) buf, (PTR) pathp, len);
- }
-
- if (pathbuf != path)
- free (pathbuf);
-
- return (buf);
-
- lose:
- if ((dotlist != dots) && dotlist)
- {
- int e = errno;
- free ((PTR) dotlist);
- errno = e;
- }
-
- lose2:
- if ((pathbuf != path) && pathbuf)
- {
- int e = errno;
- free ((PTR) pathbuf);
- errno = e;
- }
- return ((char *)NULL);
}
-#endif
diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c
new file mode 100644
index 0000000000..dcffe3aa90
--- /dev/null
+++ b/source3/smbd/dosmode.c
@@ -0,0 +1,337 @@
+/*
+ Unix SMB/CIFS implementation.
+ dos mode handling functions
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ change a dos mode to a unix mode
+ base permission for files:
+ if inheriting
+ apply read/write bits from parent directory.
+ else
+ everybody gets read bit set
+ dos readonly is represented in unix by removing everyone's write bit
+ dos archive is represented in unix by the user's execute bit
+ dos system is represented in unix by the group's execute bit
+ dos hidden is represented in unix by the other's execute bit
+ if !inheriting {
+ Then apply create mask,
+ then add force bits.
+ }
+ base permission for directories:
+ dos directory is represented in unix by unix's dir bit and the exec bit
+ if !inheriting {
+ Then apply create mask,
+ then add force bits.
+ }
+****************************************************************************/
+mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname)
+{
+ mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
+ mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
+
+ if ( !IS_DOS_READONLY(dosmode) )
+ result |= (S_IWUSR | S_IWGRP | S_IWOTH);
+
+ if (fname && lp_inherit_perms(SNUM(conn))) {
+ char *dname;
+ SMB_STRUCT_STAT sbuf;
+
+ dname = parent_dirname(fname);
+ DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
+ if (vfs_stat(conn,dname,&sbuf) != 0) {
+ DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
+ return(0); /* *** shouldn't happen! *** */
+ }
+
+ /* Save for later - but explicitly remove setuid bit for safety. */
+ dir_mode = sbuf.st_mode & ~S_ISUID;
+ DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
+ /* Clear "result" */
+ result = 0;
+ }
+
+ if (IS_DOS_DIR(dosmode)) {
+ /* We never make directories read only for the owner as under DOS a user
+ can always create a file in a read-only directory. */
+ result |= (S_IFDIR | S_IWUSR);
+
+ if (dir_mode) {
+ /* Inherit mode of parent directory. */
+ result |= dir_mode;
+ } else {
+ /* Provisionally add all 'x' bits */
+ result |= (S_IXUSR | S_IXGRP | S_IXOTH);
+
+ /* Apply directory mask */
+ result &= lp_dir_mask(SNUM(conn));
+ /* Add in force bits */
+ result |= lp_force_dir_mode(SNUM(conn));
+ }
+ } else {
+ if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
+ result |= S_IXUSR;
+
+ if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
+ result |= S_IXGRP;
+
+ if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
+ result |= S_IXOTH;
+
+ if (dir_mode) {
+ /* Inherit 666 component of parent directory mode */
+ result |= dir_mode
+ & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+ } else {
+ /* Apply mode mask */
+ result &= lp_create_mask(SNUM(conn));
+ /* Add in force bits */
+ result |= lp_force_create_mode(SNUM(conn));
+ }
+ }
+
+ DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
+ return(result);
+}
+
+
+/****************************************************************************
+ change a unix mode to a dos mode
+****************************************************************************/
+int dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf)
+{
+ int result = 0;
+
+ DEBUG(8,("dos_mode: %s\n", path));
+
+ if ((sbuf->st_mode & S_IWUSR) == 0)
+ result |= aRONLY;
+
+ if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
+ result |= aARCH;
+
+ if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
+ result |= aSYSTEM;
+
+ if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
+ result |= aHIDDEN;
+
+ if (S_ISDIR(sbuf->st_mode))
+ result = aDIR | (result & aRONLY);
+
+#ifdef S_ISLNK
+#if LINKS_READ_ONLY
+ if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+ result |= aRONLY;
+#endif
+#endif
+
+ /* hide files with a name starting with a . */
+ if (lp_hide_dot_files(SNUM(conn)))
+ {
+ char *p = strrchr_m(path,'/');
+ if (p)
+ p++;
+ else
+ p = path;
+
+ if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+ result |= aHIDDEN;
+ }
+
+ /* Optimization : Only call is_hidden_path if it's not already
+ hidden. */
+ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path))
+ {
+ result |= aHIDDEN;
+ }
+
+ DEBUG(8,("dos_mode returning "));
+
+ if (result & aHIDDEN) DEBUG(8, ("h"));
+ if (result & aRONLY ) DEBUG(8, ("r"));
+ if (result & aSYSTEM) DEBUG(8, ("s"));
+ if (result & aDIR ) DEBUG(8, ("d"));
+ if (result & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+
+ return(result);
+}
+
+/*******************************************************************
+chmod a file - but preserve some bits
+********************************************************************/
+int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st1;
+ int mask=0;
+ mode_t tmp;
+ mode_t unixmode;
+ int ret = -1;
+
+ if (!st) {
+ st = &st1;
+ if (vfs_stat(conn,fname,st))
+ return(-1);
+ }
+
+ if (S_ISDIR(st->st_mode))
+ dosmode |= aDIR;
+ else
+ dosmode &= ~aDIR;
+
+ if (dos_mode(conn,fname,st) == dosmode)
+ return(0);
+
+ unixmode = unix_mode(conn,dosmode,fname);
+
+ /* preserve the s bits */
+ mask |= (S_ISUID | S_ISGID);
+
+ /* preserve the t bit */
+#ifdef S_ISVTX
+ mask |= S_ISVTX;
+#endif
+
+ /* possibly preserve the x bits */
+ if (!MAP_ARCHIVE(conn))
+ mask |= S_IXUSR;
+ if (!MAP_SYSTEM(conn))
+ mask |= S_IXGRP;
+ if (!MAP_HIDDEN(conn))
+ mask |= S_IXOTH;
+
+ unixmode |= (st->st_mode & mask);
+
+ /* if we previously had any r bits set then leave them alone */
+ if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
+ unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
+ unixmode |= tmp;
+ }
+
+ /* if we previously had any w bits set then leave them alone
+ whilst adding in the new w bits, if the new mode is not rdonly */
+ if (!IS_DOS_READONLY(dosmode)) {
+ unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
+ }
+
+ if ((ret = vfs_chmod(conn,fname,unixmode)) == 0)
+ return 0;
+
+ if((errno != EPERM) && (errno != EACCES))
+ return -1;
+
+ if(!lp_dos_filemode(SNUM(conn)))
+ return -1;
+
+ /* We want DOS semantics, ie allow non owner with write permission to change the
+ bits on a file. Just like file_utime below.
+ */
+
+ /* Check if we have write access. */
+ if (CAN_WRITE(conn)) {
+ /*
+ * We need to open the file with write access whilst
+ * still in our current user context. This ensures we
+ * are not violating security in doing the fchmod.
+ * This file open does *not* break any oplocks we are
+ * holding. We need to review this.... may need to
+ * break batch oplocks open by others. JRA.
+ */
+ files_struct *fsp = open_file_fchmod(conn,fname,st);
+ if (!fsp)
+ return -1;
+ become_root();
+ ret = conn->vfs_ops.fchmod(fsp, fsp->fd, unixmode);
+ unbecome_root();
+ close_file_fchmod(fsp);
+ }
+
+ return( ret );
+}
+
+
+/*******************************************************************
+Wrapper around dos_utime that possibly allows DOS semantics rather
+than POSIX.
+*******************************************************************/
+int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
+{
+ extern struct current_user current_user;
+ SMB_STRUCT_STAT sb;
+ int ret = -1;
+
+ errno = 0;
+
+ if(conn->vfs_ops.utime(conn,fname, times) == 0)
+ return 0;
+
+ if((errno != EPERM) && (errno != EACCES))
+ return -1;
+
+ if(!lp_dos_filetimes(SNUM(conn)))
+ return -1;
+
+ /* We have permission (given by the Samba admin) to
+ break POSIX semantics and allow a user to change
+ the time on a file they don't own but can write to
+ (as DOS does).
+ */
+
+ if(vfs_stat(conn,fname,&sb) != 0)
+ return -1;
+
+ /* Check if we have write access. */
+ if (CAN_WRITE(conn)) {
+ if (((sb.st_mode & S_IWOTH) ||
+ conn->admin_user ||
+ ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
+ ((sb.st_mode & S_IWGRP) &&
+ in_group(sb.st_gid,current_user.gid,
+ current_user.ngroups,current_user.groups)))) {
+ /* We are allowed to become root and change the filetime. */
+ become_root();
+ ret = conn->vfs_ops.utime(conn,fname, times);
+ unbecome_root();
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+Change a filetime - possibly allowing DOS semantics.
+*******************************************************************/
+BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
+{
+ struct utimbuf times;
+
+ if (null_mtime(mtime)) return(True);
+
+ times.modtime = times.actime = mtime;
+
+ if (file_utime(conn, fname, &times)) {
+ DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
+ return False;
+ }
+
+ return(True);
+}
diff --git a/source3/smbd/error.c b/source3/smbd/error.c
new file mode 100644
index 0000000000..2f993fb41e
--- /dev/null
+++ b/source3/smbd/error.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS implementation.
+ error packet handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* these can be set by some functions to override the error codes */
+int unix_ERR_class=SMB_SUCCESS;
+int unix_ERR_code=0;
+
+/* From lib/error.c */
+extern struct unix_error_map unix_dos_nt_errmap[];
+
+/****************************************************************************
+ Create an error packet from a cached error.
+****************************************************************************/
+
+int cached_error_packet(char *outbuf,files_struct *fsp,int line,const char *file)
+{
+ write_bmpx_struct *wbmpx = fsp->wbmpx_ptr;
+
+ int32 eclass = wbmpx->wr_errclass;
+ int32 err = wbmpx->wr_error;
+
+ /* We can now delete the auxiliary struct */
+ free((char *)wbmpx);
+ fsp->wbmpx_ptr = NULL;
+ return error_packet(outbuf,NT_STATUS_OK,eclass,err,line,file);
+}
+
+/****************************************************************************
+ Create an error packet from errno.
+****************************************************************************/
+
+int unix_error_packet(char *outbuf,int def_class,uint32 def_code,
+ int line, const char *file)
+{
+ int eclass=def_class;
+ int ecode=def_code;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+ int i=0;
+
+ if (unix_ERR_class != SMB_SUCCESS) {
+ eclass = unix_ERR_class;
+ ecode = unix_ERR_code;
+ unix_ERR_class = SMB_SUCCESS;
+ unix_ERR_code = 0;
+ } else {
+ while (unix_dos_nt_errmap[i].dos_class != 0) {
+ if (unix_dos_nt_errmap[i].unix_error == errno) {
+ eclass = unix_dos_nt_errmap[i].dos_class;
+ ecode = unix_dos_nt_errmap[i].dos_code;
+ ntstatus = unix_dos_nt_errmap[i].nt_error;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return error_packet(outbuf,ntstatus,eclass,ecode,line,file);
+}
+
+
+/****************************************************************************
+ Create an error packet. Normally called using the ERROR() macro.
+****************************************************************************/
+
+int error_packet(char *outbuf,NTSTATUS ntstatus,
+ uint8 eclass,uint32 ecode,int line, const char *file)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ extern uint32 global_client_caps;
+
+ if (errno != 0)
+ DEBUG(3,("error string = %s\n",strerror(errno)));
+
+ /*
+ * We can explicitly force 32 bit error codes even when the
+ * parameter "nt status" is set to no by pre-setting the
+ * FLAGS2_32_BIT_ERROR_CODES bit in the smb_flg2 outbuf.
+ * This is to allow work arounds for client bugs that are needed
+ * when talking with clients that normally expect nt status codes. JRA.
+ */
+
+ if ((lp_nt_status_support() || (SVAL(outbuf,smb_flg2) & FLAGS2_32_BIT_ERROR_CODES)) && (global_client_caps & CAP_STATUS32)) {
+ if (NT_STATUS_V(ntstatus) == 0 && eclass)
+ ntstatus = dos_to_ntstatus(eclass, ecode);
+ SIVAL(outbuf,smb_rcls,NT_STATUS_V(ntstatus));
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)|FLAGS2_32_BIT_ERROR_CODES);
+ DEBUG(3,("error packet at %s(%d) cmd=%d (%s) %s\n",
+ file, line,
+ (int)CVAL(outbuf,smb_com),
+ smb_fn_name(CVAL(outbuf,smb_com)),
+ nt_errstr(ntstatus)));
+ return outsize;
+ }
+
+ if (eclass == 0 && NT_STATUS_V(ntstatus))
+ ntstatus_to_dos(ntstatus, &eclass, &ecode);
+
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)&~FLAGS2_32_BIT_ERROR_CODES);
+ SSVAL(outbuf,smb_rcls,eclass);
+ SSVAL(outbuf,smb_err,ecode);
+
+ DEBUG(3,("error packet at %s(%d) cmd=%d (%s) eclass=%d ecode=%d\n",
+ file, line,
+ (int)CVAL(outbuf,smb_com),
+ smb_fn_name(CVAL(outbuf,smb_com)),
+ eclass,
+ ecode));
+
+ return outsize;
+}
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
new file mode 100644
index 0000000000..addbcb0b3c
--- /dev/null
+++ b/source3/smbd/fileio.c
@@ -0,0 +1,659 @@
+/*
+ Unix SMB/CIFS implementation.
+ read/write to a files_struct
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
+
+/****************************************************************************
+seek a file. Try to avoid the seek if possible
+****************************************************************************/
+
+SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos)
+{
+ SMB_OFF_T offset = 0;
+ SMB_OFF_T seek_ret;
+
+ if (fsp->print_file && lp_postscript(fsp->conn->service))
+ offset = 3;
+
+ seek_ret = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,pos+offset,SEEK_SET);
+
+ if(seek_ret == -1) {
+ DEBUG(0,("seek_file: sys_lseek failed. Error was %s\n", strerror(errno) ));
+ fsp->pos = -1;
+ return -1;
+ }
+
+ fsp->pos = seek_ret - offset;
+
+ DEBUG(10,("seek_file: requested pos = %.0f, new pos = %.0f\n",
+ (double)(pos+offset), (double)fsp->pos ));
+
+ return(fsp->pos);
+}
+
+/****************************************************************************
+ Read from write cache if we can.
+****************************************************************************/
+
+
+BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+ write_cache *wcp = fsp->wcp;
+
+ if(!wcp)
+ return False;
+
+ if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
+ return False;
+
+ memcpy(data, wcp->data + (pos - wcp->offset), n);
+
+ DO_PROFILE_INC(writecache_read_hits);
+
+ return True;
+}
+
+/****************************************************************************
+read from a file
+****************************************************************************/
+
+ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+ ssize_t ret=0,readret;
+
+ /* you can't read from print files */
+ if (fsp->print_file)
+ return -1;
+
+ /*
+ * Serve from write cache if we can.
+ */
+
+ if(read_from_write_cache(fsp, data, pos, n))
+ return n;
+
+ flush_write_cache(fsp, READ_FLUSH);
+
+ if (seek_file(fsp,pos) == -1) {
+ DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos));
+ return(ret);
+ }
+
+ if (n > 0) {
+#ifdef DMF_FIX
+ int numretries = 3;
+tryagain:
+ readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
+ if (readret == -1) {
+ if ((errno == EAGAIN) && numretries) {
+ DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
+ (void)sleep(10);
+ --numretries;
+ goto tryagain;
+ }
+ return -1;
+ }
+#else /* NO DMF fix. */
+ readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
+ if (readret == -1)
+ return -1;
+#endif
+ if (readret > 0)
+ ret += readret;
+ }
+
+ return(ret);
+}
+
+/* how many write cache buffers have been allocated */
+static unsigned int allocated_write_caches;
+
+/****************************************************************************
+ *Really* write to a file.
+****************************************************************************/
+
+static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
+{
+ if ((pos != -1) && (seek_file(fsp,pos) == -1))
+ return -1;
+
+ return vfs_write_data(fsp,data,n);
+}
+
+/****************************************************************************
+write to a file
+****************************************************************************/
+
+ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
+{
+ write_cache *wcp = fsp->wcp;
+ ssize_t total_written = 0;
+ int write_path = -1;
+
+ if (fsp->print_file) {
+ return print_job_write(fsp->print_jobid, data, n);
+ }
+
+ if (!fsp->can_write) {
+ errno = EPERM;
+ return(0);
+ }
+
+ if (!fsp->modified) {
+ SMB_STRUCT_STAT st;
+ fsp->modified = True;
+
+ if (fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&st) == 0) {
+ int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
+ fsp->size = st.st_size;
+ if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode)) {
+ file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
+ }
+
+ /*
+ * If this is the first write and we have an exclusive oplock then setup
+ * the write cache.
+ */
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
+ setup_write_cache(fsp, st.st_size);
+ wcp = fsp->wcp;
+ }
+ }
+ }
+
+#ifdef WITH_PROFILE
+ DO_PROFILE_INC(writecache_total_writes);
+ if (!fsp->oplock_type) {
+ DO_PROFILE_INC(writecache_non_oplock_writes);
+ }
+#endif
+
+ /*
+ * If this file is level II oplocked then we need
+ * to grab the shared memory lock and inform all
+ * other files with a level II lock that they need
+ * to flush their read caches. We keep the lock over
+ * the shared memory area whilst doing this.
+ */
+
+ release_level_2_oplocks_on_change(fsp);
+
+#ifdef WITH_PROFILE
+ if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
+ DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
+nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
+ profile_p->writecache_init_writes,
+ profile_p->writecache_abutted_writes,
+ profile_p->writecache_total_writes,
+ profile_p->writecache_non_oplock_writes,
+ profile_p->writecache_allocated_write_caches,
+ profile_p->writecache_num_write_caches,
+ profile_p->writecache_direct_writes,
+ profile_p->writecache_num_perfect_writes,
+ profile_p->writecache_read_hits ));
+
+ DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
+ profile_p->writecache_flushed_writes[SEEK_FLUSH],
+ profile_p->writecache_flushed_writes[READ_FLUSH],
+ profile_p->writecache_flushed_writes[WRITE_FLUSH],
+ profile_p->writecache_flushed_writes[READRAW_FLUSH],
+ profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
+ profile_p->writecache_flushed_writes[CLOSE_FLUSH],
+ profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
+ }
+#endif
+
+ if(!wcp) {
+ DO_PROFILE_INC(writecache_direct_writes);
+ total_written = real_write_file(fsp, data, pos, n);
+ if ((total_written != -1) && (pos + total_written > fsp->size))
+ fsp->size = pos + total_written;
+ return total_written;
+ }
+
+ DEBUG(9,("write_file(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
+ fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
+
+ /*
+ * If we have active cache and it isn't contiguous then we flush.
+ * NOTE: There is a small problem with running out of disk ....
+ */
+
+ if (wcp->data_size) {
+
+ BOOL cache_flush_needed = False;
+
+ if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
+
+ /*
+ * Start of write overlaps or abutts the existing data.
+ */
+
+ size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
+
+ memcpy(wcp->data + (pos - wcp->offset), data, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + data_used > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + data_used - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size)
+ fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+
+ /*
+ * If we used all the data then
+ * return here.
+ */
+
+ if(n == data_used)
+ return n;
+ else
+ cache_flush_needed = True;
+
+ /*
+ * Move the start of data forward by the amount used,
+ * cut down the amount left by the same amount.
+ */
+
+ data += data_used;
+ pos += data_used;
+ n -= data_used;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 1;
+
+ } else if ((pos < wcp->offset) && (pos + n > wcp->offset) &&
+ (pos + n <= wcp->offset + wcp->alloc_size)) {
+
+ /*
+ * End of write overlaps the existing data.
+ */
+
+ size_t data_used = pos + n - wcp->offset;
+
+ memcpy(wcp->data, data + n - data_used, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + n > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + n - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size)
+ fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+
+ /*
+ * We don't need to move the start of data, but we
+ * cut down the amount left by the amount used.
+ */
+
+ n -= data_used;
+
+ /*
+ * We cannot have used all the data here.
+ */
+
+ cache_flush_needed = True;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 2;
+
+ } else if ( (pos >= wcp->file_size) &&
+ (wcp->offset + wcp->data_size == wcp->file_size) &&
+ (pos > wcp->offset + wcp->data_size) &&
+ (pos < wcp->offset + wcp->alloc_size) ) {
+
+ /*
+ * Non-contiguous write part of which fits within
+ * the cache buffer and is extending the file
+ * and the cache contents reflect the current
+ * data up to the current end of the file.
+ */
+
+ size_t data_used;
+
+ if(pos + n <= wcp->offset + wcp->alloc_size)
+ data_used = n;
+ else
+ data_used = wcp->offset + wcp->alloc_size - pos;
+
+ /*
+ * Fill in the non-continuous area with zeros.
+ */
+
+ memset(wcp->data + wcp->data_size, '\0',
+ pos - (wcp->offset + wcp->data_size) );
+
+ memcpy(wcp->data + (pos - wcp->offset), data, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + data_used > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + data_used - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size)
+ fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+
+ /*
+ * If we used all the data then
+ * return here.
+ */
+
+ if(n == data_used)
+ return n;
+ else
+ cache_flush_needed = True;
+
+ /*
+ * Move the start of data forward by the amount used,
+ * cut down the amount left by the same amount.
+ */
+
+ data += data_used;
+ pos += data_used;
+ n -= data_used;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 3;
+
+ } else {
+
+ /*
+ * Write is bigger than buffer, or there is no overlap on the
+ * low or high ends.
+ */
+
+ DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
+len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
+
+ /*
+ * Update the file size if needed.
+ */
+
+ if(pos + n > wcp->file_size)
+ fsp->size = wcp->file_size = pos + n;
+
+ /*
+ * If write would fit in the cache, and is larger than
+ * the data already in the cache, flush the cache and
+ * preferentially copy the data new data into it. Otherwise
+ * just write the data directly.
+ */
+
+ if ( n <= wcp->alloc_size && n > wcp->data_size) {
+ cache_flush_needed = True;
+ } else {
+ ssize_t ret = real_write_file(fsp, data, pos, n);
+
+ DO_PROFILE_INC(writecache_direct_writes);
+ if (ret == -1)
+ return ret;
+
+ if (pos + ret > wcp->file_size)
+ fsp->size = wcp->file_size = pos + ret;
+
+ return ret;
+ }
+
+ write_path = 4;
+
+ }
+
+ if(wcp->data_size > wcp->file_size)
+ fsp->size = wcp->file_size = wcp->data_size;
+
+ if (cache_flush_needed) {
+ DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
+n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
+ write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
+ (double)wcp->offset, (unsigned int)wcp->data_size ));
+
+ flush_write_cache(fsp, WRITE_FLUSH);
+ }
+ }
+
+ /*
+ * If the write request is bigger than the cache
+ * size, write it all out.
+ */
+
+ if (n > wcp->alloc_size ) {
+ ssize_t ret = real_write_file(fsp, data, pos, n);
+ if (ret == -1)
+ return -1;
+
+ if (pos + ret > wcp->file_size)
+ fsp->size = wcp->file_size = pos + n;
+
+ DO_PROFILE_INC(writecache_direct_writes);
+ return total_written + n;
+ }
+
+ /*
+ * If there's any data left, cache it.
+ */
+
+ if (n) {
+#ifdef WITH_PROFILE
+ if (wcp->data_size) {
+ DO_PROFILE_INC(writecache_abutted_writes);
+ } else {
+ DO_PROFILE_INC(writecache_init_writes);
+ }
+#endif
+ memcpy(wcp->data+wcp->data_size, data, n);
+ if (wcp->data_size == 0) {
+ wcp->offset = pos;
+ DO_PROFILE_INC(writecache_num_write_caches);
+ }
+ wcp->data_size += n;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size)
+ fsp->size = wcp->file_size = wcp->offset + wcp->data_size;
+ DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
+ (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
+
+ total_written += n;
+ return total_written; /* .... that's a write :) */
+ }
+
+ return total_written;
+}
+
+/****************************************************************************
+ Delete the write cache structure.
+****************************************************************************/
+
+void delete_write_cache(files_struct *fsp)
+{
+ write_cache *wcp;
+
+ if(!fsp)
+ return;
+
+ if(!(wcp = fsp->wcp))
+ return;
+
+ DO_PROFILE_DEC(writecache_allocated_write_caches);
+ allocated_write_caches--;
+
+ SMB_ASSERT(wcp->data_size == 0);
+
+ SAFE_FREE(wcp->data);
+ SAFE_FREE(fsp->wcp);
+
+ DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
+
+}
+
+/****************************************************************************
+ Setup the write cache structure.
+****************************************************************************/
+
+static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
+{
+ ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
+ write_cache *wcp;
+
+ if (allocated_write_caches >= MAX_WRITE_CACHES)
+ return False;
+
+ if(alloc_size == 0 || fsp->wcp)
+ return False;
+
+ if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
+ DEBUG(0,("setup_write_cache: malloc fail.\n"));
+ return False;
+ }
+
+ wcp->file_size = file_size;
+ wcp->offset = 0;
+ wcp->alloc_size = alloc_size;
+ wcp->data_size = 0;
+ if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
+ DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
+ (unsigned int)wcp->alloc_size ));
+ SAFE_FREE(wcp);
+ return False;
+ }
+
+ memset(wcp->data, '\0', wcp->alloc_size );
+
+ fsp->wcp = wcp;
+ DO_PROFILE_INC(writecache_allocated_write_caches);
+ allocated_write_caches++;
+
+ DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n",
+ fsp->fsp_name, wcp->alloc_size ));
+
+ return True;
+}
+
+/****************************************************************************
+ Cope with a size change.
+****************************************************************************/
+
+void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
+{
+ fsp->size = file_size;
+ if(fsp->wcp) {
+ /* The cache *must* have been flushed before we do this. */
+ if (fsp->wcp->data_size != 0) {
+ pstring msg;
+ slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
+on file %s with write cache size = %u\n", fsp->fsp_name, fsp->wcp->data_size );
+ smb_panic(msg);
+ }
+ fsp->wcp->file_size = file_size;
+ }
+}
+
+/*******************************************************************
+ Flush a write cache struct to disk.
+********************************************************************/
+
+ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
+{
+ write_cache *wcp = fsp->wcp;
+ size_t data_size;
+ ssize_t ret;
+
+ if(!wcp || !wcp->data_size)
+ return 0;
+
+ data_size = wcp->data_size;
+ wcp->data_size = 0;
+
+ DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
+
+ DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
+ fsp->fd, (double)wcp->offset, (unsigned int)data_size));
+
+#ifdef WITH_PROFILE
+ if(data_size == wcp->alloc_size)
+ DO_PROFILE_INC(writecache_num_perfect_writes);
+#endif
+
+ ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
+
+ /*
+ * Ensure file size if kept up to date if write extends file.
+ */
+
+ if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
+ wcp->file_size = wcp->offset + ret;
+
+ return ret;
+}
+
+/*******************************************************************
+sync a file
+********************************************************************/
+
+void sync_file(connection_struct *conn, files_struct *fsp)
+{
+ if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
+ flush_write_cache(fsp, SYNC_FLUSH);
+ conn->vfs_ops.fsync(fsp,fsp->fd);
+ }
+}
+
+/************************************************************
+ Perform a stat whether a valid fd or not.
+************************************************************/
+
+int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
+{
+ if (fsp->fd == -1)
+ return vfs_stat(fsp->conn, fsp->fsp_name, pst);
+ else
+ return vfs_fstat(fsp,fsp->fd, pst);
+}
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
new file mode 100644
index 0000000000..cb6a6d31a4
--- /dev/null
+++ b/source3/smbd/filename.c
@@ -0,0 +1,503 @@
+/*
+ Unix SMB/CIFS implementation.
+ filename handling routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1999-200
+ Copyright (C) Ying Chen 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * New hash table stat cache code added by Ying Chen.
+ */
+
+#include "includes.h"
+
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern fstring remote_machine;
+extern BOOL use_mangled_map;
+
+static BOOL scan_directory(char *path, char *name,connection_struct *conn,BOOL docache);
+
+/****************************************************************************
+ Check if two filenames are equal.
+ This needs to be careful about whether we are case sensitive.
+****************************************************************************/
+static BOOL fname_equal(char *name1, char *name2)
+{
+ int l1 = strlen(name1);
+ int l2 = strlen(name2);
+
+ /* handle filenames ending in a single dot */
+ if (l1-l2 == 1 && name1[l1-1] == '.' && lp_strip_dot())
+ {
+ BOOL ret;
+ name1[l1-1] = 0;
+ ret = fname_equal(name1,name2);
+ name1[l1-1] = '.';
+ return(ret);
+ }
+
+ if (l2-l1 == 1 && name2[l2-1] == '.' && lp_strip_dot())
+ {
+ BOOL ret;
+ name2[l2-1] = 0;
+ ret = fname_equal(name1,name2);
+ name2[l2-1] = '.';
+ return(ret);
+ }
+
+ /* now normal filename handling */
+ if (case_sensitive)
+ return(strcmp(name1,name2) == 0);
+
+ return(strequal(name1,name2));
+}
+
+
+/****************************************************************************
+ Mangle the 2nd name and check if it is then equal to the first name.
+****************************************************************************/
+static BOOL mangled_equal(char *name1, char *name2, int snum)
+{
+ pstring tmpname;
+ if (mangle_is_8_3(name2, True)) {
+ return False;
+ }
+
+ pstrcpy(tmpname, name2);
+ return mangle_map(tmpname, True, False, snum) &&
+ strequal(name1, tmpname);
+}
+
+
+/****************************************************************************
+This routine is called to convert names from the dos namespace to unix
+namespace. It needs to handle any case conversions, mangling, format
+changes etc.
+
+We assume that we have already done a chdir() to the right "root" directory
+for this service.
+
+The function will return False if some part of the name except for the last
+part cannot be resolved
+
+If the saved_last_component != 0, then the unmodified last component
+of the pathname is returned there. This is used in an exceptional
+case in reply_mv (so far). If saved_last_component == 0 then nothing
+is returned there.
+
+The bad_path arg is set to True if the filename walk failed. This is
+used to pick the correct error code to return between ENOENT and ENOTDIR
+as Windows applications depend on ERRbadpath being returned if a component
+of a pathname does not exist.
+
+On exit from unix_convert, if *pst was not null, then the file stat
+struct will be returned if the file exists and was found, if not this
+stat struct will be filled with zeros (and this can be detected by checking
+for nlinks = 0, which can never be true for any file).
+****************************************************************************/
+
+BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_component,
+ BOOL *bad_path, SMB_STRUCT_STAT *pst)
+{
+ SMB_STRUCT_STAT st;
+ char *start, *end;
+ pstring dirpath;
+ pstring orig_path;
+ BOOL component_was_mangled = False;
+ BOOL name_has_wildcard = False;
+
+ ZERO_STRUCTP(pst);
+
+ *dirpath = 0;
+ *bad_path = False;
+ if(saved_last_component)
+ *saved_last_component = 0;
+
+ if (conn->printer) {
+ /* we don't ever use the filenames on a printer share as a
+ filename - so don't convert them */
+ return True;
+ }
+
+ DEBUG(5, ("unix_convert called on file \"%s\"\n", name));
+
+ /*
+ * Convert to basic unix format - removing \ chars and cleaning it up.
+ */
+
+ unix_format(name);
+ unix_clean_name(name);
+
+ /*
+ * Names must be relative to the root of the service - trim any leading /.
+ * also trim trailing /'s.
+ */
+
+ trim_string(name,"/","/");
+
+ /*
+ * If we trimmed down to a single '\0' character
+ * then we should use the "." directory to avoid
+ * searching the cache, but not if we are in a
+ * printing share.
+ */
+
+ if (!*name) {
+ name[0] = '.';
+ name[1] = '\0';
+ }
+
+ /*
+ * Ensure saved_last_component is valid even if file exists.
+ */
+
+ if(saved_last_component) {
+ end = strrchr_m(name, '/');
+ if(end)
+ pstrcpy(saved_last_component, end + 1);
+ else
+ pstrcpy(saved_last_component, name);
+ }
+
+ if (!case_sensitive &&
+ (!case_preserve || (mangle_is_8_3(name, False) && !short_case_preserve)))
+ strnorm(name);
+
+ /*
+ * If we trimmed down to a single '\0' character
+ * then we will be using the "." directory.
+ * As we know this is valid we can return true here.
+ */
+
+ if(!*name)
+ return(True);
+
+ start = name;
+ while (strncmp(start,"./",2) == 0)
+ start += 2;
+
+ pstrcpy(orig_path, name);
+
+ if(stat_cache_lookup(conn, name, dirpath, &start, &st)) {
+ *pst = st;
+ return True;
+ }
+
+ /*
+ * stat the name - if it exists then we are all done!
+ */
+
+/* ZZZ: stat1 */ if (vfs_stat(conn,name,&st) == 0) {
+ stat_cache_add(orig_path, name);
+ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ *pst = st;
+ return(True);
+ }
+
+ DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n",
+ name, dirpath, start));
+
+ /*
+ * A special case - if we don't have any mangling chars and are case
+ * sensitive then searching won't help.
+ */
+
+ if (case_sensitive && !mangle_is_mangled(name) &&
+ !lp_strip_dot() && !use_mangled_map)
+ return(False);
+
+ name_has_wildcard = ms_has_wild(start);
+
+ /*
+ * is_mangled() was changed to look at an entire pathname, not
+ * just a component. JRA.
+ */
+
+ if (mangle_is_mangled(start))
+ component_was_mangled = True;
+
+ /*
+ * Now we need to recursively match the name against the real
+ * directory structure.
+ */
+
+ /*
+ * Match each part of the path name separately, trying the names
+ * as is first, then trying to scan the directory for matching names.
+ */
+
+ for (; start ; start = (end?end+1:(char *)NULL)) {
+ /*
+ * Pinpoint the end of this section of the filename.
+ */
+ end = strchr_m(start, '/');
+
+ /*
+ * Chop the name at this point.
+ */
+ if (end)
+ *end = 0;
+
+ if(saved_last_component != 0)
+ pstrcpy(saved_last_component, end ? end + 1 : start);
+
+ /*
+ * Check if the name exists up to this point.
+ */
+
+ if (vfs_stat(conn,name, &st) == 0) {
+ /*
+ * It exists. it must either be a directory or this must be
+ * the last part of the path for it to be OK.
+ */
+ if (end && !(st.st_mode & S_IFDIR)) {
+ /*
+ * An intermediate part of the name isn't a directory.
+ */
+ DEBUG(5,("Not a dir %s\n",start));
+ *end = '/';
+ return(False);
+ }
+
+ } else {
+ pstring rest;
+
+ /* Stat failed - ensure we don't use it. */
+ ZERO_STRUCT(st);
+ *rest = 0;
+
+ /*
+ * Remember the rest of the pathname so it can be restored
+ * later.
+ */
+
+ if (end)
+ pstrcpy(rest,end+1);
+
+ /*
+ * Try to find this part of the path in the directory.
+ */
+
+ if (ms_has_wild(start) || !scan_directory(dirpath, start, conn, end?True:False)) {
+ if (end) {
+ /*
+ * An intermediate part of the name can't be found.
+ */
+ DEBUG(5,("Intermediate not found %s\n",start));
+ *end = '/';
+
+ /*
+ * We need to return the fact that the intermediate
+ * name resolution failed. This is used to return an
+ * error of ERRbadpath rather than ERRbadfile. Some
+ * Windows applications depend on the difference between
+ * these two errors.
+ */
+ *bad_path = True;
+ return(False);
+ }
+
+ /*
+ * Just the last part of the name doesn't exist.
+ * We may need to strupper() or strlower() it in case
+ * this conversion is being used for file creation
+ * purposes. If the filename is of mixed case then
+ * don't normalise it.
+ */
+
+ if (!case_preserve && (!strhasupper(start) || !strhaslower(start)))
+ strnorm(start);
+
+ /*
+ * check on the mangled stack to see if we can recover the
+ * base of the filename.
+ */
+
+ if (mangle_is_mangled(start)) {
+ mangle_check_cache( start );
+ }
+
+ DEBUG(5,("New file %s\n",start));
+ return(True);
+ }
+
+ /*
+ * Restore the rest of the string. If the string was mangled the size
+ * may have changed.
+ */
+ if (end) {
+ end = start + strlen(start);
+ pstrcat(start,"/");
+ pstrcat(start,rest);
+ *end = '\0';
+ }
+ } /* end else */
+
+ /*
+ * Add to the dirpath that we have resolved so far.
+ */
+ if (*dirpath)
+ pstrcat(dirpath,"/");
+
+ pstrcat(dirpath,start);
+
+ /*
+ * Don't cache a name with mangled or wildcard components
+ * as this can change the size.
+ */
+
+ if(!component_was_mangled && !name_has_wildcard)
+ stat_cache_add(orig_path, dirpath);
+
+ /*
+ * Restore the / that we wiped out earlier.
+ */
+ if (end)
+ *end = '/';
+ }
+
+ /*
+ * Don't cache a name with mangled or wildcard components
+ * as this can change the size.
+ */
+
+ if(!component_was_mangled && !name_has_wildcard)
+ stat_cache_add(orig_path, name);
+
+ /*
+ * If we ended up resolving the entire path then return a valid
+ * stat struct if we got one.
+ */
+
+ if (VALID_STAT(st) && (strlen(orig_path) == strlen(name)))
+ *pst = st;
+
+ /*
+ * The name has been resolved.
+ */
+
+ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ return(True);
+}
+
+
+/****************************************************************************
+check a filename - possibly caling reducename
+
+This is called by every routine before it allows an operation on a filename.
+It does any final confirmation necessary to ensure that the filename is
+a valid one for the user to access.
+****************************************************************************/
+BOOL check_name(char *name,connection_struct *conn)
+{
+ BOOL ret;
+
+ errno = 0;
+
+ if (IS_VETO_PATH(conn, name)) {
+ DEBUG(5,("file path name %s vetoed\n",name));
+ return(0);
+ }
+
+ ret = reduce_name(conn,name,conn->connectpath,lp_widelinks(SNUM(conn)));
+
+ /* Check if we are allowing users to follow symlinks */
+ /* Patch from David Clerc <David.Clerc@cui.unige.ch>
+ University of Geneva */
+
+#ifdef S_ISLNK
+ if (!lp_symlinks(SNUM(conn))) {
+ SMB_STRUCT_STAT statbuf;
+ if ( (conn->vfs_ops.lstat(conn,name,&statbuf) != -1) &&
+ (S_ISLNK(statbuf.st_mode)) ) {
+ DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name));
+ ret=0;
+ }
+ }
+#endif
+
+ if (!ret)
+ DEBUG(5,("check_name on %s failed\n",name));
+
+ return(ret);
+}
+
+
+/****************************************************************************
+scan a directory to find a filename, matching without case sensitivity
+
+If the name looks like a mangled name then try via the mangling functions
+****************************************************************************/
+static BOOL scan_directory(char *path, char *name,connection_struct *conn,BOOL docache)
+{
+ void *cur_dir;
+ char *dname;
+ BOOL mangled;
+ pstring name2;
+
+ mangled = mangle_is_mangled(name);
+
+ /* handle null paths */
+ if (*path == 0)
+ path = ".";
+
+ if (docache && (dname = DirCacheCheck(path,name,SNUM(conn)))) {
+ pstrcpy(name, dname);
+ return(True);
+ }
+
+ /*
+ * The incoming name can be mangled, and if we de-mangle it
+ * here it will not compare correctly against the filename (name2)
+ * read from the directory and then mangled by the mangle_map()
+ * call. We need to mangle both names or neither.
+ * (JRA).
+ */
+ if (mangled)
+ mangled = !mangle_check_cache( name );
+
+ /* open the directory */
+ if (!(cur_dir = OpenDir(conn, path, True))) {
+ DEBUG(3,("scan dir didn't open dir [%s]\n",path));
+ return(False);
+ }
+
+ /* now scan for matching names */
+ while ((dname = ReadDirName(cur_dir))) {
+ if (*dname == '.' && (strequal(dname,".") || strequal(dname,"..")))
+ continue;
+
+ pstrcpy(name2,dname);
+ if (!mangle_map(name2,False,True,SNUM(conn)))
+ continue;
+
+ if ((mangled && mangled_equal(name,name2,SNUM(conn))) || fname_equal(name, dname)) {
+ /* we've found the file, change it's name and return */
+ if (docache)
+ DirCacheAdd(path,name,dname,SNUM(conn));
+ pstrcpy(name, dname);
+ CloseDir(cur_dir);
+ return(True);
+ }
+ }
+
+ CloseDir(cur_dir);
+ return(False);
+}
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
new file mode 100644
index 0000000000..c055fb54eb
--- /dev/null
+++ b/source3/smbd/files.c
@@ -0,0 +1,400 @@
+/*
+ Unix SMB/CIFS implementation.
+ Files[] structure handling
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static int real_max_open_files;
+
+#define VALID_FNUM(fnum) (((fnum) >= 0) && ((fnum) < real_max_open_files))
+
+#define FILE_HANDLE_OFFSET 0x1000
+
+static struct bitmap *file_bmap;
+
+static files_struct *Files;
+
+/* a fsp to use when chaining */
+static files_struct *chain_fsp = NULL;
+/* a fsp to use to save when breaking an oplock. */
+static files_struct *oplock_save_chain_fsp = NULL;
+
+static int files_used;
+
+/****************************************************************************
+ Return a unique number identifying this fsp over the life of this pid.
+****************************************************************************/
+
+static unsigned long get_gen_count(void)
+{
+ static unsigned long file_gen_counter;
+
+ if ((++file_gen_counter) == 0)
+ return ++file_gen_counter;
+ return file_gen_counter;
+}
+
+/****************************************************************************
+ Find first available file slot.
+****************************************************************************/
+
+files_struct *file_new(connection_struct *conn)
+{
+ int i;
+ static int first_file;
+ files_struct *fsp, *next;
+
+ /* we want to give out file handles differently on each new
+ connection because of a common bug in MS clients where they try to
+ reuse a file descriptor from an earlier smb connection. This code
+ increases the chance that the errant client will get an error rather
+ than causing corruption */
+ if (first_file == 0) {
+ first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files;
+ }
+
+ i = bitmap_find(file_bmap, first_file);
+ if (i == -1) {
+ /*
+ * Before we give up, go through the open files
+ * and see if there are any files opened with a
+ * batch oplock. If so break the oplock and then
+ * re-use that entry (if it becomes closed).
+ * This may help as NT/95 clients tend to keep
+ * files batch oplocked for quite a long time
+ * after they have finished with them.
+ */
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if (attempt_close_oplocked_file(fsp)) {
+ return file_new(conn);
+ }
+ }
+
+ DEBUG(0,("ERROR! Out of file structures\n"));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRnofids;
+ return NULL;
+ }
+
+ fsp = (files_struct *)malloc(sizeof(*fsp));
+ if (!fsp) {
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRnofids;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(fsp);
+ fsp->fd = -1;
+ fsp->conn = conn;
+ fsp->file_id = get_gen_count();
+ GetTimeOfDay(&fsp->open_time);
+
+ first_file = (i+1) % real_max_open_files;
+
+ bitmap_set(file_bmap, i);
+ files_used++;
+
+ fsp->fnum = i + FILE_HANDLE_OFFSET;
+ string_set(&fsp->fsp_name,"");
+
+ DLIST_ADD(Files, fsp);
+
+ DEBUG(5,("allocated file structure %d, fnum = %d (%d used)\n",
+ i, fsp->fnum, files_used));
+
+ chain_fsp = fsp;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Close all open files for a connection.
+****************************************************************************/
+
+void file_close_conn(connection_struct *conn)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next = fsp->next;
+ if (fsp->conn == conn) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+/****************************************************************************
+ Initialise file structures.
+****************************************************************************/
+
+#define MAX_OPEN_FUDGEFACTOR 10
+
+void file_init(void)
+{
+ int request_max_open_files = lp_max_open_files();
+ int real_lim;
+
+ /*
+ * Set the max_open files to be the requested
+ * max plus a fudgefactor to allow for the extra
+ * fd's we need such as log files etc...
+ */
+ real_lim = set_maxfiles(request_max_open_files + MAX_OPEN_FUDGEFACTOR);
+
+ real_max_open_files = real_lim - MAX_OPEN_FUDGEFACTOR;
+
+ if(real_max_open_files != request_max_open_files) {
+ DEBUG(1,("file_init: Information only: requested %d \
+open files, %d are available.\n", request_max_open_files, real_max_open_files));
+ }
+
+ file_bmap = bitmap_allocate(real_max_open_files);
+
+ if (!file_bmap) {
+ exit_server("out of memory in file_init");
+ }
+
+ /*
+ * Ensure that pipe_handle_oppset is set correctly.
+ */
+ set_pipe_handle_offset(real_max_open_files);
+}
+
+/****************************************************************************
+ Close files open by a specified vuid.
+****************************************************************************/
+
+void file_close_user(int vuid)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if (fsp->vuid == vuid) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+/****************************************************************************
+ Find a fsp given a file descriptor.
+****************************************************************************/
+
+files_struct *file_find_fd(int fd)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ if (fsp->fd == fd) {
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find a fsp given a device, inode and file_id.
+****************************************************************************/
+
+files_struct *file_find_dif(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ if (fsp->fd != -1 &&
+ fsp->dev == dev &&
+ fsp->inode == inode &&
+ fsp->file_id == file_id ) {
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Check if an fsp still exists.
+****************************************************************************/
+
+files_struct *file_find_fsp(files_struct *orig_fsp)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if (fsp == orig_fsp)
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the first fsp given a device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if ( fsp->fd != -1 &&
+ fsp->dev == dev &&
+ fsp->inode == inode )
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the next fsp having the same device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_next(files_struct *start_fsp)
+{
+ files_struct *fsp;
+
+ for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
+ if ( fsp->fd != -1 &&
+ fsp->dev == start_fsp->dev &&
+ fsp->inode == start_fsp->inode )
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find a fsp that is open for printing.
+****************************************************************************/
+
+files_struct *file_find_print(void)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if (fsp->print_file) return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Sync open files on a connection.
+****************************************************************************/
+
+void file_sync_all(connection_struct *conn)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if ((conn == fsp->conn) && (fsp->fd != -1)) {
+ sync_file(conn,fsp);
+ }
+ }
+}
+
+/****************************************************************************
+ Free up a fsp.
+****************************************************************************/
+
+void file_free(files_struct *fsp)
+{
+ DLIST_REMOVE(Files, fsp);
+
+ string_free(&fsp->fsp_name);
+
+ bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET);
+ files_used--;
+
+ DEBUG(5,("freed files structure %d (%d used)\n",
+ fsp->fnum, files_used));
+
+ /* this is paranoia, just in case someone tries to reuse the
+ information */
+ ZERO_STRUCTP(fsp);
+
+ if (fsp == chain_fsp) chain_fsp = NULL;
+
+ SAFE_FREE(fsp);
+}
+
+/****************************************************************************
+ Get a fsp from a packet given the offset of a 16 bit fnum.
+****************************************************************************/
+
+files_struct *file_fsp(char *buf, int where)
+{
+ int fnum, count=0;
+ files_struct *fsp;
+
+ if (chain_fsp)
+ return chain_fsp;
+
+ fnum = SVAL(buf, where);
+
+ for (fsp=Files;fsp;fsp=fsp->next, count++) {
+ if (fsp->fnum == fnum) {
+ chain_fsp = fsp;
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ Reset the chained fsp - done at the start of a packet reply.
+****************************************************************************/
+
+void file_chain_reset(void)
+{
+ chain_fsp = NULL;
+}
+
+/****************************************************************************
+Save the chained fsp - done when about to do an oplock break.
+****************************************************************************/
+
+void file_chain_save(void)
+{
+ oplock_save_chain_fsp = chain_fsp;
+}
+
+/****************************************************************************
+Restore the chained fsp - done after an oplock break.
+****************************************************************************/
+
+void file_chain_restore(void)
+{
+ chain_fsp = oplock_save_chain_fsp;
+}
diff --git a/source3/smbd/groupname.c b/source3/smbd/groupname.c
new file mode 100644
index 0000000000..812488571a
--- /dev/null
+++ b/source3/smbd/groupname.c
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/CIFS implementation.
+ Groupname handling
+ Copyright (C) Jeremy Allison 1998.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifdef USING_GROUPNAME_MAP
+
+#include "includes.h"
+extern DOM_SID global_sam_sid;
+
+/**************************************************************************
+ Groupname map functionality. The code loads a groupname map file and
+ (currently) loads it into a linked list. This is slow and memory
+ hungry, but can be changed into a more efficient storage format
+ if the demands on it become excessive.
+***************************************************************************/
+
+typedef struct groupname_map {
+ ubi_slNode next;
+
+ char *windows_name;
+ DOM_SID windows_sid;
+ char *unix_name;
+ gid_t unix_gid;
+} groupname_map_entry;
+
+static ubi_slList groupname_map_list;
+
+/**************************************************************************
+ Delete all the entries in the groupname map list.
+***************************************************************************/
+
+static void delete_groupname_map_list(void)
+{
+ groupname_map_entry *gmep;
+
+ while((gmep = (groupname_map_entry *)ubi_slRemHead( &groupname_map_list )) != NULL) {
+ SAFE_FREE(gmep->windows_name);
+ SAFE_FREE(gmep->unix_name);
+ SAFE_FREE(gmep);
+ }
+}
+
+/**************************************************************************
+ Load a groupname map file. Sets last accessed timestamp.
+***************************************************************************/
+
+void load_groupname_map(void)
+{
+ static time_t groupmap_file_last_modified = (time_t)0;
+ static BOOL initialized = False;
+ char *groupname_map_file = lp_groupname_map();
+ SMB_STRUCT_STAT st;
+ char **lines;
+ int i;
+ groupname_map_entry *new_ep;
+
+ if(!initialized) {
+ ubi_slInitList( &groupname_map_list );
+ initialized = True;
+ }
+
+ if (!*groupname_map_file)
+ return;
+
+ if(sys_stat(groupname_map_file, &st) != 0) {
+ DEBUG(0, ("load_groupname_map: Unable to stat file %s. Error was %s\n",
+ groupname_map_file, strerror(errno) ));
+ return;
+ }
+
+ /*
+ * Check if file has changed.
+ */
+ if( st.st_mtime <= groupmap_file_last_modified)
+ return;
+
+ groupmap_file_last_modified = st.st_mtime;
+
+ /*
+ * Load the file.
+ */
+
+ lines = file_lines_load(groupname_map_file,NULL,False);
+ if (!lines) {
+ DEBUG(0,("load_groupname_map: can't open groupname map %s. Error was %s\n",
+ groupname_map_file, strerror(errno)));
+ return;
+ }
+ file_lines_slashcont(lines);
+
+ /*
+ * Throw away any previous list.
+ */
+ delete_groupname_map_list();
+
+ DEBUG(4,("load_groupname_map: Scanning groupname map %s\n",groupname_map_file));
+
+ for (i=0; lines[i]; i++) {
+ pstring unixname;
+ pstring windows_name;
+ gid_t gid;
+ DOM_SID tmp_sid;
+ char *s = lines[i];
+
+ DEBUG(10,("load_groupname_map: Read line |%s|\n", s));
+
+ if (!*s || strchr_m("#;",*s))
+ continue;
+
+ if(!next_token(&s,unixname, "\t\n\r=", sizeof(unixname)))
+ continue;
+
+ if(!next_token(&s,windows_name, "\t\n\r=", sizeof(windows_name)))
+ continue;
+
+ trim_string(unixname, " ", " ");
+ trim_string(windows_name, " ", " ");
+
+ if (!*windows_name)
+ continue;
+
+ if(!*unixname)
+ continue;
+
+ DEBUG(5,("load_groupname_map: unixname = %s, windowsname = %s.\n",
+ unixname, windows_name));
+
+ /*
+ * Attempt to get the unix gid_t for this name.
+ */
+
+ if ((gid = nametogid(unixname)) == (gid_t)-1)
+ DEBUG(0,("load_groupname_map: nametogid for group %s failed.\
+Error was %s.\n", unixname, strerror(errno) ));
+ continue;
+ }
+
+ /*
+ * Now map to an NT SID.
+ */
+
+ if(!lookup_wellknown_sid_from_name(windows_name, &tmp_sid)) {
+ /*
+ * It's not a well known name, convert the UNIX gid_t
+ * to a rid within this domain SID.
+ */
+ tmp_sid = global_sam_sid;
+ tmp_sid.sub_auths[tmp_sid.num_auths++] =
+ pdb_gid_to_group_rid(gid);
+ }
+
+ /*
+ * Create the list entry and add it onto the list.
+ */
+
+ if((new_ep = (groupname_map_entry *)malloc( sizeof(groupname_map_entry) ))== NULL) {
+ DEBUG(0,("load_groupname_map: malloc fail for groupname_map_entry.\n"));
+ fclose(fp);
+ return;
+ }
+
+ new_ep->unix_gid = gid;
+ new_ep->windows_sid = tmp_sid;
+ new_ep->windows_name = strdup( windows_name );
+ new_ep->unix_name = strdup( unixname );
+
+ if(new_ep->windows_name == NULL || new_ep->unix_name == NULL) {
+ DEBUG(0,("load_groupname_map: malloc fail for names in groupname_map_entry.\n"));
+ fclose(fp);
+ SAFE_FREE(new_ep->windows_name);
+ SAFE_FREE(new_ep->unix_name);
+ SAFE_FREE(new_ep);
+ file_lines_free(lines);
+ return;
+ }
+ memset((char *)&new_ep->next, '\0', sizeof(new_ep->next) );
+
+ ubi_slAddHead( &groupname_map_list, (ubi_slNode *)new_ep);
+ }
+
+ DEBUG(10,("load_groupname_map: Added %ld entries to groupname map.\n",
+ ubi_slCount(&groupname_map_list)));
+
+ file_lines_free(lines);
+}
+
+/***********************************************************
+ Lookup a SID entry by gid_t.
+************************************************************/
+
+void map_gid_to_sid( gid_t gid, DOM_SID *psid)
+{
+ groupname_map_entry *gmep;
+
+ /*
+ * Initialize and load if not already loaded.
+ */
+ load_groupname_map();
+
+ for( gmep = (groupname_map_entry *)ubi_slFirst( &groupname_map_list);
+ gmep; gmep = (groupname_map_entry *)ubi_slNext( gmep )) {
+
+ if( gmep->unix_gid == gid) {
+ *psid = gmep->windows_sid;
+ DEBUG(7,("map_gid_to_sid: Mapping unix group %s to windows group %s.\n",
+ gmep->unix_name, gmep->windows_name ));
+ return;
+ }
+ }
+
+ /*
+ * If there's no map, convert the UNIX gid_t
+ * to a rid within this domain SID.
+ */
+ *psid = global_sam_sid;
+ psid->sub_auths[psid->num_auths++] = pdb_gid_to_group_rid(gid);
+
+ return;
+}
+#else /* USING_GROUPNAME_MAP */
+ void load_groupname_map(void) {;}
+#endif /* USING_GROUPNAME_MAP */
diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c
index 8852e57e8b..c2f3b7b2f0 100644
--- a/source3/smbd/ipc.c
+++ b/source3/smbd/ipc.c
@@ -1,8 +1,10 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
Inter-process communication and named pipe handling
- Copyright (C) Andrew Tridgell 1992-1995
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,2756 +26,495 @@
*/
#include "includes.h"
-#include "loadparm.h"
-#include "pcap.h"
-
-#ifdef CHECK_TYPES
-#undef CHECK_TYPES
-#endif
-#define CHECK_TYPES 0
-extern int DEBUGLEVEL;
-extern int maxxmit;
-extern files_struct Files[];
-extern connection_struct Connections[];
+extern int max_send;
extern fstring local_machine;
-#define NERR_Success 0
-#define NERR_badpass 86
#define NERR_notsupported 50
-#define NERR_BASE (2100)
-#define NERR_BufTooSmall (NERR_BASE+23)
-#define NERR_JobNotFound (NERR_BASE+51)
-#define NERR_DestNotFound (NERR_BASE+52)
-#define ERROR_INVALID_LEVEL 124
-#define ERROR_MORE_DATA 234
-
-#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024))
-
-#define ACCESS_READ 0x01
-#define ACCESS_WRITE 0x02
-#define ACCESS_CREATE 0x04
-
-#define SHPWLEN 8 /* share password length */
-#define NNLEN 12 /* 8.3 net name length */
-#define SNLEN 15 /* service name length */
-#define QNLEN 12 /* queue name maximum length */
-
-extern int Client;
-
-static int CopyExpanded(int cnum, int snum, char** dst, char* src, int* n)
-{
- pstring buf;
- int l;
-
- if (!src || !dst || !n || !(*dst)) return(0);
-
- StrnCpy(buf,src,sizeof(buf)/2);
- string_sub(buf,"%S",lp_servicename(snum));
- standard_sub(cnum,buf);
- StrnCpy(*dst,buf,*n);
- l = strlen(*dst) + 1;
- (*dst) += l;
- (*n) -= l;
- return l;
-}
-
-static int CopyAndAdvance(char** dst, char* src, int* n)
-{
- int l;
- if (!src || !dst || !n || !(*dst)) return(0);
- StrnCpy(*dst,src,*n);
- l = strlen(*dst) + 1;
- (*dst) += l;
- (*n) -= l;
- return l;
-}
-
-static int StrlenExpanded(int cnum, int snum, char* s)
-{
- pstring buf;
- if (!s) return(0);
- StrnCpy(buf,s,sizeof(buf)/2);
- string_sub(buf,"%S",lp_servicename(snum));
- standard_sub(cnum,buf);
- return strlen(buf) + 1;
-}
-
-static char* Expand(int cnum, int snum, char* s)
-{
- static pstring buf;
- if (!s) return(NULL);
- StrnCpy(buf,s,sizeof(buf)/2);
- string_sub(buf,"%S",lp_servicename(snum));
- standard_sub(cnum,buf);
- return &buf[0];
-}
+extern int smb_read_error;
/*******************************************************************
- check a API string for validity when we only need to check the prefix
- ******************************************************************/
-static BOOL prefix_ok(char *str,char *prefix)
-{
- return(strncmp(str,prefix,strlen(prefix)) == 0);
-}
+ copies parameters and data, as needed, into the smb buffer
+ *both* the data and params sections should be aligned. this
+ is fudged in the rpc pipes by
+ at present, only the data section is. this may be a possible
+ cause of some of the ipc problems being experienced. lkcl26dec97
-/****************************************************************************
- send a trans reply
- ****************************************************************************/
-static void send_trans_reply(char *outbuf,char *data,char *param,uint16 *setup,
- int ldata,int lparam,int lsetup)
-{
- int i;
- int this_ldata,this_lparam;
- int tot_data=0,tot_param=0;
- int align;
-
- this_lparam = MIN(lparam,maxxmit - (500+lsetup*SIZEOFWORD)); /* hack */
- this_ldata = MIN(ldata,maxxmit - (500+lsetup*SIZEOFWORD+this_lparam));
-
- align = (this_lparam%4);
-
- set_message(outbuf,10+lsetup,align+this_ldata+this_lparam,True);
- if (this_lparam)
- memcpy(smb_buf(outbuf),param,this_lparam);
- if (this_ldata)
- memcpy(smb_buf(outbuf)+this_lparam+align,data,this_ldata);
-
- SSVAL(outbuf,smb_vwv0,lparam);
- SSVAL(outbuf,smb_vwv1,ldata);
- SSVAL(outbuf,smb_vwv3,this_lparam);
- SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
- SSVAL(outbuf,smb_vwv5,0);
- SSVAL(outbuf,smb_vwv6,this_ldata);
- SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
- SSVAL(outbuf,smb_vwv8,0);
- SSVAL(outbuf,smb_vwv9,lsetup);
- for (i=0;i<lsetup;i++)
- SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]);
-
- show_msg(outbuf);
- send_smb(Client,outbuf);
-
- tot_data = this_ldata;
- tot_param = this_lparam;
-
- while (tot_data < ldata || tot_param < lparam)
- {
- this_lparam = MIN(lparam-tot_param,maxxmit - 500); /* hack */
- this_ldata = MIN(ldata-tot_data,maxxmit - (500+this_lparam));
-
- align = (this_lparam%4);
-
- set_message(outbuf,10,this_ldata+this_lparam+align,False);
- if (this_lparam)
- memcpy(smb_buf(outbuf),param+tot_param,this_lparam);
- if (this_ldata)
- memcpy(smb_buf(outbuf)+this_lparam+align,data+tot_data,this_ldata);
-
- SSVAL(outbuf,smb_vwv3,this_lparam);
- SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
- SSVAL(outbuf,smb_vwv5,tot_param);
- SSVAL(outbuf,smb_vwv6,this_ldata);
- SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
- SSVAL(outbuf,smb_vwv8,tot_data);
- SSVAL(outbuf,smb_vwv9,0);
-
- show_msg(outbuf);
- send_smb(Client,outbuf);
-
- tot_data += this_ldata;
- tot_param += this_lparam;
- }
-}
-
+ ******************************************************************/
-
-/****************************************************************************
- get a print queue
- ****************************************************************************/
-
-struct pack_desc {
- char* format; /* formatstring for structure */
- char* subformat; /* subformat for structure */
- char* base; /* baseaddress of buffer */
- int buflen; /* remaining size for fixed part; on init: length of base */
- int subcount; /* count of substructures */
- char* structbuf; /* pointer into buffer for remaining fixed part */
- int stringlen; /* remaining size for variable part */
- char* stringbuf; /* pointer into buffer for remaining variable part */
- int neededlen; /* total needed size */
- int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */
- char* curpos; /* current position; pointer into format or subformat */
- int errcode;
-};
-
-static int get_counter(char** p)
+static void copy_trans_params_and_data(char *outbuf, int align,
+ char *rparam, int param_offset, int param_len,
+ char *rdata, int data_offset, int data_len)
{
- int i, n;
- if (!p || !(*p)) return(1);
- if (!isdigit(**p)) return 1;
- for (n = 0;;) {
- i = **p;
- if (isdigit(i))
- n = 10 * n + (i - '0');
- else
- return n;
- (*p)++;
- }
-}
+ char *copy_into = smb_buf(outbuf)+1;
-static int getlen(char* p)
-{
- int n = 0;
- if (!p) return(0);
- while (*p) {
- switch( *p++ ) {
- case 'W': /* word (2 byte) */
- n += 2;
- break;
- case 'N': /* count of substructures (word) at end */
- n += 2;
- break;
- case 'D': /* double word (4 byte) */
- case 'z': /* offset to zero terminated string (4 byte) */
- case 'l': /* offset to user data (4 byte) */
- n += 4;
- break;
- case 'b': /* offset to data (with counter) (4 byte) */
- n += 4;
- get_counter(&p);
- break;
- case 'B': /* byte (with optional counter) */
- n += get_counter(&p);
- break;
- }
- }
- return n;
-}
+ if(param_len < 0)
+ param_len = 0;
-static BOOL init_package(struct pack_desc* p, int count, int subcount)
-{
- int n = p->buflen;
- int i;
-
- if (!p->format || !p->base) return(False);
-
- i = count * getlen(p->format);
- if (p->subformat) i += subcount * getlen(p->subformat);
- p->structbuf = p->base;
- p->neededlen = 0;
- p->usedlen = 0;
- p->subcount = 0;
- p->curpos = p->format;
- if (i > n) {
- i = n = 0;
- p->errcode = NERR_BufTooSmall;
- }
-
- p->errcode = NERR_Success;
- p->buflen = i;
- n -= i;
- p->stringbuf = p->base + i;
- p->stringlen = n;
- return(p->errcode == NERR_Success);
-}
-
-#ifdef __STDC__
-static int package(struct pack_desc* p, ...)
-{
-#else
-static int package(va_alist)
-va_dcl
-{
- struct pack_desc* p;
-#endif
- va_list args;
- int needed=0, stringneeded;
- char* str=NULL;
- int is_string=0, stringused;
- int32 temp;
-
-#ifdef __STDC__
- va_start(args,p);
-#else
- va_start(args);
- p = va_arg(args,struct pack_desc *);
-#endif
-
- if (!*p->curpos) {
- if (!p->subcount)
- p->curpos = p->format;
- else {
- p->curpos = p->subformat;
- p->subcount--;
- }
- }
-#if CHECK_TYPES
- str = va_arg(args,char*);
- if (strncmp(str,p->curpos,strlen(str)) != 0) {
- DEBUG(2,("type error in package: %s instead of %*s\n",str,
- strlen(str),p->curpos));
- va_end(args);
-#if AJT
- ajt_panic();
-#endif
- return 0;
- }
-#endif
- stringneeded = -1;
-
- if (!p->curpos) return(0);
-
- switch( *p->curpos++ ) {
- case 'W': /* word (2 byte) */
- needed = 2;
- temp = va_arg(args,int);
- if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
- break;
- case 'N': /* count of substructures (word) at end */
- needed = 2;
- p->subcount = va_arg(args,int);
- if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
- break;
- case 'D': /* double word (4 byte) */
- needed = 4;
- temp = va_arg(args,int);
- if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
- break;
- case 'B': /* byte (with optional counter) */
- needed = get_counter(&p->curpos);
- {
- char *s = va_arg(args,char*);
- if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed);
- }
- break;
- case 'z': /* offset to zero terminated string (4 byte) */
- str = va_arg(args,char*);
- stringneeded = (str ? strlen(str)+1 : 0);
- is_string = 1;
- break;
- case 'l': /* offset to user data (4 byte) */
- str = va_arg(args,char*);
- stringneeded = va_arg(args,int);
- is_string = 0;
- break;
- case 'b': /* offset to data (with counter) (4 byte) */
- str = va_arg(args,char*);
- stringneeded = get_counter(&p->curpos);
- is_string = 0;
- break;
- }
- va_end(args);
- if (stringneeded >= 0) {
- needed = 4;
- if (p->buflen >= needed) {
- stringused = stringneeded;
- if (stringused > p->stringlen) {
- stringused = (is_string ? p->stringlen : 0);
- if (p->errcode == NERR_Success) p->errcode = ERROR_MORE_DATA;
- }
- if (!stringused)
- SIVAL(p->structbuf,0,0);
- else {
- SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
- memcpy(p->stringbuf,str?str:"",stringused);
- if (is_string) p->stringbuf[stringused-1] = '\0';
- p->stringbuf += stringused;
- p->stringlen -= stringused;
- p->usedlen += stringused;
- }
- }
- p->neededlen += stringneeded;
- }
- p->neededlen += needed;
- if (p->buflen >= needed) {
- p->structbuf += needed;
- p->buflen -= needed;
- p->usedlen += needed;
- }
- else {
- if (p->errcode == NERR_Success) p->errcode = NERR_BufTooSmall;
- }
- return 1;
-}
+ if(data_len < 0)
+ data_len = 0;
-#if CHECK_TYPES
-#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
-#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
-#else
-#define PACK(desc,t,v) package(desc,v)
-#define PACKl(desc,t,v,l) package(desc,v,l)
-#endif
+ DEBUG(5,("copy_trans_params_and_data: params[%d..%d] data[%d..%d]\n",
+ param_offset, param_offset + param_len,
+ data_offset , data_offset + data_len));
-static void PACKI(struct pack_desc* desc,char *t,int v)
-{
- PACK(desc,t,v);
-}
+ if (param_len)
+ memcpy(copy_into, &rparam[param_offset], param_len);
-static void PACKS(struct pack_desc* desc,char *t,char *v)
-{
- PACK(desc,t,v);
-}
+ copy_into += param_len + align;
-static void PackDriverData(struct pack_desc* desc)
-{
- char drivdata[4+4+32];
- SIVAL(drivdata,0,sizeof drivdata); /* cb */
- SIVAL(drivdata,4,1000); /* lVersion */
- memset(drivdata+8,0,32); /* szDeviceName */
- strcpy(drivdata+8,"NULL");
- PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
+ if (data_len )
+ memcpy(copy_into, &rdata[data_offset], data_len);
}
-static int check_printq_info(struct pack_desc* desc,
- int uLevel, const char* id1, const char* id2)
-{
- desc->subformat = NULL;
- switch( uLevel ) {
- case 0:
- desc->format = "B13";
- break;
- case 1:
- desc->format = "B13BWWWzzzzzWW";
- break;
- case 2:
- desc->format = "B13BWWWzzzzzWN";
- desc->subformat = "WB21BB16B10zWWzDDz";
- break;
- case 3:
- desc->format = "zWWWWzzzzWWzzl";
- break;
- case 4:
- desc->format = "zWWWWzzzzWNzzl";
- desc->subformat = "WWzWWDDzz";
- break;
- case 5:
- desc->format = "z";
- break;
- default: return False;
- }
- if (strcmp(desc->format,id1) != 0) return False;
- if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
- return True;
-}
-
-static void fill_printjob_info(int cnum, int snum, int uLevel,
- struct pack_desc* desc,
- print_queue_struct* queue, int n)
-{
- time_t t = queue->time;
-
- /* the client expects localtime */
- t += GMT_TO_LOCAL*TimeDiff(t);
-
- PACKI(desc,"W",((snum%0xFF)<<8) | (queue->job%0xFF)); /* uJobId */
- if (uLevel == 1) {
- PACKS(desc,"B21",queue->user); /* szUserName */
- PACKS(desc,"B",""); /* pad */
- PACKS(desc,"B16",""); /* szNotifyName */
- PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */
- PACKS(desc,"z",""); /* pszParms */
- PACKI(desc,"W",n+1); /* uPosition */
- PACKI(desc,"W",queue->status); /* fsStatus */
- PACKS(desc,"z",""); /* pszStatus */
- PACKI(desc,"D",queue->time); /* ulSubmitted */
- PACKI(desc,"D",queue->size); /* ulSize */
- PACKS(desc,"z",queue->file); /* pszComment */
- }
- if (uLevel == 2 || uLevel == 3) {
- PACKI(desc,"W",queue->priority); /* uPriority */
- PACKS(desc,"z",queue->user); /* pszUserName */
- PACKI(desc,"W",n+1); /* uPosition */
- PACKI(desc,"W",queue->status); /* fsStatus */
- PACKI(desc,"D",queue->time); /* ulSubmitted */
- PACKI(desc,"D",queue->size); /* ulSize */
- PACKS(desc,"z","Samba"); /* pszComment */
- PACKS(desc,"z",queue->file); /* pszDocument */
- if (uLevel == 3) {
- PACKS(desc,"z",""); /* pszNotifyName */
- PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */
- PACKS(desc,"z",""); /* pszParms */
- PACKS(desc,"z",""); /* pszStatus */
- PACKS(desc,"z",SERVICE(snum)); /* pszQueue */
- PACKS(desc,"z","lpd"); /* pszQProcName */
- PACKS(desc,"z",""); /* pszQProcParms */
- PACKS(desc,"z","NULL"); /* pszDriverName */
- PackDriverData(desc); /* pDriverData */
- PACKS(desc,"z",""); /* pszPrinterName */
- }
- }
-}
-
-static void fill_printq_info(int cnum, int snum, int uLevel,
- struct pack_desc* desc,
- int count, print_queue_struct* queue,
- print_status_struct* status)
-{
- if (uLevel < 3) {
- PACKS(desc,"B13",SERVICE(snum));
- } else {
- PACKS(desc,"z",Expand(cnum,snum,SERVICE(snum)));
- }
- if (uLevel == 1 || uLevel == 2) {
- PACKS(desc,"B",""); /* alignment */
- PACKI(desc,"W",5); /* priority */
- PACKI(desc,"W",0); /* start time */
- PACKI(desc,"W",0); /* until time */
- PACKS(desc,"z",""); /* pSepFile */
- PACKS(desc,"z","lpd"); /* pPrProc */
- PACKS(desc,"z",SERVICE(snum)); /* pDestinations */
- PACKS(desc,"z",""); /* pParms */
- if (snum < 0) {
- PACKS(desc,"z","UNKNOWN PRINTER");
- PACKI(desc,"W",LPSTAT_ERROR);
- }
- else if (!status || !status->message[0]) {
- PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum)));
- PACKI(desc,"W",LPSTAT_OK); /* status */
- } else {
- PACKS(desc,"z",status->message);
- PACKI(desc,"W",status->status); /* status */
- }
- PACKI(desc,(uLevel == 1 ? "W" : "N"),count);
- }
- if (uLevel == 3 || uLevel == 4) {
- PACKI(desc,"W",5); /* uPriority */
- PACKI(desc,"W",0); /* uStarttime */
- PACKI(desc,"W",0); /* uUntiltime */
- PACKI(desc,"W",5); /* pad1 */
- PACKS(desc,"z",""); /* pszSepFile */
- PACKS(desc,"z","lpd"); /* pszPrProc */
- PACKS(desc,"z",""); /* pszParms */
- if (!status || !status->message[0]) {
- PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); /* pszComment */
- PACKI(desc,"W",LPSTAT_OK); /* fsStatus */
- } else {
- PACKS(desc,"z",status->message); /* pszComment */
- PACKI(desc,"W",status->status); /* fsStatus */
- }
- PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */
- PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */
- PACKS(desc,"z","NULL"); /* pszDriverName */
- PackDriverData(desc); /* pDriverData */
- }
- if (uLevel == 2 || uLevel == 4) {
- int i;
- for (i=0;i<count;i++)
- fill_printjob_info(cnum,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
- }
-
- DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",SERVICE(snum),count));
-}
-
-static BOOL api_DosPrintQGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- char *QueueName = p;
- int uLevel,cbBuf;
- int count=0;
- int snum;
- char* str3;
- struct pack_desc desc;
- print_queue_struct *queue=NULL;
- print_status_struct status;
-
- bzero(&status,sizeof(status));
- bzero(&desc,sizeof(desc));
-
- p = skip_string(p,1);
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
- str3 = p + 4;
-
- if ((p = strchr(QueueName,'%'))) *p = 0;
-
- DEBUG(3,("PrintQueue uLevel=%d name=%s\n",uLevel,QueueName));
-
- /* check it's a supported varient */
- if (!prefix_ok(str1,"zWrLh")) return False;
- if (!check_printq_info(&desc,uLevel,str2,str3)) return False;
-
- snum = lp_servicenumber(QueueName);
- if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
- int pnum = lp_servicenumber(PRINTERS_NAME);
- if (pnum >= 0) {
- lp_add_printer(QueueName,pnum);
- snum = lp_servicenumber(QueueName);
- }
- }
-
- if (snum < 0 || !VALID_SNUM(snum)) return(False);
-
- count = get_printqueue(snum,cnum,&queue,&status);
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- if (init_package(&desc,1,count)) {
- desc.subcount = count;
- fill_printq_info(cnum,snum,uLevel,&desc,count,queue,&status);
- }
-
- *rdata_len = desc.usedlen;
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,desc.neededlen);
-
- DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));
-
- if (queue) free(queue);
-
- return(True);
-}
-
-
/****************************************************************************
- view list of all print jobs on all queues
- ****************************************************************************/
-static BOOL api_DosPrintQEnum(int cnum, int uid, char* param, char* data,
- int mdrcnt, int mprcnt,
- char **rdata, char** rparam,
- int *rdata_len, int *rparam_len)
-{
- char *param_format = param+2;
- char *output_format1 = skip_string(param_format,1);
- char *p = skip_string(output_format1,1);
- int uLevel = SVAL(p,0);
- char *output_format2 = p + 4;
- int services = lp_numservices();
- int i, n;
- struct pack_desc desc;
- print_queue_struct **queue = NULL;
- print_status_struct *status = NULL;
- int* subcntarr = NULL;
- int queuecnt, subcnt=0, succnt=0;
-
- bzero(&desc,sizeof(desc));
-
- DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
-
- if (prefix_ok(param_format,"WrLeh")) return False;
- if (!check_printq_info(&desc,uLevel,output_format1,output_format2))
- return False;
- queuecnt = 0;
- for (i = 0; i < services; i++)
- if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
- queuecnt++;
- if (uLevel > 0) {
- queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*));
- memset(queue,0,queuecnt*sizeof(print_queue_struct*));
- status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct));
- memset(status,0,queuecnt*sizeof(print_status_struct));
- subcntarr = (int*)malloc(queuecnt*sizeof(int));
- subcnt = 0;
- n = 0;
- for (i = 0; i < services; i++)
- if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
- subcntarr[n] = get_printqueue(i,cnum,&queue[n],&status[n]);
- subcnt += subcntarr[n];
- n++;
- }
- }
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
-
- if (init_package(&desc,queuecnt,subcnt)) {
- n = 0;
- succnt = 0;
- for (i = 0; i < services; i++)
- if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
- fill_printq_info(cnum,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
- n++;
- if (desc.errcode == NERR_Success) succnt = n;
- }
- }
-
- if (subcntarr) free(subcntarr);
-
- *rdata_len = desc.usedlen;
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,succnt);
- SSVAL(*rparam,6,queuecnt);
-
- for (i = 0; i < queuecnt; i++) {
- if (queue && queue[i]) free(queue[i]);
- }
-
- if (queue) free(queue);
- if (status) free(status);
-
- return True;
-}
+ Send a trans reply.
+ ****************************************************************************/
-/****************************************************************************
- get info level for a server list query
- ****************************************************************************/
-static BOOL check_server_info(int uLevel, char* id)
+void send_trans_reply(char *outbuf,
+ char *rparam, int rparam_len,
+ char *rdata, int rdata_len,
+ BOOL buffer_too_large)
{
- switch( uLevel ) {
- case 0:
- if (strcmp(id,"B16") != 0) return False;
- break;
- case 1:
- if (strcmp(id,"B16BBDz") != 0) return False;
- break;
- default:
- return False;
- }
- return True;
-}
+ int this_ldata,this_lparam;
+ int tot_data_sent = 0;
+ int tot_param_sent = 0;
+ int align;
-/* used for server information: client, nameserv and ipc */
-struct srv_info_struct
-{
- fstring name;
- uint32 type;
- fstring comment;
- fstring domain; /* used ONLY in ipc.c NOT namework.c */
- BOOL server_added; /* used ONLY in ipc.c NOT namework.c */
-};
+ int ldata = rdata ? rdata_len : 0;
+ int lparam = rparam ? rparam_len : 0;
-/*******************************************************************
- filter out unwanted server info
- ******************************************************************/
-static BOOL filter_server_info(struct srv_info_struct *server,
- char *domain)
-{
- if (*domain)
- return(strequal(domain, server->domain));
-
- return (True); /* be indiscriminate: get all servers! */
-}
+ if (buffer_too_large)
+ DEBUG(5,("send_trans_reply: buffer %d too large\n", ldata ));
-/*******************************************************************
- find server in the files saved by nmbd. Return True if we find it.
- ******************************************************************/
-static BOOL find_server(struct srv_info_struct *servers, int num_servers,
- char *domain, char *name)
-{
- int count;
+ this_lparam = MIN(lparam,max_send - 500); /* hack */
+ this_ldata = MIN(ldata,max_send - (500+this_lparam));
- if (!servers || num_servers == 0) return (False);
+ align = ((this_lparam)%4);
- for (count = 0; count < num_servers; count++) {
- struct srv_info_struct *s;
+ if (buffer_too_large) {
+ ERROR_NT(STATUS_BUFFER_OVERFLOW);
+ }
- s = &servers[count];
+ set_message(outbuf,10,1+align+this_ldata+this_lparam,True);
- if (strequal(name, s->name)) {
- StrnCpy(domain, s->domain, sizeof(pstring)-1);
- return (True);
- }
- }
- return (False);
-}
+ copy_trans_params_and_data(outbuf, align,
+ rparam, tot_param_sent, this_lparam,
+ rdata, tot_data_sent, this_ldata);
+ SSVAL(outbuf,smb_vwv0,lparam);
+ SSVAL(outbuf,smb_vwv1,ldata);
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
+ SSVAL(outbuf,smb_vwv5,0);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,0);
+ SSVAL(outbuf,smb_vwv9,0);
-/*******************************************************************
- get server info lists from the files saved by nmbd. Return the
- number of entries
- ******************************************************************/
-static int get_server_info(uint32 servertype,
- struct srv_info_struct **servers)
-{
- FILE *f;
- pstring fname;
- int count=0;
- int alloced=0;
- pstring line;
-
- strcpy(fname,lp_lockdir());
- trim_string(fname,NULL,"/");
- strcat(fname,"/");
- strcat(fname,SERVER_LIST);
-
- f = fopen(fname,"r");
-
- if (!f) {
- DEBUG(4,("Can't open %s - %s\n",fname,strerror(errno)));
- return(0);
- }
- if (servertype == SV_TYPE_ALL) servertype &= ~SV_TYPE_DOMAIN_ENUM;
-
- while (!feof(f))
- {
- fstring stype;
- struct srv_info_struct *s;
- char *ptr = line;
- *ptr = 0;
-
- fgets(line,sizeof(line)-1,f);
- if (!*line) continue;
-
- if (count == alloced) {
- alloced += 10;
- (*servers) = (struct srv_info_struct *)
- Realloc(*servers,sizeof(**servers)*alloced);
- if (!(*servers)) return(0);
- bzero((char *)((*servers)+count),sizeof(**servers)*(alloced-count));
- }
- s = &(*servers)[count];
-
- s->server_added = True;
-
- if (!next_token(&ptr,s->name , NULL)) continue;
- if (!next_token(&ptr,stype , NULL)) continue;
- if (!next_token(&ptr,s->comment, NULL)) continue;
- if (!next_token(&ptr,s->domain , NULL)) {
- /* this allows us to cop with an old nmbd */
- strcpy(s->domain,my_workgroup());
- }
-
- if (sscanf(stype,"%X",&s->type) != 1) continue;
-
- /* doesn't match up: don't want it */
- if (!(servertype & s->type)) continue;
-
- /* server entry is a domain, we haven't asked for domains: don't want it */
- if ((s->type&SV_TYPE_DOMAIN_ENUM) && !(servertype&SV_TYPE_DOMAIN_ENUM))
- continue;
-
- DEBUG(4,("Server %20s %8x %25s %15s\n",
- s->name, stype, s->comment, s->domain));
-
- count++;
- }
-
- fclose(f);
- return(count);
-}
+ show_msg(outbuf);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans_reply: send_smb failed.");
-/*******************************************************************
- fill in a server info structure
- ******************************************************************/
-static int fill_srv_info(struct srv_info_struct *service,
- int uLevel, char **buf, int *buflen,
- char **stringbuf, int *stringspace, char *baseaddr)
-{
- int struct_len;
- char* p;
- char* p2;
- int l2;
- int len;
-
- switch (uLevel) {
- case 0: struct_len = 16; break;
- case 1: struct_len = 26; break;
- default: return -1;
- }
-
- if (!buf)
- {
- len = 0;
- switch (uLevel)
- {
- case 1:
- len = strlen(service->comment)+1;
- break;
- }
-
- if (buflen) *buflen = struct_len;
- if (stringspace) *stringspace = len;
- return struct_len + len;
- }
-
- len = struct_len;
- p = *buf;
- if (*buflen < struct_len) return -1;
- if (stringbuf)
- {
- p2 = *stringbuf;
- l2 = *stringspace;
- }
- else
- {
- p2 = p + struct_len;
- l2 = *buflen - struct_len;
- }
- if (!baseaddr) baseaddr = p;
-
- switch (uLevel)
- {
- case 0:
- StrnCpy(p,service->name,15);
- break;
-
- case 1:
- StrnCpy(p,service->name,15);
- SIVAL(p,18,service->type);
- SIVAL(p,22,PTR_DIFF(p2,baseaddr));
- len += CopyAndAdvance(&p2,service->comment,&l2);
- break;
- }
-
- if (stringbuf)
- {
- *buf = p + struct_len;
- *buflen -= struct_len;
- *stringbuf = p2;
- *stringspace = l2;
- }
- else
- {
- *buf = p2;
- *buflen -= len;
- }
- return len;
-}
+ tot_data_sent = this_ldata;
+ tot_param_sent = this_lparam;
-
-/****************************************************************************
- view list of servers available (or possibly domains). The info is
- extracted from lists saved by nmbd on the local host
- ****************************************************************************/
-static BOOL api_RNetServerEnum(int cnum, int uid, char *param, char *data,
- int mdrcnt, int mprcnt, char **rdata,
- char **rparam, int *rdata_len, int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel = SVAL(p,0);
- int buf_len = SVAL(p,2);
- uint32 servertype = IVAL(p,4);
- char *p2;
- int data_len, fixed_len, string_len;
- int f_len, s_len;
- struct srv_info_struct *servers=NULL;
- int counted=0,total=0;
- int i;
- fstring domain;
- BOOL domain_request = (servertype & SV_TYPE_DOMAIN_ENUM) &&
- !(servertype == SV_TYPE_ALL);
-
- domain[0] = 0;
- p += 8;
-
- if (!prefix_ok(str1,"WrLehD")) return False;
- if (!check_server_info(uLevel,str2)) return False;
-
- DEBUG(4, ("server request level: %s\n", str2));
-
- if (strcmp(str1, "WrLehDO") == 0)
- {
- /* asking for servers. we will have to work out which workgroup was
- requested, as we maintain lists for multiple workgroups */
- }
- else if (strcmp(str1, "WrLehDz") == 0)
- {
- /* asking for a specific workgroup */
- StrnCpy(domain, p, sizeof(fstring)-1);
- }
-
- if (lp_browse_list())
- {
- total = get_server_info(servertype,&servers);
- }
-
- if (!domain[0] && !domain_request) {
- extern fstring remote_machine;
- /* must be a server request with an assumed domain. find a domain */
-
- if (find_server(servers, total, domain, remote_machine)) {
- DEBUG(4, ("No domain specified: using %s for %s\n",
- domain, remote_machine));
- } else {
- /* default to soemthing sensible */
- strcpy(domain,my_workgroup());
- }
- }
-
- data_len = fixed_len = string_len = 0;
-
- for (i=0;i<total;i++)
- if (filter_server_info(&servers[i],domain)) {
- data_len += fill_srv_info(&servers[i],uLevel,0,&f_len,0,&s_len,0);
- if (data_len <= buf_len)
+ while (tot_data_sent < ldata || tot_param_sent < lparam)
{
- counted++;
- fixed_len += f_len;
- string_len += s_len;
- }
- }
+ this_lparam = MIN(lparam-tot_param_sent, max_send - 500); /* hack */
+ this_ldata = MIN(ldata -tot_data_sent, max_send - (500+this_lparam));
- *rdata_len = fixed_len + string_len;
- *rdata = REALLOC(*rdata,*rdata_len);
- bzero(*rdata,*rdata_len);
-
- p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */
- p = *rdata;
- f_len = fixed_len;
- s_len = string_len;
-
- {
- int count2 = counted;
- for (i = 0; i < total && count2;i++) {
- if (filter_server_info(&servers[i],domain)) {
- fill_srv_info(&servers[i],uLevel,&p,&f_len,&p2,&s_len,*rdata);
- count2--;
- }
- }
- }
-
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,counted);
- SSVAL(*rparam,6,total);
+ if(this_lparam < 0)
+ this_lparam = 0;
- if (servers) free(servers);
+ if(this_ldata < 0)
+ this_ldata = 0;
- DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n",
- domain,uLevel,counted,total));
-
- return(True);
-}
+ align = (this_lparam%4);
+ set_message(outbuf,10,1+this_ldata+this_lparam+align,False);
-/****************************************************************************
- get info about a share
- ****************************************************************************/
-static BOOL check_share_info(int uLevel, char* id)
-{
- switch( uLevel ) {
- case 0:
- if (strcmp(id,"B13") != 0) return False;
- break;
- case 1:
- if (strcmp(id,"B13BWz") != 0) return False;
- break;
- case 2:
- if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
- break;
- case 91:
- if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
- break;
- default: return False;
- }
- return True;
-}
+ copy_trans_params_and_data(outbuf, align,
+ rparam, tot_param_sent, this_lparam,
+ rdata, tot_data_sent, this_ldata);
-static int fill_share_info(int cnum, int snum, int uLevel,
- char** buf, int* buflen,
- char** stringbuf, int* stringspace, char* baseaddr)
-{
- int struct_len;
- char* p;
- char* p2;
- int l2;
- int len;
-
- switch( uLevel ) {
- case 0: struct_len = 13; break;
- case 1: struct_len = 20; break;
- case 2: struct_len = 40; break;
- case 91: struct_len = 68; break;
- default: return -1;
- }
-
-
- if (!buf)
- {
- len = 0;
- if (uLevel > 0) len += StrlenExpanded(cnum,snum,lp_comment(snum));
- if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
- if (buflen) *buflen = struct_len;
- if (stringspace) *stringspace = len;
- return struct_len + len;
- }
-
- len = struct_len;
- p = *buf;
- if ((*buflen) < struct_len) return -1;
- if (stringbuf)
- {
- p2 = *stringbuf;
- l2 = *stringspace;
- }
- else
- {
- p2 = p + struct_len;
- l2 = (*buflen) - struct_len;
- }
- if (!baseaddr) baseaddr = p;
-
- StrnCpy(p,lp_servicename(snum),13);
-
- if (uLevel > 0)
- {
- int type;
- CVAL(p,13) = 0;
- type = STYPE_DISKTREE;
- if (lp_print_ok(snum)) type = STYPE_PRINTQ;
- if (strequal("IPC$",lp_servicename(snum))) type = STYPE_IPC;
- SSVAL(p,14,type); /* device type */
- SIVAL(p,16,PTR_DIFF(p2,baseaddr));
- len += CopyExpanded(cnum,snum,&p2,lp_comment(snum),&l2);
- }
-
- if (uLevel > 1)
- {
- SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
- SSVALS(p,22,-1); /* max uses */
- SSVAL(p,24,1); /* current uses */
- SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
- len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
- memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
- }
-
- if (uLevel > 2)
- {
- memset(p+40,0,SHPWLEN+2);
- SSVAL(p,50,0);
- SIVAL(p,52,0);
- SSVAL(p,56,0);
- SSVAL(p,58,0);
- SIVAL(p,60,0);
- SSVAL(p,64,0);
- SSVAL(p,66,0);
- }
-
- if (stringbuf)
- {
- (*buf) = p + struct_len;
- (*buflen) -= struct_len;
- (*stringbuf) = p2;
- (*stringspace) = l2;
- }
- else
- {
- (*buf) = p2;
- (*buflen) -= len;
- }
- return len;
-}
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
+ SSVAL(outbuf,smb_vwv5,tot_param_sent);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,tot_data_sent);
+ SSVAL(outbuf,smb_vwv9,0);
-static BOOL api_RNetShareGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *netname = skip_string(str2,1);
- char *p = skip_string(netname,1);
- int uLevel = SVAL(p,0);
- int snum = find_service(netname);
-
- if (snum < 0) return False;
-
- /* check it's a supported varient */
- if (!prefix_ok(str1,"zWrLh")) return False;
- if (!check_share_info(uLevel,str2)) return False;
-
- *rdata = REALLOC(*rdata,mdrcnt);
- p = *rdata;
- *rdata_len = fill_share_info(cnum,snum,uLevel,&p,&mdrcnt,0,0,0);
- if (*rdata_len < 0) return False;
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
- SSVAL(*rparam,4,*rdata_len);
-
- return(True);
-}
+ show_msg(outbuf);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans_reply: send_smb failed.");
-/****************************************************************************
- view list of shares available
- ****************************************************************************/
-static BOOL api_RNetShareEnum(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel = SVAL(p,0);
- int buf_len = SVAL(p,2);
- char *p2;
- int count=lp_numservices();
- int total=0,counted=0;
- int i;
- int data_len, fixed_len, string_len;
- int f_len, s_len;
-
- if (!prefix_ok(str1,"WrLeh")) return False;
- if (!check_share_info(uLevel,str2)) return False;
-
- data_len = fixed_len = string_len = 0;
- for (i=0;i<count;i++)
- if (lp_browseable(i) && lp_snum_ok(i))
- {
- total++;
- data_len += fill_share_info(cnum,i,uLevel,0,&f_len,0,&s_len,0);
- if (data_len <= buf_len)
- {
- counted++;
- fixed_len += f_len;
- string_len += s_len;
- }
- }
- *rdata_len = fixed_len + string_len;
- *rdata = REALLOC(*rdata,*rdata_len);
- memset(*rdata,0,*rdata_len);
-
- p2 = (*rdata) + fixed_len; /* auxillery data (strings) will go here */
- p = *rdata;
- f_len = fixed_len;
- s_len = string_len;
- for (i = 0; i < count;i++)
- if (lp_browseable(i) && lp_snum_ok(i))
- if (fill_share_info(cnum,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0)
- break;
-
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,counted);
- SSVAL(*rparam,6,total);
-
- DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
- counted,total,uLevel,
- buf_len,*rdata_len,mdrcnt));
- return(True);
+ tot_data_sent += this_ldata;
+ tot_param_sent += this_lparam;
+ }
}
-
-
/****************************************************************************
- get the time of day info
- ****************************************************************************/
-static BOOL api_NetRemoteTOD(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *p;
- *rparam_len = 4;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 21;
- *rdata = REALLOC(*rdata,*rdata_len);
-
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
-
- p = *rdata;
-
- {
- struct tm *t;
- time_t unixdate = time(NULL);
-
- put_dos_date3(p,0,unixdate); /* this is the time that is looked at
- by NT in a "net time" operation,
- it seems to ignore the one below */
-
- /* the client expects to get localtime, not GMT, in this bit
- (I think, this needs testing) */
- t = LocalTime(&unixdate,GMT_TO_LOCAL);
-
- SIVAL(p,4,0); /* msecs ? */
- CVAL(p,8) = t->tm_hour;
- CVAL(p,9) = t->tm_min;
- CVAL(p,10) = t->tm_sec;
- CVAL(p,11) = 0; /* hundredths of seconds */
- SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */
- SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */
- CVAL(p,16) = t->tm_mday;
- CVAL(p,17) = t->tm_mon + 1;
- SSVAL(p,18,1900+t->tm_year);
- CVAL(p,20) = t->tm_wday;
- }
-
-
- return(True);
-}
+ Start the first part of an RPC reply which began with an SMBtrans request.
+****************************************************************************/
-/****************************************************************************
- set the user password
- ****************************************************************************/
-static BOOL api_SetUserPassword(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
+static BOOL api_rpc_trans_reply(char *outbuf, smb_np_struct *p)
{
- char *p = skip_string(param+2,2);
- fstring user;
- fstring pass1,pass2;
-
- strcpy(user,p);
+ BOOL is_data_outstanding;
+ char *rdata = malloc(p->max_trans_reply);
+ int data_len;
- p = skip_string(p,1);
-
- StrnCpy(pass1,p,16);
- StrnCpy(pass2,p+16,16);
-
- *rparam_len = 4;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 0;
-
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
+ if(rdata == NULL) {
+ DEBUG(0,("api_rpc_trans_reply: malloc fail.\n"));
+ return False;
+ }
- DEBUG(3,("Set password for <%s>\n",user));
+ if((data_len = read_from_pipe( p, rdata, p->max_trans_reply,
+ &is_data_outstanding)) < 0) {
+ SAFE_FREE(rdata);
+ return False;
+ }
- if (!password_ok(user,pass1,strlen(pass1),NULL,False) ||
- !chgpasswd(user,pass1,pass2))
- SSVAL(*rparam,0,NERR_badpass);
+ send_trans_reply(outbuf, NULL, 0, rdata, data_len, is_data_outstanding);
- bzero(pass1,sizeof(fstring));
- bzero(pass2,sizeof(fstring));
-
- return(True);
+ SAFE_FREE(rdata);
+ return True;
}
/****************************************************************************
- delete a print job
- Form: <W> <>
- ****************************************************************************/
-static BOOL api_RDosPrintJobDel(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- int function = SVAL(param,0);
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
- by the print queue api */
- int snum = (SVAL(p,0)>>8);
- int i, count;
-
-
- /* check it's a supported varient */
- if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
- return(False);
-
- *rparam_len = 4;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 0;
-
- SSVAL(*rparam,0,NERR_Success);
-
- if (snum >= 0 && VALID_SNUM(snum))
- {
- print_queue_struct *queue=NULL;
- lpq_reset(snum);
- count = get_printqueue(snum,cnum,&queue,NULL);
-
- for (i=0;i<count;i++)
- if ((queue[i].job%0xFF) == jobid)
- {
- switch (function) {
- case 81: /* delete */
- DEBUG(3,("Deleting queue entry %d\n",queue[i].job));
- del_printqueue(cnum,snum,queue[i].job);
- break;
- case 82: /* pause */
- case 83: /* resume */
- DEBUG(3,("%s queue entry %d\n",
- (function==82?"pausing":"resuming"),queue[i].job));
- status_printjob(cnum,snum,queue[i].job,
- (function==82?LPQ_PAUSED:LPQ_QUEUED));
- break;
- }
- break;
- }
-
- if (i==count)
- SSVAL(*rparam,0,NERR_JobNotFound);
-
- if (queue) free(queue);
- }
-
- SSVAL(*rparam,2,0); /* converter word */
-
- return(True);
-}
+ WaitNamedPipeHandleState
+****************************************************************************/
-static BOOL api_WPrintQueuePurge(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
+static BOOL api_WNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *QueueName = skip_string(str2,1);
- int snum;
-
- /* check it's a supported varient */
- if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
- return(False);
-
- *rparam_len = 4;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 0;
-
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
-
- snum = lp_servicenumber(QueueName);
- if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
- int pnum = lp_servicenumber(PRINTERS_NAME);
- if (pnum >= 0) {
- lp_add_printer(QueueName,pnum);
- snum = lp_servicenumber(QueueName);
- }
- }
-
- if (snum >= 0 && VALID_SNUM(snum)) {
- print_queue_struct *queue=NULL;
- int i, count;
- lpq_reset(snum);
-
- count = get_printqueue(snum,cnum,&queue,NULL);
- for (i = 0; i < count; i++)
- del_printqueue(cnum,snum,queue[i].job);
-
- if (queue) free(queue);
- }
-
- DEBUG(3,("Print queue purge, queue=%s\n",QueueName));
-
- return(True);
-}
+ uint16 priority;
+ if (!param || param_len < 2)
+ return False;
-/****************************************************************************
- set the property of a print job (undocumented?)
- ? function = 0xb -> set name of print job
- ? function = 0x6 -> move print job up/down
- Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz>
- or <WWsTP> <WB21BB16B10zWWzDDz>
-****************************************************************************/
-static int check_printjob_info(struct pack_desc* desc,
- int uLevel, char* id)
-{
- desc->subformat = NULL;
- switch( uLevel ) {
- case 0: desc->format = "W"; break;
- case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
- case 2: desc->format = "WWzWWDDzz"; break;
- case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
- default: return False;
- }
- if (strcmp(desc->format,id) != 0) return False;
- return True;
-}
+ priority = SVAL(param,0);
+ DEBUG(4,("WaitNamedPipeHandleState priority %x\n", priority));
-static BOOL api_PrintJobInfo(int cnum,int uid,char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- struct pack_desc desc;
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
- by the print queue api */
- int snum = (SVAL(p,0)>>8);
- int uLevel = SVAL(p,2);
- int function = SVAL(p,4); /* what is this ?? */
- int i;
- char *s = data;
-
- *rparam_len = 4;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 0;
-
- /* check it's a supported varient */
- if ((strcmp(str1,"WWsTP")) || (!check_printjob_info(&desc,uLevel,str2)))
- return(False);
-
- switch (function) {
- case 0x6: /* change job place in the queue, data gives the new place */
- if (snum >= 0 && VALID_SNUM(snum))
- {
- print_queue_struct *queue=NULL;
- int count;
-
- lpq_reset(snum);
- count = get_printqueue(snum,cnum,&queue,NULL);
- for (i=0;i<count;i++) /* find job */
- if ((queue[i].job%0xFF) == jobid) break;
-
- if (i==count) {
- desc.errcode=NERR_JobNotFound;
- if (queue) free(queue);
+ if (wait_rpc_pipe_hnd_state(p, priority)) {
+ /* now send the reply */
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return True;
}
- else {
- desc.errcode=NERR_Success;
- i++;
-#if 0
- {
- int place= SVAL(data,0);
- /* we currently have no way of doing this. Can any unix do it? */
- if (i < place) /* move down */;
- else if (i > place ) /* move up */;
- }
-#endif
- desc.errcode=NERR_notsupported; /* not yet supported */
- if (queue) free(queue);
- }
- }
- else desc.errcode=NERR_JobNotFound;
- break;
- case 0xb: /* change print job name, data gives the name */
- /* jobid, snum should be zero */
- if (isalpha(*s))
- {
- pstring name;
- int l = 0;
- while (l<64 && *s)
- {
- if (isalnum(*s) || strchr("-._",*s))
- name[l++] = *s;
- s++;
- }
- name[l] = 0;
-
- DEBUG(3,("Setting print name to %s\n",name));
-
- for (i=0;i<MAX_OPEN_FILES;i++)
- if (Files[i].open && Files[i].print_file)
- {
- pstring wd;
- GetWd(wd);
- unbecome_user();
-
- if (!become_user(Files[i].cnum,uid) ||
- !become_service(Files[i].cnum,True))
- break;
-
- if (sys_rename(Files[i].name,name) == 0)
- string_set(&Files[i].name,name);
- break;
- }
- }
- desc.errcode=NERR_Success;
-
- break;
- default: /* not implemented */
- return False;
- }
-
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0); /* converter word */
-
- return(True);
+ return False;
}
/****************************************************************************
- get info about the server
- ****************************************************************************/
-static BOOL api_RNetServerGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel = SVAL(p,0);
- char *p2;
- int struct_len;
-
- DEBUG(4,("NetServerGetInfo level %d\n",uLevel));
-
- /* check it's a supported varient */
- if (!prefix_ok(str1,"WrLh")) return False;
- switch( uLevel ) {
- case 0:
- if (strcmp(str2,"B16") != 0) return False;
- struct_len = 16;
- break;
- case 1:
- if (strcmp(str2,"B16BBDz") != 0) return False;
- struct_len = 26;
- break;
- case 2:
- if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
- != 0) return False;
- struct_len = 134;
- break;
- case 3:
- if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
- != 0) return False;
- struct_len = 144;
- break;
- case 20:
- if (strcmp(str2,"DN") != 0) return False;
- struct_len = 6;
- break;
- case 50:
- if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
- struct_len = 42;
- break;
- default: return False;
- }
-
- *rdata_len = mdrcnt;
- *rdata = REALLOC(*rdata,*rdata_len);
-
- p = *rdata;
- p2 = p + struct_len;
- if (uLevel != 20) {
- StrnCpy(p,local_machine,16);
- strupper(p);
- }
- p += 16;
- if (uLevel > 0)
- {
- struct srv_info_struct *servers=NULL;
- int i,count;
- pstring comment;
- uint32 servertype=SV_TYPE_SERVER_UNIX|SV_TYPE_WORKSTATION|
- SV_TYPE_SERVER|SV_TYPE_TIME_SOURCE;
-
- strcpy(comment,lp_serverstring());
-
- if ((count=get_server_info(SV_TYPE_ALL,&servers))>0) {
- for (i=0;i<count;i++)
- if (strequal(servers[i].name,local_machine)) {
- servertype = servers[i].type;
- strcpy(comment,servers[i].comment);
- }
- }
- if (servers) free(servers);
-
- SCVAL(p,0,2); /* version_major */
- SCVAL(p,1,0); /* version_minor */
- SIVAL(p,2,servertype);
- if (mdrcnt == struct_len) {
- SIVAL(p,6,0);
- } else {
- SIVAL(p,6,PTR_DIFF(p2,*rdata));
- standard_sub(cnum,comment);
- StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
- p2 = skip_string(p2,1);
- }
- }
- if (uLevel > 1)
- {
- return False; /* not yet implemented */
- }
-
- *rdata_len = PTR_DIFF(p2,*rdata);
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
- SSVAL(*rparam,4,*rdata_len);
-
- return(True);
-}
-
+ SetNamedPipeHandleState
+****************************************************************************/
-/****************************************************************************
- get info about the server
- ****************************************************************************/
-static BOOL api_NetWkstaGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
+static BOOL api_SNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- char *p2;
- extern pstring sesssetup_user;
- int level = SVAL(p,0);
-
- DEBUG(4,("NetWkstaGetInfo level %d\n",level));
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- /* check it's a supported varient */
- if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
- return(False);
+ uint16 id;
- *rdata_len = mdrcnt + 1024;
- *rdata = REALLOC(*rdata,*rdata_len);
+ if (!param || param_len < 2)
+ return False;
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
+ id = SVAL(param,0);
+ DEBUG(4,("SetNamedPipeHandleState to code %x\n", id));
- p = *rdata;
- p2 = p + 22;
-
- SIVAL(p,0,PTR_DIFF(p2,*rdata));
- strcpy(p2,local_machine);
- p2 = skip_string(p2,1);
- p += 4;
-
- SIVAL(p,0,PTR_DIFF(p2,*rdata));
- strcpy(p2,sesssetup_user);
- p2 = skip_string(p2,1);
- p += 4;
-
- SIVAL(p,0,PTR_DIFF(p2,*rdata));
- strcpy(p2,my_workgroup());
- p2 = skip_string(p2,1);
- p += 4;
-
- SCVAL(p,0,2); /* major version?? */
- SCVAL(p,1,1); /* minor version?? */
- p += 2;
-
- SIVAL(p,0,PTR_DIFF(p2,*rdata));
- strcpy(p2,my_workgroup()); /* login domain?? */
- p2 = skip_string(p2,1);
- p += 4;
-
- SIVAL(p,0,PTR_DIFF(p2,*rdata));
- strcpy(p2,"");
- p2 = skip_string(p2,1);
- p += 4;
-
- *rdata_len = PTR_DIFF(p2,*rdata);
-
- SSVAL(*rparam,4,*rdata_len);
-
- return(True);
+ if (set_rpc_pipe_hnd_state(p, id)) {
+ /* now send the reply */
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return True;
+ }
+ return False;
}
/****************************************************************************
- get info about a user
- ****************************************************************************/
+ When no reply is generated, indicate unsupported.
+ ****************************************************************************/
-#define USER_PRIV_GUEST 0
-#define USER_PRIV_USER 1
-#define USER_PRIV_ADMIN 2
-
-static BOOL api_RNetUserGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *UserName = skip_string(str2,1);
- char *p = skip_string(UserName,1);
- int uLevel = SVAL(p,0);
- char *p2;
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- /* check it's a supported varient */
- if (strcmp(str1,"zWrLh") != 0) return False;
- switch( uLevel ) {
- case 0: p2 = "B21"; break;
- case 1: p2 = "B21BB16DWzzWz"; break;
- case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
- case 10: p2 = "B21Bzzz"; break;
- case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
- default: return False;
- }
- if (strcmp(p2,str2) != 0) return False;
-
- *rdata_len = mdrcnt + 1024;
- *rdata = REALLOC(*rdata,*rdata_len);
-
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
-
- p = *rdata;
- p2 = p + 86;
-
- memset(p,0,21);
- strcpy(p,UserName);
- if (uLevel > 0) {
- SCVAL(p,21,0);
- *p2 = 0;
- if (uLevel >= 10) {
- SIVAL(p,22,PTR_DIFF(p2,p)); /* comment */
- strcpy(p2,"<Comment>");
- p2 = skip_string(p2,1);
- SIVAL(p,26,PTR_DIFF(p2,p)); /* user_comment */
- strcpy(p2,"<UserComment>");
- p2 = skip_string(p2,1);
- SIVAL(p,30,PTR_DIFF(p2,p)); /* full name */
- strcpy(p2,"<FullName>");
- p2 = skip_string(p2,1);
- }
- if (uLevel == 11) { /* modelled after NTAS 3.51 reply */
- SSVAL(p,34,USER_PRIV_USER); /* user privilege */
- SIVAL(p,36,0); /* auth flags */
- SIVALS(p,40,-1); /* password age */
- SIVAL(p,44,PTR_DIFF(p2,p)); /* home dir */
- strcpy(p2,"\\\\%L\\HOMES");
- standard_sub_basic(p2);
- p2 = skip_string(p2,1);
- SIVAL(p,48,PTR_DIFF(p2,p)); /* parms */
- strcpy(p2,"");
- p2 = skip_string(p2,1);
- SIVAL(p,52,0); /* last logon */
- SIVAL(p,56,0); /* last logoff */
- SSVALS(p,60,-1); /* bad pw counts */
- SSVALS(p,62,-1); /* num logons */
- SIVAL(p,64,PTR_DIFF(p2,p)); /* logon server */
- strcpy(p2,"\\\\*");
- p2 = skip_string(p2,1);
- SSVAL(p,68,0); /* country code */
-
- SIVAL(p,70,PTR_DIFF(p2,p)); /* workstations */
- strcpy(p2,"");
- p2 = skip_string(p2,1);
-
- SIVALS(p,74,-1); /* max storage */
- SSVAL(p,78,168); /* units per week */
- SIVAL(p,80,PTR_DIFF(p2,p)); /* logon hours */
- memset(p2,-1,21);
- SCVAL(p2,21,0); /* fix zero termination */
- p2 = skip_string(p2,1);
-
- SSVAL(p,84,0); /* code page */
- }
- if (uLevel == 1 || uLevel == 2) {
- memset(p+22,' ',16); /* password */
- SIVALS(p,38,-1); /* password age */
- SSVAL(p,42,USER_PRIV_ADMIN); /* user privilege */
- SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
- strcpy(p2,"\\\\%L\\HOMES");
- standard_sub_basic(p2);
- p2 = skip_string(p2,1);
- SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
- *p2++ = 0;
- SSVAL(p,52,0); /* flags */
- SIVAL(p,54,0); /* script_path */
- if (uLevel == 2) {
- SIVAL(p,60,0); /* auth_flags */
- SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
- strcpy(p2,"<Full Name>");
- p2 = skip_string(p2,1);
- SIVAL(p,68,0); /* urs_comment */
- SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
- strcpy(p2,"");
- p2 = skip_string(p2,1);
- SIVAL(p,76,0); /* workstations */
- SIVAL(p,80,0); /* last_logon */
- SIVAL(p,84,0); /* last_logoff */
- SIVALS(p,88,-1); /* acct_expires */
- SIVALS(p,92,-1); /* max_storage */
- SSVAL(p,96,168); /* units_per_week */
- SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
- memset(p2,-1,21);
- p2 += 21;
- SSVALS(p,102,-1); /* bad_pw_count */
- SSVALS(p,104,-1); /* num_logons */
- SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
- strcpy(p2,"\\\\%L");
- standard_sub_basic(p2);
- p2 = skip_string(p2,1);
- SSVAL(p,110,49); /* country_code */
- SSVAL(p,112,860); /* code page */
- }
- }
- }
-
- *rdata_len = PTR_DIFF(p2,*rdata);
-
- SSVAL(*rparam,4,*rdata_len); /* is this right?? */
-
- return(True);
-}
-
-
-/*******************************************************************
- get groups that a user is a member of
- ******************************************************************/
-static BOOL api_NetUserGetGroups(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
+static BOOL api_no_reply(char *outbuf, int max_rdata_len)
{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *UserName = skip_string(str2,1);
- char *p = skip_string(UserName,1);
- int uLevel = SVAL(p,0);
- char *p2;
- int count=0;
-
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- /* check it's a supported varient */
- if (strcmp(str1,"zWrLeh") != 0) return False;
- switch( uLevel ) {
- case 0: p2 = "B21"; break;
- default: return False;
- }
- if (strcmp(p2,str2) != 0) return False;
-
- *rdata_len = mdrcnt + 1024;
- *rdata = REALLOC(*rdata,*rdata_len);
-
- SSVAL(*rparam,0,NERR_Success);
- SSVAL(*rparam,2,0); /* converter word */
+ char rparam[4];
- p = *rdata;
+ /* unsupported */
+ SSVAL(rparam,0,NERR_notsupported);
+ SSVAL(rparam,2,0); /* converter word */
- /* XXXX we need a real SAM database some day */
- strcpy(p,"Users"); p += 21; count++;
- strcpy(p,"Domain Users"); p += 21; count++;
- strcpy(p,"Guests"); p += 21; count++;
- strcpy(p,"Domain Guests"); p += 21; count++;
+ DEBUG(3,("Unsupported API fd command\n"));
- *rdata_len = PTR_DIFF(p,*rdata);
+ /* now send the reply */
+ send_trans_reply(outbuf, rparam, 4, NULL, 0, False);
- SSVAL(*rparam,4,count); /* is this right?? */
- SSVAL(*rparam,6,count); /* is this right?? */
-
- return(True);
+ return -1;
}
-
-static BOOL api_WWkstaUserLogon(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel;
- struct pack_desc desc;
- char* name;
-
- uLevel = SVAL(p,0);
- name = p + 2;
-
- bzero(&desc,sizeof(desc));
-
- DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));
-
- /* check it's a supported varient */
- if (strcmp(str1,"OOWb54WrLh") != 0) return False;
- if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- desc.subformat = NULL;
- desc.format = str2;
-
-
-
- if (init_package(&desc,1,0)) {
- PACKI(&desc,"W",0); /* code */
- PACKS(&desc,"B21",name); /* eff. name */
- PACKS(&desc,"B",""); /* pad */
- PACKI(&desc,"W",
- Connections[cnum].admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
- PACKI(&desc,"D",0); /* auth flags XXX */
- PACKI(&desc,"W",0); /* num logons */
- PACKI(&desc,"W",0); /* bad pw count */
- PACKI(&desc,"D",-1); /* last logon */
- PACKI(&desc,"D",-1); /* last logoff */
- PACKI(&desc,"D",-1); /* logoff time */
- PACKI(&desc,"D",-1); /* kickoff time */
- PACKI(&desc,"D",0); /* password age */
- PACKI(&desc,"D",0); /* password can change */
- PACKI(&desc,"D",-1); /* password must change */
- {
- fstring mypath;
- strcpy(mypath,"\\\\");
- strcat(mypath,local_machine);
- strupper(mypath);
- PACKS(&desc,"z",mypath); /* computer */
- }
- PACKS(&desc,"z",my_workgroup());/* domain */
- PACKS(&desc,"z",lp_logon_script()); /* script path */
- PACKI(&desc,"D",0); /* reserved */
- }
-
- *rdata_len = desc.usedlen;
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,desc.neededlen);
-
- DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
- return(True);
-}
-
-
/****************************************************************************
- api_WAccessGetUserPerms
- ****************************************************************************/
-static BOOL api_WAccessGetUserPerms(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *user = skip_string(str2,1);
- char *resource = skip_string(user,1);
-
- DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));
-
- /* check it's a supported varient */
- if (strcmp(str1,"zzh") != 0) return False;
- if (strcmp(str2,"") != 0) return False;
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,0); /* errorcode */
- SSVAL(*rparam,2,0); /* converter word */
- SSVAL(*rparam,4,0x7f); /* permission flags */
-
- return(True);
-}
-
-/****************************************************************************
- api_WPrintJobEnumerate
- ****************************************************************************/
-static BOOL api_WPrintJobGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uJobId = SVAL(p,0);
- int uLevel,cbBuf;
- int count;
- int i;
- int snum;
- int job;
- struct pack_desc desc;
- print_queue_struct *queue=NULL;
- print_status_struct status;
-
- uLevel = SVAL(p,2);
- cbBuf = SVAL(p,4);
-
- bzero(&desc,sizeof(desc));
- bzero(&status,sizeof(status));
-
- DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,uJobId));
-
- /* check it's a supported varient */
- if (strcmp(str1,"WWrLh") != 0) return False;
- if (!check_printjob_info(&desc,uLevel,str2)) return False;
-
- snum = (unsigned int)uJobId >> 8; /*## valid serice number??*/
- job = uJobId & 0xFF;
-
- if (snum < 0 || !VALID_SNUM(snum)) return(False);
-
- count = get_printqueue(snum,cnum,&queue,&status);
- for (i = 0; i < count; i++) {
- if ((queue[i].job % 0xFF) == job) break;
- }
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
-
- if (init_package(&desc,1,0)) {
- if (i < count) {
- fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
- *rdata_len = desc.usedlen;
- }
- else {
- desc.errcode = NERR_JobNotFound;
- *rdata_len = 0;
- }
- }
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,desc.neededlen);
-
- if (queue) free(queue);
-
- DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
- return(True);
-}
+ Handle remote api calls delivered to a named pipe already opened.
+ ****************************************************************************/
-static BOOL api_WPrintJobEnumerate(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
+static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- char* name = p;
- int uLevel,cbBuf;
- int count;
- int i, succnt=0;
- int snum;
- struct pack_desc desc;
- print_queue_struct *queue=NULL;
- print_status_struct status;
-
- bzero(&desc,sizeof(desc));
- bzero(&status,sizeof(status));
-
- p = skip_string(p,1);
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
-
- DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));
-
- /* check it's a supported varient */
- if (strcmp(str1,"zWrLeh") != 0) return False;
- if (uLevel > 2) return False; /* defined only for uLevel 0,1,2 */
- if (!check_printjob_info(&desc,uLevel,str2)) return False;
-
- snum = lp_servicenumber(name);
- if (snum < 0 && pcap_printername_ok(name,NULL)) {
- int pnum = lp_servicenumber(PRINTERS_NAME);
- if (pnum >= 0) {
- lp_add_printer(name,pnum);
- snum = lp_servicenumber(name);
- }
- }
-
- if (snum < 0 || !VALID_SNUM(snum)) return(False);
-
- count = get_printqueue(snum,cnum,&queue,&status);
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
-
- if (init_package(&desc,count,0)) {
- succnt = 0;
- for (i = 0; i < count; i++) {
- fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
- if (desc.errcode == NERR_Success) succnt = i+1;
- }
- }
-
- *rdata_len = desc.usedlen;
-
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,succnt);
- SSVAL(*rparam,6,count);
-
- if (queue) free(queue);
-
- DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
- return(True);
-}
-
-static int check_printdest_info(struct pack_desc* desc,
- int uLevel, char* id)
-{
- desc->subformat = NULL;
- switch( uLevel ) {
- case 0: desc->format = "B9"; break;
- case 1: desc->format = "B9B21WWzW"; break;
- case 2: desc->format = "z"; break;
- case 3: desc->format = "zzzWWzzzWW"; break;
- default: return False;
- }
- if (strcmp(desc->format,id) != 0) return False;
- return True;
-}
-
-static void fill_printdest_info(int cnum, int snum, int uLevel,
- struct pack_desc* desc)
-{
- char buf[100];
- strcpy(buf,SERVICE(snum));
- strupper(buf);
- if (uLevel <= 1) {
- PACKS(desc,"B9",buf); /* szName */
- if (uLevel == 1) {
- PACKS(desc,"B21",""); /* szUserName */
- PACKI(desc,"W",0); /* uJobId */
- PACKI(desc,"W",0); /* fsStatus */
- PACKS(desc,"z",""); /* pszStatus */
- PACKI(desc,"W",0); /* time */
- }
- }
- if (uLevel == 2 || uLevel == 3) {
- PACKS(desc,"z",buf); /* pszPrinterName */
- if (uLevel == 3) {
- PACKS(desc,"z",""); /* pszUserName */
- PACKS(desc,"z",""); /* pszLogAddr */
- PACKI(desc,"W",0); /* uJobId */
- PACKI(desc,"W",0); /* fsStatus */
- PACKS(desc,"z",""); /* pszStatus */
- PACKS(desc,"z",""); /* pszComment */
- PACKS(desc,"z","NULL"); /* pszDrivers */
- PACKI(desc,"W",0); /* time */
- PACKI(desc,"W",0); /* pad1 */
- }
- }
-}
-
-static BOOL api_WPrintDestGetInfo(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- char* PrinterName = p;
- int uLevel,cbBuf;
- struct pack_desc desc;
- int snum;
-
- bzero(&desc,sizeof(desc));
-
- p = skip_string(p,1);
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
-
- DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));
-
- /* check it's a supported varient */
- if (strcmp(str1,"zWrLh") != 0) return False;
- if (!check_printdest_info(&desc,uLevel,str2)) return False;
-
- snum = lp_servicenumber(PrinterName);
- if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
- int pnum = lp_servicenumber(PRINTERS_NAME);
- if (pnum >= 0) {
- lp_add_printer(PrinterName,pnum);
- snum = lp_servicenumber(PrinterName);
- }
- }
-
- if (snum < 0) {
- *rdata_len = 0;
- desc.errcode = NERR_DestNotFound;
- desc.neededlen = 0;
- }
- else {
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- if (init_package(&desc,1,0)) {
- fill_printdest_info(cnum,snum,uLevel,&desc);
- }
- *rdata_len = desc.usedlen;
- }
-
- *rparam_len = 6;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,desc.neededlen);
-
- DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
- return(True);
-}
-
-static BOOL api_WPrintDestEnum(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel,cbBuf;
- int queuecnt;
- int i, n, succnt=0;
- struct pack_desc desc;
- int services = lp_numservices();
-
- bzero(&desc,sizeof(desc));
-
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
-
- DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));
-
- /* check it's a supported varient */
- if (strcmp(str1,"WrLeh") != 0) return False;
- if (!check_printdest_info(&desc,uLevel,str2)) return False;
-
- queuecnt = 0;
- for (i = 0; i < services; i++)
- if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
- queuecnt++;
-
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- if (init_package(&desc,queuecnt,0)) {
- succnt = 0;
- n = 0;
- for (i = 0; i < services; i++) {
- if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
- fill_printdest_info(cnum,i,uLevel,&desc);
- n++;
- if (desc.errcode == NERR_Success) succnt = n;
- }
- }
- }
-
- *rdata_len = desc.usedlen;
-
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,succnt);
- SSVAL(*rparam,6,queuecnt);
-
- DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
- return(True);
-}
-
-static BOOL api_WPrintDriverEnum(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel,cbBuf;
- int succnt;
- struct pack_desc desc;
-
- bzero(&desc,sizeof(desc));
-
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
-
- DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));
-
- /* check it's a supported varient */
- if (strcmp(str1,"WrLeh") != 0) return False;
- if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;
-
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- if (init_package(&desc,1,0)) {
- PACKS(&desc,"B41","NULL");
- }
-
- succnt = (desc.errcode == NERR_Success ? 1 : 0);
+ BOOL reply = False;
+ smb_np_struct *p = NULL;
+ int pnum;
+ int subcommand;
- *rdata_len = desc.usedlen;
+ DEBUG(5,("api_fd_reply\n"));
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,succnt);
- SSVAL(*rparam,6,1);
-
- DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
- return(True);
-}
-
-static BOOL api_WPrintQProcEnum(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel,cbBuf;
- int succnt;
- struct pack_desc desc;
-
- bzero(&desc,sizeof(desc));
-
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
-
- DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));
+ /* First find out the name of this file. */
+ if (suwcnt != 2) {
+ DEBUG(0,("Unexpected named pipe transaction.\n"));
+ return(-1);
+ }
- /* check it's a supported varient */
- if (strcmp(str1,"WrLeh") != 0) return False;
- if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;
+ /* Get the file handle and hence the file name. */
+ /*
+ * NB. The setup array has already been transformed
+ * via SVAL and so is in gost byte order.
+ */
+ pnum = ((int)setup[1]) & 0xFFFF;
+ subcommand = ((int)setup[0]) & 0xFFFF;
+
+ if(!(p = get_rpc_pipe(pnum))) {
+ DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum));
+ return api_no_reply(outbuf, mdrcnt);
+ }
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- desc.format = str2;
- if (init_package(&desc,1,0)) {
- PACKS(&desc,"B13","lpd");
- }
+ DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)", subcommand, p->name, pnum));
- succnt = (desc.errcode == NERR_Success ? 1 : 0);
+ /* record maximum data length that can be transmitted in an SMBtrans */
+ p->max_trans_reply = mdrcnt;
- *rdata_len = desc.usedlen;
+ DEBUG(10,("api_fd_reply: p:%p max_trans_reply: %d\n", p, p->max_trans_reply));
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,succnt);
- SSVAL(*rparam,6,1);
+ switch (subcommand) {
+ case 0x26:
+ /* dce/rpc command */
+ reply = write_to_pipe(p, data, tdscnt);
+ if (reply)
+ reply = api_rpc_trans_reply(outbuf, p);
+ break;
+ case 0x53:
+ /* Wait Named Pipe Handle state */
+ reply = api_WNPHS(outbuf, p, params, tpscnt);
+ break;
+ case 0x01:
+ /* Set Named Pipe Handle state */
+ reply = api_SNPHS(outbuf, p, params, tpscnt);
+ break;
+ }
- DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
- return(True);
-}
+ if (!reply)
+ return api_no_reply(outbuf, mdrcnt);
-static BOOL api_WPrintPortEnum(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- char *str1 = param+2;
- char *str2 = skip_string(str1,1);
- char *p = skip_string(str2,1);
- int uLevel,cbBuf;
- int succnt;
- struct pack_desc desc;
-
- bzero(&desc,sizeof(desc));
-
- uLevel = SVAL(p,0);
- cbBuf = SVAL(p,2);
-
- DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));
-
- /* check it's a supported varient */
- if (strcmp(str1,"WrLeh") != 0) return False;
- if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;
-
- if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
- bzero(&desc,sizeof(desc));
- desc.base = *rdata;
- desc.buflen = mdrcnt;
- desc.format = str2;
- if (init_package(&desc,1,0)) {
- PACKS(&desc,"B13","lp0");
- }
-
- succnt = (desc.errcode == NERR_Success ? 1 : 0);
-
- *rdata_len = desc.usedlen;
-
- *rparam_len = 8;
- *rparam = REALLOC(*rparam,*rparam_len);
- SSVALS(*rparam,0,desc.errcode);
- SSVAL(*rparam,2,0);
- SSVAL(*rparam,4,succnt);
- SSVAL(*rparam,6,1);
-
- DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
- return(True);
+ return -1;
}
/****************************************************************************
- the buffer was too small
+ handle named pipe commands
****************************************************************************/
-static BOOL api_TooSmall(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
+static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *name,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,
+ int msrcnt,int mdrcnt,int mprcnt)
{
- *rparam_len = MIN(*rparam_len,mprcnt);
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 0;
-
- SSVAL(*rparam,0,NERR_BufTooSmall);
+ DEBUG(3,("named pipe command on <%s> name\n", name));
- DEBUG(3,("Supplied buffer too small in API command\n"));
+ if (strequal(name,"LANMAN"))
+ return api_reply(conn,vuid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt);
- return(True);
-}
-
-
-/****************************************************************************
- the request is not supported
- ****************************************************************************/
-static BOOL api_Unsupported(int cnum,int uid, char *param,char *data,
- int mdrcnt,int mprcnt,
- char **rdata,char **rparam,
- int *rdata_len,int *rparam_len)
-{
- *rparam_len = 4;
- *rparam = REALLOC(*rparam,*rparam_len);
-
- *rdata_len = 0;
+ if (strequal(name,"WKSSVC") ||
+ strequal(name,"SRVSVC") ||
+ strequal(name,"WINREG") ||
+ strequal(name,"SAMR") ||
+ strequal(name,"LSARPC"))
+ {
+ DEBUG(4,("named pipe command from Win95 (wow!)\n"));
+ return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt);
+ }
- SSVAL(*rparam,0,NERR_notsupported);
- SSVAL(*rparam,2,0); /* converter word */
+ if (strlen(name) < 1)
+ return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt);
- DEBUG(3,("Unsupported API command\n"));
+ if (setup)
+ DEBUG(3,("unknown named pipe: setup 0x%X setup1=%d\n", (int)setup[0],(int)setup[1]));
- return(True);
+ return 0;
}
-
-
-struct
-{
- char *name;
- int id;
- BOOL (*fn)();
- int flags;
-} api_commands[] = {
- {"RNetShareEnum", 0, api_RNetShareEnum,0},
- {"RNetShareGetInfo", 1, api_RNetShareGetInfo,0},
- {"RNetServerGetInfo", 13, api_RNetServerGetInfo,0},
- {"RNetUserGetInfo", 56, api_RNetUserGetInfo,0},
- {"NetUserGetGroups", 59, api_NetUserGetGroups,0},
- {"NetWkstaGetInfo", 63, api_NetWkstaGetInfo,0},
- {"DosPrintQEnum", 69, api_DosPrintQEnum,0},
- {"DosPrintQGetInfo", 70, api_DosPrintQGetInfo,0},
- {"WPrintJobEnumerate",76, api_WPrintJobEnumerate,0},
- {"WPrintJobGetInfo", 77, api_WPrintJobGetInfo,0},
- {"RDosPrintJobDel", 81, api_RDosPrintJobDel,0},
- {"RDosPrintJobPause", 82, api_RDosPrintJobDel,0},
- {"RDosPrintJobResume",83, api_RDosPrintJobDel,0},
- {"WPrintDestEnum", 84, api_WPrintDestEnum,0},
- {"WPrintDestGetInfo", 85, api_WPrintDestGetInfo,0},
- {"NetRemoteTOD", 91, api_NetRemoteTOD,0},
- {"WPrintQueuePurge", 103, api_WPrintQueuePurge,0},
- {"NetServerEnum", 104, api_RNetServerEnum,0},
- {"WAccessGetUserPerms",105, api_WAccessGetUserPerms,0},
- {"SetUserPassword", 115, api_SetUserPassword,0},
- {"WWkstaUserLogon", 132, api_WWkstaUserLogon,0},
- {"PrintJobInfo", 147, api_PrintJobInfo,0},
- {"WPrintDriverEnum", 205, api_WPrintDriverEnum,0},
- {"WPrintQProcEnum", 206, api_WPrintQProcEnum,0},
- {"WPrintPortEnum", 207, api_WPrintPortEnum,0},
- {NULL, -1, api_Unsupported,0}};
-
-
/****************************************************************************
- handle remote api calls
- ****************************************************************************/
-static int api_reply(int cnum,int uid,char *outbuf,char *data,char *params,
- int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
-{
- int api_command = SVAL(params,0);
- char *rdata = NULL;
- char *rparam = NULL;
- int rdata_len = 0;
- int rparam_len = 0;
- BOOL reply=False;
- int i;
-
- DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
- api_command,params+2,skip_string(params+2,1),
- tdscnt,tpscnt,mdrcnt,mprcnt));
-
- for (i=0;api_commands[i].name;i++)
- if (api_commands[i].id == api_command && api_commands[i].fn)
- {
- DEBUG(3,("Doing %s\n",api_commands[i].name));
- break;
- }
-
- rdata = (char *)malloc(1024); if (rdata) bzero(rdata,1024);
- rparam = (char *)malloc(1024); if (rparam) bzero(rparam,1024);
-
- reply = api_commands[i].fn(cnum,uid,params,data,mdrcnt,mprcnt,
- &rdata,&rparam,&rdata_len,&rparam_len);
-
-
- if (rdata_len > mdrcnt ||
- rparam_len > mprcnt)
- {
- reply = api_TooSmall(cnum,uid,params,data,mdrcnt,mprcnt,
- &rdata,&rparam,&rdata_len,&rparam_len);
- }
-
-
- /* if we get False back then it's actually unsupported */
- if (!reply)
- api_Unsupported(cnum,uid,params,data,mdrcnt,mprcnt,
- &rdata,&rparam,&rdata_len,&rparam_len);
-
-
-
- /* now send the reply */
- send_trans_reply(outbuf,rdata,rparam,NULL,rdata_len,rparam_len,0);
-
- if (rdata)
- free(rdata);
- if (rparam)
- free(rparam);
+ Reply to a SMBtrans.
+ ****************************************************************************/
+
+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);
+ int tpscnt = SVAL(inbuf,smb_vwv0);
+ int tdscnt = SVAL(inbuf,smb_vwv1);
+ int mprcnt = SVAL(inbuf,smb_vwv2);
+ int mdrcnt = SVAL(inbuf,smb_vwv3);
+ int msrcnt = CVAL(inbuf,smb_vwv4);
+ BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
+ BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
+ int pscnt = SVAL(inbuf,smb_vwv9);
+ int psoff = SVAL(inbuf,smb_vwv10);
+ int dscnt = SVAL(inbuf,smb_vwv11);
+ int dsoff = SVAL(inbuf,smb_vwv12);
+ int suwcnt = CVAL(inbuf,smb_vwv13);
+ START_PROFILE(SMBtrans);
+
+ memset(name, '\0',sizeof(name));
+ srvstr_pull(inbuf, name, smb_buf(inbuf), sizeof(name), -1, STR_TERMINATE);
+
+ if (dscnt > tdscnt || pscnt > tpscnt) {
+ exit_server("invalid trans parameters");
+ }
- return(-1);
-}
-
-/****************************************************************************
- handle named pipe commands
- ****************************************************************************/
-static int named_pipe(int cnum,int uid, char *outbuf,char *name,
- uint16 *setup,char *data,char *params,
- int suwcnt,int tdscnt,int tpscnt,
- int msrcnt,int mdrcnt,int mprcnt)
-{
+ if (tdscnt) {
+ if((data = (char *)malloc(tdscnt)) == NULL) {
+ DEBUG(0,("reply_trans: data malloc fail for %d bytes !\n", tdscnt));
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ memcpy(data,smb_base(inbuf)+dsoff,dscnt);
+ }
- if (strequal(name,"LANMAN"))
- return(api_reply(cnum,uid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt));
+ if (tpscnt) {
+ if((params = (char *)malloc(tpscnt)) == NULL) {
+ DEBUG(0,("reply_trans: param malloc fail for %d bytes !\n", tpscnt));
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ memcpy(params,smb_base(inbuf)+psoff,pscnt);
+ }
- DEBUG(3,("named pipe command on <%s> 0x%X setup1=%d\n",
- name,(int)setup[0],(int)setup[1]));
-
- return(0);
-}
+ if (suwcnt) {
+ int i;
+ if((setup = (uint16 *)malloc(suwcnt*sizeof(uint16))) == NULL) {
+ DEBUG(0,("reply_trans: setup malloc fail for %d bytes !\n", (int)(suwcnt * sizeof(uint16))));
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ for (i=0;i<suwcnt;i++)
+ setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
+ }
-/****************************************************************************
- reply to a SMBtrans
- ****************************************************************************/
-int reply_trans(char *inbuf,char *outbuf)
-{
- fstring name;
-
- char *data=NULL,*params=NULL;
- uint16 *setup=NULL;
-
- int outsize = 0;
- int cnum = SVAL(inbuf,smb_tid);
- int uid = SVAL(inbuf,smb_uid);
-
- int tpscnt = SVAL(inbuf,smb_vwv0);
- int tdscnt = SVAL(inbuf,smb_vwv1);
- int mprcnt = SVAL(inbuf,smb_vwv2);
- int mdrcnt = SVAL(inbuf,smb_vwv3);
- int msrcnt = CVAL(inbuf,smb_vwv4);
- BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
- BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
- int pscnt = SVAL(inbuf,smb_vwv9);
- int psoff = SVAL(inbuf,smb_vwv10);
- int dscnt = SVAL(inbuf,smb_vwv11);
- int dsoff = SVAL(inbuf,smb_vwv12);
- int suwcnt = CVAL(inbuf,smb_vwv13);
-
- StrnCpy(name,smb_buf(inbuf),sizeof(name)-1);
-
- if (tdscnt)
- {
- data = (char *)malloc(tdscnt);
- memcpy(data,smb_base(inbuf)+dsoff,dscnt);
- }
- if (tpscnt)
- {
- params = (char *)malloc(tpscnt);
- memcpy(params,smb_base(inbuf)+psoff,pscnt);
- }
-
- if (suwcnt)
- {
- int i;
- setup = (uint16 *)malloc(suwcnt*sizeof(setup[0]));
- for (i=0;i<suwcnt;i++)
- setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
- }
-
-
- 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);
- send_smb(Client,outbuf);
- }
-
- /* receive the rest of the trans packet */
- while (pscnt < tpscnt || dscnt < tdscnt)
- {
- int pcnt,poff,dcnt,doff,pdisp,ddisp;
-
- receive_smb(Client,inbuf, 0);
- show_msg(inbuf);
-
- /* Ensure this is still a trans packet (sanity check) */
- if(CVAL(inbuf, smb_com) != SMBtrans)
- {
- DEBUG(2,("Invalid secondary trans2 packet\n"));
- if (params) free(params);
- if (data) free(data);
- if (setup) free(setup);
- return(ERROR(ERRSRV,ERRerror));
+ 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);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_trans: send_smb failed.");
}
-
- tpscnt = SVAL(inbuf,smb_vwv0);
- tdscnt = SVAL(inbuf,smb_vwv1);
- pcnt = SVAL(inbuf,smb_vwv2);
- poff = SVAL(inbuf,smb_vwv3);
- pdisp = SVAL(inbuf,smb_vwv4);
+ /* receive the rest of the trans packet */
+ while (pscnt < tpscnt || dscnt < tdscnt) {
+ BOOL ret;
+ int pcnt,poff,dcnt,doff,pdisp,ddisp;
- dcnt = SVAL(inbuf,smb_vwv5);
- doff = SVAL(inbuf,smb_vwv6);
- ddisp = SVAL(inbuf,smb_vwv7);
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ 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);
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRSRV,ERRerror));
+ }
+
+ show_msg(inbuf);
- pscnt += pcnt;
- dscnt += dcnt;
-
- if (pcnt)
- memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
- if (dcnt)
- memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);
- }
-
-
- DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",name,tdscnt,tpscnt,suwcnt));
-
-
- if (strncmp(name,"\\PIPE\\",strlen("\\PIPE\\")) == 0)
- outsize = named_pipe(cnum,uid,outbuf,name+strlen("\\PIPE\\"),setup,data,params,
- suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt);
-
-
- if (data) free(data);
- if (params) free(params);
- if (setup) free(setup);
-
- if (close_on_completion)
- close_cnum(cnum,uid);
+ tpscnt = SVAL(inbuf,smb_vwv0);
+ 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) {
+ exit_server("invalid trans parameters");
+ }
+
+ if (pcnt)
+ memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
+ if (dcnt)
+ memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);
+ }
+
+
+ DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",
+ name,tdscnt,tpscnt,suwcnt));
+
+ /*
+ * WinCE wierdness....
+ */
+
+ if (name[0] == '\\' && (StrnCaseCmp(&name[1],local_machine, strlen(local_machine)) == 0) &&
+ (name[strlen(local_machine)+1] == '\\'))
+ name_offset = strlen(local_machine)+1;
+
+ if (strnequal(&name[name_offset], "\\PIPE", strlen("\\PIPE"))) {
+ name_offset += strlen("\\PIPE");
+
+ /* Win9x weirdness. When talking to a unicode server Win9x
+ only sends \PIPE instead of \PIPE\ */
+
+ if (name[name_offset] == '\\')
+ name_offset++;
+
+ 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;
+ }
- if (one_way)
- return(-1);
-
- if (outsize == 0)
- return(ERROR(ERRSRV,ERRnosupport));
+
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ SAFE_FREE(setup);
+
+ if (close_on_completion)
+ close_cnum(conn,vuid);
- return(outsize);
+ if (one_way) {
+ END_PROFILE(SMBtrans);
+ return(-1);
+ }
+
+ if (outsize == 0) {
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRSRV,ERRnosupport));
+ }
+
+ END_PROFILE(SMBtrans);
+ return(outsize);
}
-
-
diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c
new file mode 100644
index 0000000000..666bbb5f61
--- /dev/null
+++ b/source3/smbd/lanman.c
@@ -0,0 +1,3650 @@
+/*
+ Unix SMB/CIFS implementation.
+ Inter-process communication and named pipe handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ This file handles the named pipe and mailslot calls
+ in the SMBtrans protocol
+ */
+
+#include "includes.h"
+
+#ifdef CHECK_TYPES
+#undef CHECK_TYPES
+#endif
+#define CHECK_TYPES 0
+
+extern fstring local_machine;
+extern pstring global_myname;
+extern fstring global_myworkgroup;
+
+#define NERR_Success 0
+#define NERR_badpass 86
+#define NERR_notsupported 50
+
+#define NERR_BASE (2100)
+#define NERR_BufTooSmall (NERR_BASE+23)
+#define NERR_JobNotFound (NERR_BASE+51)
+#define NERR_DestNotFound (NERR_BASE+52)
+
+#define ACCESS_READ 0x01
+#define ACCESS_WRITE 0x02
+#define ACCESS_CREATE 0x04
+
+#define SHPWLEN 8 /* share password length */
+
+static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len);
+static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len);
+
+
+static int CopyExpanded(connection_struct *conn,
+ int snum, char** dst, char* src, int* n)
+{
+ pstring buf;
+ int l;
+
+ if (!src || !dst || !n || !(*dst)) return(0);
+
+ StrnCpy(buf,src,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf);
+ l = push_ascii(*dst,buf,*n-1, STR_TERMINATE);
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int CopyAndAdvance(char** dst, char* src, int* n)
+{
+ int l;
+ if (!src || !dst || !n || !(*dst)) return(0);
+ l = push_ascii(*dst,src,*n, STR_TERMINATE);
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int StrlenExpanded(connection_struct *conn, int snum, char* s)
+{
+ pstring buf;
+ if (!s) return(0);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf);
+ return strlen(buf) + 1;
+}
+
+static char* Expand(connection_struct *conn, int snum, char* s)
+{
+ static pstring buf;
+ if (!s) return(NULL);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf);
+ return &buf[0];
+}
+
+/*******************************************************************
+ check a API string for validity when we only need to check the prefix
+ ******************************************************************/
+static BOOL prefix_ok(char *str,char *prefix)
+{
+ return(strncmp(str,prefix,strlen(prefix)) == 0);
+}
+
+struct pack_desc {
+ char* format; /* formatstring for structure */
+ char* subformat; /* subformat for structure */
+ char* base; /* baseaddress of buffer */
+ int buflen; /* remaining size for fixed part; on init: length of base */
+ int subcount; /* count of substructures */
+ char* structbuf; /* pointer into buffer for remaining fixed part */
+ int stringlen; /* remaining size for variable part */
+ char* stringbuf; /* pointer into buffer for remaining variable part */
+ int neededlen; /* total needed size */
+ int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */
+ char* curpos; /* current position; pointer into format or subformat */
+ int errcode;
+};
+
+static int get_counter(char** p)
+{
+ int i, n;
+ if (!p || !(*p)) return(1);
+ if (!isdigit((int)**p)) return 1;
+ for (n = 0;;) {
+ i = **p;
+ if (isdigit(i))
+ n = 10 * n + (i - '0');
+ else
+ return n;
+ (*p)++;
+ }
+}
+
+static int getlen(char* p)
+{
+ int n = 0;
+ if (!p) return(0);
+ while (*p) {
+ switch( *p++ ) {
+ case 'W': /* word (2 byte) */
+ n += 2;
+ break;
+ case 'K': /* status word? (2 byte) */
+ n += 2;
+ break;
+ case 'N': /* count of substructures (word) at end */
+ n += 2;
+ break;
+ case 'D': /* double word (4 byte) */
+ case 'z': /* offset to zero terminated string (4 byte) */
+ case 'l': /* offset to user data (4 byte) */
+ n += 4;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ n += 4;
+ get_counter(&p);
+ break;
+ case 'B': /* byte (with optional counter) */
+ n += get_counter(&p);
+ break;
+ }
+ }
+ return n;
+}
+
+static BOOL init_package(struct pack_desc* p, int count, int subcount)
+{
+ int n = p->buflen;
+ int i;
+
+ if (!p->format || !p->base) return(False);
+
+ i = count * getlen(p->format);
+ if (p->subformat) i += subcount * getlen(p->subformat);
+ p->structbuf = p->base;
+ p->neededlen = 0;
+ p->usedlen = 0;
+ p->subcount = 0;
+ p->curpos = p->format;
+ if (i > n) {
+ p->neededlen = i;
+ i = n = 0;
+#if 0
+ /*
+ * This is the old error code we used. Aparently
+ * WinNT/2k systems return ERRbuftoosmall (2123) and
+ * OS/2 needs this. I'm leaving this here so we can revert
+ * if needed. JRA.
+ */
+ p->errcode = ERRmoredata;
+#else
+ p->errcode = ERRbuftoosmall;
+#endif
+ }
+ else
+ p->errcode = NERR_Success;
+ p->buflen = i;
+ n -= i;
+ p->stringbuf = p->base + i;
+ p->stringlen = n;
+ return(p->errcode == NERR_Success);
+}
+
+static int package(struct pack_desc* p, ...)
+{
+ va_list args;
+ int needed=0, stringneeded;
+ char* str=NULL;
+ int is_string=0, stringused;
+ int32 temp;
+
+ va_start(args,p);
+
+ if (!*p->curpos) {
+ if (!p->subcount)
+ p->curpos = p->format;
+ else {
+ p->curpos = p->subformat;
+ p->subcount--;
+ }
+ }
+#if CHECK_TYPES
+ str = va_arg(args,char*);
+ SMB_ASSERT(strncmp(str,p->curpos,strlen(str)) == 0);
+#endif
+ stringneeded = -1;
+
+ if (!p->curpos) {
+ va_end(args);
+ return(0);
+ }
+
+ switch( *p->curpos++ ) {
+ case 'W': /* word (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'K': /* status word? (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'N': /* count of substructures (word) at end */
+ needed = 2;
+ p->subcount = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
+ break;
+ case 'D': /* double word (4 byte) */
+ needed = 4;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
+ break;
+ case 'B': /* byte (with optional counter) */
+ needed = get_counter(&p->curpos);
+ {
+ char *s = va_arg(args,char*);
+ if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed-1);
+ }
+ break;
+ case 'z': /* offset to zero terminated string (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = (str ? strlen(str)+1 : 0);
+ is_string = 1;
+ break;
+ case 'l': /* offset to user data (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = va_arg(args,int);
+ is_string = 0;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = get_counter(&p->curpos);
+ is_string = 0;
+ break;
+ }
+ va_end(args);
+ if (stringneeded >= 0) {
+ needed = 4;
+ if (p->buflen >= needed) {
+ stringused = stringneeded;
+ if (stringused > p->stringlen) {
+ stringused = (is_string ? p->stringlen : 0);
+ if (p->errcode == NERR_Success) p->errcode = ERRmoredata;
+ }
+ if (!stringused)
+ SIVAL(p->structbuf,0,0);
+ else {
+ SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
+ memcpy(p->stringbuf,str?str:"",stringused);
+ if (is_string) p->stringbuf[stringused-1] = '\0';
+ p->stringbuf += stringused;
+ p->stringlen -= stringused;
+ p->usedlen += stringused;
+ }
+ }
+ p->neededlen += stringneeded;
+ }
+ p->neededlen += needed;
+ if (p->buflen >= needed) {
+ p->structbuf += needed;
+ p->buflen -= needed;
+ p->usedlen += needed;
+ }
+ else {
+ if (p->errcode == NERR_Success) p->errcode = ERRmoredata;
+ }
+ return 1;
+}
+
+#if CHECK_TYPES
+#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
+#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
+#else
+#define PACK(desc,t,v) package(desc,v)
+#define PACKl(desc,t,v,l) package(desc,v,l)
+#endif
+
+static void PACKI(struct pack_desc* desc,char *t,int v)
+{
+ PACK(desc,t,v);
+}
+
+static void PACKS(struct pack_desc* desc,char *t,char *v)
+{
+ PACK(desc,t,v);
+}
+
+
+/****************************************************************************
+ get a print queue
+ ****************************************************************************/
+static void PackDriverData(struct pack_desc* desc)
+{
+ char drivdata[4+4+32];
+ SIVAL(drivdata,0,sizeof drivdata); /* cb */
+ SIVAL(drivdata,4,1000); /* lVersion */
+ memset(drivdata+8,0,32); /* szDeviceName */
+ push_ascii(drivdata+8,"NULL",-1, STR_TERMINATE);
+ PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
+}
+
+static int check_printq_info(struct pack_desc* desc,
+ int uLevel, char *id1, char *id2)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0:
+ desc->format = "B13";
+ break;
+ case 1:
+ desc->format = "B13BWWWzzzzzWW";
+ break;
+ case 2:
+ desc->format = "B13BWWWzzzzzWN";
+ desc->subformat = "WB21BB16B10zWWzDDz";
+ break;
+ case 3:
+ desc->format = "zWWWWzzzzWWzzl";
+ break;
+ case 4:
+ desc->format = "zWWWWzzzzWNzzl";
+ desc->subformat = "WWzWWDDzz";
+ break;
+ case 5:
+ desc->format = "z";
+ break;
+ case 51:
+ desc->format = "K";
+ break;
+ case 52:
+ desc->format = "WzzzzzzzzN";
+ desc->subformat = "z";
+ break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id1) != 0) return False;
+ if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
+ return True;
+}
+
+
+#define RAP_JOB_STATUS_QUEUED 0
+#define RAP_JOB_STATUS_PAUSED 1
+#define RAP_JOB_STATUS_SPOOLING 2
+#define RAP_JOB_STATUS_PRINTING 3
+#define RAP_JOB_STATUS_PRINTED 4
+
+#define RAP_QUEUE_STATUS_PAUSED 1
+#define RAP_QUEUE_STATUS_ERROR 2
+
+/* turn a print job status into a on the wire status
+*/
+static int printj_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return RAP_JOB_STATUS_QUEUED;
+ case LPQ_PAUSED:
+ return RAP_JOB_STATUS_PAUSED;
+ case LPQ_SPOOLING:
+ return RAP_JOB_STATUS_SPOOLING;
+ case LPQ_PRINTING:
+ return RAP_JOB_STATUS_PRINTING;
+ }
+ return 0;
+}
+
+/* turn a print queue status into a on the wire status
+*/
+static int printq_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return 0;
+ case LPQ_PAUSED:
+ return RAP_QUEUE_STATUS_PAUSED;
+ }
+ return RAP_QUEUE_STATUS_ERROR;
+}
+
+static void fill_printjob_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ print_queue_struct* queue, int n)
+{
+ time_t t = queue->time;
+
+ /* the client expects localtime */
+ t -= TimeDiff(t);
+
+ PACKI(desc,"W",queue->job); /* uJobId */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",queue->fs_user); /* szUserName */
+ PACKS(desc,"B",""); /* pad */
+ PACKS(desc,"B16",""); /* szNotifyName */
+ PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",t); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z",queue->fs_file); /* pszComment */
+ }
+ if (uLevel == 2 || uLevel == 3 || uLevel == 4) {
+ PACKI(desc,"W",queue->priority); /* uPriority */
+ PACKS(desc,"z",queue->fs_user); /* pszUserName */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */
+ PACKI(desc,"D",t); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z","Samba"); /* pszComment */
+ PACKS(desc,"z",queue->fs_file); /* pszDocument */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszNotifyName */
+ PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",SERVICE(snum)); /* pszQueue */
+ PACKS(desc,"z","lpd"); /* pszQProcName */
+ PACKS(desc,"z",""); /* pszQProcParms */
+ PACKS(desc,"z","NULL"); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ PACKS(desc,"z",""); /* pszPrinterName */
+ } else if (uLevel == 4) { /* OS2 */
+ PACKS(desc,"z",""); /* pszSpoolFileName */
+ PACKS(desc,"z",""); /* pszPortName */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",0); /* ulPagesSpooled */
+ PACKI(desc,"D",0); /* ulPagesSent */
+ PACKI(desc,"D",0); /* ulPagesPrinted */
+ PACKI(desc,"D",0); /* ulTimePrinted */
+ PACKI(desc,"D",0); /* ulExtendJobStatus */
+ PACKI(desc,"D",0); /* ulStartPage */
+ PACKI(desc,"D",0); /* ulEndPage */
+ }
+ }
+}
+
+/********************************************************************
+ Return a driver name given an snum.
+ Looks in a tdb first. Returns True if from tdb, False otherwise.
+ ********************************************************************/
+
+static BOOL get_driver_name(int snum, pstring drivername)
+{
+ NT_PRINTER_INFO_LEVEL *info = NULL;
+ BOOL in_tdb = False;
+
+ get_a_printer (&info, 2, lp_servicename(snum));
+ if (info != NULL) {
+ pstrcpy( drivername, info->info_2->drivername);
+ in_tdb = True;
+ free_a_printer(&info, 2);
+ } else {
+ pstrcpy( drivername, lp_printerdriver(snum));
+ }
+
+ return in_tdb;
+}
+
+/********************************************************************
+ Respond to the DosPrintQInfo command with a level of 52
+ This is used to get printer driver information for Win9x clients
+ ********************************************************************/
+static void fill_printq_info_52(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ int count, print_queue_struct* queue,
+ print_status_struct* status)
+{
+ int i;
+ BOOL ok = False;
+ pstring tok,driver,datafile,langmon,helpfile,datatype;
+ char *p;
+ char **lines = NULL;
+ pstring gen_line;
+ BOOL in_tdb = False;
+ fstring location;
+ pstring drivername;
+
+ /*
+ * Check in the tdb *first* before checking the legacy
+ * files. This allows an NT upload to take precedence over
+ * the existing fileset. JRA.
+ *
+ * we need to lookup the driver name prior to making the call
+ * to get_a_printer_driver_9x_compatible() and not rely on the
+ * 'print driver' parameter --jerry
+ */
+
+
+ if ((get_driver_name(snum,drivername)) &&
+ ((ok = get_a_printer_driver_9x_compatible(gen_line, drivername)) == True))
+ {
+ in_tdb = True;
+ p = gen_line;
+ DEBUG(10,("9x compatable driver line for [%s]: [%s]\n", drivername, gen_line));
+ }
+ else
+ {
+ /* didn't find driver in tdb */
+
+ DEBUG(10,("snum: %d\nprinterdriver: [%s]\nlp_driverfile: [%s]\n",
+ snum, drivername, lp_driverfile(snum)));
+
+ lines = file_lines_load(lp_driverfile(snum),NULL);
+ if (!lines)
+ {
+ DEBUG(3,("Can't open %s - %s\n", lp_driverfile(snum),
+ strerror(errno)));
+ desc->errcode=NERR_notsupported;
+ goto done;
+ }
+
+ /* lookup the long printer driver name in the file description */
+ for (i=0;lines[i] && !ok;i++)
+ {
+ p = lines[i];
+ if (next_token(&p,tok,":",sizeof(tok)) &&
+ (strlen(drivername) == strlen(tok)) &&
+ (!strncmp(tok,drivername,strlen(drivername))))
+ {
+ ok = True;
+ }
+ }
+ }
+
+ if (ok)
+ {
+ /* driver file name */
+ if (!next_token(&p,driver,":",sizeof(driver)))
+ goto err;
+
+ /* data file name */
+ if (!next_token(&p,datafile,":",sizeof(datafile)))
+ goto err;
+
+ /*
+ * for the next tokens - which may be empty - I have
+ * to check for empty tokens first because the
+ * next_token function will skip all empty token
+ * fields */
+
+ /* help file */
+ if (*p == ':')
+ {
+ *helpfile = '\0';
+ p++;
+ }
+ else if (!next_token(&p,helpfile,":",sizeof(helpfile)))
+ goto err;
+
+ /* language monitor */
+ if (*p == ':')
+ {
+ *langmon = '\0';
+ p++;
+ }
+ else if (!next_token(&p,langmon,":",sizeof(langmon)))
+ goto err;
+
+ /* default data type */
+ if (!next_token(&p,datatype,":",sizeof(datatype)))
+ goto err;
+
+ PACKI(desc,"W",0x0400); /* don't know */
+ PACKS(desc,"z",drivername); /* long printer name */
+ PACKS(desc,"z",driver); /* Driverfile Name */
+ PACKS(desc,"z",datafile); /* Datafile name */
+ PACKS(desc,"z",langmon); /* language monitor */
+ if (in_tdb)
+ {
+ fstrcpy(location, "\\\\");
+ fstrcat(location, global_myname);
+ fstrcat(location, "\\print$\\WIN40\\0");
+ PACKS(desc,"z",location); /* share to retrieve files */
+ }
+ else
+ {
+ PACKS(desc,"z",lp_driverlocation(snum)); /* share to retrieve files */
+ }
+ PACKS(desc,"z",datatype); /* default data type */
+ PACKS(desc,"z",helpfile); /* helpfile name */
+ PACKS(desc,"z",driver); /* driver name */
+
+ DEBUG(3,("printerdriver:%s:\n",drivername));
+ DEBUG(3,("Driver:%s:\n",driver));
+ DEBUG(3,("Data File:%s:\n",datafile));
+ DEBUG(3,("Language Monitor:%s:\n",langmon));
+ if (in_tdb)
+ DEBUG(3,("lp_driverlocation:%s:\n",location));
+ else
+ DEBUG(3,("lp_driverlocation:%s:\n",lp_driverlocation(snum)));
+ DEBUG(3,("Data Type:%s:\n",datatype));
+ DEBUG(3,("Help File:%s:\n",helpfile));
+ PACKI(desc,"N",count); /* number of files to copy */
+
+ for (i=0;i<count;i++)
+ {
+ /* no need to check return value here
+ * - it was already tested in
+ * get_printerdrivernumber */
+ next_token(&p,tok,",",sizeof(tok));
+ PACKS(desc,"z",tok); /* driver files to copy */
+ DEBUG(3,("file:%s:\n",tok));
+ }
+
+ DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",
+ SERVICE(snum),count));
+
+ desc->errcode=NERR_Success;
+ goto done;
+ }
+
+ err:
+
+ DEBUG(3,("fill_printq_info: Can't supply driver files\n"));
+ desc->errcode=NERR_notsupported;
+
+ done:
+ file_lines_free(lines);
+}
+
+
+static void fill_printq_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ int count, print_queue_struct* queue,
+ print_status_struct* status)
+{
+ switch (uLevel) {
+ case 1:
+ case 2:
+ PACKS(desc,"B13",SERVICE(snum));
+ break;
+ case 3:
+ case 4:
+ case 5:
+ PACKS(desc,"z",Expand(conn,snum,SERVICE(snum)));
+ break;
+ case 51:
+ PACKI(desc,"K",printq_status(status->status));
+ break;
+ }
+
+ if (uLevel == 1 || uLevel == 2) {
+ PACKS(desc,"B",""); /* alignment */
+ PACKI(desc,"W",5); /* priority */
+ PACKI(desc,"W",0); /* start time */
+ PACKI(desc,"W",0); /* until time */
+ PACKS(desc,"z",""); /* pSepFile */
+ PACKS(desc,"z","lpd"); /* pPrProc */
+ PACKS(desc,"z",SERVICE(snum)); /* pDestinations */
+ PACKS(desc,"z",""); /* pParms */
+ if (snum < 0) {
+ PACKS(desc,"z","UNKNOWN PRINTER");
+ PACKI(desc,"W",LPSTAT_ERROR);
+ }
+ else if (!status || !status->message[0]) {
+ PACKS(desc,"z",Expand(conn,snum,lp_comment(snum)));
+ PACKI(desc,"W",LPSTAT_OK); /* status */
+ } else {
+ PACKS(desc,"z",status->message);
+ PACKI(desc,"W",printq_status(status->status)); /* status */
+ }
+ PACKI(desc,(uLevel == 1 ? "W" : "N"),count);
+ }
+
+ if (uLevel == 3 || uLevel == 4) {
+ pstring drivername;
+
+ PACKI(desc,"W",5); /* uPriority */
+ PACKI(desc,"W",0); /* uStarttime */
+ PACKI(desc,"W",0); /* uUntiltime */
+ PACKI(desc,"W",5); /* pad1 */
+ PACKS(desc,"z",""); /* pszSepFile */
+ PACKS(desc,"z","WinPrint"); /* pszPrProc */
+ PACKS(desc,"z",NULL); /* pszParms */
+ PACKS(desc,"z",NULL); /* pszComment - don't ask.... JRA */
+ /* "don't ask" that it's done this way to fix corrupted
+ Win9X/ME printer comments. */
+ if (!status) {
+ PACKI(desc,"W",LPSTAT_OK); /* fsStatus */
+ } else {
+ PACKI(desc,"W",printq_status(status->status)); /* fsStatus */
+ }
+ PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */
+ PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */
+ get_driver_name(snum,drivername);
+ PACKS(desc,"z",drivername); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ }
+
+ if (uLevel == 2 || uLevel == 4) {
+ int i;
+ for (i=0;i<count;i++)
+ fill_printjob_info(conn,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
+ }
+
+ if (uLevel==52) {
+ fill_printq_info_52(conn, snum, uLevel, desc, count, queue, status);
+ }
+}
+
+/* This function returns the number of files for a given driver */
+static int get_printerdrivernumber(int snum)
+{
+ int i, result = 0;
+ BOOL ok = False;
+ pstring tok;
+ char *p;
+ char **lines = NULL;
+ pstring gen_line;
+ pstring drivername;
+
+ /*
+ * Check in the tdb *first* before checking the legacy
+ * files. This allows an NT upload to take precedence over
+ * the existing fileset. JRA.
+ *
+ * we need to lookup the driver name prior to making the call
+ * to get_a_printer_driver_9x_compatible() and not rely on the
+ * 'print driver' parameter --jerry
+ */
+
+ if ((get_driver_name(snum,drivername)) &&
+ (ok = get_a_printer_driver_9x_compatible(gen_line, drivername) == True))
+ {
+ p = gen_line;
+ DEBUG(10,("9x compatable driver line for [%s]: [%s]\n", drivername, gen_line));
+ }
+ else
+ {
+ /* didn't find driver in tdb */
+
+ DEBUG(10,("snum: %d\nprinterdriver: [%s]\nlp_driverfile: [%s]\n",
+ snum, drivername, lp_driverfile(snum)));
+
+ lines = file_lines_load(lp_driverfile(snum), NULL);
+ if (!lines)
+ {
+ DEBUG(3,("Can't open %s - %s\n", lp_driverfile(snum),strerror(errno)));
+ goto done;
+ }
+
+ /* lookup the long printer driver name in the file description */
+ for (i=0;lines[i] && !ok;i++)
+ {
+ p = lines[i];
+ if (next_token(&p,tok,":",sizeof(tok)) &&
+ (strlen(drivername) == strlen(tok)) &&
+ (!strncmp(tok,drivername,strlen(drivername))))
+ {
+ ok = True;
+ }
+ }
+ }
+
+ if( ok )
+ {
+ /* skip 5 fields */
+ i = 5;
+ while (*p && i) {
+ if (*p++ == ':') i--;
+ }
+ if (!*p || i) {
+ DEBUG(3,("Can't determine number of printer driver files\n"));
+ goto done;
+ }
+
+ /* count the number of files */
+ while (next_token(&p,tok,",",sizeof(tok)))
+ i++;
+
+ result = i;
+ }
+
+ done:
+
+ file_lines_free(lines);
+
+ return result;
+}
+
+static BOOL api_DosPrintQGetInfo(connection_struct *conn,
+ uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *QueueName = p;
+ int uLevel;
+ int count=0;
+ int snum;
+ char* str3;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ char* tmpdata=NULL;
+
+ memset((char *)&status,'\0',sizeof(status));
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+ str3 = p + 4;
+
+ /* remove any trailing username */
+ if ((p = strchr_m(QueueName,'%')))
+ *p = 0;
+
+ DEBUG(3,("api_DosPrintQGetInfo uLevel=%d name=%s\n",uLevel,QueueName));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh"))
+ return False;
+ if (!check_printq_info(&desc,uLevel,str2,str3)) {
+ /*
+ * Patch from Scott Moomaw <scott@bridgewater.edu>
+ * to return the 'invalid info level' error if an
+ * unknown level was requested.
+ */
+ *rdata_len = 0;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,ERRunknownlevel);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,0);
+ return(True);
+ }
+
+ snum = lp_servicenumber(QueueName);
+ if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(QueueName,pnum);
+ snum = lp_servicenumber(QueueName);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum))
+ return(False);
+
+ if (uLevel==52) {
+ count = get_printerdrivernumber(snum);
+ DEBUG(3,("api_DosPrintQGetInfo: Driver files count: %d\n",count));
+ } else {
+ count = print_queue_status(snum, &queue,&status);
+ }
+
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *) malloc (desc.buflen);
+ }
+
+ if (init_package(&desc,1,count)) {
+ desc.subcount = count;
+ fill_printq_info(conn,snum,uLevel,&desc,count,queue,&status);
+ }
+
+ *rdata_len = desc.usedlen;
+
+ /*
+ * We must set the return code to ERRbuftoosmall
+ * in order to support lanman style printing with Win NT/2k
+ * clients --jerry
+ */
+ if (!mdrcnt && lp_disable_spoolss())
+ desc.errcode = ERRbuftoosmall;
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));
+
+ SAFE_FREE(queue);
+ SAFE_FREE(tmpdata);
+
+ return(True);
+}
+
+/****************************************************************************
+ View list of all print jobs on all queues.
+****************************************************************************/
+
+static BOOL api_DosPrintQEnum(connection_struct *conn, uint16 vuid, char* param, char* data,
+ int mdrcnt, int mprcnt,
+ char **rdata, char** rparam,
+ int *rdata_len, int *rparam_len)
+{
+ char *param_format = param+2;
+ char *output_format1 = skip_string(param_format,1);
+ char *p = skip_string(output_format1,1);
+ int uLevel = SVAL(p,0);
+ char *output_format2 = p + 4;
+ int services = lp_numservices();
+ int i, n;
+ struct pack_desc desc;
+ print_queue_struct **queue = NULL;
+ print_status_struct *status = NULL;
+ int* subcntarr = NULL;
+ int queuecnt, subcnt=0, succnt=0;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
+
+ if (!prefix_ok(param_format,"WrLeh")) return False;
+ if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) {
+ /*
+ * Patch from Scott Moomaw <scott@bridgewater.edu>
+ * to return the 'invalid info level' error if an
+ * unknown level was requested.
+ */
+ *rdata_len = 0;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,ERRunknownlevel);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,0);
+ return(True);
+ }
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+ if (uLevel > 0) {
+ if((queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ memset(queue,0,queuecnt*sizeof(print_queue_struct*));
+ if((status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ memset(status,0,queuecnt*sizeof(print_status_struct));
+ if((subcntarr = (int*)malloc(queuecnt*sizeof(int))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ subcnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ subcntarr[n] = print_queue_status(i, &queue[n],&status[n]);
+ subcnt += subcntarr[n];
+ n++;
+ }
+ }
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,queuecnt,subcnt)) {
+ n = 0;
+ succnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printq_info(conn,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+
+ SAFE_FREE(subcntarr);
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ for (i = 0; i < queuecnt; i++) {
+ if (queue) SAFE_FREE(queue[i]);
+ }
+
+ SAFE_FREE(queue);
+ SAFE_FREE(status);
+
+ return True;
+}
+
+/****************************************************************************
+ get info level for a server list query
+ ****************************************************************************/
+static BOOL check_server_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B16") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B16BBDz") != 0) return False;
+ break;
+ default:
+ return False;
+ }
+ return True;
+}
+
+struct srv_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain;
+ BOOL server_added;
+};
+
+
+/*******************************************************************
+ get server info lists from the files saved by nmbd. Return the
+ number of entries
+ ******************************************************************/
+static int get_server_info(uint32 servertype,
+ struct srv_info_struct **servers,
+ char *domain)
+{
+ int count=0;
+ int alloced=0;
+ char **lines;
+ BOOL local_list_only;
+ int i;
+
+ lines = file_lines_load(lock_path(SERVER_LIST), NULL);
+ if (!lines) {
+ DEBUG(4,("Can't open %s - %s\n",lock_path(SERVER_LIST),strerror(errno)));
+ return(0);
+ }
+
+ /* request for everything is code for request all servers */
+ if (servertype == SV_TYPE_ALL)
+ servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+
+ local_list_only = (servertype & SV_TYPE_LOCAL_LIST_ONLY);
+
+ DEBUG(4,("Servertype search: %8x\n",servertype));
+
+ for (i=0;lines[i];i++) {
+ fstring stype;
+ struct srv_info_struct *s;
+ char *ptr = lines[i];
+ BOOL ok = True;
+
+ if (!*ptr) continue;
+
+ if (count == alloced) {
+ struct srv_info_struct *ts;
+
+ alloced += 10;
+ ts = (struct srv_info_struct *)
+ Realloc(*servers,sizeof(**servers)*alloced);
+ if (!ts) {
+ DEBUG(0,("get_server_info: failed to enlarge servers info struct!\n"));
+ return(0);
+ }
+ else *servers = ts;
+ memset((char *)((*servers)+count),'\0',sizeof(**servers)*(alloced-count));
+ }
+ s = &(*servers)[count];
+
+ if (!next_token(&ptr,s->name , NULL, sizeof(s->name))) continue;
+ if (!next_token(&ptr,stype , NULL, sizeof(stype))) continue;
+ if (!next_token(&ptr,s->comment, NULL, sizeof(s->comment))) continue;
+ if (!next_token(&ptr,s->domain , NULL, sizeof(s->domain))) {
+ /* this allows us to cope with an old nmbd */
+ pstrcpy(s->domain,global_myworkgroup);
+ }
+
+ if (sscanf(stype,"%X",&s->type) != 1) {
+ DEBUG(4,("r:host file "));
+ ok = False;
+ }
+
+ /* Filter the servers/domains we return based on what was asked for. */
+
+ /* Check to see if we are being asked for a local list only. */
+ if(local_list_only && ((s->type & SV_TYPE_LOCAL_LIST_ONLY) == 0)) {
+ DEBUG(4,("r: local list only"));
+ ok = False;
+ }
+
+ /* doesn't match up: don't want it */
+ if (!(servertype & s->type)) {
+ DEBUG(4,("r:serv type "));
+ ok = False;
+ }
+
+ if ((servertype & SV_TYPE_DOMAIN_ENUM) !=
+ (s->type & SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("s: dom mismatch "));
+ ok = False;
+ }
+
+ if (!strequal(domain, s->domain) && !(servertype & SV_TYPE_DOMAIN_ENUM))
+ {
+ ok = False;
+ }
+
+ /* We should never return a server type with a SV_TYPE_LOCAL_LIST_ONLY set. */
+ s->type &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ if (ok)
+ {
+ DEBUG(4,("**SV** %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ s->server_added = True;
+ count++;
+ }
+ else
+ {
+ DEBUG(4,("%20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ }
+ }
+
+ file_lines_free(lines);
+ return(count);
+}
+
+
+/*******************************************************************
+ fill in a server info structure
+ ******************************************************************/
+static int fill_srv_info(struct srv_info_struct *service,
+ int uLevel, char **buf, int *buflen,
+ char **stringbuf, int *stringspace, char *baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch (uLevel) {
+ case 0: struct_len = 16; break;
+ case 1: struct_len = 26; break;
+ default: return -1;
+ }
+
+ if (!buf)
+ {
+ len = 0;
+ switch (uLevel)
+ {
+ case 1:
+ len = strlen(service->comment)+1;
+ break;
+ }
+
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if (*buflen < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = *buflen - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ switch (uLevel)
+ {
+ case 0:
+ push_ascii(p,service->name, 15, STR_TERMINATE);
+ break;
+
+ case 1:
+ push_ascii(p,service->name,15, STR_TERMINATE);
+ SIVAL(p,18,service->type);
+ SIVAL(p,22,PTR_DIFF(p2,baseaddr));
+ len += CopyAndAdvance(&p2,service->comment,&l2);
+ break;
+ }
+
+ if (stringbuf)
+ {
+ *buf = p + struct_len;
+ *buflen -= struct_len;
+ *stringbuf = p2;
+ *stringspace = l2;
+ }
+ else
+ {
+ *buf = p2;
+ *buflen -= len;
+ }
+ return len;
+}
+
+
+static BOOL srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2)
+{
+ return(strcmp(s1->name,s2->name));
+}
+
+/****************************************************************************
+ view list of servers available (or possibly domains). The info is
+ extracted from lists saved by nmbd on the local host
+ ****************************************************************************/
+static BOOL api_RNetServerEnum(connection_struct *conn, uint16 vuid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ uint32 servertype = IVAL(p,4);
+ char *p2;
+ int data_len, fixed_len, string_len;
+ int f_len = 0, s_len = 0;
+ struct srv_info_struct *servers=NULL;
+ int counted=0,total=0;
+ int i,missed;
+ fstring domain;
+ BOOL domain_request;
+ BOOL local_request;
+
+ /* If someone sets all the bits they don't really mean to set
+ DOMAIN_ENUM and LOCAL_LIST_ONLY, they just want all the
+ known servers. */
+
+ if (servertype == SV_TYPE_ALL)
+ servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+
+ /* If someone sets SV_TYPE_LOCAL_LIST_ONLY but hasn't set
+ any other bit (they may just set this bit on it's own) they
+ want all the locally seen servers. However this bit can be
+ set on its own so set the requested servers to be
+ ALL - DOMAIN_ENUM. */
+
+ if ((servertype & SV_TYPE_LOCAL_LIST_ONLY) && !(servertype & SV_TYPE_DOMAIN_ENUM))
+ servertype = SV_TYPE_ALL & ~(SV_TYPE_DOMAIN_ENUM);
+
+ domain_request = ((servertype & SV_TYPE_DOMAIN_ENUM) != 0);
+ local_request = ((servertype & SV_TYPE_LOCAL_LIST_ONLY) != 0);
+
+ p += 8;
+
+ if (!prefix_ok(str1,"WrLehD")) return False;
+ if (!check_server_info(uLevel,str2)) return False;
+
+ DEBUG(4, ("server request level: %s %8x ", str2, servertype));
+ DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request)));
+ DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request)));
+
+ if (strcmp(str1, "WrLehDz") == 0) {
+ pull_ascii_fstring(domain, p);
+ } else {
+ fstrcpy(domain, global_myworkgroup);
+ }
+
+ if (lp_browse_list())
+ total = get_server_info(servertype,&servers,domain);
+
+ data_len = fixed_len = string_len = 0;
+ missed = 0;
+
+ if (total > 0)
+ qsort(servers,total,sizeof(servers[0]),QSORT_CAST srv_comp);
+
+ {
+ char *lastname=NULL;
+
+ for (i=0;i<total;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (lastname && strequal(lastname,s->name)) continue;
+ lastname = s->name;
+ data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ if (data_len <= buf_len) {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ } else {
+ missed++;
+ }
+ }
+ }
+
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,'\0',*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+
+ {
+ char *lastname=NULL;
+ int count2 = counted;
+ for (i = 0; i < total && count2;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (lastname && strequal(lastname,s->name)) continue;
+ lastname = s->name;
+ fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ count2--;
+ }
+ }
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,(missed == 0 ? NERR_Success : ERRmoredata));
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,counted+missed);
+
+ SAFE_FREE(servers);
+
+ DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n",
+ domain,uLevel,counted,counted+missed));
+
+ return(True);
+}
+
+/****************************************************************************
+ command 0x34 - suspected of being a "Lookup Names" stub api
+ ****************************************************************************/
+static BOOL api_RNetGroupGetUsers(connection_struct *conn, uint16 vuid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ int counted=0;
+ int missed=0;
+
+ DEBUG(5,("RNetGroupGetUsers: %s %s %s %d %d\n",
+ str1, str2, p, uLevel, buf_len));
+
+ if (!prefix_ok(str1,"zWrLeh")) return False;
+
+ *rdata_len = 0;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam,0,0x08AC); /* informational warning message */
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,counted+missed);
+
+ return(True);
+}
+
+/****************************************************************************
+ get info about a share
+ ****************************************************************************/
+static BOOL check_share_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B13") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B13BWz") != 0) return False;
+ break;
+ case 2:
+ if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
+ break;
+ case 91:
+ if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
+ break;
+ default: return False;
+ }
+ return True;
+}
+
+static int fill_share_info(connection_struct *conn, int snum, int uLevel,
+ char** buf, int* buflen,
+ char** stringbuf, int* stringspace, char* baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch( uLevel ) {
+ case 0: struct_len = 13; break;
+ case 1: struct_len = 20; break;
+ case 2: struct_len = 40; break;
+ case 91: struct_len = 68; break;
+ default: return -1;
+ }
+
+
+ if (!buf)
+ {
+ len = 0;
+ if (uLevel > 0) len += StrlenExpanded(conn,snum,lp_comment(snum));
+ if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if ((*buflen) < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = (*buflen) - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ push_ascii(p,lp_servicename(snum),13, STR_TERMINATE);
+
+ if (uLevel > 0)
+ {
+ int type;
+ SCVAL(p,13,0);
+ type = STYPE_DISKTREE;
+ if (lp_print_ok(snum)) type = STYPE_PRINTQ;
+ if (strequal("IPC",lp_fstype(snum))) type = STYPE_IPC;
+ SSVAL(p,14,type); /* device type */
+ SIVAL(p,16,PTR_DIFF(p2,baseaddr));
+ len += CopyExpanded(conn,snum,&p2,lp_comment(snum),&l2);
+ }
+
+ if (uLevel > 1)
+ {
+ SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
+ SSVALS(p,22,-1); /* max uses */
+ SSVAL(p,24,1); /* current uses */
+ SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
+ len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
+ memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
+ }
+
+ if (uLevel > 2)
+ {
+ memset(p+40,0,SHPWLEN+2);
+ SSVAL(p,50,0);
+ SIVAL(p,52,0);
+ SSVAL(p,56,0);
+ SSVAL(p,58,0);
+ SIVAL(p,60,0);
+ SSVAL(p,64,0);
+ SSVAL(p,66,0);
+ }
+
+ if (stringbuf)
+ {
+ (*buf) = p + struct_len;
+ (*buflen) -= struct_len;
+ (*stringbuf) = p2;
+ (*stringspace) = l2;
+ }
+ else
+ {
+ (*buf) = p2;
+ (*buflen) -= len;
+ }
+ return len;
+}
+
+static BOOL api_RNetShareGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *netname = skip_string(str2,1);
+ char *p = skip_string(netname,1);
+ int uLevel = SVAL(p,0);
+ int snum = find_service(netname);
+
+ if (snum < 0) return False;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ *rdata = REALLOC(*rdata,mdrcnt);
+ p = *rdata;
+ *rdata_len = fill_share_info(conn,snum,uLevel,&p,&mdrcnt,0,0,0);
+ if (*rdata_len < 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ view list of shares available
+ ****************************************************************************/
+static BOOL api_RNetShareEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ char *p2;
+ int count=lp_numservices();
+ int total=0,counted=0;
+ BOOL missed = False;
+ int i;
+ int data_len, fixed_len, string_len;
+ int f_len = 0, s_len = 0;
+
+ if (!prefix_ok(str1,"WrLeh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ data_len = fixed_len = string_len = 0;
+ for (i=0;i<count;i++)
+ if (lp_browseable(i) && lp_snum_ok(i))
+ {
+ total++;
+ data_len += fill_share_info(conn,i,uLevel,0,&f_len,0,&s_len,0);
+ if (data_len <= buf_len)
+ {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ }
+ else
+ missed = True;
+ }
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,0,*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxiliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+ for (i = 0; i < count;i++)
+ if (lp_browseable(i) && lp_snum_ok(i))
+ if (fill_share_info(conn,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0)
+ break;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,missed ? ERRmoredata : NERR_Success);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,total);
+
+ DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
+ counted,total,uLevel,
+ buf_len,*rdata_len,mdrcnt));
+ return(True);
+}
+
+/****************************************************************************
+ Add a share
+ ****************************************************************************/
+static BOOL api_RNetShareAdd(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ fstring sharename;
+ fstring comment;
+ pstring pathname;
+ char *command, *cmdname;
+ uint offset;
+ int snum;
+ int res = ERRunsup;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,RAP_WShareAdd_REQ)) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+ if (uLevel != 2) return False;
+
+ pull_ascii_fstring(sharename,data);
+ snum = find_service(sharename);
+ if (snum >= 0) { /* already exists */
+ res = ERRfilexists;
+ goto error_exit;
+ }
+
+ /* only support disk share adds */
+ if (SVAL(data,14)!=STYPE_DISKTREE) return False;
+
+ offset = IVAL(data, 16);
+ if (offset >= mdrcnt) {
+ res = ERRinvalidparam;
+ goto error_exit;
+ }
+ pull_ascii_fstring(comment, offset? (data+offset) : "");
+
+ offset = IVAL(data, 26);
+ if (offset >= mdrcnt) {
+ res = ERRinvalidparam;
+ goto error_exit;
+ }
+ pull_ascii_pstring(pathname, offset? (data+offset) : "");
+
+ string_replace(sharename, '"', ' ');
+ string_replace(pathname, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ cmdname = lp_add_share_cmd();
+
+ if (!cmdname || *cmdname == '\0') return False;
+
+ asprintf(&command, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_add_share_cmd(), dyn_CONFIGFILE, sharename, pathname, comment);
+
+ if (command) {
+ DEBUG(10,("api_RNetShareAdd: Running [%s]\n", command ));
+ if ((res = smbrun(command, NULL)) != 0) {
+ DEBUG(1,("api_RNetShareAdd: Running [%s] returned (%d)\n", command, res ));
+ SAFE_FREE(command);
+ res = ERRnoaccess;
+ goto error_exit;
+ } else {
+ SAFE_FREE(command);
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+ }
+ } else return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+ *rdata_len = 0;
+
+ return True;
+
+ error_exit:
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+ SSVAL(*rparam,0,res);
+ SSVAL(*rparam,2,0);
+ return True;
+
+}
+
+/****************************************************************************
+ view list of groups available
+ ****************************************************************************/
+static BOOL api_RNetGroupEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int count=0;
+
+ if (!prefix_ok(str1,"WrLeh")) return False;
+
+ /* check it's a supported variant */
+ switch( uLevel )
+ {
+ case 0:
+ p2 = "B21";
+ break;
+ default:
+ return False;
+ }
+
+ if (strcmp(p2,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ /* XXXX we need a real SAM database some day */
+ pstrcpy(p,"Users"); p += 21; count++;
+ pstrcpy(p,"Domain Users"); p += 21; count++;
+ pstrcpy(p,"Guests"); p += 21; count++;
+ pstrcpy(p,"Domain Guests"); p += 21; count++;
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam,4,count); /* is this right?? */
+ SSVAL(*rparam,6,count); /* is this right?? */
+
+ DEBUG(3,("api_RNetGroupEnum gave %d entries\n", count));
+
+ return(True);
+}
+
+/****************************************************************************
+ view list of groups available
+ ****************************************************************************/
+static BOOL api_RNetUserEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int count=0;
+
+ if (!prefix_ok(str1,"WrLeh")) return False;
+
+ /* check it's a supported variant */
+ switch( uLevel )
+ {
+ case 0:
+ p2 = "B21";
+ break;
+ default:
+ return False;
+ }
+
+ if (strcmp(p2,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ /* XXXX we need a real SAM database some day */
+ pstrcpy(p,"Users"); p += 21; count++;
+ pstrcpy(p,"Domain Users"); p += 21; count++;
+ pstrcpy(p,"Guests"); p += 21; count++;
+ pstrcpy(p,"Domain Guests"); p += 21; count++;
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam,4,count); /* is this right?? */
+ SSVAL(*rparam,6,count); /* is this right?? */
+
+ DEBUG(3,("api_RNetUserEnum gave %d entries\n", count));
+
+ return(True);
+}
+
+
+
+/****************************************************************************
+ get the time of day info
+ ****************************************************************************/
+static BOOL api_NetRemoteTOD(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p;
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 21;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ {
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ put_dos_date3(p,0,unixdate); /* this is the time that is looked at
+ by NT in a "net time" operation,
+ it seems to ignore the one below */
+
+ /* the client expects to get localtime, not GMT, in this bit
+ (I think, this needs testing) */
+ t = LocalTime(&unixdate);
+
+ SIVAL(p,4,0); /* msecs ? */
+ SCVAL(p,8,t->tm_hour);
+ SCVAL(p,9,t->tm_min);
+ SCVAL(p,10,t->tm_sec);
+ SCVAL(p,11,0); /* hundredths of seconds */
+ SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */
+ SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */
+ SCVAL(p,16,t->tm_mday);
+ SCVAL(p,17,t->tm_mon + 1);
+ SSVAL(p,18,1900+t->tm_year);
+ SCVAL(p,20,t->tm_wday);
+ }
+
+
+ return(True);
+}
+
+/****************************************************************************
+ Set the user password.
+*****************************************************************************/
+
+static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p = skip_string(param+2,2);
+ fstring user;
+ fstring pass1,pass2;
+
+ pull_ascii_fstring(user,p);
+
+ p = skip_string(p,1);
+
+ memset(pass1,'\0',sizeof(pass1));
+ memset(pass2,'\0',sizeof(pass2));
+ memcpy(pass1,p,16);
+ memcpy(pass2,p+16,16);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_badpass);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Set password for <%s>\n",user));
+
+ /*
+ * Attempt to verify the old password against smbpasswd entries
+ * Win98 clients send old and new password in plaintext for this call.
+ */
+
+ {
+ auth_serversupplied_info *server_info = NULL;
+ DATA_BLOB password = data_blob(pass1, strlen(pass1)+1);
+ if (NT_STATUS_IS_OK(check_plaintext_password(user,password,&server_info))) {
+
+ /*
+ * If unix password sync was requested, attempt to change
+ * the /etc/passwd database first. Return failure if this cannot
+ * be done.
+ *
+ * This occurs before the oem change, becouse we don't want to
+ * update it if chgpasswd failed.
+ *
+ * Conditional on lp_unix_password_sync() becouse we don't want
+ * to touch the unix db unless we have admin permission.
+ */
+
+ if(lp_unix_password_sync() && IS_SAM_UNIX_USER(server_info->sam_account)
+ && !chgpasswd(pdb_get_username(server_info->sam_account),
+ pass1,pass2,False)) {
+ SSVAL(*rparam,0,NERR_badpass);
+ }
+
+ if (change_oem_password(server_info->sam_account,pass2))
+ {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+
+ free_server_info(&server_info);
+ }
+ data_blob_clear_free(&password);
+ }
+
+ /*
+ * If the plaintext change failed, attempt
+ * the old encrypted method. NT will generate this
+ * after trying the samr method. Note that this
+ * method is done as a last resort as this
+ * password change method loses the NT password hash
+ * and cannot change the UNIX password as no plaintext
+ * is received.
+ */
+
+ if(SVAL(*rparam,0) != NERR_Success)
+ {
+ SAM_ACCOUNT *hnd = NULL;
+
+ if (check_lanman_password(user,(unsigned char *)pass1,(unsigned char *)pass2, &hnd) &&
+ change_lanman_password(hnd,(unsigned char *)pass1,(unsigned char *)pass2))
+ {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+ pdb_free_sam(&hnd);
+ }
+
+
+ memset((char *)pass1,'\0',sizeof(fstring));
+ memset((char *)pass2,'\0',sizeof(fstring));
+
+ return(True);
+}
+
+/****************************************************************************
+ Set the user password (SamOEM version - gets plaintext).
+****************************************************************************/
+
+static BOOL api_SamOEMChangePassword(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ fstring user;
+ char *p = param + 2;
+ *rparam_len = 2;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_badpass);
+
+ /*
+ * Check the parameter definition is correct.
+ */
+ if(!strequal(param + 2, "zsT")) {
+ DEBUG(0,("api_SamOEMChangePassword: Invalid parameter string %s\n", param + 2));
+ return False;
+ }
+ p = skip_string(p, 1);
+
+ if(!strequal(p, "B516B16")) {
+ DEBUG(0,("api_SamOEMChangePassword: Invalid data parameter string %s\n", p));
+ return False;
+ }
+ p = skip_string(p,1);
+
+ p += pull_ascii_fstring(user,p);
+
+ DEBUG(3,("api_SamOEMChangePassword: Change password for <%s>\n",user));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(user);
+
+ if (pass_oem_change(user, (uchar*) data, (uchar *)&data[516], NULL, NULL))
+ {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ delete a print job
+ Form: <W> <>
+ ****************************************************************************/
+static BOOL api_RDosPrintJobDel(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int jobid, errcode;
+ extern struct current_user current_user;
+ WERROR werr = WERR_OK;
+
+ jobid = SVAL(p,0);
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+
+ if (!print_job_exists(jobid)) {
+ errcode = NERR_JobNotFound;
+ goto out;
+ }
+
+ errcode = NERR_notsupported;
+
+ switch (function) {
+ case 81: /* delete */
+ if (print_job_delete(&current_user, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ case 82: /* pause */
+ if (print_job_pause(&current_user, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ case 83: /* resume */
+ if (print_job_resume(&current_user, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(werr))
+ errcode = W_ERROR_V(werr);
+
+ out:
+ SSVAL(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+/****************************************************************************
+ Purge a print queue - or pause or resume it.
+ ****************************************************************************/
+static BOOL api_WPrintQueueCtrl(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *QueueName = skip_string(str2,1);
+ int errcode = NERR_notsupported;
+ int snum;
+ WERROR werr = WERR_OK;
+ extern struct current_user current_user;
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+
+ snum = print_queue_snum(QueueName);
+
+ if (snum == -1) {
+ errcode = NERR_JobNotFound;
+ goto out;
+ }
+
+ switch (function) {
+ case 74: /* Pause queue */
+ if (print_queue_pause(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ case 75: /* Resume queue */
+ if (print_queue_resume(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ case 103: /* Purge */
+ if (print_queue_purge(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) errcode = W_ERROR_V(werr);
+
+ out:
+ SSVAL(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ set the property of a print job (undocumented?)
+ ? function = 0xb -> set name of print job
+ ? function = 0x6 -> move print job up/down
+ Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz>
+ or <WWsTP> <WB21BB16B10zWWzDDz>
+****************************************************************************/
+static int check_printjob_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "W"; break;
+ case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
+ case 2: desc->format = "WWzWWDDzz"; break;
+ case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
+ case 4: desc->format = "WWzWWDDzzzzzDDDDDDD"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static BOOL api_PrintJobInfo(connection_struct *conn,uint16 vuid,char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ struct pack_desc desc;
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int jobid;
+ int uLevel = SVAL(p,2);
+ int function = SVAL(p,4);
+ int place, errcode;
+
+ jobid = SVAL(p,0);
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ /* check it's a supported varient */
+ if ((strcmp(str1,"WWsTP")) ||
+ (!check_printjob_info(&desc,uLevel,str2)))
+ return(False);
+
+ if (!print_job_exists(jobid)) {
+ errcode=NERR_JobNotFound;
+ goto out;
+ }
+
+ errcode = NERR_notsupported;
+
+ switch (function) {
+ case 0x6:
+ /* change job place in the queue,
+ data gives the new place */
+ place = SVAL(data,0);
+ if (print_job_set_place(jobid, place)) {
+ errcode=NERR_Success;
+ }
+ break;
+
+ case 0xb:
+ /* change print job name, data gives the name */
+ if (print_job_set_name(jobid, data)) {
+ errcode=NERR_Success;
+ }
+ break;
+
+ default:
+ return False;
+ }
+
+ out:
+ SSVALS(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_RNetServerGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int struct_len;
+
+ DEBUG(4,("NetServerGetInfo level %d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"WrLh")) return False;
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(str2,"B16") != 0) return False;
+ struct_len = 16;
+ break;
+ case 1:
+ if (strcmp(str2,"B16BBDz") != 0) return False;
+ struct_len = 26;
+ break;
+ case 2:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
+ != 0) return False;
+ struct_len = 134;
+ break;
+ case 3:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
+ != 0) return False;
+ struct_len = 144;
+ break;
+ case 20:
+ if (strcmp(str2,"DN") != 0) return False;
+ struct_len = 6;
+ break;
+ case 50:
+ if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
+ struct_len = 42;
+ break;
+ default: return False;
+ }
+
+ *rdata_len = mdrcnt;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+ p2 = p + struct_len;
+ if (uLevel != 20) {
+ srvstr_push(NULL, p,local_machine,16,
+ STR_ASCII|STR_UPPER|STR_TERMINATE);
+ }
+ p += 16;
+ if (uLevel > 0)
+ {
+ struct srv_info_struct *servers=NULL;
+ int i,count;
+ pstring comment;
+ uint32 servertype= lp_default_server_announce();
+
+ pstrcpy(comment,string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+
+ if ((count=get_server_info(SV_TYPE_ALL,&servers,global_myworkgroup))>0) {
+ for (i=0;i<count;i++)
+ if (strequal(servers[i].name,local_machine))
+ {
+ servertype = servers[i].type;
+ pstrcpy(comment,servers[i].comment);
+ }
+ }
+ SAFE_FREE(servers);
+
+ SCVAL(p,0,lp_major_announce_version());
+ SCVAL(p,1,lp_minor_announce_version());
+ SIVAL(p,2,servertype);
+
+ if (mdrcnt == struct_len) {
+ SIVAL(p,6,0);
+ } else {
+ SIVAL(p,6,PTR_DIFF(p2,*rdata));
+ standard_sub_conn(conn,comment);
+ StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
+ p2 = skip_string(p2,1);
+ }
+ }
+ if (uLevel > 1)
+ {
+ return False; /* not yet implemented */
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_NetWkstaGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *p2;
+ extern userdom_struct current_user_info;
+ int level = SVAL(p,0);
+
+ DEBUG(4,("NetWkstaGetInfo level %d\n",level));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
+ return(False);
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + 22;
+
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* host name */
+ pstrcpy(p2,local_machine);
+ strupper(p2);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ pstrcpy(p2,current_user_info.smb_name);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* login domain */
+ pstrcpy(p2,global_myworkgroup);
+ strupper(p2);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SCVAL(p,0,lp_major_announce_version()); /* system version - e.g 4 in 4.1 */
+ SCVAL(p,1,lp_minor_announce_version()); /* system version - e.g .1 in 4.1 */
+ p += 2;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ pstrcpy(p2,global_myworkgroup); /* don't know. login domain?? */
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* don't know */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ get info about a user
+
+ struct user_info_11 {
+ char usri11_name[21]; 0-20
+ char usri11_pad; 21
+ char *usri11_comment; 22-25
+ char *usri11_usr_comment; 26-29
+ unsigned short usri11_priv; 30-31
+ unsigned long usri11_auth_flags; 32-35
+ long usri11_password_age; 36-39
+ char *usri11_homedir; 40-43
+ char *usri11_parms; 44-47
+ long usri11_last_logon; 48-51
+ long usri11_last_logoff; 52-55
+ unsigned short usri11_bad_pw_count; 56-57
+ unsigned short usri11_num_logons; 58-59
+ char *usri11_logon_server; 60-63
+ unsigned short usri11_country_code; 64-65
+ char *usri11_workstations; 66-69
+ unsigned long usri11_max_storage; 70-73
+ unsigned short usri11_units_per_week; 74-75
+ unsigned char *usri11_logon_hours; 76-79
+ unsigned short usri11_code_page; 80-81
+ };
+
+where:
+
+ usri11_name specifies the user name for which information is retireved
+
+ usri11_pad aligns the next data structure element to a word boundary
+
+ usri11_comment is a null terminated ASCII comment
+
+ usri11_user_comment is a null terminated ASCII comment about the user
+
+ usri11_priv specifies the level of the privilege assigned to the user.
+ The possible values are:
+
+Name Value Description
+USER_PRIV_GUEST 0 Guest privilege
+USER_PRIV_USER 1 User privilege
+USER_PRV_ADMIN 2 Administrator privilege
+
+ usri11_auth_flags specifies the account operator privileges. The
+ possible values are:
+
+Name Value Description
+AF_OP_PRINT 0 Print operator
+
+
+Leach, Naik [Page 28]
+
+
+
+INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997
+
+
+AF_OP_COMM 1 Communications operator
+AF_OP_SERVER 2 Server operator
+AF_OP_ACCOUNTS 3 Accounts operator
+
+
+ usri11_password_age specifies how many seconds have elapsed since the
+ password was last changed.
+
+ usri11_home_dir points to a null terminated ASCII string that contains
+ the path name of the user's home directory.
+
+ usri11_parms points to a null terminated ASCII string that is set
+ aside for use by applications.
+
+ usri11_last_logon specifies the time when the user last logged on.
+ This value is stored as the number of seconds elapsed since
+ 00:00:00, January 1, 1970.
+
+ usri11_last_logoff specifies the time when the user last logged off.
+ This value is stored as the number of seconds elapsed since
+ 00:00:00, January 1, 1970. A value of 0 means the last logoff
+ time is unknown.
+
+ usri11_bad_pw_count specifies the number of incorrect passwords
+ entered since the last successful logon.
+
+ usri11_log1_num_logons specifies the number of times this user has
+ logged on. A value of -1 means the number of logons is unknown.
+
+ usri11_logon_server points to a null terminated ASCII string that
+ contains the name of the server to which logon requests are sent.
+ A null string indicates logon requests should be sent to the
+ domain controller.
+
+ usri11_country_code specifies the country code for the user's language
+ of choice.
+
+ usri11_workstations points to a null terminated ASCII string that
+ contains the names of workstations the user may log on from.
+ There may be up to 8 workstations, with the names separated by
+ commas. A null strings indicates there are no restrictions.
+
+ usri11_max_storage specifies the maximum amount of disk space the user
+ can occupy. A value of 0xffffffff indicates there are no
+ restrictions.
+
+ usri11_units_per_week specifies the equal number of time units into
+ which a week is divided. This value must be equal to 168.
+
+ usri11_logon_hours points to a 21 byte (168 bits) string that
+ specifies the time during which the user can log on. Each bit
+ represents one unique hour in a week. The first bit (bit 0, word
+ 0) is Sunday, 0:00 to 0:59, the second bit (bit 1, word 0) is
+
+
+
+Leach, Naik [Page 29]
+
+
+
+INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997
+
+
+ Sunday, 1:00 to 1:59 and so on. A null pointer indicates there
+ are no restrictions.
+
+ usri11_code_page specifies the code page for the user's language of
+ choice
+
+All of the pointers in this data structure need to be treated
+specially. The pointer is a 32 bit pointer. The higher 16 bits need
+to be ignored. The converter word returned in the parameters section
+needs to be subtracted from the lower 16 bits to calculate an offset
+into the return buffer where this ASCII string resides.
+
+There is no auxiliary data in the response.
+
+ ****************************************************************************/
+
+#define usri11_name 0
+#define usri11_pad 21
+#define usri11_comment 22
+#define usri11_usr_comment 26
+#define usri11_full_name 30
+#define usri11_priv 34
+#define usri11_auth_flags 36
+#define usri11_password_age 40
+#define usri11_homedir 44
+#define usri11_parms 48
+#define usri11_last_logon 52
+#define usri11_last_logoff 56
+#define usri11_bad_pw_count 60
+#define usri11_num_logons 62
+#define usri11_logon_server 64
+#define usri11_country_code 68
+#define usri11_workstations 70
+#define usri11_max_storage 74
+#define usri11_units_per_week 78
+#define usri11_logon_hours 80
+#define usri11_code_page 84
+#define usri11_end 86
+
+#define USER_PRIV_GUEST 0
+#define USER_PRIV_USER 1
+#define USER_PRIV_ADMIN 2
+
+#define AF_OP_PRINT 0
+#define AF_OP_COMM 1
+#define AF_OP_SERVER 2
+#define AF_OP_ACCOUNTS 3
+
+
+static BOOL api_RNetUserGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+
+ /* get NIS home of a previously validated user - simeon */
+ /* With share level security vuid will always be zero.
+ Don't depend on vuser being non-null !!. JRA */
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if(vuser != NULL)
+ DEBUG(3,(" Username of UID %d is %s\n", (int)vuser->uid,
+ vuser->user.unix_name));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ DEBUG(4,("RNetUserGetInfo level=%d\n", uLevel));
+
+ /* check it's a supported variant */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ switch( uLevel )
+ {
+ case 0: p2 = "B21"; break;
+ case 1: p2 = "B21BB16DWzzWz"; break;
+ case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
+ case 10: p2 = "B21Bzzz"; break;
+ case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
+ default: return False;
+ }
+
+ if (strcmp(p2,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + usri11_end;
+
+ memset(p,0,21);
+ fstrcpy(p+usri11_name,UserName); /* 21 bytes - user name */
+
+ if (uLevel > 0)
+ {
+ SCVAL(p,usri11_pad,0); /* padding - 1 byte */
+ *p2 = 0;
+ }
+ if (uLevel >= 10)
+ {
+ SIVAL(p,usri11_comment,PTR_DIFF(p2,p)); /* comment */
+ pstrcpy(p2,"Comment");
+ p2 = skip_string(p2,1);
+
+ SIVAL(p,usri11_usr_comment,PTR_DIFF(p2,p)); /* user_comment */
+ pstrcpy(p2,"UserComment");
+ p2 = skip_string(p2,1);
+
+ /* EEK! the cifsrap.txt doesn't have this in!!!! */
+ SIVAL(p,usri11_full_name,PTR_DIFF(p2,p)); /* full name */
+ pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName));
+ p2 = skip_string(p2,1);
+ }
+
+ if (uLevel == 11) /* modelled after NTAS 3.51 reply */
+ {
+ SSVAL(p,usri11_priv,conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ SIVAL(p,usri11_auth_flags,AF_OP_PRINT); /* auth flags */
+ SIVALS(p,usri11_password_age,-1); /* password age */
+ SIVAL(p,usri11_homedir,PTR_DIFF(p2,p)); /* home dir */
+ pstrcpy(p2, lp_logon_home());
+ standard_sub_conn(conn, p2);
+ p2 = skip_string(p2,1);
+ SIVAL(p,usri11_parms,PTR_DIFF(p2,p)); /* parms */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,usri11_last_logon,0); /* last logon */
+ SIVAL(p,usri11_last_logoff,0); /* last logoff */
+ SSVALS(p,usri11_bad_pw_count,-1); /* bad pw counts */
+ SSVALS(p,usri11_num_logons,-1); /* num logons */
+ SIVAL(p,usri11_logon_server,PTR_DIFF(p2,p)); /* logon server */
+ pstrcpy(p2,"\\\\*");
+ p2 = skip_string(p2,1);
+ SSVAL(p,usri11_country_code,0); /* country code */
+
+ SIVAL(p,usri11_workstations,PTR_DIFF(p2,p)); /* workstations */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+
+ SIVALS(p,usri11_max_storage,-1); /* max storage */
+ SSVAL(p,usri11_units_per_week,168); /* units per week */
+ SIVAL(p,usri11_logon_hours,PTR_DIFF(p2,p)); /* logon hours */
+
+ /* a simple way to get logon hours at all times. */
+ memset(p2,0xff,21);
+ SCVAL(p2,21,0); /* fix zero termination */
+ p2 = skip_string(p2,1);
+
+ SSVAL(p,usri11_code_page,0); /* code page */
+ }
+ if (uLevel == 1 || uLevel == 2)
+ {
+ memset(p+22,' ',16); /* password */
+ SIVALS(p,38,-1); /* password age */
+ SSVAL(p,42,
+ conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
+ pstrcpy(p2,lp_logon_home());
+ standard_sub_conn(conn, p2);
+ p2 = skip_string(p2,1);
+ SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
+ *p2++ = 0;
+ SSVAL(p,52,0); /* flags */
+ SIVAL(p,54,PTR_DIFF(p2,*rdata)); /* script_path */
+ pstrcpy(p2,lp_logon_script());
+ standard_sub_conn( conn, p2 );
+ p2 = skip_string(p2,1);
+ if (uLevel == 2)
+ {
+ SIVAL(p,60,0); /* auth_flags */
+ SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
+ pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName));
+ p2 = skip_string(p2,1);
+ SIVAL(p,68,0); /* urs_comment */
+ SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,76,0); /* workstations */
+ SIVAL(p,80,0); /* last_logon */
+ SIVAL(p,84,0); /* last_logoff */
+ SIVALS(p,88,-1); /* acct_expires */
+ SIVALS(p,92,-1); /* max_storage */
+ SSVAL(p,96,168); /* units_per_week */
+ SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
+ memset(p2,-1,21);
+ p2 += 21;
+ SSVALS(p,102,-1); /* bad_pw_count */
+ SSVALS(p,104,-1); /* num_logons */
+ SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
+ pstrcpy(p2,"\\\\%L");
+ standard_sub_conn(conn, p2);
+ p2 = skip_string(p2,1);
+ SSVAL(p,110,49); /* country_code */
+ SSVAL(p,112,860); /* code page */
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len); /* is this right?? */
+
+ return(True);
+}
+
+/*******************************************************************
+ get groups that a user is a member of
+ ******************************************************************/
+static BOOL api_NetUserGetGroups(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int count=0;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLeh") != 0) return False;
+ switch( uLevel ) {
+ case 0: p2 = "B21"; break;
+ default: return False;
+ }
+ if (strcmp(p2,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ /* XXXX we need a real SAM database some day */
+ pstrcpy(p,"Users"); p += 21; count++;
+ pstrcpy(p,"Domain Users"); p += 21; count++;
+ pstrcpy(p,"Guests"); p += 21; count++;
+ pstrcpy(p,"Domain Guests"); p += 21; count++;
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ SSVAL(*rparam,4,count); /* is this right?? */
+ SSVAL(*rparam,6,count); /* is this right?? */
+
+ return(True);
+}
+
+
+static BOOL api_WWkstaUserLogon(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ char* name;
+
+ uLevel = SVAL(p,0);
+ name = p + 2;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"OOWb54WrLh") != 0) return False;
+ if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.subformat = NULL;
+ desc.format = str2;
+
+ if (init_package(&desc,1,0))
+ {
+ PACKI(&desc,"W",0); /* code */
+ PACKS(&desc,"B21",name); /* eff. name */
+ PACKS(&desc,"B",""); /* pad */
+ PACKI(&desc,"W",
+ conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ PACKI(&desc,"D",0); /* auth flags XXX */
+ PACKI(&desc,"W",0); /* num logons */
+ PACKI(&desc,"W",0); /* bad pw count */
+ PACKI(&desc,"D",0); /* last logon */
+ PACKI(&desc,"D",-1); /* last logoff */
+ PACKI(&desc,"D",-1); /* logoff time */
+ PACKI(&desc,"D",-1); /* kickoff time */
+ PACKI(&desc,"D",0); /* password age */
+ PACKI(&desc,"D",0); /* password can change */
+ PACKI(&desc,"D",-1); /* password must change */
+ {
+ fstring mypath;
+ fstrcpy(mypath,"\\\\");
+ fstrcat(mypath,local_machine);
+ strupper(mypath);
+ PACKS(&desc,"z",mypath); /* computer */
+ }
+ PACKS(&desc,"z",global_myworkgroup);/* domain */
+
+/* JHT - By calling lp_logon_script() and standard_sub() we have */
+/* made sure all macros are fully substituted and available */
+ {
+ pstring logon_script;
+ pstrcpy(logon_script,lp_logon_script());
+ standard_sub_conn( conn, logon_script );
+ PACKS(&desc,"z", logon_script); /* script path */
+ }
+/* End of JHT mods */
+
+ PACKI(&desc,"D",0x00000000); /* reserved */
+ }
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+
+/****************************************************************************
+ api_WAccessGetUserPerms
+ ****************************************************************************/
+static BOOL api_WAccessGetUserPerms(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *user = skip_string(str2,1);
+ char *resource = skip_string(user,1);
+
+ DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zzh") != 0) return False;
+ if (strcmp(str2,"") != 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,0); /* errorcode */
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,0x7f); /* permission flags */
+
+ return(True);
+}
+
+/****************************************************************************
+ api_WPrintJobEnumerate
+ ****************************************************************************/
+static BOOL api_WPrintJobGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int count;
+ int i;
+ int snum;
+ int job;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ char *tmpdata=NULL;
+
+ uLevel = SVAL(p,2);
+
+ memset((char *)&desc,'\0',sizeof(desc));
+ memset((char *)&status,'\0',sizeof(status));
+
+ DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,SVAL(p,0)));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WWrLh") != 0) return False;
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ job = SVAL(p,0);
+ snum = print_job_snum(job);
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = print_queue_status(snum,&queue,&status);
+ for (i = 0; i < count; i++) {
+ if (queue[i].job == job) break;
+ }
+
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *)malloc ( desc.buflen );
+ }
+
+ if (init_package(&desc,1,0)) {
+ if (i < count) {
+ fill_printjob_info(conn,snum,uLevel,&desc,&queue[i],i);
+ *rdata_len = desc.usedlen;
+ }
+ else {
+ desc.errcode = NERR_JobNotFound;
+ *rdata_len = 0;
+ }
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ SAFE_FREE(queue);
+ SAFE_FREE(tmpdata);
+
+ DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintJobEnumerate(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* name = p;
+ int uLevel;
+ int count;
+ int i, succnt=0;
+ int snum;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+ memset((char *)&status,'\0',sizeof(status));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLeh") != 0) return False;
+ if (uLevel > 2) return False; /* defined only for uLevel 0,1,2 */
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(name);
+ if (snum < 0 && pcap_printername_ok(name,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(name,pnum);
+ snum = lp_servicenumber(name);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = print_queue_status(snum,&queue,&status);
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,count,0)) {
+ succnt = 0;
+ for (i = 0; i < count; i++) {
+ fill_printjob_info(conn,snum,uLevel,&desc,&queue[i],i);
+ if (desc.errcode == NERR_Success) succnt = i+1;
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,count);
+
+ SAFE_FREE(queue);
+
+ DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static int check_printdest_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "B9"; break;
+ case 1: desc->format = "B9B21WWzW"; break;
+ case 2: desc->format = "z"; break;
+ case 3: desc->format = "zzzWWzzzWW"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static void fill_printdest_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc)
+{
+ char buf[100];
+ strncpy(buf,SERVICE(snum),sizeof(buf)-1);
+ buf[sizeof(buf)-1] = 0;
+ strupper(buf);
+ if (uLevel <= 1) {
+ PACKS(desc,"B9",buf); /* szName */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",""); /* szUserName */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"W",0); /* time */
+ }
+ }
+ if (uLevel == 2 || uLevel == 3) {
+ PACKS(desc,"z",buf); /* pszPrinterName */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszUserName */
+ PACKS(desc,"z",""); /* pszLogAddr */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",""); /* pszComment */
+ PACKS(desc,"z","NULL"); /* pszDrivers */
+ PACKI(desc,"W",0); /* time */
+ PACKI(desc,"W",0); /* pad1 */
+ }
+ }
+}
+
+static BOOL api_WPrintDestGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* PrinterName = p;
+ int uLevel;
+ struct pack_desc desc;
+ int snum;
+ char *tmpdata=NULL;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(PrinterName);
+ if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(PrinterName,pnum);
+ snum = lp_servicenumber(PrinterName);
+ }
+ }
+
+ if (snum < 0) {
+ *rdata_len = 0;
+ desc.errcode = NERR_DestNotFound;
+ desc.neededlen = 0;
+ }
+ else {
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *)malloc ( desc.buflen );
+ }
+ if (init_package(&desc,1,0)) {
+ fill_printdest_info(conn,snum,uLevel,&desc);
+ }
+ *rdata_len = desc.usedlen;
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
+ SAFE_FREE(tmpdata);
+ return(True);
+}
+
+static BOOL api_WPrintDestEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int queuecnt;
+ int i, n, succnt=0;
+ struct pack_desc desc;
+ int services = lp_numservices();
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,queuecnt,0)) {
+ succnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++) {
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printdest_info(conn,i,uLevel,&desc);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintDriverEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B41","NULL");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintQProcEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lpd");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintPortEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ memset((char *)&desc,'\0',sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lp0");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+struct session_info {
+ char machine[31];
+ char username[24];
+ char clitype[24];
+ int opens;
+ int time;
+};
+
+struct sessions_info {
+ int count;
+ struct session_info *session_list;
+};
+
+static int gather_sessioninfo(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct sessions_info *sinfo = state;
+ struct session_info *curinfo = NULL;
+ struct sessionid *sessid = (struct sessionid *) dbuf.dptr;
+
+ sinfo->count += 1;
+ sinfo->session_list = REALLOC(sinfo->session_list, sinfo->count * sizeof(struct session_info));
+
+ curinfo = &(sinfo->session_list[sinfo->count - 1]);
+
+ safe_strcpy(curinfo->machine, sessid->remote_machine,
+ sizeof(curinfo->machine));
+ safe_strcpy(curinfo->username, uidtoname(sessid->uid),
+ sizeof(curinfo->username));
+ DEBUG(7,("gather_sessioninfo session from %s@%s\n",
+ curinfo->username, curinfo->machine));
+ return 0;
+}
+
+/****************************************************************************
+ List open sessions
+ ****************************************************************************/
+static BOOL api_RNetSessionEnum(connection_struct *conn,uint16 vuid, char *param, char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ struct sessions_info sinfo;
+ int i;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("RNetSessionEnum uLevel=%d\n",uLevel));
+ DEBUG(7,("RNetSessionEnum req string=%s\n",str1));
+ DEBUG(7,("RNetSessionEnum ret string=%s\n",str2));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,RAP_NetSessionEnum_REQ) != 0) return False;
+ if (uLevel != 2 || strcmp(str2,RAP_SESSION_INFO_L2) != 0) return False;
+
+ sinfo.count = 0;
+ sinfo.session_list = NULL;
+
+ if (!session_traverse(gather_sessioninfo, &sinfo)) {
+ DEBUG(4,("RNetSessionEnum session_traverse failed\n"));
+ return False;
+ }
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ memset((char *)&desc,'\0',sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (!init_package(&desc,sinfo.count,0)) {
+ return False;
+ }
+
+ for(i=0; i<sinfo.count; i++) {
+ PACKS(&desc, "z", sinfo.session_list[i].machine);
+ PACKS(&desc, "z", sinfo.session_list[i].username);
+ PACKI(&desc, "W", 1); /* num conns */
+ PACKI(&desc, "W", 0); /* num opens */
+ PACKI(&desc, "W", 1); /* num users */
+ PACKI(&desc, "D", 0); /* session time */
+ PACKI(&desc, "D", 0); /* idle time */
+ PACKI(&desc, "D", 0); /* flags */
+ PACKS(&desc, "z", "Unknown Client"); /* client type string */
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0); /* converter */
+ SSVAL(*rparam,4,sinfo.count); /* count */
+
+ DEBUG(4,("RNetSessionEnum: errorcode %d\n",desc.errcode));
+ return True;
+}
+
+
+/****************************************************************************
+ The buffer was too small
+ ****************************************************************************/
+
+static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = MIN(*rparam_len,mprcnt);
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_BufTooSmall);
+
+ DEBUG(3,("Supplied buffer too small in API command\n"));
+
+ return(True);
+}
+
+
+/****************************************************************************
+ The request is not supported
+ ****************************************************************************/
+
+static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_notsupported);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Unsupported API command\n"));
+
+ return(True);
+}
+
+
+
+
+struct
+{
+ char *name;
+ int id;
+ BOOL (*fn)(connection_struct *,uint16,char *,char *,
+ int,int,char **,char **,int *,int *);
+ int flags;
+} api_commands[] = {
+ {"RNetShareEnum", RAP_WshareEnum, api_RNetShareEnum,0},
+ {"RNetShareGetInfo", RAP_WshareGetInfo, api_RNetShareGetInfo,0},
+ {"RNetShareAdd", RAP_WshareAdd, api_RNetShareAdd,0},
+ {"RNetSessionEnum", RAP_WsessionEnum, api_RNetSessionEnum,0},
+ {"RNetServerGetInfo", RAP_WserverGetInfo, api_RNetServerGetInfo,0},
+ {"RNetGroupEnum", RAP_WGroupEnum, api_RNetGroupEnum,0},
+ {"RNetGroupGetUsers", RAP_WGroupGetUsers, api_RNetGroupGetUsers,0},
+ {"RNetUserEnum", RAP_WUserEnum, api_RNetUserEnum,0},
+ {"RNetUserGetInfo", RAP_WUserGetInfo, api_RNetUserGetInfo,0},
+ {"NetUserGetGroups", RAP_WUserGetGroups, api_NetUserGetGroups,0},
+ {"NetWkstaGetInfo", RAP_WWkstaGetInfo, api_NetWkstaGetInfo,0},
+ {"DosPrintQEnum", RAP_WPrintQEnum, api_DosPrintQEnum,0},
+ {"DosPrintQGetInfo", RAP_WPrintQGetInfo, api_DosPrintQGetInfo,0},
+ {"WPrintQueuePause", RAP_WPrintQPause, api_WPrintQueueCtrl,0},
+ {"WPrintQueueResume", RAP_WPrintQContinue, api_WPrintQueueCtrl,0},
+ {"WPrintJobEnumerate",RAP_WPrintJobEnum, api_WPrintJobEnumerate,0},
+ {"WPrintJobGetInfo", RAP_WPrintJobGetInfo, api_WPrintJobGetInfo,0},
+ {"RDosPrintJobDel", RAP_WPrintJobDel, api_RDosPrintJobDel,0},
+ {"RDosPrintJobPause", RAP_WPrintJobPause, api_RDosPrintJobDel,0},
+ {"RDosPrintJobResume",RAP_WPrintJobContinue, api_RDosPrintJobDel,0},
+ {"WPrintDestEnum", RAP_WPrintDestEnum, api_WPrintDestEnum,0},
+ {"WPrintDestGetInfo", RAP_WPrintDestGetInfo, api_WPrintDestGetInfo,0},
+ {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD,0},
+ {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl,0},
+ {"NetServerEnum", RAP_NetServerEnum2, api_RNetServerEnum,0},
+ {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms,0},
+ {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword,0},
+ {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon,0},
+ {"PrintJobInfo", RAP_WPrintJobSetInfo, api_PrintJobInfo,0},
+ {"WPrintDriverEnum", RAP_WPrintDriverEnum, api_WPrintDriverEnum,0},
+ {"WPrintQProcEnum", RAP_WPrintQProcessorEnum,api_WPrintQProcEnum,0},
+ {"WPrintPortEnum", RAP_WPrintPortEnum, api_WPrintPortEnum,0},
+ {"SamOEMChangePassword",RAP_SamOEMChgPasswordUser2_P,api_SamOEMChangePassword,0},
+ {NULL, -1, api_Unsupported,0}};
+
+
+/****************************************************************************
+ Handle remote api calls
+ ****************************************************************************/
+
+int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data,char *params,
+ int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+ int api_command;
+ char *rdata = NULL;
+ char *rparam = NULL;
+ int rdata_len = 0;
+ int rparam_len = 0;
+ BOOL reply=False;
+ int i;
+
+ if (!params) {
+ DEBUG(0,("ERROR: NULL params in api_reply()\n"));
+ return 0;
+ }
+
+ api_command = SVAL(params,0);
+
+ DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
+ api_command,
+ params+2,
+ skip_string(params+2,1),
+ tdscnt,tpscnt,mdrcnt,mprcnt));
+
+ for (i=0;api_commands[i].name;i++) {
+ if (api_commands[i].id == api_command && api_commands[i].fn) {
+ DEBUG(3,("Doing %s\n",api_commands[i].name));
+ break;
+ }
+ }
+
+ rdata = (char *)malloc(1024);
+ if (rdata)
+ memset(rdata,'\0',1024);
+
+ rparam = (char *)malloc(1024);
+ if (rparam)
+ memset(rparam,'\0',1024);
+
+ if(!rdata || !rparam) {
+ DEBUG(0,("api_reply: malloc fail !\n"));
+ return -1;
+ }
+
+ reply = api_commands[i].fn(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+
+ if (rdata_len > mdrcnt ||
+ rparam_len > mprcnt) {
+ reply = api_TooSmall(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+ }
+
+ /* if we get False back then it's actually unsupported */
+ if (!reply)
+ api_Unsupported(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+ send_trans_reply(outbuf, rparam, rparam_len, rdata, rdata_len, False);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return -1;
+}
diff --git a/source3/smbd/mangle.c b/source3/smbd/mangle.c
index 8f1490c528..20b2b419cf 100644
--- a/source3/smbd/mangle.c
+++ b/source3/smbd/mangle.c
@@ -1,8 +1,7 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
- Name mangling
- Copyright (C) Andrew Tridgell 1992-1995
+ Unix SMB/CIFS implementation.
+ Name mangling interface
+ Copyright (C) Andrew Tridgell 2002
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,591 +19,99 @@
*/
#include "includes.h"
-#include "loadparm.h"
-
-extern int DEBUGLEVEL;
-extern int case_default;
-extern BOOL case_mangle;
-
-/****************************************************************************
-provide a checksum on a string
-****************************************************************************/
-int str_checksum(char *s)
-{
- int res = 0;
- int c;
- int i=0;
- while (*s)
- {
- c = *s;
- res ^= (c << (i % 15)) ^ (c >> (15-(i%15)));
- s++; i++;
- }
- return(res);
-}
-
-/****************************************************************************
-return True if a name is a special msdos reserved name
-****************************************************************************/
-static BOOL is_reserved_msdos(char *fname)
-{
- char upperFname[13];
- char *p;
-
- StrnCpy (upperFname, fname, 12);
-
- /* lpt1.txt and con.txt etc are also illegal */
- p=strchr(upperFname,'.');
- if (p)
- *p='\0';
- strupper (upperFname);
- if ((strcmp(upperFname,"CLOCK$") == 0) ||
- (strcmp(upperFname,"CON") == 0) ||
- (strcmp(upperFname,"AUX") == 0) ||
- (strcmp(upperFname,"COM1") == 0) ||
- (strcmp(upperFname,"COM2") == 0) ||
- (strcmp(upperFname,"COM3") == 0) ||
- (strcmp(upperFname,"COM4") == 0) ||
- (strcmp(upperFname,"LPT1") == 0) ||
- (strcmp(upperFname,"LPT2") == 0) ||
- (strcmp(upperFname,"LPT3") == 0) ||
- (strcmp(upperFname,"NUL") == 0) ||
- (strcmp(upperFname,"PRN") == 0))
- return (True) ;
-
- return (False);
-}
-
-
-
-/****************************************************************************
-return True if a name is in 8.3 dos format
-****************************************************************************/
-BOOL is_8_3(char *fname)
-{
- int len;
- char *dot_pos;
- char *slash_pos = strrchr(fname,'/');
- int l;
-
- if (slash_pos) fname = slash_pos+1;
- len = strlen(fname);
-
- dot_pos = strchr(fname,'.');
-
- DEBUG(5,("checking %s for 8.3\n",fname));
-
- if (case_mangle)
- switch (case_default)
- {
- case CASE_LOWER:
- if (strhasupper(fname)) return(False);
- break;
- case CASE_UPPER:
- if (strhaslower(fname)) return(False);
- break;
- }
-
- /* can't be longer than 12 chars */
- if (len == 0 || len > 12)
- return(False);
-
- /* can't be an MS-DOS Special file such as lpt1 or even lpt1.txt */
- if (is_reserved_msdos(fname))
- return(False);
-
- /* can't contain invalid dos chars */
- /* Windows use the ANSI charset.
- But filenames are translated in the PC charset.
- This Translation may be more or less relaxed depending
- the Windows application. */
-
- /* %%% A nice improvment to name mangling would be to translate
- filename to ANSI charset on the smb server host */
-
- {
- char *p = fname;
-#ifdef KANJI
- dot_pos = 0;
- while (*p)
- {
- if (is_shift_jis (*p)) {
- p += 2;
- } else if (is_kana (*p)) {
- p ++;
- } else {
- if (*p == '.' && !dot_pos)
- dot_pos = (char *) p;
- if (!isdoschar(*p))
- return(False);
- p++;
- }
- }
-#else
- while (*p)
- {
- if (!isdoschar(*p))
- return(False);
- p++;
- }
-#endif /* KANJI */
- }
-
- /* no dot and less than 9 means OK */
- if (!dot_pos)
- return(len <= 8);
-
- l = PTR_DIFF(dot_pos,fname);
-
- /* base must be at least 1 char except special cases . and .. */
- if (l == 0)
- return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0);
-
- /* base can't be greater than 8 */
- if (l > 8)
- return(False);
-
- if (lp_strip_dot() &&
- len - l == 1 &&
- !strchr(dot_pos+1,'.'))
- {
- *dot_pos = 0;
- return(True);
- }
-
- /* extension must be between 1 and 3 */
- if ( (len - l < 2 ) || (len - l > 4) )
- return(False);
-
- /* extension can't have a dot */
- if (strchr(dot_pos+1,'.'))
- return(False);
-
- /* must be in 8.3 format */
- return(True);
-}
+static struct mangle_fns *mangle_fns;
+/* this allows us to add more mangling backends */
+static struct {
+ char *name;
+ struct mangle_fns *(*init_fn)(void);
+} mangle_backends[] = {
+ { "hash", mangle_hash_init },
+ { "hash2", mangle_hash2_init },
+ { NULL, NULL }
+};
/*
-keep a stack of name mangling results - just
-so file moves and copies have a chance of working
+ initialise the mangling subsystem
*/
-fstring *mangled_stack = NULL;
-int mangled_stack_size = 0;
-int mangled_stack_len = 0;
-
-/****************************************************************************
-create the mangled stack
-****************************************************************************/
-void create_mangled_stack(int size)
-{
- if (mangled_stack)
- {
- free(mangled_stack);
- mangled_stack_size = 0;
- mangled_stack_len = 0;
- }
- if (size > 0)
- mangled_stack = (fstring *)malloc(sizeof(fstring)*size);
- if (mangled_stack) mangled_stack_size = size;
-}
-
-/****************************************************************************
-push a mangled name onto the stack
-****************************************************************************/
-static void push_mangled_name(char *s)
+static void mangle_init(void)
{
- int i;
- char *p;
-
- if (!mangled_stack)
- return;
-
- for (i=0;i<mangled_stack_len;i++)
- if (strcmp(s,mangled_stack[i]) == 0)
- {
- array_promote(mangled_stack[0],sizeof(fstring),i);
- return;
- }
+ int i;
+ char *method;
- memmove(mangled_stack[1],mangled_stack[0],
- sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1));
- strcpy(mangled_stack[0],s);
- p = strrchr(mangled_stack[0],'.');
- if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4))
- *p = 0;
- mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1);
-}
-
-/****************************************************************************
-check for a name on the mangled name stack
-****************************************************************************/
-BOOL check_mangled_stack(char *s)
-{
- int i;
- pstring tmpname;
- char extension[5];
- char *p = strrchr(s,'.');
- BOOL check_extension = False;
+ if (mangle_fns) return;
- extension[0] = 0;
+ method = lp_mangling_method();
- if (!mangled_stack) return(False);
-
- if (p)
- {
- check_extension = True;
- StrnCpy(extension,p,4);
- strlower(extension); /* XXXXXXX */
- }
-
- for (i=0;i<mangled_stack_len;i++)
- {
- strcpy(tmpname,mangled_stack[i]);
- mangle_name_83(tmpname);
- if (strequal(tmpname,s))
- {
- strcpy(s,mangled_stack[i]);
- break;
- }
- if (check_extension && !strchr(mangled_stack[i],'.'))
- {
- strcpy(tmpname,mangled_stack[i]);
- strcat(tmpname,extension);
- mangle_name_83(tmpname);
- if (strequal(tmpname,s))
- {
- strcpy(s,mangled_stack[i]);
- strcat(s,extension);
- break;
- }
+ /* find the first mangling method that manages to initialise and
+ matches the "mangling method" parameter */
+ for (i=0; mangle_backends[i].name && !mangle_fns; i++) {
+ if (!method || !*method || strcmp(method, mangle_backends[i].name) == 0) {
+ mangle_fns = mangle_backends[i].init_fn();
+ }
}
- }
-
- if (i < mangled_stack_len)
- {
- DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i]));
- array_promote(mangled_stack[0],sizeof(fstring),i);
- return(True);
- }
-
- return(False);
-}
-static char *map_filename(char *s, /* This is null terminated */
- char *pattern, /* This isn't. */
- int len) /* This is the length of pattern. */
-{
- static pstring matching_bit; /* The bit of the string which matches */
- /* a * in pattern if indeed there is a * */
- char *sp; /* Pointer into s. */
- char *pp; /* Pointer into p. */
- char *match_start; /* Where the matching bit starts. */
- pstring pat;
-
- StrnCpy(pat, pattern, len); /* Get pattern into a proper string! */
- strcpy(matching_bit,""); /* Match but no star gets this. */
- pp = pat; /* Initialise the pointers. */
- sp = s;
- if ((len == 1) && (*pattern == '*')) {
- return NULL; /* Impossible, too ambiguous for */
- /* words! */
- }
-
- while ((*sp) /* Not the end of the string. */
- && (*pp) /* Not the end of the pattern. */
- && (*sp == *pp) /* The two match. */
- && (*pp != '*')) { /* No wildcard. */
- sp++; /* Keep looking. */
- pp++;
- }
- if (!*sp && !*pp) /* End of pattern. */
- return matching_bit; /* Simple match. Return empty string. */
- if (*pp == '*') {
- pp++; /* Always interrested in the chacter */
- /* after the '*' */
- if (!*pp) { /* It is at the end of the pattern. */
- StrnCpy(matching_bit, s, sp-s);
- return matching_bit;
- } else {
- /* The next character in pattern must match a character further */
- /* along s than sp so look for that character. */
- match_start = sp;
- while ((*sp) /* Not the end of s. */
- && (*sp != *pp)) /* Not the same */
- sp++; /* Keep looking. */
- if (!*sp) { /* Got to the end without a match. */
- return NULL;
- } else { /* Still hope for a match. */
- /* Now sp should point to a matching character. */
- StrnCpy(matching_bit, match_start, sp-match_start);
- /* Back to needing a stright match again. */
- while ((*sp) /* Not the end of the string. */
- && (*pp) /* Not the end of the pattern. */
- && (*sp == *pp)) { /* The two match. */
- sp++; /* Keep looking. */
- pp++;
- }
- if (!*sp && !*pp) /* Both at end so it matched */
- return matching_bit;
- else
- return NULL;
- }
- }
- }
- return NULL; /* No match. */
+ if (!mangle_fns) {
+ DEBUG(0,("Failed to initialise mangling system '%s'\n", method));
+ exit_server("mangling init failed");
+ }
}
-/* this is the magic char used for mangling */
-char magic_char = '~';
-
-
-/****************************************************************************
-determine whther is name could be a mangled name
-****************************************************************************/
-BOOL is_mangled(char *s)
+/*
+ reset the cache. This is called when smb.conf has been reloaded
+*/
+void mangle_reset_cache(void)
{
- char *m = strchr(s,magic_char);
- if (!m) return(False);
-
- /* we use two base 36 chars efore the extension */
- if (m[1] == '.' || m[1] == 0 ||
- m[2] == '.' || m[2] == 0 ||
- (m[3] != '.' && m[3] != 0))
- return(is_mangled(m+1));
+ mangle_init();
- /* it could be */
- return(True);
+ mangle_fns->reset();
}
-
-
-/****************************************************************************
-return a base 36 character. v must be from 0 to 35.
-****************************************************************************/
-static char base36(int v)
+/*
+ see if a filename has come out of our mangling code
+*/
+BOOL mangle_is_mangled(const char *s)
{
- v = v % 36;
- if (v < 10)
- return('0'+v);
- else /* needed to work around a DEC C compiler bug */
- return('A' + (v-10));
+ return mangle_fns->is_mangled(s);
}
-
-static void do_fwd_mangled_map(char *s, char *MangledMap)
+/*
+ see if a filename matches the rules of a 8.3 filename
+*/
+BOOL mangle_is_8_3(const char *fname, BOOL check_case)
{
- /* MangledMap is a series of name pairs in () separated by spaces.
- * If s matches the first of the pair then the name given is the
- * second of the pair. A * means any number of any character and if
- * present in the second of the pair as well as the first the
- * matching part of the first string takes the place of the * in the
- * second.
- *
- * I wanted this so that we could have RCS files which can be used
- * by UNIX and DOS programs. My mapping string is (RCS rcs) which
- * converts the UNIX RCS file subdirectory to lowercase thus
- * preventing mangling.
- */
- char *start=MangledMap; /* Use this to search for mappings. */
- char *end; /* Used to find the end of strings. */
- char *match_string;
- pstring new_string; /* Make up the result here. */
- char *np; /* Points into new_string. */
-
- DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap));
- while (*start) {
- while ((*start) && (*start != '('))
- start++;
- start++; /* Skip the ( */
- if (!*start)
- continue; /* Always check for the end. */
- end = start; /* Search for the ' ' or a ')' */
- DEBUG(5,("Start of first in pair '%s'\n", start));
- while ((*end) && !((*end == ' ') || (*end == ')')))
- end++;
- if (!*end) {
- start = end;
- continue; /* Always check for the end. */
- }
- DEBUG(5,("End of first in pair '%s'\n", end));
- if ((match_string = map_filename(s, start, end-start))) {
- DEBUG(5,("Found a match\n"));
- /* Found a match. */
- start = end+1; /* Point to start of what it is to become. */
- DEBUG(5,("Start of second in pair '%s'\n", start));
- end = start;
- np = new_string;
- while ((*end) /* Not the end of string. */
- && (*end != ')') /* Not the end of the pattern. */
- && (*end != '*')) /* Not a wildcard. */
- *np++ = *end++;
- if (!*end) {
- start = end;
- continue; /* Always check for the end. */
- }
- if (*end == '*') {
- strcpy(np, match_string);
- np += strlen(match_string);
- end++; /* Skip the '*' */
- while ((*end) /* Not the end of string. */
- && (*end != ')') /* Not the end of the pattern. */
- && (*end != '*')) /* Not a wildcard. */
- *np++ = *end++;
- }
- if (!*end) {
- start = end;
- continue; /* Always check for the end. */
- }
- *np++ = '\0'; /* NULL terminate it. */
- DEBUG(5,("End of second in pair '%s'\n", end));
- strcpy(s, new_string); /* Substitute with the new name. */
- DEBUG(5,("s is now '%s'\n", s));
- }
- start = end; /* Skip a bit which cannot be wanted */
- /* anymore. */
- start++;
- }
+ return mangle_fns->is_8_3(fname, check_case);
}
-/****************************************************************************
-do the actual mangling to 8.3 format
-****************************************************************************/
-void mangle_name_83(char *s)
-{
- int csum = str_checksum(s);
- char *p;
- char extension[4];
- char base[9];
- int baselen = 0;
- int extlen = 0;
-
- extension[0]=0;
- base[0]=0;
-
- p = strrchr(s,'.');
- if (p && (strlen(p+1)<4) )
- {
- BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */
- if (all_normal && p[1] != 0)
- {
- *p = 0;
- csum = str_checksum(s);
- *p = '.';
- }
- }
-
-
- strupper(s);
-
- DEBUG(5,("Mangling name %s to ",s));
-
- if (p)
- {
- if (p == s)
- strcpy(extension,"___");
- else
- {
- *p++ = 0;
- while (*p && extlen < 3)
- {
- if (isdoschar(*p) && *p != '.')
- extension[extlen++] = *p;
- p++;
- }
- extension[extlen] = 0;
- }
- }
-
- p = s;
-
- while (*p && baselen < 5)
- {
- if (isdoschar(*p) && *p != '.')
- base[baselen++] = *p;
- p++;
- }
- base[baselen] = 0;
-
- csum = csum % (36*36);
-
- sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36));
-
- if (*extension)
- {
- strcat(s,".");
- strcat(s,extension);
- }
- DEBUG(5,("%s\n",s));
-}
-
-
-
-/*******************************************************************
- work out if a name is illegal, even for long names
- ******************************************************************/
-static BOOL illegal_name(char *name)
+/*
+ try to reverse map a 8.3 name to the original filename. This doesn't have to
+ always succeed, as the directory handling code in smbd will scan the directory
+ looking for a matching name if it doesn't. It should succeed most of the time
+ or there will be a huge performance penalty
+*/
+BOOL mangle_check_cache(char *s)
{
- static unsigned char illegal[256];
- static BOOL initialised=False;
- unsigned char *s;
-
- if (!initialised) {
- char *ill = "*\\/?<>|\":{}";
- initialised = True;
-
- bzero((char *)illegal,256);
- for (s = (unsigned char *)ill; *s; s++)
- illegal[*s] = True;
- }
-
-#ifdef KANJI
- for (s = (unsigned char *)name; *s;) {
- if (is_shift_jis (*s)) {
- s += 2;
- } else if (illegal[*s]) {
- return(True);
- } else {
- s++;
- }
- }
-#else
- for (s = (unsigned char *)name;*s;s++)
- if (illegal[*s]) return(True);
-#endif
-
-
- return(False);
+ return mangle_fns->check_cache(s);
}
-
-/****************************************************************************
-convert a filename to DOS format. return True if successful.
-****************************************************************************/
-BOOL name_map_mangle(char *OutName,BOOL need83,int snum)
+/*
+ map a long filename to a 8.3 name.
+ */
+BOOL mangle_map(char *OutName, BOOL need83, BOOL cache83, int snum)
{
-#ifdef MANGLE_LONG_FILENAMES
- if (!need83 && illegal_name(OutName)) need83 = True;
-#endif
-
- /* apply any name mappings */
- {
- char *map = lp_mangled_map(snum);
- if (map && *map)
- do_fwd_mangled_map(OutName,map);
- }
+ /* name mangling can be disabled for speed, in which case
+ we just truncate the string */
+ if (!lp_manglednames(snum)) {
+ if (need83) {
+ string_truncate(OutName, 12);
+ }
+ return True;
+ }
- /* check if it's already in 8.3 format */
- if (need83 && !is_8_3(OutName)) {
- if (!lp_manglednames(snum)) return(False);
+ /* invoke the inane "mangled map" code */
+ mangle_map_filename(OutName, snum);
- /* mangle it into 8.3 */
- push_mangled_name(OutName);
- mangle_name_83(OutName);
- }
-
- return(True);
+ return mangle_fns->name_map(OutName, need83, cache83);
}
-
diff --git a/source3/smbd/mangle_hash.c b/source3/smbd/mangle_hash.c
new file mode 100644
index 0000000000..f38c2ae2c4
--- /dev/null
+++ b/source3/smbd/mangle_hash.c
@@ -0,0 +1,775 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mangling
+ Copyright (C) Andrew Tridgell 1992-2002
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/* -------------------------------------------------------------------------- **
+ * Notable problems...
+ *
+ * March/April 1998 CRH
+ * - Many of the functions in this module overwrite string buffers passed to
+ * them. This causes a variety of problems and is, generally speaking,
+ * dangerous and scarry. See the kludge notes in name_map()
+ * below.
+ * - It seems that something is calling name_map() twice. The
+ * first call is probably some sort of test. Names which contain
+ * illegal characters are being doubly mangled. I'm not sure, but
+ * I'm guessing the problem is in server.c.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * History...
+ *
+ * March/April 1998 CRH
+ * Updated a bit. Rewrote is_mangled() to be a bit more selective.
+ * Rewrote the mangled name cache. Added comments here and there.
+ * &c.
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "includes.h"
+
+
+/* -------------------------------------------------------------------------- **
+ * External Variables...
+ */
+
+extern int case_default; /* Are conforming 8.3 names all upper or lower? */
+extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */
+
+/* -------------------------------------------------------------------------- **
+ * Other stuff...
+ *
+ * magic_char - This is the magic char used for mangling. It's
+ * global. There is a call to lp_magicchar() in server.c
+ * that is used to override the initial value.
+ *
+ * MANGLE_BASE - This is the number of characters we use for name mangling.
+ *
+ * basechars - The set characters used for name mangling. This
+ * is static (scope is this file only).
+ *
+ * mangle() - Macro used to select a character from basechars (i.e.,
+ * mangle(n) will return the nth digit, modulo MANGLE_BASE).
+ *
+ * chartest - array 0..255. The index range is the set of all possible
+ * values of a byte. For each byte value, the content is a
+ * two nibble pair. See BASECHAR_MASK and ILLEGAL_MASK,
+ * below.
+ *
+ * ct_initialized - False until the chartest array has been initialized via
+ * a call to init_chartest().
+ *
+ * BASECHAR_MASK - Masks the upper nibble of a one-byte value.
+ *
+ * ILLEGAL_MASK - Masks the lower nibble of a one-byte value.
+ *
+ * isbasecahr() - Given a character, check the chartest array to see
+ * if that character is in the basechars set. This is
+ * faster than using strchr_m().
+ *
+ * isillegal() - Given a character, check the chartest array to see
+ * if that character is in the illegal characters set.
+ * This is faster than using strchr_m().
+ *
+ * mangled_cache - Cache header used for storing mangled -> original
+ * reverse maps.
+ *
+ * mc_initialized - False until the mangled_cache structure has been
+ * initialized via a call to reset_mangled_cache().
+ *
+ * MANGLED_CACHE_MAX_ENTRIES - Default maximum number of entries for the
+ * cache. A value of 0 indicates "infinite".
+ *
+ * MANGLED_CACHE_MAX_MEMORY - Default maximum amount of memory for the
+ * cache. When the cache was kept as an array of 256
+ * byte strings, the default cache size was 50 entries.
+ * This required a fixed 12.5Kbytes of memory. The
+ * mangled stack parameter is no longer used (though
+ * this might change). We're now using a fixed 16Kbyte
+ * maximum cache size. This will probably be much more
+ * than 50 entries.
+ */
+
+char magic_char = '~';
+
+static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
+#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1)
+
+static unsigned char chartest[256] = { 0 };
+static BOOL ct_initialized = False;
+
+#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
+#define BASECHAR_MASK 0xf0
+#define ILLEGAL_MASK 0x0f
+#define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK )
+#define isillegal(C) ( (chartest[ ((C) & 0xff) ]) & ILLEGAL_MASK )
+
+static ubi_cacheRoot mangled_cache[1] = { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0 } };
+static BOOL mc_initialized = False;
+#define MANGLED_CACHE_MAX_ENTRIES 0
+#define MANGLED_CACHE_MAX_MEMORY 16384
+
+
+/* -------------------------------------------------------------------------- **
+ * External Variables...
+ */
+
+extern int case_default; /* Are conforming 8.3 names all upper or lower? */
+extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */
+
+/* -------------------------------------------------------------------- */
+
+static NTSTATUS has_valid_chars(const smb_ucs2_t *s)
+{
+ if (!s || !*s) return NT_STATUS_INVALID_PARAMETER;
+
+ /* CHECK: this should not be necessary if the ms wild chars
+ are not valid in valid.dat --- simo */
+ if (ms_has_wild_w(s)) return NT_STATUS_UNSUCCESSFUL;
+
+ while (*s) {
+ if(!isvalid83_w(*s)) return NT_STATUS_UNSUCCESSFUL;
+ s++;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* return False if something fail and
+ * return 2 alloced unicode strings that contain prefix and extension
+ */
+static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix, smb_ucs2_t **extension)
+{
+ size_t ext_len;
+ smb_ucs2_t *p;
+
+ *extension = 0;
+ *prefix = strdup_w(ucs2_string);
+ if (!*prefix) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if ((p = strrchr_w(*prefix, UCS2_CHAR('.'))))
+ {
+ ext_len = strlen_w(p+1);
+ if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) &&
+ (NT_STATUS_IS_OK(has_valid_chars(p+1)))) /* check extension */
+ {
+ *p = 0;
+ *extension = strdup_w(p+1);
+ if (!*extension) {
+ SAFE_FREE(*prefix);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/* ************************************************************************** **
+ * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name.
+ *
+ * Input: fname - String containing the name to be tested.
+ *
+ * Output: NT_STATUS_UNSUCCESSFUL, if the name matches one of the list of reserved names.
+ *
+ * Notes: This is a static function called by is_8_3(), below.
+ *
+ * ************************************************************************** **
+ */
+static NTSTATUS is_valid_name(const smb_ucs2_t *fname)
+{
+ smb_ucs2_t *str, *p;
+ NTSTATUS ret = NT_STATUS_OK;
+
+ if (!fname || !*fname) return NT_STATUS_INVALID_PARAMETER;
+
+ if (*fname == UCS2_CHAR('.')) return NT_STATUS_UNSUCCESSFUL;
+
+ ret = has_valid_chars(fname);
+ if (NT_STATUS_IS_ERR(ret)) return ret;
+
+ str = strdup_w(fname);
+ p = strchr_w(str, UCS2_CHAR('.'));
+ if (p) *p = 0;
+ strupper_w(str);
+ p = &(str[1]);
+
+ switch(str[0])
+ {
+ case UCS2_CHAR('A'):
+ if(strcmp_wa(p, "UX") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('C'):
+ if((strcmp_wa(p, "LOCK$") == 0)
+ || (strcmp_wa(p, "ON") == 0)
+ || (strcmp_wa(p, "OM1") == 0)
+ || (strcmp_wa(p, "OM2") == 0)
+ || (strcmp_wa(p, "OM3") == 0)
+ || (strcmp_wa(p, "OM4") == 0)
+ )
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('L'):
+ if((strcmp_wa(p, "PT1") == 0)
+ || (strcmp_wa(p, "PT2") == 0)
+ || (strcmp_wa(p, "PT3") == 0)
+ )
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('N'):
+ if(strcmp_wa(p, "UL") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('P'):
+ if(strcmp_wa(p, "RN") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ default:
+ break;
+ }
+
+ SAFE_FREE(str);
+ return ret;
+}
+
+static NTSTATUS is_8_3_w(const smb_ucs2_t *fname)
+{
+ smb_ucs2_t *pref = 0, *ext = 0;
+ size_t plen;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!fname || !*fname) return NT_STATUS_INVALID_PARAMETER;
+
+ if (strlen_w(fname) > 12) return NT_STATUS_UNSUCCESSFUL;
+
+ if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0)
+ return NT_STATUS_OK;
+
+ if (NT_STATUS_IS_ERR(is_valid_name(fname))) goto done;
+
+ if (NT_STATUS_IS_ERR(mangle_get_prefix(fname, &pref, &ext))) goto done;
+ plen = strlen_w(pref);
+
+ if (strchr_wa(pref, '.')) goto done;
+ if (plen < 1 || plen > 8) goto done;
+ if (ext) if (strlen_w(ext) > 3) goto done;
+
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(pref);
+ SAFE_FREE(ext);
+ return ret;
+}
+
+static BOOL is_8_3(const char *fname, BOOL check_case)
+{
+ const char *f;
+ smb_ucs2_t *ucs2name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!fname || !*fname) return False;
+ if ((f = strrchr(fname, '/')) == NULL) f = fname;
+ else f++;
+
+ if (strlen(f) > 12) return False;
+
+ ucs2name = acnv_uxu2(f);
+ if (!ucs2name)
+ {
+ DEBUG(0,("is_8_3: internal error acnv_uxu2() failed!\n"));
+ goto done;
+ }
+
+ ret = is_8_3_w(ucs2name);
+
+done:
+ SAFE_FREE(ucs2name);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+/* ************************************************************************** **
+ * Initialize the static character test array.
+ *
+ * Input: none
+ *
+ * Output: none
+ *
+ * Notes: This function changes (loads) the contents of the <chartest>
+ * array. The scope of <chartest> is this file.
+ *
+ * ************************************************************************** **
+ */
+static void init_chartest( void )
+ {
+ char *illegalchars = "*\\/?<>|\":";
+ unsigned char *s;
+
+ memset( (char *)chartest, '\0', 256 );
+
+ for( s = (unsigned char *)illegalchars; *s; s++ )
+ chartest[*s] = ILLEGAL_MASK;
+
+ for( s = (unsigned char *)basechars; *s; s++ )
+ chartest[*s] |= BASECHAR_MASK;
+
+ ct_initialized = True;
+ } /* init_chartest */
+
+
+/* ************************************************************************** **
+ * Return True if the name *could be* a mangled name.
+ *
+ * Input: s - A path name - in UNIX pathname format.
+ *
+ * Output: True if the name matches the pattern described below in the
+ * notes, else False.
+ *
+ * Notes: The input name is *not* tested for 8.3 compliance. This must be
+ * done separately. This function returns true if the name contains
+ * a magic character followed by excactly two characters from the
+ * basechars list (above), which in turn are followed either by the
+ * nul (end of string) byte or a dot (extension) or by a '/' (end of
+ * a directory name).
+ *
+ * ************************************************************************** **
+ */
+static BOOL is_mangled(const char *s)
+ {
+ char *magic;
+
+ if( !ct_initialized )
+ init_chartest();
+
+ magic = strchr_m( s, magic_char );
+ while( magic && magic[1] && magic[2] ) /* 3 chars, 1st is magic. */
+ {
+ if( ('.' == magic[3] || '/' == magic[3] || !(magic[3])) /* Ends with '.' or nul or '/' ? */
+ && isbasechar( toupper(magic[1]) ) /* is 2nd char basechar? */
+ && isbasechar( toupper(magic[2]) ) ) /* is 3rd char basechar? */
+ return( True ); /* If all above, then true, */
+ magic = strchr_m( magic+1, magic_char ); /* else seek next magic. */
+ }
+ return( False );
+ } /* is_mangled */
+
+
+/* ************************************************************************** **
+ * Compare two cache keys and return a value indicating their ordinal
+ * relationship.
+ *
+ * Input: ItemPtr - Pointer to a comparison key. In this case, this will
+ * be a mangled name string.
+ * NodePtr - Pointer to a node in the cache. The node structure
+ * will be followed in memory by a mangled name string.
+ *
+ * Output: A signed integer, as follows:
+ * (x < 0) <==> Key1 less than Key2
+ * (x == 0) <==> Key1 equals Key2
+ * (x > 0) <==> Key1 greater than Key2
+ *
+ * Notes: This is a ubiqx-style comparison routine. See ubi_BinTree for
+ * more info.
+ *
+ * ************************************************************************** **
+ */
+static signed int cache_compare( ubi_btItemPtr ItemPtr, ubi_btNodePtr NodePtr )
+ {
+ char *Key1 = (char *)ItemPtr;
+ char *Key2 = (char *)(((ubi_cacheEntryPtr)NodePtr) + 1);
+
+ return( StrCaseCmp( Key1, Key2 ) );
+ } /* cache_compare */
+
+/* ************************************************************************** **
+ * Free a cache entry.
+ *
+ * Input: WarrenZevon - Pointer to the entry that is to be returned to
+ * Nirvana.
+ * Output: none.
+ *
+ * Notes: This function gets around the possibility that the standard
+ * free() function may be implemented as a macro, or other evil
+ * subversions (oh, so much fun).
+ *
+ * ************************************************************************** **
+ */
+static void cache_free_entry( ubi_trNodePtr WarrenZevon )
+ {
+ ZERO_STRUCTP(WarrenZevon);
+ SAFE_FREE( WarrenZevon );
+ } /* cache_free_entry */
+
+/* ************************************************************************** **
+ * Initializes or clears the mangled cache.
+ *
+ * Input: none.
+ * Output: none.
+ *
+ * Notes: There is a section below that is commented out. It shows how
+ * one might use lp_ calls to set the maximum memory and entry size
+ * of the cache. You might also want to remove the constants used
+ * in ubi_cacheInit() and replace them with lp_ calls. If so, then
+ * the calls to ubi_cacheSetMax*() would be moved into the else
+ * clause. Another option would be to pass in the max_entries and
+ * max_memory values as parameters. crh 09-Apr-1998.
+ *
+ * ************************************************************************** **
+ */
+static void mangle_reset( void )
+ {
+ if( !mc_initialized )
+ {
+ (void)ubi_cacheInit( mangled_cache,
+ cache_compare,
+ cache_free_entry,
+ MANGLED_CACHE_MAX_ENTRIES,
+ MANGLED_CACHE_MAX_MEMORY );
+ mc_initialized = True;
+ }
+ else
+ {
+ (void)ubi_cacheClear( mangled_cache );
+ }
+
+ /*
+ (void)ubi_cacheSetMaxEntries( mangled_cache, lp_mangled_cache_entries() );
+ (void)ubi_cacheSetMaxMemory( mangled_cache, lp_mangled_cache_memory() );
+ */
+ } /* reset_mangled_cache */
+
+
+/* ************************************************************************** **
+ * Add a mangled name into the cache.
+ *
+ * Notes: If the mangled cache has not been initialized, then the
+ * function will simply fail. It could initialize the cache,
+ * but that's not the way it was done before I changed the
+ * cache mechanism, so I'm sticking with the old method.
+ *
+ * If the extension of the raw name maps directly to the
+ * extension of the mangled name, then we'll store both names
+ * *without* extensions. That way, we can provide consistent
+ * reverse mangling for all names that match. The test here is
+ * a bit more careful than the one done in earlier versions of
+ * mangle.c:
+ *
+ * - the extension must exist on the raw name,
+ * - it must be all lower case
+ * - it must match the mangled extension (to prove that no
+ * mangling occurred).
+ *
+ * crh 07-Apr-1998
+ *
+ * ************************************************************************** **
+ */
+static void cache_mangled_name( char *mangled_name, char *raw_name )
+ {
+ ubi_cacheEntryPtr new_entry;
+ char *s1;
+ char *s2;
+ size_t mangled_len;
+ size_t raw_len;
+ size_t i;
+
+ /* If the cache isn't initialized, give up. */
+ if( !mc_initialized )
+ return;
+
+ /* Init the string lengths. */
+ mangled_len = strlen( mangled_name );
+ raw_len = strlen( raw_name );
+
+ /* See if the extensions are unmangled. If so, store the entry
+ * without the extension, thus creating a "group" reverse map.
+ */
+ s1 = strrchr( mangled_name, '.' );
+ if( s1 && (s2 = strrchr( raw_name, '.' )) )
+ {
+ i = 1;
+ while( s1[i] && (tolower( s1[i] ) == s2[i]) )
+ i++;
+ if( !s1[i] && !s2[i] )
+ {
+ mangled_len -= i;
+ raw_len -= i;
+ }
+ }
+
+ /* Allocate a new cache entry. If the allocation fails, just return. */
+ i = sizeof( ubi_cacheEntry ) + mangled_len + raw_len + 2;
+ new_entry = malloc( i );
+ if( !new_entry )
+ return;
+
+ /* Fill the new cache entry, and add it to the cache. */
+ s1 = (char *)(new_entry + 1);
+ s2 = (char *)&(s1[mangled_len + 1]);
+ (void)StrnCpy( s1, mangled_name, mangled_len );
+ (void)StrnCpy( s2, raw_name, raw_len );
+ ubi_cachePut( mangled_cache, i, new_entry, s1 );
+ } /* cache_mangled_name */
+
+/* ************************************************************************** **
+ * Check for a name on the mangled name stack
+ *
+ * Input: s - Input *and* output string buffer.
+ *
+ * Output: True if the name was found in the cache, else False.
+ *
+ * Notes: If a reverse map is found, the function will overwrite the string
+ * space indicated by the input pointer <s>. This is frightening.
+ * It should be rewritten to return NULL if the long name was not
+ * found, and a pointer to the long name if it was found.
+ *
+ * ************************************************************************** **
+ */
+
+static BOOL check_cache( char *s )
+{
+ ubi_cacheEntryPtr FoundPtr;
+ char *ext_start = NULL;
+ char *found_name;
+ char *saved_ext = NULL;
+
+ /* If the cache isn't initialized, give up. */
+ if( !mc_initialized )
+ return( False );
+
+ FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s );
+
+ /* If we didn't find the name *with* the extension, try without. */
+ if( !FoundPtr )
+ {
+ ext_start = strrchr( s, '.' );
+ if( ext_start )
+ {
+ if((saved_ext = strdup(ext_start)) == NULL)
+ return False;
+
+ *ext_start = '\0';
+ FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s );
+ /*
+ * At this point s is the name without the
+ * extension. We re-add the extension if saved_ext
+ * is not null, before freeing saved_ext.
+ */
+ }
+ }
+
+ /* Okay, if we haven't found it we're done. */
+ if( !FoundPtr )
+ {
+ if(saved_ext)
+ {
+ /* Replace the saved_ext as it was truncated. */
+ (void)pstrcat( s, saved_ext );
+ SAFE_FREE(saved_ext);
+ }
+ return( False );
+ }
+
+ /* If we *did* find it, we need to copy it into the string buffer. */
+ found_name = (char *)(FoundPtr + 1);
+ found_name += (strlen( found_name ) + 1);
+
+ (void)pstrcpy( s, found_name );
+ if( saved_ext )
+ {
+ /* Replace the saved_ext as it was truncated. */
+ (void)pstrcat( s, saved_ext );
+ SAFE_FREE(saved_ext);
+ }
+
+ return( True );
+} /* check_mangled_cache */
+
+
+/*****************************************************************************
+ * do the actual mangling to 8.3 format
+ * the buffer must be able to hold 13 characters (including the null)
+ *****************************************************************************
+ */
+static void to_8_3(char *s)
+ {
+ int csum;
+ char *p;
+ char extension[4];
+ char base[9];
+ int baselen = 0;
+ int extlen = 0;
+
+ extension[0] = 0;
+ base[0] = 0;
+
+ p = strrchr(s,'.');
+ if( p && (strlen(p+1) < (size_t)4) )
+ {
+ BOOL all_normal = ( strisnormal(p+1) ); /* XXXXXXXXX */
+
+ if( all_normal && p[1] != 0 )
+ {
+ *p = 0;
+ csum = str_checksum( s );
+ *p = '.';
+ }
+ else
+ csum = str_checksum(s);
+ }
+ else
+ csum = str_checksum(s);
+
+ strupper( s );
+
+ if( p )
+ {
+ if( p == s )
+ safe_strcpy( extension, "___", 3 );
+ else
+ {
+ *p++ = 0;
+ while( *p && extlen < 3 )
+ {
+ if ( *p != '.') {
+ extension[extlen++] = p[0];
+ }
+ p++;
+ }
+ extension[extlen] = 0;
+ }
+ }
+
+ p = s;
+
+ while( *p && baselen < 5 )
+ {
+ if (*p != '.') {
+ base[baselen++] = p[0];
+ }
+ p++;
+ }
+ base[baselen] = 0;
+
+ csum = csum % (MANGLE_BASE*MANGLE_BASE);
+
+ (void)slprintf(s, 12, "%s%c%c%c",
+ base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) );
+
+ if( *extension )
+ {
+ (void)pstrcat( s, "." );
+ (void)pstrcat( s, extension );
+ }
+
+ } /* mangle_name_83 */
+
+/*****************************************************************************
+ * Convert a filename to DOS format. Return True if successful.
+ *
+ * Input: OutName - Source *and* destination buffer.
+ *
+ * NOTE that OutName must point to a memory space that
+ * is at least 13 bytes in size!
+ *
+ * need83 - If False, name mangling will be skipped unless the
+ * name contains illegal characters. Mapping will still
+ * be done, if appropriate. This is probably used to
+ * signal that a client does not require name mangling,
+ * thus skipping the name mangling even on shares which
+ * have name-mangling turned on.
+ * cache83 - If False, the mangled name cache will not be updated.
+ * This is usually used to prevent that we overwrite
+ * a conflicting cache entry prematurely, i.e. before
+ * we know whether the client is really interested in the
+ * current name. (See PR#13758). UKD.
+ *
+ * Output: Returns False only if the name wanted mangling but the share does
+ * not have name mangling turned on.
+ *
+ * ****************************************************************************
+ */
+static BOOL name_map(char *OutName, BOOL need83, BOOL cache83)
+{
+ smb_ucs2_t *OutName_ucs2;
+ DEBUG(5,("name_map( %s, need83 = %s, cache83 = %s)\n", OutName,
+ need83 ? "True" : "False", cache83 ? "True" : "False"));
+
+ if (push_ucs2_allocate((void **)&OutName_ucs2, OutName) < 0) {
+ DEBUG(0, ("push_ucs2_allocate failed!\n"));
+ return False;
+ }
+
+ /* check if it's already in 8.3 format */
+ if (need83 && !NT_STATUS_IS_OK(is_8_3_w(OutName_ucs2))) {
+ char *tmp = NULL;
+
+ /* mangle it into 8.3 */
+ if (cache83)
+ tmp = strdup(OutName);
+
+ to_8_3(OutName);
+
+ if(tmp != NULL) {
+ cache_mangled_name(OutName, tmp);
+ SAFE_FREE(tmp);
+ }
+ }
+
+ DEBUG(5,("name_map() ==> [%s]\n", OutName));
+ SAFE_FREE(OutName_ucs2);
+ return(True);
+} /* name_map */
+
+
+/*
+ the following provides the abstraction layer to make it easier
+ to drop in an alternative mangling implementation
+*/
+static struct mangle_fns mangle_fns = {
+ is_mangled,
+ is_8_3,
+ mangle_reset,
+ check_cache,
+ name_map
+};
+
+/* return the methods for this mangling implementation */
+struct mangle_fns *mangle_hash_init(void)
+{
+ mangle_reset();
+
+ return &mangle_fns;
+}
diff --git a/source3/smbd/mangle_hash2.c b/source3/smbd/mangle_hash2.c
new file mode 100644
index 0000000000..96ca7360b8
--- /dev/null
+++ b/source3/smbd/mangle_hash2.c
@@ -0,0 +1,587 @@
+/*
+ Unix SMB/CIFS implementation.
+ new hash based name mangling implementation
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ this mangling scheme uses the following format
+
+ Annnn~n.AAA
+
+ where nnnnn is a base 36 hash, and A represents characters from the original string
+
+ The hash is taken of the leading part of the long filename, in uppercase
+
+ for simplicity, we only allow ascii characters in 8.3 names
+ */
+
+
+/*
+ ===============================================================================
+ NOTE NOTE NOTE!!!
+
+ This file deliberately uses non-multibyte string functions in many places. This
+ is *not* a mistake. This code is multi-byte safe, but it gets this property
+ through some very subtle knowledge of the way multi-byte strings are encoded
+ and the fact that this mangling algorithm only supports ascii characters in
+ 8.3 names.
+
+ please don't convert this file to use the *_m() functions!!
+ ===============================================================================
+*/
+
+
+#include "includes.h"
+
+#if 0
+#define M_DEBUG(level, x) DEBUG(level, x)
+#else
+#define M_DEBUG(level, x)
+#endif
+
+/* these flags are used to mark characters in as having particular
+ properties */
+#define FLAG_BASECHAR 1
+#define FLAG_ASCII 2
+#define FLAG_ILLEGAL 4
+#define FLAG_WILDCARD 8
+
+/* the "possible" flags are used as a fast way to find possible DOS
+ reserved filenames */
+#define FLAG_POSSIBLE1 16
+#define FLAG_POSSIBLE2 32
+#define FLAG_POSSIBLE3 64
+#define FLAG_POSSIBLE4 128
+
+/* by default have a max of 4096 entries in the cache. */
+#ifndef MANGLE_CACHE_SIZE
+#define MANGLE_CACHE_SIZE 4096
+#endif
+
+/* these tables are used to provide fast tests for characters */
+static unsigned char char_flags[256];
+
+#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag))
+
+/* we will use a very simple direct mapped prefix cache. The big
+ advantage of this cache structure is speed and low memory usage
+
+ The cache is indexed by the low-order bits of the hash, and confirmed by
+ hashing the resulting cache entry to match the known hash
+*/
+static char **prefix_cache;
+static u32 *prefix_cache_hashes;
+
+/* these are the characters we use in the 8.3 hash. Must be 36 chars long */
+const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static unsigned char base_reverse[256];
+#define base_forward(v) basechars[v]
+
+/* the list of reserved dos names - all of these are illegal */
+const char *reserved_names[] = { "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4",
+ "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
+
+/*
+ hash a string of the specified length. The string does not need to be
+ null terminated
+
+ this hash needs to be fast with a low collision rate (what hash doesn't?)
+*/
+static u32 mangle_hash(const char *key, unsigned length)
+{
+ u32 value;
+ u32 i;
+ fstring str;
+
+ /* we have to uppercase here to ensure that the mangled name
+ doesn't depend on the case of the long name. Note that this
+ is the only place where we need to use a multi-byte string
+ function */
+ strncpy(str, key, length);
+ str[length] = 0;
+ strupper_m(str);
+
+ /* the length of a multi-byte string can change after a strupper_m */
+ length = strlen(str);
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF * length, i=0; i < length; i++) {
+ value = (value + (((unsigned char)str[i]) << (i*5 % 24)));
+ }
+
+ /* note that we force it to a 31 bit hash, to keep within the limits
+ of the 36^6 mangle space */
+ return (1103515243 * value + 12345) & ~0x80000000;
+}
+
+/*
+ initialise (ie. allocate) the prefix cache
+ */
+static BOOL cache_init(void)
+{
+ if (prefix_cache) return True;
+
+ prefix_cache = malloc(sizeof(char *) * MANGLE_CACHE_SIZE);
+ if (!prefix_cache) return False;
+
+ prefix_cache_hashes = malloc(sizeof(u32) * MANGLE_CACHE_SIZE);
+ if (!prefix_cache_hashes) return False;
+
+ memset(prefix_cache, 0, sizeof(char *) * MANGLE_CACHE_SIZE);
+ memset(prefix_cache_hashes, 0, sizeof(char *) * MANGLE_CACHE_SIZE);
+ return True;
+}
+
+/*
+ insert an entry into the prefix cache. The string might not be null
+ terminated */
+static void cache_insert(const char *prefix, int length, u32 hash)
+{
+ int i = hash % MANGLE_CACHE_SIZE;
+
+ if (prefix_cache[i]) {
+ free(prefix_cache[i]);
+ }
+
+ prefix_cache[i] = strndup(prefix, length);
+ prefix_cache_hashes[i] = hash;
+}
+
+/*
+ lookup an entry in the prefix cache. Return NULL if not found.
+*/
+static const char *cache_lookup(u32 hash)
+{
+ int i = hash % MANGLE_CACHE_SIZE;
+
+ if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) {
+ return NULL;
+ }
+
+ /* yep, it matched */
+ return prefix_cache[i];
+}
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+*/
+static BOOL is_mangled(const char *name)
+{
+ int len, i;
+
+ M_DEBUG(0,("is_mangled %s ?\n", name));
+
+ /* the best distinguishing characteristic is the ~ */
+ if (name[6] != '~') return False;
+
+ /* check the length */
+ len = strlen(name);
+ if (len > 12 || len < 8) return False;
+
+ /* check extension */
+ if (len > 8) {
+ if (name[8] != '.') return False;
+ for (i=9; name[i]; i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return False;
+ }
+ }
+ }
+
+ /* check first character */
+ if (! FLAG_CHECK(name[0], FLAG_ASCII)) {
+ return False;
+ }
+
+ /* check rest of hash */
+ if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
+ return False;
+ }
+ for (i=1;i<6;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
+ return False;
+ }
+ }
+
+ M_DEBUG(0,("is_mangled %s -> yes\n", name));
+
+ return True;
+}
+
+
+/*
+ see if a filename is an allowable 8.3 name.
+
+ we are only going to allow ascii characters in 8.3 names, as this
+ simplifies things greatly (it means that we know the string won't
+ get larger when converted from UNIX to DOS formats)
+*/
+static BOOL is_8_3(const char *name, BOOL check_case)
+{
+ int len, i;
+ char *dot_p;
+
+ /* as a special case, the names '.' and '..' are allowable 8.3 names */
+ if (name[0] == '.') {
+ if (!name[1] || (name[1] == '.' && !name[2])) {
+ return True;
+ }
+ }
+
+ /* the simplest test is on the overall length of the
+ filename. Note that we deliberately use the ascii string
+ length (not the multi-byte one) as it is faster, and gives us
+ the result we need in this case. Using strlen_m would not
+ only be slower, it would be incorrect */
+ len = strlen(name);
+ if (len > 12) return False;
+
+ /* find the '.'. Note that once again we use the non-multibyte
+ function */
+ dot_p = strchr(name, '.');
+
+ if (!dot_p) {
+ /* if the name doesn't contain a '.' then its length
+ must be less than 8 */
+ if (len > 8) {
+ return False;
+ }
+ } else {
+ int prefix_len, suffix_len;
+
+ /* if it does contain a dot then the prefix must be <=
+ 8 and the suffix <= 3 in length */
+ prefix_len = PTR_DIFF(dot_p, name);
+ suffix_len = len - (prefix_len+1);
+
+ if (prefix_len > 8 || suffix_len > 3) {
+ return False;
+ }
+
+ /* a 8.3 name cannot contain more than 1 '.' */
+ if (strchr(dot_p+1, '.')) {
+ return False;
+ }
+ }
+
+ /* the length are all OK. Now check to see if the characters themselves are OK */
+ for (i=0; name[i]; i++) {
+ /* note that we allow wildcard petterns! */
+ if (!FLAG_CHECK(name[i], FLAG_ASCII|FLAG_WILDCARD) && name[i] != '.') {
+ return False;
+ }
+ }
+
+ /* it is a good 8.3 name */
+ return True;
+}
+
+
+/*
+ reset the mangling cache on a smb.conf reload. This only really makes sense for
+ mangling backends that have parameters in smb.conf, and as this backend doesn't
+ this is a NULL operation
+*/
+static void mangle_reset(void)
+{
+ /* noop */
+}
+
+
+/*
+ try to find a 8.3 name in the cache, and if found then
+ replace the string with the original long name.
+
+ The filename must be able to hold at least sizeof(fstring)
+*/
+static BOOL check_cache(char *name)
+{
+ u32 hash, multiplier;
+ int i;
+ const char *prefix;
+ char extension[4];
+
+ /* make sure that this is a mangled name from this cache */
+ if (!is_mangled(name)) {
+ M_DEBUG(0,("check_cache: %s -> not mangled\n", name));
+ return False;
+ }
+
+ /* we need to extract the hash from the 8.3 name */
+ hash = base_reverse[(unsigned char)name[7]];
+ for (multiplier=36, i=5;i>=1;i--) {
+ u32 v = base_reverse[(unsigned char)name[i]];
+ hash += multiplier * v;
+ multiplier *= 36;
+ }
+
+ /* now look in the prefix cache for that hash */
+ prefix = cache_lookup(hash);
+ if (!prefix) {
+ M_DEBUG(0,("check_cache: %s -> %08X -> not found\n", name, hash));
+ return False;
+ }
+
+ /* we found it - construct the full name */
+ strncpy(extension, name+9, 3);
+
+ if (extension[0]) {
+ M_DEBUG(0,("check_cache: %s -> %s.%s\n", name, prefix, extension));
+ slprintf(name, sizeof(fstring), "%s.%s", prefix, extension);
+ } else {
+ M_DEBUG(0,("check_cache: %s -> %s\n", name, prefix));
+ fstrcpy(name, prefix);
+ }
+
+ return True;
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+static BOOL is_reserved_name(const char *name)
+{
+ if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) &&
+ FLAG_CHECK(name[1], FLAG_POSSIBLE2) &&
+ FLAG_CHECK(name[2], FLAG_POSSIBLE3) &&
+ FLAG_CHECK(name[3], FLAG_POSSIBLE4)) {
+ /* a likely match, scan the lot */
+ int i;
+ for (i=0; reserved_names[i]; i++) {
+ int len = strlen(reserved_names[i]);
+ /* note that we match on COM1 as well as COM1.foo */
+ if (strncasecmp(name, reserved_names[i], len) == 0 &&
+ (name[len] == '.' || name[len] == 0)) {
+ return True;
+ }
+ }
+ }
+
+ return False;
+}
+
+/*
+ see if a filename is a legal long filename
+*/
+static BOOL is_legal_name(const char *name)
+{
+ while (*name) {
+ if (FLAG_CHECK(name[0], FLAG_ILLEGAL)) {
+ return False;
+ }
+ name++;
+ }
+
+ return True;
+}
+
+/*
+ the main forward mapping function, which converts a long filename to
+ a 8.3 name
+
+ if need83 is not set then we only do the mangling if the name is illegal
+ as a long name
+
+ if cache83 is not set then we don't cache the result
+
+ the name parameter must be able to hold 13 bytes
+*/
+static BOOL name_map(char *name, BOOL need83, BOOL cache83)
+{
+ char *dot_p;
+ char lead_char;
+ char extension[4];
+ int extension_length, i;
+ int prefix_len;
+ u32 hash, v;
+ char new_name[13];
+
+ /* reserved names are handled specially */
+ if (!is_reserved_name(name)) {
+ /* if the name is already a valid 8.3 name then we don't need to
+ do anything */
+ if (is_8_3(name, False)) {
+ return True;
+ }
+
+ /* if the caller doesn't strictly need 8.3 then just check for illegal
+ filenames */
+ if (!need83 && is_legal_name(name)) {
+ return True;
+ }
+ }
+
+ /* find the '.' if any */
+ dot_p = strrchr(name, '.');
+
+ /* the leading character in the mangled name is taken from
+ the first character of the name, if it is ascii
+ otherwise '_' is used
+ */
+ lead_char = name[0];
+ if (! FLAG_CHECK(lead_char, FLAG_ASCII)) {
+ lead_char = '_';
+ }
+ lead_char = toupper(lead_char);
+
+ /* the prefix is anything up to the first dot */
+ if (dot_p) {
+ prefix_len = PTR_DIFF(dot_p, name);
+ } else {
+ prefix_len = strlen(name);
+ }
+
+ /* the extension of the mangled name is taken from the first 3
+ ascii chars after the dot */
+ extension_length = 0;
+ if (dot_p) {
+ for (i=1; extension_length < 3 && dot_p[i]; i++) {
+ char c = dot_p[i];
+ if (FLAG_CHECK(c, FLAG_ASCII)) {
+ extension[extension_length++] = toupper(c);
+ }
+ }
+ }
+
+ /* find the hash for this prefix */
+ v = hash = mangle_hash(name, prefix_len);
+
+ /* now form the mangled name. */
+ new_name[0] = lead_char;
+ new_name[7] = base_forward(v % 36);
+ new_name[6] = '~';
+ for (i=5; i>=1; i--) {
+ v = v / 36;
+ new_name[i] = base_forward(v % 36);
+ }
+
+ /* add the extension */
+ if (extension_length) {
+ new_name[8] = '.';
+ memcpy(&new_name[9], extension, extension_length);
+ new_name[9+extension_length] = 0;
+ } else {
+ new_name[8] = 0;
+ }
+
+ if (cache83) {
+ /* put it in the cache */
+ cache_insert(name, prefix_len, hash);
+ }
+
+ M_DEBUG(0,("name_map: %s -> %08X -> %s (cache=%d)\n",
+ name, hash, new_name, cache83));
+
+ /* and overwrite the old name */
+ fstrcpy(name, new_name);
+
+ /* all done, we've managed to mangle it */
+ return True;
+}
+
+
+/* initialise the flags table
+
+ we allow only a very restricted set of characters as 'ascii' in this
+ mangling backend. This isn't a significant problem as modern clients
+ use the 'long' filenames anyway, and those don't have these
+ restrictions.
+*/
+static void init_tables(void)
+{
+ int i;
+
+ memset(char_flags, 0, sizeof(char_flags));
+
+ for (i=0;i<128;i++) {
+ if ((i >= '0' && i <= '9') ||
+ (i >= 'a' && i <= 'z') ||
+ (i >= 'A' && i <= 'Z')) {
+ char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
+ }
+ if (strchr("_-$~", i)) {
+ char_flags[i] |= FLAG_ASCII;
+ }
+
+ if (strchr("*\\/?<>|\":", i)) {
+ char_flags[i] |= FLAG_ILLEGAL;
+ }
+
+ if (strchr("*?\"<>", i)) {
+ char_flags[i] |= FLAG_WILDCARD;
+ }
+ }
+
+ memset(base_reverse, 0, sizeof(base_reverse));
+ for (i=0;i<36;i++) {
+ base_reverse[(unsigned char)base_forward(i)] = i;
+ }
+
+ /* fill in the reserved names flags. These are used as a very
+ fast filter for finding possible DOS reserved filenames */
+ for (i=0; reserved_names[i]; i++) {
+ unsigned char c1, c2, c3, c4;
+
+ c1 = (unsigned char)reserved_names[i][0];
+ c2 = (unsigned char)reserved_names[i][1];
+ c3 = (unsigned char)reserved_names[i][2];
+ c4 = (unsigned char)reserved_names[i][3];
+
+ char_flags[c1] |= FLAG_POSSIBLE1;
+ char_flags[c2] |= FLAG_POSSIBLE2;
+ char_flags[c3] |= FLAG_POSSIBLE3;
+ char_flags[c4] |= FLAG_POSSIBLE4;
+ char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
+ char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
+ char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
+ char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
+
+ char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
+ }
+}
+
+
+/*
+ the following provides the abstraction layer to make it easier
+ to drop in an alternative mangling implementation */
+static struct mangle_fns mangle_fns = {
+ is_mangled,
+ is_8_3,
+ mangle_reset,
+ check_cache,
+ name_map
+};
+
+/* return the methods for this mangling implementation */
+struct mangle_fns *mangle_hash2_init(void)
+{
+ init_tables();
+ mangle_reset();
+
+ if (!cache_init()) {
+ return NULL;
+ }
+
+ return &mangle_fns;
+}
diff --git a/source3/smbd/mangle_map.c b/source3/smbd/mangle_map.c
new file mode 100644
index 0000000000..71d9340718
--- /dev/null
+++ b/source3/smbd/mangle_map.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mapping code
+ Copyright (C) Jeremy Allison 1998
+ Copyright (C) Andrew Tridgell 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/* ************************************************************************** **
+ * Used only in do_fwd_mangled_map(), below.
+ * ************************************************************************** **
+ */
+static char *map_filename( char *s, /* This is null terminated */
+ const char *pattern, /* This isn't. */
+ int len ) /* This is the length of pattern. */
+ {
+ static pstring matching_bit; /* The bit of the string which matches */
+ /* a * in pattern if indeed there is a * */
+ char *sp; /* Pointer into s. */
+ char *pp; /* Pointer into p. */
+ char *match_start; /* Where the matching bit starts. */
+ pstring pat;
+
+ StrnCpy( pat, pattern, len ); /* Get pattern into a proper string! */
+ pstrcpy( matching_bit, "" ); /* Match but no star gets this. */
+ pp = pat; /* Initialize the pointers. */
+ sp = s;
+
+ if( strequal(s, ".") || strequal(s, ".."))
+ {
+ return NULL; /* Do not map '.' and '..' */
+ }
+
+ if( (len == 1) && (*pattern == '*') )
+ {
+ return NULL; /* Impossible, too ambiguous for */
+ } /* words! */
+
+ while( (*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) /* The two match. */
+ && (*pp != '*') ) /* No wildcard. */
+ {
+ sp++; /* Keep looking. */
+ pp++;
+ }
+
+ if( !*sp && !*pp ) /* End of pattern. */
+ return( matching_bit ); /* Simple match. Return empty string. */
+
+ if( *pp == '*' )
+ {
+ pp++; /* Always interrested in the chacter */
+ /* after the '*' */
+ if( !*pp ) /* It is at the end of the pattern. */
+ {
+ StrnCpy( matching_bit, s, sp-s );
+ return( matching_bit );
+ }
+ else
+ {
+ /* The next character in pattern must match a character further */
+ /* along s than sp so look for that character. */
+ match_start = sp;
+ while( (*sp) /* Not the end of s. */
+ && (*sp != *pp) ) /* Not the same */
+ sp++; /* Keep looking. */
+ if( !*sp ) /* Got to the end without a match. */
+ {
+ return( NULL );
+ } /* Still hope for a match. */
+ else
+ {
+ /* Now sp should point to a matching character. */
+ StrnCpy(matching_bit, match_start, sp-match_start);
+ /* Back to needing a stright match again. */
+ while( (*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) ) /* The two match. */
+ {
+ sp++; /* Keep looking. */
+ pp++;
+ }
+ if( !*sp && !*pp ) /* Both at end so it matched */
+ return( matching_bit );
+ else
+ return( NULL );
+ }
+ }
+ }
+ return( NULL ); /* No match. */
+ } /* map_filename */
+
+
+/* ************************************************************************** **
+ * MangledMap is a series of name pairs in () separated by spaces.
+ * If s matches the first of the pair then the name given is the
+ * second of the pair. A * means any number of any character and if
+ * present in the second of the pair as well as the first the
+ * matching part of the first string takes the place of the * in the
+ * second.
+ *
+ * I wanted this so that we could have RCS files which can be used
+ * by UNIX and DOS programs. My mapping string is (RCS rcs) which
+ * converts the UNIX RCS file subdirectory to lowercase thus
+ * preventing mangling.
+ *
+ * See 'mangled map' in smb.conf(5).
+ *
+ * ************************************************************************** **
+ */
+static void mangled_map(char *s, const char *MangledMap)
+{
+ char *start=MangledMap; /* Use this to search for mappings. */
+ char *end; /* Used to find the end of strings. */
+ char *match_string;
+ pstring new_string; /* Make up the result here. */
+ char *np; /* Points into new_string. */
+
+ DEBUG( 5, ("Mangled Mapping '%s' map '%s'\n", s, MangledMap) );
+ while( *start ) {
+ while( (*start) && (*start != '(') )
+ start++;
+ if( !*start )
+ continue; /* Always check for the end. */
+ start++; /* Skip the ( */
+ end = start; /* Search for the ' ' or a ')' */
+ DEBUG( 5, ("Start of first in pair '%s'\n", start) );
+ while( (*end) && !((*end == ' ') || (*end == ')')) )
+ end++;
+ if( !*end ) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ DEBUG( 5, ("End of first in pair '%s'\n", end) );
+ if( (match_string = map_filename( s, start, end-start )) ) {
+ DEBUG( 5, ("Found a match\n") );
+ /* Found a match. */
+ start = end + 1; /* Point to start of what it is to become. */
+ DEBUG( 5, ("Start of second in pair '%s'\n", start) );
+ end = start;
+ np = new_string;
+ while( (*end) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*') ) /* Not a wildcard. */
+ *np++ = *end++;
+
+ if( !*end ) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ if( *end == '*' ) {
+ pstrcpy( np, match_string );
+ np += strlen( match_string );
+ end++; /* Skip the '*' */
+ while ((*end) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*'))/* Not a wildcard. */
+ *np++ = *end++;
+ }
+ if (!*end) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ *np++ = '\0'; /* NULL terminate it. */
+ DEBUG(5,("End of second in pair '%s'\n", end));
+ pstrcpy( s, new_string ); /* Substitute with the new name. */
+ DEBUG( 5, ("s is now '%s'\n", s) );
+ }
+ start = end; /* Skip a bit which cannot be wanted anymore. */
+ start++;
+ }
+}
+
+/*
+ front end routine to the mangled map code
+ personally I think that the whole idea of "mangled map" is completely bogus
+*/
+void mangle_map_filename(char *fname, int snum)
+{
+ char *map;
+
+ map = lp_mangled_map(snum);
+ if (!map || !*map) return;
+
+ mangled_map(fname, map);
+}
diff --git a/source3/smbd/message.c b/source3/smbd/message.c
index 6a96b4c7a9..971834c012 100644
--- a/source3/smbd/message.c
+++ b/source3/smbd/message.c
@@ -1,8 +1,7 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
SMB messaging
- Copyright (C) Andrew Tridgell 1992-1995
+ Copyright (C) Andrew Tridgell 1992-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,26 +24,23 @@
#include "includes.h"
-#include "loadparm.h"
-
-/* look in server.c for some explanation of these variables */
-extern int DEBUGLEVEL;
+extern userdom_struct current_user_info;
+/* look in server.c for some explanation of these variables */
static char msgbuf[1600];
-static int msgpos=0;
-static fstring msgfrom="";
-static fstring msgto="";
+static int msgpos;
+static fstring msgfrom;
+static fstring msgto;
/****************************************************************************
deliver the message
****************************************************************************/
static void msg_deliver(void)
{
- pstring s;
- fstring name;
- FILE *f;
+ pstring name;
int i;
+ int fd;
if (! (*lp_msg_command()))
{
@@ -54,34 +50,43 @@ static void msg_deliver(void)
}
/* put it in a temporary file */
- sprintf(s,"/tmp/msg.XXXXXX");
- strcpy(name,(char *)mktemp(s));
+ slprintf(name,sizeof(name)-1, "%s/msg.XXXXXX",tmpdir());
+ fd = smb_mkstemp(name);
- f = fopen(name,"w");
- if (!f)
- {
- DEBUG(1,("can't open message file %s\n",name));
- return;
- }
+ if (fd == -1) {
+ DEBUG(1,("can't open message file %s\n",name));
+ return;
+ }
- for (i=0;i<msgpos;)
- {
- if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n')
- i++;
- fputc(msgbuf[i++],f);
- }
+ /*
+ * Incoming message is in DOS codepage format. Convert to UNIX.
+ */
- fclose(f);
+ if(msgpos > 0) {
+ msgbuf[msgpos] = '\0'; /* Ensure null terminated. */
+ }
+
+ for (i=0;i<msgpos;) {
+ if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n') {
+ i++; continue;
+ }
+ write(fd,&msgbuf[i++],1);
+ }
+ close(fd);
/* run the command */
if (*lp_msg_command())
{
- strcpy(s,lp_msg_command());
- string_sub(s,"%s",name);
- string_sub(s,"%f",msgfrom);
- string_sub(s,"%t",msgto);
- standard_sub(-1,s);
+ fstring alpha_msgfrom;
+ fstring alpha_msgto;
+ pstring s;
+
+ pstrcpy(s,lp_msg_command());
+ pstring_sub(s,"%f",alpha_strcpy(alpha_msgfrom,msgfrom,NULL,sizeof(alpha_msgfrom)));
+ pstring_sub(s,"%t",alpha_strcpy(alpha_msgto,msgto,NULL,sizeof(alpha_msgto)));
+ standard_sub_basic(current_user_info.smb_name, s);
+ pstring_sub(s,"%s",name);
smbrun(s,NULL);
}
@@ -93,37 +98,42 @@ static void msg_deliver(void)
/****************************************************************************
reply to a sends
****************************************************************************/
-int reply_sends(char *inbuf,char *outbuf)
+int reply_sends(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
int len;
- char *orig,*dest,*msg;
+ char *msg;
int outsize = 0;
+ char *p;
- msgpos = 0;
+ START_PROFILE(SMBsends);
+ msgpos = 0;
- if (! (*lp_msg_command()))
- return(ERROR(ERRSRV,ERRmsgoff));
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsends);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
outsize = set_message(outbuf,0,0,True);
- orig = smb_buf(inbuf)+1;
- dest = skip_string(orig,1)+1;
- msg = skip_string(dest,1)+1;
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull(inbuf, msgfrom, p, sizeof(msgfrom), -1, STR_TERMINATE) + 1;
+ p += srvstr_pull(inbuf, msgto, p, sizeof(msgto), -1, STR_TERMINATE) + 1;
- strcpy(msgfrom,orig);
- strcpy(msgto,dest);
+ msg = p;
len = SVAL(msg,0);
- len = MIN(len,1600-msgpos);
+ len = MIN(len,sizeof(msgbuf)-msgpos);
+
+ memset(msgbuf,'\0',sizeof(msgbuf));
memcpy(&msgbuf[msgpos],msg+2,len);
msgpos += len;
- DEBUG(3,("%s SMBsends (from %s to %s)\n",timestring(),orig,dest));
-
msg_deliver();
+ END_PROFILE(SMBsends);
return(outsize);
}
@@ -131,26 +141,31 @@ int reply_sends(char *inbuf,char *outbuf)
/****************************************************************************
reply to a sendstrt
****************************************************************************/
-int reply_sendstrt(char *inbuf,char *outbuf)
+int reply_sendstrt(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- char *orig,*dest;
int outsize = 0;
+ char *p;
- if (! (*lp_msg_command()))
- return(ERROR(ERRSRV,ERRmsgoff));
+ START_PROFILE(SMBsendstrt);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendstrt);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
outsize = set_message(outbuf,1,0,True);
+ memset(msgbuf,'\0',sizeof(msgbuf));
msgpos = 0;
- orig = smb_buf(inbuf)+1;
- dest = skip_string(orig,1)+1;
-
- strcpy(msgfrom,orig);
- strcpy(msgto,dest);
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull(inbuf, msgfrom, p, sizeof(msgfrom), -1, STR_TERMINATE) + 1;
+ p += srvstr_pull(inbuf, msgto, p, sizeof(msgto), -1, STR_TERMINATE) + 1;
- DEBUG(3,("%s SMBsendstrt (from %s to %s)\n",timestring(),orig,dest));
+ DEBUG( 3, ( "SMBsendstrt (from %s to %s)\n", msgfrom, msgto ) );
+ END_PROFILE(SMBsendstrt);
return(outsize);
}
@@ -158,27 +173,32 @@ int reply_sendstrt(char *inbuf,char *outbuf)
/****************************************************************************
reply to a sendtxt
****************************************************************************/
-int reply_sendtxt(char *inbuf,char *outbuf)
+int reply_sendtxt(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
int len;
int outsize = 0;
char *msg;
+ START_PROFILE(SMBsendtxt);
- if (! (*lp_msg_command()))
- return(ERROR(ERRSRV,ERRmsgoff));
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendtxt);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
outsize = set_message(outbuf,0,0,True);
msg = smb_buf(inbuf) + 1;
len = SVAL(msg,0);
- len = MIN(len,1600-msgpos);
+ len = MIN(len,sizeof(msgbuf)-msgpos);
memcpy(&msgbuf[msgpos],msg+2,len);
msgpos += len;
- DEBUG(3,("%s SMBsendtxt\n",timestring()));
+ DEBUG( 3, ( "SMBsendtxt\n" ) );
+ END_PROFILE(SMBsendtxt);
return(outsize);
}
@@ -186,19 +206,23 @@ int reply_sendtxt(char *inbuf,char *outbuf)
/****************************************************************************
reply to a sendend
****************************************************************************/
-int reply_sendend(char *inbuf,char *outbuf)
+int reply_sendend(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
int outsize = 0;
+ START_PROFILE(SMBsendend);
- if (! (*lp_msg_command()))
- return(ERROR(ERRSRV,ERRmsgoff));
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendend);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
outsize = set_message(outbuf,0,0,True);
- DEBUG(3,("%s SMBsendend\n",timestring()));
+ DEBUG(3,("SMBsendend\n"));
msg_deliver();
+ END_PROFILE(SMBsendend);
return(outsize);
}
-
diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c
new file mode 100644
index 0000000000..18682e6c9f
--- /dev/null
+++ b/source3/smbd/negprot.c
@@ -0,0 +1,509 @@
+/*
+ Unix SMB/CIFS implementation.
+ negprot reply code
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern int Protocol;
+extern int max_recv;
+extern fstring global_myworkgroup;
+extern fstring remote_machine;
+BOOL global_encrypted_passwords_negotiated = False;
+BOOL global_spnego_negotiated = False;
+struct auth_context *negprot_global_auth_context = NULL;
+
+static void get_challenge(char buff[8])
+{
+ NTSTATUS nt_status;
+ const uint8 *cryptkey;
+
+ /* We might be called more than once, muliple negprots are premitted */
+ if (negprot_global_auth_context) {
+ DEBUG(3, ("get challenge: is this a secondary negprot? negprot_global_auth_context is non-NULL!\n"));
+ (negprot_global_auth_context->free)(&negprot_global_auth_context);
+ }
+
+ DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&negprot_global_auth_context))) {
+ DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status)));
+ smb_panic("cannot make_negprot_global_auth_context!\n");
+ }
+ DEBUG(10, ("get challenge: getting challenge\n"));
+ cryptkey = negprot_global_auth_context->get_ntlm_challenge(negprot_global_auth_context);
+ memcpy(buff, cryptkey, 8);
+}
+
+/****************************************************************************
+reply for the core protocol
+****************************************************************************/
+static int reply_corep(char *inbuf, char *outbuf)
+{
+ int outsize = set_message(outbuf,1,0,True);
+
+ Protocol = PROTOCOL_CORE;
+
+ return outsize;
+}
+
+
+/****************************************************************************
+reply for the coreplus protocol
+****************************************************************************/
+static int reply_coreplus(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int outsize = set_message(outbuf,13,0,True);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw and writebraw (possibly) */
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */
+
+ Protocol = PROTOCOL_COREPLUS;
+
+ return outsize;
+}
+
+
+/****************************************************************************
+reply for the lanman 1.0 protocol
+****************************************************************************/
+static int reply_lanman1(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ time_t t = time(NULL);
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ if (lp_security()>=SEC_USER) secword |= 1;
+ if (global_encrypted_passwords_negotiated) secword |= 2;
+
+ set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(smb_buf(outbuf));
+ }
+
+ Protocol = PROTOCOL_LANMAN1;
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv2,max_recv);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw writebraw (possibly) */
+ SIVAL(outbuf,smb_vwv6,sys_getpid());
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+
+/****************************************************************************
+reply for the lanman 2.0 protocol
+****************************************************************************/
+static int reply_lanman2(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ time_t t = time(NULL);
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ if (lp_security()>=SEC_USER) secword |= 1;
+ if (global_encrypted_passwords_negotiated) secword |= 2;
+
+ set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+ SIVAL(outbuf,smb_vwv6,sys_getpid());
+
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(smb_buf(outbuf));
+ }
+
+ Protocol = PROTOCOL_LANMAN2;
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv2,max_recv);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux());
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+
+
+/*
+ generate the spnego negprot reply blob. Return the number of bytes used
+*/
+static int negprot_spnego(char *p)
+{
+ DATA_BLOB blob;
+ extern pstring global_myname;
+ uint8 guid[16];
+ const char *OIDs_krb5[] = {OID_NTLMSSP,
+ OID_KERBEROS5,
+ OID_KERBEROS5_OLD,
+ NULL};
+ const char *OIDs_plain[] = {OID_NTLMSSP, NULL};
+ char *principal;
+ int len;
+
+ global_spnego_negotiated = True;
+
+ memset(guid, 0, 16);
+ safe_strcpy((char *)guid, global_myname, 16);
+ strlower((char *)guid);
+
+#if 0
+ /* strangely enough, NT does not sent the single OID NTLMSSP when
+ not a ADS member, it sends no OIDs at all
+
+ we can't do this until we teach our sesssion setup parser to know
+ about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)
+ */
+ if (lp_security() != SEC_ADS) {
+ memcpy(p, guid, 16);
+ return 16;
+ }
+#endif
+ if (lp_security() != SEC_ADS) {
+ blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE");
+ } else {
+ ADS_STRUCT *ads;
+ ads = ads_init(NULL, NULL, NULL, NULL);
+ /* win2000 uses host$@REALM, which we will probably use eventually,
+ but for now this works */
+ asprintf(&principal, "HOST/%s@%s", guid, ads->realm);
+ blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal);
+ free(principal);
+ ads_destroy(&ads);
+ }
+ memcpy(p, blob.data, blob.length);
+ len = blob.length;
+ data_blob_free(&blob);
+ return len;
+}
+
+
+
+/****************************************************************************
+reply for the nt protocol
+****************************************************************************/
+static int reply_nt1(char *inbuf, char *outbuf)
+{
+ /* dual names + lock_and_read + nt SMBs + remote API calls */
+ int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ|
+ CAP_LEVEL_II_OPLOCKS;
+
+ int secword=0;
+ time_t t = time(NULL);
+ char *p, *q;
+ BOOL negotiate_spnego = False;
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ /* do spnego in user level security if the client
+ supports it and we can do encrypted passwords */
+
+ if (global_encrypted_passwords_negotiated &&
+ (lp_security() != SEC_SHARE) &&
+ lp_use_spnego() &&
+ (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+ negotiate_spnego = True;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS|CAP_UNIX;
+
+ if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64))
+ capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS;
+
+ if (SMB_OFF_T_BITS == 64)
+ capabilities |= CAP_LARGE_FILES;
+
+ if (lp_readraw() && lp_writeraw())
+ capabilities |= CAP_RAW_MODE;
+
+ /* allow for disabling unicode */
+ if (lp_unicode())
+ capabilities |= CAP_UNICODE;
+
+ if (lp_nt_status_support())
+ capabilities |= CAP_STATUS32;
+
+ if (lp_host_msdfs())
+ capabilities |= CAP_DFS;
+
+ if (lp_security() >= SEC_USER)
+ secword |= 1;
+ if (global_encrypted_passwords_negotiated)
+ secword |= 2;
+
+ set_message(outbuf,17,0,True);
+
+ SCVAL(outbuf,smb_vwv1,secword);
+
+ Protocol = PROTOCOL_NT1;
+
+ SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
+ SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
+ SIVAL(outbuf,smb_vwv3+1,0xffff); /* max buffer. LOTS! */
+ SIVAL(outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */
+ SIVAL(outbuf,smb_vwv7+1,sys_getpid()); /* session key */
+ SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
+ put_long_date(outbuf+smb_vwv11+1,t);
+ SSVALS(outbuf,smb_vwv15+1,TimeDiff(t)/60);
+
+ p = q = smb_buf(outbuf);
+ if (!negotiate_spnego) {
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(p);
+ }
+ SSVALS(outbuf,smb_vwv16+1,8);
+ p += 8;
+ p += srvstr_push(outbuf, p, global_myworkgroup, -1,
+ STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ DEBUG(3,("not using SPNEGO\n"));
+ } else {
+ int len = negprot_spnego(p);
+
+ SSVALS(outbuf,smb_vwv16+1,len);
+ p += len;
+ DEBUG(3,("using SPNEGO\n"));
+ }
+
+ SSVAL(outbuf,smb_vwv17, p - q); /* length of challenge+domain strings */
+ set_message_end(outbuf, p);
+
+ return (smb_len(outbuf)+4);
+}
+
+/* these are the protocol lists used for auto architecture detection:
+
+WinNT 3.51:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win95:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win2K:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+OS/2:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [LANMAN1.0]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+*/
+
+/*
+ * Modified to recognize the architecture of the remote machine better.
+ *
+ * This appears to be the matrix of which protocol is used by which
+ * MS product.
+ Protocol WfWg Win95 WinNT Win2K OS/2
+ PC NETWORK PROGRAM 1.0 1 1 1 1 1
+ XENIX CORE 2 2
+ MICROSOFT NETWORKS 3.0 2 2
+ DOS LM1.2X002 3 3
+ MICROSOFT NETWORKS 1.03 3
+ DOS LANMAN2.1 4 4
+ LANMAN1.0 4 2 3
+ Windows for Workgroups 3.1a 5 5 5 3
+ LM1.2X002 6 4 4
+ LANMAN2.1 7 5 5
+ NT LM 0.12 6 8 6
+ *
+ * tim@fsg.com 09/29/95
+ * Win2K added by matty 17/7/99
+ */
+
+#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */
+#define ARCH_WIN95 0x2
+#define ARCH_WINNT 0x4
+#define ARCH_WIN2K 0xC /* Win2K is like NT */
+#define ARCH_OS2 0x14 /* Again OS/2 is like NT */
+#define ARCH_SAMBA 0x20
+
+#define ARCH_ALL 0x3F
+
+/* List of supported protocols, most desired first */
+static struct {
+ char *proto_name;
+ char *short_name;
+ int (*proto_reply_fn)(char *, char *);
+ int protocol_level;
+} supported_protocols[] = {
+ {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
+ {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
+ {NULL,NULL,NULL,0},
+};
+
+
+/****************************************************************************
+ reply to a negprot
+****************************************************************************/
+int reply_negprot(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = set_message(outbuf,1,0,True);
+ int Index=0;
+ int choice= -1;
+ int protocol;
+ char *p;
+ int bcc = SVAL(smb_buf(inbuf),-2);
+ int arch = ARCH_ALL;
+ START_PROFILE(SMBnegprot);
+
+ p = smb_buf(inbuf)+1;
+ while (p < (smb_buf(inbuf) + bcc))
+ {
+ Index++;
+ DEBUG(3,("Requested protocol [%s]\n",p));
+ if (strcsequal(p,"Windows for Workgroups 3.1a"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ else if (strcsequal(p,"DOS LM1.2X002"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"DOS LANMAN2.1"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"NT LM 0.12"))
+ arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ else if (strcsequal(p,"LANMAN2.1"))
+ arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+ else if (strcsequal(p,"LM1.2X002"))
+ arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+ else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
+ arch &= ARCH_WINNT;
+ else if (strcsequal(p,"XENIX CORE"))
+ arch &= ( ARCH_WINNT | ARCH_OS2 );
+ else if (strcsequal(p,"Samba")) {
+ arch = ARCH_SAMBA;
+ break;
+ }
+
+ p += strlen(p) + 2;
+ }
+
+ switch ( arch ) {
+ case ARCH_SAMBA:
+ set_remote_arch(RA_SAMBA);
+ break;
+ case ARCH_WFWG:
+ set_remote_arch(RA_WFWG);
+ break;
+ case ARCH_WIN95:
+ set_remote_arch(RA_WIN95);
+ break;
+ case ARCH_WINNT:
+ if(SVAL(inbuf,smb_flg2)==FLAGS2_WIN2K_SIGNATURE)
+ set_remote_arch(RA_WIN2K);
+ else
+ set_remote_arch(RA_WINNT);
+ break;
+ case ARCH_WIN2K:
+ set_remote_arch(RA_WIN2K);
+ break;
+ case ARCH_OS2:
+ set_remote_arch(RA_OS2);
+ break;
+ default:
+ set_remote_arch(RA_UNKNOWN);
+ break;
+ }
+
+ /* possibly reload - change of architecture */
+ reload_services(True);
+
+ /* Check for protocols, most desirable first */
+ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++)
+ {
+ p = smb_buf(inbuf)+1;
+ Index = 0;
+ if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) &&
+ (supported_protocols[protocol].protocol_level >= lp_minprotocol()))
+ while (p < (smb_buf(inbuf) + bcc))
+ {
+ if (strequal(p,supported_protocols[protocol].proto_name))
+ choice = Index;
+ Index++;
+ p += strlen(p) + 2;
+ }
+ if(choice != -1)
+ break;
+ }
+
+ SSVAL(outbuf,smb_vwv0,choice);
+ if(choice != -1) {
+ extern fstring remote_proto;
+ fstrcpy(remote_proto,supported_protocols[protocol].short_name);
+ reload_services(True);
+ outsize = supported_protocols[protocol].proto_reply_fn(inbuf, outbuf);
+ DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
+ }
+ else {
+ DEBUG(0,("No protocol supported !\n"));
+ }
+ SSVAL(outbuf,smb_vwv0,choice);
+
+ DEBUG( 5, ( "negprot index=%d\n", choice ) );
+
+ END_PROFILE(SMBnegprot);
+ return(outsize);
+}
+
diff --git a/source3/smbd/noquotas.c b/source3/smbd/noquotas.c
new file mode 100644
index 0000000000..85caef57e1
--- /dev/null
+++ b/source3/smbd/noquotas.c
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ No support for quotas :-).
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ * Needed for auto generation of proto.h.
+ */
+
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ (*bsize) = 512; /* This value should be ignored */
+
+ /* And just to be sure we set some values that hopefully */
+ /* will be larger that any possible real-world value */
+ (*dfree) = (SMB_BIG_UINT)-1;
+ (*dsize) = (SMB_BIG_UINT)-1;
+
+ /* As we have select not to use quotas, allways fail */
+ return False;
+}
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
new file mode 100644
index 0000000000..0895ddaf87
--- /dev/null
+++ b/source3/smbd/notify.c
@@ -0,0 +1,220 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+static struct cnotify_fns *cnotify;
+
+/****************************************************************************
+ This is the structure to queue to implement NT change
+ notify. It consists of smb_size bytes stored from the
+ transact command (to keep the mid, tid etc around).
+ Plus the fid to examine and notify private data.
+*****************************************************************************/
+
+struct change_notify {
+ struct change_notify *next, *prev;
+ files_struct *fsp;
+ connection_struct *conn;
+ uint32 flags;
+ char request_buf[smb_size];
+ void *change_data;
+};
+
+static struct change_notify *change_notify_list;
+
+/****************************************************************************
+ Setup the common parts of the return packet and send it.
+*****************************************************************************/
+static void change_notify_reply_packet(char *inbuf, NTSTATUS error_code)
+{
+ char outbuf[smb_size+38];
+
+ memset(outbuf, '\0', sizeof(outbuf));
+ construct_reply_common(inbuf, outbuf);
+
+ ERROR_NT(error_code);
+
+ /*
+ * Seems NT needs a transact command with an error code
+ * in it. This is a longer packet than a simple error.
+ */
+ set_message(outbuf,18,0,False);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("change_notify_reply_packet: send_smb failed.");
+}
+
+/****************************************************************************
+ Remove an entry from the list and free it, also closing any
+ directory handle if necessary.
+*****************************************************************************/
+
+static void change_notify_remove(struct change_notify *cnbp)
+{
+ cnotify->remove_notify(cnbp->change_data);
+ DLIST_REMOVE(change_notify_list, cnbp);
+ ZERO_STRUCTP(cnbp);
+ SAFE_FREE(cnbp);
+}
+
+/****************************************************************************
+ Delete entries by fnum from the change notify pending queue.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if (cnbp->fsp->fnum == fsp->fnum) {
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Delete entries by mid from the change notify pending queue. Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_mid(int mid)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if(SVAL(cnbp->request_buf,smb_mid) == mid) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Delete entries by filename and cnum from the change notify pending queue.
+ Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_filename(files_struct *fsp)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ /*
+ * We know it refers to the same directory if the connection number and
+ * the filename are identical.
+ */
+ if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Return true if there are pending change notifies.
+****************************************************************************/
+
+int change_notify_timeout(void)
+{
+ return cnotify->select_time;
+}
+
+/****************************************************************************
+ Process the change notify queue. Note that this is only called as root.
+ Returns True if there are still outstanding change notify requests on the
+ queue.
+*****************************************************************************/
+
+BOOL process_pending_change_notify_queue(time_t t)
+{
+ struct change_notify *cnbp, *next;
+ uint16 vuid;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+
+ vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid);
+
+ if (cnotify->check_notify(cnbp->conn, vuid, cnbp->fsp->fsp_name, cnbp->flags, cnbp->change_data, t)) {
+ DEBUG(10,("process_pending_change_notify_queue: dir %s changed !\n", cnbp->fsp->fsp_name ));
+ change_notify_reply_packet(cnbp->request_buf,STATUS_NOTIFY_ENUM_DIR);
+ change_notify_remove(cnbp);
+ }
+ }
+
+ return (change_notify_list != NULL);
+}
+
+/****************************************************************************
+ Now queue an entry on the notify change list.
+ We only need to save smb_size bytes from this incoming packet
+ as we will always by returning a 'read the directory yourself'
+ error.
+****************************************************************************/
+
+BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags)
+{
+ struct change_notify *cnbp;
+
+ if((cnbp = (struct change_notify *)malloc(sizeof(*cnbp))) == NULL) {
+ DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" ));
+ return -1;
+ }
+
+ ZERO_STRUCTP(cnbp);
+
+ memcpy(cnbp->request_buf, inbuf, smb_size);
+ cnbp->fsp = fsp;
+ cnbp->conn = conn;
+ cnbp->flags = flags;
+ cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, flags);
+
+ if (!cnbp->change_data) {
+ SAFE_FREE(cnbp);
+ return False;
+ }
+
+ DLIST_ADD(change_notify_list, cnbp);
+
+ return True;
+}
+
+/****************************************************************************
+ Initialise the change notify subsystem.
+****************************************************************************/
+
+BOOL init_change_notify(void)
+{
+#if HAVE_KERNEL_CHANGE_NOTIFY
+ cnotify = kernel_notify_init();
+#endif
+ if (!cnotify) cnotify = hash_notify_init();
+
+ if (!cnotify) {
+ DEBUG(0,("Failed to init change notify system\n"));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c
new file mode 100644
index 0000000000..d8b35462ac
--- /dev/null
+++ b/source3/smbd/notify_hash.c
@@ -0,0 +1,225 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling - hash based implementation
+ Copyright (C) Jeremy Allison 1994-1998
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct change_data {
+ time_t last_check_time; /* time we last checked this entry */
+ time_t modify_time; /* Info from the directory we're monitoring. */
+ time_t status_time; /* Info from the directory we're monitoring. */
+ time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
+ unsigned int num_entries; /* Zero or the number of files in the directory. */
+ unsigned int mode_sum;
+ unsigned char name_hash[16];
+};
+
+/****************************************************************************
+ Create the hash we will use to determine if the contents changed.
+*****************************************************************************/
+
+static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
+ struct change_data *data, struct change_data *old_data)
+{
+ SMB_STRUCT_STAT st;
+ pstring full_name;
+ char *p;
+ char *fname;
+ size_t remaining_len;
+ size_t fullname_len;
+ void *dp;
+
+ ZERO_STRUCTP(data);
+
+ if(vfs_stat(conn,path, &st) == -1)
+ return False;
+
+ data->modify_time = st.st_mtime;
+ data->status_time = st.st_ctime;
+
+ if (old_data) {
+ /*
+ * Shortcut to avoid directory scan if the time
+ * has changed - we always must return true then.
+ */
+ if (old_data->modify_time != data->modify_time ||
+ old_data->status_time != data->status_time ) {
+ return True;
+ }
+ }
+
+ /*
+ * If we are to watch for changes that are only stored
+ * in inodes of files, not in the directory inode, we must
+ * scan the directory and produce a unique identifier with
+ * which we can determine if anything changed. We use the
+ * modify and change times from all the files in the
+ * directory, added together (ignoring wrapping if it's
+ * larger than the max time_t value).
+ */
+
+ dp = OpenDir(conn, path, True);
+ if (dp == NULL)
+ return False;
+
+ data->num_entries = 0;
+
+ pstrcpy(full_name, path);
+ pstrcat(full_name, "/");
+
+ fullname_len = strlen(full_name);
+ remaining_len = sizeof(full_name) - fullname_len - 1;
+ p = &full_name[fullname_len];
+
+ while ((fname = ReadDirName(dp))) {
+ if(strequal(fname, ".") || strequal(fname, ".."))
+ continue;
+
+ data->num_entries++;
+ safe_strcpy(p, fname, remaining_len);
+
+ ZERO_STRUCT(st);
+
+ /*
+ * Do the stat - but ignore errors.
+ */
+ vfs_stat(conn,full_name, &st);
+
+ /*
+ * Always sum the times.
+ */
+
+ data->total_time += (st.st_mtime + st.st_ctime);
+
+ /*
+ * If requested hash the names.
+ */
+
+ if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
+ int i;
+ unsigned char tmp_hash[16];
+ mdfour(tmp_hash, (unsigned char *)fname, strlen(fname));
+ for (i=0;i<16;i++)
+ data->name_hash[i] ^= tmp_hash[i];
+ }
+
+ /*
+ * If requested sum the mode_t's.
+ */
+
+ if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
+ data->mode_sum = st.st_mode;
+ }
+
+ CloseDir(dp);
+
+ return True;
+}
+
+/****************************************************************************
+ Register a change notify request.
+*****************************************************************************/
+
+static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+
+ if (!notify_hash(conn, path, flags, &data, NULL))
+ return NULL;
+
+ data.last_check_time = time(NULL);
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+/****************************************************************************
+ Check if a change notify should be issued.
+ A time of zero means instantaneous check - don't modify the last check time.
+*****************************************************************************/
+
+static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+ struct change_data data2;
+
+ if (t && t < data->last_check_time + lp_change_notify_timeout())
+ return False;
+
+ if (!change_to_user(conn,vuid))
+ return True;
+ if (!set_current_service(conn,True)) {
+ change_to_root_user();
+ return True;
+ }
+
+ if (!notify_hash(conn, path, flags, &data2, data) ||
+ data2.modify_time != data->modify_time ||
+ data2.status_time != data->status_time ||
+ data2.total_time != data->total_time ||
+ data2.num_entries != data->num_entries ||
+ data2.mode_sum != data->mode_sum ||
+ memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
+ change_to_root_user();
+ return True;
+ }
+
+ if (t)
+ data->last_check_time = t;
+
+ change_to_root_user();
+
+ return False;
+}
+
+/****************************************************************************
+ Remove a change notify data structure.
+*****************************************************************************/
+
+static void hash_remove_notify(void *datap)
+{
+ free(datap);
+}
+
+/****************************************************************************
+ Setup hash based change notify.
+****************************************************************************/
+
+struct cnotify_fns *hash_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+
+ cnotify.register_notify = hash_register_notify;
+ cnotify.check_notify = hash_check_notify;
+ cnotify.remove_notify = hash_remove_notify;
+ cnotify.select_time = lp_change_notify_timeout();
+
+ return &cnotify;
+}
+
+/*
+ change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
+ change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
+
+ chain_size = 0;
+ file_chain_reset();
+
+ uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
+ SVAL(cnbp->request_buf,smb_uid);
+*/
diff --git a/source3/smbd/notify_kernel.c b/source3/smbd/notify_kernel.c
new file mode 100644
index 0000000000..19ea41e195
--- /dev/null
+++ b/source3/smbd/notify_kernel.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling - linux kernel based implementation
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_CHANGE_NOTIFY
+
+static VOLATILE sig_atomic_t fd_pending;
+static VOLATILE sig_atomic_t signals_received;
+static VOLATILE sig_atomic_t signals_processed;
+
+#ifndef DN_ACCESS
+#define DN_ACCESS 0x00000001 /* File accessed in directory */
+#define DN_MODIFY 0x00000002 /* File modified in directory */
+#define DN_CREATE 0x00000004 /* File created in directory */
+#define DN_DELETE 0x00000008 /* File removed from directory */
+#define DN_RENAME 0x00000010 /* File renamed in directory */
+#define DN_ATTRIB 0x00000020 /* File changed attribute */
+#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
+#endif
+
+
+#ifndef RT_SIGNAL_NOTIFY
+#define RT_SIGNAL_NOTIFY 34
+#endif
+
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
+#ifndef F_NOTIFY
+#define F_NOTIFY 1026
+#endif
+
+/****************************************************************************
+ This is the structure to keep the information needed to
+ determine if a directory has changed.
+*****************************************************************************/
+struct change_data {
+ int directory_handle;
+};
+
+/****************************************************************************
+the signal handler for change notify
+*****************************************************************************/
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ BlockSignals(True, sig);
+ fd_pending = (sig_atomic_t)info->si_fd;
+ signals_received++;
+ sys_select_signal();
+}
+
+
+
+/****************************************************************************
+ Check if a change notify should be issued.
+ time non-zero means timeout check (used for hash). Ignore this (async method
+ where time is zero will be used instead).
+*****************************************************************************/
+static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+
+ if (t)
+ return False;
+
+ if (data->directory_handle != (int)fd_pending) return False;
+
+ DEBUG(3,("kernel change notify on %s fd=%d\n", path, (int)fd_pending));
+
+ close((int)fd_pending);
+ fd_pending = (sig_atomic_t)-1;
+ data->directory_handle = -1;
+ signals_processed++;
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ return True;
+}
+
+/****************************************************************************
+remove a change notify data structure
+*****************************************************************************/
+static void kernel_remove_notify(void *datap)
+{
+ struct change_data *data = (struct change_data *)datap;
+ int fd = data->directory_handle;
+ if (fd != -1) {
+ if (fd == (int)fd_pending) {
+ fd_pending = (sig_atomic_t)-1;
+ signals_processed++;
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ }
+ close(fd);
+ }
+ SAFE_FREE(data);
+ DEBUG(3,("removed kernel change notify fd=%d\n", fd));
+}
+
+
+/****************************************************************************
+register a change notify request
+*****************************************************************************/
+static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+ int fd;
+ unsigned long kernel_flags;
+
+ fd = conn->vfs_ops.open(conn, path, O_RDONLY, 0);
+
+ if (fd == -1) {
+ DEBUG(3,("Failed to open directory %s for change notify\n", path));
+ return NULL;
+ }
+
+ if (fcntl(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
+ DEBUG(3,("Failed to set signal handler for change notify\n"));
+ return NULL;
+ }
+
+ kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion changes everything! */
+ if (flags & FILE_NOTIFY_CHANGE_FILE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+ if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS;
+ if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE;
+ if (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+
+ if (fcntl(fd, F_NOTIFY, kernel_flags) == -1) {
+ DEBUG(3,("Failed to set async flag for change notify\n"));
+ return NULL;
+ }
+
+ data.directory_handle = fd;
+
+ DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) fd=%d\n",
+ path, (int)flags, (int)kernel_flags, fd));
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+/****************************************************************************
+see if the kernel supports change notify
+****************************************************************************/
+static BOOL kernel_notify_available(void)
+{
+ int fd, ret;
+ fd = open("/tmp", O_RDONLY);
+ if (fd == -1) return False; /* uggh! */
+ ret = fcntl(fd, F_NOTIFY, 0);
+ close(fd);
+ return ret == 0;
+}
+
+
+/****************************************************************************
+setup kernel based change notify
+****************************************************************************/
+struct cnotify_fns *kernel_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+ struct sigaction act;
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
+ return NULL;
+ }
+
+ if (!kernel_notify_available()) return NULL;
+
+ cnotify.register_notify = kernel_register_notify;
+ cnotify.check_notify = kernel_check_notify;
+ cnotify.remove_notify = kernel_remove_notify;
+ cnotify.select_time = -1;
+
+ return &cnotify;
+}
+
+
+#else
+ void notify_kernel_dummy(void) {}
+#endif /* HAVE_KERNEL_CHANGE_NOTIFY */
diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
new file mode 100644
index 0000000000..ed2979b3a4
--- /dev/null
+++ b/source3/smbd/nttrans.c
@@ -0,0 +1,1818 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT transaction handling
+ Copyright (C) Jeremy Allison 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern int Protocol;
+extern int smb_read_error;
+extern int global_oplock_break;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+
+static char *known_nt_pipes[] = {
+ "\\LANMAN",
+ "\\srvsvc",
+ "\\samr",
+ "\\wkssvc",
+ "\\NETLOGON",
+ "\\ntlsa",
+ "\\ntsvcs",
+ "\\lsass",
+ "\\lsarpc",
+ "\\winreg",
+ "\\spoolss",
+ "\\netdfs",
+ NULL
+};
+
+/* Map generic permissions to file object specific permissions */
+
+struct generic_mapping file_generic_mapping = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_GENERIC_ALL
+};
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+
+static int send_nt_replies(char *inbuf, char *outbuf, int bufsize, NTSTATUS nt_error, char *params,
+ int paramsize, char *pdata, int datasize)
+{
+ extern int max_send;
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ char *pp = params;
+ char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 3;
+ int data_alignment_offset = 0;
+
+ /*
+ * Initially set the wcnt area to be 18 - this is true for all
+ * transNT replies.
+ */
+
+ set_message(outbuf,18,0,True);
+
+ if (NT_STATUS_V(nt_error)) {
+ ERROR_NT(nt_error);
+ }
+
+ /*
+ * If there genuinely are no parameters or data to send just send
+ * the empty packet.
+ */
+
+ if(params_to_send == 0 && data_to_send == 0) {
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_nt_replies: send_smb failed.");
+ return 0;
+ }
+
+ /*
+ * When sending params and data ensure that both are nicely aligned.
+ * Only do this alignment when there is also data to send - else
+ * can cause NT redirector problems.
+ */
+
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
+
+ /*
+ * Space is bufsize minus Netbios over TCP header minus SMB header.
+ * The alignment_offset is to align the param bytes on a four byte
+ * boundary (2 bytes for data len, one byte pad).
+ * NT needs this to work correctly.
+ */
+
+ useable_space = bufsize - ((smb_buf(outbuf)+
+ alignment_offset+data_alignment_offset) -
+ outbuf);
+
+ /*
+ * useable_space can never be more than max_send minus the
+ * alignment offset.
+ */
+
+ useable_space = MIN(useable_space,
+ max_send - (alignment_offset+data_alignment_offset));
+
+
+ while (params_to_send || data_to_send) {
+
+ /*
+ * Calculate whether we will totally or partially fill this packet.
+ */
+
+ total_sent_thistime = params_to_send + data_to_send +
+ alignment_offset + data_alignment_offset;
+
+ /*
+ * We can never send more than useable_space.
+ */
+
+ total_sent_thistime = MIN(total_sent_thistime, useable_space);
+
+ set_message(outbuf, 18, total_sent_thistime, True);
+
+ /*
+ * Set total params and data to be sent.
+ */
+
+ SIVAL(outbuf,smb_ntr_TotalParameterCount,paramsize);
+ SIVAL(outbuf,smb_ntr_TotalDataCount,datasize);
+
+ /*
+ * Calculate how many parameters and data we can fit into
+ * this packet. Parameters get precedence.
+ */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SIVAL(outbuf,smb_ntr_ParameterCount,params_sent_thistime);
+
+ if(params_sent_thistime == 0) {
+ SIVAL(outbuf,smb_ntr_ParameterOffset,0);
+ SIVAL(outbuf,smb_ntr_ParameterDisplacement,0);
+ } else {
+ /*
+ * smb_ntr_ParameterOffset is the offset from the start of the SMB header to the
+ * parameter bytes, however the first 4 bytes of outbuf are
+ * the Netbios over TCP header. Thus use smb_base() to subtract
+ * them from the calculation.
+ */
+
+ SIVAL(outbuf,smb_ntr_ParameterOffset,
+ ((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+ /*
+ * Absolute displacement of param bytes sent in this packet.
+ */
+
+ SIVAL(outbuf,smb_ntr_ParameterDisplacement,pp - params);
+ }
+
+ /*
+ * Deal with the data portion.
+ */
+
+ SIVAL(outbuf,smb_ntr_DataCount, data_sent_thistime);
+
+ if(data_sent_thistime == 0) {
+ SIVAL(outbuf,smb_ntr_DataOffset,0);
+ SIVAL(outbuf,smb_ntr_DataDisplacement, 0);
+ } else {
+ /*
+ * The offset of the data bytes is the offset of the
+ * parameter bytes plus the number of parameters being sent this time.
+ */
+
+ SIVAL(outbuf,smb_ntr_DataOffset,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime + data_alignment_offset);
+ SIVAL(outbuf,smb_ntr_DataDisplacement, pd - pdata);
+ }
+
+ /*
+ * Copy the param bytes into the packet.
+ */
+
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+
+ /*
+ * Copy in the data bytes
+ */
+
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+
+ data_alignment_offset,pd,data_sent_thistime);
+
+ DEBUG(9,("nt_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("nt_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ /* Send the packet */
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_nt_replies: send_smb failed.");
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /*
+ * Sanity check
+ */
+
+ if(params_to_send < 0 || data_to_send < 0) {
+ DEBUG(0,("send_nt_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Save case statics.
+****************************************************************************/
+
+static BOOL saved_case_sensitive;
+static BOOL saved_case_preserve;
+static BOOL saved_short_case_preserve;
+
+/****************************************************************************
+ Save case semantics.
+****************************************************************************/
+
+static void set_posix_case_semantics(uint32 file_attributes)
+{
+ if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS))
+ return;
+
+ saved_case_sensitive = case_sensitive;
+ saved_case_preserve = case_preserve;
+ saved_short_case_preserve = short_case_preserve;
+
+ /* Set to POSIX. */
+ case_sensitive = True;
+ case_preserve = True;
+ short_case_preserve = True;
+}
+
+/****************************************************************************
+ Restore case semantics.
+****************************************************************************/
+
+static void restore_case_semantics(uint32 file_attributes)
+{
+ if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS))
+ return;
+
+ case_sensitive = saved_case_sensitive;
+ case_preserve = saved_case_preserve;
+ short_case_preserve = saved_short_case_preserve;
+}
+
+/****************************************************************************
+ Utility function to map create disposition.
+****************************************************************************/
+
+static int map_create_disposition( uint32 create_disposition)
+{
+ int ret;
+
+ switch( create_disposition ) {
+ case FILE_CREATE:
+ /* create if not exist, fail if exist */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL);
+ break;
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE_IF:
+ /* create if not exist, trunc if exist */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+ break;
+ case FILE_OPEN:
+ /* fail if not exist, open if exists */
+ ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+ break;
+ case FILE_OPEN_IF:
+ /* create if not exist, open if exists */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+ break;
+ case FILE_OVERWRITE:
+ /* fail if not exist, truncate if exists */
+ ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+ break;
+ default:
+ DEBUG(0,("map_create_disposition: Incorrect value for create_disposition = %d\n",
+ create_disposition ));
+ return -1;
+ }
+
+ DEBUG(10,("map_create_disposition: Mapped create_disposition 0x%lx to 0x%x\n",
+ (unsigned long)create_disposition, ret ));
+
+ return ret;
+}
+
+/****************************************************************************
+ Utility function to map share modes.
+****************************************************************************/
+
+static int map_share_mode( char *fname, uint32 create_options,
+ uint32 *desired_access, uint32 share_access, uint32 file_attributes)
+{
+ int smb_open_mode = -1;
+
+ /*
+ * Convert GENERIC bits to specific bits.
+ */
+
+ se_map_generic(desired_access, &file_generic_mapping);
+
+ switch( *desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) {
+ case FILE_READ_DATA:
+ smb_open_mode = DOS_OPEN_RDONLY;
+ break;
+ case FILE_WRITE_DATA:
+ case FILE_APPEND_DATA:
+ case FILE_WRITE_DATA|FILE_APPEND_DATA:
+ smb_open_mode = DOS_OPEN_WRONLY;
+ break;
+ case FILE_READ_DATA|FILE_WRITE_DATA:
+ case FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA:
+ case FILE_READ_DATA|FILE_APPEND_DATA:
+ smb_open_mode = DOS_OPEN_RDWR;
+ break;
+ }
+
+ /*
+ * NB. For DELETE_ACCESS we should really check the
+ * directory permissions, as that is what controls
+ * delete, and for WRITE_DAC_ACCESS we should really
+ * check the ownership, as that is what controls the
+ * chmod. Note that this is *NOT* a security hole (this
+ * note is for you, Andrew) as we are not *allowing*
+ * the access at this point, the actual unlink or
+ * chown or chmod call would do this. We are just helping
+ * clients out by telling them if they have a hope
+ * of any of this succeeding. POSIX acls may still
+ * deny the real call. JRA.
+ */
+
+ if (smb_open_mode == -1) {
+
+ if(*desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|
+ FILE_EXECUTE|FILE_READ_ATTRIBUTES|
+ FILE_READ_EA|FILE_WRITE_EA|SYSTEM_SECURITY_ACCESS|
+ FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS)) {
+ smb_open_mode = DOS_OPEN_RDONLY;
+ } else if(*desired_access == 0) {
+
+ /*
+ * JRA - NT seems to sometimes send desired_access as zero. play it safe
+ * and map to a stat open.
+ */
+
+ smb_open_mode = DOS_OPEN_RDONLY;
+
+ } else {
+ DEBUG(0,("map_share_mode: Incorrect value 0x%lx for desired_access to file %s\n",
+ (unsigned long)*desired_access, fname));
+ return -1;
+ }
+ }
+
+ /*
+ * Set the special bit that means allow share delete.
+ * This is held outside the normal share mode bits at 1<<15.
+ * JRA.
+ */
+
+ if(share_access & FILE_SHARE_DELETE) {
+ smb_open_mode |= ALLOW_SHARE_DELETE;
+ DEBUG(10,("map_share_mode: FILE_SHARE_DELETE requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ /*
+ * We need to store the intent to open for Delete. This
+ * is what determines if a delete on close flag can be set.
+ * This is the wrong way (and place) to store this, but for 2.2 this
+ * is the only practical way. JRA.
+ */
+
+ if(*desired_access & DELETE_ACCESS) {
+ DEBUG(10,("map_share_mode: DELETE_ACCESS requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ if (create_options & FILE_DELETE_ON_CLOSE) {
+ /* Implicit delete access is *NOT* requested... */
+ smb_open_mode |= DELETE_ON_CLOSE_FLAG;
+ DEBUG(10,("map_share_mode: FILE_DELETE_ON_CLOSE requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ /* Add in the requested share mode. */
+ switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) {
+ case FILE_SHARE_READ:
+ smb_open_mode |= SET_DENY_MODE(DENY_WRITE);
+ break;
+ case FILE_SHARE_WRITE:
+ smb_open_mode |= SET_DENY_MODE(DENY_READ);
+ break;
+ case (FILE_SHARE_READ|FILE_SHARE_WRITE):
+ smb_open_mode |= SET_DENY_MODE(DENY_NONE);
+ break;
+ case FILE_SHARE_NONE:
+ smb_open_mode |= SET_DENY_MODE(DENY_ALL);
+ break;
+ }
+
+ /*
+ * Handle an O_SYNC request.
+ */
+
+ if(file_attributes & FILE_FLAG_WRITE_THROUGH)
+ smb_open_mode |= FILE_SYNC_OPENMODE;
+
+ DEBUG(10,("map_share_mode: Mapped desired access 0x%lx, share access 0x%lx, file attributes 0x%lx \
+to open_mode 0x%x\n", (unsigned long)*desired_access, (unsigned long)share_access,
+ (unsigned long)file_attributes, smb_open_mode ));
+
+ return smb_open_mode;
+}
+
+/****************************************************************************
+ Reply to an NT create and X call on a pipe.
+****************************************************************************/
+static int nt_open_pipe(char *fname, connection_struct *conn,
+ char *inbuf, char *outbuf, int *ppnum)
+{
+ smb_np_struct *p = NULL;
+
+ uint16 vuid = SVAL(inbuf, smb_uid);
+ int i;
+
+ DEBUG(4,("nt_open_pipe: Opening pipe %s.\n", fname));
+
+ /* See if it is one we want to handle. */
+
+ if (lp_disable_spoolss() && strequal(fname, "\\spoolss"))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ for( i = 0; known_nt_pipes[i]; i++ )
+ if( strequal(fname,known_nt_pipes[i]))
+ break;
+
+ if ( known_nt_pipes[i] == NULL )
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ /* Strip \\ off the name. */
+ fname++;
+
+ DEBUG(3,("nt_open_pipe: Known pipe %s opening.\n", fname));
+
+ p = open_rpc_pipe_p(fname, conn, vuid);
+ if (!p)
+ return(ERROR_DOS(ERRSRV,ERRnofids));
+
+ *ppnum = p->pnum;
+
+ return 0;
+}
+
+/****************************************************************************
+ Reply to an NT create and X call for pipes.
+****************************************************************************/
+
+static int do_ntcreate_pipe_open(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ int ret;
+ int pnum = -1;
+ char *p = NULL;
+
+ srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE);
+
+ if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0)
+ return ret;
+
+ /*
+ * Deal with pipe return.
+ */
+
+ set_message(outbuf,34,0,True);
+
+ p = outbuf + smb_vwv2;
+ p++;
+ SSVAL(p,0,pnum);
+ p += 2;
+ SIVAL(p,0,FILE_WAS_OPENED);
+ p += 4;
+ p += 32;
+ SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */
+ p += 20;
+ /* File type. */
+ SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE);
+ /* Device state. */
+ SSVAL(p,2, 0x5FF); /* ? */
+
+ DEBUG(5,("do_ntcreate_pipe_open: open pipe = %s\n", fname));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to an NT create and X call.
+****************************************************************************/
+
+int reply_ntcreate_and_X(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int result;
+ pstring fname;
+ uint32 flags = IVAL(inbuf,smb_ntcreate_Flags);
+ uint32 desired_access = IVAL(inbuf,smb_ntcreate_DesiredAccess);
+ uint32 file_attributes = IVAL(inbuf,smb_ntcreate_FileAttributes);
+ uint32 share_access = IVAL(inbuf,smb_ntcreate_ShareAccess);
+ uint32 create_disposition = IVAL(inbuf,smb_ntcreate_CreateDisposition);
+ uint32 create_options = IVAL(inbuf,smb_ntcreate_CreateOptions);
+ uint16 root_dir_fid = (uint16)IVAL(inbuf,smb_ntcreate_RootDirectoryFid);
+ int smb_ofun;
+ int smb_open_mode;
+ int smb_attr = (file_attributes & SAMBA_ATTRIBUTES_MASK);
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ int oplock_request = 0;
+ mode_t unixmode;
+ int fmode=0,rmode=0;
+ SMB_OFF_T file_len = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp=NULL;
+ char *p = NULL;
+ time_t c_time;
+ START_PROFILE(SMBntcreateX);
+
+ /* If it's an IPC, use the pipe handler. */
+
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support()) {
+ END_PROFILE(SMBntcreateX);
+ return do_ntcreate_pipe_open(conn,inbuf,outbuf,length,bufsize);
+ } else {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadaccess));
+ }
+ }
+
+
+ /*
+ * We need to construct the open_and_X ofun value from the
+ * NT values, as that's what our code is structured to accept.
+ */
+
+ if((smb_ofun = map_create_disposition( create_disposition )) == -1) {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadaccess));
+ }
+
+ /*
+ * Get the file name.
+ */
+
+ if(root_dir_fid != 0) {
+ /*
+ * This filename is relative to a directory fid.
+ */
+ files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid);
+ size_t dir_name_len;
+
+ if(!dir_fsp) {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+ }
+
+ if(!dir_fsp->is_directory) {
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE);
+
+ if( strchr_m(fname, ':')) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+ }
+
+ /*
+ * Copy in the base directory name.
+ */
+
+ pstrcpy( fname, dir_fsp->fsp_name );
+ dir_name_len = strlen(fname);
+
+ /*
+ * Ensure it ends in a '\'.
+ */
+
+ if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
+ pstrcat(fname, "\\");
+ dir_name_len++;
+ }
+
+ srvstr_pull(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len,
+ -1, STR_TERMINATE);
+ } else {
+ srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE);
+ }
+
+ /*
+ * Now contruct the smb_open_mode value from the filename,
+ * desired access and the share access.
+ */
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ if((smb_open_mode = map_share_mode(fname, create_options, &desired_access,
+ share_access,
+ file_attributes)) == -1) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ if (oplock_request) {
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+ }
+
+ /*
+ * Ordinary file or directory.
+ */
+
+ /*
+ * Check if POSIX semantics are wanted.
+ */
+
+ set_posix_case_semantics(file_attributes);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ unixmode = unix_mode(conn,smb_attr | aARCH, fname);
+
+ /*
+ * If it's a request for a directory open, deal with it separately.
+ */
+
+ if(create_options & FILE_DIRECTORY_FILE) {
+ oplock_request = 0;
+
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action);
+
+ restore_case_semantics(file_attributes);
+
+ if(!fsp) {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBntcreateX);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ } else {
+ /*
+ * Ordinary file case.
+ */
+
+ /* NB. We have a potential bug here. If we
+ * cause an oplock break to ourselves, then we
+ * could end up processing filename related
+ * SMB requests whilst we await the oplock
+ * break response. As we may have changed the
+ * filename case semantics to be POSIX-like,
+ * this could mean a filename request could
+ * fail when it should succeed. This is a rare
+ * condition, but eventually we must arrange
+ * to restore the correct case semantics
+ * before issuing an oplock break request to
+ * our client. JRA. */
+
+ fsp = open_file_shared1(conn,fname,&sbuf,
+ desired_access,
+ smb_open_mode,
+ smb_ofun,unixmode, oplock_request,
+ &rmode,&smb_action);
+
+ if (!fsp) {
+ /* We cheat here. There are two cases we
+ * care about. One is a directory rename,
+ * where the NT client will attempt to
+ * open the source directory for
+ * DELETE access. Note that when the
+ * NT client does this it does *not*
+ * set the directory bit in the
+ * request packet. This is translated
+ * into a read/write open
+ * request. POSIX states that any open
+ * for write request on a directory
+ * will generate an EISDIR error, so
+ * we can catch this here and open a
+ * pseudo handle that is flagged as a
+ * directory. The second is an open
+ * for a permissions read only, which
+ * we handle in the open_file_stat case. JRA.
+ */
+
+ if(errno == EISDIR) {
+
+ /*
+ * Fail the open if it was explicitly a non-directory file.
+ */
+
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ restore_case_semantics(file_attributes);
+ SSVAL(outbuf, smb_flg2,
+ SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ oplock_request = 0;
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBntcreateX);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ } else {
+
+ restore_case_semantics(file_attributes);
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBntcreateX);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+ }
+
+ restore_case_semantics(file_attributes);
+
+ file_len = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ if(fmode == 0)
+ fmode = FILE_ATTRIBUTE_NORMAL;
+ if (!fsp->is_directory && (fmode & aDIR)) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * If the caller set the extended oplock request bit
+ * and we granted one (by whatever means) - set the
+ * correct bit for extended oplock reply.
+ */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+#if 0
+ /* W2K sends back 42 words here ! If we do the same it breaks offline sync. Go figure... ? JRA. */
+ set_message(outbuf,42,0,True);
+#else
+ set_message(outbuf,34,0,True);
+#endif
+
+ p = outbuf + smb_vwv2;
+
+ /*
+ * Currently as we don't support level II oplocks we just report
+ * exclusive & batch here.
+ */
+
+ if (smb_action & EXTENDED_OPLOCK_GRANTED) {
+ if (flags & REQUEST_BATCH_OPLOCK) {
+ SCVAL(p,0, BATCH_OPLOCK_RETURN);
+ } else {
+ SCVAL(p,0, EXCLUSIVE_OPLOCK_RETURN);
+ }
+ } else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+ SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+ } else {
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+ }
+
+ p++;
+ SSVAL(p,0,fsp->fnum);
+ p += 2;
+ SIVAL(p,0,smb_action);
+ p += 4;
+
+ /* Create time. */
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ put_long_date(p,c_time);
+ p += 8;
+ put_long_date(p,sbuf.st_atime); /* access time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* write time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* change time */
+ p += 8;
+ SIVAL(p,0,fmode); /* File Attributes. */
+ p += 4;
+ SOFF_T(p, 0, SMB_ROUNDUP_ALLOCATION(file_len));
+ p += 8;
+ SOFF_T(p,0,file_len);
+ p += 12;
+ SCVAL(p,0,fsp->is_directory ? 1 : 0);
+
+ DEBUG(5,("reply_ntcreate_and_X: fnum = %d, open name = %s\n", fsp->fnum, fsp->fsp_name));
+
+ result = chain_reply(inbuf,outbuf,length,bufsize);
+ END_PROFILE(SMBntcreateX);
+ return result;
+}
+
+/****************************************************************************
+ Reply to a NT_TRANSACT_CREATE call to open a pipe.
+****************************************************************************/
+
+static int do_nt_transact_create_pipe( connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize, char **ppsetup, char **ppparams,
+ char **ppdata)
+{
+ pstring fname;
+ int total_parameter_count = (int)IVAL(inbuf, smb_nt_TotalParameterCount);
+ char *params = *ppparams;
+ int ret;
+ int pnum = -1;
+ char *p = NULL;
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if(total_parameter_count < 54) {
+ DEBUG(0,("do_nt_transact_create_pipe - insufficient parameters (%u)\n", (unsigned int)total_parameter_count));
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+ srvstr_pull(inbuf, fname, params+53, sizeof(fname), -1, STR_TERMINATE);
+
+ if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0)
+ return ret;
+
+ /* Realloc the size of parameters and data we will return */
+ params = Realloc(*ppparams, 69);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppparams = params;
+
+ memset((char *)params,'\0',69);
+
+ p = params;
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+
+ p += 2;
+ SSVAL(p,0,pnum);
+ p += 2;
+ SIVAL(p,0,FILE_WAS_OPENED);
+ p += 8;
+
+ p += 32;
+ SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */
+ p += 20;
+ /* File type. */
+ SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE);
+ /* Device state. */
+ SSVAL(p,2, 0x5FF); /* ? */
+
+ DEBUG(5,("do_nt_transact_create_pipe: open name = %s\n", fname));
+
+ /* Send the required number of replies */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ Internal fn to set security descriptors.
+****************************************************************************/
+
+static BOOL set_sd(files_struct *fsp, char *data, uint32 sd_len, uint32 security_info_sent, int *pdef_class,uint32 *pdef_code)
+{
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+ TALLOC_CTX *mem_ctx;
+ BOOL ret;
+
+ if (sd_len == 0) {
+ *pdef_class = ERRDOS;
+ *pdef_code = ERRbadaccess;
+ return False;
+ }
+
+ /*
+ * Init the parse struct we will unmarshall from.
+ */
+
+ if ((mem_ctx = talloc_init()) == NULL) {
+ DEBUG(0,("set_sd: talloc_init failed.\n"));
+ *pdef_class = ERRDOS;
+ *pdef_code = ERRnomem;
+ return False;
+ }
+
+ prs_init(&pd, 0, mem_ctx, UNMARSHALL);
+
+ /*
+ * Setup the prs_struct to point at the memory we just
+ * allocated.
+ */
+
+ prs_give_memory( &pd, data, sd_len, False);
+
+ /*
+ * Finally, unmarshall from the data buffer.
+ */
+
+ if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+ DEBUG(0,("set_sd: Error in unmarshalling security descriptor.\n"));
+ /*
+ * Return access denied for want of a better error message..
+ */
+ talloc_destroy(mem_ctx);
+ *pdef_class = ERRDOS;
+ *pdef_code = ERRnomem;
+ return False;
+ }
+
+ if (psd->off_owner_sid==0)
+ security_info_sent &= ~OWNER_SECURITY_INFORMATION;
+ if (psd->off_grp_sid==0)
+ security_info_sent &= ~GROUP_SECURITY_INFORMATION;
+ if (psd->off_sacl==0)
+ security_info_sent &= ~SACL_SECURITY_INFORMATION;
+ if (psd->off_dacl==0)
+ security_info_sent &= ~DACL_SECURITY_INFORMATION;
+
+ ret = fsp->conn->vfs_ops.fset_nt_acl( fsp, fsp->fd, security_info_sent, psd);
+
+ if (!ret) {
+ talloc_destroy(mem_ctx);
+ *pdef_class = ERRDOS;
+ *pdef_code = ERRnoaccess;
+ return False;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ *pdef_class = 0;
+ *pdef_code = 0;
+ return True;
+}
+
+/****************************************************************************
+ Reply to a NT_TRANSACT_CREATE call (needs to process SD's).
+****************************************************************************/
+
+static int call_nt_transact_create(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize, char **ppsetup, char **ppparams,
+ char **ppdata)
+{
+ pstring fname;
+ char *params = *ppparams;
+ char *data = *ppdata;
+ int total_parameter_count = (int)IVAL(inbuf, smb_nt_TotalParameterCount);
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ int oplock_request = 0;
+ mode_t unixmode;
+ int fmode=0,rmode=0;
+ SMB_OFF_T file_len = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp = NULL;
+ char *p = NULL;
+ uint32 flags;
+ uint32 desired_access;
+ uint32 file_attributes;
+ uint32 share_access;
+ uint32 create_disposition;
+ uint32 create_options;
+ uint32 sd_len;
+ uint16 root_dir_fid;
+ int smb_ofun;
+ int smb_open_mode;
+ int smb_attr;
+ int error_class;
+ uint32 error_code;
+ time_t c_time;
+
+ DEBUG(5,("call_nt_transact_create\n"));
+
+ /*
+ * If it's an IPC, use the pipe handler.
+ */
+
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support())
+ return do_nt_transact_create_pipe(conn, inbuf, outbuf, length,
+ bufsize, ppsetup, ppparams, ppdata);
+ else
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if(total_parameter_count < 54) {
+ DEBUG(0,("call_nt_transact_create - insufficient parameters (%u)\n", (unsigned int)total_parameter_count));
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+ flags = IVAL(params,0);
+ desired_access = IVAL(params,8);
+ file_attributes = IVAL(params,20);
+ share_access = IVAL(params,24);
+ create_disposition = IVAL(params,28);
+ create_options = IVAL(params,32);
+ sd_len = IVAL(params,36);
+ root_dir_fid = (uint16)IVAL(params,4);
+ smb_attr = (file_attributes & SAMBA_ATTRIBUTES_MASK);
+
+ /*
+ * We need to construct the open_and_X ofun value from the
+ * NT values, as that's what our code is structured to accept.
+ */
+
+ if((smb_ofun = map_create_disposition( create_disposition )) == -1)
+ return ERROR_DOS(ERRDOS,ERRbadmem);
+
+ /*
+ * Get the file name.
+ */
+
+ if(root_dir_fid != 0) {
+ /*
+ * This filename is relative to a directory fid.
+ */
+
+ files_struct *dir_fsp = file_fsp(params,4);
+ size_t dir_name_len;
+
+ if(!dir_fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if(!dir_fsp->is_directory) {
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ srvstr_pull(inbuf, fname, params+53, sizeof(fname), -1, STR_TERMINATE);
+
+ if( strchr_m(fname, ':')) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /*
+ * Copy in the base directory name.
+ */
+
+ pstrcpy( fname, dir_fsp->fsp_name );
+ dir_name_len = strlen(fname);
+
+ /*
+ * Ensure it ends in a '\'.
+ */
+
+ if((fname[dir_name_len-1] != '\\') && (fname[dir_name_len-1] != '/')) {
+ pstrcat(fname, "\\");
+ dir_name_len++;
+ }
+
+ srvstr_pull(inbuf, &fname[dir_name_len], params+53, sizeof(fname)-dir_name_len,
+ -1, STR_TERMINATE);
+ } else {
+ srvstr_pull(inbuf, fname, params+53, sizeof(fname), -1, STR_TERMINATE);
+ }
+
+ /*
+ * Now contruct the smb_open_mode value from the desired access
+ * and the share access.
+ */
+
+ if((smb_open_mode = map_share_mode( fname, create_options, &desired_access,
+ share_access, file_attributes)) == -1)
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+
+ /*
+ * Check if POSIX semantics are wanted.
+ */
+
+ set_posix_case_semantics(file_attributes);
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ unixmode = unix_mode(conn,smb_attr | aARCH, fname);
+
+ /*
+ * If it's a request for a directory open, deal with it separately.
+ */
+
+ if(create_options & FILE_DIRECTORY_FILE) {
+
+ oplock_request = 0;
+
+ /*
+ * We will get a create directory here if the Win32
+ * app specified a security descriptor in the
+ * CreateDirectory() call.
+ */
+
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ set_bad_path_error(errno, bad_path);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ } else {
+
+ /*
+ * Ordinary file case.
+ */
+
+ fsp = open_file_shared1(conn,fname,&sbuf,desired_access,
+ smb_open_mode,smb_ofun,unixmode,
+ oplock_request,&rmode,&smb_action);
+
+ if (!fsp) {
+
+ if(errno == EISDIR) {
+
+ /*
+ * Fail the open if it was explicitly a non-directory file.
+ */
+
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ restore_case_semantics(file_attributes);
+ SSVAL(outbuf, smb_flg2,
+ SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ oplock_request = 0;
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ set_bad_path_error(errno, bad_path);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ } else {
+
+ restore_case_semantics(file_attributes);
+ set_bad_path_error(errno, bad_path);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ file_len = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ if(fmode == 0)
+ fmode = FILE_ATTRIBUTE_NORMAL;
+
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ restore_case_semantics(file_attributes);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * If the caller set the extended oplock request bit
+ * and we granted one (by whatever means) - set the
+ * correct bit for extended oplock reply.
+ */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+ }
+
+ /*
+ * Now try and apply the desired SD.
+ */
+
+ if (!set_sd( fsp, data, sd_len, ALL_SECURITY_INFORMATION, &error_class, &error_code)) {
+ close_file(fsp,False);
+ restore_case_semantics(file_attributes);
+ return ERROR_DOS(error_class, error_code);
+ }
+
+ restore_case_semantics(file_attributes);
+
+ /* Realloc the size of parameters and data we will return */
+ params = Realloc(*ppparams, 69);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppparams = params;
+
+ memset((char *)params,'\0',69);
+
+ p = params;
+ if (smb_action & EXTENDED_OPLOCK_GRANTED)
+ SCVAL(p,0, BATCH_OPLOCK_RETURN);
+ else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+ else
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+
+ p += 2;
+ SSVAL(p,0,fsp->fnum);
+ p += 2;
+ SIVAL(p,0,smb_action);
+ p += 8;
+
+ /* Create time. */
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ put_long_date(p,c_time);
+ p += 8;
+ put_long_date(p,sbuf.st_atime); /* access time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* write time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* change time */
+ p += 8;
+ SIVAL(p,0,fmode); /* File Attributes. */
+ p += 4;
+ SOFF_T(p, 0, SMB_ROUNDUP_ALLOCATION(file_len));
+ p += 8;
+ SOFF_T(p,0,file_len);
+
+ DEBUG(5,("call_nt_transact_create: open name = %s\n", fname));
+
+ /* Send the required number of replies */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a NT CANCEL request.
+****************************************************************************/
+int reply_ntcancel(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ /*
+ * Go through and cancel any pending change notifies.
+ */
+
+ int mid = SVAL(inbuf,smb_mid);
+ START_PROFILE(SMBntcancel);
+ remove_pending_change_notify_requests_by_mid(mid);
+ remove_pending_lock_requests_by_mid(mid);
+
+ DEBUG(3,("reply_ntcancel: cancel called on mid = %d.\n", mid));
+
+ END_PROFILE(SMBntcancel);
+ return(-1);
+}
+
+/****************************************************************************
+ 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.
+****************************************************************************/
+static int call_nt_transact_notify_change(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
+ char **ppsetup,
+ char **ppparams, char **ppdata)
+{
+ char *setup = *ppsetup;
+ files_struct *fsp;
+ uint32 flags;
+
+ fsp = file_fsp(setup,4);
+ flags = IVAL(setup, 0);
+
+ DEBUG(3,("call_nt_transact_notify_change\n"));
+
+ if(!fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if((!fsp->is_directory) || (conn != fsp->conn))
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if (!change_notify_set(inbuf, fsp, conn, flags))
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+
+ DEBUG(3,("call_nt_transact_notify_change: notify change called on directory \
+name = %s\n", fsp->fsp_name ));
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to an NT transact rename command.
+****************************************************************************/
+
+static int call_nt_transact_rename(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
+ char **ppsetup, char **ppparams, char **ppdata)
+{
+ char *params = *ppparams;
+ pstring new_name;
+ files_struct *fsp = file_fsp(params, 0);
+ BOOL replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False;
+ NTSTATUS status;
+
+ CHECK_FSP(fsp, conn);
+ srvstr_pull(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE);
+
+ status = rename_internals(conn, fsp->fsp_name,
+ new_name, replace_if_exists);
+ if (!NT_STATUS_IS_OK(status))
+ return ERROR_NT(status);
+
+ /*
+ * Rename was successful.
+ */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+
+ DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n",
+ fsp->fsp_name, new_name));
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+
+ process_pending_change_notify_queue((time_t)0);
+
+ return -1;
+}
+
+/******************************************************************************
+ Fake up a completely empty SD.
+*******************************************************************************/
+
+static size_t get_null_nt_acl(TALLOC_CTX *mem_ctx, SEC_DESC **ppsd)
+{
+ extern DOM_SID global_sid_World;
+ size_t sd_size;
+
+ *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size);
+ if(!*ppsd) {
+ DEBUG(0,("get_null_nt_acl: Unable to malloc space for security descriptor.\n"));
+ sd_size = 0;
+ }
+
+ return sd_size;
+}
+
+/****************************************************************************
+ Reply to query a security descriptor - currently this is not implemented (it
+ is planned to be though). Right now it just returns the same thing NT would
+ when queried on a FAT filesystem. JRA.
+****************************************************************************/
+
+static int call_nt_transact_query_security_desc(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **ppsetup, char **ppparams, char **ppdata)
+{
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ char *params = *ppparams;
+ char *data = *ppdata;
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ TALLOC_CTX *mem_ctx;
+
+ files_struct *fsp = file_fsp(params,0);
+
+ if(!fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ DEBUG(3,("call_nt_transact_query_security_desc: file = %s\n", fsp->fsp_name ));
+
+ params = Realloc(*ppparams, 4);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppparams = params;
+
+ if ((mem_ctx = talloc_init()) == NULL) {
+ DEBUG(0,("call_nt_transact_query_security_desc: talloc_init failed.\n"));
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /*
+ * Get the permissions to return.
+ */
+
+ if (!lp_nt_acl_support(SNUM(conn)))
+ sd_size = get_null_nt_acl(mem_ctx, &psd);
+ else
+ sd_size = conn->vfs_ops.fget_nt_acl(fsp, fsp->fd, &psd);
+
+ if (sd_size == 0) {
+ talloc_destroy(mem_ctx);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %d.\n",(int)sd_size));
+
+ SIVAL(params,0,(uint32)sd_size);
+
+ if(max_data_count < sd_size) {
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_BUFFER_TOO_SMALL,
+ params, 4, *ppdata, 0);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /*
+ * Allocate the data we will point this at.
+ */
+
+ data = Realloc(*ppdata, sd_size);
+ if(data == NULL) {
+ talloc_destroy(mem_ctx);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ *ppdata = data;
+
+ memset(data, '\0', sd_size);
+
+ /*
+ * Init the parse struct we will marshall into.
+ */
+
+ prs_init(&pd, 0, mem_ctx, MARSHALL);
+
+ /*
+ * Setup the prs_struct to point at the memory we just
+ * allocated.
+ */
+
+ prs_give_memory( &pd, data, (uint32)sd_size, False);
+
+ /*
+ * Finally, linearize into the outgoing buffer.
+ */
+
+ if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+ DEBUG(0,("call_nt_transact_query_security_desc: Error in marshalling \
+security descriptor.\n"));
+ /*
+ * Return access denied for want of a better error message..
+ */
+ talloc_destroy(mem_ctx);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Now we can delete the security descriptor.
+ */
+
+ talloc_destroy(mem_ctx);
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 4, data, (int)sd_size);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor. Map to UNIX perms.
+****************************************************************************/
+
+static int call_nt_transact_set_security_desc(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize, char **ppsetup,
+ char **ppparams, char **ppdata)
+{
+ uint32 total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount);
+ char *params= *ppparams;
+ char *data = *ppdata;
+ uint32 total_data_count = (uint32)IVAL(inbuf, smb_nts_TotalDataCount);
+ files_struct *fsp = NULL;
+ uint32 security_info_sent = 0;
+ int error_class;
+ uint32 error_code;
+
+ if(total_parameter_count < 8)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ if((fsp = file_fsp(params,0)) == NULL)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if(!lp_nt_acl_support(SNUM(conn)))
+ goto done;
+
+ security_info_sent = IVAL(params,4);
+
+ DEBUG(3,("call_nt_transact_set_security_desc: file = %s, sent 0x%x\n", fsp->fsp_name,
+ (unsigned int)security_info_sent ));
+
+ if (!set_sd( fsp, data, total_data_count, security_info_sent, &error_class, &error_code))
+ return ERROR_DOS(error_class, error_code);
+
+ done:
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to IOCTL - not implemented - no plans.
+****************************************************************************/
+static int call_nt_transact_ioctl(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
+ char **ppsetup, char **ppparams, char **ppdata)
+{
+ static BOOL logged_message = False;
+
+ if(!logged_message) {
+ DEBUG(0,("call_nt_transact_ioctl: Currently not implemented.\n"));
+ logged_message = True; /* Only print this once... */
+ }
+ return ERROR_DOS(ERRSRV,ERRnosupport);
+}
+
+/****************************************************************************
+ Reply to a SMBNTtrans.
+****************************************************************************/
+int reply_nttrans(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+#if 0 /* Not used. */
+ uint16 max_setup_count = CVAL(inbuf, smb_nt_MaxSetupCount);
+ uint32 max_parameter_count = IVAL(inbuf, smb_nt_MaxParameterCount);
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+#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* */
+ uint16 function_code = SVAL( inbuf, smb_nt_Function);
+ char *params = NULL, *data = NULL, *setup = NULL;
+ uint32 num_params_sofar, num_data_sofar;
+ START_PROFILE(SMBnttrans);
+
+ if(global_oplock_break && (function_code == NT_TRANSACT_CREATE)) {
+ /*
+ * Queue this open message as we are the process of an oplock break.
+ */
+
+ DEBUG(2,("reply_nttrans: queueing message NT_TRANSACT_CREATE \
+due to being in oplock break state.\n" ));
+
+ push_oplock_pending_smb_message( inbuf, length);
+ END_PROFILE(SMBnttrans);
+ return -1;
+ }
+
+ if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) {
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /*
+ * All nttrans messages we handle have smb_wct == 19 + setup_count.
+ * Ensure this is so as a sanity check.
+ */
+
+ if(CVAL(inbuf, smb_wct) != 19 + (setup_count/2)) {
+ DEBUG(2,("Invalid smb_wct %d in nttrans call (should be %d)\n",
+ CVAL(inbuf, smb_wct), 19 + (setup_count/2)));
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* Allocate the space for the setup, the maximum needed parameters and data */
+
+ if(setup_count > 0)
+ setup = (char *)malloc(setup_count);
+ if (total_parameter_count > 0)
+ params = (char *)malloc(total_parameter_count);
+ if (total_data_count > 0)
+ data = (char *)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)
+ exit_server("reply_nttrans: invalid sizes in packet.");
+
+ if(setup) {
+ memcpy( setup, &inbuf[smb_nt_SetupStart], setup_count);
+ DEBUG(10,("reply_nttrans: setup_count = %d\n", setup_count));
+ dump_data(10, setup, setup_count);
+ }
+ if(params) {
+ memcpy( params, smb_base(inbuf) + parameter_offset, parameter_count);
+ DEBUG(10,("reply_nttrans: parameter_count = %d\n", parameter_count));
+ dump_data(10, params, parameter_count);
+ }
+ if(data) {
+ memcpy( data, smb_base(inbuf) + data_offset, data_count);
+ DEBUG(10,("reply_nttrans: data_count = %d\n",data_count));
+ dump_data(10, data, data_count);
+ }
+
+ 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);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_nttrans: send_smb failed.");
+
+ while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) {
+ BOOL ret;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ 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" ));
+ }
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* Revise total_params and total_data in case they have changed downwards */
+ total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount);
+ total_data_count = IVAL(inbuf, smb_nts_TotalDataCount);
+ num_params_sofar += (parameter_count = IVAL(inbuf,smb_nts_ParameterCount));
+ num_data_sofar += ( data_count = IVAL(inbuf, smb_nts_DataCount));
+ if (num_params_sofar > total_parameter_count || num_data_sofar > total_data_count)
+ exit_server("reply_nttrans2: data overflow in secondary nttrans packet");
+
+ memcpy( &params[ IVAL(inbuf, smb_nts_ParameterDisplacement)],
+ smb_base(inbuf) + IVAL(inbuf, smb_nts_ParameterOffset), parameter_count);
+ memcpy( &data[IVAL(inbuf, smb_nts_DataDisplacement)],
+ smb_base(inbuf)+ IVAL(inbuf, smb_nts_DataOffset), data_count);
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ /* 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, &params, &data);
+ 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, &params, &data);
+ 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, &params, &data);
+ 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, &params, &data);
+ 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, &params, &data);
+ 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,
+ length, bufsize,
+ &setup, &params, &data);
+ END_PROFILE_NESTED(NT_transact_query_security_desc);
+ break;
+ 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);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* 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.
+ */
+
+ 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. */
+}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
new file mode 100644
index 0000000000..29a854a397
--- /dev/null
+++ b/source3/smbd/open.c
@@ -0,0 +1,1328 @@
+/*
+ Unix SMB/CIFS implementation.
+ file opening and share modes
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern userdom_struct current_user_info;
+extern uint16 global_oplock_port;
+extern BOOL global_client_failed_oplock_break;
+
+/****************************************************************************
+ fd support routines - attempt to do a dos_open.
+****************************************************************************/
+
+static int fd_open(struct connection_struct *conn, char *fname,
+ int flags, mode_t mode)
+{
+ int fd;
+#ifdef O_NOFOLLOW
+ if (!lp_symlinks(SNUM(conn)))
+ flags |= O_NOFOLLOW;
+#endif
+
+ fd = conn->vfs_ops.open(conn,fname,flags,mode);
+
+ /* Fix for files ending in '.' */
+ if((fd == -1) && (errno == ENOENT) &&
+ (strchr_m(fname,'.')==NULL)) {
+ pstrcat(fname,".");
+ fd = conn->vfs_ops.open(conn,fname,flags,mode);
+ }
+
+ DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n", fname,
+ flags, (int)mode, fd, (fd == -1) ? strerror(errno) : "" ));
+
+ return fd;
+}
+
+/****************************************************************************
+ Close the file associated with a fsp.
+****************************************************************************/
+
+int fd_close(struct connection_struct *conn, files_struct *fsp)
+{
+ if (fsp->fd == -1)
+ return 0; /* what we used to call a stat open. */
+ return fd_close_posix(conn, fsp);
+}
+
+
+/****************************************************************************
+ Check a filename for the pipe string.
+****************************************************************************/
+
+static void check_for_pipe(char *fname)
+{
+ /* special case of pipe opens */
+ char s[10];
+ StrnCpy(s,fname,sizeof(s)-1);
+ strlower(s);
+ if (strstr(s,"pipe/")) {
+ DEBUG(3,("Rejecting named pipe open for %s\n",fname));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRaccess;
+ }
+}
+
+/****************************************************************************
+ Open a file.
+****************************************************************************/
+
+static BOOL open_file(files_struct *fsp,connection_struct *conn,
+ char *fname1,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access)
+{
+ extern struct current_user current_user;
+ pstring fname;
+ int accmode = (flags & O_ACCMODE);
+ int local_flags = flags;
+
+ fsp->fd = -1;
+ fsp->oplock_type = NO_OPLOCK;
+ errno = EPERM;
+
+ pstrcpy(fname,fname1);
+
+ /* Check permissions */
+
+ /*
+ * This code was changed after seeing a client open request
+ * containing the open mode of (DENY_WRITE/read-only) with
+ * the 'create if not exist' bit set. The previous code
+ * would fail to open the file read only on a read-only share
+ * as it was checking the flags parameter directly against O_RDONLY,
+ * this was failing as the flags parameter was set to O_RDONLY|O_CREAT.
+ * JRA.
+ */
+
+ if (!CAN_WRITE(conn)) {
+ /* It's a read-only share - fail if we wanted to write. */
+ if(accmode != O_RDONLY) {
+ DEBUG(3,("Permission denied opening %s\n",fname));
+ check_for_pipe(fname);
+ return False;
+ } else if(flags & O_CREAT) {
+ /* We don't want to write - but we must make sure that O_CREAT
+ doesn't create the file if we have write access into the
+ directory.
+ */
+ flags &= ~O_CREAT;
+ }
+ }
+
+ /*
+ * This little piece of insanity is inspired by the
+ * fact that an NT client can open a file for O_RDONLY,
+ * but set the create disposition to FILE_EXISTS_TRUNCATE.
+ * If the client *can* write to the file, then it expects to
+ * truncate the file, even though it is opening for readonly.
+ * Quicken uses this stupid trick in backup file creation...
+ * Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net>
+ * for helping track this one down. It didn't bite us in 2.0.x
+ * as we always opened files read-write in that release. JRA.
+ */
+
+ if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC))
+ local_flags = (flags & ~O_ACCMODE)|O_RDWR;
+
+ /*
+ * We can't actually truncate here as the file may be locked.
+ * open_file_shared will take care of the truncate later. JRA.
+ */
+
+ local_flags &= ~O_TRUNC;
+
+ if ((desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+ (local_flags & O_CREAT)) {
+
+ /* actually do the open */
+ fsp->fd = fd_open(conn, fname, local_flags, mode);
+
+ if (fsp->fd == -1) {
+ DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n",
+ fname,strerror(errno),local_flags,flags));
+ check_for_pipe(fname);
+ return False;
+ }
+ } else
+ fsp->fd = -1; /* What we used to call a stat open. */
+
+ if (!VALID_STAT(*psbuf)) {
+ int ret;
+
+ if (fsp->fd == -1)
+ ret = vfs_stat(conn, fname, psbuf);
+ else
+ ret = vfs_fstat(fsp,fsp->fd,psbuf);
+
+ if (ret == -1) {
+ DEBUG(0,("Error doing fstat on open file %s (%s)\n", fname,strerror(errno) ));
+ fd_close(conn, fsp);
+ return False;
+ }
+ }
+
+ /*
+ * POSIX allows read-only opens of directories. We don't
+ * want to do this (we use a different code path for this)
+ * so catch a directory open and return an EISDIR. JRA.
+ */
+
+ if(S_ISDIR(psbuf->st_mode)) {
+ fd_close(conn, fsp);
+ errno = EISDIR;
+ return False;
+ }
+
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->vuid = current_user.vuid;
+ fsp->size = psbuf->st_size;
+ fsp->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = ((flags & O_WRONLY)==0);
+ fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+ fsp->share_mode = 0;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->directory_delete_on_close = False;
+ fsp->conn = conn;
+ string_set(&fsp->fsp_name,fname);
+ fsp->wcp = NULL; /* Write cache pointer. */
+
+ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
+ *current_user_info.smb_name ? current_user_info.smb_name : conn->user,fsp->fsp_name,
+ BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+ conn->num_files_open + 1));
+
+ return True;
+}
+
+/****************************************************************************
+ C. Hoch 11/22/95
+ Helper for open_file_shared.
+ Truncate a file after checking locking; close file if locked.
+ **************************************************************************/
+
+static int truncate_unless_locked(struct connection_struct *conn, files_struct *fsp)
+{
+ SMB_BIG_UINT mask = (SMB_BIG_UINT)-1;
+
+ if (is_locked(fsp,fsp->conn,mask,0,WRITE_LOCK,True)){
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRlock;
+ return -1;
+ } else {
+ return conn->vfs_ops.ftruncate(fsp,fsp->fd,0);
+ }
+}
+
+/*******************************************************************
+return True if the filename is one of the special executable types
+********************************************************************/
+static BOOL is_executable(const char *fname)
+{
+ if ((fname = strrchr_m(fname,'.'))) {
+ if (strequal(fname,".com") ||
+ strequal(fname,".dll") ||
+ strequal(fname,".exe") ||
+ strequal(fname,".sym")) {
+ return True;
+ }
+ }
+ return False;
+}
+
+enum {AFAIL,AREAD,AWRITE,AALL};
+
+/*******************************************************************
+reproduce the share mode access table
+this is horrendoously complex, and really can't be justified on any
+rational grounds except that this is _exactly_ what NT does. See
+the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
+test routines.
+********************************************************************/
+static int access_table(int new_deny,int old_deny,int old_mode,
+ BOOL same_pid, BOOL isexe)
+{
+ if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL);
+
+ if (same_pid) {
+ if (isexe && old_mode == DOS_OPEN_RDONLY &&
+ old_deny == DENY_DOS && new_deny == DENY_READ) {
+ return AFAIL;
+ }
+ if (!isexe && old_mode == DOS_OPEN_RDONLY &&
+ old_deny == DENY_DOS && new_deny == DENY_DOS) {
+ return AREAD;
+ }
+ if (new_deny == DENY_FCB && old_deny == DENY_DOS) {
+ if (isexe) return AFAIL;
+ if (old_mode == DOS_OPEN_RDONLY) return AFAIL;
+ return AALL;
+ }
+ if (old_mode == DOS_OPEN_RDONLY && old_deny == DENY_DOS) {
+ if (new_deny == DENY_FCB || new_deny == DENY_READ) {
+ if (isexe) return AREAD;
+ return AFAIL;
+ }
+ }
+ if (old_deny == DENY_FCB) {
+ if (new_deny == DENY_DOS || new_deny == DENY_FCB) return AALL;
+ return AFAIL;
+ }
+ }
+
+ if (old_deny == DENY_DOS || new_deny == DENY_DOS ||
+ old_deny == DENY_FCB || new_deny == DENY_FCB) {
+ if (isexe) {
+ if (old_deny == DENY_FCB || new_deny == DENY_FCB) {
+ return AFAIL;
+ }
+ if (old_deny == DENY_DOS) {
+ if (new_deny == DENY_READ &&
+ (old_mode == DOS_OPEN_RDONLY ||
+ old_mode == DOS_OPEN_RDWR)) {
+ return AFAIL;
+ }
+ if (new_deny == DENY_WRITE &&
+ (old_mode == DOS_OPEN_WRONLY ||
+ old_mode == DOS_OPEN_RDWR)) {
+ return AFAIL;
+ }
+ return AALL;
+ }
+ if (old_deny == DENY_NONE) return AALL;
+ if (old_deny == DENY_READ) return AWRITE;
+ if (old_deny == DENY_WRITE) return AREAD;
+ }
+ /* it isn't a exe, dll, sym or com file */
+ if (old_deny == new_deny && same_pid)
+ return(AALL);
+
+ if (old_deny == DENY_READ || new_deny == DENY_READ) return AFAIL;
+ if (old_mode == DOS_OPEN_RDONLY) return(AREAD);
+
+ return(AFAIL);
+ }
+
+ switch (new_deny)
+ {
+ case DENY_WRITE:
+ if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_RDONLY) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==DOS_OPEN_RDONLY) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==DOS_OPEN_RDONLY) return(AALL);
+ return(AFAIL);
+ case DENY_READ:
+ if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_WRONLY) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==DOS_OPEN_WRONLY) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==DOS_OPEN_WRONLY) return(AALL);
+ return(AFAIL);
+ case DENY_NONE:
+ if (old_deny==DENY_WRITE) return(AREAD);
+ if (old_deny==DENY_READ) return(AWRITE);
+ if (old_deny==DENY_NONE) return(AALL);
+ return(AFAIL);
+ }
+ return(AFAIL);
+}
+
+
+/****************************************************************************
+check if we can open a file with a share mode
+****************************************************************************/
+
+static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access,
+ const char *fname, BOOL fcbopen, int *flags)
+{
+ int deny_mode = GET_DENY_MODE(share_mode);
+ int old_open_mode = GET_OPEN_MODE(share->share_mode);
+ int old_deny_mode = GET_DENY_MODE(share->share_mode);
+
+ /*
+ * share modes = false means don't bother to check for
+ * DENY mode conflict. This is a *really* bad idea :-). JRA.
+ */
+
+ if(!lp_share_modes(SNUM(conn)))
+ return True;
+
+ /*
+ * Don't allow any opens once the delete on close flag has been
+ * set.
+ */
+
+ if (GET_DELETE_ON_CLOSE_FLAG(share->share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete on close flag is set.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRnoaccess;
+ return False;
+ }
+
+ /* this is a nasty hack, but necessary until we rewrite our open
+ handling to use a NTCreateX call as the basic call.
+ NT may open a file with neither read nor write access, and in
+ this case it expects the open not to conflict with any
+ existing deny modes. This happens (for example) during a
+ "xcopy /o" where the second file descriptor is used for
+ ACL sets
+ (tridge)
+ */
+
+ /*
+ * This is a bit wierd - the test for desired access not having the
+ * critical bits seems seems odd. Firstly, if both opens have no
+ * critical bits then always ignore. Then check the "allow delete"
+ * then check for either. This probably isn't quite right yet but
+ * gets us much closer. JRA.
+ */
+
+ /*
+ * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE
+ * and the existing desired_acces then share modes don't conflict.
+ */
+
+ if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) &&
+ !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+
+ /*
+ * Wrinkle discovered by smbtorture....
+ * If both are non-io open and requester is asking for delete and current open has delete access
+ * but neither open has allowed file share delete then deny.... this is very strange and
+ * seems to be the only case in which non-io opens conflict. JRA.
+ */
+
+ if ((desired_access & DELETE_ACCESS) && (share->desired_access & DELETE_ACCESS) &&
+ (!GET_ALLOW_SHARE_DELETE(share->share_mode) || !GET_ALLOW_SHARE_DELETE(share_mode))) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access requests conflict.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+
+ return False;
+ }
+
+ DEBUG(5,("check_share_mode: Allowing open on file %s as both desired access (0x%x) \
+and existing desired access (0x%x) are non-data opens\n",
+ fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
+ }
+
+ /*
+ * If delete access was requested and the existing share mode doesn't have
+ * ALLOW_SHARE_DELETE then deny.
+ */
+
+ if ((desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share->share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access requested and allow share delete not set.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+
+ return False;
+ }
+
+ /*
+ * The inverse of the above.
+ * If delete access was granted and the new share mode doesn't have
+ * ALLOW_SHARE_DELETE then deny.
+ */
+
+ if ((share->desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access granted and allow share delete not requested.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+
+ return False;
+ }
+
+ /*
+ * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE
+ * then share modes don't conflict. Likewise with existing desired access.
+ */
+
+ if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+ !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+ DEBUG(5,("check_share_mode: Allowing open on file %s as desired access (0x%x) doesn't conflict with\
+existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
+ }
+
+ {
+ int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
+ (share->pid == sys_getpid()),is_executable(fname));
+
+ if ((access_allowed == AFAIL) ||
+ (!fcbopen && (access_allowed == AREAD && *flags == O_RDWR)) ||
+ (access_allowed == AREAD && *flags != O_RDONLY) ||
+ (access_allowed == AWRITE && *flags != O_WRONLY)) {
+
+ DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s,fcbopen = %d, flags = %d) = %d\n",
+ deny_mode,old_deny_mode,old_open_mode,
+ (int)share->pid,fname, fcbopen, *flags, access_allowed));
+
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+
+ return False;
+ }
+
+ if (access_allowed == AREAD)
+ *flags = O_RDONLY;
+
+ if (access_allowed == AWRITE)
+ *flags = O_WRONLY;
+
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Deal with open deny mode and oplock break processing.
+ Invarient: Share mode must be locked on entry and exit.
+ Returns -1 on error, or number of share modes on success (may be zero).
+****************************************************************************/
+
+static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T dev,
+ SMB_INO_T inode,
+ uint32 desired_access,
+ int share_mode, int *p_flags, int *p_oplock_request,
+ BOOL *p_all_current_opens_are_level_II)
+{
+ int i;
+ int num_share_modes;
+ int oplock_contention_count = 0;
+ share_mode_entry *old_shares = 0;
+ BOOL fcbopen = False;
+ BOOL broke_oplock;
+
+ if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB)
+ fcbopen = True;
+
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+
+ if(num_share_modes == 0)
+ return 0;
+
+ /*
+ * Check if the share modes will give us access.
+ */
+
+ do {
+ share_mode_entry broken_entry;
+
+ broke_oplock = False;
+ *p_all_current_opens_are_level_II = True;
+
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+ /*
+ * By observation of NetBench, oplocks are broken *before* share
+ * modes are checked. This allows a file to be closed by the client
+ * if the share mode would deny access and the client has an oplock.
+ * Check if someone has an oplock on this file. If so we must break
+ * it before continuing.
+ */
+
+ if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) ||
+ (!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) {
+
+ BOOL opb_ret;
+
+ DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", *p_oplock_request, share_entry->op_type, fname, (unsigned int)dev, (double)inode));
+
+ /* Oplock break - unlock to request it. */
+ unlock_share_entry(conn, dev, inode);
+
+ opb_ret = request_oplock_break(share_entry);
+
+ /* Now relock. */
+ lock_share_entry(conn, dev, inode);
+
+ if(opb_ret == False) {
+ DEBUG(0,("open_mode_check: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (double)inode));
+ SAFE_FREE(old_shares);
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ return -1;
+ }
+
+ broke_oplock = True;
+ broken_entry = *share_entry;
+ break;
+
+ } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ *p_all_current_opens_are_level_II = False;
+ }
+
+ /* someone else has a share lock on it, check to see if we can too */
+ if (!check_share_mode(conn, share_entry, share_mode, desired_access,
+ fname, fcbopen, p_flags)) {
+ SAFE_FREE(old_shares);
+ errno = EACCES;
+ return -1;
+ }
+
+ } /* end for */
+
+ if(broke_oplock) {
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ oplock_contention_count++;
+
+ /* Paranoia check that this is no longer an exlusive entry. */
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+ if (share_modes_identical(&broken_entry, share_entry) &&
+ EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) {
+
+ /*
+ * This should not happen. The target left this oplock
+ * as exlusive.... The process *must* be dead....
+ */
+
+ DEBUG(0,("open_mode_check: exlusive oplock left by process %d after break ! For file %s, \
+dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fname, (unsigned int)dev, (double)inode));
+
+ if (process_exists(broken_entry.pid)) {
+ DEBUG(0,("open_mode_check: Existent process %d left active oplock.\n",
+ broken_entry.pid ));
+ }
+
+ if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) {
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ return -1;
+ }
+
+ /*
+ * We must reload the share modes after deleting the
+ * other process's entry.
+ */
+
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ break;
+ }
+ } /* end for paranoia... */
+ } /* end if broke_oplock */
+
+ } while(broke_oplock);
+
+ if(old_shares != 0)
+ SAFE_FREE(old_shares);
+
+ /*
+ * Refuse to grant an oplock in case the contention limit is
+ * reached when going through the lock list multiple times.
+ */
+
+ if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) {
+ *p_oplock_request = 0;
+ DEBUG(4,("open_mode_check: oplock contention = %d. Not granting oplock.\n",
+ oplock_contention_count ));
+ }
+
+ return num_share_modes;
+}
+
+/****************************************************************************
+set a kernel flock on a file for NFS interoperability
+this requires a patch to Linux
+****************************************************************************/
+static void kernel_flock(files_struct *fsp, int deny_mode)
+{
+#if HAVE_KERNEL_SHARE_MODES
+ int kernel_mode = 0;
+ if (deny_mode == DENY_READ) kernel_mode = LOCK_MAND|LOCK_WRITE;
+ else if (deny_mode == DENY_WRITE) kernel_mode = LOCK_MAND|LOCK_READ;
+ else if (deny_mode == DENY_ALL) kernel_mode = LOCK_MAND;
+ if (kernel_mode) flock(fsp->fd, kernel_mode);
+#endif
+ ;;
+}
+
+
+/****************************************************************************
+ Open a file with a share mode. On output from this open we are guarenteeing
+ that
+****************************************************************************/
+files_struct *open_file_shared(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf,
+ int share_mode,int ofun, mode_t mode,int oplock_request,
+ int *Access,int *action)
+{
+ return open_file_shared1(conn, fname, psbuf, 0, share_mode, ofun, mode,
+ oplock_request, Access, action);
+}
+
+/****************************************************************************
+ Open a file with a share mode. On output from this open we are guarenteeing
+ that
+****************************************************************************/
+files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access,
+ int share_mode,int ofun, mode_t mode,int oplock_request,
+ int *Access,int *action)
+{
+ int flags=0;
+ int flags2=0;
+ int deny_mode = GET_DENY_MODE(share_mode);
+ BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode);
+ BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode);
+ BOOL file_existed = VALID_STAT(*psbuf);
+ BOOL fcbopen = False;
+ BOOL def_acl = False;
+ SMB_DEV_T dev = 0;
+ SMB_INO_T inode = 0;
+ int num_share_modes = 0;
+ BOOL all_current_opens_are_level_II = False;
+ BOOL fsp_open = False;
+ files_struct *fsp = NULL;
+ int open_mode=0;
+ uint16 port = 0;
+
+ if (conn->printer) {
+ /* printers are handled completely differently. Most of the passed parameters are
+ ignored */
+ if (Access)
+ *Access = DOS_OPEN_WRONLY;
+ if (action)
+ *action = FILE_WAS_CREATED;
+ return print_fsp_open(conn, fname);
+ }
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(10,("open_file_shared: fname = %s, share_mode = %x, ofun = %x, mode = %o, oplock request = %d\n",
+ fname, share_mode, ofun, (int)mode, oplock_request ));
+
+ if (!check_name(fname,conn)) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ /* ignore any oplock requests if oplocks are disabled */
+ if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) {
+ oplock_request = 0;
+ }
+
+ /* this is for OS/2 EAs - try and say we don't support them */
+ if (strstr(fname,".+,;=[].")) {
+ unix_ERR_class = ERRDOS;
+ /* OS/2 Workplace shell fix may be main code stream in a later release. */
+#if 1 /* OS2_WPS_FIX - Recent versions of OS/2 need this. */
+ unix_ERR_code = ERRcannotopen;
+#else /* OS2_WPS_FIX */
+ unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
+#endif /* OS2_WPS_FIX */
+
+ DEBUG(5,("open_file_shared: OS/2 EA's are not supported.\n"));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if ((GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) && file_existed) {
+ DEBUG(5,("open_file_shared: create new requested for file %s and file already exists.\n",
+ fname ));
+ file_free(fsp);
+ errno = EEXIST;
+ return NULL;
+ }
+
+ if (CAN_WRITE(conn) && (GET_FILE_CREATE_DISPOSITION(ofun) == FILE_CREATE_IF_NOT_EXIST))
+ flags2 |= O_CREAT;
+
+ if (CAN_WRITE(conn) && (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE))
+ flags2 |= O_TRUNC;
+
+ if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL)
+ flags2 |= O_EXCL;
+
+ /* note that we ignore the append flag as
+ append does not mean the same thing under dos and unix */
+
+ switch (GET_OPEN_MODE(share_mode)) {
+ case DOS_OPEN_WRONLY:
+ flags = O_WRONLY;
+ if (desired_access == 0)
+ desired_access = FILE_WRITE_DATA;
+ break;
+ case DOS_OPEN_FCB:
+ fcbopen = True;
+ flags = O_RDWR;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA|FILE_WRITE_DATA;
+ break;
+ case DOS_OPEN_RDWR:
+ flags = O_RDWR;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA|FILE_WRITE_DATA;
+ break;
+ default:
+ flags = O_RDONLY;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA;
+ break;
+ }
+
+#if defined(O_SYNC)
+ if (GET_FILE_SYNC_OPENMODE(share_mode)) {
+ flags2 |= O_SYNC;
+ }
+#endif /* O_SYNC */
+
+ if (flags != O_RDONLY && file_existed &&
+ (!CAN_WRITE(conn) || IS_DOS_READONLY(dos_mode(conn,fname,psbuf)))) {
+ if (!fcbopen) {
+ DEBUG(5,("open_file_shared: read/write access requested for file %s on read only %s\n",
+ fname, !CAN_WRITE(conn) ? "share" : "file" ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ flags = O_RDONLY;
+ }
+
+ if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
+ DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname));
+ file_free(fsp);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (file_existed) {
+
+ dev = psbuf->st_dev;
+ inode = psbuf->st_ino;
+
+ lock_share_entry(conn, dev, inode);
+
+ num_share_modes = open_mode_check(conn, fname, dev, inode,
+ desired_access,
+ share_mode,
+ &flags, &oplock_request, &all_current_opens_are_level_II);
+ if(num_share_modes == -1) {
+
+ /*
+ * This next line is a subtlety we need for MS-Access. If a file open will
+ * fail due to share permissions and also for security (access)
+ * reasons, we need to return the access failed error, not the
+ * share error. This means we must attempt to open the file anyway
+ * in order to get the UNIX access error - even if we're going to
+ * fail the open for share reasons. This is bad, as we're burning
+ * another fd if there are existing locks but there's nothing else
+ * we can do. We also ensure we're not going to create or tuncate
+ * the file as we only want an access decision at this stage. JRA.
+ */
+ fsp_open = open_file(fsp,conn,fname,psbuf,
+ flags|(flags2&~(O_TRUNC|O_CREAT)),mode,desired_access);
+
+ DEBUG(4,("open_file_shared : share_mode deny - calling open_file with \
+flags=0x%X flags2=0x%X mode=0%o returned %d\n",
+ flags,(flags2&~(O_TRUNC|O_CREAT)),(int)mode,(int)fsp_open ));
+
+ unlock_share_entry(conn, dev, inode);
+ if (fsp_open)
+ fd_close(conn, fsp);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * We exit this block with the share entry *locked*.....
+ */
+ }
+
+ /*
+ * Ensure we pay attention to default ACLs on directories if required.
+ */
+
+ if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) &&
+ (def_acl = directory_has_default_acl(conn, parent_dirname(fname))))
+ mode = 0777;
+
+ DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
+ flags,flags2,(int)mode));
+
+ /*
+ * open_file strips any O_TRUNC flags itself.
+ */
+
+ fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,mode,desired_access);
+
+ if (!fsp_open && (flags == O_RDWR) && (errno != ENOENT) && fcbopen) {
+ if((fsp_open = open_file(fsp,conn,fname,psbuf,O_RDONLY,mode,desired_access)) == True)
+ flags = O_RDONLY;
+ }
+
+ if (!fsp_open) {
+ if(file_existed)
+ unlock_share_entry(conn, dev, inode);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * Deal with the race condition where two smbd's detect the file doesn't
+ * exist and do the create at the same time. One of them will win and
+ * set a share mode, the other (ie. this one) should check if the
+ * requested share mode for this create is allowed.
+ */
+
+ if (!file_existed) {
+
+ lock_share_entry_fsp(fsp);
+
+ num_share_modes = open_mode_check(conn, fname, dev, inode,
+ desired_access,
+ share_mode,
+ &flags, &oplock_request, &all_current_opens_are_level_II);
+
+ if(num_share_modes == -1) {
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * If there are any share modes set then the file *did*
+ * exist. Ensure we return the correct value for action.
+ */
+
+ if (num_share_modes > 0)
+ file_existed = True;
+
+ /*
+ * We exit this block with the share entry *locked*.....
+ */
+ }
+
+ /* note that we ignore failure for the following. It is
+ basically a hack for NFS, and NFS will never set one of
+ these only read them. Nobody but Samba can ever set a deny
+ mode and we have already checked our more authoritative
+ locking database for permission to set this deny mode. If
+ the kernel refuses the operations then the kernel is wrong */
+ kernel_flock(fsp, deny_mode);
+
+ /*
+ * At this point onwards, we can guarentee that the share entry
+ * is locked, whether we created the file or not, and that the
+ * deny mode is compatible with all current opens.
+ */
+
+ /*
+ * If requested, truncate the file.
+ */
+
+ if (flags2&O_TRUNC) {
+ /*
+ * We are modifing the file after open - update the stat struct..
+ */
+ if ((truncate_unless_locked(conn,fsp) == -1) || (vfs_fstat(fsp,fsp->fd,psbuf)==-1)) {
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+ }
+
+ switch (flags) {
+ case O_RDONLY:
+ open_mode = DOS_OPEN_RDONLY;
+ break;
+ case O_RDWR:
+ open_mode = DOS_OPEN_RDWR;
+ break;
+ case O_WRONLY:
+ open_mode = DOS_OPEN_WRONLY;
+ break;
+ }
+
+ fsp->share_mode = SET_DENY_MODE(deny_mode) |
+ SET_OPEN_MODE(open_mode) |
+ SET_ALLOW_SHARE_DELETE(allow_share_delete);
+
+ DEBUG(10,("open_file_shared : share_mode = %x\n", fsp->share_mode ));
+
+ if (Access)
+ (*Access) = open_mode;
+
+ if (action) {
+ if (file_existed && !(flags2 & O_TRUNC))
+ *action = FILE_WAS_OPENED;
+ if (!file_existed)
+ *action = FILE_WAS_CREATED;
+ if (file_existed && (flags2 & O_TRUNC))
+ *action = FILE_WAS_OVERWRITTEN;
+ }
+
+ /*
+ * Setup the oplock info in both the shared memory and
+ * file structs.
+ */
+
+ if(oplock_request && (num_share_modes == 0) &&
+ !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp, oplock_request) ) {
+ port = global_oplock_port;
+ } else if (oplock_request && all_current_opens_are_level_II) {
+ port = global_oplock_port;
+ oplock_request = LEVEL_II_OPLOCK;
+ set_file_oplock(fsp, oplock_request);
+ } else {
+ port = 0;
+ oplock_request = 0;
+ }
+
+ set_share_mode(fsp, port, oplock_request);
+
+ if (delete_on_close) {
+ NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
+ /* Remember to delete the mode we just added. */
+ del_share_mode(fsp, NULL);
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+ }
+
+ /*
+ * Take care of inherited ACLs on created files - if default ACL not
+ * selected.
+ */
+
+ if (!file_existed && !def_acl && (conn->vfs_ops.fchmod_acl != NULL)) {
+ int saved_errno = errno; /* We might get ENOSYS in the next call.. */
+ if (conn->vfs_ops.fchmod_acl(fsp, fsp->fd, mode) == -1 && errno == ENOSYS)
+ errno = saved_errno; /* Ignore ENOSYS */
+ }
+
+ unlock_share_entry_fsp(fsp);
+
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Open a file for for write to ensure that we can fchmod it.
+****************************************************************************/
+
+files_struct *open_file_fchmod(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf)
+{
+ files_struct *fsp = NULL;
+ BOOL fsp_open;
+
+ if (!VALID_STAT(*psbuf))
+ return NULL;
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ fsp_open = open_file(fsp,conn,fname,psbuf,O_WRONLY,0,0);
+
+ /*
+ * This is not a user visible file open.
+ * Don't set a share mode and don't increment
+ * the conn->num_files_open.
+ */
+
+ if (!fsp_open) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ return fsp;
+}
+
+/****************************************************************************
+ Close the fchmod file fd - ensure no locks are lost.
+****************************************************************************/
+
+int close_file_fchmod(files_struct *fsp)
+{
+ int ret = fd_close(fsp->conn, fsp);
+ file_free(fsp);
+ return ret;
+}
+
+/****************************************************************************
+ Open a directory from an NT SMB call.
+****************************************************************************/
+
+files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access, int share_mode, int smb_ofun, mode_t unixmode, int *action)
+{
+ extern struct current_user current_user;
+ BOOL got_stat = False;
+ files_struct *fsp = file_new(conn);
+ BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode);
+
+ if(!fsp)
+ return NULL;
+
+ fsp->conn = conn; /* The vfs_fXXX() macros need this. */
+
+ if (VALID_STAT(*psbuf))
+ got_stat = True;
+
+ if (got_stat && (GET_FILE_OPEN_DISPOSITION(smb_ofun) == FILE_EXISTS_FAIL)) {
+ file_free(fsp);
+ errno = EEXIST; /* Setup so correct error is returned to client. */
+ return NULL;
+ }
+
+ if (GET_FILE_CREATE_DISPOSITION(smb_ofun) == FILE_CREATE_IF_NOT_EXIST) {
+
+ if (got_stat) {
+
+ if(!S_ISDIR(psbuf->st_mode)) {
+ DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ *action = FILE_WAS_OPENED;
+
+ } else {
+
+ /*
+ * Try and create the directory.
+ */
+
+ if(!CAN_WRITE(conn)) {
+ DEBUG(2,("open_directory: failing create on read-only share\n"));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+
+ if(vfs_mkdir(conn,fname, unix_mode(conn,aDIR, fname)) < 0) {
+ DEBUG(2,("open_directory: unable to create %s. Error was %s\n",
+ fname, strerror(errno) ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if(vfs_stat(conn,fname, psbuf) != 0) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ *action = FILE_WAS_CREATED;
+
+ }
+ } else {
+
+ /*
+ * Don't create - just check that it *was* a directory.
+ */
+
+ if(!got_stat) {
+ DEBUG(0,("open_directory: unable to stat name = %s. Error was %s\n",
+ fname, strerror(errno) ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if(!S_ISDIR(psbuf->st_mode)) {
+ DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ *action = FILE_WAS_OPENED;
+ }
+
+ DEBUG(5,("open_directory: opening directory %s\n", fname));
+
+ /*
+ * Setup the files_struct for it.
+ */
+
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->size = psbuf->st_size;
+ fsp->vuid = current_user.vuid;
+ fsp->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = False;
+ fsp->can_write = False;
+ fsp->share_mode = share_mode;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = True;
+ fsp->directory_delete_on_close = False;
+ fsp->conn = conn;
+ string_set(&fsp->fsp_name,fname);
+
+ if (delete_on_close) {
+ NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
+ file_free(fsp);
+ return NULL;
+ }
+ }
+ conn->num_files_open++;
+
+ return fsp;
+}
+#if 0
+
+Old code - I have replaced with correct desired_access checking. JRA.
+
+/*******************************************************************
+ Check if the share mode on a file allows it to be deleted or unlinked.
+ Return True if sharing doesn't prevent the operation.
+********************************************************************/
+
+BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op)
+{
+ int i;
+ int ret = False;
+ share_mode_entry *old_shares = 0;
+ int num_share_modes;
+ SMB_STRUCT_STAT sbuf;
+ pid_t pid = sys_getpid();
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+
+ if (vfs_stat(conn,fname,&sbuf) == -1)
+ return(True);
+
+ dev = sbuf.st_dev;
+ inode = sbuf.st_ino;
+
+ lock_share_entry(conn, dev, inode);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+
+ /*
+ * Check if the share modes will give us access.
+ */
+
+ if(num_share_modes != 0) {
+ BOOL broke_oplock;
+
+ do {
+
+ broke_oplock = False;
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+ /*
+ * Break oplocks before checking share modes. See comment in
+ * open_file_shared for details.
+ * Check if someone has an oplock on this file. If so we must
+ * break it before continuing.
+ */
+ if(BATCH_OPLOCK_TYPE(share_entry->op_type)) {
+
+ DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", share_entry->op_type, fname, (unsigned int)dev, (double)inode));
+
+ /* Oplock break.... */
+ unlock_share_entry(conn, dev, inode);
+
+ if(request_oplock_break(share_entry) == False) {
+ DEBUG(0,("check_file_sharing: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (double)inode));
+
+ SAFE_FREE(old_shares);
+ return False;
+ }
+ lock_share_entry(conn, dev, inode);
+ broke_oplock = True;
+ break;
+ }
+
+ /*
+ * If this is a delete request and ALLOW_SHARE_DELETE is set then allow
+ * this to proceed. This takes precedence over share modes.
+ */
+
+ if(!rename_op && GET_ALLOW_SHARE_DELETE(share_entry->share_mode))
+ continue;
+
+ /*
+ * Someone else has a share lock on it, check to see
+ * if we can too.
+ */
+ if ((GET_DENY_MODE(share_entry->share_mode) != DENY_DOS) ||
+ (share_entry->pid != pid))
+ goto free_and_exit;
+
+ } /* end for */
+
+ if(broke_oplock) {
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ }
+ } while(broke_oplock);
+ }
+
+ /*
+ * XXXX exactly what share mode combinations should be allowed for
+ * deleting/renaming?
+ */
+
+ /*
+ * If we got here then either there were no share modes or
+ * all share modes were DENY_DOS and the pid == getpid() or
+ * delete access was requested and all share modes had the
+ * ALLOW_SHARE_DELETE bit set (takes precedence over other
+ * share modes).
+ */
+
+ ret = True;
+
+free_and_exit:
+
+ unlock_share_entry(conn, dev, inode);
+ SAFE_FREE(old_shares);
+ return(ret);
+}
+#endif
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
new file mode 100644
index 0000000000..23606f1d14
--- /dev/null
+++ b/source3/smbd/oplock.c
@@ -0,0 +1,1219 @@
+/*
+ Unix SMB/CIFS implementation.
+ oplock processing
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1998 - 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Oplock ipc UDP socket. */
+static int oplock_sock = -1;
+uint16 global_oplock_port = 0;
+
+/* Current number of oplocks we have outstanding. */
+static int32 exclusive_oplocks_open = 0;
+static int32 level_II_oplocks_open = 0;
+BOOL global_client_failed_oplock_break = False;
+BOOL global_oplock_break = False;
+
+extern int smb_read_error;
+
+static struct kernel_oplocks *koplocks;
+
+static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local);
+
+/****************************************************************************
+ Get the number of current exclusive oplocks.
+****************************************************************************/
+
+int32 get_number_of_exclusive_open_oplocks(void)
+{
+ return exclusive_oplocks_open;
+}
+
+/****************************************************************************
+ Return True if an oplock message is pending.
+****************************************************************************/
+
+BOOL oplock_message_waiting(fd_set *fds)
+{
+ if (koplocks && koplocks->msg_waiting(fds))
+ return True;
+
+ if (FD_ISSET(oplock_sock, fds))
+ return True;
+
+ return False;
+}
+
+/****************************************************************************
+ Read an oplock break message from either the oplock UDP fd or the
+ kernel (if kernel oplocks are supported).
+
+ If timeout is zero then *fds contains the file descriptors that
+ are ready to be read and acted upon. If timeout is non-zero then
+ *fds contains the file descriptors to be selected on for read.
+ The timeout is in milliseconds
+
+****************************************************************************/
+
+BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeout)
+{
+ struct sockaddr_in from;
+ int fromlen = sizeof(from);
+ int32 msg_len = 0;
+
+ smb_read_error = 0;
+
+ if(timeout != 0) {
+ struct timeval to;
+ int selrtn;
+ int maxfd = oplock_sock;
+
+ if (koplocks && koplocks->notification_fd != -1) {
+ FD_SET(koplocks->notification_fd, fds);
+ maxfd = MAX(maxfd, koplocks->notification_fd);
+ }
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ selrtn = sys_select(maxfd+1,fds,NULL,NULL,&to);
+
+ if (selrtn == -1 && errno == EINTR) {
+ /* could be a kernel oplock interrupt */
+ if (koplocks && koplocks->msg_waiting(fds)) {
+ return koplocks->receive_message(fds, buffer, buffer_len);
+ }
+ }
+
+ /* Check if error */
+ if(selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+ }
+
+ if (koplocks && koplocks->msg_waiting(fds)) {
+ return koplocks->receive_message(fds, buffer, buffer_len);
+ }
+
+ if (!FD_ISSET(oplock_sock, fds))
+ return False;
+
+ /*
+ * From here down we deal with the smbd <--> smbd
+ * oplock break protocol only.
+ */
+
+ /*
+ * Read a loopback udp message.
+ */
+ msg_len = recvfrom(oplock_sock, &buffer[OPBRK_CMD_HEADER_LEN],
+ buffer_len - OPBRK_CMD_HEADER_LEN, 0, (struct sockaddr *)&from, &fromlen);
+
+ if(msg_len < 0) {
+ DEBUG(0,("receive_local_message. Error in recvfrom. (%s).\n",strerror(errno)));
+ return False;
+ }
+
+ /* Validate message length. */
+ if(msg_len > (buffer_len - OPBRK_CMD_HEADER_LEN)) {
+ DEBUG(0,("receive_local_message: invalid msg_len (%d) max can be %d\n", msg_len,
+ buffer_len - OPBRK_CMD_HEADER_LEN));
+ return False;
+ }
+
+ /* Validate message from address (must be localhost). */
+ if(from.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ DEBUG(0,("receive_local_message: invalid 'from' address \
+(was %lx should be 127.0.0.1)\n", (long)from.sin_addr.s_addr));
+ return False;
+ }
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,msg_len);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,ntohs(from.sin_port));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an oplock on a file. Always succeeds if kernel oplocks are
+ disabled (just sets flags). Returns True if oplock set.
+****************************************************************************/
+
+BOOL set_file_oplock(files_struct *fsp, int oplock_type)
+{
+ if (koplocks && !koplocks->set_oplock(fsp, oplock_type))
+ return False;
+
+ fsp->oplock_type = oplock_type;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ if (oplock_type == LEVEL_II_OPLOCK)
+ level_II_oplocks_open++;
+ else
+ exclusive_oplocks_open++;
+
+ DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, file_id = %lu, \
+tv_sec = %x, tv_usec = %x\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id,
+ (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec ));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to release an oplock on a file. Decrements oplock count.
+****************************************************************************/
+
+void release_file_oplock(files_struct *fsp)
+{
+ if (koplocks)
+ koplocks->release_oplock(fsp);
+
+ if (fsp->oplock_type == LEVEL_II_OPLOCK)
+ level_II_oplocks_open--;
+ else
+ exclusive_oplocks_open--;
+
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+
+ flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
+}
+
+/****************************************************************************
+ Attempt to downgrade an oplock on a file. Doesn't decrement oplock count.
+****************************************************************************/
+
+static void downgrade_file_oplock(files_struct *fsp)
+{
+ if (koplocks)
+ koplocks->release_oplock(fsp);
+ fsp->oplock_type = LEVEL_II_OPLOCK;
+ exclusive_oplocks_open--;
+ level_II_oplocks_open++;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+}
+
+/****************************************************************************
+ Remove a file oplock. Copes with level II and exclusive.
+ Locks then unlocks the share mode lock. Client can decide to go directly
+ to none even if a "break-to-level II" was sent.
+****************************************************************************/
+
+BOOL remove_oplock(files_struct *fsp, BOOL break_to_none)
+{
+ SMB_DEV_T dev = fsp->dev;
+ SMB_INO_T inode = fsp->inode;
+ BOOL ret = True;
+
+ /* Remove the oplock flag from the sharemode. */
+ if (lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("remove_oplock: failed to lock share entry for file %s\n",
+ fsp->fsp_name ));
+ ret = False;
+ }
+
+ if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT || break_to_none) {
+ /*
+ * Deal with a reply when a break-to-none was sent.
+ */
+
+ if(remove_share_oplock(fsp)==False) {
+ DEBUG(0,("remove_oplock: failed to remove share oplock for file %s fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
+ ret = False;
+ }
+
+ release_file_oplock(fsp);
+ } else {
+ /*
+ * Deal with a reply when a break-to-level II was sent.
+ */
+ if(downgrade_share_oplock(fsp)==False) {
+ DEBUG(0,("remove_oplock: failed to downgrade share oplock for file %s fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
+ ret = False;
+ }
+
+ downgrade_file_oplock(fsp);
+ }
+
+ unlock_share_entry_fsp(fsp);
+ return ret;
+}
+
+/****************************************************************************
+ Setup the listening set of file descriptors for an oplock break
+ message either from the UDP socket or from the kernel. Returns the maximum
+ fd used.
+****************************************************************************/
+
+int setup_oplock_select_set( fd_set *fds)
+{
+ int maxfd = oplock_sock;
+
+ if(oplock_sock == -1)
+ return 0;
+
+ FD_SET(oplock_sock,fds);
+
+ if (koplocks && koplocks->notification_fd != -1) {
+ FD_SET(koplocks->notification_fd, fds);
+ maxfd = MAX(maxfd, koplocks->notification_fd);
+ }
+
+ return maxfd;
+}
+
+/****************************************************************************
+ Process an oplock break message - whether it came from the UDP socket
+ or from the kernel.
+****************************************************************************/
+
+BOOL process_local_message(char *buffer, int buf_size)
+{
+ int32 msg_len;
+ uint16 from_port;
+ char *msg_start;
+ pid_t remotepid;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ unsigned long file_id;
+ uint16 break_cmd_type;
+
+ msg_len = IVAL(buffer,OPBRK_CMD_LEN_OFFSET);
+ from_port = SVAL(buffer,OPBRK_CMD_PORT_OFFSET);
+
+ msg_start = &buffer[OPBRK_CMD_HEADER_LEN];
+
+ DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n",
+ msg_len, from_port));
+
+ /*
+ * Pull the info out of the requesting packet.
+ */
+
+ break_cmd_type = SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET);
+
+ switch(break_cmd_type) {
+ case KERNEL_OPLOCK_BREAK_CMD:
+ if (!koplocks) {
+ DEBUG(0,("unexpected kernel oplock break!\n"));
+ break;
+ }
+ if (!koplocks->parse_message(msg_start, msg_len, &inode, &dev, &file_id)) {
+ DEBUG(0,("kernel oplock break parse failure!\n"));
+ }
+ break;
+
+ case OPLOCK_BREAK_CMD:
+ case LEVEL_II_OPLOCK_BREAK_CMD:
+
+ /* Ensure that the msg length is correct. */
+ if(msg_len != OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id));
+
+ DEBUG(5,("process_local_message: (%s) oplock break request from \
+pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
+ (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+ break;
+
+ /*
+ * Keep this as a debug case - eventually we can remove it.
+ */
+ case 0x8001:
+ DEBUG(0,("process_local_message: Received unsolicited break \
+reply - dumping info.\n"));
+
+ if(msg_len != OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("process_local_message: ubr: incorrect length for reply \
+(was %d, should be %d).\n", (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id));
+
+ DEBUG(0,("process_local_message: unsolicited oplock break reply from \
+pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+
+ return False;
+
+ default:
+ DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n",
+ (unsigned int)SVAL(msg_start,0)));
+ return False;
+ }
+
+ /*
+ * Now actually process the break request.
+ */
+
+ if((exclusive_oplocks_open + level_II_oplocks_open) != 0) {
+ if (oplock_break(dev, inode, file_id, False) == False) {
+ DEBUG(0,("process_local_message: oplock break failed.\n"));
+ return False;
+ }
+ } else {
+ /*
+ * If we have no record of any currently open oplocks,
+ * it's not an error, as a close command may have
+ * just been issued on the file that was oplocked.
+ * Just log a message and return success in this case.
+ */
+ DEBUG(3,("process_local_message: oplock break requested with no outstanding \
+oplocks. Returning success.\n"));
+ }
+
+ /*
+ * Do the appropriate reply - none in the kernel or level II case.
+ */
+
+ if(SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET) == OPLOCK_BREAK_CMD) {
+ struct sockaddr_in toaddr;
+
+ /* Send the message back after OR'ing in the 'REPLY' bit. */
+ SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY);
+
+ memset((char *)&toaddr,'\0',sizeof(toaddr));
+ toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ toaddr.sin_port = htons(from_port);
+ toaddr.sin_family = AF_INET;
+
+ if(sendto( oplock_sock, msg_start, OPLOCK_BREAK_MSG_LEN, 0,
+ (struct sockaddr *)&toaddr, sizeof(toaddr)) < 0) {
+ DEBUG(0,("process_local_message: sendto process %d failed. Errno was %s\n",
+ (int)remotepid, strerror(errno)));
+ return False;
+ }
+
+ DEBUG(5,("process_local_message: oplock break reply sent to \
+pid %d, port %d, for file dev = %x, inode = %.0f, file_id = %lu\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set up an oplock break message.
+****************************************************************************/
+
+static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2)
+{
+ memset(outbuf,'\0',smb_size);
+ set_message(outbuf,8,0,True);
+
+ SCVAL(outbuf,smb_com,SMBlockingX);
+ SSVAL(outbuf,smb_tid,fsp->conn->cnum);
+ SSVAL(outbuf,smb_pid,0xFFFF);
+ SSVAL(outbuf,smb_uid,0);
+ SSVAL(outbuf,smb_mid,0xFFFF);
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ SSVAL(outbuf,smb_vwv2,fsp->fnum);
+ SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(outbuf,smb_vwv3+1,level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+}
+
+/****************************************************************************
+ Function to do the waiting before sending a local break.
+****************************************************************************/
+
+static void wait_before_sending_break(BOOL local_request)
+{
+ extern struct timeval smb_last_time;
+
+ if(local_request) {
+ struct timeval cur_tv;
+ long wait_left = (long)lp_oplock_break_wait_time();
+
+ if (wait_left == 0)
+ return;
+
+ GetTimeOfDay(&cur_tv);
+
+ wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) +
+ ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000);
+
+ if(wait_left > 0) {
+ wait_left = MIN(wait_left, 1000);
+ sys_usleep(wait_left * 1000);
+ }
+ }
+}
+
+/****************************************************************************
+ Ensure that we have a valid oplock.
+****************************************************************************/
+
+static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+{
+ files_struct *fsp = NULL;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: called for dev = %x, inode = %.0f file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id);
+ dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n",
+ exclusive_oplocks_open, level_II_oplocks_open );
+ }
+
+ /*
+ * We need to search the file open table for the
+ * entry containing this dev and inode, and ensure
+ * we have an oplock on it.
+ */
+
+ fsp = file_find_dif(dev, inode, file_id);
+
+ if(fsp == NULL) {
+ /* The file could have been closed in the meantime - return success. */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: cannot find open file with " );
+ dbgtext( "dev = %x, inode = %.0f file_id = %lu", (unsigned int)dev,
+ (double)inode, file_id);
+ dbgtext( "allowing break to succeed.\n" );
+ }
+ return NULL;
+ }
+
+ /* Ensure we have an oplock on the file */
+
+ /*
+ * There is a potential race condition in that an oplock could
+ * have been broken due to another udp request, and yet there are
+ * still oplock break messages being sent in the udp message
+ * queue for this file. So return true if we don't have an oplock,
+ * as we may have just freed it.
+ */
+
+ if(fsp->oplock_type == NO_OPLOCK) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: file %s ", fsp->fsp_name );
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu) has no oplock.\n",
+ (unsigned int)dev, (double)inode, fsp->file_id );
+ dbgtext( "Allowing break to succeed regardless.\n" );
+ }
+ return NULL;
+ }
+
+ return fsp;
+}
+
+/****************************************************************************
+ Process a level II oplock break directly.
+****************************************************************************/
+
+BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
+{
+ extern uint32 global_client_caps;
+ char outbuf[128];
+ BOOL got_lock = False;
+ SMB_DEV_T dev = fsp->dev;
+ SMB_INO_T inode = fsp->inode;
+
+ /*
+ * We can have a level II oplock even if the client is not
+ * level II oplock aware. In this case just remove the
+ * flags and don't send the break-to-none message to
+ * the client.
+ */
+
+ if (global_client_caps & CAP_LEVEL_II_OPLOCKS) {
+ /*
+ * If we are sending an oplock break due to an SMB sent
+ * by our own client we ensure that we wait at leat
+ * lp_oplock_break_wait_time() milliseconds before sending
+ * the packet. Sending the packet sooner can break Win9x
+ * and has reported to cause problems on NT. JRA.
+ */
+
+ wait_before_sending_break(local_request);
+
+ /* Prepare the SMBlockingX message. */
+
+ prepare_break_message( outbuf, fsp, False);
+ if (!send_smb(smbd_server_fd(), outbuf))
+ exit_server("oplock_break_level2: send_smb failed.");
+ }
+
+ /*
+ * Now we must update the shared memory structure to tell
+ * everyone else we no longer have a level II oplock on
+ * this open file. If local_request is true then token is
+ * the existing lock on the shared memory area.
+ */
+
+ if(!local_request && lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name ));
+ } else {
+ got_lock = True;
+ }
+
+ if(remove_share_oplock(fsp)==False) {
+ DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name ));
+ }
+
+ if (!local_request && got_lock)
+ unlock_share_entry_fsp(fsp);
+
+ fsp->oplock_type = NO_OPLOCK;
+ level_II_oplocks_open--;
+
+ if(level_II_oplocks_open < 0) {
+ DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n",
+ level_II_oplocks_open));
+ abort();
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "oplock_break_level2: returning success for " );
+ dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, fsp->file_id );
+ dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open );
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Process an oplock break directly.
+****************************************************************************/
+
+static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local_request)
+{
+ extern uint32 global_client_caps;
+ extern struct current_user current_user;
+ char *inbuf = NULL;
+ char *outbuf = NULL;
+ files_struct *fsp = NULL;
+ time_t start_time;
+ BOOL shutdown_server = False;
+ BOOL oplock_timeout = False;
+ connection_struct *saved_user_conn;
+ connection_struct *saved_fsp_conn;
+ int saved_vuid;
+ pstring saved_dir;
+ int timeout = (OPLOCK_BREAK_TIMEOUT * 1000);
+ pstring file_name;
+ BOOL using_levelII;
+
+ if((fsp = initial_break_processing(dev, inode, file_id)) == NULL)
+ return True;
+
+ /*
+ * Deal with a level II oplock going break to none separately.
+ */
+
+ if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ return oplock_break_level2(fsp, local_request, -1);
+
+ /* Mark the oplock break as sent - we don't want to send twice! */
+ if (fsp->sent_oplock_break) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "oplock_break: ERROR: oplock_break already sent for " );
+ dbgtext( "file %s ", fsp->fsp_name);
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu)\n", (unsigned int)dev, (double)inode, fsp->file_id );
+ }
+
+ /*
+ * We have to fail the open here as we cannot send another oplock break on
+ * this file whilst we are awaiting a response from the client - neither
+ * can we allow another open to succeed while we are waiting for the client.
+ */
+ return False;
+ }
+
+ if(global_oplock_break) {
+ DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n"));
+ abort();
+ }
+
+ /*
+ * Now comes the horrid part. We must send an oplock break to the client,
+ * and then process incoming messages until we get a close or oplock release.
+ * At this point we know we need a new inbuf/outbuf buffer pair.
+ * We cannot use these staticaly as we may recurse into here due to
+ * messages crossing on the wire.
+ */
+
+ if((inbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+ DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
+ return False;
+ }
+
+ if((outbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+ DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
+ SAFE_FREE(inbuf);
+ return False;
+ }
+
+ /*
+ * If we are sending an oplock break due to an SMB sent
+ * by our own client we ensure that we wait at leat
+ * lp_oplock_break_wait_time() milliseconds before sending
+ * the packet. Sending the packet sooner can break Win9x
+ * and has reported to cause problems on NT. JRA.
+ */
+
+ wait_before_sending_break(local_request);
+
+ /* Prepare the SMBlockingX message. */
+
+ if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ !koplocks && /* NOTE: we force levelII off for kernel oplocks - this will change when it is supported */
+ lp_level2_oplocks(SNUM(fsp->conn))) {
+ using_levelII = True;
+ } else {
+ using_levelII = False;
+ }
+
+ prepare_break_message( outbuf, fsp, using_levelII);
+ /* Remember if we just sent a break to level II on this file. */
+ fsp->sent_oplock_break = using_levelII? LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT;
+
+ if (!send_smb(smbd_server_fd(), outbuf))
+ exit_server("oplock_break: send_smb failed.");
+
+ /* We need this in case a readraw crosses on the wire. */
+ global_oplock_break = True;
+
+ /* Process incoming messages. */
+
+ /*
+ * JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT
+ * seconds we should just die....
+ */
+
+ start_time = time(NULL);
+
+ /*
+ * Save the information we need to re-become the
+ * user, then unbecome the user whilst we're doing this.
+ */
+ saved_user_conn = current_user.conn;
+ saved_vuid = current_user.vuid;
+ saved_fsp_conn = fsp->conn;
+ change_to_root_user();
+ vfs_GetWd(saved_fsp_conn,saved_dir);
+ /* Save the chain fnum. */
+ file_chain_save();
+
+ /*
+ * From Charles Hoch <hoch@exemplary.com>. If the break processing
+ * code closes the file (as it often does), then the fsp pointer here
+ * points to free()'d memory. We *must* revalidate fsp each time
+ * around the loop.
+ */
+
+ pstrcpy(file_name, fsp->fsp_name);
+
+ while((fsp = initial_break_processing(dev, inode, file_id)) &&
+ OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ if(receive_smb(smbd_server_fd(),inbuf, timeout) == False) {
+ /*
+ * Die if we got an error.
+ */
+
+ if (smb_read_error == READ_EOF) {
+ DEBUG( 0, ( "oplock_break: end of file from client\n" ) );
+ shutdown_server = True;
+ } else if (smb_read_error == READ_ERROR) {
+ DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
+ shutdown_server = True;
+ } else if (smb_read_error == READ_TIMEOUT) {
+ DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", OPLOCK_BREAK_TIMEOUT ) );
+ oplock_timeout = True;
+ }
+
+ DEBUGADD( 0, ( "oplock_break failed for file %s ", file_name ) );
+ DEBUGADD( 0, ( "(dev = %x, inode = %.0f, file_id = %lu).\n",
+ (unsigned int)dev, (double)inode, file_id));
+
+ break;
+ }
+
+ /*
+ * There are certain SMB requests that we shouldn't allow
+ * to recurse. opens, renames and deletes are the obvious
+ * ones. This is handled in the switch_message() function.
+ * If global_oplock_break is set they will push the packet onto
+ * the pending smb queue and return -1 (no reply).
+ * JRA.
+ */
+
+ process_smb(inbuf, outbuf);
+
+ /*
+ * Die if we go over the time limit.
+ */
+
+ if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "oplock_break: no break received from client " );
+ dbgtext( "within %d seconds.\n", OPLOCK_BREAK_TIMEOUT );
+ dbgtext( "oplock_break failed for file %s ", fsp->fsp_name );
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu).\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+ oplock_timeout = True;
+ break;
+ }
+ }
+
+ /*
+ * Go back to being the user who requested the oplock
+ * break.
+ */
+ if((saved_user_conn != NULL) && (saved_vuid != UID_FIELD_INVALID) && !change_to_user(saved_user_conn, saved_vuid)) {
+ DEBUG( 0, ( "oplock_break: unable to re-become user!" ) );
+ DEBUGADD( 0, ( "Shutting down server\n" ) );
+ close(oplock_sock);
+ exit_server("unable to re-become user");
+ }
+
+ /* Including the directory. */
+ vfs_ChDir(saved_fsp_conn,saved_dir);
+
+ /* Restore the chain fnum. */
+ file_chain_restore();
+
+ /* Free the buffers we've been using to recurse. */
+ SAFE_FREE(inbuf);
+ SAFE_FREE(outbuf);
+
+ /* We need this in case a readraw crossed on the wire. */
+ if(global_oplock_break)
+ global_oplock_break = False;
+
+ /*
+ * If the client timed out then clear the oplock (or go to level II)
+ * and continue. This seems to be what NT does and is better than dropping
+ * the connection.
+ */
+
+ if(oplock_timeout && (fsp = initial_break_processing(dev, inode, file_id)) &&
+ OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("oplock_break: client failure in oplock break in file %s\n", fsp->fsp_name));
+ remove_oplock(fsp,True);
+#if FASCIST_OPLOCK_BACKOFF
+ global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */
+#endif
+ }
+
+ /*
+ * If the client had an error we must die.
+ */
+
+ if(shutdown_server) {
+ DEBUG( 0, ( "oplock_break: client failure in break - " ) );
+ DEBUGADD( 0, ( "shutting down this smbd.\n" ) );
+ close(oplock_sock);
+ exit_server("oplock break failure");
+ }
+
+ /* Santity check - remove this later. JRA */
+ if(exclusive_oplocks_open < 0) {
+ DEBUG(0,("oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR\n", exclusive_oplocks_open));
+ abort();
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "oplock_break: returning success for " );
+ dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open );
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Send an oplock break message to another smbd process. If the oplock is held
+by the local smbd then call the oplock break function directly.
+****************************************************************************/
+
+BOOL request_oplock_break(share_mode_entry *share_entry)
+{
+ char op_break_msg[OPLOCK_BREAK_MSG_LEN];
+ struct sockaddr_in addr_out;
+ pid_t pid = sys_getpid();
+ time_t start_time;
+ int time_left;
+ SMB_DEV_T dev = share_entry->dev;
+ SMB_INO_T inode = share_entry->inode;
+ unsigned long file_id = share_entry->share_file_id;
+
+ if(pid == share_entry->pid) {
+ /* We are breaking our own oplock, make sure it's us. */
+ if(share_entry->op_port != global_oplock_port) {
+ DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
+should be %d\n", (int)pid, share_entry->op_port, global_oplock_port));
+ return False;
+ }
+
+ DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
+
+#if 1 /* JRA PARANOIA TEST.... */
+ {
+ files_struct *fsp = file_find_dif(dev, inode, file_id);
+ if (!fsp) {
+ DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \
+dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n",
+ (unsigned int)dev, (double)inode, file_id ));
+ smb_panic("request_oplock_break: no fsp found for our own oplock\n");
+ }
+ }
+#endif /* END JRA PARANOIA TEST... */
+
+ /* Call oplock break direct. */
+ return oplock_break(dev, inode, file_id, True);
+ }
+
+ /* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */
+
+ if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,LEVEL_II_OPLOCK_BREAK_CMD);
+ } else {
+ SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD);
+ }
+
+ memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid));
+ memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev));
+ memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode));
+ memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id));
+
+ /* Set the address and port. */
+ memset((char *)&addr_out,'\0',sizeof(addr_out));
+ addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr_out.sin_port = htons( share_entry->op_port );
+ addr_out.sin_family = AF_INET;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "request_oplock_break: sending a oplock break message to " );
+ dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+
+ if(sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: failed when sending a oplock " );
+ dbgtext( "break message to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Error was %s\n", strerror(errno) );
+ }
+ return False;
+ }
+
+ /*
+ * If we just sent a message to a level II oplock share entry then
+ * we are done and may return.
+ */
+
+ if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ DEBUG(3,("request_oplock_break: sent break message to level II entry.\n"));
+ return True;
+ }
+
+ /*
+ * Now we must await the oplock broken message coming back
+ * from the target smbd process. Timeout if it fails to
+ * return in (OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) seconds.
+ * While we get messages that aren't ours, loop.
+ */
+
+ start_time = time(NULL);
+ time_left = OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR;
+
+ while(time_left >= 0) {
+ char op_break_reply[OPBRK_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
+ uint16 reply_from_port;
+ char *reply_msg_start;
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(oplock_sock,&fds);
+
+ if (koplocks && koplocks->notification_fd != -1) {
+ FD_SET(koplocks->notification_fd, &fds);
+ }
+
+ if(receive_local_message(&fds, op_break_reply, sizeof(op_break_reply),
+ time_left ? time_left * 1000 : 1) == False) {
+ if(smb_read_error == READ_TIMEOUT) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: no response received to oplock " );
+ dbgtext( "break request to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+
+ /*
+ * This is a hack to make handling of failing clients more robust.
+ * If a oplock break response message is not received in the timeout
+ * period we may assume that the smbd servicing that client holding
+ * the oplock has died and the client changes were lost anyway, so
+ * we should continue to try and open the file.
+ */
+ break;
+ } else {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: error in response received " );
+ dbgtext( "to oplock break request to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Error was (%s).\n", strerror(errno) );
+ }
+ }
+ return False;
+ }
+
+ reply_from_port = SVAL(op_break_reply,OPBRK_CMD_PORT_OFFSET);
+ reply_msg_start = &op_break_reply[OPBRK_CMD_HEADER_LEN];
+
+ /*
+ * Test to see if this is the reply we are awaiting.
+ */
+ if((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & CMD_REPLY) &&
+ ((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & ~CMD_REPLY) == OPLOCK_BREAK_CMD) &&
+ (reply_from_port == share_entry->op_port) &&
+ (memcmp(&reply_msg_start[OPLOCK_BREAK_PID_OFFSET], &op_break_msg[OPLOCK_BREAK_PID_OFFSET],
+ OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET) == 0)) {
+
+ /*
+ * This is the reply we've been waiting for.
+ */
+ break;
+ } else {
+ /*
+ * This is another message - a break request.
+ * Note that both kernel oplock break requests
+ * and UDP inter-smbd oplock break requests will
+ * be processed here.
+ *
+ * Process it to prevent potential deadlock.
+ * Note that the code in switch_message() prevents
+ * us from recursing into here as any SMB requests
+ * we might process that would cause another oplock
+ * break request to be made will be queued.
+ * JRA.
+ */
+
+ process_local_message(op_break_reply, sizeof(op_break_reply));
+ }
+
+ time_left -= (time(NULL) - start_time);
+ }
+
+ DEBUG(3,("request_oplock_break: broke oplock.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to break an oplock on a file (if oplocked).
+ Returns True if the file was closed as a result of
+ the oplock break, False otherwise.
+ Used as a last ditch attempt to free a space in the
+ file table when we have run out.
+****************************************************************************/
+
+BOOL attempt_close_oplocked_file(files_struct *fsp)
+{
+ DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name));
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fd != -1)) {
+ /* Try and break the oplock. */
+ if (oplock_break(fsp->dev, fsp->inode, fsp->file_id, True)) {
+ if(file_find_fsp(fsp) == NULL) /* Did the oplock break close the file ? */
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ This function is called on any file modification or lock request. If a file
+ is level 2 oplocked then it must tell all other level 2 holders to break to none.
+****************************************************************************/
+
+void release_level_2_oplocks_on_change(files_struct *fsp)
+{
+ share_mode_entry *share_list = NULL;
+ pid_t pid = sys_getpid();
+ int token = -1;
+ int num_share_modes = 0;
+ int i;
+
+ /*
+ * If this file is level II oplocked then we need
+ * to grab the shared memory lock and inform all
+ * other files with a level II lock that they need
+ * to flush their read caches. We keep the lock over
+ * the shared memory area whilst doing this.
+ */
+
+ if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ return;
+
+ if (lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("release_level_2_oplocks_on_change: failed to lock share mode entry for file %s.\n", fsp->fsp_name ));
+ }
+
+ num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list);
+
+ DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n",
+ num_share_modes ));
+
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &share_list[i];
+
+ /*
+ * As there could have been multiple writes waiting at the lock_share_entry
+ * gate we may not be the first to enter. Hence the state of the op_types
+ * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II
+ * oplock. It will do no harm to re-send break messages to those smbd's
+ * that are still waiting their turn to remove their LEVEL_II state, and
+ * also no harm to ignore existing NO_OPLOCK states. JRA.
+ */
+
+ DEBUG(10,("release_level_2_oplocks_on_change: share_entry[%i]->op_type == %d\n",
+ i, share_entry->op_type ));
+
+ if (share_entry->op_type == NO_OPLOCK)
+ continue;
+
+ /* Paranoia .... */
+ if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is an exlusive oplock !\n", i ));
+ unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+ abort();
+ }
+
+ /*
+ * Check if this is a file we have open (including the
+ * file we've been called to do write_file on. If so
+ * then break it directly without releasing the lock.
+ */
+
+ if (pid == share_entry->pid) {
+ files_struct *new_fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id);
+
+ /* Paranoia check... */
+ if(new_fsp == NULL) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is not a local file !\n", i ));
+ unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+ abort();
+ }
+
+ DEBUG(10,("release_level_2_oplocks_on_change: breaking our own oplock.\n"));
+
+ oplock_break_level2(new_fsp, True, token);
+
+ } else {
+
+ /*
+ * This is a remote file and so we send an asynchronous
+ * message.
+ */
+
+ DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock.\n"));
+ request_oplock_break(share_entry);
+ }
+ }
+
+ SAFE_FREE(share_list);
+ unlock_share_entry_fsp(fsp);
+
+ /* Paranoia check... */
+ if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name));
+ smb_panic("release_level_2_oplocks_on_change");
+ }
+}
+
+/****************************************************************************
+setup oplocks for this process
+****************************************************************************/
+
+BOOL init_oplocks(void)
+{
+ struct sockaddr_in sock_name;
+ socklen_t len = sizeof(sock_name);
+
+ DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n"));
+
+ /* Open a lookback UDP socket on a random port. */
+ oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK),False);
+ if (oplock_sock == -1) {
+ DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
+address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno)));
+ global_oplock_port = 0;
+ return(False);
+ }
+
+ /* Find out the transient UDP port we have been allocated. */
+ if(getsockname(oplock_sock, (struct sockaddr *)&sock_name, &len)<0) {
+ DEBUG(0,("open_oplock_ipc: Failed to get local UDP port. Error was %s\n",
+ strerror(errno)));
+ close(oplock_sock);
+ oplock_sock = -1;
+ global_oplock_port = 0;
+ return False;
+ }
+ global_oplock_port = ntohs(sock_name.sin_port);
+
+ if (lp_kernel_oplocks()) {
+#if HAVE_KERNEL_OPLOCKS_IRIX
+ koplocks = irix_init_kernel_oplocks();
+#elif HAVE_KERNEL_OPLOCKS_LINUX
+ koplocks = linux_init_kernel_oplocks();
+#endif
+ }
+
+ DEBUG(3,("open_oplock ipc: pid = %d, global_oplock_port = %u\n",
+ (int)sys_getpid(), global_oplock_port));
+
+ return True;
+}
diff --git a/source3/smbd/oplock_irix.c b/source3/smbd/oplock_irix.c
new file mode 100644
index 0000000000..14f6de27c4
--- /dev/null
+++ b/source3/smbd/oplock_irix.c
@@ -0,0 +1,285 @@
+/*
+ Unix SMB/CIFS implementation.
+ IRIX kernel oplock processing
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_OPLOCKS_IRIX
+
+static int oplock_pipe_write = -1;
+static int oplock_pipe_read = -1;
+
+/****************************************************************************
+ Test to see if IRIX kernel oplocks work.
+****************************************************************************/
+
+static BOOL irix_oplocks_available(void)
+{
+ int fd;
+ int pfd[2];
+ pstring tmpname;
+
+ oplock_set_capability(True, False);
+
+ slprintf(tmpname,sizeof(tmpname)-1, "%s/koplock.%d", lp_lockdir(), (int)sys_getpid());
+
+ if(pipe(pfd) != 0) {
+ DEBUG(0,("check_kernel_oplocks: Unable to create pipe. Error was %s\n",
+ strerror(errno) ));
+ return False;
+ }
+
+ if((fd = sys_open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600)) < 0) {
+ DEBUG(0,("check_kernel_oplocks: Unable to open temp test file %s. Error was %s\n",
+ tmpname, strerror(errno) ));
+ unlink( tmpname );
+ close(pfd[0]);
+ close(pfd[1]);
+ return False;
+ }
+
+ unlink(tmpname);
+
+ if(fcntl(fd, F_OPLKREG, pfd[1]) == -1) {
+ DEBUG(0,("check_kernel_oplocks: Kernel oplocks are not available on this machine. \
+Disabling kernel oplock support.\n" ));
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+ return False;
+ }
+
+ if(fcntl(fd, F_OPLKACK, OP_REVOKE) < 0 ) {
+ DEBUG(0,("check_kernel_oplocks: Error when removing kernel oplock. Error was %s. \
+Disabling kernel oplock support.\n", strerror(errno) ));
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+ return False;
+ }
+
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+
+ return True;
+}
+
+/****************************************************************************
+ * Deal with the IRIX kernel <--> smbd
+ * oplock break protocol.
+****************************************************************************/
+
+static BOOL irix_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+{
+ extern int smb_read_error;
+ oplock_stat_t os;
+ char dummy;
+ files_struct *fsp;
+
+ /*
+ * Read one byte of zero to clear the
+ * kernel break notify message.
+ */
+
+ if(read(oplock_pipe_read, &dummy, 1) != 1) {
+ DEBUG(0,("receive_local_message: read of kernel notification failed. \
+Error was %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * Do a query to get the
+ * device and inode of the file that has the break
+ * request outstanding.
+ */
+
+ if(fcntl(oplock_pipe_read, F_OPLKSTAT, &os) < 0) {
+ DEBUG(0,("receive_local_message: fcntl of kernel notification failed. \
+Error was %s.\n", strerror(errno) ));
+ if(errno == EAGAIN) {
+ /*
+ * Duplicate kernel break message - ignore.
+ */
+ memset(buffer, '\0', KERNEL_OPLOCK_BREAK_MSG_LEN);
+ return True;
+ }
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * We only have device and inode info here - we have to guess that this
+ * is the first fsp open with this dev,ino pair.
+ */
+
+ if ((fsp = file_find_di_first((SMB_DEV_T)os.os_dev, (SMB_INO_T)os.os_ino)) == NULL) {
+ DEBUG(0,("receive_local_message: unable to find open file with dev = %x, inode = %.0f\n",
+ (unsigned int)os.os_dev, (double)os.os_ino ));
+ return False;
+ }
+
+ DEBUG(5,("receive_local_message: kernel oplock break request received for \
+dev = %x, inode = %.0f\n, file_id = %ul", (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id ));
+
+ /*
+ * Create a kernel oplock break message.
+ */
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
+
+ buffer += OPBRK_CMD_HEADER_LEN;
+
+ SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
+
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an kernel oplock on a file.
+****************************************************************************/
+
+static BOOL irix_set_kernel_oplock(files_struct *fsp, int oplock_type)
+{
+ if (fcntl(fsp->fd, F_OPLKREG, oplock_pipe_write) == -1) {
+ if(errno != EAGAIN) {
+ DEBUG(0,("set_file_oplock: Unable to get kernel oplock on file %s, dev = %x, \
+inode = %.0f, file_id = %ul. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id,
+ strerror(errno) ));
+ } else {
+ DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f, file_id = %ul. Another process had the file open.\n",
+ fsp->fsp_name, fsp->fd, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id ));
+ }
+ return False;
+ }
+
+ DEBUG(10,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %ul\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Release a kernel oplock on a file.
+****************************************************************************/
+
+static void irix_release_kernel_oplock(files_struct *fsp)
+{
+ if (DEBUGLVL(10)) {
+ /*
+ * Check and print out the current kernel
+ * oplock state of this file.
+ */
+ int state = fcntl(fsp->fd, F_OPLKACK, -1);
+ dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %ul, has kernel \
+oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, state );
+ }
+
+ /*
+ * Remove the kernel oplock on this file.
+ */
+ if(fcntl(fsp->fd, F_OPLKACK, OP_REVOKE) < 0) {
+ if( DEBUGLVL( 0 )) {
+ dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
+ dbgtext("%s, dev = %x, inode = %.0f, file_id = %ul. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, strerror(errno) );
+ }
+ }
+}
+
+/****************************************************************************
+ Parse a kernel oplock message.
+****************************************************************************/
+
+static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len,
+ SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id)
+{
+ /* Ensure that the msg length is correct. */
+ if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
+ memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
+ memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
+
+ DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %ul\n",
+ (unsigned int)*dev, (double)*inode, *file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Set *maxfd to include oplock read pipe.
+****************************************************************************/
+
+static BOOL irix_oplock_msg_waiting(fd_set *fds)
+{
+ if (oplock_pipe_read == -1)
+ return False;
+
+ return FD_ISSET(oplock_pipe_read,fds);
+}
+
+/****************************************************************************
+ Setup kernel oplocks.
+****************************************************************************/
+
+struct kernel_oplocks *irix_init_kernel_oplocks(void)
+{
+ int pfd[2];
+ static struct kernel_oplocks koplocks;
+
+ if (!irix_oplocks_available())
+ return NULL;
+
+ if(pipe(pfd) != 0) {
+ DEBUG(0,("setup_kernel_oplock_pipe: Unable to create pipe. Error was %s\n",
+ strerror(errno) ));
+ return False;
+ }
+
+ oplock_pipe_read = pfd[0];
+ oplock_pipe_write = pfd[1];
+
+ koplocks.receive_message = irix_oplock_receive_message;
+ koplocks.set_oplock = irix_set_kernel_oplock;
+ koplocks.release_oplock = irix_release_kernel_oplock;
+ koplocks.parse_message = irix_kernel_oplock_parse;
+ koplocks.msg_waiting = irix_oplock_msg_waiting;
+ koplocks.notification_fd = oplock_pipe_read;
+
+ return &koplocks;
+}
+#else
+ void oplock_irix_dummy(void) {}
+#endif /* HAVE_KERNEL_OPLOCKS_IRIX */
diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c
new file mode 100644
index 0000000000..d946578380
--- /dev/null
+++ b/source3/smbd/oplock_linux.c
@@ -0,0 +1,300 @@
+/*
+ Unix SMB/CIFS implementation.
+ kernel oplock processing for Linux
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#if HAVE_KERNEL_OPLOCKS_LINUX
+
+static VOLATILE sig_atomic_t signals_received;
+static VOLATILE sig_atomic_t signals_processed;
+static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal */
+
+#ifndef F_SETLEASE
+#define F_SETLEASE 1024
+#endif
+
+#ifndef F_GETLEASE
+#define F_GETLEASE 1025
+#endif
+
+#ifndef CAP_LEASE
+#define CAP_LEASE 28
+#endif
+
+#ifndef RT_SIGNAL_LEASE
+#define RT_SIGNAL_LEASE 33
+#endif
+
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
+/****************************************************************************
+ Handle a LEASE signal, incrementing the signals_received and blocking the signal.
+****************************************************************************/
+
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ BlockSignals(True, sig);
+ fd_pending = (sig_atomic_t)info->si_fd;
+ signals_received++;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Try to gain a linux capability.
+****************************************************************************/
+
+static void set_capability(unsigned capability)
+{
+#ifndef _LINUX_CAPABILITY_VERSION
+#define _LINUX_CAPABILITY_VERSION 0x19980330
+#endif
+ /* these can be removed when they are in glibc headers */
+ struct {
+ uint32 version;
+ int pid;
+ } header;
+ struct {
+ uint32 effective;
+ uint32 permitted;
+ uint32 inheritable;
+ } data;
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+
+ if (capget(&header, &data) == -1) {
+ DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
+ return;
+ }
+
+ data.effective |= (1<<capability);
+
+ if (capset(&header, &data) == -1) {
+ DEBUG(3,("Unable to set %d capability (%s)\n",
+ capability, strerror(errno)));
+ }
+}
+
+/****************************************************************************
+ Call SETLEASE. If we get EACCES then we try setting up the right capability and
+ try again
+****************************************************************************/
+
+static int linux_setlease(int fd, int leasetype)
+{
+ int ret;
+
+ if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
+ DEBUG(3,("Failed to set signal handler for kernel lease\n"));
+ return -1;
+ }
+
+ ret = fcntl(fd, F_SETLEASE, leasetype);
+ if (ret == -1 && errno == EACCES) {
+ set_capability(CAP_LEASE);
+ ret = fcntl(fd, F_SETLEASE, leasetype);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Deal with the Linux kernel <--> smbd
+ * oplock break protocol.
+****************************************************************************/
+
+static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+{
+ BOOL ret = True;
+ struct files_struct *fsp;
+
+ if (signals_received == signals_processed)
+ return False;
+
+ if ((fsp = file_find_fd(fd_pending)) == NULL) {
+ DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd_pending));
+ ret = False;
+ goto out;
+ }
+
+ DEBUG(3,("receive_local_message: kernel oplock break request received for \
+dev = %x, inode = %.0f\n", (unsigned int)fsp->dev, (double)fsp->inode ));
+
+ /*
+ * Create a kernel oplock break message.
+ */
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
+
+ buffer += OPBRK_CMD_HEADER_LEN;
+
+ SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
+
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
+
+ out:
+ /* now we can receive more signals */
+ fd_pending = (sig_atomic_t)-1;
+ signals_processed++;
+ BlockSignals(False, RT_SIGNAL_LEASE);
+
+ return ret;
+}
+
+/****************************************************************************
+ Attempt to set an kernel oplock on a file.
+****************************************************************************/
+
+static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
+{
+ if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
+ DEBUG(3,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f. (%s)\n",
+ fsp->fsp_name, fsp->fd,
+ (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
+ return False;
+ }
+
+ DEBUG(3,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %lu\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Release a kernel oplock on a file.
+****************************************************************************/
+
+static void linux_release_kernel_oplock(files_struct *fsp)
+{
+ if (DEBUGLVL(10)) {
+ /*
+ * Check and print out the current kernel
+ * oplock state of this file.
+ */
+ int state = fcntl(fsp->fd, F_GETLEASE, 0);
+ dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %lu has kernel \
+oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, state );
+ }
+
+ /*
+ * Remove the kernel oplock on this file.
+ */
+ if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
+ if (DEBUGLVL(0)) {
+ dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " );
+ dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, strerror(errno) );
+ }
+ }
+}
+
+/****************************************************************************
+ Parse a kernel oplock message.
+****************************************************************************/
+
+static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode,
+ SMB_DEV_T *dev, unsigned long *file_id)
+{
+ /* Ensure that the msg length is correct. */
+ if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
+ memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
+ memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
+
+ DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)*dev, (double)*inode, *file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ See if a oplock message is waiting.
+****************************************************************************/
+
+static BOOL linux_oplock_msg_waiting(fd_set *fds)
+{
+ return signals_processed != signals_received;
+}
+
+/****************************************************************************
+ See if the kernel supports oplocks.
+****************************************************************************/
+
+static BOOL linux_oplocks_available(void)
+{
+ int fd, ret;
+ fd = open("/dev/null", O_RDONLY);
+ if (fd == -1)
+ return False; /* uggh! */
+ ret = fcntl(fd, F_GETLEASE, 0);
+ close(fd);
+ return ret == F_UNLCK;
+}
+
+/****************************************************************************
+ Setup kernel oplocks.
+****************************************************************************/
+
+struct kernel_oplocks *linux_init_kernel_oplocks(void)
+{
+ static struct kernel_oplocks koplocks;
+ struct sigaction act;
+
+ if (!linux_oplocks_available()) {
+ DEBUG(3,("Linux kernel oplocks not available\n"));
+ return NULL;
+ }
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
+ return NULL;
+ }
+
+ koplocks.receive_message = linux_oplock_receive_message;
+ koplocks.set_oplock = linux_set_kernel_oplock;
+ koplocks.release_oplock = linux_release_kernel_oplock;
+ koplocks.parse_message = linux_kernel_oplock_parse;
+ koplocks.msg_waiting = linux_oplock_msg_waiting;
+ koplocks.notification_fd = -1;
+
+ DEBUG(3,("Linux kernel oplocks enabled\n"));
+
+ return &koplocks;
+}
+#else
+ void oplock_linux_dummy(void) {}
+#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
diff --git a/source3/smbd/password.c b/source3/smbd/password.c
index 87c1fef94c..629157f22d 100644
--- a/source3/smbd/password.c
+++ b/source3/smbd/password.c
@@ -1,8 +1,7 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
Password and authentication handling
- Copyright (C) Andrew Tridgell 1992-1995
+ Copyright (C) Andrew Tridgell 1992-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,1397 +19,595 @@
*/
#include "includes.h"
-#include "loadparm.h"
-
-extern int DEBUGLEVEL;
-extern int Protocol;
/* users from session setup */
static pstring session_users="";
-/* these are kept here to keep the string_combinations function simple */
-static char this_user[100]="";
-static char this_salt[100]="";
-static char this_crypted[100]="";
-
-#ifdef SMB_PASSWD
-/* Data to do lanman1/2 password challenge. */
-static unsigned char saved_challenge[8];
-static BOOL challenge_sent=False;
-
-/*******************************************************************
-Get the next challenge value - no repeats.
-********************************************************************/
-void generate_next_challenge(char *challenge)
-{
- extern void E1(char *,char *,char *);
- static int counter = 0;
- struct timeval tval;
- int v1,v2;
- GetTimeOfDay(&tval);
- v1 = (counter++) + getpid() + tval.tv_sec;
- v2 = (counter++) * getpid() + tval.tv_usec;
- SIVAL(challenge,0,v1);
- SIVAL(challenge,4,v2);
- E1(challenge,"SAMBA",saved_challenge);
- memcpy(challenge,saved_challenge,8);
- challenge_sent = True;
-}
-
-/*******************************************************************
-set the last challenge sent, usually from a password server
-********************************************************************/
-BOOL set_challenge(char *challenge)
-{
- memcpy(saved_challenge,challenge,8);
- challenge_sent = True;
- return(True);
-}
-
-/*******************************************************************
-get the last challenge sent
-********************************************************************/
-BOOL last_challenge(char *challenge)
-{
- if (!challenge_sent) return(False);
- memcpy(challenge,saved_challenge,8);
- return(True);
-}
-#endif
-
/* this holds info on user ids that are already validated for this VC */
-static user_struct *validated_users = NULL;
-static int num_validated_users = 0;
-
-/****************************************************************************
-check if a uid has been validated, and return an index if it has. -1 if not
-****************************************************************************/
-int valid_uid(int uid)
-{
- int i;
- if (uid == -1) return(-1);
-
- for (i=0;i<num_validated_users;i++)
- if (validated_users[i].uid == uid)
- {
- DEBUG(3,("valid uid %d mapped to vuid %d (user=%s)\n",
- uid,i,validated_users[i].name));
- return(i);
- }
- return(-1);
-}
+static user_struct *validated_users;
+static int next_vuid = VUID_OFFSET;
+static int num_validated_vuids;
/****************************************************************************
check if a uid has been validated, and return an pointer to the user_struct
-if it has. NULL if not
-****************************************************************************/
-user_struct *get_valid_user_struct(int uid)
-{
- int vuid = valid_uid(uid);
- if(vuid == -1 || validated_users[vuid].guest)
- return NULL;
- return &validated_users[vuid];
-}
-
-/****************************************************************************
-invalidate a uid
+if it has. NULL if not. vuid is biased by an offset. This allows us to
+tell random client vuid's (normally zero) from valid vuids.
****************************************************************************/
-void invalidate_uid(int uid)
+user_struct *get_valid_user_struct(uint16 vuid)
{
- int i;
- for (i=0;i<num_validated_users;i++)
- if (validated_users[i].uid == uid)
- {
- user_struct *vuser = &validated_users[i];
- vuser->uid = -1;
- vuser->gid = -1;
- vuser->user_ngroups = 0;
- if(vuser->user_groups &&
- (vuser->user_groups != (gid_t *)vuser->user_igroups))
- free(vuser->user_groups);
- vuser->user_groups = NULL;
- if(vuser->user_igroups)
- free(vuser->user_igroups);
- vuser->user_igroups = NULL;
- }
-}
-
+ user_struct *usp;
+ int count=0;
+
+ if (vuid == UID_FIELD_INVALID)
+ return NULL;
+
+ for (usp=validated_users;usp;usp=usp->next,count++) {
+ if (vuid == usp->vuid) {
+ if (count > 10) {
+ DLIST_PROMOTE(validated_users, usp);
+ }
+ return usp;
+ }
+ }
-/****************************************************************************
-return a validated username
-****************************************************************************/
-char *validated_username(int vuid)
-{
- return(validated_users[vuid].name);
+ return NULL;
}
/****************************************************************************
-register a uid/name pair as being valid and that a valid password
-has been given.
+invalidate a uid
****************************************************************************/
-void register_uid(int uid,int gid, char *name,BOOL guest)
+void invalidate_vuid(uint16 vuid)
{
- user_struct *vuser;
-
- if (valid_uid(uid) >= 0)
- return;
- validated_users = (user_struct *)Realloc(validated_users,
- sizeof(user_struct)*
- (num_validated_users+1));
+ user_struct *vuser = get_valid_user_struct(vuid);
- if (!validated_users)
- {
- DEBUG(0,("Failed to realloc users struct!\n"));
- return;
- }
+ if (vuser == NULL)
+ return;
- vuser = &validated_users[num_validated_users];
- vuser->uid = uid;
- vuser->gid = gid;
- vuser->guest = guest;
- strcpy(vuser->name,name);
+ SAFE_FREE(vuser->homedir);
- vuser->user_ngroups = 0;
- vuser->user_groups = NULL;
- vuser->user_igroups = NULL;
+ session_yield(vuser);
- /* Find all the groups this uid is in and store them.
- Used by become_user() */
- setup_groups(name,uid,gid,
- &vuser->user_ngroups,
- &vuser->user_igroups,
- &vuser->user_groups);
+ DLIST_REMOVE(validated_users, vuser);
- DEBUG(3,("uid %d registered to name %s\n",uid,name));
-
- num_validated_users++;
+ SAFE_FREE(vuser->groups);
+ delete_nt_token(&vuser->nt_user_token);
+ SAFE_FREE(vuser);
+ num_validated_vuids--;
}
-
/****************************************************************************
-add a name to the session users list
+invalidate all vuid entries for this process
****************************************************************************/
-void add_session_user(char *user)
+void invalidate_all_vuids(void)
{
- fstring suser;
- StrnCpy(suser,user,sizeof(suser)-1);
-
- if (!Get_Pwnam(suser,True)) return;
+ user_struct *usp, *next=NULL;
- if (suser && *suser && !in_list(suser,session_users,False))
- {
- if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring))
- DEBUG(1,("Too many session users??\n"));
- else
- {
- strcat(session_users," ");
- strcat(session_users,suser);
+ for (usp=validated_users;usp;usp=next) {
+ next = usp->next;
+
+ invalidate_vuid(usp->vuid);
}
- }
}
-
-#ifdef NO_GETSPNAM
-/* a fake shadow password routine which just fills a fake spwd struct
- * with the sp_pwdp field. (sreiz@aie.nl)
- */
-static struct spwd *getspnam(char *username) /* fake shadow password routine */
-{
- FILE *f;
- char line[1024];
- static char pw[20];
- static struct spwd static_spwd;
-
- static_spwd.sp_pwdp=0;
- if (!(f=fopen("/etc/master.passwd", "r")))
- return 0;
- while (fgets(line, 1024, f)) {
- if (!strncmp(line, username, strlen(username)) &&
- line[strlen(username)]==':') { /* found entry */
- char *p, *q;
-
- p=line+strlen(username)+1;
- if ((q=strchr(p, ':'))) {
- *q=0;
- if (q-p+1>20)
- break;
- strcpy(pw, p);
- static_spwd.sp_pwdp=pw;
- }
- break;
- }
- }
- fclose(f);
- if (static_spwd.sp_pwdp)
- return &static_spwd;
- return 0;
-}
-#endif
-
-
-#ifdef OSF1_ENH_SEC
/****************************************************************************
-an enhanced crypt for OSF1
+return a validated username
****************************************************************************/
-static char *osf1_bigcrypt(char *password,char *salt1)
+char *validated_username(uint16 vuid)
{
- static char result[AUTH_MAX_PASSWD_LENGTH] = "";
- char *p1;
- char *p2=password;
- char salt[3];
- int i;
- int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
- if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS)
- parts++;
-
- StrnCpy(salt,salt1,2);
- StrnCpy(result,salt1,2);
-
- for (i=0; i<parts;i++)
- {
- p1 = crypt(p2,salt);
- strcat(result,p1+2);
- StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
- p2 += AUTH_CLEARTEXT_SEG_CHARS;
- }
-
- return(result);
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if (vuser == NULL)
+ return 0;
+ return(vuser->user.unix_name);
}
-#endif
-
/****************************************************************************
-update the enhanced security database. Only relevant for OSF1 at the moment.
+return a validated domain
****************************************************************************/
-static void update_protected_database( char *user, BOOL result)
-{
-#ifdef OSF1_ENH_SEC
- struct pr_passwd *mypasswd;
- time_t starttime;
- long tz;
-
- mypasswd = getprpwnam (user);
- starttime = time (NULL);
- tz = mktime ( localtime ( &starttime ) );
-
- if (result)
- {
- mypasswd->ufld.fd_slogin = tz;
- mypasswd->ufld.fd_nlogins = 0;
-
- putprpwnam(user,mypasswd);
-
- DEBUG(3,("Update protected database for Account %s after succesful connection\n",user));
- }
- else
- {
- mypasswd->ufld.fd_ulogin = tz;
- mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1;
- if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries )
- {
- mypasswd->uflg.fg_lock = 0;
- DEBUG(3,("Account is disabled -- see Account Administrator.\n"));
- }
- putprpwnam ( user , mypasswd );
- DEBUG(3,("Update protected database for Account %s after refusing connection\n",user));
- }
-#else
- DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result)));
-#endif
-}
-
-
-#ifdef AFS_AUTH
-/*******************************************************************
-check on AFS authentication
-********************************************************************/
-static BOOL afs_auth(char *this_user,char *password)
-{
- long password_expires = 0;
- char *reason;
-
- /* For versions of AFS prior to 3.3, this routine has few arguments, */
- /* but since I can't find the old documentation... :-) */
- setpag();
- if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
- this_user,
- (char *) 0, /* instance */
- (char *) 0, /* cell */
- password,
- 0, /* lifetime, default */
- &password_expires, /*days 'til it expires */
- 0, /* spare 2 */
- &reason) == 0)
- return(True);
- return(False);
-}
-#endif
-
-
-#ifdef DFS_AUTH
-
-sec_login_handle_t my_dce_sec_context;
-int dcelogin_atmost_once = 0;
-
-/*******************************************************************
-check on a DCE/DFS authentication
-********************************************************************/
-static BOOL dfs_auth(char *this_user,char *password)
+char *validated_domain(uint16 vuid)
{
- error_status_t err;
- int err2;
- int prterr;
- boolean32 password_reset;
- sec_passwd_rec_t my_dce_password;
- sec_login_auth_src_t auth_src = sec_login_auth_src_network;
- unsigned char dce_errstr[dce_c_error_string_len];
-
- /*
- * We only go for a DCE login context if the given password
- * matches that stored in the local password file..
- * Assumes local passwd file is kept in sync w/ DCE RGY!
- */
-
- if (!strcmp((char *)crypt(password,this_salt),this_crypted) ||
- dcelogin_atmost_once)
- return(False);
-
- if (sec_login_setup_identity(
- (unsigned char *)this_user,
- sec_login_no_flags,
- &my_dce_sec_context,
- &err) == 0)
- {
- dce_error_inq_text(err, dce_errstr, &err2);
- DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
- this_user,dce_errstr));
- return(False);
- }
-
- my_dce_password.version_number = sec_passwd_c_version_none;
- my_dce_password.pepper = NULL;
- my_dce_password.key.key_type = sec_passwd_plain;
- my_dce_password.key.tagged_union.plain = (idl_char *)password;
-
- if (sec_login_valid_and_cert_ident(my_dce_sec_context,
- &my_dce_password,
- &password_reset,
- &auth_src,
- &err) == 0 )
- {
- dce_error_inq_text(err, dce_errstr, &err2);
- DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
- this_user,dce_errstr));
-
- return(False);
- }
-
- sec_login_set_context(my_dce_sec_context, &err);
- if (err != error_status_ok )
- {
- dce_error_inq_text(err, dce_errstr, &err2);
- DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n",
- this_user,dce_errstr));
- sec_login_purge_context(my_dce_sec_context, &err);
- return(False);
- }
- else
- {
- DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
- this_user, getpid()));
- }
-
- dcelogin_atmost_once = 1;
- return (True);
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if (vuser == NULL)
+ return 0;
+ return(vuser->user.domain);
}
-void dfs_unlogin(void)
-{
- error_status_t err;
- int err2;
- unsigned char dce_errstr[dce_c_error_string_len];
-
- sec_login_purge_context(my_dce_sec_context, &err);
- if (err != error_status_ok )
- {
- dce_error_inq_text(err, dce_errstr, &err2);
- DEBUG(0,("DCE purge login context failed for server instance %d: %s\n",
- getpid(), dce_errstr));
- }
-}
-
-#endif
-
-#ifdef LINUX_BIGCRYPT
/****************************************************************************
-an enhanced crypt for Linux to handle password longer than 8 characters
+ Create the SID list for this user.
****************************************************************************/
-static int linux_bigcrypt(char *password,char *salt1, char *crypted)
+
+NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest, NT_USER_TOKEN *sup_tok)
{
-#define LINUX_PASSWORD_SEG_CHARS 8
- char salt[3];
- int i;
-
- StrnCpy(salt,salt1,2);
- crypted +=2;
-
- for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
- char * p = crypt(password,salt) + 2;
- if(strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
- return(0);
- password += LINUX_PASSWORD_SEG_CHARS;
- crypted += strlen(p);
- }
-
- return(1);
-}
-#endif
+ extern DOM_SID global_sid_World;
+ extern DOM_SID global_sid_Network;
+ extern DOM_SID global_sid_Builtin_Guests;
+ extern DOM_SID global_sid_Authenticated_Users;
+ NT_USER_TOKEN *token;
+ DOM_SID *psids;
+ int i, psid_ndx = 0;
+ size_t num_sids = 0;
+ fstring sid_str;
+
+ if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(token);
+
+ /* We always have uid/gid plus World and Network and Authenticated Users or Guest SIDs. */
+ num_sids = 5 + ngroups;
+
+ if (sup_tok && sup_tok->num_sids)
+ num_sids += sup_tok->num_sids;
+
+ if ((token->user_sids = (DOM_SID *)malloc( num_sids*sizeof(DOM_SID))) == NULL) {
+ SAFE_FREE(token);
+ return NULL;
+ }
+ psids = token->user_sids;
-/****************************************************************************
-apply a function to upper/lower case combinations
-of a string and return true if one of them returns true.
-try all combinations with N uppercase letters.
-offset is the first char to try and change (start with 0)
-it assumes the string starts lowercased
-****************************************************************************/
-static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N)
-{
- int len = strlen(s);
- int i;
+ /*
+ * Note - user SID *MUST* be first in token !
+ * se_access_check depends on this.
+ */
-#ifdef PASSWORD_LENGTH
- len = MIN(len,PASSWORD_LENGTH);
-#endif
+ uid_to_sid( &psids[PRIMARY_USER_SID_INDEX], uid);
+ psid_ndx++;
- if (N <= 0 || offset >= len)
- return(fn(s));
-
- for (i=offset;i<(len-(N-1));i++)
- {
- char c = s[i];
- if (!islower(c)) continue;
- s[i] = toupper(c);
- if (string_combinations2(s,i+1,fn,N-1))
- return(True);
- s[i] = c;
- }
- return(False);
-}
+ /*
+ * Primary group SID is second in token. Convention.
+ */
-/****************************************************************************
-apply a function to upper/lower case combinations
-of a string and return true if one of them returns true.
-try all combinations with up to N uppercase letters.
-offset is the first char to try and change (start with 0)
-it assumes the string starts lowercased
-****************************************************************************/
-static BOOL string_combinations(char *s,BOOL (*fn)(),int N)
-{
- int n;
- for (n=1;n<=N;n++)
- if (string_combinations2(s,0,fn,n)) return(True);
- return(False);
-}
+ gid_to_sid( &psids[PRIMARY_GROUP_SID_INDEX], gid);
+ psid_ndx++;
+ /* Now add the group SIDs. */
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] != gid) {
+ gid_to_sid( &psids[psid_ndx++], groups[i]);
+ }
+ }
-/****************************************************************************
-core of password checking routine
-****************************************************************************/
-BOOL password_check(char *password)
-{
-#ifdef AFS_AUTH
- if (afs_auth(this_user,password)) return(True);
-#endif
+ if (sup_tok) {
+ /* Now add the additional SIDs from the supplimentary token. */
+ for (i = 0; i < sup_tok->num_sids; i++)
+ sid_copy( &psids[psid_ndx++], &sup_tok->user_sids[i] );
+ }
-#ifdef DFS_AUTH
- if (dfs_auth(this_user,password)) return(True);
-#endif
+ /*
+ * Finally add the "standard" SIDs.
+ * The only difference between guest and "anonymous" (which we
+ * don't really support) is the addition of Authenticated_Users.
+ */
-#ifdef PWDAUTH
- if (pwdauth(this_user,password) == 0)
- return(True);
-#endif
+ sid_copy( &psids[psid_ndx++], &global_sid_World);
+ sid_copy( &psids[psid_ndx++], &global_sid_Network);
-#ifdef OSF1_ENH_SEC
- return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
-#endif
+ if (is_guest)
+ sid_copy( &psids[psid_ndx++], &global_sid_Builtin_Guests);
+ else
+ sid_copy( &psids[psid_ndx++], &global_sid_Authenticated_Users);
-#ifdef ULTRIX_AUTH
- return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
-#endif
+ token->num_sids = psid_ndx;
-#ifdef LINUX_BIGCRYPT
- return(linux_bigcrypt(password,this_salt,this_crypted));
-#endif
+ /* Dump list of sids in token */
-#ifdef NO_CRYPT
- DEBUG(1,("Warning - no crypt available\n"));
- return(False);
-#else
- return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
-#endif
-}
+ for (i = 0; i < token->num_sids; i++) {
+ DEBUG(5, ("user token sid %s\n",
+ sid_to_string(sid_str, &token->user_sids[i])));
+ }
-#ifdef SMB_PASSWD
-/****************************************************************************
-core of smb password checking routine.
-****************************************************************************/
-BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8)
-{
- /* Finish the encryption of part_passwd. */
- unsigned char p21[21];
- unsigned char p24[24];
-
- if(part_passwd == NULL)
- DEBUG(10,("No password set - allowing access\n"));
- /* No password set - always true ! */
- if(part_passwd == NULL)
- return 1;
-
- memset(p21,'\0',21);
- memcpy(p21,part_passwd,16);
- E_P24(p21, c8, p24);
-#if DEBUG_PASSWORD
- {
- int i;
- DEBUG(100,("Part password (P16) was |"));
- for(i = 0; i < 16; i++)
- DEBUG(100,("%X ", (unsigned char)part_passwd[i]));
- DEBUG(100,("|\n"));
- DEBUG(100,("Password from client was |"));
- for(i = 0; i < 24; i++)
- DEBUG(100,("%X ", (unsigned char)password[i]));
- DEBUG(100,("|\n"));
- DEBUG(100,("Given challenge was |"));
- for(i = 0; i < 8; i++)
- DEBUG(100,("%X ", (unsigned char)c8[i]));
- DEBUG(100,("|\n"));
- DEBUG(100,("Value from encryption was |"));
- for(i = 0; i < 24; i++)
- DEBUG(100,("%X ", (unsigned char)p24[i]));
- DEBUG(100,("|\n"));
- }
-#endif
- return (memcmp(p24, password, 24) == 0);
+ return token;
}
-#endif
/****************************************************************************
-check if a username/password is OK
+register a uid/name pair as being valid and that a valid password
+has been given. vuid is biased by an offset. This allows us to
+tell random client vuid's (normally zero) from valid vuids.
****************************************************************************/
-BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password)
-{
- pstring pass2;
- int level = lp_passwordlevel();
- struct passwd *pass;
-#ifdef SMB_PASSWD
- char challenge[8];
- struct smb_passwd *smb_pass;
- BOOL challenge_done = False;
-#endif
-
- if (password) password[pwlen] = 0;
-#ifdef SMB_PASSWD
- if (pwlen == 24)
- challenge_done = last_challenge(challenge);
-#endif
-
-#if DEBUG_PASSWORD
-#ifdef SMB_PASSWD
- if (challenge_done)
- {
- int i;
- DEBUG(100,("checking user=[%s] pass=[",user));
- for( i = 0; i < 24; i++)
- DEBUG(100,("%0x ", (unsigned char)password[i]));
- DEBUG(100,("]\n"));
- }
- else
-#endif
- DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
-#endif
-
- if (!password)
- return(False);
-
- if (((!*password) || (!pwlen)) && !lp_null_passwords())
- return(False);
+int register_vuid(auth_serversupplied_info *server_info, char *smb_name)
+{
+ user_struct *vuser = NULL;
+ uid_t uid;
+ gid_t gid;
- if (pwd && !user)
- {
- pass = (struct passwd *) pwd;
- user = pass->pw_name;
- }
- else
- pass = Get_Pwnam(user,True);
+ /* Ensure no vuid gets registered in share level security. */
+ if(lp_security() == SEC_SHARE)
+ return UID_FIELD_INVALID;
-#ifdef SMB_PASSWD
+ /* Limit allowed vuids to 16bits - VUID_OFFSET. */
+ if (num_validated_vuids >= 0xFFFF-VUID_OFFSET)
+ return UID_FIELD_INVALID;
- DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done));
+ if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) {
+ DEBUG(0,("Failed to malloc users struct!\n"));
+ return UID_FIELD_INVALID;
+ }
- if((pwlen == 24) && challenge_done)
- {
- DEBUG(4,("Checking SMB password for user %s (l=24)\n",user));
+ ZERO_STRUCTP(vuser);
- if (!pass)
- {
- DEBUG(3,("Couldn't find user %s\n",user));
- return(False);
+ if (!IS_SAM_UNIX_USER(server_info->sam_account)) {
+ DEBUG(0,("Attempted session setup with invalid user. No uid/gid in SAM_ACCOUNT (flags:%x)\n", pdb_get_init_flag(server_info->sam_account)));
+ free(vuser);
+ return UID_FIELD_INVALID;
}
- smb_pass = get_smbpwnam(user);
- if(!smb_pass)
- {
- DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user));
- return(False);
- }
+ uid = pdb_get_uid(server_info->sam_account);
+ gid = pdb_get_gid(server_info->sam_account);
- /* Ensure the uid's match */
- if(smb_pass->smb_userid != pass->pw_uid)
- {
- DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n"));
- return(False);
+ /* Allocate a free vuid. Yes this is a linear search... :-) */
+ while( get_valid_user_struct(next_vuid) != NULL ) {
+ next_vuid++;
+ /* Check for vuid wrap. */
+ if (next_vuid == UID_FIELD_INVALID)
+ next_vuid = VUID_OFFSET;
}
- if(Protocol >= PROTOCOL_NT1 && is_nt_password)
- {
- /* We have the NT MD4 hash challenge available - see if we can
- use it (ie. does it exist in the smbpasswd file).
- */
- if(smb_pass->smb_nt_passwd != NULL)
- {
- DEBUG(4,("Checking NT MD4 password\n"));
- if(smb_password_check(password, smb_pass->smb_nt_passwd, challenge))
- {
- update_protected_database(user,True);
- return(True);
- }
- DEBUG(4,("NT MD4 password check failed\n"));
- return (False);
- }
- }
+ DEBUG(10,("register_vuid: allocated vuid = %u\n", (unsigned int)next_vuid ));
- /* Try against the lanman password */
+ vuser->vuid = next_vuid;
+ vuser->uid = uid;
+ vuser->gid = gid;
+ vuser->guest = server_info->guest;
+ fstrcpy(vuser->user.unix_name, pdb_get_username(server_info->sam_account));
+ fstrcpy(vuser->user.smb_name, smb_name);
+ fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account));
+ fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account));
- if(smb_password_check(password, smb_pass->smb_passwd, challenge))
{
- update_protected_database(user,True);
- return(True);
+ /* Keep the homedir handy */
+ const char *homedir = pdb_get_homedir(server_info->sam_account);
+ if (homedir) {
+ vuser->homedir = smb_xstrdup(homedir);
+ }
}
- DEBUG(3,("Error smb_password_check failed\n"));
- }
-#endif
-
- DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
-
- if (!pass)
- {
- DEBUG(3,("Couldn't find user %s\n",user));
- return(False);
- }
-
-#ifdef SHADOW_PWD
- {
- struct spwd *spass;
+ memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->session_key));
- /* many shadow systems require you to be root to get the password,
- in most cases this should already be the case when this
- function is called, except perhaps for IPC password changing
- requests */
+ DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n",
+ (unsigned int)vuser->uid,
+ (unsigned int)vuser->gid,
+ vuser->user.unix_name, vuser->user.smb_name, vuser->user.domain, vuser->guest ));
- spass = getspnam(pass->pw_name);
- if (spass && spass->sp_pwdp)
- pass->pw_passwd = spass->sp_pwdp;
- }
-#endif
+ DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,vuser->user.full_name));
-#ifdef SecureWare
- {
- struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
- if (pr_pw && pr_pw->ufld.fd_encrypt)
- pass->pw_passwd = pr_pw->ufld.fd_encrypt;
- }
-#endif
+ vuser->n_groups = 0;
+ vuser->groups = NULL;
-#ifdef HPUX_10_TRUSTED
- {
- struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
- if (pr_pw && pr_pw->ufld.fd_encrypt)
- pass->pw_passwd = pr_pw->ufld.fd_encrypt;
- }
-#endif
+ /* Find all the groups this uid is in and store them.
+ Used by change_to_user() */
+ initialise_groups(vuser->user.unix_name, vuser->uid, vuser->gid);
+ get_current_groups( &vuser->n_groups, &vuser->groups);
-#ifdef OSF1_ENH_SEC
- {
- struct pr_passwd *mypasswd;
- DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",user));
- mypasswd = getprpwnam (user);
- if ( mypasswd )
- {
- strcpy(pass->pw_name,mypasswd->ufld.fd_name);
- strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
- }
- else
- {
- DEBUG(5,("No entry for user %s in protected database !\n",user));
- return(False);
- }
- }
-#endif
+ if (server_info->ptok)
+ add_supplementary_nt_login_groups(&vuser->n_groups, &vuser->groups, &server_info->ptok);
-#ifdef ULTRIX_AUTH
- {
- AUTHORIZATION *ap = getauthuid( pass->pw_uid );
- if (ap)
- {
- strcpy( pass->pw_passwd, ap->a_password );
- endauthent();
- }
- }
-#endif
-
- /* extract relevant info */
- strcpy(this_user,pass->pw_name);
- strcpy(this_salt,pass->pw_passwd);
- strcpy(this_crypted,pass->pw_passwd);
-
- if (!*this_crypted) {
- if (!lp_null_passwords()) {
- DEBUG(2,("Disallowing access to %s due to null password\n",this_user));
- return(False);
- }
-#ifndef PWDAUTH
- if (!*password) {
- DEBUG(3,("Allowing access to %s with null password\n",this_user));
- return(True);
- }
-#endif
- }
-
- /* try it as it came to us */
- if (password_check(password))
- {
- update_protected_database(user,True);
- return(True);
- }
-
- /* if the password was given to us with mixed case then we don't
- need to proceed as we know it hasn't been case modified by the
- client */
- if (strhasupper(password) && strhaslower(password))
- return(False);
-
- /* make a copy of it */
- StrnCpy(pass2,password,sizeof(pstring)-1);
-
- /* try all lowercase */
- strlower(password);
- if (password_check(password))
- {
- update_protected_database(user,True);
- return(True);
- }
-
- /* give up? */
- if(level < 1)
- {
- update_protected_database(user,False);
+ /* Create an NT_USER_TOKEN struct for this user. */
+ vuser->nt_user_token = create_nt_token(vuser->uid, vuser->gid, vuser->n_groups, vuser->groups, vuser->guest, server_info->ptok);
- /* restore it */
- strcpy(password,pass2);
+ DEBUG(3,("uid %d registered to name %s\n",(int)vuser->uid,vuser->user.unix_name));
- return(False);
- }
+ next_vuid++;
+ num_validated_vuids++;
- /* last chance - all combinations of up to level chars upper! */
- strlower(password);
+ DLIST_ADD(validated_users, vuser);
- if (string_combinations(password,password_check,level))
- {
- update_protected_database(user,True);
- return(True);
- }
+ if (!session_claim(vuser)) {
+ DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid));
+ invalidate_vuid(vuser->vuid);
+ return -1;
+ }
- update_protected_database(user,False);
-
- /* restore it */
- strcpy(password,pass2);
-
- return(False);
+ /* Register a home dir service for this user */
+ if ((!vuser->guest) && vuser->homedir && *(vuser->homedir)
+ && (lp_servicenumber(vuser->user.unix_name) < 0)) {
+ add_home_service(vuser->user.unix_name, vuser->homedir);
+ }
+
+ return vuser->vuid;
}
-
/****************************************************************************
-check if a username is valid
+add a name to the session users list
****************************************************************************/
-BOOL user_ok(char *user,int snum)
+void add_session_user(char *user)
{
- pstring valid, invalid;
- BOOL ret;
-
- StrnCpy(valid, lp_valid_users(snum), sizeof(pstring));
- StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring));
-
- string_sub(valid,"%S",lp_servicename(snum));
- string_sub(invalid,"%S",lp_servicename(snum));
-
- ret = !user_in_list(user,invalid);
-
- if (ret && valid && *valid)
- ret = user_in_list(user,valid);
-
- if (ret && lp_onlyuser(snum)) {
- char *user_list = lp_username(snum);
- string_sub(user_list,"%S",lp_servicename(snum));
- ret = user_in_list(user,user_list);
- }
-
- return(ret);
-}
-
-
+ fstring suser;
+ StrnCpy(suser,user,sizeof(suser)-1);
+ if (!Get_Pwnam_Modify(suser)) return;
-/****************************************************************************
-validate a group username entry. Return the username or NULL
-****************************************************************************/
-static char *validate_group(char *group,char *password,int pwlen,int snum)
-{
-#ifdef NETGROUP
- {
- char *host, *user, *domain;
- setnetgrent(group);
- while (getnetgrent(&host, &user, &domain)) {
- if (user) {
- if (user_ok(user, snum) &&
- password_ok(user,password,pwlen,NULL,False)) {
- endnetgrent();
- return(user);
- }
- }
- }
- endnetgrent();
- }
-#endif
-
-#if HAVE_GETGRNAM
- {
- struct group *gptr = (struct group *)getgrnam(group);
- char **member;
- if (gptr)
- {
- member = gptr->gr_mem;
- while (member && *member)
- {
- static fstring name;
- strcpy(name,*member);
- if (user_ok(name,snum) &&
- password_ok(name,password,pwlen,NULL,False))
- return(&name[0]);
- member++;
- }
-#ifdef GROUP_CHECK_PWENT
+ if (suser && *suser && !in_list(suser,session_users,False))
+ {
+ if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring))
+ DEBUG(1,("Too many session users??\n"));
+ else
{
- struct passwd *pwd;
- static fstring tm;
-
- setpwent ();
- while (pwd = getpwent ()) {
- if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) {
- /* This Entry have PASSWORD and same GID then check pwd */
- if (password_ok(NULL, password, pwlen, pwd,False)) {
- strcpy(tm, pwd->pw_name);
- endpwent ();
- return tm;
- }
- }
- }
- endpwent ();
+ pstrcat(session_users," ");
+ pstrcat(session_users,suser);
}
-#endif /* GROUP_CHECK_PWENT */
- }
- }
-#endif
- return(NULL);
+ }
}
-
/****************************************************************************
-check for authority to login to a service with a given username/password
+check if a username is valid
****************************************************************************/
-BOOL authorise_login(int snum,char *user,char *password, int pwlen,
- BOOL *guest,BOOL *force,int vuid)
+BOOL user_ok(char *user,int snum)
{
- BOOL ok = False;
-
- *guest = False;
-
-#if DEBUG_PASSWORD
- DEBUG(100,("checking authorisation on user=%s pass=%s\n",user,password));
-#endif
-
- /* there are several possabilities:
- 1) login as the given user with given password
- 2) login as a previously registered username with the given password
- 3) login as a session list username with the given password
- 4) login as a previously validated user/password pair
- 5) login as the "user =" user with given password
- 6) login as the "user =" user with no password (guest connection)
- 7) login as guest user with no password
+ char **valid, **invalid;
+ BOOL ret;
- if the service is guest_only then steps 1 to 5 are skipped
- */
+ valid = invalid = NULL;
+ ret = True;
- if (GUEST_ONLY(snum)) *force = True;
-
- if (!(GUEST_ONLY(snum) && GUEST_OK(snum)))
- {
-
- /* check the given username and password */
- if (!ok && (*user) && user_ok(user,snum)) {
- ok = password_ok(user,password, pwlen, NULL, False);
- if (ok) DEBUG(3,("ACCEPTED: given username password ok\n"));
- }
-
- /* check for a previously registered guest username */
- if (!ok && (vuid >= 0) && validated_users[vuid].guest) {
- if (user_ok(validated_users[vuid].name,snum) &&
- password_ok(validated_users[vuid].name, password, pwlen, NULL, False)) {
- strcpy(user, validated_users[vuid].name);
- validated_users[vuid].guest = False;
- DEBUG(3,("ACCEPTED: given password with registered user %s\n", user));
- ok = True;
+ if (lp_invalid_users(snum)) {
+ lp_list_copy(&invalid, lp_invalid_users(snum));
+ if (invalid && lp_list_substitute(invalid, "%S", lp_servicename(snum))) {
+ ret = !user_in_list(user, invalid);
+ }
}
- }
-
+ if (invalid) lp_list_free (&invalid);
- /* now check the list of session users */
- if (!ok)
- {
- char *auser;
- char *user_list = strdup(session_users);
- if (!user_list) return(False);
-
- for (auser=strtok(user_list,LIST_SEP);
- !ok && auser;
- auser = strtok(NULL,LIST_SEP))
- {
- fstring user2;
- strcpy(user2,auser);
- if (!user_ok(user2,snum)) continue;
-
- if (password_ok(user2,password, pwlen, NULL, False)) {
- ok = True;
- strcpy(user,user2);
- DEBUG(3,("ACCEPTED: session list username and given password ok\n"));
- }
- }
- free(user_list);
+ if (ret && lp_valid_users(snum)) {
+ lp_list_copy(&valid, lp_valid_users(snum));
+ if (valid && lp_list_substitute(valid, "%S", lp_servicename(snum))) {
+ ret = user_in_list(user,valid);
+ }
}
+ if (valid) lp_list_free (&valid);
- /* check for a previously validated username/password pair */
- if (!ok && !lp_revalidate(snum) &&
- (vuid >= 0) && !validated_users[vuid].guest &&
- user_ok(validated_users[vuid].name,snum)) {
- strcpy(user,validated_users[vuid].name);
- *guest = False;
- DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n"));
- ok = True;
- }
-
- /* check for a rhosts entry */
- if (!ok && user_ok(user,snum) && check_hosts_equiv(user)) {
- ok = True;
- DEBUG(3,("ACCEPTED: hosts equiv or rhosts entry\n"));
- }
-
- /* check the user= fields and the given password */
- if (!ok && lp_username(snum)) {
- char *auser;
- pstring user_list;
- StrnCpy(user_list,lp_username(snum),sizeof(pstring));
-
- string_sub(user_list,"%S",lp_servicename(snum));
-
- for (auser=strtok(user_list,LIST_SEP);
- auser && !ok;
- auser = strtok(NULL,LIST_SEP))
- {
- if (*auser == '@')
- {
- auser = validate_group(auser+1,password,pwlen,snum);
- if (auser)
- {
- ok = True;
- strcpy(user,auser);
- DEBUG(3,("ACCEPTED: group username and given password ok\n"));
- }
- }
- else
- {
- fstring user2;
- strcpy(user2,auser);
- if (user_ok(user2,snum) &&
- password_ok(user2,password,pwlen,NULL, False))
- {
- ok = True;
- strcpy(user,user2);
- DEBUG(3,("ACCEPTED: user list username and given password ok\n"));
- }
- }
- }
- }
- } /* not guest only */
-
- /* check for a normal guest connection */
- if (!ok && GUEST_OK(snum))
- {
- fstring guestname;
- StrnCpy(guestname,lp_guestaccount(snum),sizeof(guestname)-1);
- if (Get_Pwnam(guestname,True))
- {
- strcpy(user,guestname);
- ok = True;
- DEBUG(3,("ACCEPTED: guest account and guest ok\n"));
+ if (ret && lp_onlyuser(snum)) {
+ char **user_list = lp_list_make (lp_username(snum));
+ if (user_list && lp_list_substitute(user_list, "%S", lp_servicename(snum))) {
+ ret = user_in_list(user, user_list);
+ }
+ if (user_list) lp_list_free (&user_list);
}
- else
- DEBUG(0,("Invalid guest account %s??\n",guestname));
- *guest = True;
- *force = True;
- }
- if (ok && !user_ok(user,snum))
- {
- DEBUG(0,("rejected invalid user %s\n",user));
- ok = False;
- }
-
- return(ok);
+ return(ret);
}
-
/****************************************************************************
-read the a hosts.equiv or .rhosts file and check if it
-allows this user from this machine
+validate a group username entry. Return the username or NULL
****************************************************************************/
-static BOOL check_user_equiv(char *user, char *remote, char *equiv_file)
+static char *validate_group(char *group, DATA_BLOB password,int snum)
{
- pstring buf;
- int plus_allowed = 1;
- char *file_host;
- char *file_user;
- FILE *fp = fopen(equiv_file, "r");
- DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
- if (! fp) return False;
- while(fgets(buf, sizeof(buf), fp))
- {
- trim_string(buf," "," ");
-
- if (buf[0] != '#' && buf[0] != '\n')
- {
- BOOL is_group = False;
- int plus = 1;
- char *bp = buf;
- if (strcmp(buf, "NO_PLUS\n") == 0)
- {
- DEBUG(6, ("check_user_equiv NO_PLUS\n"));
- plus_allowed = 0;
- }
- else {
- if (buf[0] == '+')
+#ifdef HAVE_NETGROUP
{
- bp++;
- if (*bp == '\n' && plus_allowed)
- {
- /* a bare plus means everbody allowed */
- DEBUG(6, ("check_user_equiv everybody allowed\n"));
- fclose(fp);
- return True;
- }
- }
- else if (buf[0] == '-')
- {
- bp++;
- plus = 0;
- }
- if (*bp == '@')
- {
- is_group = True;
- bp++;
+ char *host, *user, *domain;
+ setnetgrent(group);
+ while (getnetgrent(&host, &user, &domain)) {
+ if (user) {
+ if (user_ok(user, snum) &&
+ password_ok(user,password)) {
+ endnetgrent();
+ return(user);
+ }
+ }
+ }
+ endnetgrent();
}
- file_host = strtok(bp, " \t\n");
- file_user = strtok(NULL, " \t\n");
- DEBUG(7, ("check_user_equiv %s %s\n", file_host, file_user));
- if (file_host && *file_host)
- {
- BOOL host_ok = False;
-
-#ifdef NETGROUP
- /* THIS IS UNTESTED!! */
- if (is_group)
- {
- static char *mydomain = NULL;
- if (!mydomain)
- yp_get_default_domain(&mydomain);
- if (mydomain && innetgr(remote,file_host,user,mydomain))
- host_ok = True;
- }
-#else
- if (is_group)
- {
- DEBUG(1,("Netgroups not configured - add -DNETGROUP and recompile\n"));
- continue;
- }
#endif
-
- /* is it this host */
- /* the fact that remote has come from a call of gethostbyaddr
- * means that it may have the fully qualified domain name
- * so we could look up the file version to get it into
- * a canonical form, but I would rather just type it
- * in full in the equiv file
- */
- if (!host_ok && !is_group && strequal(remote, file_host))
- host_ok = True;
-
- if (!host_ok)
- continue;
-
- /* is it this user */
- if (file_user == 0 || strequal(user, file_user))
- {
- fclose(fp);
- DEBUG(5, ("check_user_equiv matched %s%s %s\n",
- (plus ? "+" : "-"), file_host,
- (file_user ? file_user : "")));
- return (plus ? True : False);
- }
- }
- }
- }
- }
- fclose(fp);
- return False;
-}
-
-
-/****************************************************************************
-check for a possible hosts equiv or rhosts entry for the user
-****************************************************************************/
-BOOL check_hosts_equiv(char *user)
-{
- char *fname = NULL;
- pstring rhostsfile;
- struct passwd *pass = Get_Pwnam(user,True);
-
- extern struct from_host Client_info;
- extern int Client;
-
- if (!pass)
- return(False);
-
- fromhost(Client,&Client_info);
-
- fname = lp_hosts_equiv();
-
- /* note: don't allow hosts.equiv on root */
- if (fname && *fname && (pass->pw_uid != 0))
- {
- if (check_user_equiv(user,Client_info.name,fname))
- return(True);
- }
- if (lp_use_rhosts())
- {
- char *home = get_home_dir(user);
- if (home)
+#ifdef HAVE_GETGRENT
{
- sprintf(rhostsfile, "%s/.rhosts", home);
- if (check_user_equiv(user,Client_info.name,rhostsfile))
- return(True);
- }
- }
+ struct group *gptr;
+ setgrent();
+ while ((gptr = (struct group *)getgrent())) {
+ if (strequal(gptr->gr_name,group))
+ break;
+ }
- return(False);
+ /*
+ * As user_ok can recurse doing a getgrent(), we must
+ * copy the member list into a pstring on the stack before
+ * use. Bug pointed out by leon@eatworms.swmed.edu.
+ */
+
+ if (gptr) {
+ pstring member_list;
+ char *member;
+ size_t copied_len = 0;
+ int i;
+
+ *member_list = '\0';
+ member = member_list;
+
+ for(i = 0; gptr->gr_mem && gptr->gr_mem[i]; i++) {
+ size_t member_len = strlen(gptr->gr_mem[i]) + 1;
+ if( copied_len + member_len < sizeof(pstring)) {
+
+ DEBUG(10,("validate_group: = gr_mem = %s\n", gptr->gr_mem[i]));
+
+ safe_strcpy(member, gptr->gr_mem[i], sizeof(pstring) - copied_len - 1);
+ copied_len += member_len;
+ member += copied_len;
+ } else {
+ *member = '\0';
+ }
+ }
+
+ endgrent();
+
+ member = member_list;
+ while (*member) {
+ static fstring name;
+ fstrcpy(name,member);
+ if (user_ok(name,snum) &&
+ password_ok(name,password)) {
+ endgrent();
+ return(&name[0]);
+ }
+
+ DEBUG(10,("validate_group = member = %s\n", member));
+
+ member += strlen(member) + 1;
+ }
+ } else {
+ endgrent();
+ return NULL;
+ }
+ }
+#endif
+ return(NULL);
}
-
-static int password_client = -1;
-static fstring pserver;
-
/****************************************************************************
-attempted support for server level security
+ Check for authority to login to a service with a given username/password.
+ Note this is *NOT* used when logging on using sessionsetup_and_X.
****************************************************************************/
-BOOL server_cryptkey(char *buf)
-{
- pstring inbuf,outbuf;
- fstring pass_protocol;
- extern fstring remote_machine;
- char *p;
- int len;
- fstring desthost;
- struct in_addr dest_ip;
- extern struct in_addr myip;
- int port = 139;
- BOOL ret;
-
- if (password_client >= 0)
- close(password_client);
- password_client = -1;
-
- if (Protocol < PROTOCOL_NT1) {
- strcpy(pass_protocol,"LM1.2X002");
- } else {
- strcpy(pass_protocol,"NT LM 0.12");
- }
-
- bzero(inbuf,sizeof(inbuf));
- bzero(outbuf,sizeof(outbuf));
-
- for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) {
- strcpy(desthost,p);
- standard_sub_basic(desthost);
- strupper(desthost);
-
- dest_ip = *interpret_addr2(desthost);
- if (zero_ip(dest_ip)) {
- DEBUG(1,("Can't resolve address for %s\n",p));
- continue;
- }
- if (memcmp(&dest_ip,&myip,sizeof(dest_ip)) == 0) {
- DEBUG(1,("Password server loop - disabling password server %s\n",p));
- continue;
- }
-
- password_client = open_socket_out(SOCK_STREAM, &dest_ip, port);
- if (password_client >= 0) {
- DEBUG(3,("connected to password server %s\n",p));
- StrnCpy(pserver,p,sizeof(pserver)-1);
- break;
- }
- }
-
- if (password_client < 0) {
- DEBUG(1,("password server not available\n"));
- return(False);
- }
+BOOL authorise_login(int snum,char *user, DATA_BLOB password,
+ BOOL *guest,BOOL *force,uint16 vuid)
+{
+ BOOL ok = False;
+ user_struct *vuser = get_valid_user_struct(vuid);
+#if DEBUG_PASSWORD
+ DEBUG(100,("authorise_login: checking authorisation on user=%s pass=%s vuid=%d\n",
+ user,password.data, vuid));
+#endif
- /* send a session request (RFC 8002) */
+ *guest = False;
+
+ if (GUEST_ONLY(snum))
+ *force = True;
- /* put in the destination name */
- len = 4;
- p = outbuf+len;
- name_mangle(desthost,p,' ');
- len += name_len(p);
+ if (!GUEST_ONLY(snum) && (lp_security() > SEC_SHARE)) {
- /* and my name */
- p = outbuf+len;
- name_mangle(remote_machine,p,' ');
- len += name_len(p);
+ /*
+ * We should just use the given vuid from a sessionsetup_and_X.
+ */
- _smb_setlen(outbuf,len);
- CVAL(outbuf,0) = 0x81;
+ if (!vuser) {
+ DEBUG(1,("authorise_login: refusing user '%s' with no session setup\n", user));
+ return False;
+ }
- send_smb(password_client,outbuf);
- receive_smb(password_client,inbuf,5000);
+ if ((!vuser->guest && user_ok(vuser->user.unix_name,snum)) ||
+ (vuser->guest && GUEST_OK(snum))) {
+ fstrcpy(user,vuser->user.unix_name);
+ *guest = vuser->guest;
+ DEBUG(3,("authorise_login: ACCEPTED: validated based on vuid as %sguest \
+(user=%s)\n", vuser->guest ? "" : "non-", user));
+ return True;
+ }
+ }
- if (CVAL(inbuf,0) != 0x82) {
- DEBUG(1,("%s rejected the session\n",pserver));
- close(password_client); password_client = -1;
- return(False);
- }
-
- DEBUG(3,("got session\n"));
-
- bzero(outbuf,smb_size);
-
- /* setup the protocol string */
- set_message(outbuf,0,strlen(pass_protocol)+2,True);
- p = smb_buf(outbuf);
- *p++ = 2;
- strcpy(p,pass_protocol);
-
- CVAL(outbuf,smb_com) = SMBnegprot;
- CVAL(outbuf,smb_flg) = 0x8;
- SSVAL(outbuf,smb_flg2,0x1);
-
- send_smb(password_client,outbuf);
- ret = receive_smb(password_client,inbuf,5000);
-
- if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) {
- DEBUG(1,("%s rejected the protocol\n",pserver));
- close(password_client); password_client= -1;
- return(False);
- }
-
- if (!(CVAL(inbuf,smb_vwv1) & 1)) {
- DEBUG(1,("%s isn't in user level security mode\n",pserver));
- close(password_client); password_client= -1;
- return(False);
- }
-
- memcpy(buf,inbuf,smb_len(inbuf)+4);
-
- DEBUG(3,("password server OK\n"));
-
- return(True);
-}
-
-/****************************************************************************
-attempted support for server level security
-****************************************************************************/
-BOOL server_validate(char *buf)
-{
- pstring inbuf,outbuf;
- BOOL ret;
-
- if (password_client < 0) {
- DEBUG(1,("%s not connected\n",pserver));
- return(False);
- }
-
- bzero(inbuf,sizeof(inbuf));
- memcpy(outbuf,buf,sizeof(outbuf));
-
- /* send a session setup command */
- CVAL(outbuf,smb_flg) = 0x8;
- SSVAL(outbuf,smb_flg2,0x1);
- CVAL(outbuf,smb_vwv0) = 0xFF;
-
- set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False);
-
- SCVAL(inbuf,smb_rcls,1);
+ /* there are several possibilities:
+ 1) login as the given user with given password
+ 2) login as a previously registered username with the given password
+ 3) login as a session list username with the given password
+ 4) login as a previously validated user/password pair
+ 5) login as the "user =" user with given password
+ 6) login as the "user =" user with no password (guest connection)
+ 7) login as guest user with no password
+
+ if the service is guest_only then steps 1 to 5 are skipped
+ */
+
+ if (!(GUEST_ONLY(snum) && GUEST_OK(snum))) {
+ /* check for a previously registered guest username */
+ if (!ok && (vuser != 0) && vuser->guest) {
+ if (user_ok(vuser->user.unix_name,snum) &&
+ password_ok(vuser->user.unix_name, password)) {
+ fstrcpy(user, vuser->user.unix_name);
+ *guest = False;
+ DEBUG(3,("authorise_login: ACCEPTED: given password with registered user %s\n", user));
+ ok = True;
+ }
+ }
- send_smb(password_client,outbuf);
- ret = receive_smb(password_client,inbuf,5000);
+ /* now check the list of session users */
+ if (!ok) {
+ char *auser;
+ char *user_list = strdup(session_users);
+ if (!user_list)
+ return(False);
+
+ for (auser=strtok(user_list,LIST_SEP); !ok && auser;
+ auser = strtok(NULL,LIST_SEP)) {
+ fstring user2;
+ fstrcpy(user2,auser);
+ if (!user_ok(user2,snum))
+ continue;
+
+ if (password_ok(user2,password)) {
+ ok = True;
+ fstrcpy(user,user2);
+ DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \
+and given password ok\n", user));
+ }
+ }
+
+ SAFE_FREE(user_list);
+ }
- if (!ret || CVAL(inbuf,smb_rcls) != 0) {
- DEBUG(1,("password server %s rejected the password\n",pserver));
- return(False);
- }
+ /* check for a previously validated username/password pair */
+ if (!ok && (lp_security() > SEC_SHARE) && (vuser != 0) && !vuser->guest &&
+ user_ok(vuser->user.unix_name,snum)) {
+ fstrcpy(user,vuser->user.unix_name);
+ *guest = False;
+ DEBUG(3,("authorise_login: ACCEPTED: validated uid (%s) as non-guest\n",
+ user));
+ ok = True;
+ }
- /* if logged in as guest then reject */
- if ((SVAL(inbuf,smb_vwv2) & 1) != 0) {
- DEBUG(1,("password server %s gave us guest only\n",pserver));
- return(False);
- }
+ /* check the user= fields and the given password */
+ if (!ok && lp_username(snum)) {
+ char *auser;
+ pstring user_list;
+ StrnCpy(user_list,lp_username(snum),sizeof(pstring));
- DEBUG(3,("password server %s accepted the password\n",pserver));
+ pstring_sub(user_list,"%S",lp_servicename(snum));
+
+ for (auser=strtok(user_list,LIST_SEP); auser && !ok;
+ auser = strtok(NULL,LIST_SEP)) {
+ if (*auser == '@') {
+ auser = validate_group(auser+1,password,snum);
+ if (auser) {
+ ok = True;
+ fstrcpy(user,auser);
+ DEBUG(3,("authorise_login: ACCEPTED: group username \
+and given password ok (%s)\n", user));
+ }
+ } else {
+ fstring user2;
+ fstrcpy(user2,auser);
+ if (user_ok(user2,snum) && password_ok(user2,password)) {
+ ok = True;
+ fstrcpy(user,user2);
+ DEBUG(3,("authorise_login: ACCEPTED: user list username \
+and given password ok (%s)\n", user));
+ }
+ }
+ }
+ }
+ } /* not guest only */
+
+ /* check for a normal guest connection */
+ if (!ok && GUEST_OK(snum)) {
+ fstring guestname;
+ StrnCpy(guestname,lp_guestaccount(),sizeof(guestname)-1);
+ if (Get_Pwnam(guestname)) {
+ fstrcpy(user,guestname);
+ ok = True;
+ DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n",
+ user));
+ } else {
+ DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname));
+ }
+ *guest = True;
+ }
-#ifndef KEEP_PASSWORD_SERVER_OPEN
- close(password_client); password_client= -1;
-#endif
+ if (ok && !user_ok(user,snum)) {
+ DEBUG(0,("authorise_login: rejected invalid user %s\n",user));
+ ok = False;
+ }
- return(True);
+ return(ok);
}
-
-
diff --git a/source3/smbd/pipes.c b/source3/smbd/pipes.c
new file mode 100644
index 0000000000..6c1e6efa73
--- /dev/null
+++ b/source3/smbd/pipes.c
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ Pipe SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 1997-1998.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ This file handles reply_ calls on named pipes that the server
+ makes to handle specific protocols
+*/
+
+
+#include "includes.h"
+
+#define PIPE "\\PIPE\\"
+#define PIPELEN strlen(PIPE)
+
+extern struct pipe_id_info pipe_names[];
+
+/****************************************************************************
+ reply to an open and X on a named pipe
+
+ This code is basically stolen from reply_open_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+int reply_open_pipe_and_X(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ pstring pipe_name;
+ uint16 vuid = SVAL(inbuf, smb_uid);
+ smb_np_struct *p;
+ int smb_ofun = SVAL(inbuf,smb_vwv8);
+ int size=0,fmode=0,mtime=0,rmode=0;
+ int i;
+
+ /* XXXX we need to handle passed times, sattr and flags */
+ srvstr_pull(inbuf, pipe_name, smb_buf(inbuf), sizeof(pipe_name), -1, STR_TERMINATE);
+
+ /* If the name doesn't start \PIPE\ then this is directed */
+ /* at a mailslot or something we really, really don't understand, */
+ /* not just something we really don't understand. */
+ if ( strncmp(pipe_name,PIPE,PIPELEN) != 0 )
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ DEBUG(4,("Opening pipe %s.\n", pipe_name));
+
+ /* See if it is one we want to handle. */
+ for( i = 0; pipe_names[i].client_pipe ; i++ )
+ if( strequal(pipe_name,pipe_names[i].client_pipe) )
+ break;
+
+ if (pipe_names[i].client_pipe == NULL)
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ /* Strip \PIPE\ off the name. */
+ pstrcpy(fname, pipe_name + PIPELEN);
+
+
+#if 0
+ /*
+ * Hack for NT printers... JRA.
+ */
+ if(should_fail_next_srvsvc_open(fname))
+ return(ERROR(ERRSRV,ERRaccess));
+#endif
+
+ /* Known pipes arrive with DIR attribs. Remove it so a regular file */
+ /* can be opened and add it in after the open. */
+ DEBUG(3,("Known pipe %s opening.\n",fname));
+ smb_ofun |= FILE_CREATE_IF_NOT_EXIST;
+
+ p = open_rpc_pipe_p(fname, conn, vuid);
+ if (!p) return(ERROR_DOS(ERRSRV,ERRnofids));
+
+ /* Prepare the reply */
+ set_message(outbuf,15,0,True);
+
+ /* Mark the opened file as an existing named pipe in message mode. */
+ SSVAL(outbuf,smb_vwv9,2);
+ SSVAL(outbuf,smb_vwv10,0xc700);
+
+ if (rmode == 2) {
+ DEBUG(4,("Resetting open result to open from create.\n"));
+ rmode = 1;
+ }
+
+ SSVAL(outbuf,smb_vwv2, p->pnum);
+ SSVAL(outbuf,smb_vwv3,fmode);
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,size);
+ SSVAL(outbuf,smb_vwv8,rmode);
+ SSVAL(outbuf,smb_vwv11,0x0001);
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a write on a pipe
+****************************************************************************/
+int reply_pipe_write(char *inbuf,char *outbuf,int length,int dum_bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0);
+ size_t numtowrite = SVAL(inbuf,smb_vwv1);
+ int nwritten;
+ int outsize;
+ char *data;
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ data = smb_buf(inbuf) + 3;
+
+ if (numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_to_pipe(p, data, numtowrite);
+
+ if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0))
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("write-IPC pnum=%04x nwritten=%d\n",
+ p->pnum, nwritten));
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+
+ This code is basically stolen from reply_write_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+
+int reply_pipe_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2);
+ size_t numtowrite = SVAL(inbuf,smb_vwv10);
+ int nwritten = -1;
+ int smb_doff = SVAL(inbuf, smb_vwv11);
+ BOOL pipe_start_message_raw = ((SVAL(inbuf, smb_vwv7) & (PIPE_START_MESSAGE|PIPE_RAW_MODE)) ==
+ (PIPE_START_MESSAGE|PIPE_RAW_MODE));
+ char *data;
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ data = smb_base(inbuf) + smb_doff;
+
+ if (numtowrite == 0)
+ nwritten = 0;
+ else {
+ if(pipe_start_message_raw) {
+ /*
+ * For the start of a message in named pipe byte mode,
+ * the first two bytes are a length-of-pdu field. Ignore
+ * them (we don't trust the client. JRA.
+ */
+ if(numtowrite < 2) {
+ DEBUG(0,("reply_pipe_write_and_X: start of message set and not enough data sent.(%u)\n",
+ (unsigned int)numtowrite ));
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ data += 2;
+ numtowrite -= 2;
+ }
+ nwritten = write_to_pipe(p, data, numtowrite);
+ }
+
+ if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0))
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+
+ set_message(outbuf,6,0,True);
+
+ nwritten = (pipe_start_message_raw ? nwritten + 2 : nwritten);
+ SSVAL(outbuf,smb_vwv2,nwritten);
+
+ DEBUG(3,("writeX-IPC pnum=%04x nwritten=%d\n",
+ p->pnum, nwritten));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a read and X
+
+ This code is basically stolen from reply_read_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+int reply_pipe_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2);
+ int smb_maxcnt = SVAL(inbuf,smb_vwv5);
+ int smb_mincnt = SVAL(inbuf,smb_vwv6);
+ int nread = -1;
+ char *data;
+ BOOL unused;
+
+ /* we don't use the offset given to use for pipe reads. This
+ is deliberate, instead we always return the next lump of
+ data on the pipe */
+#if 0
+ uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+#endif
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ set_message(outbuf,12,0,True);
+ data = smb_buf(outbuf);
+
+ nread = read_from_pipe(p, data, smb_maxcnt, &unused);
+
+ if (nread < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ SSVAL(outbuf,smb_vwv5,nread);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,nread);
+
+ DEBUG(3,("readX-IPC pnum=%04x min=%d max=%d nread=%d\n",
+ p->pnum, smb_mincnt, smb_maxcnt, nread));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a close
+****************************************************************************/
+int reply_pipe_close(connection_struct *conn, char *inbuf,char *outbuf)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0);
+ int outsize = set_message(outbuf,0,0,True);
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ DEBUG(5,("reply_pipe_close: pnum:%x\n", p->pnum));
+
+ if (!close_rpc_pipe_hnd(p))
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ return(outsize);
+}
diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c
new file mode 100644
index 0000000000..9c8835214f
--- /dev/null
+++ b/source3/smbd/posix_acls.c
@@ -0,0 +1,2313 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT Security Descriptor / Unix permission conversion.
+ Copyright (C) Jeremy Allison 1994-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Data structures representing the internal ACE format.
+****************************************************************************/
+
+enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
+enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
+
+typedef union posix_id {
+ uid_t uid;
+ gid_t gid;
+ int world;
+} posix_id;
+
+typedef struct canon_ace {
+ struct canon_ace *next, *prev;
+ SMB_ACL_TAG_T type;
+ mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
+ DOM_SID trustee;
+ enum ace_owner owner_type;
+ enum ace_attribute attr;
+ posix_id unix_ug;
+} canon_ace;
+
+#define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
+
+/****************************************************************************
+ Functions to manipulate the internal ACE format.
+****************************************************************************/
+
+/****************************************************************************
+ Count a linked list of canonical ACE entries.
+****************************************************************************/
+
+static size_t count_canon_ace_list( canon_ace *list_head )
+{
+ size_t count = 0;
+ canon_ace *ace;
+
+ for (ace = list_head; ace; ace = ace->next)
+ count++;
+
+ return count;
+}
+
+/****************************************************************************
+ Free a linked list of canonical ACE entries.
+****************************************************************************/
+
+static void free_canon_ace_list( canon_ace *list_head )
+{
+ while (list_head) {
+ canon_ace *old_head = list_head;
+ DLIST_REMOVE(list_head, list_head);
+ SAFE_FREE(old_head);
+ }
+}
+
+/****************************************************************************
+ Function to duplicate a canon_ace entry.
+****************************************************************************/
+
+static canon_ace *dup_canon_ace( canon_ace *src_ace)
+{
+ canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
+
+ if (dst_ace == NULL)
+ return NULL;
+
+ *dst_ace = *src_ace;
+ dst_ace->prev = dst_ace->next = NULL;
+ return dst_ace;
+}
+
+/****************************************************************************
+ Print out a canon ace.
+****************************************************************************/
+
+static void print_canon_ace(canon_ace *pace, int num)
+{
+ fstring str;
+
+ dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
+ dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
+ if (pace->owner_type == UID_ACE) {
+ char *u_name = uidtoname(pace->unix_ug.uid);
+ dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name);
+ } else if (pace->owner_type == GID_ACE) {
+ char *g_name = gidtoname(pace->unix_ug.gid);
+ dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name);
+ } else
+ dbgtext( "other ");
+ switch (pace->type) {
+ case SMB_ACL_USER:
+ dbgtext( "SMB_ACL_USER ");
+ break;
+ case SMB_ACL_USER_OBJ:
+ dbgtext( "SMB_ACL_USER_OBJ ");
+ break;
+ case SMB_ACL_GROUP:
+ dbgtext( "SMB_ACL_GROUP ");
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ dbgtext( "SMB_ACL_GROUP_OBJ ");
+ break;
+ case SMB_ACL_OTHER:
+ dbgtext( "SMB_ACL_OTHER ");
+ break;
+ }
+ dbgtext( "perms ");
+ dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
+ dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
+ dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
+}
+
+/****************************************************************************
+ Print out a canon ace list.
+****************************************************************************/
+
+static void print_canon_ace_list(const char *name, canon_ace *ace_list)
+{
+ int count = 0;
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext( "print_canon_ace_list: %s\n", name );
+ for (;ace_list; ace_list = ace_list->next, count++)
+ print_canon_ace(ace_list, count );
+ }
+}
+
+/****************************************************************************
+ Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ mode_t ret = 0;
+
+ ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
+ ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
+ ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
+
+ return ret;
+}
+
+/****************************************************************************
+ Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
+{
+ mode_t ret = 0;
+
+ if (mode & r_mask)
+ ret |= S_IRUSR;
+ if (mode & w_mask)
+ ret |= S_IWUSR;
+ if (mode & x_mask)
+ ret |= S_IXUSR;
+
+ return ret;
+}
+
+/****************************************************************************
+ Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
+ an SMB_ACL_PERMSET_T.
+****************************************************************************/
+
+static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
+{
+ if (conn->vfs_ops.sys_acl_clear_perms(conn, *p_permset) == -1)
+ return -1;
+ if (mode & S_IRUSR) {
+ if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_READ) == -1)
+ return -1;
+ }
+ if (mode & S_IWUSR) {
+ if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_WRITE) == -1)
+ return -1;
+ }
+ if (mode & S_IXUSR) {
+ if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
+ return -1;
+ }
+ return 0;
+}
+/****************************************************************************
+ Function to create owner and group SIDs from a SMB_STRUCT_STAT.
+****************************************************************************/
+
+static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
+{
+ uid_to_sid( powner_sid, psbuf->st_uid );
+ gid_to_sid( pgroup_sid, psbuf->st_gid );
+}
+
+/****************************************************************************
+ Merge aces with a common sid - if both are allow or deny, OR the permissions together and
+ delete the second one. If the first is deny, mask the permissions off and delete the allow
+ if the permissions become zero, delete the deny if the permissions are non zero.
+****************************************************************************/
+
+static void merge_aces( canon_ace **pp_list_head )
+{
+ canon_ace *list_head = *pp_list_head;
+ canon_ace *curr_ace_outer;
+ canon_ace *curr_ace_outer_next;
+
+ /*
+ * First, merge allow entries with identical SIDs, and deny entries
+ * with identical SIDs.
+ */
+
+ for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ canon_ace *curr_ace;
+ canon_ace *curr_ace_next;
+
+ curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->attr == curr_ace_outer->attr)) {
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("merge_aces: Merging ACE's\n");
+ print_canon_ace( curr_ace_outer, 0);
+ print_canon_ace( curr_ace, 0);
+ }
+
+ /* Merge two allow or two deny ACE's. */
+
+ curr_ace_outer->perms |= curr_ace->perms;
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+ }
+ }
+ }
+
+ /*
+ * Now go through and mask off allow permissions with deny permissions.
+ * We can delete either the allow or deny here as we know that each SID
+ * appears only once in the list.
+ */
+
+ for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ canon_ace *curr_ace;
+ canon_ace *curr_ace_next;
+
+ curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ /*
+ * Subtract ACE's with different entries. Due to the ordering constraints
+ * we've put on the ACL, we know the deny must be the first one.
+ */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("merge_aces: Masking ACE's\n");
+ print_canon_ace( curr_ace_outer, 0);
+ print_canon_ace( curr_ace, 0);
+ }
+
+ curr_ace->perms &= ~curr_ace_outer->perms;
+
+ if (curr_ace->perms == 0) {
+
+ /*
+ * The deny overrides the allow. Remove the allow.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+
+ } else {
+
+ /*
+ * Even after removing permissions, there
+ * are still allow permissions - delete the deny.
+ * It is safe to delete the deny here,
+ * as we are guarenteed by the deny first
+ * ordering that all the deny entries for
+ * this SID have already been merged into one
+ * before we can get to an allow ace.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace_outer);
+ SAFE_FREE(curr_ace_outer);
+ break;
+ }
+ }
+
+ } /* end for curr_ace */
+ } /* end for curr_ace_outer */
+
+ /* We may have modified the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Map canon_ace perms to permission bits NT.
+ The attr element is not used here - we only process deny entries on set,
+ not get. Deny entries are implicit on get with ace->perms = 0.
+****************************************************************************/
+
+static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
+{
+ SEC_ACCESS sa;
+ uint32 nt_mask = 0;
+
+ *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+
+ if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
+ nt_mask = UNIX_ACCESS_RWX;
+ } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
+ nt_mask = UNIX_ACCESS_NONE;
+ } else {
+ nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
+ nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
+ nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
+ }
+
+ DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
+ (unsigned int)ace->perms, (unsigned int)nt_mask ));
+
+ init_sec_access(&sa,nt_mask);
+ return sa;
+}
+
+/****************************************************************************
+ Map NT perms to a UNIX mode_t.
+****************************************************************************/
+
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
+
+static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
+{
+ mode_t mode = 0;
+
+ switch(type) {
+ case S_IRUSR:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRUSR|S_IWUSR|S_IXUSR;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
+ }
+ break;
+ case S_IRGRP:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRGRP|S_IWGRP|S_IXGRP;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
+ }
+ break;
+ case S_IROTH:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IROTH|S_IWOTH|S_IXOTH;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
+ }
+ break;
+ }
+
+ return mode;
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into a UNIX owner and group.
+****************************************************************************/
+
+static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+{
+ DOM_SID owner_sid;
+ DOM_SID grp_sid;
+ enum SID_NAME_USE sid_type;
+
+ *puser = (uid_t)-1;
+ *pgrp = (gid_t)-1;
+
+ if(security_info_sent == 0) {
+ DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
+ return True;
+ }
+
+ /*
+ * Validate the owner and group SID's.
+ */
+
+ memset(&owner_sid, '\0', sizeof(owner_sid));
+ memset(&grp_sid, '\0', sizeof(grp_sid));
+
+ DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
+
+ /*
+ * Don't immediately fail if the owner sid cannot be validated.
+ * This may be a group chown only set.
+ */
+
+ if (security_info_sent & OWNER_SECURITY_INFORMATION) {
+ sid_copy(&owner_sid, psd->owner_sid);
+ if (!sid_to_uid( &owner_sid, puser, &sid_type)) {
+ DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
+ sid_string_static(&owner_sid)));
+ return False;
+ }
+ }
+
+ /*
+ * Don't immediately fail if the group sid cannot be validated.
+ * This may be an owner chown only set.
+ */
+
+ if (security_info_sent & GROUP_SECURITY_INFORMATION) {
+ sid_copy(&grp_sid, psd->grp_sid);
+ if (!sid_to_gid( &grp_sid, pgrp, &sid_type)) {
+ DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
+ return False;
+ }
+ }
+
+ DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Ensure the enforced permissions for this share apply.
+****************************************************************************/
+
+static mode_t apply_default_perms(files_struct *fsp, mode_t perms, mode_t type)
+{
+ int snum = SNUM(fsp->conn);
+ mode_t and_bits = (mode_t)0;
+ mode_t or_bits = (mode_t)0;
+
+ /* Get the initial bits to apply. */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ /* Now bounce them into the S_USR space. */
+ switch(type) {
+ case S_IRUSR:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+ break;
+ case S_IRGRP:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+ break;
+ case S_IROTH:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
+ break;
+ }
+
+ return ((perms & and_bits)|or_bits);
+}
+
+/****************************************************************************
+ A well formed POSIX file or default ACL has at least 3 entries, a
+ SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
+ In addition, the owner must always have at least read access.
+ When using this call on get_acl, the pst struct is valid and contains
+ the mode of the file. When using this call on set_acl, the pst struct has
+ been modified to have a mode containing the default for this file or directory
+ type.
+****************************************************************************/
+
+static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
+ files_struct *fsp,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ SMB_STRUCT_STAT *pst,
+ BOOL setting_acl)
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *pace;
+ BOOL got_user = False;
+ BOOL got_grp = False;
+ BOOL got_other = False;
+
+ for (pace = *pp_ace; pace; pace = pace->next) {
+ if (pace->type == SMB_ACL_USER_OBJ) {
+
+ if (setting_acl) {
+ /* Ensure owner has read access. */
+ pace->perms |= S_IRUSR;
+ if (fsp->is_directory)
+ pace->perms |= (S_IWUSR|S_IXUSR);
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ pace->perms = apply_default_perms(fsp, pace->perms, S_IRUSR);
+ }
+
+ got_user = True;
+ } else if (pace->type == SMB_ACL_GROUP_OBJ) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl)
+ pace->perms = apply_default_perms(fsp, pace->perms, S_IRGRP);
+ got_grp = True;
+ } else if (pace->type == SMB_ACL_OTHER) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl)
+ pace->perms = apply_default_perms(fsp, pace->perms, S_IROTH);
+ got_other = True;
+ }
+ }
+
+ if (!got_user) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_USER_OBJ;
+ pace->owner_type = UID_ACE;
+ pace->unix_ug.uid = pst->st_uid;
+ pace->trustee = *pfile_owner_sid;
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ pace->attr = ALLOW_ACE;
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_grp) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_GROUP_OBJ;
+ pace->owner_type = GID_ACE;
+ pace->unix_ug.uid = pst->st_gid;
+ pace->trustee = *pfile_grp_sid;
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ pace->attr = ALLOW_ACE;
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_other) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_OTHER;
+ pace->owner_type = WORLD_ACE;
+ pace->unix_ug.world = -1;
+ pace->trustee = global_sid_World;
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
+ pace->attr = ALLOW_ACE;
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into two canonical ace lists.
+****************************************************************************/
+
+static BOOL create_canon_ace_lists(files_struct *fsp,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ canon_ace **ppfile_ace, canon_ace **ppdir_ace,
+ SEC_ACL *dacl)
+{
+ extern DOM_SID global_sid_World;
+ extern struct generic_mapping file_generic_mapping;
+ BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+ canon_ace *tmp_ace = NULL;
+ canon_ace *current_ace = NULL;
+ BOOL got_dir_allow = False;
+ BOOL got_file_allow = False;
+ int i, j;
+
+ *ppfile_ace = NULL;
+ *ppdir_ace = NULL;
+
+ /*
+ * Convert the incoming ACL into a more regular form.
+ */
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa = &dacl->ace[i];
+
+ if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
+ DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
+ return False;
+ }
+
+ /*
+ * The security mask may be UNIX_ACCESS_NONE which should map into
+ * no permissions (we overload the WRITE_OWNER bit for this) or it
+ * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
+ * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
+ */
+
+ /*
+ * Convert GENERIC bits to specific bits.
+ */
+
+ se_map_generic(&psa->info.mask, &file_generic_mapping);
+
+ psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
+
+ if(psa->info.mask != UNIX_ACCESS_NONE)
+ psa->info.mask &= ~UNIX_ACCESS_NONE;
+ }
+
+ /*
+ * Deal with the fact that NT 4.x re-writes the canonical format
+ * that we return for default ACLs. If a directory ACE is identical
+ * to a inherited directory ACE then NT changes the bits so that the
+ * first ACE is set to OI|IO and the second ACE for this SID is set
+ * to CI. We need to repair this. JRA.
+ */
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa1 = &dacl->ace[i];
+
+ for (j = i + 1; j < dacl->num_aces; j++) {
+ SEC_ACE *psa2 = &dacl->ace[j];
+
+ if (psa1->info.mask != psa2->info.mask)
+ continue;
+
+ if (!sid_equal(&psa1->trustee, &psa2->trustee))
+ continue;
+
+ /*
+ * Ok - permission bits and SIDs are equal.
+ * Check if flags were re-written.
+ */
+
+ if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+ psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+ psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+
+ } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+ psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+ psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+
+ }
+ }
+ }
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ enum SID_NAME_USE sid_type;
+ SEC_ACE *psa = &dacl->ace[i];
+
+ /*
+ * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
+ */
+
+ if (non_mappable_sid(&psa->trustee)) {
+ fstring str;
+ DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
+ sid_to_string(str, &psa->trustee) ));
+ continue;
+ }
+
+ /*
+ * Create a cannon_ace entry representing this NT DACL ACE.
+ */
+
+ if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(current_ace);
+
+ sid_copy(&current_ace->trustee, &psa->trustee);
+
+ /*
+ * Try and work out if the SID is a user or group
+ * as we need to flag these differently for POSIX.
+ */
+
+ if( sid_equal(&current_ace->trustee, &global_sid_World)) {
+ current_ace->owner_type = WORLD_ACE;
+ current_ace->unix_ug.world = -1;
+ } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid, &sid_type)) {
+ current_ace->owner_type = UID_ACE;
+ } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid, &sid_type)) {
+ current_ace->owner_type = GID_ACE;
+ } else {
+ fstring str;
+
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
+ sid_to_string(str, &current_ace->trustee) ));
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ /*
+ * Map the given NT permissions into a UNIX mode_t containing only
+ * S_I(R|W|X)USR bits.
+ */
+
+ current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
+ current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
+
+ /*
+ * Now note what kind of a POSIX ACL this should map to.
+ */
+
+ if(sid_equal(&current_ace->trustee, pfile_owner_sid)) {
+
+ current_ace->type = SMB_ACL_USER_OBJ;
+
+ } else if( sid_equal(&current_ace->trustee, pfile_grp_sid)) {
+
+ current_ace->type = SMB_ACL_GROUP_OBJ;
+
+ } else if( sid_equal(&current_ace->trustee, &global_sid_World)) {
+
+ current_ace->type = SMB_ACL_OTHER;
+
+ } else {
+ /*
+ * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
+ * looking at owner_type.
+ */
+
+ current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
+ }
+
+ /*
+ * Now add the created ace to either the file list, the directory
+ * list, or both. We *MUST* preserve the order here (hence we use
+ * DLIST_ADD_END) as NT ACLs are order dependent.
+ */
+
+ if (fsp->is_directory) {
+
+ /*
+ * We can only add to the default POSIX ACE list if the ACE is
+ * designed to be inherited by both files and directories.
+ */
+
+ if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
+ (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+
+ DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
+
+ /*
+ * Note if this was an allow ace. We can't process
+ * any further deny ace's after this.
+ */
+
+ if (current_ace->attr == ALLOW_ACE)
+ got_dir_allow = True;
+
+ if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
+ DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
+Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("create_canon_ace_lists: adding dir ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+
+ /*
+ * If this is not an inherit only ACE we need to add a duplicate
+ * to the file acl.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ canon_ace *dup_ace = dup_canon_ace(current_ace);
+
+ if (!dup_ace) {
+ DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ current_ace = dup_ace;
+ } else {
+ current_ace = NULL;
+ }
+ }
+ }
+
+ /*
+ * Only add to the file ACL if not inherit only.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ DLIST_ADD_END(file_ace, current_ace, tmp_ace);
+
+ /*
+ * Note if this was an allow ace. We can't process
+ * any further deny ace's after this.
+ */
+
+ if (current_ace->attr == ALLOW_ACE)
+ got_file_allow = True;
+
+ if ((current_ace->attr == DENY_ACE) && got_file_allow) {
+ DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
+Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("create_canon_ace_lists: adding file ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+ all_aces_are_inherit_only = False;
+ current_ace = NULL;
+ }
+
+ /*
+ * Free if ACE was not added.
+ */
+
+ SAFE_FREE(current_ace);
+ }
+
+ if (fsp->is_directory && all_aces_are_inherit_only) {
+ /*
+ * Windows 2000 is doing one of these weird 'inherit acl'
+ * traverses to conserve NTFS ACL resources. Just pretend
+ * there was no DACL sent. JRA.
+ */
+
+ DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ file_ace = NULL;
+ dir_ace = NULL;
+ }
+
+ *ppfile_ace = file_ace;
+ *ppdir_ace = dir_ace;
+
+ return True;
+}
+
+/****************************************************************************
+ Check if a given uid/SID is in a group gid/SID. This is probably very
+ expensive and will need optimisation. A *lot* of optimisation :-). JRA.
+****************************************************************************/
+
+static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
+{
+ extern DOM_SID global_sid_World;
+ fstring u_name;
+ fstring g_name;
+
+ /* "Everyone" always matches every uid. */
+
+ if (sid_equal(&group_ace->trustee, &global_sid_World))
+ return True;
+
+ fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
+ fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
+
+ /*
+ * Due to the winbind interfaces we need to do this via names,
+ * not uids/gids.
+ */
+
+ return user_in_group_list(u_name, g_name );
+}
+
+/****************************************************************************
+ ASCII art time again... JRA :-).
+
+ We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
+ we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
+ entries). Secondly, the merge code has ensured that all duplicate SID entries for
+ allow or deny have been merged, so the same SID can only appear once in the deny
+ list or once in the allow list.
+
+ We then process as follows :
+
+ ---------------------------------------------------------------------------
+ First pass - look for a Everyone DENY entry.
+
+ If it is deny all (rwx) trunate the list at this point.
+ Else, walk the list from this point and use the deny permissions of this
+ entry as a mask on all following allow entries. Finally, delete
+ the Everyone DENY entry (we have applied it to everything possible).
+
+ In addition, in this pass we remove any DENY entries that have
+ no permissions (ie. they are a DENY nothing).
+ ---------------------------------------------------------------------------
+ Second pass - only deal with deny user entries.
+
+ DENY user1 (perms XXX)
+
+ new_perms = 0
+ for all following allow group entries where user1 is in group
+ new_perms |= group_perms;
+
+ user1 entry perms = new_perms & ~ XXX;
+
+ Convert the deny entry to an allow entry with the new perms and
+ push to the end of the list. Note if the user was in no groups
+ this maps to a specific allow nothing entry for this user.
+
+ The common case from the NT ACL choser (userX deny all) is
+ optimised so we don't do the group lookup - we just map to
+ an allow nothing entry.
+
+ What we're doing here is inferring the allow permissions the
+ person setting the ACE on user1 wanted by looking at the allow
+ permissions on the groups the user is currently in. This will
+ be a snapshot, depending on group membership but is the best
+ we can do and has the advantage of failing closed rather than
+ open.
+ ---------------------------------------------------------------------------
+ Third pass - only deal with deny group entries.
+
+ DENY group1 (perms XXX)
+
+ for all following allow user entries where user is in group1
+ user entry perms = user entry perms & ~ XXX;
+
+ If there is a group Everyone allow entry with permissions YYY,
+ convert the group1 entry to an allow entry and modify its
+ permissions to be :
+
+ new_perms = YYY & ~ XXX
+
+ and push to the end of the list.
+
+ If there is no group Everyone allow entry then convert the
+ group1 entry to a allow nothing entry and push to the end of the list.
+
+ Note that the common case from the NT ACL choser (groupX deny all)
+ cannot be optimised here as we need to modify user entries who are
+ in the group to change them to a deny all also.
+
+ What we're doing here is modifying the allow permissions of
+ user entries (which are more specific in POSIX ACLs) to mask
+ out the explicit deny set on the group they are in. This will
+ be a snapshot depending on current group membership but is the
+ best we can do and has the advantage of failing closed rather
+ than open.
+ ---------------------------------------------------------------------------
+
+ Note we *MUST* do the deny user pass first as this will convert deny user
+ entries into allow user entries which can then be processed by the deny
+ group pass.
+
+ The above algorithm took a *lot* of thinking about - hence this
+ explaination :-). JRA.
+****************************************************************************/
+
+/****************************************************************************
+ Process a canon_ace list entries. This is very complex code. We need
+ to go through and remove the "deny" permissions from any allow entry that matches
+ the id of this entry. We have already refused any NT ACL that wasn't in correct
+ order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
+ we just remove it (to fail safe). We have already removed any duplicate ace
+ entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
+ allow entries.
+****************************************************************************/
+
+static void process_deny_list( canon_ace **pp_ace_list )
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *ace_list = *pp_ace_list;
+ canon_ace *curr_ace = NULL;
+ canon_ace *curr_ace_next = NULL;
+
+ /* Pass 1 above - look for an Everyone, deny entry. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *allow_ace_p;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->perms == (mode_t)0) {
+
+ /* Deny nothing entry - delete. */
+
+ DLIST_REMOVE(ace_list, curr_ace);
+ continue;
+ }
+
+ if (!sid_equal(&curr_ace->trustee, &global_sid_World))
+ continue;
+
+ /* JRATEST - assert. */
+ SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
+
+ if (curr_ace->perms == ALL_ACE_PERMS) {
+
+ /*
+ * Optimisation. This is a DENY_ALL to Everyone. Truncate the
+ * list at this point including this entry.
+ */
+
+ canon_ace *prev_entry = curr_ace->prev;
+
+ free_canon_ace_list( curr_ace );
+ if (prev_entry)
+ prev_entry->next = NULL;
+ else {
+ /* We deleted the entire list. */
+ ace_list = NULL;
+ }
+ break;
+ }
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ /*
+ * Only mask off allow entries.
+ */
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ allow_ace_p->perms &= ~curr_ace->perms;
+ }
+
+ /*
+ * Now it's been applied, remove it.
+ */
+
+ DLIST_REMOVE(ace_list, curr_ace);
+ }
+
+ /* Pass 2 above - deal with deny user entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ mode_t new_perms = (mode_t)0;
+ canon_ace *allow_ace_p;
+ canon_ace *tmp_ace;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->owner_type != UID_ACE)
+ continue;
+
+ if (curr_ace->perms == ALL_ACE_PERMS) {
+
+ /*
+ * Optimisation - this is a deny everything to this user.
+ * Convert to an allow nothing and push to the end of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ curr_ace->perms = (mode_t)0;
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+ continue;
+ }
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* We process GID_ACE and WORLD_ACE entries only. */
+
+ if (allow_ace_p->owner_type == UID_ACE)
+ continue;
+
+ if (uid_entry_in_group( curr_ace, allow_ace_p))
+ new_perms |= allow_ace_p->perms;
+ }
+
+ /*
+ * Convert to a allow entry, modify the perms and push to the end
+ * of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ curr_ace->perms = (new_perms & ~curr_ace->perms);
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+ }
+
+ /* Pass 3 above - deal with deny group entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *tmp_ace;
+ canon_ace *allow_ace_p;
+ canon_ace *allow_everyone_p = NULL;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->owner_type != GID_ACE)
+ continue;
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* Store a pointer to the Everyone allow, if it exists. */
+ if (allow_ace_p->owner_type == WORLD_ACE)
+ allow_everyone_p = allow_ace_p;
+
+ /* We process UID_ACE entries only. */
+
+ if (allow_ace_p->owner_type != UID_ACE)
+ continue;
+
+ /* Mask off the deny group perms. */
+
+ if (uid_entry_in_group( allow_ace_p, curr_ace))
+ allow_ace_p->perms &= ~curr_ace->perms;
+ }
+
+ /*
+ * Convert the deny to an allow with the correct perms and
+ * push to the end of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ if (allow_everyone_p)
+ curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
+ else
+ curr_ace->perms = (mode_t)0;
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+
+ }
+
+ *pp_ace_list = ace_list;
+}
+
+/****************************************************************************
+ Create a default mode that will be used if a security descriptor entry has
+ no user/group/world entries.
+****************************************************************************/
+
+static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
+{
+ int snum = SNUM(fsp->conn);
+ mode_t and_bits = (mode_t)0;
+ mode_t or_bits = (mode_t)0;
+ mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
+
+ if (fsp->is_directory)
+ mode |= (S_IWUSR|S_IXUSR);
+
+ /*
+ * Now AND with the create mode/directory mode bits then OR with the
+ * force create mode/force directory mode bits.
+ */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ return ((mode & and_bits)|or_bits);
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
+ succeeding.
+****************************************************************************/
+
+static BOOL unpack_canon_ace(files_struct *fsp,
+ SMB_STRUCT_STAT *pst,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ canon_ace **ppfile_ace, canon_ace **ppdir_ace,
+ uint32 security_info_sent, SEC_DESC *psd)
+{
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+
+ *ppfile_ace = NULL;
+ *ppdir_ace = NULL;
+
+ if(security_info_sent == 0) {
+ DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
+ return False;
+ }
+
+ /*
+ * If no DACL then this is a chown only security descriptor.
+ */
+
+ if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
+ return True;
+
+ /*
+ * Now go through the DACL and create the canon_ace lists.
+ */
+
+ if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
+ &file_ace, &dir_ace, psd->dacl))
+ return False;
+
+ if ((file_ace == NULL) && (dir_ace == NULL)) {
+ /* W2K traverse DACL set - ignore. */
+ return True;
+ }
+
+ /*
+ * Go through the canon_ace list and merge entries
+ * belonging to identical users of identical allow or deny type.
+ * We can do this as all deny entries come first, followed by
+ * all allow entries (we have mandated this before accepting this acl).
+ */
+
+ print_canon_ace_list( "file ace - before merge", file_ace);
+ merge_aces( &file_ace );
+
+ print_canon_ace_list( "dir ace - before merge", dir_ace);
+ merge_aces( &dir_ace );
+
+ /*
+ * NT ACLs are order dependent. Go through the acl lists and
+ * process DENY entries by masking the allow entries.
+ */
+
+ print_canon_ace_list( "file ace - before deny", file_ace);
+ process_deny_list( &file_ace);
+
+ print_canon_ace_list( "dir ace - before deny", dir_ace);
+ process_deny_list( &dir_ace);
+
+ /*
+ * A well formed POSIX file or default ACL has at least 3 entries, a
+ * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
+ * and optionally a mask entry. Ensure this is the case.
+ */
+
+ print_canon_ace_list( "file ace - before valid", file_ace);
+
+ /*
+ * A default 3 element mode entry for a file should be r-- --- ---.
+ * A default 3 element mode entry for a directory should be rwx --- ---.
+ */
+
+ pst->st_mode = create_default_mode(fsp, False);
+
+ if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ print_canon_ace_list( "dir ace - before valid", dir_ace);
+
+ /*
+ * A default inheritable 3 element mode entry for a directory should be the
+ * mode Samba will use to create a file within. Ensure user rwx bits are set if
+ * it's a directory.
+ */
+
+ pst->st_mode = create_default_mode(fsp, True);
+
+ if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ print_canon_ace_list( "file ace - return", file_ace);
+ print_canon_ace_list( "dir ace - return", dir_ace);
+
+ *ppfile_ace = file_ace;
+ *ppdir_ace = dir_ace;
+ return True;
+
+}
+
+/******************************************************************************
+ When returning permissions, try and fit NT display
+ semantics if possible. Note the the canon_entries here must have been malloced.
+ The list format should be - first entry = owner, followed by group and other user
+ entries, last entry = other.
+
+ Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
+ are not ordered, and match on the most specific entry rather than walking a list,
+ then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
+
+ Entry 0: owner : deny all except read and write.
+ Entry 1: group : deny all except read.
+ Entry 2: Everyone : deny all except read.
+ Entry 3: owner : allow read and write.
+ Entry 4: group : allow read.
+ Entry 5: Everyone : allow read.
+
+ But NT cannot display this in their ACL editor !
+********************************************************************************/
+
+static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
+{
+ canon_ace *list_head = *pp_list_head;
+ canon_ace *owner_ace = NULL;
+ canon_ace *other_ace = NULL;
+ canon_ace *ace = NULL;
+
+ for (ace = list_head; ace; ace = ace->next) {
+ if (ace->type == SMB_ACL_USER_OBJ)
+ owner_ace = ace;
+ else if (ace->type == SMB_ACL_OTHER) {
+ /* Last ace - this is "other" */
+ other_ace = ace;
+ }
+ }
+
+ if (!owner_ace || !other_ace) {
+ DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
+ filename ));
+ return;
+ }
+
+ /*
+ * The POSIX algorithm applies to owner first, and other last,
+ * so ensure they are arranged in this order.
+ */
+
+ if (owner_ace) {
+ DLIST_PROMOTE(list_head, owner_ace);
+ }
+
+ if (other_ace) {
+ DLIST_DEMOTE(list_head, other_ace, ace);
+ }
+
+ /* We have probably changed the head of the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Create a linked list of canonical ACE entries.
+****************************************************************************/
+
+static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
+ DOM_SID *powner, DOM_SID *pgroup)
+{
+ extern DOM_SID global_sid_World;
+ connection_struct *conn = fsp->conn;
+ mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
+ canon_ace *list_head = NULL;
+ canon_ace *ace = NULL;
+ canon_ace *next_ace = NULL;
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ size_t ace_count;
+
+ while ( posix_acl && (conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1)) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ DOM_SID sid;
+ posix_id unix_ug;
+ enum ace_owner owner_type;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ /* Is this a MASK entry ? */
+ if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
+ continue;
+
+ if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
+ continue;
+
+ /* Decide which SID to use based on the ACL type. */
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ /* Get the SID from the owner. */
+ uid_to_sid( &sid, psbuf->st_uid );
+ unix_ug.uid = psbuf->st_uid;
+ owner_type = UID_ACE;
+ break;
+ case SMB_ACL_USER:
+ {
+ uid_t *puid = (uid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
+ if (puid == NULL) {
+ DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
+ continue;
+ }
+ uid_to_sid( &sid, *puid);
+ unix_ug.uid = *puid;
+ owner_type = UID_ACE;
+ conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)puid,tagtype);
+ break;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ /* Get the SID from the owning group. */
+ gid_to_sid( &sid, psbuf->st_gid );
+ unix_ug.gid = psbuf->st_gid;
+ owner_type = GID_ACE;
+ break;
+ case SMB_ACL_GROUP:
+ {
+ gid_t *pgid = (gid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
+ if (pgid == NULL) {
+ DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
+ continue;
+ }
+ gid_to_sid( &sid, *pgid);
+ unix_ug.gid = *pgid;
+ owner_type = GID_ACE;
+ conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)pgid,tagtype);
+ break;
+ }
+ case SMB_ACL_MASK:
+ acl_mask = convert_permset_to_mode_t(conn, permset);
+ continue; /* Don't count the mask as an entry. */
+ case SMB_ACL_OTHER:
+ /* Use the Everyone SID */
+ sid = global_sid_World;
+ unix_ug.world = -1;
+ owner_type = WORLD_ACE;
+ break;
+ default:
+ DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
+ continue;
+ }
+
+ /*
+ * Add this entry to the list.
+ */
+
+ if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
+ goto fail;
+
+ ZERO_STRUCTP(ace);
+ ace->type = tagtype;
+ ace->perms = convert_permset_to_mode_t(conn, permset);
+ ace->attr = ALLOW_ACE;
+ ace->trustee = sid;
+ ace->unix_ug = unix_ug;
+ ace->owner_type = owner_type;
+
+ DLIST_ADD(list_head, ace);
+ }
+
+ /*
+ * This next call will ensure we have at least a user/group/world set.
+ */
+
+ if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
+ goto fail;
+
+ arrange_posix_perms(fsp->fsp_name,&list_head );
+
+ /*
+ * Now go through the list, masking the permissions with the
+ * acl_mask. Ensure all DENY Entries are at the start of the list.
+ */
+
+ DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
+
+ for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
+ next_ace = ace->next;
+
+ /* Masks are only applied to entries other than USER_OBJ and OTHER. */
+ if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
+ ace->perms &= acl_mask;
+
+ if (ace->perms == 0) {
+ DLIST_PROMOTE(list_head, ace);
+ }
+
+ if( DEBUGLVL( 10 ) ) {
+ print_canon_ace(ace, ace_count);
+ }
+ }
+
+ print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
+
+ return list_head;
+
+ fail:
+
+ free_canon_ace_list(list_head);
+ return NULL;
+}
+
+/****************************************************************************
+ Attempt to apply an ACL to a file or directory.
+****************************************************************************/
+
+static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
+{
+ connection_struct *conn = fsp->conn;
+ BOOL ret = False;
+ SMB_ACL_T the_acl = conn->vfs_ops.sys_acl_init(conn, (int)count_canon_ace_list(the_ace) + 1);
+ canon_ace *p_ace;
+ int i;
+ SMB_ACL_ENTRY_T mask_entry;
+ SMB_ACL_PERMSET_T mask_permset;
+ SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
+
+ if (the_acl == NULL) {
+
+ if (errno != ENOSYS) {
+ /*
+ * Only print this error message if we have some kind of ACL
+ * support that's not working. Otherwise we would always get this.
+ */
+ DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
+ default_ace ? "default" : "file", strerror(errno) ));
+ }
+ *pacl_set_support = False;
+ return False;
+ }
+
+ for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
+ SMB_ACL_ENTRY_T the_entry;
+ SMB_ACL_PERMSET_T the_permset;
+
+ /*
+ * Get the entry for this ACE.
+ */
+
+ if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &the_entry) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Ok - we now know the ACL calls should be working, don't
+ * allow fallback to chmod.
+ */
+
+ *pacl_set_support = True;
+
+ /*
+ * Initialise the entry from the canon_ace.
+ */
+
+ /*
+ * First tell the entry what type of ACE this is.
+ */
+
+ if (conn->vfs_ops.sys_acl_set_tag_type(conn, the_entry, p_ace->type) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Only set the qualifier (user or group id) if the entry is a user
+ * or group id ACE.
+ */
+
+ if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
+ if (conn->vfs_ops.sys_acl_set_qualifier(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+ }
+
+ /*
+ * Convert the mode_t perms in the canon_ace to a POSIX permset.
+ */
+
+ if (conn->vfs_ops.sys_acl_get_permset(conn, the_entry, &the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
+ (unsigned int)p_ace->perms, i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * ..and apply them to the entry.
+ */
+
+ if (conn->vfs_ops.sys_acl_set_permset(conn, the_entry, the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if( DEBUGLVL( 10 ))
+ print_canon_ace( p_ace, i);
+ }
+
+ /*
+ * Add in a mask of rwx.
+ */
+
+ if (conn->vfs_ops.sys_acl_create_entry( conn, &the_acl, &mask_entry) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
+ goto done;
+ }
+
+ if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Check if the ACL is valid.
+ */
+
+ if (conn->vfs_ops.sys_acl_valid(conn, the_acl) == -1) {
+ DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Finally apply it to the file or directory.
+ */
+
+ if(default_ace || fsp->is_directory || fsp->fd == -1) {
+ if (conn->vfs_ops.sys_acl_set_file(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
+ /*
+ * Some systems allow all the above calls and only fail with no ACL support
+ * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+ */
+ if (errno == ENOSYS)
+ *pacl_set_support = False;
+ DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ fsp->fsp_name, strerror(errno) ));
+ goto done;
+ }
+ } else {
+ if (conn->vfs_ops.sys_acl_set_fd(fsp, fsp->fd, the_acl) == -1) {
+ /*
+ * Some systems allow all the above calls and only fail with no ACL support
+ * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+ */
+ if (errno == ENOSYS)
+ *pacl_set_support = False;
+ DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
+ fsp->fsp_name, strerror(errno) ));
+ goto done;
+ }
+ }
+
+ ret = True;
+
+ done:
+
+ if (the_acl != NULL)
+ conn->vfs_ops.sys_acl_free_acl(conn, the_acl);
+
+ return ret;
+}
+
+/****************************************************************************
+ Convert a canon_ace to a generic 3 element permission - if possible.
+****************************************************************************/
+
+#define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
+
+static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
+{
+ int snum = SNUM(fsp->conn);
+ size_t ace_count = count_canon_ace_list(file_ace_list);
+ canon_ace *ace_p;
+ canon_ace *owner_ace = NULL;
+ canon_ace *group_ace = NULL;
+ canon_ace *other_ace = NULL;
+ mode_t and_bits;
+ mode_t or_bits;
+
+ if (ace_count != 3) {
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
+posix perms.\n", fsp->fsp_name ));
+ return False;
+ }
+
+ for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
+ if (ace_p->owner_type == UID_ACE)
+ owner_ace = ace_p;
+ else if (ace_p->owner_type == GID_ACE)
+ group_ace = ace_p;
+ else if (ace_p->owner_type == WORLD_ACE)
+ other_ace = ace_p;
+ }
+
+ if (!owner_ace || !group_ace || !other_ace) {
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ *posix_perms = (mode_t)0;
+
+ *posix_perms |= owner_ace->perms;
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
+
+ /* The owner must have at least read access. */
+
+ *posix_perms |= S_IRUSR;
+ if (fsp->is_directory)
+ *posix_perms |= (S_IWUSR|S_IXUSR);
+
+ /* If requested apply the masks. */
+
+ /* Get the initial bits to apply. */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ *posix_perms = (((*posix_perms) & and_bits)|or_bits);
+
+ DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
+ (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
+ fsp->fsp_name ));
+
+ return True;
+}
+
+static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
+{
+ if (a1->type == a2->type)
+ return 0;
+
+ if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
+ return -1;
+ return 1;
+}
+
+/****************************************************************************
+ Reply to query a security descriptor from an fsp. If it succeeds it allocates
+ the space for the return elements and returns the size needed to return the
+ security descriptor. This should be the only external function needed for
+ the UNIX style get ACL.
+****************************************************************************/
+
+size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
+{
+ connection_struct *conn = fsp->conn;
+ SMB_STRUCT_STAT sbuf;
+ SEC_ACE *nt_ace_list = NULL;
+ DOM_SID owner_sid;
+ DOM_SID group_sid;
+ size_t sd_size = 0;
+ SEC_ACL *psa = NULL;
+ size_t num_acls = 0;
+ size_t num_dir_acls = 0;
+ size_t num_aces = 0;
+ SMB_ACL_T posix_acl = NULL;
+ SMB_ACL_T dir_acl = NULL;
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+
+ *ppdesc = NULL;
+
+ DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
+
+ if(fsp->is_directory || fsp->fd == -1) {
+
+ /* Get the stat struct for the owner info. */
+ if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
+ return 0;
+ }
+ /*
+ * Get the ACL from the path.
+ */
+
+ posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+
+ /*
+ * If it's a directory get the default POSIX ACL.
+ */
+
+ if(fsp->is_directory)
+ dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+
+ /* Get the stat struct for the owner info. */
+ if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
+ return 0;
+ }
+ /*
+ * Get the ACL from the fd.
+ */
+ posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
+ }
+
+ DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
+ posix_acl ? "present" : "absent",
+ dir_acl ? "present" : "absent" ));
+
+ /*
+ * Get the owner, group and world SIDs.
+ */
+
+ create_file_sids(&sbuf, &owner_sid, &group_sid);
+
+ /* Create the canon_ace lists. */
+ file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid);
+ num_acls = count_canon_ace_list(file_ace);
+
+ /* We must have *some* ACLS. */
+
+ if (num_acls == 0) {
+ DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
+ return 0;
+ }
+
+ if (fsp->is_directory) {
+ /*
+ * If we have to fake a default ACL then this is the mode to use.
+ */
+ sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
+
+ dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
+ num_dir_acls = count_canon_ace_list(dir_ace);
+ }
+
+ /* Allocate the ace list. */
+ if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
+ goto done;
+ }
+
+ memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
+
+ /*
+ * Create the NT ACE list from the canonical ace lists.
+ */
+
+ {
+ canon_ace *ace;
+ int nt_acl_type;
+ int i;
+
+ ace = file_ace;
+
+ for (i = 0; i < num_acls; i++, ace = ace->next) {
+ SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
+ }
+
+ ace = dir_ace;
+
+ for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
+ SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
+ SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
+ }
+
+ /*
+ * Sort to force deny entries to the front.
+ */
+
+ if (num_acls + num_dir_acls)
+ qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
+ }
+
+ if (num_acls) {
+ if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
+ goto done;
+ }
+ }
+
+ *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
+
+ if(!*ppdesc) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
+ sd_size = 0;
+ }
+
+ done:
+
+ if (posix_acl)
+ conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
+ if (dir_acl)
+ conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(nt_ace_list);
+
+ return sd_size;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor on an fsp. security_info_sent is the
+ description of the following NT ACL.
+ This should be the only external function needed for the UNIX style set ACL.
+****************************************************************************/
+
+BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
+{
+ connection_struct *conn = fsp->conn;
+ uid_t user = (uid_t)-1;
+ gid_t grp = (gid_t)-1;
+ SMB_STRUCT_STAT sbuf;
+ DOM_SID file_owner_sid;
+ DOM_SID file_grp_sid;
+ canon_ace *file_ace_list = NULL;
+ canon_ace *dir_ace_list = NULL;
+ BOOL acl_perms = False;
+ mode_t orig_mode = (mode_t)0;
+ uid_t orig_uid;
+ gid_t orig_gid;
+
+ DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
+
+ /*
+ * Get the current state of the file.
+ */
+
+ if(fsp->is_directory || fsp->fd == -1) {
+ if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
+ return False;
+ } else {
+ if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
+ return False;
+ }
+
+ /* Save the original elements we check against. */
+ orig_mode = sbuf.st_mode;
+ orig_uid = sbuf.st_uid;
+ orig_gid = sbuf.st_gid;
+
+ /*
+ * Unpack the user/group/world id's.
+ */
+
+ if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
+ return False;
+
+ /*
+ * Do we need to chown ?
+ */
+
+ if((user != (uid_t)-1 || grp != (uid_t)-1) && (orig_uid != user || orig_gid != grp)) {
+
+ DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+
+ if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
+ DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+ return False;
+ }
+
+ /*
+ * Recheck the current state of the file, which may have changed.
+ * (suid/sgid bits, for instance)
+ */
+
+ if(fsp->is_directory) {
+ if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
+ return False;
+ }
+ } else {
+
+ int ret;
+
+ if(fsp->fd == -1)
+ ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
+ else
+ ret = vfs_fstat(fsp,fsp->fd,&sbuf);
+
+ if(ret != 0)
+ return False;
+ }
+
+ /* Save the original elements we check against. */
+ orig_mode = sbuf.st_mode;
+ orig_uid = sbuf.st_uid;
+ orig_gid = sbuf.st_gid;
+ }
+
+ create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
+
+ acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
+ &file_ace_list, &dir_ace_list, security_info_sent, psd);
+
+ if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
+ /* W2K traverse DACL set - ignore. */
+ return True;
+ }
+
+ if (!acl_perms) {
+ DEBUG(3,("set_nt_acl: cannot set permissions\n"));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+
+ /*
+ * Only change security if we got a DACL.
+ */
+
+ if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
+
+ BOOL acl_set_support = False;
+ BOOL ret = False;
+
+ /*
+ * Try using the POSIX ACL set first. Fall back to chmod if
+ * we have no ACL support on this filesystem.
+ */
+
+ if (acl_perms && file_ace_list) {
+ ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
+ if (acl_set_support && ret == False) {
+ DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+
+ if (acl_perms && acl_set_support && fsp->is_directory) {
+ if (dir_ace_list) {
+ if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
+ DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ } else {
+
+ /*
+ * No default ACL - delete one if it exists.
+ */
+
+ if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) {
+ DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
+ free_canon_ace_list(file_ace_list);
+ return False;
+ }
+ }
+ }
+
+ /*
+ * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
+ */
+
+ if(!acl_set_support && acl_perms) {
+ mode_t posix_perms;
+
+ if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ if (orig_mode != posix_perms) {
+
+ DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
+ fsp->fsp_name, (unsigned int)posix_perms ));
+
+ if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
+ DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+ }
+ }
+
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+
+ return True;
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ int num_entries = 0;
+
+ while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ mode_t perms;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
+ return -1;
+
+ if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
+ return -1;
+
+ num_entries++;
+
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ break;
+ case SMB_ACL_MASK:
+ perms = S_IRUSR|S_IWUSR|S_IXUSR;
+ break;
+ case SMB_ACL_OTHER:
+ perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
+ break;
+ default:
+ continue;
+ }
+
+ if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
+ return -1;
+
+ if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1)
+ return -1;
+ }
+
+ /*
+ * If this is a simple 3 element ACL or no elements then it's a standard
+ * UNIX permission set. Just use chmod...
+ */
+
+ if ((num_entries == 3) || (num_entries == 0))
+ return -1;
+
+ return 0;
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+ Note that name is in UNIX character set.
+****************************************************************************/
+
+int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
+{
+ SMB_ACL_T posix_acl = NULL;
+ int ret = -1;
+
+ if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, name, SMB_ACL_TYPE_ACCESS)) == NULL)
+ return -1;
+
+ if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
+ goto done;
+
+ ret = conn->vfs_ops.sys_acl_set_file(conn, name, SMB_ACL_TYPE_ACCESS, posix_acl);
+
+ done:
+
+ conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
+ return ret;
+}
+
+/****************************************************************************
+ Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
+{
+ connection_struct *conn = fsp->conn;
+ SMB_ACL_T posix_acl = NULL;
+ int ret = -1;
+
+ if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL)
+ return -1;
+
+ if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
+ goto done;
+
+ ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl);
+
+ done:
+
+ conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
+ return ret;
+}
+
+BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
+{
+ SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT);
+ BOOL has_acl = False;
+ SMB_ACL_ENTRY_T entry;
+
+ if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
+ has_acl = True;
+
+ conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
+ return has_acl;
+}
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
new file mode 100644
index 0000000000..007621f6bb
--- /dev/null
+++ b/source3/smbd/process.c
@@ -0,0 +1,1301 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct timeval smb_last_time;
+
+static char *InBuffer = NULL;
+char *OutBuffer = NULL;
+char *last_inbuf = NULL;
+
+/*
+ * Size of data we can send to client. Set
+ * by the client for all protocols above CORE.
+ * Set by us for CORE protocol.
+ */
+int max_send = BUFFER_SIZE;
+/*
+ * Size of the data we can receive. Set by us.
+ * Can be modified by the max xmit parameter.
+ */
+int max_recv = BUFFER_SIZE;
+
+extern int last_message;
+extern int global_oplock_break;
+extern userdom_struct current_user_info;
+extern int smb_read_error;
+extern VOLATILE sig_atomic_t reload_after_sighup;
+extern VOLATILE sig_atomic_t got_sig_term;
+extern BOOL global_machine_password_needs_changing;
+extern fstring global_myworkgroup;
+extern pstring global_myname;
+extern int max_send;
+
+/****************************************************************************
+ structure to hold a linked list of queued messages.
+ for processing.
+****************************************************************************/
+
+typedef struct {
+ ubi_slNode msg_next;
+ char *msg_buf;
+ int msg_len;
+} pending_message_list;
+
+static ubi_slList smb_oplock_queue = { NULL, (ubi_slNodePtr)&smb_oplock_queue, 0};
+
+/****************************************************************************
+ Function to push a message onto the tail of a linked list of smb messages ready
+ for processing.
+****************************************************************************/
+
+static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len)
+{
+ pending_message_list *msg = (pending_message_list *)
+ malloc(sizeof(pending_message_list));
+
+ if(msg == NULL)
+ {
+ DEBUG(0,("push_message: malloc fail (1)\n"));
+ return False;
+ }
+
+ msg->msg_buf = (char *)malloc(msg_len);
+ if(msg->msg_buf == NULL)
+ {
+ DEBUG(0,("push_message: malloc fail (2)\n"));
+ SAFE_FREE(msg);
+ return False;
+ }
+
+ memcpy(msg->msg_buf, buf, msg_len);
+ msg->msg_len = msg_len;
+
+ ubi_slAddTail( list_head, msg);
+
+ return True;
+}
+
+/****************************************************************************
+ Function to push a smb message onto a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
+{
+ return push_message(&smb_oplock_queue, buf, msg_len);
+}
+
+/****************************************************************************
+ Do all async processing in here. This includes UDB oplock messages, kernel
+ oplock messages, change notify events etc.
+****************************************************************************/
+
+static void async_processing(fd_set *fds, char *buffer, int buffer_len)
+{
+ /* check for oplock messages (both UDP and kernel) */
+ if (receive_local_message(fds, buffer, buffer_len, 0)) {
+ process_local_message(buffer, buffer_len);
+ }
+
+ if (got_sig_term) {
+ exit_server("Caught TERM signal");
+ }
+
+ /* check for async change notify events */
+ process_pending_change_notify_queue(0);
+
+ /* check for sighup processing */
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+}
+
+/****************************************************************************
+ Do a select on an two fd's - with timeout.
+
+ If a local udp message has been pushed onto the
+ queue (this can only happen during oplock break
+ processing) call async_processing()
+
+ If a pending smb message has been pushed onto the
+ queue (this can only happen during oplock break
+ processing) return this next.
+
+ If the first smbfd is ready then read an smb from it.
+ if the second (loopback UDP) fd is ready then read a message
+ from it and setup the buffer header to identify the length
+ and from address.
+ Returns False on timeout or error.
+ Else returns True.
+
+The timeout is in milli seconds
+****************************************************************************/
+
+static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
+{
+ fd_set fds;
+ int selrtn;
+ struct timeval to;
+ int maxfd;
+
+ smb_read_error = 0;
+
+ again:
+
+ /*
+ * Note that this call must be before processing any SMB
+ * messages as we need to synchronously process any messages
+ * we may have sent to ourselves from the previous SMB.
+ */
+ message_dispatch();
+
+ /*
+ * Check to see if we already have a message on the smb queue.
+ * If so - copy and return it.
+ */
+ if(ubi_slCount(&smb_oplock_queue) != 0) {
+ pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
+ memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+
+ /* Free the message we just copied. */
+ SAFE_FREE(msg->msg_buf);
+ SAFE_FREE(msg);
+
+ DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
+ return True;
+ }
+
+
+ /*
+ * Setup the select read fd set.
+ */
+
+ FD_ZERO(&fds);
+ FD_SET(smbd_server_fd(),&fds);
+ maxfd = setup_oplock_select_set(&fds);
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL);
+
+ /* if we get EINTR then maybe we have received an oplock
+ signal - treat this as select returning 1. This is ugly, but
+ is the best we can do until the oplock code knows more about
+ signals */
+ if (selrtn == -1 && errno == EINTR) {
+ async_processing(&fds, buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ /* Check if error */
+ if (selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+
+ /*
+ * Ensure we process oplock break messages by preference.
+ * This is IMPORTANT ! Otherwise we can starve other processes
+ * sending us an oplock break message. JRA.
+ */
+
+ if (oplock_message_waiting(&fds)) {
+ async_processing(&fds, buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ return receive_smb(smbd_server_fd(), buffer, 0);
+}
+
+/****************************************************************************
+Get the next SMB packet, doing the local message processing automatically.
+****************************************************************************/
+
+BOOL receive_next_smb(char *inbuf, int bufsize, int timeout)
+{
+ BOOL got_keepalive;
+ BOOL ret;
+
+ do {
+ ret = receive_message_or_smb(inbuf,bufsize,timeout);
+
+ got_keepalive = (ret && (CVAL(inbuf,0) == SMBkeepalive));
+ } while (ret && got_keepalive);
+
+ return ret;
+}
+
+/****************************************************************************
+ We're terminating and have closed all our files/connections etc.
+ If there are any pending local messages we need to respond to them
+ before termination so that other smbds don't think we just died whilst
+ holding oplocks.
+****************************************************************************/
+
+void respond_to_all_remaining_local_messages(void)
+{
+ char buffer[1024];
+ fd_set fds;
+
+ /*
+ * Assert we have no exclusive open oplocks.
+ */
+
+ if(get_number_of_exclusive_open_oplocks()) {
+ DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n",
+ get_number_of_exclusive_open_oplocks() ));
+ return;
+ }
+
+ /*
+ * Setup the select read fd set.
+ */
+
+ FD_ZERO(&fds);
+ if(!setup_oplock_select_set(&fds))
+ return;
+
+ /*
+ * Keep doing receive_local_message with a 1 ms timeout until
+ * we have no more messages.
+ */
+ while(receive_local_message(&fds, buffer, sizeof(buffer), 1)) {
+ /* Deal with oplock break requests from other smbd's. */
+ process_local_message(buffer, sizeof(buffer));
+
+ FD_ZERO(&fds);
+ (void)setup_oplock_select_set(&fds);
+ }
+
+ return;
+}
+
+
+/*
+These flags determine some of the permissions required to do an operation
+
+Note that I don't set NEED_WRITE on some write operations because they
+are used by some brain-dead clients when printing, and I don't want to
+force write permissions on print services.
+*/
+#define AS_USER (1<<0)
+#define NEED_WRITE (1<<1)
+#define TIME_INIT (1<<2)
+#define CAN_IPC (1<<3)
+#define AS_GUEST (1<<5)
+#define QUEUE_IN_OPLOCK (1<<6)
+
+/*
+ define a list of possible SMB messages and their corresponding
+ functions. Any message that has a NULL function is unimplemented -
+ please feel free to contribute implementations!
+*/
+static struct smb_message_struct
+{
+ char *name;
+ int (*fn)(connection_struct *conn, char *, char *, int, int);
+ int flags;
+}
+ smb_messages[256] = {
+
+/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+/* 0x02 */ { "SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK },
+/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER},
+/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
+/* 0x05 */ { "SMBflush",reply_flush,AS_USER},
+/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER},
+/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+/* 0x0a */ { "SMBread",reply_read,AS_USER},
+/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC },
+/* 0x0c */ { "SMBlock",reply_lock,AS_USER},
+/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
+/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK },
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
+/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER},
+/* 0x11 */ { "SMBexit",reply_exit,0},
+/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
+/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER},
+/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER},
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
+/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
+/* 0x1c */ { "SMBreadBs",NULL,0 },
+/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
+/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
+/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
+/* 0x20 */ { "SMBwritec",NULL,0},
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
+/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
+/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER },
+/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK},
+/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC},
+/* 0x27 */ { "SMBioctl",reply_ioctl,0},
+/* 0x28 */ { "SMBioctls",NULL,AS_USER},
+/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0x2b */ { "SMBecho",reply_echo,0},
+/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER},
+/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | QUEUE_IN_OPLOCK | CAN_IPC },
+/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER},
+/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER},
+/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER},
+/* 0x36 */ { NULL, NULL, 0 },
+/* 0x37 */ { NULL, NULL, 0 },
+/* 0x38 */ { NULL, NULL, 0 },
+/* 0x39 */ { NULL, NULL, 0 },
+/* 0x3a */ { NULL, NULL, 0 },
+/* 0x3b */ { NULL, NULL, 0 },
+/* 0x3c */ { NULL, NULL, 0 },
+/* 0x3d */ { NULL, NULL, 0 },
+/* 0x3e */ { NULL, NULL, 0 },
+/* 0x3f */ { NULL, NULL, 0 },
+/* 0x40 */ { NULL, NULL, 0 },
+/* 0x41 */ { NULL, NULL, 0 },
+/* 0x42 */ { NULL, NULL, 0 },
+/* 0x43 */ { NULL, NULL, 0 },
+/* 0x44 */ { NULL, NULL, 0 },
+/* 0x45 */ { NULL, NULL, 0 },
+/* 0x46 */ { NULL, NULL, 0 },
+/* 0x47 */ { NULL, NULL, 0 },
+/* 0x48 */ { NULL, NULL, 0 },
+/* 0x49 */ { NULL, NULL, 0 },
+/* 0x4a */ { NULL, NULL, 0 },
+/* 0x4b */ { NULL, NULL, 0 },
+/* 0x4c */ { NULL, NULL, 0 },
+/* 0x4d */ { NULL, NULL, 0 },
+/* 0x4e */ { NULL, NULL, 0 },
+/* 0x4f */ { NULL, NULL, 0 },
+/* 0x50 */ { NULL, NULL, 0 },
+/* 0x51 */ { NULL, NULL, 0 },
+/* 0x52 */ { NULL, NULL, 0 },
+/* 0x53 */ { NULL, NULL, 0 },
+/* 0x54 */ { NULL, NULL, 0 },
+/* 0x55 */ { NULL, NULL, 0 },
+/* 0x56 */ { NULL, NULL, 0 },
+/* 0x57 */ { NULL, NULL, 0 },
+/* 0x58 */ { NULL, NULL, 0 },
+/* 0x59 */ { NULL, NULL, 0 },
+/* 0x5a */ { NULL, NULL, 0 },
+/* 0x5b */ { NULL, NULL, 0 },
+/* 0x5c */ { NULL, NULL, 0 },
+/* 0x5d */ { NULL, NULL, 0 },
+/* 0x5e */ { NULL, NULL, 0 },
+/* 0x5f */ { NULL, NULL, 0 },
+/* 0x60 */ { NULL, NULL, 0 },
+/* 0x61 */ { NULL, NULL, 0 },
+/* 0x62 */ { NULL, NULL, 0 },
+/* 0x63 */ { NULL, NULL, 0 },
+/* 0x64 */ { NULL, NULL, 0 },
+/* 0x65 */ { NULL, NULL, 0 },
+/* 0x66 */ { NULL, NULL, 0 },
+/* 0x67 */ { NULL, NULL, 0 },
+/* 0x68 */ { NULL, NULL, 0 },
+/* 0x69 */ { NULL, NULL, 0 },
+/* 0x6a */ { NULL, NULL, 0 },
+/* 0x6b */ { NULL, NULL, 0 },
+/* 0x6c */ { NULL, NULL, 0 },
+/* 0x6d */ { NULL, NULL, 0 },
+/* 0x6e */ { NULL, NULL, 0 },
+/* 0x6f */ { NULL, NULL, 0 },
+/* 0x70 */ { "SMBtcon",reply_tcon,0},
+/* 0x71 */ { "SMBtdis",reply_tdis,0},
+/* 0x72 */ { "SMBnegprot",reply_negprot,0},
+/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
+/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
+/* 0x76 */ { NULL, NULL, 0 },
+/* 0x77 */ { NULL, NULL, 0 },
+/* 0x78 */ { NULL, NULL, 0 },
+/* 0x79 */ { NULL, NULL, 0 },
+/* 0x7a */ { NULL, NULL, 0 },
+/* 0x7b */ { NULL, NULL, 0 },
+/* 0x7c */ { NULL, NULL, 0 },
+/* 0x7d */ { NULL, NULL, 0 },
+/* 0x7e */ { NULL, NULL, 0 },
+/* 0x7f */ { NULL, NULL, 0 },
+/* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER},
+/* 0x81 */ { "SMBsearch",reply_search,AS_USER},
+/* 0x82 */ { "SMBffirst",reply_search,AS_USER},
+/* 0x83 */ { "SMBfunique",reply_search,AS_USER},
+/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER},
+/* 0x85 */ { NULL, NULL, 0 },
+/* 0x86 */ { NULL, NULL, 0 },
+/* 0x87 */ { NULL, NULL, 0 },
+/* 0x88 */ { NULL, NULL, 0 },
+/* 0x89 */ { NULL, NULL, 0 },
+/* 0x8a */ { NULL, NULL, 0 },
+/* 0x8b */ { NULL, NULL, 0 },
+/* 0x8c */ { NULL, NULL, 0 },
+/* 0x8d */ { NULL, NULL, 0 },
+/* 0x8e */ { NULL, NULL, 0 },
+/* 0x8f */ { NULL, NULL, 0 },
+/* 0x90 */ { NULL, NULL, 0 },
+/* 0x91 */ { NULL, NULL, 0 },
+/* 0x92 */ { NULL, NULL, 0 },
+/* 0x93 */ { NULL, NULL, 0 },
+/* 0x94 */ { NULL, NULL, 0 },
+/* 0x95 */ { NULL, NULL, 0 },
+/* 0x96 */ { NULL, NULL, 0 },
+/* 0x97 */ { NULL, NULL, 0 },
+/* 0x98 */ { NULL, NULL, 0 },
+/* 0x99 */ { NULL, NULL, 0 },
+/* 0x9a */ { NULL, NULL, 0 },
+/* 0x9b */ { NULL, NULL, 0 },
+/* 0x9c */ { NULL, NULL, 0 },
+/* 0x9d */ { NULL, NULL, 0 },
+/* 0x9e */ { NULL, NULL, 0 },
+/* 0x9f */ { NULL, NULL, 0 },
+/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK},
+/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
+/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 },
+/* 0xa5 */ { NULL, NULL, 0 },
+/* 0xa6 */ { NULL, NULL, 0 },
+/* 0xa7 */ { NULL, NULL, 0 },
+/* 0xa8 */ { NULL, NULL, 0 },
+/* 0xa9 */ { NULL, NULL, 0 },
+/* 0xaa */ { NULL, NULL, 0 },
+/* 0xab */ { NULL, NULL, 0 },
+/* 0xac */ { NULL, NULL, 0 },
+/* 0xad */ { NULL, NULL, 0 },
+/* 0xae */ { NULL, NULL, 0 },
+/* 0xaf */ { NULL, NULL, 0 },
+/* 0xb0 */ { NULL, NULL, 0 },
+/* 0xb1 */ { NULL, NULL, 0 },
+/* 0xb2 */ { NULL, NULL, 0 },
+/* 0xb3 */ { NULL, NULL, 0 },
+/* 0xb4 */ { NULL, NULL, 0 },
+/* 0xb5 */ { NULL, NULL, 0 },
+/* 0xb6 */ { NULL, NULL, 0 },
+/* 0xb7 */ { NULL, NULL, 0 },
+/* 0xb8 */ { NULL, NULL, 0 },
+/* 0xb9 */ { NULL, NULL, 0 },
+/* 0xba */ { NULL, NULL, 0 },
+/* 0xbb */ { NULL, NULL, 0 },
+/* 0xbc */ { NULL, NULL, 0 },
+/* 0xbd */ { NULL, NULL, 0 },
+/* 0xbe */ { NULL, NULL, 0 },
+/* 0xbf */ { NULL, NULL, 0 },
+/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
+/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER},
+/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER},
+/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER},
+/* 0xc4 */ { NULL, NULL, 0 },
+/* 0xc5 */ { NULL, NULL, 0 },
+/* 0xc6 */ { NULL, NULL, 0 },
+/* 0xc7 */ { NULL, NULL, 0 },
+/* 0xc8 */ { NULL, NULL, 0 },
+/* 0xc9 */ { NULL, NULL, 0 },
+/* 0xca */ { NULL, NULL, 0 },
+/* 0xcb */ { NULL, NULL, 0 },
+/* 0xcc */ { NULL, NULL, 0 },
+/* 0xcd */ { NULL, NULL, 0 },
+/* 0xce */ { NULL, NULL, 0 },
+/* 0xcf */ { NULL, NULL, 0 },
+/* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST},
+/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST},
+/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST},
+/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST},
+/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST},
+/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST},
+/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST},
+/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST},
+/* 0xd8 */ { NULL, NULL, 0 },
+/* 0xd9 */ { NULL, NULL, 0 },
+/* 0xda */ { NULL, NULL, 0 },
+/* 0xdb */ { NULL, NULL, 0 },
+/* 0xdc */ { NULL, NULL, 0 },
+/* 0xdd */ { NULL, NULL, 0 },
+/* 0xde */ { NULL, NULL, 0 },
+/* 0xdf */ { NULL, NULL, 0 },
+/* 0xe0 */ { NULL, NULL, 0 },
+/* 0xe1 */ { NULL, NULL, 0 },
+/* 0xe2 */ { NULL, NULL, 0 },
+/* 0xe3 */ { NULL, NULL, 0 },
+/* 0xe4 */ { NULL, NULL, 0 },
+/* 0xe5 */ { NULL, NULL, 0 },
+/* 0xe6 */ { NULL, NULL, 0 },
+/* 0xe7 */ { NULL, NULL, 0 },
+/* 0xe8 */ { NULL, NULL, 0 },
+/* 0xe9 */ { NULL, NULL, 0 },
+/* 0xea */ { NULL, NULL, 0 },
+/* 0xeb */ { NULL, NULL, 0 },
+/* 0xec */ { NULL, NULL, 0 },
+/* 0xed */ { NULL, NULL, 0 },
+/* 0xee */ { NULL, NULL, 0 },
+/* 0xef */ { NULL, NULL, 0 },
+/* 0xf0 */ { NULL, NULL, 0 },
+/* 0xf1 */ { NULL, NULL, 0 },
+/* 0xf2 */ { NULL, NULL, 0 },
+/* 0xf3 */ { NULL, NULL, 0 },
+/* 0xf4 */ { NULL, NULL, 0 },
+/* 0xf5 */ { NULL, NULL, 0 },
+/* 0xf6 */ { NULL, NULL, 0 },
+/* 0xf7 */ { NULL, NULL, 0 },
+/* 0xf8 */ { NULL, NULL, 0 },
+/* 0xf9 */ { NULL, NULL, 0 },
+/* 0xfa */ { NULL, NULL, 0 },
+/* 0xfb */ { NULL, NULL, 0 },
+/* 0xfc */ { NULL, NULL, 0 },
+/* 0xfd */ { NULL, NULL, 0 },
+/* 0xfe */ { NULL, NULL, 0 },
+/* 0xff */ { NULL, NULL, 0 }
+
+};
+
+/*******************************************************************
+dump a prs to a file
+ ********************************************************************/
+static void smb_dump(char *name, int type, char *data, ssize_t len)
+{
+ int fd, i;
+ pstring fname;
+ if (DEBUGLEVEL < 50) return;
+
+ if (len < 4) len = smb_len(data)+4;
+ for (i=1;i<100;i++) {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.%s", name, i,
+ type ? "req" : "resp");
+ fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd != -1 || errno != EEXIST) break;
+ }
+ if (fd != -1) {
+ ssize_t ret = write(fd, data, len);
+ if (ret != len)
+ DEBUG(0,("smb_dump: problem: write returned %d\n", (int)ret ));
+ close(fd);
+ DEBUG(0,("created %s len %d\n", fname, len));
+ }
+}
+
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static pid_t pid= (pid_t)-1;
+ int outsize = 0;
+ extern uint16 global_smbpid;
+
+ type &= 0xff;
+
+ if (pid == (pid_t)-1)
+ pid = sys_getpid();
+
+ errno = 0;
+ last_message = type;
+
+ /* make sure this is an SMB packet */
+ if (strncmp(smb_base(inbuf),"\377SMB",4) != 0)
+ {
+ DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf)));
+ return(-1);
+ }
+
+ /* yuck! this is an interim measure before we get rid of our
+ current inbuf/outbuf system */
+ global_smbpid = SVAL(inbuf,smb_pid);
+
+ if (smb_messages[type].fn == NULL)
+ {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ smb_dump("Unknown", 1, inbuf, size);
+ outsize = reply_unknown(inbuf,outbuf);
+ }
+ else
+ {
+ int flags = smb_messages[type].flags;
+ static uint16 last_session_tag = UID_FIELD_INVALID;
+ /* In share mode security we must ignore the vuid. */
+ uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+
+ DEBUG(3,("switch message %s (pid %d)\n",smb_fn_name(type),(int)pid));
+
+ smb_dump(smb_fn_name(type), 1, inbuf, size);
+ if(global_oplock_break)
+ {
+ if(flags & QUEUE_IN_OPLOCK)
+ {
+ /*
+ * Queue this message as we are the process of an oplock break.
+ */
+
+ DEBUG( 2, ( "switch_message: queueing message due to being in " ) );
+ DEBUGADD( 2, ( "oplock break state.\n" ) );
+
+ push_oplock_pending_smb_message( inbuf, size );
+ return -1;
+ }
+ }
+
+ /* Ensure this value is replaced in the incoming packet. */
+ SSVAL(inbuf,smb_uid,session_tag);
+
+ /*
+ * Ensure the correct username is in current_user_info.
+ * This is a really ugly bugfix for problems with
+ * multiple session_setup_and_X's being done and
+ * allowing %U and %G substitutions to work correctly.
+ * There is a reason this code is done here, don't
+ * move it unless you know what you're doing... :-).
+ * JRA.
+ */
+
+ if (session_tag != last_session_tag) {
+ user_struct *vuser = NULL;
+
+ last_session_tag = session_tag;
+ if(session_tag != UID_FIELD_INVALID)
+ vuser = get_valid_user_struct(session_tag);
+ if(vuser != NULL)
+ current_user_info = vuser->user;
+ }
+
+ /* does this protocol need to be run as root? */
+ if (!(flags & AS_USER))
+ change_to_root_user();
+
+ /* does this protocol need a valid tree connection? */
+ if ((flags & AS_USER) && !conn) {
+ return ERROR_DOS(ERRSRV, ERRinvnid);
+ }
+
+
+ /* does this protocol need to be run as the connected user? */
+ if ((flags & AS_USER) && !change_to_user(conn,session_tag)) {
+ if (flags & AS_GUEST)
+ flags &= ~AS_USER;
+ else
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
+
+ /* this code is to work around a bug is MS client 3 without
+ introducing a security hole - it needs to be able to do
+ print queue checks as guest if it isn't logged in properly */
+ if (flags & AS_USER)
+ flags &= ~AS_GUEST;
+
+ /* does it need write permission? */
+ if ((flags & NEED_WRITE) && !CAN_WRITE(conn))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* ipc services are limited */
+ if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) {
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
+
+ /* load service specific parameters */
+ if (conn && !set_current_service(conn,(flags & AS_USER)?True:False)) {
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
+
+ /* does this protocol need to be run as guest? */
+ if ((flags & AS_GUEST) &&
+ (!change_to_guest() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1)))) {
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
+
+ last_inbuf = inbuf;
+
+ outsize = smb_messages[type].fn(conn, inbuf,outbuf,size,bufsize);
+ }
+
+ smb_dump(smb_fn_name(type), 0, outbuf, outsize);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ construct a reply to the incoming packet
+****************************************************************************/
+static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ int type = CVAL(inbuf,smb_com);
+ int outsize = 0;
+ int msg_type = CVAL(inbuf,0);
+
+ GetTimeOfDay(&smb_last_time);
+
+ chain_size = 0;
+ file_chain_reset();
+ reset_chain_p();
+
+ if (msg_type != 0)
+ return(reply_special(inbuf,outbuf));
+
+ construct_reply_common(inbuf, outbuf);
+
+ outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+
+ outsize += chain_size;
+
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+ return(outsize);
+}
+
+/****************************************************************************
+ Keep track of the number of running smbd's. This functionality is used to
+ 'hard' limit Samba overhead on resource constrained systems.
+****************************************************************************/
+static BOOL smbd_process_limit(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes()) {
+
+ /* Always add one to the smbd process count, as exit_server() always
+ * subtracts one.
+ */
+
+ total_smbds = 1; /* In case we need to create the entry. */
+
+ if (!conn_tdb_ctx()) {
+ DEBUG(0,("smbd_process_limit: max smbd processes parameter set with status parameter not \
+set. Ignoring max smbd restriction.\n"));
+ return False;
+ }
+
+ if (tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, 1) == -1)
+ return True;
+
+ return total_smbds > lp_max_smbd_processes();
+ }
+ else
+ return False;
+}
+
+/****************************************************************************
+ process an smb from the client - split out from the smbd_process() code so
+ it can be used by the oplock break code.
+****************************************************************************/
+void process_smb(char *inbuf, char *outbuf)
+{
+#ifdef WITH_SSL
+ extern BOOL sslEnabled; /* don't use function for performance reasons */
+ static int sslConnected = 0;
+#endif /* WITH_SSL */
+ static int trans_num;
+ int msg_type = CVAL(inbuf,0);
+ int32 len = smb_len(inbuf);
+ int nread = len + 4;
+
+ DO_PROFILE_INC(smb_count);
+
+ if (trans_num == 0) {
+ /* on the first packet, check the global hosts allow/ hosts
+ deny parameters before doing any parsing of the packet
+ passed to us by the client. This prevents attacks on our
+ parsing code from hosts not in the hosts allow list */
+ if (smbd_process_limit() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1))) {
+ /* send a negative session response "not listening on calling
+ name" */
+ static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+ DEBUG( 1, ( "Connection denied from %s\n",
+ client_addr() ) );
+ (void)send_smb(smbd_server_fd(),(char *)buf);
+ exit_server("connection denied");
+ }
+ }
+
+ DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) );
+ DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) );
+
+#ifdef WITH_SSL
+ if(sslEnabled && !sslConnected){
+ sslConnected = sslutil_negotiate_ssl(smbd_server_fd(), msg_type);
+ if(sslConnected < 0){ /* an error occured */
+ exit_server("SSL negotiation failed");
+ }else if(sslConnected){
+ trans_num++;
+ return;
+ }
+ }
+#endif /* WITH_SSL */
+
+ if (msg_type == 0)
+ show_msg(inbuf);
+ else if(msg_type == SMBkeepalive)
+ return; /* Keepalive packet. */
+
+ nread = construct_reply(inbuf,outbuf,nread,max_send);
+
+ if(nread > 0)
+ {
+ if (CVAL(outbuf,0) == 0)
+ show_msg(outbuf);
+
+ if (nread != smb_len(outbuf) + 4)
+ {
+ DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+ nread, smb_len(outbuf)));
+ }
+ else
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("process_smb: send_smb failed.");
+ }
+ trans_num++;
+}
+
+
+
+/****************************************************************************
+return a string containing the function name of a SMB command
+****************************************************************************/
+char *smb_fn_name(int type)
+{
+ static char *unknown_name = "SMBunknown";
+
+ if (smb_messages[type].name == NULL)
+ return(unknown_name);
+
+ return(smb_messages[type].name);
+}
+
+
+/****************************************************************************
+ Helper function for contruct_reply.
+****************************************************************************/
+
+void construct_reply_common(char *inbuf,char *outbuf)
+{
+ memset(outbuf,'\0',smb_size);
+
+ set_message(outbuf,0,0,True);
+ SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
+
+ memcpy(outbuf+4,inbuf+4,4);
+ SCVAL(outbuf,smb_rcls,SMB_SUCCESS);
+ SCVAL(outbuf,smb_reh,0);
+ SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES));
+ SSVAL(outbuf,smb_flg2,
+ (SVAL(inbuf,smb_flg2) & FLAGS2_UNICODE_STRINGS) |
+ FLAGS2_LONG_PATH_COMPONENTS |
+ FLAGS2_32_BIT_ERROR_CODES | FLAGS2_EXTENDED_SECURITY);
+
+ SSVAL(outbuf,smb_err,SMB_SUCCESS);
+ SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
+ SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
+ SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
+ SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
+}
+
+/****************************************************************************
+ construct a chained reply and add it to the already made reply
+ **************************************************************************/
+int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static char *orig_inbuf;
+ static char *orig_outbuf;
+ int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
+ unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
+ char *inbuf2, *outbuf2;
+ int outsize2;
+ char inbuf_saved[smb_wct];
+ char outbuf_saved[smb_wct];
+ int wct = CVAL(outbuf,smb_wct);
+ int outsize = smb_size + 2*wct + SVAL(outbuf,smb_vwv0+2*wct);
+
+ /* maybe its not chained */
+ if (smb_com2 == 0xFF) {
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ return outsize;
+ }
+
+ if (chain_size == 0) {
+ /* this is the first part of the chain */
+ orig_inbuf = inbuf;
+ orig_outbuf = outbuf;
+ }
+
+ /*
+ * The original Win95 redirector dies on a reply to
+ * a lockingX and read chain unless the chain reply is
+ * 4 byte aligned. JRA.
+ */
+
+ outsize = (outsize + 3) & ~3;
+
+ /* we need to tell the client where the next part of the reply will be */
+ SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
+ SCVAL(outbuf,smb_vwv0,smb_com2);
+
+ /* remember how much the caller added to the chain, only counting stuff
+ after the parameter words */
+ chain_size += outsize - smb_wct;
+
+ /* work out pointers into the original packets. The
+ headers on these need to be filled in */
+ inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
+ outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct;
+
+ /* remember the original command type */
+ smb_com1 = CVAL(orig_inbuf,smb_com);
+
+ /* save the data which will be overwritten by the new headers */
+ memcpy(inbuf_saved,inbuf2,smb_wct);
+ memcpy(outbuf_saved,outbuf2,smb_wct);
+
+ /* give the new packet the same header as the last part of the SMB */
+ memmove(inbuf2,inbuf,smb_wct);
+
+ /* create the in buffer */
+ SCVAL(inbuf2,smb_com,smb_com2);
+
+ /* create the out buffer */
+ construct_reply_common(inbuf2, outbuf2);
+
+ DEBUG(3,("Chained message\n"));
+ show_msg(inbuf2);
+
+ /* process the request */
+ outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size,
+ bufsize-chain_size);
+
+ /* copy the new reply and request headers over the old ones, but
+ preserve the smb_com field */
+ memmove(orig_outbuf,outbuf2,smb_wct);
+ SCVAL(orig_outbuf,smb_com,smb_com1);
+
+ /* restore the saved data, being careful not to overwrite any
+ data from the reply header */
+ memcpy(inbuf2,inbuf_saved,smb_wct);
+ {
+ int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf);
+ if (ofs < 0) ofs = 0;
+ memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs);
+ }
+
+ return outsize2;
+}
+
+/****************************************************************************
+ Setup the needed select timeout.
+****************************************************************************/
+
+static int setup_select_timeout(void)
+{
+ int select_timeout;
+ int t;
+
+ /*
+ * Increase the select timeout back to SMBD_SELECT_TIMEOUT if we
+ * have removed any blocking locks. JRA.
+ */
+
+ select_timeout = blocking_locks_pending() ? SMBD_SELECT_TIMEOUT_WITH_PENDING_LOCKS*1000 :
+ SMBD_SELECT_TIMEOUT*1000;
+
+ t = change_notify_timeout();
+ if (t != -1) select_timeout = MIN(select_timeout, t*1000);
+
+ return select_timeout;
+}
+
+/****************************************************************************
+ Check if services need reloading.
+****************************************************************************/
+
+void check_reload(int t)
+{
+ static time_t last_smb_conf_reload_time = 0;
+
+ if(last_smb_conf_reload_time == 0)
+ last_smb_conf_reload_time = t;
+
+ if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK))
+ {
+ reload_services(True);
+ reload_after_sighup = False;
+ last_smb_conf_reload_time = t;
+ }
+}
+
+/****************************************************************************
+ Process any timeout housekeeping. Return False if the caller should exit.
+****************************************************************************/
+
+static BOOL timeout_processing(int deadtime, int *select_timeout, time_t *last_timeout_processing_time)
+{
+ static time_t last_keepalive_sent_time = 0;
+ static time_t last_idle_closed_check = 0;
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
+
+ if (smb_read_error == READ_EOF)
+ {
+ DEBUG(3,("end of file from client\n"));
+ return False;
+ }
+
+ if (smb_read_error == READ_ERROR)
+ {
+ DEBUG(3,("receive_smb error (%s) exiting\n",
+ strerror(errno)));
+ return False;
+ }
+
+ *last_timeout_processing_time = t = time(NULL);
+
+ if(last_keepalive_sent_time == 0)
+ last_keepalive_sent_time = t;
+
+ if(last_idle_closed_check == 0)
+ last_idle_closed_check = t;
+
+ /* become root again if waiting */
+ change_to_root_user();
+
+ /* check if we need to reload services */
+ check_reload(t);
+
+ /* automatic timeout if all connections are closed */
+ if (conn_num_open()==0 && (t - last_idle_closed_check) >= IDLE_CLOSED_TIMEOUT)
+ {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ return False;
+ }
+ else
+ last_idle_closed_check = t;
+
+ if (keepalive && (t - last_keepalive_sent_time)>keepalive)
+ {
+ extern struct auth_context *negprot_global_auth_context;
+ if (!send_keepalive(smbd_server_fd())) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return False;
+ }
+
+ /* send a keepalive for a password server or the like.
+ This is attached to the auth_info created in the
+ negprot */
+ if (negprot_global_auth_context
+ && negprot_global_auth_context->challenge_set_method
+ && negprot_global_auth_context->challenge_set_method->send_keepalive) {
+ negprot_global_auth_context->challenge_set_method->send_keepalive
+ (&negprot_global_auth_context->challenge_set_method->private_data);
+ }
+
+ last_keepalive_sent_time = t;
+ }
+
+ /* check for connection timeouts */
+ allidle = conn_idle_all(t, deadtime);
+
+ if (allidle && conn_num_open()>0) {
+ DEBUG(2,("Closing idle connection 2.\n"));
+ return False;
+ }
+
+ if(global_machine_password_needs_changing &&
+ /* for ADS we need to do a regular ADS password change, not a domain
+ password change */
+ lp_security() == SEC_DOMAIN)
+ {
+ unsigned char trust_passwd_hash[16];
+ time_t lct;
+ pstring remote_machine_list;
+
+ /*
+ * We're in domain level security, and the code that
+ * read the machine password flagged that the machine
+ * password needs changing.
+ */
+
+ /*
+ * First, open the machine password file with an exclusive lock.
+ */
+
+ if(!secrets_fetch_trust_account_password(global_myworkgroup, trust_passwd_hash, &lct)) {
+ DEBUG(0,("process: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+ return True;
+ }
+
+ /*
+ * Make sure someone else hasn't already done this.
+ */
+
+ if(t < lct + lp_machine_password_timeout()) {
+ global_machine_password_needs_changing = False;
+ return True;
+ }
+
+ pstrcpy(remote_machine_list, lp_passwordserver());
+
+ change_trust_account_password( global_myworkgroup, remote_machine_list);
+ global_machine_password_needs_changing = False;
+ }
+
+ /*
+ * Check to see if we have any blocking locks
+ * outstanding on the queue.
+ */
+ process_blocking_lock_queue(t);
+
+ /*
+ * Check to see if we have any change notifies
+ * outstanding on the queue.
+ */
+ process_pending_change_notify_queue(t);
+
+ /*
+ * Now we are root, check if the log files need pruning.
+ * Force a log file check.
+ */
+ force_check_log_size();
+ check_log_size();
+
+ /*
+ * Modify the select timeout depending upon
+ * what we have remaining in our queues.
+ */
+
+ *select_timeout = setup_select_timeout();
+
+ return True;
+}
+
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
+
+void smbd_process(void)
+{
+ extern int smb_echo_count;
+ time_t last_timeout_processing_time = time(NULL);
+ unsigned int num_smbs = 0;
+
+ InBuffer = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
+ OutBuffer = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return;
+
+ max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+
+ /* re-initialise the timezone */
+ TimeInit();
+
+ /* register our message handlers */
+ message_register(MSG_SMB_FORCE_TDIS, msg_force_tdis);
+ talloc_init_named("dummy!");
+
+ while (True) {
+ int deadtime = lp_deadtime()*60;
+ int select_timeout = setup_select_timeout();
+ int num_echos;
+
+ if (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
+
+ errno = 0;
+
+ /* free up temporary memory */
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ while (!receive_message_or_smb(InBuffer,BUFFER_SIZE+LARGE_WRITEX_HDR_SIZE,select_timeout)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+ /*
+ * Ensure we do timeout processing if the SMB we just got was
+ * only an echo request. This allows us to set the select
+ * timeout in 'receive_message_or_smb()' to any value we like
+ * without worrying that the client will send echo requests
+ * faster than the select timeout, thus starving out the
+ * essential processing (change notify, blocking locks) that
+ * the timeout code does. JRA.
+ */
+ num_echos = smb_echo_count;
+
+ process_smb(InBuffer, OutBuffer);
+
+ if (smb_echo_count != num_echos) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+ num_smbs++;
+
+ /*
+ * If we are getting smb requests in a constant stream
+ * with no echos, make sure we attempt timeout processing
+ * every select_timeout milliseconds - but only check for this
+ * every 200 smb requests.
+ */
+
+ if ((num_smbs % 200) == 0) {
+ time_t new_check_time = time(NULL);
+ if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ last_timeout_processing_time = new_check_time; /* Reset time. */
+ }
+ }
+ }
+}
diff --git a/source3/smbd/quotas.c b/source3/smbd/quotas.c
new file mode 100644
index 0000000000..39cb8f2432
--- /dev/null
+++ b/source3/smbd/quotas.c
@@ -0,0 +1,1097 @@
+/*
+ Unix SMB/CIFS implementation.
+ support for quotas
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+/*
+ * This is one of the most system dependent parts of Samba, and its
+ * done a litle differently. Each system has its own way of doing
+ * things :-(
+ */
+
+#include "includes.h"
+
+#if defined(VXFS_QUOTA)
+
+/*
+ * In addition to their native filesystems, some systems have Veritas VxFS.
+ * Declare here, define at end: reduces likely "include" interaction problems.
+ * David Lee <T.D.Lee@durham.ac.uk>
+ */
+BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
+
+#endif /* VXFS_QUOTA */
+
+#ifdef LINUX
+
+#include <sys/types.h>
+#include <asm/types.h>
+
+/*
+ * This shouldn't be neccessary - it should be /usr/include/sys/quota.h
+ * Unfortunately, RH7.1 ships with a different quota system using struct mem_dqblk
+ * rather than the struct dqblk defined in /usr/include/sys/quota.h.
+ * This means we must include linux/quota.h to have a hope of working on
+ * RH7.1 systems. And it also means this breaks if the kernel is upgraded
+ * to a Linus 2.4.x (where x > the minor number shipped with RH7.1) until
+ * Linus synchronises with the AC patches. Sometimes I *hate* Linux :-). JRA.
+ */
+
+#include <linux/quota.h>
+#ifdef HAVE_LINUX_XQM_H
+#include <linux/xqm.h>
+#endif
+
+#include <mntent.h>
+#include <linux/unistd.h>
+
+
+#define LINUX_QUOTAS_2
+
+typedef struct _LINUX_SMB_DISK_QUOTA {
+ SMB_BIG_UINT bsize;
+ SMB_BIG_UINT hardlimit; /* In bsize units. */
+ SMB_BIG_UINT softlimit; /* In bsize units. */
+ SMB_BIG_UINT curblocks; /* In bsize units. */
+ SMB_BIG_UINT ihardlimit; /* inode hard limit. */
+ SMB_BIG_UINT isoftlimit; /* inode soft limit. */
+ SMB_BIG_UINT curinodes; /* Current used inodes. */
+} LINUX_SMB_DISK_QUOTA;
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota get call.
+****************************************************************************/
+
+static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+#ifdef HAVE_LINUX_XQM_H
+ struct fs_disk_quota D;
+ ZERO_STRUCT(D);
+
+ if ((ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D)))
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)512;
+ dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.d_icount;
+ dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the old and new Linux quota get calls.
+****************************************************************************/
+
+static int get_smb_linux_vfs_quota(char *path, uid_t euser_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ int ret;
+#ifdef LINUX_QUOTAS_1
+ struct dqblk D;
+ ZERO_STRUCT(D);
+ dp->bsize = (SMB_BIG_UINT)1024;
+#else /* LINUX_QUOTAS_2 */
+ struct mem_dqblk D;
+ ZERO_STRUCT(D);
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE 1024
+#endif
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+#endif
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D)))
+ return -1;
+
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+
+#ifdef LINUX_QUOTAS_1
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+#else /* LINUX_QUOTAS_2 */
+ dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace)/ dp->bsize;
+#endif
+
+ return 0;
+}
+
+/****************************************************************************
+try to get the disk space from disk quotas (LINUX version)
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ LINUX_SMB_DISK_QUOTA D;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+ int found;
+ uid_t euser_id;
+
+ euser_id = geteuid();
+
+ /* find the block device file */
+
+ if ( sys_stat(path, &S) == -1 )
+ return(False) ;
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+
+ if (S.st_dev == devno) {
+ found = True ;
+ break;
+ }
+ }
+
+ endmntent(fp) ;
+
+ if (!found)
+ return(False);
+
+ save_re_uid();
+ set_effective_uid(0);
+ if (strcmp(mnt->mnt_type, "xfs") == 0)
+ r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, &D);
+ else
+ r=get_smb_linux_vfs_quota(mnt->mnt_fsname, euser_id, &D);
+ restore_re_uid();
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ return(False);
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ return(False);
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return (True);
+}
+
+#elif defined(CRAY)
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas (CRAY VERSION)
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ struct mntent *mnt;
+ FILE *fd;
+ SMB_STRUCT_STAT sbuf;
+ SMB_DEV_T devno ;
+ static SMB_DEV_T devno_cached = 0 ;
+ static pstring name;
+ struct q_request request ;
+ struct qf_header header ;
+ static int quota_default = 0 ;
+ int found ;
+
+ if ( sys_stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+
+ if ( devno != devno_cached ) {
+
+ devno_cached = devno ;
+
+ if ((fd = setmntent(KMTAB)) == NULL)
+ return(False) ;
+
+ found = False ;
+
+ while ((mnt = getmntent(fd)) != NULL) {
+
+ if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+
+ if (sbuf.st_dev == devno) {
+
+ found = True ;
+ break ;
+
+ }
+
+ }
+
+ pstrcpy(name,mnt->mnt_dir) ;
+ endmntent(fd) ;
+
+ if ( ! found )
+ return(False) ;
+ }
+
+ request.qf_magic = QF_MAGIC ;
+ request.qf_entry.id = geteuid() ;
+
+ if (quotactl(name, Q_GETQUOTA, &request) == -1)
+ return(False) ;
+
+ if ( ! request.user )
+ return(False) ;
+
+ if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
+
+ if ( ! quota_default ) {
+
+ if ( quotactl(name, Q_GETHEADER, &header) == -1 )
+ return(False) ;
+ else
+ quota_default = header.user_h.def_fq ;
+ }
+
+ *dfree = quota_default ;
+
+ }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
+
+ *dfree = 0 ;
+
+ }else{
+
+ *dfree = request.qf_entry.user_q.f_quota ;
+
+ }
+
+ *dsize = request.qf_entry.user_q.f_use ;
+
+ if ( *dfree < *dsize )
+ *dfree = 0 ;
+ else
+ *dfree -= *dsize ;
+
+ *bsize = 4096 ; /* Cray blocksize */
+
+ return(True) ;
+
+}
+
+
+#elif defined(SUNOS5) || defined(SUNOS4)
+
+#include <fcntl.h>
+#include <sys/param.h>
+#if defined(SUNOS5)
+#include <sys/fs/ufs_quota.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#else /* defined(SUNOS4) */
+#include <ufs/quota.h>
+#include <mntent.h>
+#endif
+
+#if defined(SUNOS5)
+
+/****************************************************************************
+ Allows querying of remote hosts for quotas on NFS mounted shares.
+ Supports normal NFS and AMD mounts.
+ Alan Romeril <a.romeril@ic.ac.uk> July 2K.
+****************************************************************************/
+
+#include <rpc/rpc.h>
+#include <rpc/types.h>
+#include <rpcsvc/rquota.h>
+#include <rpc/nettype.h>
+#include <rpc/xdr.h>
+
+static int quotastat;
+
+static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
+{
+ if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
+ return(0);
+ if (!xdr_int(xdrsp, &args->gqa_uid))
+ return(0);
+ return (1);
+}
+
+static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
+{
+ if (!xdr_int(xdrsp, &quotastat)) {
+ DEBUG(6,("nfs_quotas: Status bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
+ DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
+ DEBUG(6,("nfs_quotas: Active bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
+ DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
+ DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
+ DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
+ return 0;
+ }
+ return (1);
+}
+
+/* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
+static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t uid = euser_id;
+ struct dqblk D;
+ char *mnttype = nfspath;
+ CLIENT *clnt;
+ struct getquota_rslt gqr;
+ struct getquota_args args;
+ char *cutstr, *pathname, *host, *testpath;
+ int len;
+ static struct timeval timeout = {2,0};
+ enum clnt_stat clnt_stat;
+ BOOL ret = True;
+
+ *bsize = *dfree = *dsize = (SMB_BIG_UINT)0;
+
+ len=strcspn(mnttype, ":");
+ pathname=strstr(mnttype, ":");
+ cutstr = (char *) malloc(sizeof(char) * len );
+ if (!cutstr)
+ return False;
+
+ host = strncat(cutstr,mnttype, sizeof(char) * len );
+ DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
+ DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
+ testpath=strchr_m(mnttype, ':');
+ args.gqa_pathp = testpath+1;
+ args.gqa_uid = uid;
+
+ DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
+
+ if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
+ ret = False;
+ goto out;
+ }
+
+ clnt->cl_auth = authunix_create_default();
+ DEBUG(9,("nfs_quotas: auth_success\n"));
+
+ clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout);
+
+ if (clnt_stat != RPC_SUCCESS) {
+ DEBUG(9,("nfs_quotas: clnt_call fail\n"));
+ ret = False;
+ goto out;
+ }
+
+ /*
+ * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
+ * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
+ * something sensible.
+ */
+
+ switch ( quotastat ) {
+ case 0:
+ DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat ));
+ ret = False;
+ goto out;
+
+ case 1:
+ DEBUG(9,("nfs_quotas: Good quota data\n"));
+ D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
+ D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
+ D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
+ break;
+
+ case 2:
+ case 3:
+ D.dqb_bsoftlimit = 1;
+ D.dqb_curblocks = 1;
+ DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat ));
+ break;
+
+ default:
+ DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat ));
+ break;
+ }
+
+ DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n",
+ quotastat,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
+ gqr.getquota_rslt_u.gqr_rquota.rq_active,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
+
+ *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks == D.dqb_curblocks == 1)
+ *bsize = 512;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ out:
+
+ if (clnt) {
+ if (clnt->cl_auth)
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
+
+ DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ SAFE_FREE(cutstr);
+ DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
+ return ret;
+}
+#endif
+
+/****************************************************************************
+try to get the disk space from disk quotas (SunOS & Solaris2 version)
+Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t euser_id;
+ int ret;
+ struct dqblk D;
+#if defined(SUNOS5)
+ struct quotctl command;
+ int file;
+ static struct mnttab mnt;
+ static pstring name;
+ pstring devopt;
+#else /* SunOS4 */
+ struct mntent *mnt;
+ static pstring name;
+#endif
+ FILE *fd;
+ SMB_STRUCT_STAT sbuf;
+ SMB_DEV_T devno ;
+ static SMB_DEV_T devno_cached = 0 ;
+ static int found ;
+
+ euser_id = geteuid();
+
+ if ( sys_stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+ DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno));
+ if ( devno != devno_cached ) {
+ devno_cached = devno ;
+#if defined(SUNOS5)
+ if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
+ return(False) ;
+
+ found = False ;
+ slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno);
+ while (getmntent(fd, &mnt) == 0) {
+ if( !hasmntopt(&mnt, devopt) )
+ continue;
+
+ DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt));
+
+ /* quotas are only on vxfs, UFS or NFS */
+ if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
+ strcmp( mnt.mnt_fstype, "nfs" ) == 0 ||
+ strcmp( mnt.mnt_fstype, "vxfs" ) == 0 ) {
+ found = True ;
+ break;
+ }
+ }
+
+ pstrcpy(name,mnt.mnt_mountp) ;
+ pstrcat(name,"/quotas") ;
+ fclose(fd) ;
+#else /* SunOS4 */
+ if ((fd = setmntent(MOUNTED, "r")) == NULL)
+ return(False) ;
+
+ found = False ;
+ while ((mnt = getmntent(fd)) != NULL) {
+ if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+ DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev));
+ if (sbuf.st_dev == devno) {
+ found = True ;
+ break;
+ }
+ }
+
+ pstrcpy(name,mnt->mnt_fsname) ;
+ endmntent(fd) ;
+#endif
+ }
+
+ if ( ! found )
+ return(False) ;
+
+ save_re_uid();
+ set_effective_uid(0);
+
+#if defined(SUNOS5)
+ if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) {
+ BOOL retval;
+ DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special));
+ retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize);
+ restore_re_uid();
+ return retval;
+ }
+
+ DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
+ if((file=sys_open(name, O_RDONLY,0))<0) {
+ restore_re_uid();
+ return(False);
+ }
+ command.op = Q_GETQUOTA;
+ command.uid = euser_id;
+ command.addr = (caddr_t) &D;
+ ret = ioctl(file, Q_QUOTACTL, &command);
+ close(file);
+#else
+ DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
+ ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
+#endif
+
+ restore_re_uid();
+
+ if (ret < 0) {
+ DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
+
+#if defined(SUNOS5) && defined(VXFS_QUOTA)
+ /* If normal quotactl() fails, try vxfs private calls */
+ set_effective_uid(euser_id);
+ DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype));
+ if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) {
+ BOOL retval;
+ retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize);
+ return(retval);
+ }
+#else
+ return(False);
+#endif
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ *bsize = DEV_BSIZE;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
+ path,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ return(True);
+}
+
+
+#elif defined(OSF1)
+#include <ufs/quota.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas - OSF1 version
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r, save_errno;
+ struct dqblk D;
+ SMB_STRUCT_STAT S;
+ uid_t euser_id;
+
+ /*
+ * This code presumes that OSF1 will only
+ * give out quota info when the real uid
+ * matches the effective uid. JRA.
+ */
+ euser_id = geteuid();
+ save_re_uid();
+ if (set_re_uid() != 0) return False;
+
+ r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
+ if (r) {
+ save_errno = errno;
+ }
+
+ restore_re_uid();
+
+ *bsize = DEV_BSIZE;
+
+ if (r)
+ {
+ if (save_errno == EDQUOT) /* disk quota exceeded */
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ return (True);
+ }
+ else
+ return (False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+
+#elif defined (IRIX6)
+/****************************************************************************
+try to get the disk space from disk quotas (IRIX 6.2 version)
+****************************************************************************/
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t euser_id;
+ int r;
+ struct dqblk D;
+ struct fs_disk_quota F;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+ int found;
+
+ /* find the block device file */
+
+ if ( sys_stat(path, &S) == -1 ) {
+ return(False) ;
+ }
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+ if (S.st_dev == devno) {
+ found = True ;
+ break ;
+ }
+ }
+ endmntent(fp) ;
+
+ if (!found) {
+ return(False);
+ }
+
+ euser_id=geteuid();
+ save_re_uid();
+ set_effective_uid(0);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+
+ *bsize = 512;
+
+ if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
+ {
+ r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
+
+ restore_re_uid();
+
+ if (r==-1)
+ return(False);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
+ (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
+ (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
+ (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
+ )
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
+ {
+ return(False);
+ }
+ else
+ {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+
+ }
+ else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
+ {
+ r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
+
+ restore_re_uid();
+
+ if (r==-1)
+ return(False);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
+ (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
+ (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
+ (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
+ )
+ {
+ *dfree = 0;
+ *dsize = F.d_bcount;
+ }
+ else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
+ {
+ return(False);
+ }
+ else
+ {
+ *dfree = (F.d_blk_softlimit - F.d_bcount);
+ *dsize = F.d_blk_softlimit;
+ }
+
+ }
+ else
+ {
+ restore_re_uid();
+ return(False);
+ }
+
+ return (True);
+
+}
+
+#else
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <ufs/ufs/quota.h>
+#include <machine/param.h>
+#elif AIX
+/* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
+#include <jfs/quota.h>
+/* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
+#define dqb_curfiles dqb_curinodes
+#define dqb_fhardlimit dqb_ihardlimit
+#define dqb_fsoftlimit dqb_isoftlimit
+#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+#include <sys/quota.h>
+#include <devnm.h>
+#endif
+
+/****************************************************************************
+try to get the disk space from disk quotas - default version
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r;
+ struct dqblk D;
+ uid_t euser_id;
+#if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
+ char dev_disk[256];
+ SMB_STRUCT_STAT S;
+ /* find the block device file */
+ if ((sys_stat(path, &S)<0) ||
+ (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
+#endif
+
+ euser_id = geteuid();
+
+#ifdef HPUX
+ /* for HPUX, real uid must be same as euid to execute quotactl for euid */
+ save_re_uid();
+ if (set_re_uid() != 0) return False;
+
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+
+ restore_re_uid();
+#else
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ {
+ /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
+ gid_t egrp_id;
+
+ save_re_uid();
+ set_effective_uid(0);
+
+ egrp_id = getegid();
+ r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+
+ /* As FreeBSD has group quotas, if getting the user
+ quota fails, try getting the group instead. */
+ if (r) {
+ r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
+ }
+
+ restore_re_uid();
+ }
+#elif defined(AIX)
+ /* AIX has both USER and GROUP quotas:
+ Get the USER quota (ohnielse@fysik.dtu.dk) */
+ r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+#endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+#endif /* HPUX */
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ *bsize = DEV_BSIZE;
+#else /* !__FreeBSD__ && !__OpenBSD__ */
+ *bsize = 1024;
+#endif /*!__FreeBSD__ && !__OpenBSD__ */
+
+ if (r)
+ {
+ if (errno == EDQUOT)
+ {
+ *dfree =0;
+ *dsize =D.dqb_curblocks;
+ return (True);
+ }
+ else return(False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
+#endif
+ ) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+
+#endif
+
+#if defined(VXFS_QUOTA)
+
+/****************************************************************************
+Try to get the disk space from Veritas disk quotas.
+ David Lee <T.D.Lee@durham.ac.uk> August 1999.
+
+Background assumptions:
+ Potentially under many Operating Systems. Initially Solaris 2.
+
+ My guess is that Veritas is largely, though not entirely,
+ independent of OS. So I have separated it out.
+
+ There may be some details. For example, OS-specific "include" files.
+
+ It is understood that HPUX 10 somehow gets Veritas quotas without
+ any special effort; if so, this routine need not be compiled in.
+ Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
+
+Warning:
+ It is understood that Veritas do not publicly support this ioctl interface.
+ Rather their preference would be for the user (us) to call the native
+ OS and then for the OS itself to call through to the VxFS filesystem.
+ Presumably HPUX 10, see above, does this.
+
+Hints for porting:
+ Add your OS to "IFLIST" below.
+ Get it to compile successfully:
+ Almost certainly "include"s require attention: see SUNOS5.
+ In the main code above, arrange for it to be called: see SUNOS5.
+ Test!
+
+****************************************************************************/
+
+/* "IFLIST"
+ * This "if" is a list of ports:
+ * if defined(OS1) || defined(OS2) || ...
+ */
+#if defined(SUNOS5)
+
+#if defined(SUNOS5)
+#include <sys/fs/vx_solaris.h>
+#endif
+#include <sys/fs/vx_machdep.h>
+#include <sys/fs/vx_layout.h>
+#include <sys/fs/vx_quota.h>
+#include <sys/fs/vx_aioctl.h>
+#include <sys/fs/vx_ioctl.h>
+
+BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t user_id, euser_id;
+ int ret;
+ struct vx_dqblk D;
+ struct vx_quotctl quotabuf;
+ struct vx_genioctl genbuf;
+ pstring qfname;
+ int file;
+
+ /*
+ * "name" may or may not include a trailing "/quotas".
+ * Arranging consistency of calling here in "quotas.c" may not be easy and
+ * it might be easier to examine and adjust it here.
+ * Fortunately, VxFS seems not to mind at present.
+ */
+ pstrcpy(qfname, name) ;
+ /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */
+
+ euser_id = geteuid();
+ set_effective_uid(0);
+
+ DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname));
+ if((file=sys_open(qfname, O_RDONLY,0))<0) {
+ set_effective_uid(euser_id);
+ return(False);
+ }
+ genbuf.ioc_cmd = VX_QUOTACTL;
+ genbuf.ioc_up = (void *) &quotabuf;
+
+ quotabuf.cmd = VX_GETQUOTA;
+ quotabuf.uid = euser_id;
+ quotabuf.addr = (caddr_t) &D;
+ ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf);
+ close(file);
+
+ set_effective_uid(euser_id);
+
+ if (ret < 0) {
+ DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) ));
+ return(False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+ DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
+ path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit,
+ D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit));
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ *bsize = DEV_BSIZE;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
+ path,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ return(True);
+}
+
+#endif /* SUNOS5 || ... */
+
+#endif /* VXFS_QUOTA */
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index b7b51775bb..fbb981781f 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -1,9 +1,9 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
Main SMB reply routines
- Copyright (C) Andrew Tridgell 1992-1995
-
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -25,222 +25,282 @@
#include "includes.h"
-#include "loadparm.h"
-#include "trans2.h"
/* look in server.c for some explanation of these variables */
extern int Protocol;
-extern int DEBUGLEVEL;
-extern int chain_size;
-extern int maxxmit;
-extern int chain_fnum;
+extern int max_send;
+extern int max_recv;
extern char magic_char;
-extern connection_struct Connections[];
-extern files_struct Files[];
extern BOOL case_sensitive;
-extern pstring sesssetup_user;
-extern int Client;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern pstring global_myname;
+extern int global_oplock_break;
+unsigned int smb_echo_count = 0;
-/* this macro should always be used to extract an fnum (smb_fid) from
-a packet to ensure chaining works correctly */
-#define GETFNUM(buf,where) (chain_fnum!= -1?chain_fnum:SVAL(buf,where))
+extern fstring remote_machine;
+extern BOOL global_encrypted_passwords_negotiated;
/****************************************************************************
reply to an special message
****************************************************************************/
+
int reply_special(char *inbuf,char *outbuf)
{
- int outsize = 4;
- int msg_type = CVAL(inbuf,0);
- int msg_flags = CVAL(inbuf,1);
- pstring name1,name2;
- extern fstring remote_machine;
- extern fstring local_machine;
- char *p;
+ int outsize = 4;
+ int msg_type = CVAL(inbuf,0);
+ int msg_flags = CVAL(inbuf,1);
+ pstring name1,name2;
+
+ extern fstring local_machine;
+ int len;
+ char name_type = 0;
+
+ *name1 = *name2 = 0;
+
+ memset(outbuf,'\0',smb_size);
- *name1 = *name2 = 0;
+ smb_setlen(outbuf,0);
+
+ switch (msg_type) {
+ case 0x81: /* session request */
+ SCVAL(outbuf,0,0x82);
+ SCVAL(outbuf,3,0);
+ if (name_len(inbuf+4) > 50 ||
+ name_len(inbuf+4 + name_len(inbuf + 4)) > 50) {
+ DEBUG(0,("Invalid name length in session request\n"));
+ return(0);
+ }
+ name_extract(inbuf,4,name1);
+ name_extract(inbuf,4 + name_len(inbuf + 4),name2);
+ DEBUG(2,("netbios connect: name1=%s name2=%s\n",
+ name1,name2));
+
+ fstrcpy(remote_machine,name2);
+ remote_machine[15] = 0;
+ trim_string(remote_machine," "," ");
+ strlower(remote_machine);
+ alpha_strcpy(remote_machine,remote_machine,SAFE_NETBIOS_CHARS,sizeof(remote_machine)-1);
+
+ fstrcpy(local_machine,name1);
+ len = strlen(local_machine);
+ if (len == 16) {
+ name_type = local_machine[15];
+ local_machine[15] = 0;
+ }
+ trim_string(local_machine," "," ");
+ strlower(local_machine);
+ alpha_strcpy(local_machine,local_machine,SAFE_NETBIOS_CHARS,sizeof(local_machine)-1);
+
+ DEBUG(2,("netbios connect: local=%s remote=%s\n",
+ local_machine, remote_machine ));
+
+ if (name_type == 'R') {
+ /* We are being asked for a pathworks session ---
+ no thanks! */
+ SCVAL(outbuf, 0,0x83);
+ break;
+ }
- smb_setlen(outbuf,0);
+ /* only add the client's machine name to the list
+ of possibly valid usernames if we are operating
+ in share mode security */
+ if (lp_security() == SEC_SHARE) {
+ add_session_user(remote_machine);
+ }
- switch (msg_type)
- {
- case 0x81: /* session request */
- CVAL(outbuf,0) = 0x82;
- CVAL(outbuf,3) = 0;
- if (name_len(inbuf+4) > 50)
- {
- DEBUG(0,("Invalid name length in session request\n"));
- return(0);
+ reload_services(True);
+ reopen_logs();
+
+ claim_connection(NULL,"",MAXSTATUS,True);
+
+ break;
+
+ case 0x89: /* session keepalive request
+ (some old clients produce this?) */
+ SCVAL(outbuf,0,SMBkeepalive);
+ SCVAL(outbuf,3,0);
+ break;
+
+ case 0x82: /* positive session response */
+ case 0x83: /* negative session response */
+ case 0x84: /* retarget session response */
+ DEBUG(0,("Unexpected session response\n"));
+ break;
+
+ case SMBkeepalive: /* session keepalive */
+ default:
+ return(0);
}
- name_extract(inbuf,4,name1);
- name_extract(inbuf,4 + name_len(inbuf + 4),name2);
- DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2));
-
- strcpy(remote_machine,name2);
- trim_string(remote_machine," "," ");
- p = strchr(remote_machine,' ');
- strlower(remote_machine);
- if (p) *p = 0;
-
- strcpy(local_machine,name1);
- trim_string(local_machine," "," ");
- p = strchr(local_machine,' ');
- strlower(local_machine);
- if (p) *p = 0;
-
- add_session_user(remote_machine);
-
- reload_services(True);
- reopen_logs();
-
- break;
- case 0x85: /* session keepalive */
- default:
- return(0);
- }
-
- DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags));
-
- return(outsize);
-}
-
-
-/*******************************************************************
-work out what error to give to a failed connection
-********************************************************************/
-static int connection_error(char *inbuf,char *outbuf,int connection_num)
-{
- switch (connection_num)
- {
- case -8:
- return(ERROR(ERRSRV,ERRnoresource));
- case -7:
- return(ERROR(ERRSRV,ERRbaduid));
- case -6:
- return(ERROR(ERRSRV,ERRinvdevice));
- case -5:
- return(ERROR(ERRSRV,ERRinvnetname));
- case -4:
- return(ERROR(ERRSRV,ERRaccess));
- case -3:
- return(ERROR(ERRDOS,ERRnoipc));
- case -2:
- return(ERROR(ERRSRV,ERRinvnetname));
- }
- return(ERROR(ERRSRV,ERRbadpw));
+
+ DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n",
+ msg_type, msg_flags));
+
+ return(outsize);
}
/****************************************************************************
- reply to a tcon
+ Reply to a tcon.
****************************************************************************/
-int reply_tcon(char *inbuf,char *outbuf)
+
+int reply_tcon(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- pstring service;
- pstring user;
- pstring password;
- pstring dev;
- int connection_num;
- int outsize = 0;
- int uid = SVAL(inbuf,smb_uid);
- int vuid;
- int pwlen;
+ pstring service;
+ pstring password;
+ pstring dev;
+ int outsize = 0;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ int pwlen=0;
+ NTSTATUS nt_status;
+ char *p;
+ DATA_BLOB password_blob;
+
+ START_PROFILE(SMBtcon);
- *service = *user = *password = *dev = 0;
+ *service = *password = *dev = 0;
- vuid = valid_uid(uid);
-
- parse_connect(inbuf,service,user,password,&pwlen,dev);
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull(inbuf, service, p, sizeof(service), -1, STR_TERMINATE) + 1;
+ pwlen = srvstr_pull(inbuf, password, p, sizeof(password), -1, STR_TERMINATE) + 1;
+ p += pwlen;
+ p += srvstr_pull(inbuf, dev, p, sizeof(dev), -1, STR_TERMINATE) + 1;
+
+ p = strrchr_m(service,'\\');
+ if (p) {
+ pstrcpy(service, p+1);
+ }
+
+ password_blob = data_blob(password, pwlen+1);
+
+ conn = make_connection(service,password_blob,dev,vuid,&nt_status);
- connection_num = make_connection(service,user,password,pwlen,dev,vuid);
+ data_blob_clear_free(&password_blob);
- if (connection_num < 0)
- return(connection_error(inbuf,outbuf,connection_num));
+ if (!conn) {
+ END_PROFILE(SMBtcon);
+ return ERROR_NT(nt_status);
+ }
- outsize = set_message(outbuf,2,0,True);
- SSVAL(outbuf,smb_vwv0,maxxmit);
- SSVAL(outbuf,smb_vwv1,connection_num);
- SSVAL(outbuf,smb_tid,connection_num);
+ outsize = set_message(outbuf,2,0,True);
+ SSVAL(outbuf,smb_vwv0,max_recv);
+ SSVAL(outbuf,smb_vwv1,conn->cnum);
+ SSVAL(outbuf,smb_tid,conn->cnum);
- DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
+ DEBUG(3,("tcon service=%s cnum=%d\n",
+ service, conn->cnum));
- return(outsize);
+ END_PROFILE(SMBtcon);
+ return(outsize);
}
-
/****************************************************************************
- reply to a tcon and X
+ Reply to a tcon and X.
****************************************************************************/
-int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+
+int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
{
- pstring service;
- pstring user;
- pstring password;
- pstring devicename;
- int connection_num;
- int outsize = 0;
- int uid = SVAL(inbuf,smb_uid);
- int vuid;
- int smb_com2 = SVAL(inbuf,smb_vwv0);
- int smb_off2 = SVAL(inbuf,smb_vwv1);
- int passlen = SVAL(inbuf,smb_vwv3);
+ fstring service;
+ DATA_BLOB password;
+ pstring devicename;
+ NTSTATUS nt_status;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ int passlen = SVAL(inbuf,smb_vwv3);
+ pstring path;
+ char *p, *q;
+ extern BOOL global_encrypted_passwords_negotiated;
+ START_PROFILE(SMBtconX);
+
+ *service = *devicename = 0;
+
+ /* we might have to close an old one */
+ if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) {
+ close_cnum(conn,vuid);
+ }
- *service = *user = *password = *devicename = 0;
+ if (passlen > MAX_PASS_LEN) {
+ return ERROR_DOS(ERRDOS,ERRbuftoosmall);
+ }
+
+ if (global_encrypted_passwords_negotiated) {
+ password = data_blob(smb_buf(inbuf),passlen);
+ } else {
+ password = data_blob(smb_buf(inbuf),passlen+1);
+ /* Ensure correct termination */
+ password.data[passlen]=0;
+ }
- /* we might have to close an old one */
- if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0)
- close_cnum(SVAL(inbuf,smb_tid),uid);
-
- vuid = valid_uid(uid);
-
- {
- char *path;
- char *p;
- memcpy(password,smb_buf(inbuf),passlen);
- password[passlen]=0;
- path = smb_buf(inbuf) + passlen;
- DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen));
- strcpy(service,path+2);
- p = strchr(service,'\\');
- if (!p)
- return(ERROR(ERRSRV,ERRinvnetname));
- *p = 0;
- strcpy(service,p+1);
- p = strchr(service,'%');
- if (p)
- {
- *p++ = 0;
- strcpy(user,p);
- }
- StrnCpy(devicename,path + strlen(path) + 1,6);
- DEBUG(4,("Got device type %s\n",devicename));
- }
+ p = smb_buf(inbuf) + passlen;
+ p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE);
+
+ /*
+ * the service name can be either: \\server\share
+ * or share directly like on the DELL PowerVault 705
+ */
+ if (*path=='\\') {
+ q = strchr_m(path+2,'\\');
+ if (!q) {
+ END_PROFILE(SMBtconX);
+ return(ERROR_DOS(ERRDOS,ERRnosuchshare));
+ }
+ fstrcpy(service,q+1);
+ }
+ else
+ fstrcpy(service,path);
+
+ p += srvstr_pull(inbuf, devicename, p, sizeof(devicename), 6, STR_ASCII);
- connection_num = make_connection(service,user,password,passlen,devicename,vuid);
-
- if (connection_num < 0)
- return(connection_error(inbuf,outbuf,connection_num));
+ DEBUG(4,("Got device type %s\n",devicename));
- outsize = set_message(outbuf,2,strlen(devicename)+1,True);
-
- DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
-
- /* set the incoming and outgoing tid to the just created one */
- SSVAL(inbuf,smb_tid,connection_num);
- SSVAL(outbuf,smb_tid,connection_num);
+ conn = make_connection(service,password,devicename,vuid,&nt_status);
+
+ data_blob_clear_free(&password);
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4);
+ if (!conn) {
+ END_PROFILE(SMBtconX);
+ return ERROR_NT(nt_status);
+ }
- strcpy(smb_buf(outbuf),devicename);
+ if (Protocol < PROTOCOL_NT1) {
+ set_message(outbuf,2,0,True);
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, devicename, -1,
+ STR_TERMINATE|STR_ASCII);
+ set_message_end(outbuf,p);
+ } else {
+ /* NT sets the fstype of IPC$ to the null string */
+ char *fsname = IS_IPC(conn) ? "" : lp_fstype(SNUM(conn));
+
+ set_message(outbuf,3,0,True);
+
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, devicename, -1,
+ STR_TERMINATE|STR_ASCII);
+ p += srvstr_push(outbuf, p, fsname, -1,
+ STR_TERMINATE);
+
+ set_message_end(outbuf,p);
+
+ /* what does setting this bit do? It is set by NT4 and
+ may affect the ability to autorun mounted cdroms */
+ SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS|
+ (lp_csc_policy(SNUM(conn)) << 2));
+
+ init_dfsroot(conn, inbuf, outbuf);
+ }
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
+
+ DEBUG(3,("tconX service=%s \n",
+ service));
+
+ /* set the incoming and outgoing tid to the just created one */
+ SSVAL(inbuf,smb_tid,conn->cnum);
+ SSVAL(outbuf,smb_tid,conn->cnum);
- return(outsize);
+ END_PROFILE(SMBtconX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
}
@@ -249,245 +309,105 @@ int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize)
****************************************************************************/
int reply_unknown(char *inbuf,char *outbuf)
{
- int cnum;
- int type;
- cnum = SVAL(inbuf,smb_tid);
- type = CVAL(inbuf,smb_com);
+ int type;
+ type = CVAL(inbuf,smb_com);
- DEBUG(0,("%s unknown command type (%s): cnum=%d type=%d (0x%X)\n",
- timestring(),
- smb_fn_name(type),
- cnum,type,type));
+ DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n",
+ smb_fn_name(type), type, type));
- return(ERROR(ERRSRV,ERRunknownsmb));
+ return(ERROR_DOS(ERRSRV,ERRunknownsmb));
}
/****************************************************************************
reply to an ioctl
****************************************************************************/
-int reply_ioctl(char *inbuf,char *outbuf)
-{
- DEBUG(3,("ignoring ioctl\n"));
-
- return(ERROR(ERRSRV,ERRnosupport));
-}
-
-
-/****************************************************************************
-reply to a session setup command
-****************************************************************************/
-int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+int reply_ioctl(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int outsize = 0;
- int sess_uid;
- int gid;
- int smb_com2;
- int smb_off2;
- int smb_bufsize;
- int smb_mpxmax;
- int smb_vc_num;
- uint32 smb_sesskey;
- int smb_apasslen;
- pstring smb_apasswd;
- int smb_ntpasslen = 0;
- pstring smb_ntpasswd;
- BOOL valid_nt_password = False;
- pstring user;
- BOOL guest=False;
-
- *smb_apasswd = 0;
-
- sess_uid = SVAL(inbuf,smb_uid);
- smb_com2 = CVAL(inbuf,smb_vwv0);
- smb_off2 = SVAL(inbuf,smb_vwv1);
- smb_bufsize = SVAL(inbuf,smb_vwv2);
- smb_mpxmax = SVAL(inbuf,smb_vwv3);
- smb_vc_num = SVAL(inbuf,smb_vwv4);
- smb_sesskey = IVAL(inbuf,smb_vwv5);
-
- if (Protocol < PROTOCOL_NT1) {
- smb_apasslen = SVAL(inbuf,smb_vwv7);
- memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen);
- StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1);
- } else {
- uint16 passlen1 = SVAL(inbuf,smb_vwv7);
- uint16 passlen2 = SVAL(inbuf,smb_vwv8);
- BOOL doencrypt = SMBENCRYPT();
- char *p = smb_buf(inbuf);
- if (passlen1 > 256) passlen1 = 0;
- if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird
- lengths sometimes */
- if(doencrypt) {
- /* Save the lanman2 password and the NT md4 password. */
- smb_apasslen = passlen1;
- memcpy(smb_apasswd,p,smb_apasslen);
- smb_ntpasslen = passlen2;
- memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen);
- } else {
- /* for Win95 */
- if (passlen1 > passlen2) {
- smb_apasslen = passlen1;
- StrnCpy(smb_apasswd,p,smb_apasslen);
- } else {
- smb_apasslen = passlen2;
- StrnCpy(smb_apasswd,p + passlen1,smb_apasslen);
- }
- }
- if (passlen2 == 1) {
- /* apparently NT sometimes sets passlen2 to 1 when it means 0. This
- tries to work around that problem */
- passlen2 = 0;
- }
- p += passlen1 + passlen2;
- strcpy(user,p); p = skip_string(p,1);
- DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n",
- p,skip_string(p,1),skip_string(p,2)));
- }
-
-
- DEBUG(3,("sesssetupX:name=[%s]\n",user));
-
- if (!*user)
- strcpy(user,lp_guestaccount(-1));
-
- strlower(user);
-
- strcpy(sesssetup_user,user);
-
- reload_services(True);
+ uint16 device = SVAL(inbuf,smb_vwv1);
+ uint16 function = SVAL(inbuf,smb_vwv2);
+ uint32 ioctl_code = (device << 16) + function;
+ int replysize, outsize;
+ char *p;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBioctl);
- add_session_user(user);
+ DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code));
-
- if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) &&
- !check_hosts_equiv(user))
- {
-
- if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0))
- guest = True;
-
- /* now check if it's a valid username/password */
- /* If an NT password was supplied try and validate with that
- first. This is superior as the passwords are mixed case 128 length unicode */
- if(smb_ntpasslen && !guest)
- {
- if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL,True))
- DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n"));
- else
- valid_nt_password = True;
- }
- if (!valid_nt_password && !guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL,False))
+ switch (ioctl_code)
{
- if (lp_security() >= SEC_USER) {
-#if (GUEST_SESSSETUP == 0)
- return(ERROR(ERRSRV,ERRbadpw));
-#endif
-#if (GUEST_SESSSETUP == 1)
- if (Get_Pwnam(user,True))
- return(ERROR(ERRSRV,ERRbadpw));
-#endif
- }
- if (*smb_apasswd || !Get_Pwnam(user,True))
- strcpy(user,lp_guestaccount(-1));
- DEBUG(3,("Registered username %s for guest access\n",user));
- guest = True;
+ case IOCTL_QUERY_JOB_INFO:
+ replysize = 32;
+ break;
+ default:
+ END_PROFILE(SMBioctl);
+ return(ERROR_DOS(ERRSRV,ERRnosupport));
}
- }
-
- if (!Get_Pwnam(user,True)) {
- DEBUG(3,("No such user %s - using guest account\n",user));
- strcpy(user,lp_guestaccount(-1));
- guest = True;
- }
-
- if (!strequal(user,lp_guestaccount(-1)) &&
- lp_servicenumber(user) < 0)
- {
- int homes = lp_servicenumber(HOMES_NAME);
- char *home = get_home_dir(user);
- if (homes >= 0 && home)
- lp_add_home(user,homes,home);
- }
-
-
- /* it's ok - setup a reply */
- if (Protocol < PROTOCOL_NT1) {
- outsize = set_message(outbuf,3,0,True);
- } else {
- char *p;
- outsize = set_message(outbuf,3,3,True);
- p = smb_buf(outbuf);
- strcpy(p,"Unix"); p = skip_string(p,1);
- strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1);
- strcpy(p,my_workgroup()); p = skip_string(p,1);
- outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False);
- /* perhaps grab OS version here?? */
- }
-
- /* Set the correct uid in the outgoing and incoming packets
- We will use this on future requests to determine which
- user we should become.
- */
- {
- struct passwd *pw = Get_Pwnam(user,False);
- if (!pw) {
- DEBUG(1,("Username %s is invalid on this system\n",user));
- return(ERROR(ERRSRV,ERRbadpw));
- }
- gid = pw->pw_gid;
- SSVAL(outbuf,smb_uid,(uint16)pw->pw_uid);
- SSVAL(inbuf,smb_uid,(uint16)pw->pw_uid);
- }
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+ outsize = set_message(outbuf,8,replysize+1,True);
+ SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */
+ SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */
+ SSVAL(outbuf,smb_vwv6,52); /* Offset to data */
+ p = smb_buf(outbuf) + 1; /* Allow for alignment */
- if (guest)
- SSVAL(outbuf,smb_vwv2,1);
-
- /* register the name and uid as being validated, so further connections
- to a uid can get through without a password, on the same VC */
- register_uid(SVAL(inbuf,smb_uid),gid,user,guest);
-
- maxxmit = MIN(maxxmit,smb_bufsize);
-
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
+ switch (ioctl_code)
+ {
+ case IOCTL_QUERY_JOB_INFO:
+ SSVAL(p,0,fsp->print_jobid); /* Job number */
+ srvstr_push(outbuf, p+2, global_myname, 15, STR_TERMINATE|STR_ASCII);
+ srvstr_push(outbuf, p+18, lp_servicename(SNUM(conn)), 13, STR_TERMINATE|STR_ASCII);
+ break;
+ }
- return(outsize);
+ END_PROFILE(SMBioctl);
+ return outsize;
}
-
/****************************************************************************
reply to a chkpth
****************************************************************************/
-int reply_chkpth(char *inbuf,char *outbuf)
+int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
int outsize = 0;
- int cnum,mode;
+ int mode;
pstring name;
BOOL ok = False;
-
- cnum = SVAL(inbuf,smb_tid);
-
- strcpy(name,smb_buf(inbuf) + 1);
- unix_convert(name,cnum);
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ START_PROFILE(SMBchkpth);
+
+ srvstr_pull(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), -1, STR_TERMINATE);
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+
+ unix_convert(name,conn,0,&bad_path,&sbuf);
mode = SVAL(inbuf,smb_vwv0);
- if (check_name(name,cnum))
- ok = directory_exist(name,NULL);
+ if (check_name(name,conn)) {
+ if (VALID_STAT(sbuf) || vfs_stat(conn,name,&sbuf) == 0)
+ ok = S_ISDIR(sbuf.st_mode);
+ }
+
+ if (!ok) {
+ /* We special case this - as when a Windows machine
+ is parsing a path is steps through the components
+ one at a time - if a component fails it expects
+ ERRbadpath, not ERRbadfile.
+ */
+ if(errno == ENOENT) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
- if (!ok)
- return(ERROR(ERRDOS,ERRbadpath));
-
outsize = set_message(outbuf,0,0,True);
-
- DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode));
-
+
+ DEBUG(3,("chkpth %s mode=%d\n", name, mode));
+
+ END_PROFILE(SMBchkpth);
return(outsize);
}
@@ -495,67 +415,75 @@ int reply_chkpth(char *inbuf,char *outbuf)
/****************************************************************************
reply to a getatr
****************************************************************************/
-int reply_getatr(char *inbuf,char *outbuf)
+int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring fname;
- int cnum;
int outsize = 0;
- struct stat sbuf;
+ SMB_STRUCT_STAT sbuf;
BOOL ok = False;
int mode=0;
- uint32 size=0;
+ SMB_OFF_T size=0;
time_t mtime=0;
-
- cnum = SVAL(inbuf,smb_tid);
+ BOOL bad_path = False;
+ char *p;
+ START_PROFILE(SMBgetatr);
- strcpy(fname,smb_buf(inbuf) + 1);
- unix_convert(fname,cnum);
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_pull(inbuf, fname, p, sizeof(fname), -1, STR_TERMINATE);
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
/* dos smetimes asks for a stat of "" - it returns a "hidden directory"
under WfWg - weird! */
if (! (*fname))
- {
- mode = aHIDDEN | aDIR;
- if (!CAN_WRITE(cnum)) mode |= aRONLY;
- size = 0;
- mtime = 0;
- ok = True;
- }
+ {
+ mode = aHIDDEN | aDIR;
+ if (!CAN_WRITE(conn)) mode |= aRONLY;
+ size = 0;
+ mtime = 0;
+ ok = True;
+ }
else
- if (check_name(fname,cnum))
+ {
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (check_name(fname,conn))
+ {
+ if (VALID_STAT(sbuf) || vfs_stat(conn,fname,&sbuf) == 0)
{
- if (sys_stat(fname,&sbuf) == 0)
- {
- mode = dos_mode(cnum,fname,&sbuf);
- size = sbuf.st_size;
- mtime = sbuf.st_mtime;
- if (mode & aDIR)
- size = 0;
- ok = True;
- }
- else
- DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
+ mode = dos_mode(conn,fname,&sbuf);
+ size = sbuf.st_size;
+ mtime = sbuf.st_mtime;
+ if (mode & aDIR)
+ size = 0;
+ ok = True;
+ }
+ else
+ DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
}
+ }
if (!ok)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBgetatr);
return(UNIXERROR(ERRDOS,ERRbadfile));
-
+ }
+
outsize = set_message(outbuf,10,0,True);
SSVAL(outbuf,smb_vwv0,mode);
- put_dos_date3(outbuf,smb_vwv1,mtime);
- SIVAL(outbuf,smb_vwv3,size);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv1,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv1,mtime);
+ SIVAL(outbuf,smb_vwv3,(uint32)size);
- if (Protocol >= PROTOCOL_NT1) {
- char *p = strrchr(fname,'/');
- uint16 flg2 = SVAL(outbuf,smb_flg2);
- if (!p) p = fname;
- if (!is_8_3(fname))
- SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
- }
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME);
- DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size));
+ DEBUG( 3, ( "getatr name=%s mode=%d size=%d\n", fname, mode, (uint32)size ) );
+ END_PROFILE(SMBgetatr);
return(outsize);
}
@@ -563,37 +491,48 @@ int reply_getatr(char *inbuf,char *outbuf)
/****************************************************************************
reply to a setatr
****************************************************************************/
-int reply_setatr(char *inbuf,char *outbuf)
+int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring fname;
- int cnum;
int outsize = 0;
BOOL ok=False;
int mode;
time_t mtime;
-
- cnum = SVAL(inbuf,smb_tid);
-
- strcpy(fname,smb_buf(inbuf) + 1);
- unix_convert(fname,cnum);
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ char *p;
+
+ START_PROFILE(SMBsetatr);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_pull(inbuf, fname, p, sizeof(fname), -1, STR_TERMINATE);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
mode = SVAL(inbuf,smb_vwv0);
mtime = make_unix_date3(inbuf+smb_vwv1);
- if (directory_exist(fname,NULL))
+ if (VALID_STAT_OF_DIR(sbuf))
mode |= aDIR;
- if (check_name(fname,cnum))
- ok = (dos_chmod(cnum,fname,mode,NULL) == 0);
+ else
+ mode &= ~aDIR;
+
+ if (check_name(fname,conn))
+ ok = (file_chmod(conn,fname,mode,NULL) == 0);
if (ok)
- ok = set_filetime(fname,mtime);
+ ok = set_filetime(conn,fname,mtime);
if (!ok)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBsetatr);
return(UNIXERROR(ERRDOS,ERRnoaccess));
-
+ }
+
outsize = set_message(outbuf,0,0,True);
- DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode));
+ DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) );
+ END_PROFILE(SMBsetatr);
return(outsize);
}
@@ -601,15 +540,13 @@ int reply_setatr(char *inbuf,char *outbuf)
/****************************************************************************
reply to a dskattr
****************************************************************************/
-int reply_dskattr(char *inbuf,char *outbuf)
+int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum;
int outsize = 0;
- int dfree,dsize,bsize;
+ SMB_BIG_UINT dfree,dsize,bsize;
+ START_PROFILE(SMBdskattr);
- cnum = SVAL(inbuf,smb_tid);
-
- sys_disk_free(".",&bsize,&dfree,&dsize);
+ conn->vfs_ops.disk_free(conn,".",True,&bsize,&dfree,&dsize);
outsize = set_message(outbuf,5,0,True);
@@ -617,9 +554,10 @@ int reply_dskattr(char *inbuf,char *outbuf)
SSVAL(outbuf,smb_vwv1,bsize/512);
SSVAL(outbuf,smb_vwv2,512);
SSVAL(outbuf,smb_vwv3,dfree);
-
- DEBUG(3,("%s dskattr cnum=%d dfree=%d\n",timestring(),cnum,dfree));
-
+
+ DEBUG(3,("dskattr dfree=%d\n", (unsigned int)dfree));
+
+ END_PROFILE(SMBdskattr);
return(outsize);
}
@@ -628,15 +566,15 @@ int reply_dskattr(char *inbuf,char *outbuf)
reply to a search
Can be called from SMBsearch, SMBffirst or SMBfunique.
****************************************************************************/
-int reply_search(char *inbuf,char *outbuf)
+int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring mask;
pstring directory;
pstring fname;
- int size,mode;
+ SMB_OFF_T size;
+ int mode;
time_t date;
int dirtype;
- int cnum;
int outsize = 0;
int numentries = 0;
BOOL finished = False;
@@ -645,12 +583,14 @@ int reply_search(char *inbuf,char *outbuf)
char *p;
BOOL ok = False;
int status_len;
- char *path;
+ pstring path;
char status[21];
int dptr_num= -1;
BOOL check_descend = False;
BOOL expect_close = False;
BOOL can_open = True;
+ BOOL bad_path = False;
+ START_PROFILE(SMBsearch);
*mask = *directory = *fname = 0;
@@ -658,197 +598,171 @@ int reply_search(char *inbuf,char *outbuf)
if(CVAL(inbuf,smb_com) == SMBffirst)
expect_close = True;
- cnum = SVAL(inbuf,smb_tid);
-
outsize = set_message(outbuf,1,3,True);
maxentries = SVAL(inbuf,smb_vwv0);
dirtype = SVAL(inbuf,smb_vwv1);
- path = smb_buf(inbuf) + 1;
- status_len = SVAL(smb_buf(inbuf),3 + strlen(path));
-
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE);
+ p++;
+ status_len = SVAL(p, 0);
+ p += 2;
/* dirtype &= ~aDIR; */
- DEBUG(5,("path=%s status_len=%d\n",path,status_len));
-
-
if (status_len == 0)
- {
- pstring dir2;
-
- strcpy(directory,smb_buf(inbuf)+1);
- strcpy(dir2,smb_buf(inbuf)+1);
- unix_convert(directory,cnum);
- unix_format(dir2);
+ {
+ SMB_STRUCT_STAT sbuf;
+ pstring dir2;
- if (!check_name(directory,cnum))
- can_open = False;
+ pstrcpy(directory,path);
+ pstrcpy(dir2,path);
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ unix_format(dir2);
- p = strrchr(dir2,'/');
- if (p == NULL)
- {strcpy(mask,dir2);*dir2 = 0;}
- else
- {*p = 0;strcpy(mask,p+1);}
-
- p = strrchr(directory,'/');
- if (!p)
- *directory = 0;
- else
- *p = 0;
+ if (!check_name(directory,conn))
+ can_open = False;
- if (strlen(directory) == 0)
- strcpy(directory,"./");
- bzero(status,21);
- CVAL(status,0) = dirtype;
+ p = strrchr_m(dir2,'/');
+ if (p == NULL)
+ {
+ pstrcpy(mask,dir2);
+ *dir2 = 0;
}
- else
+ else
{
- memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
- memcpy(mask,status+1,11);
- mask[11] = 0;
- dirtype = CVAL(status,0) & 0x1F;
- Connections[cnum].dirptr = dptr_fetch(status+12,&dptr_num);
- if (!Connections[cnum].dirptr)
- goto SearchEmpty;
- string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
- if (!case_sensitive)
- strnorm(mask);
+ *p = 0;
+ pstrcpy(mask,p+1);
}
- /* turn strings of spaces into a . */
+ p = strrchr_m(directory,'/');
+ if (!p)
+ *directory = 0;
+ else
+ *p = 0;
+
+ if (strlen(directory) == 0)
+ pstrcpy(directory,"./");
+ memset((char *)status,'\0',21);
+ SCVAL(status,0,dirtype);
+ }
+ else
{
- trim_string(mask,NULL," ");
- if ((p = strrchr(mask,' ')))
- {
- fstring ext;
- strcpy(ext,p+1);
- *p = 0;
- trim_string(mask,NULL," ");
- strcat(mask,".");
- strcat(mask,ext);
- }
+ memcpy(status,p,21);
+ dirtype = CVAL(status,0) & 0x1F;
+ conn->dirptr = dptr_fetch(status+12,&dptr_num);
+ if (!conn->dirptr)
+ goto SearchEmpty;
+ string_set(&conn->dirpath,dptr_path(dptr_num));
+ fstrcpy(mask, dptr_wcard(dptr_num));
}
+ if (can_open)
{
- for (p=mask; *p; p++)
+ p = smb_buf(outbuf) + 3;
+
+ ok = True;
+
+ if (status_len == 0)
+ {
+ dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0)
{
- if (*p != '?' && *p != '*' && !isdoschar(*p))
- {
- DEBUG(5,("Invalid char [%c] in search mask?\n",*p));
- *p = '?';
- }
+ if(dptr_num == -2)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBsearch);
+ return (UNIXERROR(ERRDOS,ERRnofids));
+ }
+ END_PROFILE(SMBsearch);
+ return ERROR_DOS(ERRDOS,ERRnofids);
}
- }
-
- if (!strchr(mask,'.') && strlen(mask)>8)
- {
- fstring tmp;
- strcpy(tmp,&mask[8]);
- mask[8] = '.';
- mask[9] = 0;
- strcat(mask,tmp);
+ dptr_set_wcard(dptr_num, strdup(mask));
}
- DEBUG(5,("mask=%s directory=%s\n",mask,directory));
-
- if (can_open)
- {
- p = smb_buf(outbuf) + 3;
-
- ok = True;
-
- if (status_len == 0)
- {
- dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid));
- if (dptr_num < 0)
- return(ERROR(ERRDOS,ERRnofids));
- }
-
- DEBUG(4,("dptr_num is %d\n",dptr_num));
+ DEBUG(4,("dptr_num is %d\n",dptr_num));
- if (ok)
- {
- if ((dirtype&0x1F) == aVOLID)
- {
- memcpy(p,status,21);
- make_dir_struct(p,"???????????",volume_label(SNUM(cnum)),0,aVOLID,0);
- dptr_fill(p+12,dptr_num);
- if (dptr_zero(p+12) && (status_len==0))
- numentries = 1;
- else
- numentries = 0;
- p += DIR_STRUCT_SIZE;
- }
- else
- {
- DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
- if (in_list(Connections[cnum].dirpath,
- lp_dontdescend(SNUM(cnum)),True))
- check_descend = True;
-
- for (i=numentries;(i<maxentries) && !finished;i++)
- {
- finished =
- !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend);
- if (!finished)
- {
- memcpy(p,status,21);
- make_dir_struct(p,mask,fname,size,mode,date);
- dptr_fill(p+12,dptr_num);
- numentries++;
- }
- p += DIR_STRUCT_SIZE;
- }
- }
- }
- }
+ if (ok)
+ {
+ if ((dirtype&0x1F) == aVOLID)
+ {
+ memcpy(p,status,21);
+ make_dir_struct(p,"???????????",volume_label(SNUM(conn)),0,aVOLID,0);
+ dptr_fill(p+12,dptr_num);
+ if (dptr_zero(p+12) && (status_len==0))
+ numentries = 1;
+ else
+ numentries = 0;
+ p += DIR_STRUCT_SIZE;
+ }
+ else
+ {
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+ conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True))
+ check_descend = True;
+
+ for (i=numentries;(i<maxentries) && !finished;i++)
+ {
+ finished =
+ !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend);
+ if (!finished)
+ {
+ memcpy(p,status,21);
+ make_dir_struct(p,mask,fname,size,mode,date);
+ dptr_fill(p+12,dptr_num);
+ numentries++;
+ }
+ p += DIR_STRUCT_SIZE;
+ }
+ }
+ } /* if (ok ) */
+ }
- SearchEmpty:
+ SearchEmpty:
if (numentries == 0 || !ok)
- {
- CVAL(outbuf,smb_rcls) = ERRDOS;
- SSVAL(outbuf,smb_err,ERRnofiles);
- }
+ {
+ SCVAL(outbuf,smb_rcls,ERRDOS);
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ dptr_close(&dptr_num);
+ }
/* If we were called as SMBffirst with smb_search_id == NULL
and no entries were found then return error and close dirptr
(X/Open spec) */
if(ok && expect_close && numentries == 0 && status_len == 0)
- {
- CVAL(outbuf,smb_rcls) = ERRDOS;
- SSVAL(outbuf,smb_err,ERRnofiles);
- /* Also close the dptr - we know it's gone */
- dptr_close(dptr_num);
- }
+ {
+ SCVAL(outbuf,smb_rcls,ERRDOS);
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ /* Also close the dptr - we know it's gone */
+ dptr_close(&dptr_num);
+ }
/* If we were called as SMBfunique, then we can close the dirptr now ! */
if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique)
- dptr_close(dptr_num);
+ dptr_close(&dptr_num);
SSVAL(outbuf,smb_vwv0,numentries);
SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE);
- CVAL(smb_buf(outbuf),0) = 5;
+ SCVAL(smb_buf(outbuf),0,5);
SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE);
- if (Protocol >= PROTOCOL_NT1) {
- uint16 flg2 = SVAL(outbuf,smb_flg2);
- SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
- }
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME);
outsize += DIR_STRUCT_SIZE*numentries;
smb_setlen(outbuf,outsize - 4);
if ((! *directory) && dptr_path(dptr_num))
- sprintf(directory,"(%s)",dptr_path(dptr_num));
+ slprintf(directory, sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
- DEBUG(4,("%s %s mask=%s path=%s cnum=%d dtype=%d nument=%d of %d\n",
- timestring(),
- smb_fn_name(CVAL(inbuf,smb_com)),
- mask,directory,cnum,dirtype,numentries,maxentries));
+ DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%d of %d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries, maxentries ) );
+ END_PROFILE(SMBsearch);
return(outsize);
}
@@ -856,36 +770,41 @@ int reply_search(char *inbuf,char *outbuf)
/****************************************************************************
reply to a fclose (stop directory search)
****************************************************************************/
-int reply_fclose(char *inbuf,char *outbuf)
+int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum;
int outsize = 0;
int status_len;
- char *path;
+ pstring path;
char status[21];
- int dptr_num= -1;
+ int dptr_num= -2;
+ char *p;
- cnum = SVAL(inbuf,smb_tid);
+ START_PROFILE(SMBfclose);
outsize = set_message(outbuf,1,0,True);
- path = smb_buf(inbuf) + 1;
- status_len = SVAL(smb_buf(inbuf),3 + strlen(path));
-
-
- if (status_len == 0)
- return(ERROR(ERRSRV,ERRsrverror));
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE);
+ p++;
+ status_len = SVAL(p,0);
+ p += 2;
+
+ if (status_len == 0) {
+ END_PROFILE(SMBfclose);
+ return ERROR_DOS(ERRSRV,ERRsrverror);
+ }
- memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
+ memcpy(status,p,21);
if(dptr_fetch(status+12,&dptr_num)) {
/* Close the dptr - we know it's gone */
- dptr_close(dptr_num);
+ dptr_close(&dptr_num);
}
SSVAL(outbuf,smb_vwv0,0);
- DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum));
+ DEBUG(3,("search close\n"));
+ END_PROFILE(SMBfclose);
return(outsize);
}
@@ -893,63 +812,71 @@ int reply_fclose(char *inbuf,char *outbuf)
/****************************************************************************
reply to an open
****************************************************************************/
-int reply_open(char *inbuf,char *outbuf)
+
+int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring fname;
- int cnum;
- int fnum = -1;
int outsize = 0;
int fmode=0;
int share_mode;
- int size = 0;
+ SMB_OFF_T size = 0;
time_t mtime=0;
- int unixmode;
+ mode_t unixmode;
int rmode=0;
- struct stat sbuf;
-
- cnum = SVAL(inbuf,smb_tid);
-
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ START_PROFILE(SMBopen);
+
share_mode = SVAL(inbuf,smb_vwv0);
- strcpy(fname,smb_buf(inbuf)+1);
- unix_convert(fname,cnum);
-
- fnum = find_free_file();
- if (fnum < 0)
- return(ERROR(ERRSRV,ERRnofids));
+ srvstr_pull(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), -1, STR_TERMINATE);
- if (!check_name(fname,cnum))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- unixmode = unix_mode(cnum,aARCH);
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ unixmode = unix_mode(conn,aARCH,fname);
- open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL);
+ fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ unixmode, oplock_request,&rmode,NULL);
- if (!Files[fnum].open)
+ if (!fsp)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBopen);
return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- if (fstat(Files[fnum].fd,&sbuf) != 0) {
- close_file(fnum);
- return(ERROR(ERRDOS,ERRnoaccess));
}
-
+
size = sbuf.st_size;
- fmode = dos_mode(cnum,fname,&sbuf);
+ fmode = dos_mode(conn,fname,&sbuf);
mtime = sbuf.st_mtime;
if (fmode & aDIR) {
DEBUG(3,("attempt to open a directory %s\n",fname));
- close_file(fnum);
- return(ERROR(ERRDOS,ERRnoaccess));
+ close_file(fsp,False);
+ END_PROFILE(SMBopen);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
}
outsize = set_message(outbuf,7,0,True);
- SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
SSVAL(outbuf,smb_vwv1,fmode);
- put_dos_date3(outbuf,smb_vwv2,mtime);
- SIVAL(outbuf,smb_vwv4,size);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv2,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv2,mtime);
+ SIVAL(outbuf,smb_vwv4,(uint32)size);
SSVAL(outbuf,smb_vwv6,rmode);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ }
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ END_PROFILE(SMBopen);
return(outsize);
}
@@ -957,165 +884,211 @@ int reply_open(char *inbuf,char *outbuf)
/****************************************************************************
reply to an open and X
****************************************************************************/
-int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
{
pstring fname;
- int cnum = SVAL(inbuf,smb_tid);
- int fnum = -1;
- int outsize = 0;
- int openmode = 0;
- int smb_com2 = CVAL(inbuf,smb_vwv0);
- int smb_off2 = SVAL(inbuf,smb_vwv1);
int smb_mode = SVAL(inbuf,smb_vwv3);
int smb_attr = SVAL(inbuf,smb_vwv5);
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf);
+ BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ BOOL oplock_request = ex_oplock_request | core_oplock_request;
#if 0
int open_flags = SVAL(inbuf,smb_vwv2);
int smb_sattr = SVAL(inbuf,smb_vwv4);
uint32 smb_time = make_unix_date3(inbuf+smb_vwv6);
#endif
int smb_ofun = SVAL(inbuf,smb_vwv8);
- int unixmode;
- int size=0,fmode=0,mtime=0,rmode=0;
- struct stat sbuf;
+ mode_t unixmode;
+ SMB_OFF_T size=0;
+ int fmode=0,mtime=0,rmode=0;
+ SMB_STRUCT_STAT sbuf;
int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ START_PROFILE(SMBopenX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support()) {
+ END_PROFILE(SMBopenX);
+ return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize);
+ } else {
+ END_PROFILE(SMBopenX);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+ }
/* XXXX we need to handle passed times, sattr and flags */
+ srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE);
- strcpy(fname,smb_buf(inbuf));
- unix_convert(fname,cnum);
-
- /* now add create and trunc bits */
- if (smb_ofun & 0x10)
- openmode |= O_CREAT;
- if ((smb_ofun & 0x3) == 2)
- openmode |= O_TRUNC;
-
- fnum = find_free_file();
- if (fnum < 0)
- return(ERROR(ERRSRV,ERRnofids));
-
- if (!check_name(fname,cnum))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
- unixmode = unix_mode(cnum,smb_attr | aARCH);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ unixmode = unix_mode(conn,smb_attr | aARCH, fname);
- open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode,
- &rmode,&smb_action);
+ fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,unixmode,
+ oplock_request, &rmode,&smb_action);
- if (!Files[fnum].open)
+ if (!fsp)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBopenX);
return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- if (fstat(Files[fnum].fd,&sbuf) != 0) {
- close_file(fnum);
- return(ERROR(ERRDOS,ERRnoaccess));
}
size = sbuf.st_size;
- fmode = dos_mode(cnum,fname,&sbuf);
+ fmode = dos_mode(conn,fname,&sbuf);
mtime = sbuf.st_mtime;
if (fmode & aDIR) {
- close_file(fnum);
- return(ERROR(ERRDOS,ERRnoaccess));
+ close_file(fsp,False);
+ END_PROFILE(SMBopenX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
}
- outsize = set_message(outbuf,15,0,True);
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
- SSVAL(outbuf,smb_vwv2,fnum);
+ /* If the caller set the extended oplock request bit
+ and we granted one (by whatever means) - set the
+ correct bit for extended oplock reply.
+ */
+
+ if (ex_oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+ }
+
+ if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+ }
+
+ /* If the caller set the core oplock request bit
+ and we granted one (by whatever means) - set the
+ correct bit for core oplock reply.
+ */
+
+ if (core_oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ }
+
+ if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ }
+
+ set_message(outbuf,15,0,True);
+ SSVAL(outbuf,smb_vwv2,fsp->fnum);
SSVAL(outbuf,smb_vwv3,fmode);
- put_dos_date3(outbuf,smb_vwv4,mtime);
- SIVAL(outbuf,smb_vwv6,size);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv4,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,(uint32)size);
SSVAL(outbuf,smb_vwv8,rmode);
SSVAL(outbuf,smb_vwv11,smb_action);
- chain_fnum = fnum;
-
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
-
- chain_fnum = -1;
-
- return(outsize);
+ END_PROFILE(SMBopenX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
}
/****************************************************************************
reply to a SMBulogoffX
****************************************************************************/
-int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize)
+int reply_ulogoffX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
{
- int outsize = 0;
- int smb_com2 = CVAL(inbuf,smb_vwv0);
- int smb_off2 = SVAL(inbuf,smb_vwv1);
- int uid = SVAL(inbuf,smb_uid);
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ user_struct *vuser = get_valid_user_struct(vuid);
+ START_PROFILE(SMBulogoffX);
- invalidate_uid(uid);
+ if(vuser == 0) {
+ DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid));
+ }
- outsize = set_message(outbuf,2,0,True);
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+ /* in user level security we are supposed to close any files
+ open by this user */
+ if ((vuser != 0) && (lp_security() != SEC_SHARE)) {
+ file_close_user(vuid);
+ }
- DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid));
+ invalidate_vuid(vuid);
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
+ set_message(outbuf,2,0,True);
-
- return(outsize);
+ DEBUG( 3, ( "ulogoffX vuid=%d\n", vuid ) );
+
+ END_PROFILE(SMBulogoffX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
}
/****************************************************************************
- reply to a mknew
+ reply to a mknew or a create
****************************************************************************/
-int reply_mknew(char *inbuf,char *outbuf)
+int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring fname;
- int cnum,com;
- int fnum = -1;
+ int com;
int outsize = 0;
int createmode;
mode_t unixmode;
-
+ int ofun = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ SMB_STRUCT_STAT sbuf;
+ START_PROFILE(SMBcreate);
+
com = SVAL(inbuf,smb_com);
- cnum = SVAL(inbuf,smb_tid);
createmode = SVAL(inbuf,smb_vwv0);
- strcpy(fname,smb_buf(inbuf)+1);
- unix_convert(fname,cnum);
+ srvstr_pull(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), -1, STR_TERMINATE);
- if (createmode & aVOLID)
- {
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ if (createmode & aVOLID) {
DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
- }
+ }
- unixmode = unix_mode(cnum,createmode);
+ unixmode = unix_mode(conn,createmode,fname);
- if (com == SMBmknew && file_exist(fname,NULL))
- return(ERROR(ERRDOS,ERRfilexists));
-
- fnum = find_free_file();
- if (fnum < 0)
- return(ERROR(ERRSRV,ERRnofids));
-
- if (!check_name(fname,cnum))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ if(com == SMBmknew)
+ {
+ /* We should fail if file exists. */
+ ofun = FILE_CREATE_IF_NOT_EXIST;
+ }
+ else
+ {
+ /* SMBcreate - Create if file doesn't exist, truncate if it does. */
+ ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE;
+ }
- open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode);
+ /* Open file in dos compatibility share mode. */
+ fsp = open_file_shared(conn,fname,&sbuf,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB),
+ ofun, unixmode, oplock_request, NULL, NULL);
- if (!Files[fnum].open)
+ if (!fsp)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBcreate);
return(UNIXERROR(ERRDOS,ERRnoaccess));
-
+ }
+
outsize = set_message(outbuf,1,0,True);
- SSVAL(outbuf,smb_vwv0,fnum);
-
- DEBUG(2,("new file %s\n",fname));
- DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode));
-
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ }
+
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ DEBUG( 2, ( "new file %s\n", fname ) );
+ DEBUG( 3, ( "mknew %s fd=%d dmode=%d umode=%o\n",
+ fname, fsp->fd, createmode, (int)unixmode ) );
+
+ END_PROFILE(SMBcreate);
return(outsize);
}
@@ -1123,343 +1096,561 @@ int reply_mknew(char *inbuf,char *outbuf)
/****************************************************************************
reply to a create temporary file
****************************************************************************/
-int reply_ctemp(char *inbuf,char *outbuf)
+int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring fname;
- pstring fname2;
- int cnum;
- int fnum = -1;
int outsize = 0;
int createmode;
mode_t unixmode;
-
- cnum = SVAL(inbuf,smb_tid);
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ int tmpfd;
+ SMB_STRUCT_STAT sbuf;
+ char *p, *s;
+
+ START_PROFILE(SMBctemp);
+
createmode = SVAL(inbuf,smb_vwv0);
- sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1);
- unix_convert(fname,cnum);
+ srvstr_pull(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), -1, STR_TERMINATE);
+ pstrcat(fname,"\\TMXXXXXX");
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
- unixmode = unix_mode(cnum,createmode);
+ unixmode = unix_mode(conn,createmode,fname);
- fnum = find_free_file();
- if (fnum < 0)
- return(ERROR(ERRSRV,ERRnofids));
+ tmpfd = smb_mkstemp(fname);
+ if (tmpfd == -1) {
+ END_PROFILE(SMBctemp);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- if (!check_name(fname,cnum))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ vfs_stat(conn,fname,&sbuf);
- strcpy(fname2,(char *)mktemp(fname));
+ /* Open file in dos compatibility share mode. */
+ /* We should fail if file does not exist. */
+ fsp = open_file_shared(conn,fname,&sbuf,
+ SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB),
+ FILE_EXISTS_OPEN|FILE_FAIL_IF_NOT_EXIST,
+ unixmode, oplock_request, NULL, NULL);
- open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode);
+ /* close fd from smb_mkstemp() */
+ close(tmpfd);
- if (!Files[fnum].open)
+ if (!fsp) {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBctemp);
return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- outsize = set_message(outbuf,1,2 + strlen(fname2),True);
- SSVAL(outbuf,smb_vwv0,fnum);
- CVAL(smb_buf(outbuf),0) = 4;
- strcpy(smb_buf(outbuf) + 1,fname2);
-
- DEBUG(2,("created temp file %s\n",fname2));
- DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode));
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ /* the returned filename is relative to the directory */
+ s = strrchr_m(fname, '/');
+ if (!s) {
+ s = fname;
+ } else {
+ s++;
+ }
+
+ p = smb_buf(outbuf);
+ SSVALS(p, 0, -1); /* what is this? not in spec */
+ SSVAL(p, 2, strlen(s));
+ p += 4;
+ p += srvstr_push(outbuf, p, s, -1, STR_ASCII);
+ outsize = set_message_end(outbuf, p);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ }
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ DEBUG( 2, ( "created temp file %s\n", fname ) );
+ DEBUG( 3, ( "ctemp %s fd=%d dmode=%d umode=%o\n",
+ fname, fsp->fd, createmode, (int)unixmode ) );
+
+ END_PROFILE(SMBctemp);
return(outsize);
}
+/*******************************************************************
+ Check if a user is allowed to rename a file.
+********************************************************************/
+
+static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst)
+{
+ int smb_action;
+ int access_mode;
+ files_struct *fsp;
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (S_ISDIR(pst->st_mode))
+ return NT_STATUS_OK;
+
+ /* We need a better way to return NT status codes from open... */
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+
+ fsp = open_file_shared1(conn, fname, pst, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action);
+
+ if (!fsp) {
+ NTSTATUS ret = NT_STATUS_ACCESS_DENIED;
+ if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ ret = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ return ret;
+ }
+ close_file(fsp,False);
+ return NT_STATUS_OK;
+}
/*******************************************************************
-check if a user is allowed to delete a file
+ Check if a user is allowed to delete a file.
********************************************************************/
-static BOOL can_delete(char *fname,int cnum,int dirtype)
+
+static NTSTATUS can_delete(char *fname,connection_struct *conn, int dirtype)
{
- struct stat sbuf;
- int fmode;
-
- if (!CAN_WRITE(cnum)) return(False);
-
- if (sys_lstat(fname,&sbuf) != 0) return(False);
- fmode = dos_mode(cnum,fname,&sbuf);
- if (fmode & aDIR) return(False);
- if (fmode & aRONLY) return(False);
- if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
- return(False);
- if (!check_file_sharing(cnum,fname)) return(False);
- return(True);
+ SMB_STRUCT_STAT sbuf;
+ int fmode;
+ int smb_action;
+ int access_mode;
+ files_struct *fsp;
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (conn->vfs_ops.lstat(conn,fname,&sbuf) != 0)
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ fmode = dos_mode(conn,fname,&sbuf);
+ if (fmode & aDIR)
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ if (!lp_delete_readonly(SNUM(conn))) {
+ if (fmode & aRONLY)
+ return NT_STATUS_CANNOT_DELETE;
+ }
+ if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
+ return NT_STATUS_CANNOT_DELETE;
+
+ /* We need a better way to return NT status codes from open... */
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+
+ fsp = open_file_shared1(conn, fname, &sbuf, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action);
+
+ if (!fsp) {
+ NTSTATUS ret = NT_STATUS_ACCESS_DENIED;
+ if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ ret = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ return ret;
+ }
+ close_file(fsp,False);
+ return NT_STATUS_OK;
}
/****************************************************************************
- reply to a unlink
+ The guts of the unlink command, split out so it may be called by the NT SMB
+ code.
****************************************************************************/
-int reply_unlink(char *inbuf,char *outbuf)
+
+NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name)
{
- int outsize = 0;
- pstring name;
- int cnum;
- int dirtype;
- pstring directory;
- pstring mask;
- char *p;
- int count=0;
- int error = ERRnoaccess;
- BOOL has_wild;
- BOOL exists=False;
+ pstring directory;
+ pstring mask;
+ char *p;
+ int count=0;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL has_wild;
+ BOOL bad_path = False;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf;
+
+ *directory = *mask = 0;
+
+ rc = unix_convert(name,conn,0,&bad_path,&sbuf);
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,".");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ has_wild = ms_has_wild(mask);
+
+ if (!has_wild) {
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+ error = can_delete(directory,conn,dirtype);
+ if (!NT_STATUS_IS_OK(error)) return error;
+
+ if (vfs_unlink(conn,directory) == 0) {
+ count++;
+ }
+ } else {
+ void *dirptr = NULL;
+ char *dname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then
+ the pattern matches against the long name, otherwise the short name
+ We don't implement this yet XXXX
+ */
+
+ if (dirptr) {
+ error = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ pstrcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive)) continue;
+
+ slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
+ error = can_delete(fname,conn,dirtype);
+ if (!NT_STATUS_IS_OK(error)) continue;
+ if (vfs_unlink(conn,fname) == 0) count++;
+ DEBUG(3,("unlink_internals: succesful unlink [%s]\n",fname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0 && NT_STATUS_IS_OK(error)) {
+ error = map_nt_error_from_unix(errno);
+ }
- *directory = *mask = 0;
+ return error;
+}
- cnum = SVAL(inbuf,smb_tid);
- dirtype = SVAL(inbuf,smb_vwv0);
+/****************************************************************************
+ Reply to a unlink
+****************************************************************************/
+
+int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ int dirtype;
+ NTSTATUS status;
+ START_PROFILE(SMBunlink);
+
+ dirtype = SVAL(inbuf,smb_vwv0);
+
+ srvstr_pull(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), -1, STR_TERMINATE);
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_unlink : %s\n",name));
+
+ status = unlink_internals(conn, dirtype, name);
+ if (!NT_STATUS_IS_OK(status)) return ERROR_NT(status);
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+
+ outsize = set_message(outbuf,0,0,True);
- strcpy(name,smb_buf(inbuf) + 1);
-
- DEBUG(3,("reply_unlink : %s\n",name));
-
- unix_convert(name,cnum);
+ END_PROFILE(SMBunlink);
+ return outsize;
+}
- p = strrchr(name,'/');
- if (!p) {
- strcpy(directory,"./");
- strcpy(mask,name);
- } else {
- *p = 0;
- strcpy(directory,name);
- strcpy(mask,p+1);
- }
+/****************************************************************************
+ Fail for readbraw.
+****************************************************************************/
- if (is_mangled(mask))
- check_mangled_stack(mask);
+void fail_readraw(void)
+{
+ pstring errstr;
+ slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)",
+ strerror(errno) );
+ exit_server(errstr);
+}
- has_wild = strchr(mask,'*') || strchr(mask,'?');
+/****************************************************************************
+ Reply to a readbraw (core+ protocol).
+****************************************************************************/
- if (!has_wild) {
- strcat(directory,"/");
- strcat(directory,mask);
- if (can_delete(directory,cnum,dirtype) && !sys_unlink(directory)) count++;
- if (!count) exists = file_exist(directory,NULL);
- } else {
- void *dirptr = NULL;
- char *dname;
+int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize)
+{
+ ssize_t maxcount,mincount;
+ size_t nread = 0;
+ SMB_OFF_T startpos;
+ char *header = outbuf;
+ ssize_t ret=0;
+ files_struct *fsp;
+ START_PROFILE(SMBreadbraw);
+
+ /*
+ * Special check if an oplock break has been issued
+ * and the readraw request croses on the wire, we must
+ * return a zero length response here.
+ */
+
+ if(global_oplock_break) {
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ DEBUG(5,("readbraw - oplock break finished\n"));
+ END_PROFILE(SMBreadbraw);
+ return -1;
+ }
- if (check_name(directory,cnum))
- dirptr = OpenDir(directory);
+ fsp = file_fsp(inbuf,smb_vwv0);
+
+ if (!FNUM_OK(fsp,conn) || !fsp->can_read) {
+ /*
+ * fsp could be NULL here so use the value from the packet. JRA.
+ */
+ DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0)));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
- if (dirptr)
- {
- error = ERRbadfile;
+ CHECK_FSP(fsp,conn);
- if (strequal(mask,"????????.???"))
- strcpy(mask,"*");
+ flush_write_cache(fsp, READRAW_FLUSH);
- while ((dname = ReadDirName(dirptr)))
- {
- pstring fname;
- strcpy(fname,dname);
-
- if(!mask_match(fname, mask, case_sensitive, False)) continue;
+ startpos = IVAL(inbuf,smb_vwv1);
+ if(CVAL(inbuf,smb_wct) == 10) {
+ /*
+ * This is a large offset (64 bit) read.
+ */
+#ifdef LARGE_SMB_OFF_T
- error = ERRnoaccess;
- sprintf(fname,"%s/%s",directory,dname);
- if (!can_delete(fname,cnum,dirtype)) continue;
- if (!sys_unlink(fname)) count++;
- DEBUG(3,("reply_unlink : doing unlink on %s\n",fname));
- }
- CloseDir(dirptr);
- }
- }
-
- if (count == 0) {
- if (exists)
- return(ERROR(ERRDOS,error));
- else
- return(UNIXERROR(ERRDOS,error));
- }
-
- outsize = set_message(outbuf,0,0,True);
-
- return(outsize);
-}
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32);
+#else /* !LARGE_SMB_OFF_T */
-/****************************************************************************
- reply to a readbraw (core+ protocol)
-****************************************************************************/
-int reply_readbraw(char *inbuf, char *outbuf)
-{
- int cnum,maxcount,mincount,fnum;
- int nread = 0;
- int startpos;
- char *header = outbuf;
- int ret=0;
- int fd;
- char *fname;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
-
- startpos = IVAL(inbuf,smb_vwv1);
- maxcount = SVAL(inbuf,smb_vwv3);
- mincount = SVAL(inbuf,smb_vwv4);
-
- /* ensure we don't overrun the packet size */
- maxcount = MIN(65535,maxcount);
- maxcount = MAX(mincount,maxcount);
-
- if (!FNUM_OK(fnum,cnum) || !Files[fnum].can_read)
- {
- DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fnum));
- _smb_setlen(header,0);
- transfer_file(0,Client,0,header,4,0);
- return(-1);
- }
- else
- {
- fd = Files[fnum].fd;
- fname = Files[fnum].name;
- }
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+ if(IVAL(inbuf,smb_vwv8) != 0) {
+ DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) ));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
- if (!is_locked(fnum,cnum,maxcount,startpos))
- {
- int size = Files[fnum].size;
- int sizeneeded = startpos + maxcount;
-
- if (size < sizeneeded) {
- struct stat st;
- if (fstat(Files[fnum].fd,&st) == 0)
- size = st.st_size;
- if (!Files[fnum].can_write)
- Files[fnum].size = size;
- }
+#endif /* LARGE_SMB_OFF_T */
- nread = MIN(maxcount,size - startpos);
- }
+ if(startpos < 0) {
+ DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos ));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+ }
+ maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF);
+ mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF);
+
+ /* ensure we don't overrun the packet size */
+ maxcount = MIN(65535,maxcount);
+ maxcount = MAX(mincount,maxcount);
+
+ if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ SMB_OFF_T size = fsp->size;
+ SMB_OFF_T sizeneeded = startpos + maxcount;
+
+ if (size < sizeneeded) {
+ SMB_STRUCT_STAT st;
+ if (vfs_fstat(fsp,fsp->fd,&st) == 0)
+ size = st.st_size;
+ if (!fsp->can_write)
+ fsp->size = size;
+ }
- if (nread < mincount)
- nread = 0;
+ if (startpos >= size)
+ nread = 0;
+ else
+ nread = MIN(maxcount,(size - startpos));
+ }
+
+ if (nread < mincount)
+ nread = 0;
- DEBUG(3,("%s readbraw fnum=%d cnum=%d start=%d max=%d min=%d nread=%d\n",
- timestring(),
- fnum,cnum,startpos,
- maxcount,mincount,nread));
+ DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos,
+ (int)maxcount, (int)mincount, (int)nread ) );
-#if UNSAFE_READRAW
- {
- int predict=0;
- _smb_setlen(header,nread);
-
- if (!Files[fnum].can_write)
- predict = read_predict(fd,startpos,header+4,NULL,nread);
-
- if ((nread-predict) > 0)
- seek_file(fnum,startpos + predict);
-
- ret = transfer_file(fd,Client,nread-predict,header,4+predict,
- startpos+predict);
- }
-
- if (ret != nread+4)
- DEBUG(0,("ERROR: file read failure on %s at %d for %d bytes (%d)\n",
- fname,startpos,nread,ret));
+ if (nread > 0) {
+ ret = read_file(fsp,header+4,startpos,nread);
+ if (ret < mincount)
+ ret = 0;
+ }
-#else
- ret = read_file(fnum,header+4,startpos,nread,nread,-1,False);
- if (ret < mincount) ret = 0;
+ _smb_setlen(header,ret);
+ if (write_data(smbd_server_fd(),header,4+ret) != 4+ret)
+ fail_readraw();
- _smb_setlen(header,ret);
- transfer_file(0,Client,0,header,4+ret,0);
-#endif
-
- DEBUG(5,("readbraw finished\n"));
- return -1;
+ DEBUG(5,("readbraw finished\n"));
+ END_PROFILE(SMBreadbraw);
+ return -1;
}
-
/****************************************************************************
reply to a lockread (core+ protocol)
****************************************************************************/
-int reply_lockread(char *inbuf,char *outbuf)
+int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length, int dum_buffsiz)
{
- int cnum,fnum;
- int nread = -1;
- char *data;
- int outsize = 0;
- uint32 startpos, numtoread;
- int eclass;
- uint32 ecode;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ ssize_t nread = -1;
+ char *data;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t numtoread;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBlockread);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ /*
+ * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+
+ * protocol request that predates the read/write lock concept.
+ * Thus instead of asking for a read lock here we need to ask
+ * for a write lock. JRA.
+ */
+
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid),
+ (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK);
+
+ if (NT_STATUS_V(status)) {
+ if (lp_blocking_locks(SNUM(conn))) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, -1, 0))
+ END_PROFILE(SMBlockread);
+ return -1;
+ }
+ END_PROFILE(SMBlockread);
+ return ERROR_NT(status);
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_READ(fnum);
- CHECK_ERROR(fnum);
+ nread = read_file(fsp,data,startpos,numtoread);
- numtoread = SVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv2);
-
- outsize = set_message(outbuf,5,3,True);
- numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
- data = smb_buf(outbuf) + 3;
-
- if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode))
- return (ERROR(eclass,ecode));
+ if (nread < 0) {
+ END_PROFILE(SMBlockread);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG(3,("lockread fnum=%d num=%d nread=%d\n",
+ fsp->fnum, (int)numtoread, (int)nread));
- nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
-
- if (nread < 0)
- return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- outsize += nread;
- SSVAL(outbuf,smb_vwv0,nread);
- SSVAL(outbuf,smb_vwv5,nread+3);
- SSVAL(smb_buf(outbuf),1,nread);
-
- DEBUG(3,("%s lockread fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
-
- return(outsize);
+ END_PROFILE(SMBlockread);
+ return(outsize);
}
/****************************************************************************
reply to a read
****************************************************************************/
-int reply_read(char *inbuf,char *outbuf)
+
+int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
{
- int cnum,numtoread,fnum;
- int nread = 0;
+ size_t numtoread;
+ ssize_t nread = 0;
char *data;
- int startpos;
+ SMB_OFF_T startpos;
int outsize = 0;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBread);
- CHECK_FNUM(fnum,cnum);
- CHECK_READ(fnum);
- CHECK_ERROR(fnum);
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
numtoread = SVAL(inbuf,smb_vwv1);
startpos = IVAL(inbuf,smb_vwv2);
-
+
+
outsize = set_message(outbuf,5,3,True);
numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
data = smb_buf(outbuf) + 3;
- if (is_locked(fnum,cnum,numtoread,startpos))
- return(ERROR(ERRDOS,ERRlock));
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBread);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
if (numtoread > 0)
- nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
-
- if (nread < 0)
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ END_PROFILE(SMBread);
return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
outsize += nread;
SSVAL(outbuf,smb_vwv0,nread);
SSVAL(outbuf,smb_vwv5,nread+3);
- CVAL(smb_buf(outbuf),0) = 1;
+ SCVAL(smb_buf(outbuf),0,1);
SSVAL(smb_buf(outbuf),1,nread);
- DEBUG(3,("%s read fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
-
+ DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n",
+ fsp->fnum, (int)numtoread, (int)nread ) );
+
+ END_PROFILE(SMBread);
return(outsize);
}
@@ -1467,605 +1658,813 @@ int reply_read(char *inbuf,char *outbuf)
/****************************************************************************
reply to a read and X
****************************************************************************/
-int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
{
- int smb_com2 = CVAL(inbuf,smb_vwv0);
- int smb_off2 = SVAL(inbuf,smb_vwv1);
- int fnum = GETFNUM(inbuf,smb_vwv2);
- uint32 smb_offs = IVAL(inbuf,smb_vwv3);
- int smb_maxcnt = SVAL(inbuf,smb_vwv5);
- int smb_mincnt = SVAL(inbuf,smb_vwv6);
- int cnum;
- int nread = -1;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3);
+ size_t smb_maxcnt = SVAL(inbuf,smb_vwv5);
+ size_t smb_mincnt = SVAL(inbuf,smb_vwv6);
+ ssize_t nread = -1;
char *data;
- int outsize = 0;
- BOOL ok = False;
+ START_PROFILE(SMBreadX);
- cnum = SVAL(inbuf,smb_tid);
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBreadX);
+ return reply_pipe_read_and_X(inbuf,outbuf,length,bufsize);
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_READ(fnum);
- CHECK_ERROR(fnum);
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
- outsize = set_message(outbuf,12,0,True);
+ set_message(outbuf,12,0,True);
data = smb_buf(outbuf);
- if (is_locked(fnum,cnum,smb_maxcnt,smb_offs))
- return(ERROR(ERRDOS,ERRlock));
- nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False);
- ok = True;
-
- if (nread < 0)
+ if(CVAL(inbuf,smb_wct) == 12) {
+#ifdef LARGE_SMB_OFF_T
+ /*
+ * This is a large offset (64 bit) read.
+ */
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv10) != 0) {
+ DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) ));
+ END_PROFILE(SMBreadX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+
+ }
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBreadX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+ nread = read_file(fsp,data,startpos,smb_maxcnt);
+
+ if (nread < 0) {
+ END_PROFILE(SMBreadX);
return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- outsize += nread;
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
SSVAL(outbuf,smb_vwv5,nread);
- SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
SSVAL(smb_buf(outbuf),-2,nread);
- DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n",
- timestring(),fnum,cnum,
- smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2));
+ DEBUG( 3, ( "readX fnum=%d min=%d max=%d nread=%d\n",
+ fsp->fnum, (int)smb_mincnt, (int)smb_maxcnt, (int)nread ) );
- chain_fnum = fnum;
-
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
-
- chain_fnum = -1;
-
- return(outsize);
+ END_PROFILE(SMBreadX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
}
-
/****************************************************************************
reply to a writebraw (core+ or LANMAN1.0 protocol)
****************************************************************************/
-int reply_writebraw(char *inbuf,char *outbuf)
-{
- int nwritten=0;
- int total_written=0;
- int numtowrite=0;
- int cnum,fnum;
- int outsize = 0;
- long startpos;
- char *data=NULL;
- BOOL write_through;
- int tcount;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
-
- tcount = IVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv3);
- write_through = BITSETW(inbuf+smb_vwv7,0);
-
- /* We have to deal with slightly different formats depending
- on whether we are using the core+ or lanman1.0 protocol */
- if(Protocol <= PROTOCOL_COREPLUS) {
- numtowrite = SVAL(smb_buf(inbuf),-2);
- data = smb_buf(inbuf);
- } else {
- numtowrite = SVAL(inbuf,smb_vwv10);
- data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
- }
- /* force the error type */
- CVAL(inbuf,smb_com) = SMBwritec;
- CVAL(outbuf,smb_com) = SMBwritec;
+int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ ssize_t nwritten=0;
+ ssize_t total_written=0;
+ size_t numtowrite=0;
+ size_t tcount;
+ SMB_OFF_T startpos;
+ char *data=NULL;
+ BOOL write_through;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwritebraw);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ tcount = IVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+
+ /* We have to deal with slightly different formats depending
+ on whether we are using the core+ or lanman1.0 protocol */
+
+ if(Protocol <= PROTOCOL_COREPLUS) {
+ numtowrite = SVAL(smb_buf(inbuf),-2);
+ data = smb_buf(inbuf);
+ } else {
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
+ }
- if (is_locked(fnum,cnum,tcount,startpos))
- return(ERROR(ERRDOS,ERRlock));
+ /* force the error type */
+ SCVAL(inbuf,smb_com,SMBwritec);
+ SCVAL(outbuf,smb_com,SMBwritec);
- if (seek_file(fnum,startpos) != startpos)
- DEBUG(0,("couldn't seek to %d in writebraw\n",startpos));
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwritebraw);
+ return(ERROR_DOS(ERRDOS,ERRlock));
+ }
- if (numtowrite>0)
- nwritten = write_file(fnum,data,numtowrite);
+ if (numtowrite>0)
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- DEBUG(3,("%s writebraw1 fnum=%d cnum=%d start=%d num=%d wrote=%d sync=%d\n",
- timestring(),fnum,cnum,startpos,numtowrite,nwritten,write_through));
+ DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n",
+ fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through));
- if (nwritten < numtowrite)
- return(UNIXERROR(ERRHRD,ERRdiskfull));
+ if (nwritten < numtowrite) {
+ END_PROFILE(SMBwritebraw);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
- total_written = nwritten;
+ total_written = nwritten;
- /* Return a message to the redirector to tell it
- to send more bytes */
- CVAL(outbuf,smb_com) = SMBwritebraw;
- SSVALS(outbuf,smb_vwv0,-1);
- outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
- send_smb(Client,outbuf);
+ /* Return a message to the redirector to tell it to send more bytes */
+ SCVAL(outbuf,smb_com,SMBwritebraw);
+ SSVALS(outbuf,smb_vwv0,-1);
+ outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_writebraw: send_smb failed.");
- /* Now read the raw data into the buffer and write it */
- if(read_smb_length(Client,inbuf,0) == -1) {
- exit_server("secondary writebraw failed");
- }
+ /* Now read the raw data into the buffer and write it */
+ if (read_smb_length(smbd_server_fd(),inbuf,SMB_SECONDARY_WAIT) == -1) {
+ exit_server("secondary writebraw failed");
+ }
- /* Even though this is not an smb message, smb_len
- returns the generic length of an smb message */
- numtowrite = smb_len(inbuf);
+ /* Even though this is not an smb message, smb_len returns the generic length of an smb message */
+ numtowrite = smb_len(inbuf);
- if (tcount > nwritten+numtowrite) {
- DEBUG(3,("Client overestimated the write %d %d %d\n",
- tcount,nwritten,numtowrite));
- }
+ /* Set up outbuf to return the correct return */
+ outsize = set_message(outbuf,1,0,True);
+ SCVAL(outbuf,smb_com,SMBwritec);
+ SSVAL(outbuf,smb_vwv0,total_written);
- nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0,
- startpos+nwritten);
- total_written += nwritten;
-
- /* Set up outbuf to return the correct return */
- outsize = set_message(outbuf,1,0,True);
- CVAL(outbuf,smb_com) = SMBwritec;
- SSVAL(outbuf,smb_vwv0,total_written);
+ if (numtowrite != 0) {
- if (nwritten < numtowrite) {
- CVAL(outbuf,smb_rcls) = ERRHRD;
- SSVAL(outbuf,smb_err,ERRdiskfull);
- }
+ if (numtowrite > BUFFER_SIZE) {
+ DEBUG(0,("reply_writebraw: Oversize secondary write raw requested (%u). Terminating\n",
+ (unsigned int)numtowrite ));
+ exit_server("secondary writebraw failed");
+ }
- if (lp_syncalways(SNUM(cnum)) || write_through)
- sync_file(fnum);
+ if (tcount > nwritten+numtowrite) {
+ DEBUG(3,("Client overestimated the write %d %d %d\n",
+ (int)tcount,(int)nwritten,(int)numtowrite));
+ }
- DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n",
- timestring(),fnum,cnum,startpos,numtowrite,total_written));
+ if (read_data( smbd_server_fd(), inbuf+4, numtowrite) != numtowrite ) {
+ DEBUG(0,("reply_writebraw: Oversize secondary write raw read failed (%s). Terminating\n",
+ strerror(errno) ));
+ exit_server("secondary writebraw failed");
+ }
- /* we won't return a status if write through is not selected - this
- follows what WfWg does */
- if (!write_through && total_written==tcount)
- return(-1);
+ nwritten = write_file(fsp,inbuf+4,startpos+nwritten,numtowrite);
- return(outsize);
-}
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ if (nwritten > 0)
+ total_written += nwritten;
+ }
+
+ if ((lp_syncalways(SNUM(conn)) || write_through) && lp_strict_sync(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n",
+ fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written));
+
+ /* we won't return a status if write through is not selected - this follows what WfWg does */
+ END_PROFILE(SMBwritebraw);
+ if (!write_through && total_written==tcount) {
+
+#if RABBIT_PELLET_FIX
+ /*
+ * Fix for "rabbit pellet" mode, trigger an early TCP ack by
+ * sending a SMBkeepalive. Thanks to DaveCB at Sun for this. JRA.
+ */
+ if (!send_keepalive(smbd_server_fd()))
+ exit_server("reply_writebraw: send of keepalive failed");
+#endif
+ return(-1);
+ }
+ return(outsize);
+}
/****************************************************************************
reply to a writeunlock (core+)
****************************************************************************/
-int reply_writeunlock(char *inbuf,char *outbuf)
-{
- int cnum,fnum;
- int nwritten = -1;
- int outsize = 0;
- char *data;
- uint32 numtowrite,startpos;
- int eclass;
- uint32 ecode;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
+int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf,
+ int size, int dum_buffsize)
+{
+ ssize_t nwritten = -1;
+ size_t numtowrite;
+ SMB_OFF_T startpos;
+ char *data;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwriteunlock);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
- numtowrite = SVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv2);
- data = smb_buf(inbuf) + 3;
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
- if (is_locked(fnum,cnum,numtowrite,startpos))
- return(ERROR(ERRDOS,ERRlock));
-
- seek_file(fnum,startpos);
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos,
+ WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteunlock);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
- /* The special X/Open SMB protocol handling of
- zero length writes is *NOT* done for
- this call */
- if(numtowrite == 0)
- nwritten = 0;
- else
- nwritten = write_file(fnum,data,numtowrite);
+ /* The special X/Open SMB protocol handling of
+ zero length writes is *NOT* done for
+ this call */
+ if(numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- if (lp_syncalways(SNUM(cnum)))
- sync_file(fnum);
-
- if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ if (lp_syncalways(SNUM(conn)))
+ sync_file(conn,fsp);
- if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode))
- return(ERROR(eclass,ecode));
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteunlock);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- outsize = set_message(outbuf,1,0,True);
-
- SSVAL(outbuf,smb_vwv0,nwritten);
-
- DEBUG(3,("%s writeunlock fnum=%d cnum=%d num=%d wrote=%d\n",
- timestring(),fnum,cnum,numtowrite,nwritten));
-
- return(outsize);
+ status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite,
+ (SMB_BIG_UINT)startpos);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBwriteunlock);
+ return ERROR_NT(status);
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ END_PROFILE(SMBwriteunlock);
+ return outsize;
}
/****************************************************************************
- reply to a write
+ Reply to a write.
****************************************************************************/
-int reply_write(char *inbuf,char *outbuf,int dum1,int dum2)
+
+int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize)
{
- int cnum,numtowrite,fnum;
- int nwritten = -1;
- int outsize = 0;
- int startpos;
- char *data;
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ SMB_OFF_T startpos;
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwrite);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBwrite);
+ return reply_pipe_write(inbuf,outbuf,size,dum_buffsize);
+ }
- dum1 = dum2 = 0;
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwrite);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
- numtowrite = SVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv2);
- data = smb_buf(inbuf) + 3;
+ /*
+ * X/Open SMB protocol says that if smb_vwv1 is
+ * zero then the file size should be extended or
+ * truncated to the size given in smb_vwv[2-3].
+ */
+
+ if(numtowrite == 0) {
+ /*
+ * This is actually an allocate call, and set EOF. JRA.
+ */
+ nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos);
+ if (nwritten < 0) {
+ END_PROFILE(SMBwrite);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos);
+ if (nwritten < 0) {
+ END_PROFILE(SMBwrite);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- if (is_locked(fnum,cnum,numtowrite,startpos))
- return(ERROR(ERRDOS,ERRlock));
+ if (lp_syncalways(SNUM(conn)))
+ sync_file(conn,fsp);
- seek_file(fnum,startpos);
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwrite);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- /* X/Open SMB protocol says that if smb_vwv1 is
- zero then the file size should be extended or
- truncated to the size given in smb_vwv[2-3] */
- if(numtowrite == 0)
- nwritten = set_filelen(Files[fnum].fd, startpos);
- else
- nwritten = write_file(fnum,data,numtowrite);
+ outsize = set_message(outbuf,1,0,True);
- if (lp_syncalways(SNUM(cnum)))
- sync_file(fnum);
-
- if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ SSVAL(outbuf,smb_vwv0,nwritten);
- outsize = set_message(outbuf,1,0,True);
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
- SSVAL(outbuf,smb_vwv0,nwritten);
+ DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten));
- if (nwritten < numtowrite) {
- CVAL(outbuf,smb_rcls) = ERRHRD;
- SSVAL(outbuf,smb_err,ERRdiskfull);
- }
-
- DEBUG(3,("%s write fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,numtowrite,nwritten));
-
- return(outsize);
+ END_PROFILE(SMBwrite);
+ return(outsize);
}
/****************************************************************************
reply to a write and X
****************************************************************************/
-int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
{
- int smb_com2 = CVAL(inbuf,smb_vwv0);
- int smb_off2 = SVAL(inbuf,smb_vwv1);
- int fnum = GETFNUM(inbuf,smb_vwv2);
- uint32 smb_offs = IVAL(inbuf,smb_vwv3);
- int smb_dsize = SVAL(inbuf,smb_vwv10);
- int smb_doff = SVAL(inbuf,smb_vwv11);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3);
+ size_t numtowrite = SVAL(inbuf,smb_vwv10);
BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
- int cnum;
- int nwritten = -1;
- int outsize = 0;
+ ssize_t nwritten = -1;
+ unsigned int smb_doff = SVAL(inbuf,smb_vwv11);
+ unsigned int smblen = smb_len(inbuf);
char *data;
+ BOOL large_writeX = ((CVAL(inbuf,smb_wct) == 14) && (smblen > 0xFFFF));
+ START_PROFILE(SMBwriteX);
- cnum = SVAL(inbuf,smb_tid);
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBwriteX);
+ return reply_pipe_write_and_X(inbuf,outbuf,length,bufsize);
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ /* Deal with possible LARGE_WRITEX */
+ if (large_writeX)
+ numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16);
+
+ if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) {
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRbadmem);
+ }
data = smb_base(inbuf) + smb_doff;
- if (is_locked(fnum,cnum,smb_dsize,smb_offs))
- return(ERROR(ERRDOS,ERRlock));
+ if(CVAL(inbuf,smb_wct) == 14) {
+#ifdef LARGE_SMB_OFF_T
+ /*
+ * This is a large offset (64 bit) write.
+ */
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv12) != 0) {
+ DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) ));
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+ }
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
- seek_file(fnum,smb_offs);
-
/* X/Open SMB protocol says that, unlike SMBwrite
if the length is zero then NO truncation is
done, just a write of zero. To truncate a file,
use SMBwrite. */
- if(smb_dsize == 0)
+ if(numtowrite == 0)
nwritten = 0;
else
- nwritten = write_file(fnum,data,smb_dsize);
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0))
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteX);
return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- outsize = set_message(outbuf,6,0,True);
+ set_message(outbuf,6,0,True);
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
SSVAL(outbuf,smb_vwv2,nwritten);
-
- if (nwritten < smb_dsize) {
- CVAL(outbuf,smb_rcls) = ERRHRD;
+ if (large_writeX)
+ SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
SSVAL(outbuf,smb_err,ERRdiskfull);
}
- DEBUG(3,("%s writeX fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,smb_dsize,nwritten));
+ DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten));
- chain_fnum = fnum;
+ if (lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
- if (lp_syncalways(SNUM(cnum)) || write_through)
- sync_file(fnum);
-
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
-
- chain_fnum = -1;
-
- return(outsize);
+ END_PROFILE(SMBwriteX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
}
/****************************************************************************
reply to a lseek
****************************************************************************/
-int reply_lseek(char *inbuf,char *outbuf)
+
+int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
{
- int cnum,fnum;
- uint32 startpos;
- int32 res= -1;
+ SMB_OFF_T startpos;
+ SMB_OFF_T res= -1;
int mode,umode;
int outsize = 0;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBlseek);
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
+ CHECK_FSP(fsp,conn);
+
+ flush_write_cache(fsp, SEEK_FLUSH);
mode = SVAL(inbuf,smb_vwv1) & 3;
- startpos = IVAL(inbuf,smb_vwv2);
+ startpos = IVALS(inbuf,smb_vwv2);
- switch (mode & 3)
- {
+ switch (mode) {
case 0: umode = SEEK_SET; break;
case 1: umode = SEEK_CUR; break;
case 2: umode = SEEK_END; break;
default:
umode = SEEK_SET; break;
+ }
+
+ if((res = conn->vfs_ops.lseek(fsp,fsp->fd,startpos,umode)) == -1) {
+ /*
+ * Check for the special case where a seek before the start
+ * of the file sets the offset to zero. Added in the CIFS spec,
+ * section 4.2.7.
+ */
+
+ if(errno == EINVAL) {
+ SMB_OFF_T current_pos = startpos;
+
+ if(umode == SEEK_CUR) {
+
+ if((current_pos = conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_CUR)) == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ current_pos += startpos;
+
+ } else if (umode == SEEK_END) {
+
+ SMB_STRUCT_STAT sbuf;
+
+ if(vfs_fstat(fsp,fsp->fd, &sbuf) == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ current_pos += sbuf.st_size;
+ }
+
+ if(current_pos < 0)
+ res = conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_SET);
}
-
- res = lseek(Files[fnum].fd,startpos,umode);
- Files[fnum].pos = res;
+
+ if(res == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ fsp->pos = res;
outsize = set_message(outbuf,2,0,True);
- SIVALS(outbuf,smb_vwv0,res);
-
- DEBUG(3,("%s lseek fnum=%d cnum=%d ofs=%d mode=%d\n",timestring(),fnum,cnum,startpos,mode));
+ SIVAL(outbuf,smb_vwv0,res);
+ DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n",
+ fsp->fnum, (double)startpos, (double)res, mode));
+
+ END_PROFILE(SMBlseek);
return(outsize);
}
-
/****************************************************************************
reply to a flush
****************************************************************************/
-int reply_flush(char *inbuf,char *outbuf)
-{
- int cnum, fnum;
- int outsize = set_message(outbuf,0,0,True);
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
- if (fnum != 0xFFFF) {
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
- }
-
- if (fnum == 0xFFFF)
- {
- int i;
- for (i=0;i<MAX_OPEN_FILES;i++)
- if (OPEN_FNUM(i))
- sync_file(i);
- }
- else
- sync_file(fnum);
+int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBflush);
- DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum));
- return(outsize);
+ CHECK_FSP(fsp,conn);
+
+ if (!fsp) {
+ file_sync_all(conn);
+ } else {
+ sync_file(conn,fsp);
+ }
+
+ DEBUG(3,("flush\n"));
+ END_PROFILE(SMBflush);
+ return(outsize);
}
/****************************************************************************
reply to a exit
****************************************************************************/
-int reply_exit(char *inbuf,char *outbuf)
+int reply_exit(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int outsize = set_message(outbuf,0,0,True);
- DEBUG(3,("%s exit\n",timestring()));
-
- return(outsize);
+ int outsize;
+ START_PROFILE(SMBexit);
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("exit\n"));
+
+ END_PROFILE(SMBexit);
+ return(outsize);
}
/****************************************************************************
- reply to a close
+ Reply to a close - has to deal with closing a directory opened by NT SMB's.
****************************************************************************/
-int reply_close(char *inbuf,char *outbuf)
+int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size,
+ int dum_buffsize)
{
- int fnum,cnum;
- int outsize = 0;
- time_t mtime;
- int32 eclass = 0, err = 0;
+ int outsize = 0;
+ time_t mtime;
+ int32 eclass = 0, err = 0;
+ files_struct *fsp = NULL;
+ START_PROFILE(SMBclose);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /* If it's an IPC, pass off to the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBclose);
+ return reply_pipe_close(conn, inbuf,outbuf);
+ }
- outsize = set_message(outbuf,0,0,True);
+ fsp = file_fsp(inbuf,smb_vwv0);
- cnum = SVAL(inbuf,smb_tid);
+ /*
+ * We can only use CHECK_FSP if we know it's not a directory.
+ */
- fnum = GETFNUM(inbuf,smb_vwv0);
- CHECK_FNUM(fnum,cnum);
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBclose);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
- if(HAS_CACHED_ERROR(fnum)) {
- eclass = Files[fnum].wbmpx_ptr->wr_errclass;
- err = Files[fnum].wbmpx_ptr->wr_error;
- }
+ if(fsp->is_directory) {
+ /*
+ * Special case - close NT SMB directory handle.
+ */
+ DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum));
+ close_file(fsp,True);
+ } else {
+ /*
+ * Close ordinary file.
+ */
+ int close_err;
+ pstring file_name;
+
+ /* Save the name for time set in close. */
+ pstrcpy( file_name, fsp->fsp_name);
+
+ DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n",
+ fsp->fd, fsp->fnum,
+ conn->num_files_open));
+
+ /*
+ * close_file() returns the unix errno if an error
+ * was detected on close - normally this is due to
+ * a disk full error. If not then it was probably an I/O error.
+ */
+
+ if((close_err = close_file(fsp,True)) != 0) {
+ errno = close_err;
+ END_PROFILE(SMBclose);
+ return (UNIXERROR(ERRHRD,ERRgeneral));
+ }
- mtime = make_unix_date3(inbuf+smb_vwv1);
+ /*
+ * Now take care of any time sent in the close.
+ */
- close_file(fnum);
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ /* try and set the date */
+ set_filetime(conn, file_name, mtime);
- /* try and set the date */
- set_filetime(Files[fnum].name,mtime);
+ }
- /* We have a cached error */
- if(eclass || err)
- return(ERROR(eclass,err));
+ /* We have a cached error */
+ if(eclass || err) {
+ END_PROFILE(SMBclose);
+ return ERROR_DOS(eclass,err);
+ }
- DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n",
- timestring(),Files[fnum].fd,fnum,cnum,
- Connections[cnum].num_files_open));
-
- return(outsize);
+ END_PROFILE(SMBclose);
+ return(outsize);
}
/****************************************************************************
reply to a writeclose (Core+ protocol)
****************************************************************************/
-int reply_writeclose(char *inbuf,char *outbuf)
+
+int reply_writeclose(connection_struct *conn,
+ char *inbuf,char *outbuf, int size, int dum_buffsize)
{
- int cnum,numtowrite,fnum;
- int nwritten = -1;
- int outsize = 0;
- int startpos;
- char *data;
- time_t mtime;
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ int close_err = 0;
+ SMB_OFF_T startpos;
+ char *data;
+ time_t mtime;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteclose);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ mtime = make_unix_date3(inbuf+smb_vwv4);
+ data = smb_buf(inbuf) + 1;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteclose);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- numtowrite = SVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv2);
- mtime = make_unix_date3(inbuf+smb_vwv4);
- data = smb_buf(inbuf) + 1;
+ set_filetime(conn, fsp->fsp_name,mtime);
- if (is_locked(fnum,cnum,numtowrite,startpos))
- return(ERROR(ERRDOS,ERRlock));
-
- seek_file(fnum,startpos);
-
- nwritten = write_file(fnum,data,numtowrite);
+ close_err = close_file(fsp,True);
- close_file(fnum);
-
- set_filetime(Files[fnum].name,mtime);
-
- DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n",
- timestring(),fnum,cnum,numtowrite,nwritten,
- Connections[cnum].num_files_open));
+ DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten,
+ conn->num_files_open));
- if (nwritten <= 0)
- return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- outsize = set_message(outbuf,1,0,True);
+ if (nwritten <= 0) {
+ END_PROFILE(SMBwriteclose);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ if(close_err != 0) {
+ errno = close_err;
+ END_PROFILE(SMBwriteclose);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
- SSVAL(outbuf,smb_vwv0,nwritten);
- return(outsize);
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ END_PROFILE(SMBwriteclose);
+ return(outsize);
}
/****************************************************************************
reply to a lock
****************************************************************************/
-int reply_lock(char *inbuf,char *outbuf)
+int reply_lock(connection_struct *conn,
+ char *inbuf,char *outbuf, int length, int dum_buffsize)
{
- int fnum,cnum;
- int outsize = set_message(outbuf,0,0,True);
- uint32 count,offset;
- int eclass;
- uint32 ecode;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
-
- count = IVAL(inbuf,smb_vwv1);
- offset = IVAL(inbuf,smb_vwv3);
-
- DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
+ int outsize = set_message(outbuf,0,0,True);
+ SMB_BIG_UINT count,offset;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBlock);
+
+ CHECK_FSP(fsp,conn);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
+ offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
+
+ DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fd, fsp->fnum, (double)offset, (double)count));
+
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK);
+ if (NT_STATUS_V(status)) {
+ if (lp_blocking_locks(SNUM(conn))) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, -1, 0)) {
+ END_PROFILE(SMBlock);
+ return -1;
+ }
+ }
+ END_PROFILE(SMBlock);
+ return ERROR_NT(status);
+ }
- if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode))
- return (ERROR(eclass,ecode));
-
- return(outsize);
+ END_PROFILE(SMBlock);
+ return(outsize);
}
/****************************************************************************
reply to a unlock
****************************************************************************/
-int reply_unlock(char *inbuf,char *outbuf)
+int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size,
+ int dum_buffsize)
{
- int fnum,cnum;
- int outsize = set_message(outbuf,0,0,True);
- uint32 count,offset;
- int eclass;
- uint32 ecode;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
-
- count = IVAL(inbuf,smb_vwv1);
- offset = IVAL(inbuf,smb_vwv3);
+ int outsize = set_message(outbuf,0,0,True);
+ SMB_BIG_UINT count,offset;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBunlock);
- if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode))
- return (ERROR(eclass,ecode));
+ CHECK_FSP(fsp,conn);
+
+ count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
+ offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
+
+ status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBunlock);
+ return ERROR_NT(status);
+ }
- DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
-
- return(outsize);
+ DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fd, fsp->fnum, (double)offset, (double)count ) );
+
+ END_PROFILE(SMBunlock);
+ return(outsize);
}
/****************************************************************************
reply to a tdis
****************************************************************************/
-int reply_tdis(char *inbuf,char *outbuf)
+int reply_tdis(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum, uid;
- int outsize = set_message(outbuf,0,0,True);
-
- cnum = SVAL(inbuf,smb_tid);
- uid = SVAL(inbuf,smb_uid);
+ int outsize = set_message(outbuf,0,0,True);
+ uint16 vuid;
+ START_PROFILE(SMBtdis);
- Connections[cnum].used = False;
+ vuid = SVAL(inbuf,smb_uid);
- close_cnum(cnum,uid);
-
- DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum));
+ if (!conn) {
+ DEBUG(4,("Invalid connection in tdis\n"));
+ END_PROFILE(SMBtdis);
+ return ERROR_DOS(ERRSRV,ERRinvnid);
+ }
+
+ conn->used = False;
- return outsize;
+ close_cnum(conn,vuid);
+
+ END_PROFILE(SMBtdis);
+ return outsize;
}
@@ -2073,316 +2472,438 @@ int reply_tdis(char *inbuf,char *outbuf)
/****************************************************************************
reply to a echo
****************************************************************************/
-int reply_echo(char *inbuf,char *outbuf)
+int reply_echo(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum;
- int smb_reverb = SVAL(inbuf,smb_vwv0);
- int seq_num;
- int data_len = smb_buflen(inbuf);
- int outsize = set_message(outbuf,1,data_len,True);
+ int smb_reverb = SVAL(inbuf,smb_vwv0);
+ int seq_num;
+ unsigned int data_len = smb_buflen(inbuf);
+ int outsize = set_message(outbuf,1,data_len,True);
+ START_PROFILE(SMBecho);
- cnum = SVAL(inbuf,smb_tid);
+ data_len = MIN(data_len, (sizeof(inbuf)-(smb_buf(inbuf)-inbuf)));
- if (cnum != 0xFFFF && !OPEN_CNUM(cnum))
- {
- DEBUG(4,("Invalid cnum in echo (%d)\n",cnum));
- return(ERROR(ERRSRV,ERRinvnid));
- }
+ /* copy any incoming data back out */
+ if (data_len > 0)
+ memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);
- /* copy any incoming data back out */
- if (data_len > 0)
- memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);
+ if (smb_reverb > 100) {
+ DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
+ smb_reverb = 100;
+ }
- if (smb_reverb > 100)
- {
- DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
- smb_reverb = 100;
- }
+ for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) {
+ SSVAL(outbuf,smb_vwv0,seq_num);
- for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++)
- {
- SSVAL(outbuf,smb_vwv0,seq_num);
+ smb_setlen(outbuf,outsize - 4);
- smb_setlen(outbuf,outsize - 4);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_echo: send_smb failed.");
+ }
- send_smb(Client,outbuf);
- }
+ DEBUG(3,("echo %d times\n", smb_reverb));
- DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum));
+ smb_echo_count++;
- return -1;
+ END_PROFILE(SMBecho);
+ return -1;
}
/****************************************************************************
reply to a printopen
****************************************************************************/
-int reply_printopen(char *inbuf,char *outbuf)
+int reply_printopen(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- pstring fname;
- pstring fname2;
- int cnum;
- int fnum = -1;
- int outsize = 0;
-
- *fname = *fname2 = 0;
-
- cnum = SVAL(inbuf,smb_tid);
-
- if (!CAN_PRINT(cnum))
- return(ERROR(ERRDOS,ERRnoaccess));
-
- {
- pstring s;
- char *p;
- StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1);
- p = s;
- while (*p)
- {
- if (!(isalnum(*p) || strchr("._-",*p)))
- *p = 'X';
- p++;
- }
-
- if (strlen(s) > 10) s[10] = 0;
-
- sprintf(fname,"%s.XXXXXX",s);
- }
-
- fnum = find_free_file();
- if (fnum < 0)
- return(ERROR(ERRSRV,ERRnofids));
-
- strcpy(fname2,(char *)mktemp(fname));
-
- if (!check_name(fname2,cnum))
- return(ERROR(ERRDOS,ERRnoaccess));
+ int outsize = 0;
+ files_struct *fsp;
+ START_PROFILE(SMBsplopen);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplopen);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
- open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC,
- unix_mode(cnum,0));
+ /* Open for exclusive use, write only. */
+ fsp = print_fsp_open(conn, NULL);
- if (!Files[fnum].open)
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ if (!fsp) {
+ END_PROFILE(SMBsplopen);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- /* force it to be a print file */
- Files[fnum].print_file = True;
-
- outsize = set_message(outbuf,1,0,True);
- SSVAL(outbuf,smb_vwv0,fnum);
-
- DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum));
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
- return(outsize);
+ DEBUG(3,("openprint fd=%d fnum=%d\n",
+ fsp->fd, fsp->fnum));
+
+ END_PROFILE(SMBsplopen);
+ return(outsize);
}
/****************************************************************************
reply to a printclose
****************************************************************************/
-int reply_printclose(char *inbuf,char *outbuf)
+int reply_printclose(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int fnum,cnum;
- int outsize = set_message(outbuf,0,0,True);
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ int outsize = set_message(outbuf,0,0,True);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int close_err = 0;
+ START_PROFILE(SMBsplclose);
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
+ CHECK_FSP(fsp,conn);
- if (!CAN_PRINT(cnum))
- return(ERROR(ERRDOS,ERRnoaccess));
-
- close_file(fnum);
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplclose);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
- DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum));
+ DEBUG(3,("printclose fd=%d fnum=%d\n",
+ fsp->fd,fsp->fnum));
- return(outsize);
+ close_err = close_file(fsp,True);
+
+ if(close_err != 0) {
+ errno = close_err;
+ END_PROFILE(SMBsplclose);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ END_PROFILE(SMBsplclose);
+ return(outsize);
}
/****************************************************************************
reply to a printqueue
****************************************************************************/
-int reply_printqueue(char *inbuf,char *outbuf)
+int reply_printqueue(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum, uid;
- int outsize = set_message(outbuf,2,3,True);
- int max_count = SVAL(inbuf,smb_vwv0);
- int start_index = SVAL(inbuf,smb_vwv1);
-
- cnum = SVAL(inbuf,smb_tid);
- uid = SVAL(inbuf,smb_uid);
-
-/* allow checking the queue for anyone */
-#if 0
- if (!CAN_PRINT(cnum))
- return(ERROR(ERRDOS,ERRnoaccess));
-#endif
+ int outsize = set_message(outbuf,2,3,True);
+ int max_count = SVAL(inbuf,smb_vwv0);
+ int start_index = SVAL(inbuf,smb_vwv1);
+ START_PROFILE(SMBsplretq);
+
+ /* we used to allow the client to get the cnum wrong, but that
+ is really quite gross and only worked when there was only
+ one printer - I think we should now only accept it if they
+ get it right (tridge) */
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplretq);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
- SSVAL(outbuf,smb_vwv0,0);
- SSVAL(outbuf,smb_vwv1,0);
- CVAL(smb_buf(outbuf),0) = 1;
- SSVAL(smb_buf(outbuf),1,0);
+ SSVAL(outbuf,smb_vwv0,0);
+ SSVAL(outbuf,smb_vwv1,0);
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,0);
- DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n",
- timestring(),cnum,start_index,max_count));
-
- if (!OPEN_CNUM(cnum) || !Connections[cnum].printer)
- {
- int i;
- cnum = -1;
-
- for (i=0;i<MAX_CONNECTIONS;i++)
- if (CAN_PRINT(i) && Connections[i].printer)
- cnum = i;
+ DEBUG(3,("printqueue start_index=%d max_count=%d\n",
+ start_index, max_count));
- if (cnum == -1)
- for (i=0;i<MAX_CONNECTIONS;i++)
- if (OPEN_CNUM(i))
- cnum = i;
-
- if (!OPEN_CNUM(cnum))
- return(ERROR(ERRSRV,ERRinvnid));
-
- DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum));
- }
-
- if (!become_user(cnum,uid))
- return(ERROR(ERRSRV,ERRinvnid));
-
- {
- print_queue_struct *queue = NULL;
- char *p = smb_buf(outbuf) + 3;
- int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL);
- int num_to_get = ABS(max_count);
- int first = (max_count>0?start_index:start_index+max_count+1);
- int i;
-
- if (first >= count)
- num_to_get = 0;
- else
- num_to_get = MIN(num_to_get,count-first);
+ {
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ char *p = smb_buf(outbuf) + 3;
+ int count = print_queue_status(SNUM(conn), &queue, &status);
+ int num_to_get = ABS(max_count);
+ int first = (max_count>0?start_index:start_index+max_count+1);
+ int i;
+
+ if (first >= count)
+ num_to_get = 0;
+ else
+ num_to_get = MIN(num_to_get,count-first);
- for (i=first;i<first+num_to_get;i++)
- {
- put_dos_date2(p,0,queue[i].time);
- CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3);
- SSVAL(p,5,queue[i].job);
- SIVAL(p,7,queue[i].size);
- CVAL(p,11) = 0;
- StrnCpy(p+12,queue[i].user,16);
- p += 28;
- }
+ for (i=first;i<first+num_to_get;i++) {
+ put_dos_date2(p,0,queue[i].time);
+ SCVAL(p,4,(queue[i].status==LPQ_PRINTING?2:3));
+ SSVAL(p,5, queue[i].job);
+ SIVAL(p,7,queue[i].size);
+ SCVAL(p,11,0);
+ srvstr_push(outbuf, p+12, queue[i].fs_user, 16, STR_ASCII);
+ p += 28;
+ }
- if (count > 0)
- {
- outsize = set_message(outbuf,2,28*count+3,False);
- SSVAL(outbuf,smb_vwv0,count);
- SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
- CVAL(smb_buf(outbuf),0) = 1;
- SSVAL(smb_buf(outbuf),1,28*count);
- }
+ if (count > 0) {
+ outsize = set_message(outbuf,2,28*count+3,False);
+ SSVAL(outbuf,smb_vwv0,count);
+ SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,28*count);
+ }
- if (queue) free(queue);
+ SAFE_FREE(queue);
- DEBUG(3,("%d entries returned in queue\n",count));
- }
+ DEBUG(3,("%d entries returned in queue\n",count));
+ }
- return(outsize);
+ END_PROFILE(SMBsplretq);
+ return(outsize);
}
/****************************************************************************
reply to a printwrite
****************************************************************************/
-int reply_printwrite(char *inbuf,char *outbuf)
+int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum,numtowrite,fnum;
+ int numtowrite;
int outsize = set_message(outbuf,0,0,True);
char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBsplwr);
- cnum = SVAL(inbuf,smb_tid);
-
- if (!CAN_PRINT(cnum))
- return(ERROR(ERRDOS,ERRnoaccess));
-
- fnum = GETFNUM(inbuf,smb_vwv0);
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplwr);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
numtowrite = SVAL(smb_buf(inbuf),1);
data = smb_buf(inbuf) + 3;
- if (write_file(fnum,data,numtowrite) != numtowrite)
+ if (write_file(fsp,data,-1,numtowrite) != numtowrite) {
+ END_PROFILE(SMBsplwr);
return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) );
- DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite));
-
+ END_PROFILE(SMBsplwr);
return(outsize);
}
/****************************************************************************
- reply to a mkdir
+ The guts of the mkdir command, split out so it may be called by the NT SMB
+ code.
****************************************************************************/
-int reply_mkdir(char *inbuf,char *outbuf)
+NTSTATUS mkdir_internal(connection_struct *conn, pstring directory)
{
- pstring directory;
- int cnum;
- int outsize,ret= -1;
-
- strcpy(directory,smb_buf(inbuf) + 1);
- cnum = SVAL(inbuf,smb_tid);
- unix_convert(directory,cnum);
-
- if (check_name(directory,cnum))
- ret = sys_mkdir(directory,unix_mode(cnum,aDIR));
-
- if (ret < 0)
- return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- outsize = set_message(outbuf,0,0,True);
-
- DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret));
-
- return(outsize);
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ int ret= -1;
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+
+ if (check_name(directory, conn))
+ ret = vfs_mkdir(conn,directory,unix_mode(conn,aDIR,directory));
+
+ if (ret == -1) {
+ NTSTATUS nterr = set_bad_path_error(errno, bad_path);
+ if (!NT_STATUS_IS_OK(nterr))
+ return nterr;
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
}
+/****************************************************************************
+ Reply to a mkdir.
+****************************************************************************/
+
+int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring directory;
+ int outsize;
+ NTSTATUS status;
+ START_PROFILE(SMBmkdir);
+
+ srvstr_pull(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), -1, STR_TERMINATE);
+
+ status = mkdir_internal(conn, directory);
+ if (!NT_STATUS_IS_OK(status))
+ return ERROR_NT(status);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) );
+
+ END_PROFILE(SMBmkdir);
+ return(outsize);
+}
/****************************************************************************
- reply to a rmdir
+ Static function used by reply_rmdir to delete an entire directory
+ tree recursively. Return False on ok, True on fail.
****************************************************************************/
-int reply_rmdir(char *inbuf,char *outbuf)
+
+static BOOL recursive_rmdir(connection_struct *conn, char *directory)
+{
+ char *dname = NULL;
+ BOOL ret = False;
+ void *dirptr = OpenDir(conn, directory, False);
+
+ if(dirptr == NULL)
+ return True;
+
+ while((dname = ReadDirName(dirptr))) {
+ pstring fullname;
+ SMB_STRUCT_STAT st;
+
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+
+ /* Construct the full name. */
+ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) {
+ errno = ENOMEM;
+ ret = True;
+ break;
+ }
+
+ pstrcpy(fullname, directory);
+ pstrcat(fullname, "/");
+ pstrcat(fullname, dname);
+
+ if(conn->vfs_ops.lstat(conn,fullname, &st) != 0) {
+ ret = True;
+ break;
+ }
+
+ if(st.st_mode & S_IFDIR) {
+ if(recursive_rmdir(conn, fullname)!=0) {
+ ret = True;
+ break;
+ }
+ if(vfs_rmdir(conn,fullname) != 0) {
+ ret = True;
+ break;
+ }
+ } else if(vfs_unlink(conn,fullname) != 0) {
+ ret = True;
+ break;
+ }
+ }
+ CloseDir(dirptr);
+ return ret;
+}
+
+/****************************************************************************
+ The internals of the rmdir code - called elsewhere.
+****************************************************************************/
+
+BOOL rmdir_internals(connection_struct *conn, char *directory)
+{
+ BOOL ok;
+
+ ok = (vfs_rmdir(conn,directory) == 0);
+ if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) {
+ /*
+ * Check to see if the only thing in this directory are
+ * vetoed files/directories. If so then delete them and
+ * retry. If we fail to delete any of them (and we *don't*
+ * do a recursive delete) then fail the rmdir.
+ */
+ BOOL all_veto_files = True;
+ char *dname;
+ void *dirptr = OpenDir(conn, directory, False);
+
+ if(dirptr != NULL) {
+ int dirpos = TellDir(dirptr);
+ while ((dname = ReadDirName(dirptr))) {
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+ if(!IS_VETO_PATH(conn, dname)) {
+ all_veto_files = False;
+ break;
+ }
+ }
+
+ if(all_veto_files) {
+ SeekDir(dirptr,dirpos);
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fullname;
+ SMB_STRUCT_STAT st;
+
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+
+ /* Construct the full name. */
+ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) {
+ errno = ENOMEM;
+ break;
+ }
+
+ pstrcpy(fullname, directory);
+ pstrcat(fullname, "/");
+ pstrcat(fullname, dname);
+
+ if(conn->vfs_ops.lstat(conn,fullname, &st) != 0)
+ break;
+ if(st.st_mode & S_IFDIR) {
+ if(lp_recursive_veto_delete(SNUM(conn))) {
+ if(recursive_rmdir(conn, fullname) != 0)
+ break;
+ }
+ if(vfs_rmdir(conn,fullname) != 0)
+ break;
+ } else if(vfs_unlink(conn,fullname) != 0)
+ break;
+ }
+ CloseDir(dirptr);
+ /* Retry the rmdir */
+ ok = (vfs_rmdir(conn,directory) == 0);
+ } else {
+ CloseDir(dirptr);
+ }
+ } else {
+ errno = ENOTEMPTY;
+ }
+ }
+
+ if (!ok)
+ DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno)));
+
+ return ok;
+}
+
+/****************************************************************************
+ Reply to a rmdir.
+****************************************************************************/
+
+int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
pstring directory;
- int cnum;
int outsize = 0;
BOOL ok = False;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ START_PROFILE(SMBrmdir);
+
+ srvstr_pull(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), -1, STR_TERMINATE);
+
+ RESOLVE_DFSPATH(directory, conn, inbuf, outbuf)
+
+ unix_convert(directory,conn, NULL,&bad_path,&sbuf);
- cnum = SVAL(inbuf,smb_tid);
- strcpy(directory,smb_buf(inbuf) + 1);
- unix_convert(directory,cnum);
-
- if (check_name(directory,cnum))
- {
- dptr_closepath(directory,SVAL(inbuf,smb_pid));
- ok = (sys_rmdir(directory) == 0);
- if (!ok)
- DEBUG(3,("couldn't remove directory %s : %s\n",
- directory,strerror(errno)));
- }
+ if (check_name(directory,conn))
+ {
+ dptr_closepath(directory,SVAL(inbuf,smb_pid));
+ ok = rmdir_internals(conn, directory);
+ }
if (!ok)
+ {
+ set_bad_path_error(errno, bad_path);
+ END_PROFILE(SMBrmdir);
return(UNIXERROR(ERRDOS,ERRbadpath));
-
+ }
+
outsize = set_message(outbuf,0,0,True);
- DEBUG(3,("%s rmdir %s\n",timestring(),directory));
+ DEBUG( 3, ( "rmdir %s\n", directory ) );
+ END_PROFILE(SMBrmdir);
return(outsize);
}
@@ -2396,26 +2917,26 @@ static BOOL resolve_wildcards(char *name1,char *name2)
fstring ext1,ext2;
char *p,*p2;
- name1 = strrchr(name1,'/');
- name2 = strrchr(name2,'/');
+ name1 = strrchr_m(name1,'/');
+ name2 = strrchr_m(name2,'/');
if (!name1 || !name2) return(False);
- strcpy(root1,name1);
- strcpy(root2,name2);
- p = strrchr(root1,'.');
+ fstrcpy(root1,name1);
+ fstrcpy(root2,name2);
+ p = strrchr_m(root1,'.');
if (p) {
*p = 0;
- strcpy(ext1,p+1);
+ fstrcpy(ext1,p+1);
} else {
- strcpy(ext1,"");
+ fstrcpy(ext1,"");
}
- p = strrchr(root2,'.');
+ p = strrchr_m(root2,'.');
if (p) {
*p = 0;
- strcpy(ext2,p+1);
+ fstrcpy(ext2,p+1);
} else {
- strcpy(ext2,"");
+ fstrcpy(ext2,"");
}
p = root1;
@@ -2442,769 +2963,1289 @@ static BOOL resolve_wildcards(char *name1,char *name2)
if (*p) p++;
}
- strcpy(name2,root2);
+ pstrcpy(name2,root2);
if (ext2[0]) {
- strcat(name2,".");
- strcat(name2,ext2);
+ pstrcat(name2,".");
+ pstrcat(name2,ext2);
}
return(True);
}
-/*******************************************************************
-check if a user is allowed to rename a file
-********************************************************************/
-static BOOL can_rename(char *fname,int cnum)
-{
- struct stat sbuf;
-
- if (!CAN_WRITE(cnum)) return(False);
-
- if (sys_lstat(fname,&sbuf) != 0) return(False);
- if (!check_file_sharing(cnum,fname)) return(False);
-
- return(True);
-}
-
/****************************************************************************
- reply to a mv
+ The guts of the rename command, split out so it may be called by the NT SMB
+ code.
****************************************************************************/
-int reply_mv(char *inbuf,char *outbuf)
+
+NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BOOL replace_if_exists)
{
- int outsize = 0;
- pstring name;
- int cnum;
- pstring directory;
- pstring mask,newname;
- char *p;
- int count=0;
- int error = ERRnoaccess;
- BOOL has_wild;
- BOOL exists=False;
+ pstring directory;
+ pstring mask;
+ pstring newname_last_component;
+ char *p;
+ BOOL has_wild;
+ BOOL bad_path1 = False;
+ BOOL bad_path2 = False;
+ int count=0;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+
+ *directory = *mask = 0;
+
+ rc = unix_convert(name,conn,0,&bad_path1,&sbuf1);
+ unix_convert(newname,conn,newname_last_component,&bad_path2,&sbuf2);
+
+ /*
+ * Split the old name into directory and last component
+ * strings. Note that unix_convert may have stripped off a
+ * leading ./ from both name and newname if the rename is
+ * at the root of the share. We need to make sure either both
+ * name and newname contain a / character or neither of them do
+ * as this is checked in resolve_wildcards().
+ */
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,".");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ *p = '/'; /* Replace needed for exceptional test below. */
+ }
- *directory = *mask = 0;
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ has_wild = ms_has_wild(mask);
+
+ if (!has_wild) {
+ /*
+ * No wildcards - just process the one file.
+ */
+ BOOL is_short_name = mangle_is_8_3(name, True);
+
+ /* Add a terminating '/' to the directory name. */
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+
+ /* Ensure newname contains a '/' also */
+ if(strrchr_m(newname,'/') == 0) {
+ pstring tmpstr;
+
+ pstrcpy(tmpstr, "./");
+ pstrcat(tmpstr, newname);
+ pstrcpy(newname, tmpstr);
+ }
+
+ DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \
+directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n",
+ case_sensitive, case_preserve, short_case_preserve, directory,
+ newname, newname_last_component, is_short_name));
+
+ /*
+ * Check for special case with case preserving and not
+ * case sensitive, if directory and newname are identical,
+ * and the old last component differs from the original
+ * last component only by case, then we should allow
+ * the rename (user is trying to change the case of the
+ * filename).
+ */
+ if((case_sensitive == False) &&
+ (((case_preserve == True) &&
+ (is_short_name == False)) ||
+ ((short_case_preserve == True) &&
+ (is_short_name == True))) &&
+ strcsequal(directory, newname)) {
+ pstring newname_modified_last_component;
+
+ /*
+ * Get the last component of the modified name.
+ * Note that we guarantee that newname contains a '/'
+ * character above.
+ */
+ p = strrchr_m(newname,'/');
+ pstrcpy(newname_modified_last_component,p+1);
+
+ if(strcsequal(newname_modified_last_component,
+ newname_last_component) == False) {
+ /*
+ * Replace the modified last component with
+ * the original.
+ */
+ pstrcpy(p+1, newname_last_component);
+ }
+ }
+
+ resolve_wildcards(directory,newname);
+
+ /*
+ * The source object must exist.
+ */
+
+ if (!vfs_object_exist(conn, directory, &sbuf1)) {
+ DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n",
+ directory,newname));
+
+ if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) {
+ /*
+ * Must return different errors depending on whether the parent
+ * directory existed or not.
+ */
+
+ p = strrchr_m(directory, '/');
+ if (!p)
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ *p = '\0';
+ if (vfs_object_exist(conn, directory, NULL))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ error = map_nt_error_from_unix(errno);
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+
+ return error;
+ }
- cnum = SVAL(inbuf,smb_tid);
-
- strcpy(name,smb_buf(inbuf) + 1);
- strcpy(newname,smb_buf(inbuf) + 3 + strlen(name));
-
- DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
-
- unix_convert(name,cnum);
- unix_convert(newname,cnum);
+ error = can_rename(directory,conn,&sbuf1);
- p = strrchr(name,'/');
- if (!p) {
- strcpy(directory,"./");
- strcpy(mask,name);
- } else {
- *p = 0;
- strcpy(directory,name);
- strcpy(mask,p+1);
- }
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+ return error;
+ }
+
+ /*
+ * If the src and dest names are identical - including case,
+ * don't do the rename, just return success.
+ */
- if (is_mangled(mask))
- check_mangled_stack(mask);
+ if (strcsequal(directory, newname)) {
+ DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory));
+ return NT_STATUS_OK;
+ }
- has_wild = strchr(mask,'*') || strchr(mask,'?');
+ if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) {
+ DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n",
+ directory,newname));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
- if (!has_wild) {
- strcat(directory,"/");
- strcat(directory,mask);
- if (resolve_wildcards(directory,newname) &&
- can_rename(directory,cnum) &&
- !file_exist(newname,NULL) &&
- !sys_rename(directory,newname)) count++;
- if (!count) exists = file_exist(directory,NULL);
- if (!count && exists && file_exist(newname,NULL)) {
- exists = True;
- error = 183;
- }
- } else {
- void *dirptr = NULL;
- char *dname;
- pstring destname;
+ if(conn->vfs_ops.rename(conn,directory, newname) == 0) {
+ DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n",
+ directory,newname));
+ return NT_STATUS_OK;
+ }
- if (check_name(directory,cnum))
- dirptr = OpenDir(directory);
+ if (errno == ENOTDIR || errno == EISDIR)
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ else
+ error = map_nt_error_from_unix(errno);
+
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+
+ return error;
+ } else {
+ /*
+ * Wildcards - process each file that matches.
+ */
+ void *dirptr = NULL;
+ char *dname;
+ pstring destname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ if (dirptr) {
+ error = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+
+ pstrcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ error = NT_STATUS_ACCESS_DENIED;
+ slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname);
+ if (!vfs_object_exist(conn, fname, &sbuf1)) {
+ error = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error)));
+ continue;
+ }
+ error = can_rename(fname,conn,&sbuf1);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(6,("rename %s refused\n", fname));
+ continue;
+ }
+ pstrcpy(destname,newname);
+
+ if (!resolve_wildcards(fname,destname)) {
+ DEBUG(6,("resolve_wildcards %s %s failed\n",
+ fname, destname));
+ continue;
+ }
+
+ if (!replace_if_exists &&
+ vfs_file_exist(conn,destname, NULL)) {
+ DEBUG(6,("file_exist %s\n", destname));
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ continue;
+ }
+
+ if (!conn->vfs_ops.rename(conn,fname,destname))
+ count++;
+ DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0 && NT_STATUS_IS_OK(error)) {
+ error = map_nt_error_from_unix(errno);
+ }
+
+ return error;
+}
- if (dirptr)
- {
- error = ERRbadfile;
+/****************************************************************************
+ Reply to a mv.
+****************************************************************************/
- if (strequal(mask,"????????.???"))
- strcpy(mask,"*");
+int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring newname;
+ char *p;
+ NTSTATUS status;
- while ((dname = ReadDirName(dirptr)))
- {
- pstring fname;
- strcpy(fname,dname);
-
- if(!mask_match(fname, mask, case_sensitive, False)) continue;
+ START_PROFILE(SMBmv);
- error = ERRnoaccess;
- sprintf(fname,"%s/%s",directory,dname);
- if (!can_rename(fname,cnum)) continue;
- strcpy(destname,newname);
-
- if (!resolve_wildcards(fname,destname)) continue;
-
- if (file_exist(destname,NULL)) {
- error = 183;
- continue;
- }
- if (!sys_rename(fname,destname)) count++;
- DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname));
- }
- CloseDir(dirptr);
- }
- }
-
- if (count == 0) {
- if (exists)
- return(ERROR(ERRDOS,error));
- else
- return(UNIXERROR(ERRDOS,error));
- }
-
- outsize = set_message(outbuf,0,0,True);
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_pull(inbuf, name, p, sizeof(name), -1, STR_TERMINATE);
+ p++;
+ p += srvstr_pull(inbuf, newname, p, sizeof(newname), -1, STR_TERMINATE);
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
+
+ status = rename_internals(conn, name, newname, False);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+ outsize = set_message(outbuf,0,0,True);
- return(outsize);
+ END_PROFILE(SMBmv);
+ return(outsize);
}
/*******************************************************************
- copy a file as part of a reply_copy
- ******************************************************************/
-static BOOL copy_file(char *src,char *dest1,int cnum,int ofun,
- int count,BOOL target_is_directory)
+ Copy a file as part of a reply_copy.
+******************************************************************/
+
+static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
+ int count,BOOL target_is_directory, int *err_ret)
{
- int Access,action;
- struct stat st;
- int ret=0;
- int fnum1,fnum2;
- pstring dest;
-
- strcpy(dest,dest1);
- if (target_is_directory) {
- char *p = strrchr(src,'/');
- if (p)
- p++;
- else
- p = src;
- strcat(dest,"/");
- strcat(dest,p);
- }
+ int Access,action;
+ SMB_STRUCT_STAT src_sbuf, sbuf2;
+ SMB_OFF_T ret=-1;
+ files_struct *fsp1,*fsp2;
+ pstring dest;
+
+ *err_ret = 0;
+
+ pstrcpy(dest,dest1);
+ if (target_is_directory) {
+ char *p = strrchr_m(src,'/');
+ if (p)
+ p++;
+ else
+ p = src;
+ pstrcat(dest,"/");
+ pstrcat(dest,p);
+ }
- if (!file_exist(src,&st)) return(False);
+ if (!vfs_file_exist(conn,src,&src_sbuf))
+ return(False);
- fnum1 = find_free_file();
- if (fnum1<0) return(False);
- open_file_shared(fnum1,cnum,src,(DENY_NONE<<4),
- 1,0,&Access,&action);
+ fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),0,0,&Access,&action);
- if (!Files[fnum1].open) return(False);
+ if (!fsp1)
+ return(False);
- if (!target_is_directory && count)
- ofun = 1;
+ if (!target_is_directory && count)
+ ofun = FILE_EXISTS_OPEN;
- fnum2 = find_free_file();
- if (fnum2<0) {
- close_file(fnum1);
- return(False);
- }
- open_file_shared(fnum2,cnum,dest,(DENY_NONE<<4)|1,
- ofun,st.st_mode,&Access,&action);
+ if (vfs_stat(conn,dest,&sbuf2) == -1)
+ ZERO_STRUCTP(&sbuf2);
- if (!Files[fnum2].open) {
- close_file(fnum1);
- return(False);
- }
+ fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
+ ofun,src_sbuf.st_mode,0,&Access,&action);
- if ((ofun&3) == 1) {
- lseek(Files[fnum2].fd,0,SEEK_END);
- }
+ if (!fsp2) {
+ close_file(fsp1,False);
+ return(False);
+ }
+
+ if ((ofun&3) == 1) {
+ if(conn->vfs_ops.lseek(fsp2,fsp2->fd,0,SEEK_END) == -1) {
+ DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) ));
+ /*
+ * Stop the copy from occurring.
+ */
+ ret = -1;
+ src_sbuf.st_size = 0;
+ }
+ }
- if (st.st_size)
- ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0);
+ if (src_sbuf.st_size)
+ ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size);
- close_file(fnum1);
- close_file(fnum2);
+ close_file(fsp1,False);
- return(ret == st.st_size);
-}
+ /* Ensure the modtime is set correctly on the destination file. */
+ fsp2->pending_modtime = src_sbuf.st_mtime;
+ /*
+ * As we are opening fsp1 read-only we only expect
+ * an error on close on fsp2 if we are out of space.
+ * Thus we don't look at the error return from the
+ * close of fsp1.
+ */
+ *err_ret = close_file(fsp2,False);
+ return(ret == (SMB_OFF_T)src_sbuf.st_size);
+}
/****************************************************************************
reply to a file copy.
****************************************************************************/
-int reply_copy(char *inbuf,char *outbuf)
+int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
int outsize = 0;
pstring name;
- int cnum;
pstring directory;
pstring mask,newname;
char *p;
int count=0;
int error = ERRnoaccess;
+ int err = 0;
BOOL has_wild;
BOOL exists=False;
int tid2 = SVAL(inbuf,smb_vwv0);
int ofun = SVAL(inbuf,smb_vwv1);
int flags = SVAL(inbuf,smb_vwv2);
BOOL target_is_directory=False;
+ BOOL bad_path1 = False;
+ BOOL bad_path2 = False;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+ START_PROFILE(SMBcopy);
*directory = *mask = 0;
- cnum = SVAL(inbuf,smb_tid);
-
- strcpy(name,smb_buf(inbuf));
- strcpy(newname,smb_buf(inbuf) + 1 + strlen(name));
+ p = smb_buf(inbuf);
+ p += srvstr_pull(inbuf, name, p, sizeof(name), -1, STR_TERMINATE);
+ p += srvstr_pull(inbuf, newname, p, sizeof(newname), -1, STR_TERMINATE);
DEBUG(3,("reply_copy : %s -> %s\n",name,newname));
- if (tid2 != cnum) {
+ if (tid2 != conn->cnum) {
/* can't currently handle inter share copies XXXX */
DEBUG(3,("Rejecting inter-share copy\n"));
- return(ERROR(ERRSRV,ERRinvdevice));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRSRV,ERRinvdevice);
}
- unix_convert(name,cnum);
- unix_convert(newname,cnum);
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
- target_is_directory = directory_exist(newname,NULL);
+ rc = unix_convert(name,conn,0,&bad_path1,&sbuf1);
+ unix_convert(newname,conn,0,&bad_path2,&sbuf2);
+
+ target_is_directory = VALID_STAT_OF_DIR(sbuf2);
if ((flags&1) && target_is_directory) {
- return(ERROR(ERRDOS,ERRbadfile));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,ERRbadfile);
}
if ((flags&2) && !target_is_directory) {
- return(ERROR(ERRDOS,ERRbadpath));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,ERRbadpath);
}
- if ((flags&(1<<5)) && directory_exist(name,NULL)) {
+ if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) {
/* wants a tree copy! XXXX */
DEBUG(3,("Rejecting tree copy\n"));
- return(ERROR(ERRSRV,ERRerror));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRSRV,ERRerror);
}
- p = strrchr(name,'/');
+ p = strrchr_m(name,'/');
if (!p) {
- strcpy(directory,"./");
- strcpy(mask,name);
+ pstrcpy(directory,"./");
+ pstrcpy(mask,name);
} else {
*p = 0;
- strcpy(directory,name);
- strcpy(mask,p+1);
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
}
- if (is_mangled(mask))
- check_mangled_stack(mask);
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
- has_wild = strchr(mask,'*') || strchr(mask,'?');
+ has_wild = ms_has_wild(mask);
if (!has_wild) {
- strcat(directory,"/");
- strcat(directory,mask);
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
if (resolve_wildcards(directory,newname) &&
- copy_file(directory,newname,cnum,ofun,
- count,target_is_directory)) count++;
- if (!count) exists = file_exist(directory,NULL);
+ copy_file(directory,newname,conn,ofun,
+ count,target_is_directory,&err)) count++;
+ if(!count && err) {
+ errno = err;
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+ if (!count) exists = vfs_file_exist(conn,directory,NULL);
} else {
void *dirptr = NULL;
char *dname;
pstring destname;
- if (check_name(directory,cnum))
- dirptr = OpenDir(directory);
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
- if (dirptr)
- {
+ if (dirptr) {
error = ERRbadfile;
if (strequal(mask,"????????.???"))
- strcpy(mask,"*");
+ pstrcpy(mask,"*");
- while ((dname = ReadDirName(dirptr)))
- {
+ while ((dname = ReadDirName(dirptr))) {
pstring fname;
- strcpy(fname,dname);
+ pstrcpy(fname,dname);
- if(!mask_match(fname, mask, case_sensitive, False)) continue;
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
error = ERRnoaccess;
- sprintf(fname,"%s/%s",directory,dname);
- strcpy(destname,newname);
+ slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
+ pstrcpy(destname,newname);
if (resolve_wildcards(fname,destname) &&
- copy_file(directory,newname,cnum,ofun,
- count,target_is_directory)) count++;
+ copy_file(fname,destname,conn,ofun,
+ count,target_is_directory,&err)) count++;
DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname));
}
CloseDir(dirptr);
- }
+ }
}
if (count == 0) {
- if (exists)
- return(ERROR(ERRDOS,error));
- else
+ if(err) {
+ /* Error on close... */
+ errno = err;
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ if (exists) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,error);
+ } else
+ {
+ if((errno == ENOENT) && (bad_path1 || bad_path2))
+ {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ END_PROFILE(SMBcopy);
return(UNIXERROR(ERRDOS,error));
+ }
}
outsize = set_message(outbuf,1,0,True);
SSVAL(outbuf,smb_vwv0,count);
+ END_PROFILE(SMBcopy);
return(outsize);
}
-
-
/****************************************************************************
reply to a setdir
****************************************************************************/
-int reply_setdir(char *inbuf,char *outbuf)
+int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum,snum;
+ int snum;
int outsize = 0;
BOOL ok = False;
pstring newdir;
+ START_PROFILE(pathworks_setdir);
- cnum = SVAL(inbuf,smb_tid);
-
- snum = Connections[cnum].service;
- if (!CAN_SETDIR(snum))
- return(ERROR(ERRDOS,ERRnoaccess));
-
- strcpy(newdir,smb_buf(inbuf) + 1);
- strlower(newdir);
+ snum = SNUM(conn);
+ if (!CAN_SETDIR(snum)) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ srvstr_pull(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), -1, STR_TERMINATE);
- if (strlen(newdir) == 0)
- ok = True;
- else
- {
- ok = directory_exist(newdir,NULL);
- if (ok)
- string_set(&Connections[cnum].connectpath,newdir);
- }
+ if (strlen(newdir) == 0) {
+ ok = True;
+ } else {
+ ok = vfs_directory_exist(conn,newdir,NULL);
+ if (ok) {
+ string_set(&conn->connectpath,newdir);
+ }
+ }
- if (!ok)
- return(ERROR(ERRDOS,ERRbadpath));
+ if (!ok) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_DOS(ERRDOS,ERRbadpath);
+ }
outsize = set_message(outbuf,0,0,True);
- CVAL(outbuf,smb_reh) = CVAL(inbuf,smb_reh);
-
- DEBUG(3,("%s setdir %s cnum=%d\n",timestring(),newdir,cnum));
+ SCVAL(outbuf,smb_reh,CVAL(inbuf,smb_reh));
+ DEBUG(3,("setdir %s\n", newdir));
+
+ END_PROFILE(pathworks_setdir);
return(outsize);
}
+/****************************************************************************
+ Get a lock pid, dealing with large count requests.
+****************************************************************************/
+
+uint16 get_lock_pid( char *data, int data_offset, BOOL large_file_format)
+{
+ if(!large_file_format)
+ return SVAL(data,SMB_LPID_OFFSET(data_offset));
+ else
+ return SVAL(data,SMB_LARGE_LPID_OFFSET(data_offset));
+}
/****************************************************************************
- reply to a lockingX request
+ Get a lock count, dealing with large count requests.
****************************************************************************/
-int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
+
+SMB_BIG_UINT get_lock_count( char *data, int data_offset, BOOL large_file_format)
{
- int smb_com2 = CVAL(inbuf,smb_vwv0);
- int smb_off2 = SVAL(inbuf,smb_vwv1);
- int fnum = GETFNUM(inbuf,smb_vwv2);
- uint16 locktype = SVAL(inbuf,smb_vwv3);
- uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
- uint16 num_locks = SVAL(inbuf,smb_vwv7);
- uint32 count, offset;
-
- int cnum;
- int i;
- char *data;
- uint32 ecode=0, dummy2;
- int outsize, eclass=0, dummy1;
-
- cnum = SVAL(inbuf,smb_tid);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
-
- data = smb_buf(inbuf);
- /* Data now points at the beginning of the list
- of smb_unlkrng structs */
- for(i = 0; i < (int)num_ulocks; i++) {
- count = IVAL(data,SMB_LKLEN_OFFSET(i));
- offset = IVAL(data,SMB_LKOFF_OFFSET(i));
- if(!do_unlock(fnum,cnum,count,offset,&eclass, &ecode))
- return ERROR(eclass,ecode);
- }
-
- /* Now do any requested locks */
- data += 10*num_ulocks;
- /* Data now points at the beginning of the list
- of smb_lkrng structs */
- for(i = 0; i < (int)num_locks; i++) {
- count = IVAL(data,SMB_LKLEN_OFFSET(i));
- offset = IVAL(data,SMB_LKOFF_OFFSET(i));
- if(!do_lock(fnum,cnum,count,offset, &eclass, &ecode))
- break;
- }
-
- /* If any of the above locks failed, then we must unlock
- all of the previous locks (X/Open spec). */
- if(i != num_locks && num_locks != 0) {
- for(; i >= 0; i--) {
- count = IVAL(data,SMB_LKLEN_OFFSET(i));
- offset = IVAL(data,SMB_LKOFF_OFFSET(i));
- do_unlock(fnum,cnum,count,offset,&dummy1,&dummy2);
- }
- return ERROR(eclass,ecode);
- }
+ SMB_BIG_UINT count = 0;
- outsize = set_message(outbuf,2,0,True);
-
- CVAL(outbuf,smb_vwv0) = smb_com2;
- SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
-
- DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n",
- timestring(),fnum,cnum,locktype,num_locks,num_ulocks));
+ if(!large_file_format) {
+ count = (SMB_BIG_UINT)IVAL(data,SMB_LKLEN_OFFSET(data_offset));
+ } else {
- chain_fnum = fnum;
+#if defined(HAVE_LONGLONG)
+ count = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) |
+ ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)));
+#else /* HAVE_LONGLONG */
- if (smb_com2 != 0xFF)
- outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
- outbuf,outbuf+outsize,
- length,bufsize);
-
- chain_fnum = -1;
-
- return(outsize);
+ /*
+ * NT4.x seems to be broken in that it sends large file (64 bit)
+ * lockingX calls even if the CAP_LARGE_FILES was *not*
+ * negotiated. For boxes without large unsigned ints truncate the
+ * lock count by dropping the top 32 bits.
+ */
+
+ if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) {
+ DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n",
+ (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)),
+ (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) ));
+ SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0);
+ }
+
+ count = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset));
+#endif /* HAVE_LONGLONG */
+ }
+
+ return count;
}
+#if !defined(HAVE_LONGLONG)
+/****************************************************************************
+ Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-).
+****************************************************************************/
+static uint32 map_lock_offset(uint32 high, uint32 low)
+{
+ unsigned int i;
+ uint32 mask = 0;
+ uint32 highcopy = high;
+
+ /*
+ * Try and find out how many significant bits there are in high.
+ */
+
+ for(i = 0; highcopy; i++)
+ highcopy >>= 1;
+
+ /*
+ * We use 31 bits not 32 here as POSIX
+ * lock offsets may not be negative.
+ */
+
+ mask = (~0) << (31 - i);
+
+ if(low & mask)
+ return 0; /* Fail. */
+
+ high <<= (31 - i);
+
+ return (high|low);
+}
+#endif /* !defined(HAVE_LONGLONG) */
/****************************************************************************
- reply to a SMBreadbmpx (read block multiplex) request
+ Get a lock offset, dealing with large offset requests.
****************************************************************************/
-int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize)
+
+SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err)
{
- int cnum,fnum;
- int nread = -1;
- int total_read;
- char *data;
- int32 startpos;
- int outsize, mincount, maxcount;
- int max_per_packet;
- int tcount;
- int pad;
+ SMB_BIG_UINT offset = 0;
+
+ *err = False;
- /* this function doesn't seem to work - disable by default */
- if (!lp_readbmpx())
- return(ERROR(ERRSRV,ERRuseSTD));
+ if(!large_file_format) {
+ offset = (SMB_BIG_UINT)IVAL(data,SMB_LKOFF_OFFSET(data_offset));
+ } else {
- outsize = set_message(outbuf,8,0,True);
+#if defined(HAVE_LONGLONG)
+ offset = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) |
+ ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)));
+#else /* HAVE_LONGLONG */
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ /*
+ * NT4.x seems to be broken in that it sends large file (64 bit)
+ * lockingX calls even if the CAP_LARGE_FILES was *not*
+ * negotiated. For boxes without large unsigned ints mangle the
+ * lock offset by mapping the top 32 bits onto the lower 32.
+ */
+
+ if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) {
+ uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
+ uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset));
+ uint32 new_low = 0;
+
+ if((new_low = map_lock_offset(high, low)) == 0) {
+ *err = True;
+ return (SMB_BIG_UINT)-1;
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_READ(fnum);
- CHECK_ERROR(fnum);
+ DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n",
+ (unsigned int)high, (unsigned int)low, (unsigned int)new_low ));
+ SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0);
+ SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low);
+ }
- startpos = IVAL(inbuf,smb_vwv1);
- maxcount = SVAL(inbuf,smb_vwv3);
- mincount = SVAL(inbuf,smb_vwv4);
+ offset = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
+#endif /* HAVE_LONGLONG */
+ }
- data = smb_buf(outbuf);
- pad = ((int)data)%4;
- if (pad) pad = 4 - pad;
- data += pad;
+ return offset;
+}
- max_per_packet = bufsize-(outsize+pad);
- tcount = maxcount;
- total_read = 0;
+/****************************************************************************
+ reply to a lockingX request
+****************************************************************************/
- if (is_locked(fnum,cnum,maxcount,startpos))
- return(ERROR(ERRDOS,ERRlock));
+int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1);
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ uint16 num_locks = SVAL(inbuf,smb_vwv7);
+ SMB_BIG_UINT count = 0, offset = 0;
+ uint16 lock_pid;
+ int32 lock_timeout = IVAL(inbuf,smb_vwv4);
+ int i;
+ char *data;
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False;
+ BOOL err;
+ NTSTATUS status;
+
+ START_PROFILE(SMBlockingX);
- do
- {
- int N = MIN(max_per_packet,tcount-total_read);
-
- nread = read_file(fnum,data,startpos,N,N,-1,False);
+ CHECK_FSP(fsp,conn);
+
+ data = smb_buf(inbuf);
- if (nread <= 0) nread = 0;
+ if (locktype & (LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_CHANGE_LOCKTYPE)) {
+ /* we don't support these - and CANCEL_LOCK makes w2k
+ and XP reboot so I don't really want to be
+ compatible! (tridge) */
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /* Check if this is an oplock break on a file
+ we have granted an oplock on.
+ */
+ if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) {
+ /* Client can insist on breaking to none. */
+ BOOL break_to_none = (oplocklevel == 0);
+
+ DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n",
+ (unsigned int)oplocklevel, fsp->fnum ));
+
+ /*
+ * Make sure we have granted an exclusive or batch oplock on this file.
+ */
+
+ if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \
+no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name));
+
+ /* if this is a pure oplock break request then don't send a reply */
+ if (num_locks == 0 && num_ulocks == 0) {
+ END_PROFILE(SMBlockingX);
+ return -1;
+ } else {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+ }
- if (nread < N)
- tcount = total_read + nread;
+ if (remove_oplock(fsp, break_to_none) == False) {
+ DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n",
+ fsp->fsp_name ));
+ }
+
+ /* if this is a pure oplock break request then don't send a reply */
+ if (num_locks == 0 && num_ulocks == 0) {
+ /* Sanity check - ensure a pure oplock break is not a
+ chained request. */
+ if(CVAL(inbuf,smb_vwv0) != 0xff)
+ DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n",
+ (unsigned int)CVAL(inbuf,smb_vwv0) ));
+ END_PROFILE(SMBlockingX);
+ return -1;
+ }
+ }
- set_message(outbuf,8,nread,False);
- SIVAL(outbuf,smb_vwv0,startpos);
- SSVAL(outbuf,smb_vwv2,tcount);
- SSVAL(outbuf,smb_vwv6,nread);
- SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));
+ /*
+ * We do this check *after* we have checked this is not a oplock break
+ * response message. JRA.
+ */
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /* Data now points at the beginning of the list
+ of smb_unlkrng structs */
+ for(i = 0; i < (int)num_ulocks; i++) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
- send_smb(Client,outbuf);
+ DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n",
+ (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name ));
+
+ status = do_unlock(fsp,conn,lock_pid,count,offset);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_NT(status);
+ }
+ }
- total_read += nread;
- startpos += nread;
- }
- while (total_read < tcount);
+ /* Setup the timeout in seconds. */
- return(-1);
-}
+ lock_timeout = ((lock_timeout == -1) ? -1 : lock_timeout/1000);
+
+ /* Now do any requested locks */
+ data += ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /* Data now points at the beginning of the list
+ of smb_lkrng structs */
+
+ for(i = 0; i < (int)num_locks; i++) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n",
+ (double)offset, (double)count, (unsigned int)lock_pid,
+ fsp->fsp_name, (int)lock_timeout ));
+
+ status = do_lock_spin(fsp,conn,lock_pid, count,offset,
+ ((locktype & 1) ? READ_LOCK : WRITE_LOCK));
+ if (NT_STATUS_V(status)) {
+ if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn))) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, lock_timeout, i)) {
+ END_PROFILE(SMBlockingX);
+ return -1;
+ }
+ }
+ break;
+ }
+ }
+
+ /* If any of the above locks failed, then we must unlock
+ all of the previous locks (X/Open spec). */
+ if (i != num_locks && num_locks != 0) {
+ /*
+ * Ensure we don't do a remove on the lock that just failed,
+ * as under POSIX rules, if we have a lock already there, we
+ * will delete it (and we shouldn't) .....
+ */
+ for(i--; i >= 0; i--) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ do_unlock(fsp,conn,lock_pid,count,offset);
+ }
+ END_PROFILE(SMBlockingX);
+ return ERROR_NT(status);
+ }
+ set_message(outbuf,2,0,True);
+
+ DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n",
+ fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) );
+
+ END_PROFILE(SMBlockingX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
/****************************************************************************
- reply to a SMBwritebmpx (write block multiplex primary) request
+ Reply to a SMBreadbmpx (read block multiplex) request.
****************************************************************************/
-int reply_writebmpx(char *inbuf,char *outbuf)
+
+int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
{
- int cnum,numtowrite,fnum;
- int nwritten = -1;
- int outsize = 0;
- int32 startpos;
- int tcount, write_through, smb_doff;
- char *data;
-
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ ssize_t nread = -1;
+ ssize_t total_read;
+ char *data;
+ SMB_OFF_T startpos;
+ int outsize;
+ size_t maxcount;
+ int max_per_packet;
+ size_t tcount;
+ int pad;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBreadBmpx);
+
+ /* this function doesn't seem to work - disable by default */
+ if (!lp_readbmpx()) {
+ END_PROFILE(SMBreadBmpx);
+ return ERROR_DOS(ERRSRV,ERRuseSTD);
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
- CHECK_ERROR(fnum);
+ outsize = set_message(outbuf,8,0,True);
- tcount = SVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv3);
- write_through = BITSETW(inbuf+smb_vwv7,0);
- numtowrite = SVAL(inbuf,smb_vwv10);
- smb_doff = SVAL(inbuf,smb_vwv11);
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
- data = smb_base(inbuf) + smb_doff;
+ startpos = IVAL(inbuf,smb_vwv1);
+ maxcount = SVAL(inbuf,smb_vwv3);
- /* If this fails we need to send an SMBwriteC response,
- not an SMBwritebmpx - set this up now so we don't forget */
- CVAL(outbuf,smb_com) = SMBwritec;
+ data = smb_buf(outbuf);
+ pad = ((long)data)%4;
+ if (pad)
+ pad = 4 - pad;
+ data += pad;
- if (is_locked(fnum,cnum,tcount,startpos))
- return(ERROR(ERRDOS,ERRlock));
+ max_per_packet = bufsize-(outsize+pad);
+ tcount = maxcount;
+ total_read = 0;
- seek_file(fnum,startpos);
- nwritten = write_file(fnum,data,numtowrite);
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBreadBmpx);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
- if(lp_syncalways(SNUM(cnum)) || write_through)
- sync_file(fnum);
+ do {
+ size_t N = MIN(max_per_packet,tcount-total_read);
- if(nwritten < numtowrite)
- return(UNIXERROR(ERRHRD,ERRdiskfull));
+ nread = read_file(fsp,data,startpos,N);
- /* If the maximum to be written to this file
- is greater than what we just wrote then set
- up a secondary struct to be attached to this
- fd, we will use this to cache error messages etc. */
- if(tcount > nwritten)
- {
- write_bmpx_struct *wbms;
- if(Files[fnum].wbmpx_ptr != NULL)
- wbms = Files[fnum].wbmpx_ptr; /* Use an existing struct */
- else
- wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
- if(!wbms)
- {
- DEBUG(0,("Out of memory in reply_readmpx\n"));
- return(ERROR(ERRSRV,ERRnoresource));
- }
- wbms->wr_mode = write_through;
- wbms->wr_discard = False; /* No errors yet */
- wbms->wr_total_written = nwritten;
- wbms->wr_errclass = 0;
- wbms->wr_error = 0;
- Files[fnum].wbmpx_ptr = wbms;
- }
+ if (nread <= 0)
+ nread = 0;
- /* We are returning successfully, set the message type back to
- SMBwritebmpx */
- CVAL(outbuf,smb_com) = SMBwriteBmpx;
-
- outsize = set_message(outbuf,1,0,True);
-
- SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
-
- DEBUG(3,("%s writebmpx fnum=%d cnum=%d num=%d wrote=%d\n",
- timestring(),fnum,cnum,numtowrite,nwritten));
-
- if (write_through && tcount==nwritten) {
- /* we need to send both a primary and a secondary response */
- smb_setlen(outbuf,outsize - 4);
- send_smb(Client,outbuf);
+ if (nread < (ssize_t)N)
+ tcount = total_read + nread;
- /* now the secondary */
- outsize = set_message(outbuf,1,0,True);
- CVAL(outbuf,smb_com) = SMBwritec;
- SSVAL(outbuf,smb_vwv0,nwritten);
- }
+ set_message(outbuf,8,nread,False);
+ SIVAL(outbuf,smb_vwv0,startpos);
+ SSVAL(outbuf,smb_vwv2,tcount);
+ SSVAL(outbuf,smb_vwv6,nread);
+ SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));
- return(outsize);
-}
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_readbmpx: send_smb failed.");
+ total_read += nread;
+ startpos += nread;
+ } while (total_read < (ssize_t)tcount);
+
+ END_PROFILE(SMBreadBmpx);
+ return(-1);
+}
/****************************************************************************
- reply to a SMBwritebs (write block multiplex secondary) request
+ Reply to a SMBsetattrE.
****************************************************************************/
-int reply_writebs(char *inbuf,char *outbuf)
+
+int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
{
- int cnum,numtowrite,fnum;
- int nwritten = -1;
- int outsize = 0;
- int32 startpos;
- int tcount, write_through, smb_doff;
- char *data;
- write_bmpx_struct *wbms;
- BOOL send_response = False;
+ struct utimbuf unix_times;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBsetattrE);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBgetattrE);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /*
+ * Convert the DOS times into unix times. Ignore create
+ * time as UNIX can't set this.
+ */
+
+ unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
+ unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
- CHECK_FNUM(fnum,cnum);
- CHECK_WRITE(fnum);
+ /*
+ * Patch from Ray Frush <frush@engr.colostate.edu>
+ * Sometimes times are sent as zero - ignore them.
+ */
- tcount = SVAL(inbuf,smb_vwv1);
- startpos = IVAL(inbuf,smb_vwv2);
- numtowrite = SVAL(inbuf,smb_vwv6);
- smb_doff = SVAL(inbuf,smb_vwv7);
+ if ((unix_times.actime == 0) && (unix_times.modtime == 0)) {
+ /* Ignore request */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "reply_setattrE fnum=%d ", fsp->fnum);
+ dbgtext( "ignoring zero request - not setting timestamps of 0\n" );
+ }
+ END_PROFILE(SMBsetattrE);
+ return(outsize);
+ } else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) {
+ /* set modify time = to access time if modify time was 0 */
+ unix_times.modtime = unix_times.actime;
+ }
- data = smb_base(inbuf) + smb_doff;
+ /* Set the date on this file */
+ if(file_utime(conn, fsp->fsp_name, &unix_times)) {
+ END_PROFILE(SMBsetattrE);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n",
+ fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) );
- /* We need to send an SMBwriteC response, not an SMBwritebs */
- CVAL(outbuf,smb_com) = SMBwritec;
+ END_PROFILE(SMBsetattrE);
+ return(outsize);
+}
- /* This fd should have an auxiliary struct attached,
- check that it does */
- wbms = Files[fnum].wbmpx_ptr;
- if(!wbms) return(-1);
- /* If write through is set we can return errors, else we must
- cache them */
- write_through = wbms->wr_mode;
+/* Back from the dead for OS/2..... JRA. */
- /* Check for an earlier error */
- if(wbms->wr_discard)
- return -1; /* Just discard the packet */
+/****************************************************************************
+ Reply to a SMBwritebmpx (write block multiplex primary) request.
+****************************************************************************/
+
+int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t tcount;
+ BOOL write_through;
+ int smb_doff;
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteBmpx);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+ CHECK_ERROR(fsp);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ smb_doff = SVAL(inbuf,smb_vwv11);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* If this fails we need to send an SMBwriteC response,
+ not an SMBwritebmpx - set this up now so we don't forget */
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteBmpx);
+ return(ERROR_DOS(ERRDOS,ERRlock));
+ }
- seek_file(fnum,startpos);
- nwritten = write_file(fnum,data,numtowrite);
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- if(lp_syncalways(SNUM(cnum)) || write_through)
- sync_file(fnum);
+ if(lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
- if (nwritten < numtowrite)
- {
- if(write_through) {
- /* We are returning an error - we can delete the aux struct */
- if (wbms) free((char *)wbms);
- Files[fnum].wbmpx_ptr = NULL;
- return(ERROR(ERRHRD,ERRdiskfull));
- }
- return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
- }
+ if(nwritten < (ssize_t)numtowrite) {
+ END_PROFILE(SMBwriteBmpx);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
- /* Increment the total written, if this matches tcount
- we can discard the auxiliary struct (hurrah !) and return a writeC */
- wbms->wr_total_written += nwritten;
- if(wbms->wr_total_written >= tcount)
- {
- if (write_through) {
+ /* If the maximum to be written to this file
+ is greater than what we just wrote then set
+ up a secondary struct to be attached to this
+ fd, we will use this to cache error messages etc. */
+
+ if((ssize_t)tcount > nwritten) {
+ write_bmpx_struct *wbms;
+ if(fsp->wbmpx_ptr != NULL)
+ wbms = fsp->wbmpx_ptr; /* Use an existing struct */
+ else
+ wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
+ if(!wbms) {
+ DEBUG(0,("Out of memory in reply_readmpx\n"));
+ END_PROFILE(SMBwriteBmpx);
+ return(ERROR_DOS(ERRSRV,ERRnoresource));
+ }
+ wbms->wr_mode = write_through;
+ wbms->wr_discard = False; /* No errors yet */
+ wbms->wr_total_written = nwritten;
+ wbms->wr_errclass = 0;
+ wbms->wr_error = 0;
+ fsp->wbmpx_ptr = wbms;
+ }
+
+ /* We are returning successfully, set the message type back to
+ SMBwritebmpx */
+ SCVAL(outbuf,smb_com,SMBwriteBmpx);
+
outsize = set_message(outbuf,1,0,True);
- SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);
- send_response = True;
- }
+
+ SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
+
+ DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten ) );
- free((char *)wbms);
- Files[fnum].wbmpx_ptr = NULL;
- }
+ if (write_through && tcount==nwritten) {
+ /* We need to send both a primary and a secondary response */
+ smb_setlen(outbuf,outsize - 4);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_writebmpx: send_smb failed.");
- if(send_response)
- return(outsize);
+ /* Now the secondary */
+ outsize = set_message(outbuf,1,0,True);
+ SCVAL(outbuf,smb_com,SMBwritec);
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ }
- return(-1);
+ END_PROFILE(SMBwriteBmpx);
+ return(outsize);
}
-
/****************************************************************************
- reply to a SMBsetattrE
+ Reply to a SMBwritebs (write block multiplex secondary) request.
****************************************************************************/
-int reply_setattrE(char *inbuf,char *outbuf)
+
+int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
{
- int cnum,fnum;
- struct utimbuf unix_times;
- int outsize = 0;
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t tcount;
+ BOOL write_through;
+ int smb_doff;
+ char *data;
+ write_bmpx_struct *wbms;
+ BOOL send_response = False;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteBs);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ numtowrite = SVAL(inbuf,smb_vwv6);
+ smb_doff = SVAL(inbuf,smb_vwv7);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* We need to send an SMBwriteC response, not an SMBwritebs */
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ /* This fd should have an auxiliary struct attached,
+ check that it does */
+ wbms = fsp->wbmpx_ptr;
+ if(!wbms) {
+ END_PROFILE(SMBwriteBs);
+ return(-1);
+ }
- outsize = set_message(outbuf,0,0,True);
+ /* If write through is set we can return errors, else we must cache them */
+ write_through = wbms->wr_mode;
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ /* Check for an earlier error */
+ if(wbms->wr_discard) {
+ END_PROFILE(SMBwriteBs);
+ return -1; /* Just discard the packet */
+ }
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
+ nwritten = write_file(fsp,data,startpos,numtowrite);
- /* Convert the DOS times into unix times. Ignore create
- time as UNIX can't set this.
- */
- unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
- unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
+ if(lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
- /* Set the date on this file */
- if(sys_utime(Files[fnum].name, &unix_times))
- return(ERROR(ERRDOS,ERRnoaccess));
-
- DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
+ if (nwritten < (ssize_t)numtowrite) {
+ if(write_through) {
+ /* We are returning an error - we can delete the aux struct */
+ if (wbms)
+ free((char *)wbms);
+ fsp->wbmpx_ptr = NULL;
+ END_PROFILE(SMBwriteBs);
+ return(ERROR_DOS(ERRHRD,ERRdiskfull));
+ }
+ END_PROFILE(SMBwriteBs);
+ return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
+ }
- return(outsize);
-}
+ /* Increment the total written, if this matches tcount
+ we can discard the auxiliary struct (hurrah !) and return a writeC */
+ wbms->wr_total_written += nwritten;
+ if(wbms->wr_total_written >= tcount) {
+ if (write_through) {
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);
+ send_response = True;
+ }
+ free((char *)wbms);
+ fsp->wbmpx_ptr = NULL;
+ }
+
+ if(send_response) {
+ END_PROFILE(SMBwriteBs);
+ return(outsize);
+ }
+
+ END_PROFILE(SMBwriteBs);
+ return(-1);
+}
/****************************************************************************
- reply to a SMBgetattrE
+ Reply to a SMBgetattrE.
****************************************************************************/
-int reply_getattrE(char *inbuf,char *outbuf)
-{
- int cnum,fnum;
- struct stat sbuf;
- int outsize = 0;
- int mode;
- outsize = set_message(outbuf,11,0,True);
+int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ SMB_STRUCT_STAT sbuf;
+ int outsize = 0;
+ int mode;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBgetattrE);
- cnum = SVAL(inbuf,smb_tid);
- fnum = GETFNUM(inbuf,smb_vwv0);
+ outsize = set_message(outbuf,11,0,True);
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBgetattrE);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
- /* Do an fstat on this file */
- if(fstat(Files[fnum].fd, &sbuf))
- return(UNIXERROR(ERRDOS,ERRnoaccess));
+ /* Do an fstat on this file */
+ if(fsp_stat(fsp, &sbuf)) {
+ END_PROFILE(SMBgetattrE);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- mode = dos_mode(cnum,Files[fnum].name,&sbuf);
+ mode = dos_mode(conn,fsp->fsp_name,&sbuf);
- /* Convert the times into dos times. Set create
- date to be last modify date as UNIX doesn't save
- this */
- put_dos_date2(outbuf,smb_vwv0,sbuf.st_mtime);
- put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
- put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
- if (mode & aDIR)
- {
- SIVAL(outbuf,smb_vwv6,0);
- SIVAL(outbuf,smb_vwv8,0);
- }
- else
- {
- SIVAL(outbuf,smb_vwv6,sbuf.st_size);
- SIVAL(outbuf,smb_vwv8,ROUNDUP(sbuf.st_size,1024));
- }
- SSVAL(outbuf,smb_vwv10, mode);
+ /*
+ * Convert the times into dos times. Set create
+ * date to be last modify date as UNIX doesn't save
+ * this.
+ */
+
+ put_dos_date2(outbuf,smb_vwv0,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))));
+ put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
+ put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
+
+ if (mode & aDIR) {
+ SIVAL(outbuf,smb_vwv6,0);
+ SIVAL(outbuf,smb_vwv8,0);
+ } else {
+ SIVAL(outbuf,smb_vwv6,(uint32)sbuf.st_size);
+ SIVAL(outbuf,smb_vwv8,SMB_ROUNDUP(sbuf.st_size,1024));
+ }
+ SSVAL(outbuf,smb_vwv10, mode);
- DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
+ DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum));
- return(outsize);
+ END_PROFILE(SMBgetattrE);
+ return(outsize);
}
-
-
-
-
-
diff --git a/source3/smbd/sec_ctx.c b/source3/smbd/sec_ctx.c
new file mode 100644
index 0000000000..87bf8b1744
--- /dev/null
+++ b/source3/smbd/sec_ctx.c
@@ -0,0 +1,424 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Tim Potter 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern struct current_user current_user;
+
+struct sec_ctx {
+ uid_t uid;
+ uid_t gid;
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *token;
+};
+
+/* A stack of security contexts. We include the current context as being
+ the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
+
+static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
+static int sec_ctx_stack_ndx;
+
+/****************************************************************************
+ Become the specified uid.
+****************************************************************************/
+
+static BOOL become_uid(uid_t uid)
+{
+ /* Check for dodgy uid values */
+
+ if (uid == (uid_t)-1 ||
+ ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
+ static int done;
+
+ if (!done) {
+ DEBUG(1,("WARNING: using uid %d is a security risk\n",
+ (int)uid));
+ done = 1;
+ }
+ }
+
+ /* Set effective user id */
+
+ set_effective_uid(uid);
+
+ DO_PROFILE_INC(uid_changes);
+ return True;
+}
+
+/****************************************************************************
+ Become the specified gid.
+****************************************************************************/
+
+static BOOL become_gid(gid_t gid)
+{
+ /* Check for dodgy gid values */
+
+ if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) &&
+ (gid == (gid_t)65535))) {
+ static int done;
+
+ if (!done) {
+ DEBUG(1,("WARNING: using gid %d is a security risk\n",
+ (int)gid));
+ done = 1;
+ }
+ }
+
+ /* Set effective group id */
+
+ set_effective_gid(gid);
+ return True;
+}
+
+/****************************************************************************
+ Become the specified uid and gid.
+****************************************************************************/
+
+static BOOL become_id(uid_t uid, gid_t gid)
+{
+ return become_gid(gid) && become_uid(uid);
+}
+
+/****************************************************************************
+ Drop back to root privileges in order to change to another user.
+****************************************************************************/
+
+static void gain_root(void)
+{
+ if (non_root_mode()) {
+ return;
+ }
+
+ if (geteuid() != 0) {
+ set_effective_uid(0);
+
+ if (geteuid() != 0) {
+ DEBUG(0,
+ ("Warning: You appear to have a trapdoor "
+ "uid system\n"));
+ }
+ }
+
+ if (getegid() != 0) {
+ set_effective_gid(0);
+
+ if (getegid() != 0) {
+ DEBUG(0,
+ ("Warning: You appear to have a trapdoor "
+ "gid system\n"));
+ }
+ }
+}
+
+/****************************************************************************
+ Get the list of current groups.
+****************************************************************************/
+
+int get_current_groups(int *p_ngroups, gid_t **p_groups)
+{
+ int i;
+ gid_t grp;
+ int ngroups = sys_getgroups(0,&grp);
+ gid_t *groups;
+
+ (*p_ngroups) = 0;
+ (*p_groups) = NULL;
+
+ if (ngroups <= 0)
+ return -1;
+
+ if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL) {
+ DEBUG(0,("setup_groups malloc fail !\n"));
+ return -1;
+ }
+
+ if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
+ SAFE_FREE(groups);
+ return -1;
+ }
+
+ (*p_ngroups) = ngroups;
+ (*p_groups) = groups;
+
+ DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
+ for (i = 0; i < ngroups; i++ ) {
+ DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
+ }
+ DEBUG( 3, ( "\n" ) );
+
+ return ngroups;
+}
+
+/****************************************************************************
+ Initialize the groups a user belongs to.
+****************************************************************************/
+
+BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
+{
+ struct sec_ctx *prev_ctx_p;
+ BOOL result = True;
+
+ if (non_root_mode()) {
+ return True;
+ }
+
+ become_root();
+
+ /* Call initgroups() to get user groups */
+
+ if (initgroups(user,gid) == -1) {
+ DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
+ if (getuid() == 0) {
+ if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
+ DEBUG(0,("This is probably a problem with the account %s\n", user));
+ }
+ }
+ result = False;
+ goto done;
+ }
+
+ /* Store groups in previous user's security context. This will
+ always work as the become_root() call increments the stack
+ pointer. */
+
+ prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
+
+ SAFE_FREE(prev_ctx_p->groups);
+ prev_ctx_p->ngroups = 0;
+
+ get_current_groups(&prev_ctx_p->ngroups, &prev_ctx_p->groups);
+
+ done:
+ unbecome_root();
+
+ return result;
+}
+
+/****************************************************************************
+ Create a new security context on the stack. It is the same as the old
+ one. User changes are done using the set_sec_ctx() function.
+****************************************************************************/
+
+BOOL push_sec_ctx(void)
+{
+ struct sec_ctx *ctx_p;
+
+ /* Check we don't overflow our stack */
+
+ if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Security context stack overflow!\n"));
+ smb_panic("Security context stack overflow!\n");
+ }
+
+ /* Store previous user context */
+
+ sec_ctx_stack_ndx++;
+
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ ctx_p->uid = geteuid();
+ ctx_p->gid = getegid();
+
+ DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
+
+ ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
+
+ ctx_p->ngroups = sys_getgroups(0, NULL);
+
+ if (ctx_p->ngroups != 0) {
+ if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
+ DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
+ delete_nt_token(&ctx_p->token);
+ return False;
+ }
+
+ sys_getgroups(ctx_p->ngroups, ctx_p->groups);
+ } else {
+ ctx_p->groups = NULL;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set the current security context to a given user.
+****************************************************************************/
+
+void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
+{
+ struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ /* Set the security context */
+
+ DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
+
+ if (ngroups) {
+ int i;
+
+ DEBUG(3, ("%d user groups: \n", ngroups));
+ for (i = 0; i < ngroups; i++) {
+ DEBUGADD(3, ("%u ", (unsigned int)groups[i]));
+ }
+
+ DEBUG(3, ("\n"));
+ }
+
+
+ gain_root();
+
+#ifdef HAVE_SETGROUPS
+ sys_setgroups(ngroups, groups);
+#endif
+
+ ctx_p->ngroups = ngroups;
+
+ SAFE_FREE(ctx_p->groups);
+ if (token && (token == ctx_p->token))
+ smb_panic("DUPLICATE_TOKEN");
+
+ delete_nt_token(&ctx_p->token);
+
+ ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
+ ctx_p->token = dup_nt_token(token);
+
+ become_id(uid, gid);
+
+ ctx_p->uid = uid;
+ ctx_p->gid = gid;
+
+ /* Update current_user stuff */
+
+ current_user.uid = uid;
+ current_user.gid = gid;
+ current_user.ngroups = ngroups;
+ current_user.groups = groups;
+ current_user.nt_user_token = ctx_p->token;
+}
+
+/****************************************************************************
+ Become root context.
+****************************************************************************/
+
+void set_root_sec_ctx(void)
+{
+ /* May need to worry about supplementary groups at some stage */
+
+ set_sec_ctx(0, 0, 0, NULL, NULL);
+}
+
+/****************************************************************************
+ Pop a security context from the stack.
+****************************************************************************/
+
+BOOL pop_sec_ctx(void)
+{
+ struct sec_ctx *ctx_p;
+ struct sec_ctx *prev_ctx_p;
+
+ /* Check for stack underflow */
+
+ if (sec_ctx_stack_ndx == 0) {
+ DEBUG(0, ("Security context stack underflow!\n"));
+ smb_panic("Security context stack underflow!\n");
+ }
+
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ /* Clear previous user info */
+
+ ctx_p->uid = (uid_t)-1;
+ ctx_p->gid = (gid_t)-1;
+
+ SAFE_FREE(ctx_p->groups);
+ ctx_p->ngroups = 0;
+
+ delete_nt_token(&ctx_p->token);
+
+ /* Pop back previous user */
+
+ sec_ctx_stack_ndx--;
+
+ gain_root();
+
+ prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+#ifdef HAVE_SETGROUPS
+ sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
+#endif
+
+ become_id(prev_ctx_p->uid, prev_ctx_p->gid);
+
+ /* Update current_user stuff */
+
+ current_user.uid = prev_ctx_p->uid;
+ current_user.gid = prev_ctx_p->gid;
+ current_user.ngroups = prev_ctx_p->ngroups;
+ current_user.groups = prev_ctx_p->groups;
+ current_user.nt_user_token = prev_ctx_p->token;
+
+ DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
+
+ return True;
+}
+
+/* Initialise the security context system */
+
+void init_sec_ctx(void)
+{
+ int i;
+ struct sec_ctx *ctx_p;
+
+ /* Initialise security context stack */
+
+ memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
+
+ for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
+ sec_ctx_stack[i].uid = (uid_t)-1;
+ sec_ctx_stack[i].gid = (gid_t)-1;
+ }
+
+ /* Initialise first level of stack. It is the current context */
+ ctx_p = &sec_ctx_stack[0];
+
+ ctx_p->uid = geteuid();
+ ctx_p->gid = getegid();
+
+ get_current_groups(&ctx_p->ngroups, &ctx_p->groups);
+
+ ctx_p->token = NULL; /* Maps to guest user. */
+
+ /* Initialise current_user global */
+
+ current_user.uid = ctx_p->uid;
+ current_user.gid = ctx_p->gid;
+ current_user.ngroups = ctx_p->ngroups;
+ current_user.groups = ctx_p->groups;
+
+ /* The conn and vuid are usually taken care of by other modules.
+ We initialise them here. */
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+ current_user.nt_user_token = NULL;
+}
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 5d8facef33..c759f56e0c 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -1,8 +1,8 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
Main SMB server routines
- Copyright (C) Andrew Tridgell 1992-1995
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Martin Pool 2002
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,29 +20,11 @@
*/
#include "includes.h"
-#include "loadparm.h"
-#include "pcap.h"
-#include "trans2.h"
-#include "reply.h"
-pstring servicesf = CONFIGFILE;
-pstring OriginalDir ="/";
-extern pstring debugf;
-extern pstring sesssetup_user;
+extern fstring global_myworkgroup;
+extern pstring global_myname;
-char *InBuffer = NULL;
-char *OutBuffer = NULL;
-char *last_inbuf = NULL;
-
-int initial_uid = 0;
-int initial_gid = 0;
-
-BOOL share_mode_pending = False;
-
-/* have I done a become_user? */
-static struct {
- int cnum, uid;
-} last_user;
+int am_parent = 1;
/* the last message the was processed */
int last_message = -1;
@@ -50,4251 +32,869 @@ int last_message = -1;
/* a useful macro to debug the last message processed */
#define LAST_MESSAGE() smb_fn_name(last_message)
-extern pstring scope;
-extern int DEBUGLEVEL;
-extern int case_default;
-extern BOOL case_sensitive;
-extern BOOL case_preserve;
-extern BOOL use_mangled_map;
-extern BOOL short_case_preserve;
-extern BOOL case_mangle;
-extern time_t smb_last_time;
-
extern pstring user_socket_options;
-connection_struct Connections[MAX_CONNECTIONS];
-files_struct Files[MAX_OPEN_FILES];
-
-extern int Protocol;
-
-int maxxmit = BUFFER_SIZE;
-
-int chain_size = 0;
-
-/* a fnum to use when chaining */
-int chain_fnum = -1;
-
-/* number of open connections */
-static int num_connections_open = 0;
+#ifdef WITH_DFS
+extern int dcelogin_atmost_once;
+#endif /* WITH_DFS */
extern fstring remote_machine;
+/* really we should have a top level context structure that has the
+ client file descriptor as an element. That would require a major rewrite :(
-/* these can be set by some functions to override the error codes */
-int unix_ERR_class=SUCCESS;
-int unix_ERR_code=0;
-
-
-extern int extra_time_offset;
+ the following 2 functions are an alternative - they make the file
+ descriptor private to smbd
+ */
+static int server_fd = -1;
-extern pstring myhostname;
-extern struct in_addr myip;
-
-
-static int find_free_connection(int hash);
-
-#ifdef SMB_PASSWD
-extern void generate_next_challenge(char *challenge);
-extern void set_challenge(char *challenge);
-#endif
-
-/* for readability... */
-#define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0)
-#define IS_DOS_DIR(test_mode) (((test_mode) & aDIR) != 0)
-#define IS_DOS_ARCHIVE(test_mode) (((test_mode) & aARCH) != 0)
-#define IS_DOS_SYSTEM(test_mode) (((test_mode) & aSYSTEM) != 0)
-#define IS_DOS_HIDDEN(test_mode) (((test_mode) & aHIDDEN) != 0)
-
-
-
-/****************************************************************************
- change a dos mode to a unix mode
- base permission for files:
- everybody gets read bit set
- dos readonly is represented in unix by removing everyone's write bit
- dos archive is represented in unix by the user's execute bit
- dos system is represented in unix by the group's execute bit
- dos hidden is represented in unix by the other's execute bit
- base permission for directories:
- dos directory is represented in unix by unix's dir bit and the exec bit
-****************************************************************************/
-mode_t unix_mode(int cnum,int dosmode)
+int smbd_server_fd(void)
{
- mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
-
- if ( !IS_DOS_READONLY(dosmode) )
- result |= (S_IWUSR | S_IWGRP | S_IWOTH);
-
- if (IS_DOS_DIR(dosmode))
- result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR);
-
- if (MAP_ARCHIVE(cnum) && IS_DOS_ARCHIVE(dosmode))
- result |= S_IXUSR;
-
- if (MAP_SYSTEM(cnum) && IS_DOS_SYSTEM(dosmode))
- result |= S_IXGRP;
-
- if (MAP_HIDDEN(cnum) && IS_DOS_HIDDEN(dosmode))
- result |= S_IXOTH;
-
- result &= CREATE_MODE(cnum);
- return(result);
+ return server_fd;
}
+void smbd_set_server_fd(int fd)
+{
+ server_fd = fd;
+ client_setfd(fd);
+}
/****************************************************************************
- change a unix mode to a dos mode
+ Terminate signal.
****************************************************************************/
-int dos_mode(int cnum,char *path,struct stat *sbuf)
-{
- int result = 0;
-
-#if OLD_DOS_MODE
- if (!CAN_WRITE(cnum) || !((sbuf->st_mode & S_IWOTH) ||
- Connections[cnum].admin_user ||
- ((sbuf->st_mode & S_IWUSR) &&
- Connections[cnum].uid==sbuf->st_uid) ||
- ((sbuf->st_mode & S_IWGRP) &&
- in_group(sbuf->st_gid,Connections[cnum].gid,
- Connections[cnum].ngroups,
- Connections[cnum].igroups))))
- result |= aRONLY;
-#else
- if (CAN_WRITE(cnum) && !lp_alternate_permissions(SNUM(cnum))) {
- if (!((sbuf->st_mode & S_IWOTH) ||
- Connections[cnum].admin_user ||
- ((sbuf->st_mode & S_IWUSR) && Connections[cnum].uid==sbuf->st_uid) ||
- ((sbuf->st_mode & S_IWGRP) &&
- in_group(sbuf->st_gid,Connections[cnum].gid,
- Connections[cnum].ngroups,Connections[cnum].igroups))))
- result |= aRONLY;
- } else {
- if ((sbuf->st_mode & S_IWUSR) == 0)
- result |= aRONLY;
- }
-#endif
- if ((sbuf->st_mode & S_IXUSR) != 0)
- result |= aARCH;
+VOLATILE sig_atomic_t got_sig_term = 0;
- if (MAP_SYSTEM(cnum) && ((sbuf->st_mode & S_IXGRP) != 0))
- result |= aSYSTEM;
-
- if (MAP_HIDDEN(cnum) && ((sbuf->st_mode & S_IXOTH) != 0))
- result |= aHIDDEN;
-
- if (S_ISDIR(sbuf->st_mode))
- result = aDIR | (result & aRONLY);
-
-#if LINKS_READ_ONLY
- if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
- result |= aRONLY;
-#endif
-
- /* hide files with a name starting with a . */
- if (lp_hide_dot_files(SNUM(cnum)))
- {
- char *p = strrchr(path,'/');
- if (p)
- p++;
- else
- p = path;
-
- if (p[0] == '.' && p[1] != '.' && p[1] != 0)
- result |= aHIDDEN;
- }
-
- return(result);
-}
-
-
-/*******************************************************************
-chmod a file - but preserve some bits
-********************************************************************/
-int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st)
+static void sig_term(void)
{
- struct stat st1;
- int mask=0;
- int tmp;
- int unixmode;
-
- if (!st) {
- st = &st1;
- if (sys_stat(fname,st)) return(-1);
- }
-
- if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
-
- if (dos_mode(cnum,fname,st) == dosmode) return(0);
-
- unixmode = unix_mode(cnum,dosmode);
-
- /* preserve the s bits */
- mask |= (S_ISUID | S_ISGID);
-
- /* preserve the t bit */
-#ifdef S_ISVTX
- mask |= S_ISVTX;
-#endif
-
- /* possibly preserve the x bits */
- if (!MAP_ARCHIVE(cnum)) mask |= S_IXUSR;
- if (!MAP_SYSTEM(cnum)) mask |= S_IXGRP;
- if (!MAP_HIDDEN(cnum)) mask |= S_IXOTH;
-
- unixmode |= (st->st_mode & mask);
-
- /* if we previously had any r bits set then leave them alone */
- if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
- unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
- unixmode |= tmp;
- }
-
- /* if we previously had any w bits set then leave them alone
- if the new mode is not rdonly */
- if (!IS_DOS_READONLY(dosmode) &&
- (tmp = st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) {
- unixmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
- unixmode |= tmp;
- }
-
- return(chmod(fname,unixmode));
+ got_sig_term = 1;
+ sys_select_signal();
}
-
/****************************************************************************
-check if two filenames are equal
-
-this needs to be careful about whether we are case sensitive
+ Catch a sighup.
****************************************************************************/
-static BOOL fname_equal(char *name1, char *name2)
-{
- int l1 = strlen(name1);
- int l2 = strlen(name2);
-
- /* handle filenames ending in a single dot */
- if (l1-l2 == 1 && name1[l1-1] == '.' && lp_strip_dot())
- {
- BOOL ret;
- name1[l1-1] = 0;
- ret = fname_equal(name1,name2);
- name1[l1-1] = '.';
- return(ret);
- }
-
- if (l2-l1 == 1 && name2[l2-1] == '.' && lp_strip_dot())
- {
- BOOL ret;
- name2[l2-1] = 0;
- ret = fname_equal(name1,name2);
- name2[l2-1] = '.';
- return(ret);
- }
-
- /* now normal filename handling */
- if (case_sensitive)
- return(strcmp(name1,name2) == 0);
-
- return(strequal(name1,name2));
-}
+VOLATILE sig_atomic_t reload_after_sighup = 0;
-/****************************************************************************
-mangle the 2nd name and check if it is then equal to the first name
-****************************************************************************/
-static BOOL mangled_equal(char *name1, char *name2)
+static void sig_hup(int sig)
{
- pstring tmpname;
-
- if (is_8_3(name2))
- return(False);
-
- strcpy(tmpname,name2);
- mangle_name_83(tmpname);
-
- return(strequal(name1,tmpname));
+ reload_after_sighup = 1;
+ sys_select_signal();
}
-
/****************************************************************************
-scan a directory to find a filename, matching without case sensitivity
+ Send a SIGTERM to our process group.
+*****************************************************************************/
-If the name looks like a mangled name then try via the mangling functions
-****************************************************************************/
-static BOOL scan_directory(char *path, char *name,int snum,BOOL docache)
+static void killkids(void)
{
- void *cur_dir;
- char *dname;
- BOOL mangled;
- fstring name2;
-
- mangled = is_mangled(name);
-
- /* handle null paths */
- if (*path == 0)
- path = ".";
-
- if (docache && (dname = DirCacheCheck(path,name,snum))) {
- strcpy(name, dname);
- return(True);
- }
-
- if (mangled)
- check_mangled_stack(name);
-
- /* open the directory */
- if (!(cur_dir = OpenDir(path)))
- {
- DEBUG(3,("scan dir didn't open dir [%s]\n",path));
- return(False);
- }
-
- /* now scan for matching names */
- while ((dname = ReadDirName(cur_dir)))
- {
- if (*dname == '.' &&
- (strequal(dname,".") || strequal(dname,"..")))
- continue;
-
- strcpy(name2,dname);
- if (!name_map_mangle(name2,False,snum)) continue;
-
- if ((mangled && mangled_equal(name,name2))
- || fname_equal(name, name2))
- {
- /* we've found the file, change it's name and return */
- if (docache) DirCacheAdd(path,name,dname,snum);
- strcpy(name, dname);
- CloseDir(cur_dir);
- return(True);
- }
- }
-
- CloseDir(cur_dir);
- return(False);
+ if(am_parent) kill(0,SIGTERM);
}
/****************************************************************************
-This routine is called to convert names from the dos namespace to unix
-namespace. It needs to handle any case conversions, mangling, format
-changes etc.
-
-We assume that we have already done a chdir() to the right "root" directory
-for this service.
-
-The function will return False if some part of the name except for the last
-part cannot be resolved
+ Process a sam sync message - not sure whether to do this here or
+ somewhere else.
****************************************************************************/
-BOOL unix_convert(char *name,int cnum)
-{
- struct stat st;
- char *start, *end;
- pstring dirpath;
-
- *dirpath = 0;
-
- /* convert to basic unix format - removing \ chars and cleaning it up */
- unix_format(name);
- unix_clean_name(name);
-
- if (!case_sensitive &&
- (!case_preserve || (is_8_3(name) && !short_case_preserve)))
- strnorm(name);
-
- /* names must be relative to the root of the service - trim any leading /.
- also trim trailing /'s */
- trim_string(name,"/","/");
-
- /* check if it's a printer file */
- if (Connections[cnum].printer)
- {
- if ((! *name) || strchr(name,'/') || !is_8_3(name))
- {
- fstring name2;
- sprintf(name2,"%.6s.XXXXXX",remote_machine);
- strcpy(name,(char *)mktemp(name2));
- }
- return(True);
- }
-
- /* stat the name - if it exists then we are all done! */
- if (sys_stat(name,&st) == 0)
- return(True);
-
- DEBUG(5,("unix_convert(%s,%d)\n",name,cnum));
-
- /* a special case - if we don't have any mangling chars and are case
- sensitive then searching won't help */
- if (case_sensitive && !is_mangled(name) &&
- !lp_strip_dot() && !use_mangled_map)
- return(False);
-
- /* now we need to recursively match the name against the real
- directory structure */
-
- start = name;
- while (strncmp(start,"./",2) == 0)
- start += 2;
-
- /* now match each part of the path name separately, trying the names
- as is first, then trying to scan the directory for matching names */
- for (;start;start = (end?end+1:(char *)NULL))
- {
- /* pinpoint the end of this section of the filename */
- end = strchr(start, '/');
-
- /* chop the name at this point */
- if (end) *end = 0;
-
- /* check if the name exists up to this point */
- if (sys_stat(name, &st) == 0)
- {
- /* it exists. it must either be a directory or this must be
- the last part of the path for it to be OK */
- if (end && !(st.st_mode & S_IFDIR))
- {
- /* an intermediate part of the name isn't a directory */
- DEBUG(5,("Not a dir %s\n",start));
- *end = '/';
- return(False);
- }
- }
- else
- {
- pstring rest;
-
- *rest = 0;
-
- /* remember the rest of the pathname so it can be restored
- later */
- if (end) strcpy(rest,end+1);
-
-
- /* try to find this part of the path in the directory */
- if (strchr(start,'?') || strchr(start,'*') ||
- !scan_directory(dirpath, start, SNUM(cnum), end?True:False))
- {
- if (end)
- {
- /* an intermediate part of the name can't be found */
- DEBUG(5,("Intermediate not found %s\n",start));
- *end = '/';
- return(False);
- }
-
- /* just the last part of the name doesn't exist */
- /* we may need to strupper() or strlower() it in case
- this conversion is being used for file creation
- purposes */
- /* if the filename is of mixed case then don't normalise it */
- if (!case_preserve &&
- (!strhasupper(start) || !strhaslower(start)))
- strnorm(start);
-
- /* check on the mangled stack to see if we can recover the
- base of the filename */
- if (is_mangled(start))
- check_mangled_stack(start);
-
- DEBUG(5,("New file %s\n",start));
- return(True);
- }
-
- /* restore the rest of the string */
- if (end)
- {
- strcpy(start+strlen(start)+1,rest);
- end = start + strlen(start);
- }
- }
- /* add to the dirpath that we have resolved so far */
- if (*dirpath) strcat(dirpath,"/");
- strcat(dirpath,start);
-
- /* restore the / that we wiped out earlier */
- if (end) *end = '/';
- }
-
- /* the name has been resolved */
- DEBUG(5,("conversion finished %s\n",name));
- return(True);
-}
-
-
-
-
-#ifdef QUOTAS
-#ifdef LINUX
-/****************************************************************************
-try to get the disk space from disk quotas (LINUX version)
-****************************************************************************/
-/*
-If you didn't make the symlink to the quota package, too bad :(
-*/
-#include "quota/quotactl.c"
-#include "quota/hasquota.c"
-static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+static void msg_sam_sync(int UNUSED(msg_type), pid_t UNUSED(pid),
+ void *UNUSED(buf), size_t UNUSED(len))
{
- uid_t euser_id;
- struct dqblk D;
- struct stat S;
- dev_t devno ;
- struct mntent *mnt;
- FILE *fp;
- int found ;
- int qcmd, fd ;
- char *qfpathname;
-
- /* find the block device file */
-
- if ( stat(path, &S) == -1 )
- return(False) ;
-
- devno = S.st_dev ;
-
- fp = setmntent(MOUNTED,"r");
- found = False ;
-
- while ((mnt = getmntent(fp)) != (struct mntent *) 0) {
- if ( stat(mnt->mnt_dir,&S) == -1 )
- continue ;
- if (S.st_dev == devno) {
- found = True ;
- break ;
- }
- }
- endmntent(fp) ;
-
- if ( ! found )
- return(False) ;
-
- qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
-
- if (hasmntopt(mnt, MNTOPT_NOAUTO) || hasmntopt(mnt, MNTOPT_NOQUOTA))
- return(False) ;
-
- if (!hasquota(mnt, USRQUOTA, &qfpathname))
- return(False) ;
-
- euser_id = geteuid();
- seteuid(0);
-
- if (quotactl(qcmd, mnt->mnt_fsname, euser_id, (caddr_t)&D) != 0) {
- if ((fd = open(qfpathname, O_RDONLY)) < 0) {
- seteuid(euser_id);
- return(False);
- }
- lseek(fd, (long) dqoff(euser_id), L_SET);
- switch (read(fd, &D, sizeof(struct dqblk))) {
- case 0:/* EOF */
- memset((caddr_t)&D, 0, sizeof(struct dqblk));
- break;
- case sizeof(struct dqblk): /* OK */
- break;
- default: /* ERROR */
- close(fd);
- seteuid(euser_id);
- return(False);
- }
- }
- seteuid(euser_id);
- *bsize=1024;
-
- if (D.dqb_bsoftlimit==0)
- return(False);
- if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curinodes>D.dqb_isoftlimit))
- {
- *dfree = 0;
- *dsize = D.dqb_curblocks;
- }
- else {
- *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
- *dsize = D.dqb_bsoftlimit;
- }
- return (True);
+ DEBUG(10, ("** sam sync message received, ignoring\n"));
}
-#else
-#ifndef CRAY
-/****************************************************************************
-try to get the disk space from disk quotas
-****************************************************************************/
-static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
-{
- uid_t user_id, euser_id;
- int r;
- char dev_disk[256];
- struct dqblk D;
- struct stat S;
- /* find the block device file */
- if ((stat(path, &S)<0) ||
- (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
-
- euser_id = geteuid();
-
-#ifdef USE_SETRES
- /* for HPUX, real uid must be same as euid to execute quotactl for euid */
- user_id = getuid();
- setresuid(euser_id,-1,-1);
-#endif
- r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
- #ifdef USE_SETRES
- if (setresuid(user_id,-1,-1))
- DEBUG(5,("Unable to reset uid to %d\n", user_id));
- #endif
- /* Use softlimit to determine disk space, except when it has been exceeded */
- *bsize = 1024;
- if (r)
- {
- if (errno == EDQUOT)
- {
- *dfree =0;
- *dsize =D.dqb_curblocks;
- return (True);
- }
- else return(False);
- }
- /* Use softlimit to determine disk space, except when it has been exceeded */
- if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curfiles>D.dqb_fsoftlimit))
- {
- *dfree = 0;
- *dsize = D.dqb_curblocks;
- }
- else {
- *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
- *dsize = D.dqb_bsoftlimit;
- }
- return (True);
-}
-#else
-/****************************************************************************
-try to get the disk space from disk quotas (CRAY VERSION)
-****************************************************************************/
-static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
-{
- struct mntent *mnt;
- FILE *fd;
- struct stat sbuf;
- dev_t devno ;
- static dev_t devno_cached = 0 ;
- static char name[MNTMAXSTR] ;
- struct q_request request ;
- struct qf_header header ;
- static int quota_default = 0 ;
- int found ;
-
- if ( stat(path,&sbuf) == -1 )
- return(False) ;
-
- devno = sbuf.st_dev ;
-
- if ( devno != devno_cached ) {
-
- devno_cached = devno ;
-
- if ((fd = setmntent(KMTAB)) == NULL)
- return(False) ;
-
- found = False ;
-
- while ((mnt = getmntent(fd)) != NULL) {
-
- if ( stat(mnt->mnt_dir,&sbuf) == -1 )
- continue ;
-
- if (sbuf.st_dev == devno) {
-
- found = True ;
- break ;
-
- }
-
- }
-
- strcpy(name,mnt->mnt_dir) ;
- endmntent(fd) ;
-
- if ( ! found )
- return(False) ;
- }
-
- request.qf_magic = QF_MAGIC ;
- request.qf_entry.id = geteuid() ;
-
- if (quotactl(name, Q_GETQUOTA, &request) == -1)
- return(False) ;
-
- if ( ! request.user )
- return(False) ;
-
- if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
-
- if ( ! quota_default ) {
-
- if ( quotactl(name, Q_GETHEADER, &header) == -1 )
- return(False) ;
- else
- quota_default = header.user_h.def_fq ;
- }
-
- *dfree = quota_default ;
-
- }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
-
- *dfree = 0 ;
-
- }else{
-
- *dfree = request.qf_entry.user_q.f_quota ;
-
- }
-
- *dsize = request.qf_entry.user_q.f_use ;
-
- if ( *dfree )
- *dfree -= *dsize ;
-
- if ( *dfree < 0 )
- *dfree = 0 ;
-
- *bsize = 4096 ; /* Cray blocksize */
-
- return(True) ;
-
-}
-#endif /* CRAY */
-#endif /* LINUX */
-#endif /* QUOTAS */
-
/****************************************************************************
-normalise for DOS usage
+ Process a sam sync replicate message - not sure whether to do this here or
+ somewhere else.
****************************************************************************/
-static void disk_norm(int *bsize,int *dfree,int *dsize)
-{
- /* check if the disk is beyond the max disk size */
- int maxdisksize = lp_maxdisksize();
- if (maxdisksize) {
- /* convert to blocks - and don't overflow */
- maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
- if (*dsize > maxdisksize) *dsize = maxdisksize;
- if (*dfree > maxdisksize) *dfree = maxdisksize-1; /* the -1 should stop
- applications getting
- div by 0 errors */
- }
-
- while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512)
- {
- *dfree /= 2;
- *dsize /= 2;
- *bsize *= 2;
- if (*bsize > WORDMAX )
- {
- *bsize = WORDMAX;
- if (*dsize > WORDMAX)
- *dsize = WORDMAX;
- if (*dfree > WORDMAX)
- *dfree = WORDMAX;
- break;
- }
- }
-}
-/****************************************************************************
- return number of 1K blocks available on a path and total number
-****************************************************************************/
-int disk_free(char *path,int *bsize,int *dfree,int *dsize)
+static void msg_sam_repl(int msg_type, pid_t pid, void *buf, size_t len)
{
- char *df_command = lp_dfree_command();
-#ifndef NO_STATFS
-#ifdef USE_STATVFS
- struct statvfs fs;
-#else
-#ifdef ULTRIX
- struct fs_data fs;
-#else
- struct statfs fs;
-#endif
-#endif
-#endif
-
-#ifdef QUOTAS
- if (disk_quotas(path, bsize, dfree, dsize))
- {
- disk_norm(bsize,dfree,dsize);
- return(((*bsize)/1024)*(*dfree));
- }
-#endif
-
+ uint32 low_serial;
- /* possibly use system() to get the result */
- if (df_command && *df_command)
- {
- int ret;
- pstring syscmd;
- pstring outfile;
-
- sprintf(outfile,"/tmp/dfree.smb.%d",(int)getpid());
- sprintf(syscmd,"%s %s",df_command,path);
- standard_sub_basic(syscmd);
-
- ret = smbrun(syscmd,outfile);
- DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
-
- {
- FILE *f = fopen(outfile,"r");
- *dsize = 0;
- *dfree = 0;
- *bsize = 1024;
- if (f)
- {
- fscanf(f,"%d %d %d",dsize,dfree,bsize);
- fclose(f);
- }
- else
- DEBUG(0,("Can't open %s\n",outfile));
- }
-
- unlink(outfile);
- disk_norm(bsize,dfree,dsize);
- return(((*bsize)/1024)*(*dfree));
- }
-
-#ifdef NO_STATFS
- DEBUG(1,("Warning - no statfs function\n"));
- return(1);
-#else
-#ifdef STATFS4
- if (statfs(path,&fs,sizeof(fs),0) != 0)
-#else
-#ifdef USE_STATVFS
- if (statvfs(path, &fs))
-#else
-#ifdef STATFS3
- if (statfs(path,&fs,sizeof(fs)) == -1)
-#else
- if (statfs(path,&fs) == -1)
-#endif /* STATFS3 */
-#endif /* USE_STATVFS */
-#endif /* STATFS4 */
- {
- DEBUG(3,("dfree call failed code errno=%d\n",errno));
- *bsize = 1024;
- *dfree = 1;
- *dsize = 1;
- return(((*bsize)/1024)*(*dfree));
- }
-
-#ifdef ULTRIX
- *bsize = 1024;
- *dfree = fs.fd_req.bfree;
- *dsize = fs.fd_req.btot;
-#else
-#ifdef USE_STATVFS
- *bsize = fs.f_frsize;
-#else
-#ifdef USE_F_FSIZE
- /* eg: osf1 has f_fsize = fundamental filesystem block size,
- f_bsize = optimal transfer block size (MX: 94-04-19) */
- *bsize = fs.f_fsize;
-#else
- *bsize = fs.f_bsize;
-#endif /* STATFS3 */
-#endif /* USE_STATVFS */
-
-#ifdef STATFS4
- *dfree = fs.f_bfree;
-#else
- *dfree = fs.f_bavail;
-#endif /* STATFS4 */
- *dsize = fs.f_blocks;
-#endif /* ULTRIX */
-
-#if defined(SCO) || defined(ISC) || defined(MIPS)
- *bsize = 512;
-#endif
+ if (len != sizeof(uint32))
+ return;
-/* handle rediculous bsize values - some OSes are broken */
-if ((*bsize) < 512 || (*bsize)>0xFFFF) *bsize = 1024;
+ low_serial = *((uint32 *)buf);
- disk_norm(bsize,dfree,dsize);
-
- if (*bsize < 256)
- *bsize = 512;
- if ((*dsize)<1)
- {
- DEBUG(0,("dfree seems to be broken on your system\n"));
- *dsize = 20*1024*1024/(*bsize);
- *dfree = MAX(1,*dfree);
- }
- return(((*bsize)/1024)*(*dfree));
-#endif
+ DEBUG(3, ("received sam replication message, serial = 0x%04x\n",
+ low_serial));
}
-
/****************************************************************************
-wrap it to get filenames right
+ Open the socket communication - inetd.
****************************************************************************/
-int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize)
-{
- return(disk_free(dos_to_unix(path,False),bsize,dfree,dsize));
-}
-
-
-/****************************************************************************
-check a filename - possibly caling reducename
-
-This is called by every routine before it allows an operation on a filename.
-It does any final confirmation necessary to ensure that the filename is
-a valid one for the user to access.
-****************************************************************************/
-BOOL check_name(char *name,int cnum)
+static BOOL open_sockets_inetd(void)
{
- BOOL ret;
-
- errno = 0;
-
- ret = reduce_name(name,Connections[cnum].connectpath,lp_widelinks(SNUM(cnum)));
- if (!ret)
- DEBUG(5,("check_name on %s failed\n",name));
+ /* Started from inetd. fd 0 is the socket. */
+ /* We will abort gracefully when the client or remote system
+ goes away */
+ smbd_set_server_fd(dup(0));
+
+ /* close our standard file descriptors */
+ close_low_fds();
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
- return(ret);
+ return True;
}
-/****************************************************************************
-check a filename - possibly caling reducename
-****************************************************************************/
-static void check_for_pipe(char *fname)
+static void msg_exit_server(int msg_type, pid_t src, void *buf, size_t len)
{
- /* special case of pipe opens */
- char s[10];
- StrnCpy(s,fname,9);
- strlower(s);
- if (strstr(s,"pipe/"))
- {
- DEBUG(3,("Rejecting named pipe open for %s\n",fname));
- unix_ERR_class = ERRSRV;
- unix_ERR_code = ERRaccess;
- }
+ exit_server("Got a SHUTDOWN message");
}
/****************************************************************************
-open a file
+ Open the socket communication.
****************************************************************************/
-void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
-{
- pstring fname;
-
- Files[fnum].open = False;
- Files[fnum].fd = -1;
- errno = EPERM;
-
- strcpy(fname,fname1);
-
- /* check permissions */
- if ((flags != O_RDONLY) && !CAN_WRITE(cnum) && !Connections[cnum].printer)
- {
- DEBUG(3,("Permission denied opening %s\n",fname));
- check_for_pipe(fname);
- return;
- }
-
- /* this handles a bug in Win95 - it doesn't say to create the file when it
- should */
- if (Connections[cnum].printer)
- flags |= O_CREAT;
-
-/*
- if (flags == O_WRONLY)
- DEBUG(3,("Bug in client? Set O_WRONLY without O_CREAT\n"));
-*/
-#if UTIME_WORKAROUND
- /* XXXX - is this OK?? */
- /* this works around a utime bug but can cause other problems */
- if ((flags & (O_WRONLY|O_RDWR)) && (flags & O_CREAT) && !(flags & O_APPEND))
- sys_unlink(fname);
-#endif
-
-
- Files[fnum].fd = sys_open(fname,flags,mode);
-
- if ((Files[fnum].fd>=0) &&
- Connections[cnum].printer && lp_minprintspace(SNUM(cnum))) {
- pstring dname;
- int dum1,dum2,dum3;
- char *p;
- strcpy(dname,fname);
- p = strrchr(dname,'/');
- if (p) *p = 0;
- if (sys_disk_free(dname,&dum1,&dum2,&dum3) <
- lp_minprintspace(SNUM(cnum))) {
- close(Files[fnum].fd);
- Files[fnum].fd = -1;
- sys_unlink(fname);
- errno = ENOSPC;
- return;
- }
- }
-
-
- /* Fix for files ending in '.' */
- if((Files[fnum].fd == -1) && (errno == ENOENT) &&
- (strchr(fname,'.')==NULL))
- {
- strcat(fname,".");
- Files[fnum].fd = sys_open(fname,flags,mode);
- }
-
-#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF))
- if ((Files[fnum].fd == -1) && (errno == ENAMETOOLONG))
- {
- int max_len;
- char *p = strrchr(fname, '/');
-
- if (p == fname) /* name is "/xxx" */
- {
- max_len = pathconf("/", _PC_NAME_MAX);
- p++;
- }
- else if ((p == NULL) || (p == fname))
- {
- p = fname;
- max_len = pathconf(".", _PC_NAME_MAX);
- }
- else
- {
- *p = '\0';
- max_len = pathconf(fname, _PC_NAME_MAX);
- *p = '/';
- p++;
- }
- if (strlen(p) > max_len)
- {
- char tmp = p[max_len];
+static BOOL open_sockets(BOOL is_daemon,int port)
+{
+ int num_interfaces = iface_count();
+ int fd_listenset[FD_SETSIZE];
+ fd_set listen_set;
+ int s;
+ int i;
- p[max_len] = '\0';
- if ((Files[fnum].fd = sys_open(fname,flags,mode)) == -1)
- p[max_len] = tmp;
+ if (!is_daemon) {
+ return open_sockets_inetd();
}
- }
-#endif
- if (Files[fnum].fd < 0)
- {
- DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
- fname,strerror(errno),flags));
- check_for_pipe(fname);
- return;
- }
-
- if (Files[fnum].fd >= 0)
- {
- struct stat st;
- Connections[cnum].num_files_open++;
- fstat(Files[fnum].fd,&st);
- Files[fnum].mode = st.st_mode;
- Files[fnum].open_time = time(NULL);
- Files[fnum].size = 0;
- Files[fnum].pos = -1;
- Files[fnum].open = True;
- Files[fnum].mmap_ptr = NULL;
- Files[fnum].mmap_size = 0;
- Files[fnum].can_lock = True;
- Files[fnum].can_read = ((flags & O_WRONLY)==0);
- Files[fnum].can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
- Files[fnum].share_mode = 0;
- Files[fnum].share_pending = False;
- Files[fnum].print_file = Connections[cnum].printer;
- Files[fnum].modified = False;
- Files[fnum].cnum = cnum;
- string_set(&Files[fnum].name,fname);
- Files[fnum].wbmpx_ptr = NULL;
-
- /*
- * If the printer is marked as postscript output a leading
- * file identifier to ensure the file is treated as a raw
- * postscript file.
- * This has a similar effect as CtrlD=0 in WIN.INI file.
- * tim@fsg.com 09/06/94
- */
- if (Files[fnum].print_file && POSTSCRIPT(cnum) &&
- Files[fnum].can_write)
- {
- DEBUG(3,("Writing postscript line\n"));
- write_file(fnum,"%!\n",3);
- }
-
- DEBUG(2,("%s %s opened file %s read=%s write=%s (numopen=%d fnum=%d)\n",
- timestring(),Connections[cnum].user,fname,
- BOOLSTR(Files[fnum].can_read),BOOLSTR(Files[fnum].can_write),
- Connections[cnum].num_files_open,fnum));
-
- }
-
-#if USE_MMAP
- /* mmap it if read-only */
- if (!Files[fnum].can_write)
- {
- Files[fnum].mmap_size = file_size(fname);
- Files[fnum].mmap_ptr = (char *)mmap(NULL,Files[fnum].mmap_size,
- PROT_READ,MAP_SHARED,Files[fnum].fd,0);
-
- if (Files[fnum].mmap_ptr == (char *)-1 || !Files[fnum].mmap_ptr)
+
+#ifdef HAVE_ATEXIT
{
- DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno)));
- Files[fnum].mmap_ptr = NULL;
+ static int atexit_set;
+ if(atexit_set == 0) {
+ atexit_set=1;
+ atexit(killkids);
+ }
}
- }
-#endif
-}
-
-/*******************************************************************
-sync a file
-********************************************************************/
-void sync_file(int fnum)
-{
-#ifndef NO_FSYNC
- fsync(Files[fnum].fd);
-#endif
-}
-
-/****************************************************************************
-run a file if it is a magic script
-****************************************************************************/
-static void check_magic(int fnum,int cnum)
-{
- if (!*lp_magicscript(SNUM(cnum)))
- return;
-
- DEBUG(5,("checking magic for %s\n",Files[fnum].name));
-
- {
- char *p;
- if (!(p = strrchr(Files[fnum].name,'/')))
- p = Files[fnum].name;
- else
- p++;
-
- if (!strequal(lp_magicscript(SNUM(cnum)),p))
- return;
- }
-
- {
- int ret;
- pstring magic_output;
- pstring fname;
- strcpy(fname,Files[fnum].name);
-
- if (*lp_magicoutput(SNUM(cnum)))
- strcpy(magic_output,lp_magicoutput(SNUM(cnum)));
- else
- sprintf(magic_output,"%s.out",fname);
-
- chmod(fname,0755);
- ret = smbrun(fname,magic_output);
- DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
- unlink(fname);
- }
-}
-
-
-/****************************************************************************
-close a file - possibly invalidating the read prediction
-****************************************************************************/
-void close_file(int fnum)
-{
- int cnum = Files[fnum].cnum;
- invalidate_read_prediction(Files[fnum].fd);
- Files[fnum].open = False;
- Connections[cnum].num_files_open--;
- if(Files[fnum].wbmpx_ptr)
- {
- free((char *)Files[fnum].wbmpx_ptr);
- Files[fnum].wbmpx_ptr = NULL;
- }
-
-#if USE_MMAP
- if(Files[fnum].mmap_ptr)
- {
- munmap(Files[fnum].mmap_ptr,Files[fnum].mmap_size);
- Files[fnum].mmap_ptr = NULL;
- }
#endif
- if (lp_share_modes(SNUM(cnum)))
- del_share_mode(fnum);
-
- if (Files[fnum].modified) {
- struct stat st;
- if (fstat(Files[fnum].fd,&st) == 0) {
- int dosmode = dos_mode(cnum,Files[fnum].name,&st);
- if (!IS_DOS_ARCHIVE(dosmode)) {
- dos_chmod(cnum,Files[fnum].name,dosmode | aARCH,&st);
- }
- }
- }
-
- close(Files[fnum].fd);
-
- /* NT uses smbclose to start a print - weird */
- if (Files[fnum].print_file)
- print_file(fnum);
-
- /* check for magic scripts */
- check_magic(fnum,cnum);
-
- DEBUG(2,("%s %s closed file %s (numopen=%d)\n",
- timestring(),Connections[cnum].user,Files[fnum].name,
- Connections[cnum].num_files_open));
-}
-
-enum {AFAIL,AREAD,AWRITE,AALL};
-
-/*******************************************************************
-reproduce the share mode access table
-********************************************************************/
-static int access_table(int new_deny,int old_deny,int old_mode,
- int share_pid,char *fname)
-{
- if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL);
-
- if (new_deny == DENY_DOS || old_deny == DENY_DOS) {
- if (old_deny == new_deny && share_pid == getpid())
- return(AALL);
-
- if (old_mode == 0) return(AREAD);
-
- /* the new smbpub.zip spec says that if the file extension is
- .com, .dll, .exe or .sym then allow the open. I will force
- it to read-only as this seems sensible although the spec is
- a little unclear on this. */
- if ((fname = strrchr(fname,'.'))) {
- if (strequal(fname,".com") ||
- strequal(fname,".dll") ||
- strequal(fname,".exe") ||
- strequal(fname,".sym"))
- return(AREAD);
- }
-
- return(AFAIL);
- }
-
- switch (new_deny)
- {
- case DENY_WRITE:
- if (old_deny==DENY_WRITE && old_mode==0) return(AREAD);
- if (old_deny==DENY_READ && old_mode==0) return(AWRITE);
- if (old_deny==DENY_NONE && old_mode==0) return(AALL);
- return(AFAIL);
- case DENY_READ:
- if (old_deny==DENY_WRITE && old_mode==1) return(AREAD);
- if (old_deny==DENY_READ && old_mode==1) return(AWRITE);
- if (old_deny==DENY_NONE && old_mode==1) return(AALL);
- return(AFAIL);
- case DENY_NONE:
- if (old_deny==DENY_WRITE) return(AREAD);
- if (old_deny==DENY_READ) return(AWRITE);
- if (old_deny==DENY_NONE) return(AALL);
- return(AFAIL);
- }
- return(AFAIL);
-}
-
-/*******************************************************************
-check if the share mode on a file allows it to be deleted or unlinked
-return True if sharing doesn't prevent the operation
-********************************************************************/
-BOOL check_file_sharing(int cnum,char *fname)
-{
- int pid=0;
- int share_mode = get_share_mode_byname(cnum,fname,&pid);
-
- if (!pid || !share_mode) return(True);
+ /* Stop zombies */
+ CatchChild();
+
+
+ FD_ZERO(&listen_set);
+
+ if(lp_interfaces() && lp_bind_interfaces_only()) {
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+
+ if(num_interfaces > FD_SETSIZE) {
+ DEBUG(0,("open_sockets: Too many interfaces specified to bind to. Number was %d \
+max can be %d\n",
+ num_interfaces, FD_SETSIZE));
+ return False;
+ }
+
+ /* Now open a listen socket for each of the
+ interfaces. */
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if(ifip == NULL) {
+ DEBUG(0,("open_sockets: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+ s = fd_listenset[i] = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True);
+ if(s == -1)
+ return False;
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ if (listen(s, 5) == -1) {
+ DEBUG(0,("listen: %s\n",strerror(errno)));
+ close(s);
+ return False;
+ }
+ FD_SET(s,&listen_set);
+ }
+ } else {
+ /* Just bind to 0.0.0.0 - accept connections
+ from anywhere. */
+ num_interfaces = 1;
+
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, port, 0,
+ interpret_addr(lp_socket_address()),True);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ if (listen(s, 5) == -1) {
+ DEBUG(0,("open_sockets: listen: %s\n",
+ strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ fd_listenset[0] = s;
+ FD_SET(s,&listen_set);
+ }
+
+ /* Listen to messages */
+
+ message_register(MSG_SMB_SAM_SYNC, msg_sam_sync);
+ message_register(MSG_SMB_SAM_REPL, msg_sam_repl);
+ message_register(MSG_SHUTDOWN, msg_exit_server);
+
+ /* now accept incoming connections - forking a new process
+ for each incoming connection */
+ DEBUG(2,("waiting for a connection\n"));
+ while (1) {
+ fd_set lfds;
+ int num;
+
+ /* Free up temporary memory from the main smbd. */
+ lp_talloc_free();
+
+ /* Ensure we respond to PING and DEBUG messages from the main smbd. */
+ message_dispatch();
+
+ memcpy((char *)&lfds, (char *)&listen_set,
+ sizeof(listen_set));
+
+ num = sys_select(FD_SETSIZE,&lfds,NULL,NULL,NULL);
+
+ if (num == -1 && errno == EINTR) {
+ if (got_sig_term) {
+ exit_server("Caught TERM signal");
+ }
+
+ /* check for sighup processing */
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+
+ continue;
+ }
+
+ /* check if we need to reload services */
+ check_reload(time(NULL));
+
+ /* Find the sockets that are read-ready -
+ accept on these. */
+ for( ; num > 0; num--) {
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ s = -1;
+ for(i = 0; i < num_interfaces; i++) {
+ if(FD_ISSET(fd_listenset[i],&lfds)) {
+ s = fd_listenset[i];
+ /* Clear this so we don't look
+ at it again. */
+ FD_CLR(fd_listenset[i],&lfds);
+ break;
+ }
+ }
+
+ smbd_set_server_fd(accept(s,&addr,&in_addrlen));
+
+ if (smbd_server_fd() == -1 && errno == EINTR)
+ continue;
+
+ if (smbd_server_fd() == -1) {
+ DEBUG(0,("open_sockets: accept: %s\n",
+ strerror(errno)));
+ continue;
+ }
+
+ if (smbd_server_fd() != -1 && sys_fork()==0) {
+ /* Child code ... */
+
+ /* close the listening socket(s) */
+ for(i = 0; i < num_interfaces; i++)
+ close(fd_listenset[i]);
+
+ /* close our standard file
+ descriptors */
+ close_low_fds();
+ am_parent = 0;
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(),user_socket_options);
+
+ /* Reset global variables in util.c so
+ that client substitutions will be
+ done correctly in the process. */
+ reset_globals_after_fork();
+
+ /* tdb needs special fork handling */
+ tdb_reopen_all();
+
+ return True;
+ }
+ /* The parent doesn't need this socket */
+ close(smbd_server_fd());
+
+ /* Sun May 6 18:56:14 2001 ackley@cs.unm.edu:
+ Clear the closed fd info out of server_fd --
+ and more importantly, out of client_fd in
+ util_sock.c, to avoid a possible
+ getpeername failure if we reopen the logs
+ and use %I in the filename.
+ */
+
+ smbd_set_server_fd(-1);
+
+ /* Force parent to check log size after
+ * spawning child. Fix from
+ * klausr@ITAP.Physik.Uni-Stuttgart.De. The
+ * parent smbd will log to logserver.smb. It
+ * writes only two messages for each child
+ * started/finished. But each child writes,
+ * say, 50 messages also in logserver.smb,
+ * begining with the debug_count of the
+ * parent, before the child opens its own log
+ * file logserver.client. In a worst case
+ * scenario the size of logserver.smb would be
+ * checked after about 50*50=2500 messages
+ * (ca. 100kb).
+ * */
+ force_check_log_size();
- if (share_mode == DENY_DOS)
- return(pid == getpid());
+ } /* end for num */
+ } /* end while 1 */
- /* XXXX exactly what share mode combinations should be allowed for
- deleting/renaming? */
- return(False);
+/* NOTREACHED return True; */
}
/****************************************************************************
- C. Hoch 11/22/95
- Helper for open_file_shared.
- Truncate a file after checking locking; close file if locked.
- **************************************************************************/
-static void truncate_unless_locked(int fnum, int cnum)
-{
- if (Files[fnum].can_write){
- if (is_locked(fnum,cnum,0x3FFFFFFF,0)){
- close_file(fnum);
- errno = EACCES;
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERRlock;
- }
- else
- ftruncate(Files[fnum].fd,0);
- }
-}
+ Reload the services file.
+**************************************************************************/
-
-/****************************************************************************
-open a file with a share mode
-****************************************************************************/
-void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
- int mode,int *Access,int *action)
+BOOL reload_services(BOOL test)
{
- int flags=0;
- int flags2=0;
- int deny_mode = (share_mode>>4)&7;
- struct stat sbuf;
- BOOL file_existed = file_exist(fname,&sbuf);
- BOOL fcbopen = False;
- int share_pid=0;
-
- Files[fnum].open = False;
- Files[fnum].fd = -1;
-
- /* this is for OS/2 EAs - try and say we don't support them */
- if (strstr(fname,".+,;=[].")) {
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
- return;
- }
-
- if ((ofun & 0x3) == 0 && file_existed) {
- errno = EEXIST;
- return;
- }
-
- if (ofun & 0x10)
- flags2 |= O_CREAT;
- if ((ofun & 0x3) == 2)
- flags2 |= O_TRUNC;
-
- /* note that we ignore the append flag as
- append does not mean the same thing under dos and unix */
-
- switch (share_mode&0xF)
- {
- case 1:
- flags = O_WRONLY;
- break;
- case 0xF:
- fcbopen = True;
- flags = O_RDWR;
- break;
- case 2:
- flags = O_RDWR;
- break;
- default:
- flags = O_RDONLY;
- break;
- }
-
- if (flags != O_RDONLY && file_existed &&
- (!CAN_WRITE(cnum) || IS_DOS_READONLY(dos_mode(cnum,fname,&sbuf)))) {
- if (!fcbopen) {
- errno = EACCES;
- return;
- }
- flags = O_RDONLY;
- }
-
- if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
- DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname));
- errno = EINVAL;
- return;
- }
-
- if (deny_mode == DENY_FCB) deny_mode = DENY_DOS;
-
- if (lp_share_modes(SNUM(cnum))) {
- int old_share=0;
-
- if (file_existed)
- old_share = get_share_mode(cnum,&sbuf,&share_pid);
-
- if (share_pid) {
- /* someone else has a share lock on it, check to see
- if we can too */
- int old_open_mode = old_share&0xF;
- int old_deny_mode = (old_share>>4)&7;
-
- if (deny_mode > 4 || old_deny_mode > 4 || old_open_mode > 2) {
- DEBUG(2,("Invalid share mode (%d,%d,%d) on file %s\n",
- deny_mode,old_deny_mode,old_open_mode,fname));
- errno = EACCES;
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERRbadshare;
- return;
- }
-
- {
- int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
- share_pid,fname);
-
- if ((access_allowed == AFAIL) ||
- (access_allowed == AREAD && flags == O_WRONLY) ||
- (access_allowed == AWRITE && flags == O_RDONLY)) {
- DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s) = %d\n",
- deny_mode,old_deny_mode,old_open_mode,
- share_pid,fname,
- access_allowed));
- errno = EACCES;
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERRbadshare;
- return;
- }
-
- if (access_allowed == AREAD)
- flags = O_RDONLY;
+ BOOL ret;
- if (access_allowed == AWRITE)
- flags = O_WRONLY;
- }
- }
- }
-
- DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
- flags,flags2,mode));
-
- open_file(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode);
- if (!Files[fnum].open && flags==O_RDWR && errno!=ENOENT && fcbopen) {
- flags = O_RDONLY;
- open_file(fnum,cnum,fname,flags,mode);
- }
-
- if (Files[fnum].open) {
- int open_mode=0;
- switch (flags) {
- case O_RDONLY:
- open_mode = 0;
- break;
- case O_RDWR:
- open_mode = 2;
- break;
- case O_WRONLY:
- open_mode = 1;
- break;
- }
-
- Files[fnum].share_mode = (deny_mode<<4) | open_mode;
- Files[fnum].share_pending = True;
-
- if (Access) {
- (*Access) = open_mode;
- }
-
- if (action) {
- if (file_existed && !(flags2 & O_TRUNC)) *action = 1;
- if (!file_existed) *action = 2;
- if (file_existed && (flags2 & O_TRUNC)) *action = 3;
- }
-
- if (!share_pid)
- share_mode_pending = True;
-
- if ((flags2&O_TRUNC) && file_existed)
- truncate_unless_locked(fnum,cnum);
- }
-}
-
-
-
-/*******************************************************************
-check for files that we should now set our share modes on
-********************************************************************/
-static void check_share_modes(void)
-{
- int i;
- for (i=0;i<MAX_OPEN_FILES;i++)
- if(Files[i].open && Files[i].share_pending) {
- if (lp_share_modes(SNUM(Files[i].cnum))) {
- int pid=0;
- get_share_mode_by_fnum(Files[i].cnum,i,&pid);
- if (!pid) {
- set_share_mode(i,Files[i].share_mode);
- Files[i].share_pending = False;
- }
- } else {
- Files[i].share_pending = False;
- }
- }
-}
-
-
-/****************************************************************************
-seek a file. Try to avoid the seek if possible
-****************************************************************************/
-int seek_file(int fnum,int pos)
-{
- int offset = 0;
- if (Files[fnum].print_file && POSTSCRIPT(Files[fnum].cnum))
- offset = 3;
-
- Files[fnum].pos = lseek(Files[fnum].fd,pos+offset,SEEK_SET) - offset;
- return(Files[fnum].pos);
-}
-
-/****************************************************************************
-read from a file
-****************************************************************************/
-int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact)
-{
- int ret=0;
-
- if (!Files[fnum].can_write)
- {
- ret = read_predict(Files[fnum].fd,
- pos,
- data,
- NULL,
- maxcnt);
-
- data += ret;
- maxcnt -= ret;
- mincnt = MAX(mincnt-ret,0);
- pos += ret;
- }
-
-#if USE_MMAP
- if (Files[fnum].mmap_ptr)
- {
- int num = MIN(maxcnt,Files[fnum].mmap_size-pos);
- if (num > 0)
- {
- memcpy(data,Files[fnum].mmap_ptr+pos,num);
- data += num;
- pos += num;
- maxcnt -= num;
- mincnt = MAX(mincnt-num,0);
- ret += num;
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname, NULL) &&
+ !strcsequal(fname, dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE, fname);
+ test = False;
+ }
}
- }
-#endif
-
- if (maxcnt <= 0)
- return(ret);
-
- if (seek_file(fnum,pos) != pos)
- {
- DEBUG(3,("Failed to seek to %d\n",pos));
- return(ret);
- }
-
- if (maxcnt > 0)
- ret += read_with_timeout(Files[fnum].fd,
- data,
- mincnt,
- maxcnt,
- timeout,
- exact);
-
- return(ret);
-}
-
-
-/****************************************************************************
-write to a file
-****************************************************************************/
-int write_file(int fnum,char *data,int n)
-{
- if (!Files[fnum].can_write) {
- errno = EPERM;
- return(0);
- }
-
- Files[fnum].modified = True;
-
- return(write_data(Files[fnum].fd,data,n));
-}
-
-
-static int old_umask = 022;
-
-/****************************************************************************
-load parameters specific to a connection/service
-****************************************************************************/
-BOOL become_service(int cnum,BOOL do_chdir)
-{
- extern char magic_char;
- static int last_cnum = -1;
- int snum;
-
- if (!OPEN_CNUM(cnum))
- {
- last_cnum = -1;
- return(False);
- }
-
- Connections[cnum].lastused = smb_last_time;
-
- snum = SNUM(cnum);
-
- if (do_chdir &&
- ChDir(Connections[cnum].connectpath) != 0 &&
- ChDir(Connections[cnum].origpath) != 0)
- {
- DEBUG(0,("%s chdir (%s) failed cnum=%d\n",timestring(),
- Connections[cnum].connectpath,cnum));
- return(False);
- }
-
- if (cnum == last_cnum)
- return(True);
-
- last_cnum = cnum;
-
- case_default = lp_defaultcase(snum);
- case_preserve = lp_preservecase(snum);
- short_case_preserve = lp_shortpreservecase(snum);
- case_mangle = lp_casemangle(snum);
- case_sensitive = lp_casesensitive(snum);
- magic_char = lp_magicchar(snum);
- use_mangled_map = (*lp_mangled_map(snum) ? True:False);
- return(True);
-}
-
-/****************************************************************************
- become the specified uid
-****************************************************************************/
-static BOOL become_uid(int uid)
-{
- if (initial_uid != 0)
- return(True);
-
-#ifdef AIX
- {
- /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
- priv_t priv;
-
- priv.pv_priv[0] = 0;
- priv.pv_priv[1] = 0;
- if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
- &priv, sizeof(priv_t)) < 0 ||
- setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
- seteuid((uid_t)uid) < 0)
- DEBUG(1,("Can't set uid (AIX3)"));
- }
-#endif
+ reopen_logs();
-#ifdef USE_SETRES
- if (setresuid(-1,uid,-1) != 0)
-#else
- if ((seteuid(uid) != 0) &&
- (setuid(uid) != 0))
-#endif
- {
- DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
- uid,getuid(), geteuid()));
- if (uid > 32000)
- DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
- return(False);
- }
-
- if (((uid == -1) || (uid == 65535)) && geteuid() != uid)
- {
- DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
- return(False);
- }
-
- return(True);
-}
-
-
-/****************************************************************************
- become the specified gid
-****************************************************************************/
-static BOOL become_gid(int gid)
-{
- if (initial_uid != 0)
- return(True);
-
-#ifdef USE_SETRES
- if (setresgid(-1,gid,-1) != 0)
-#else
- if (setgid(gid) != 0)
-#endif
- {
- DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
- gid,getgid(),getegid()));
- if (gid > 32000)
- DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
- return(False);
- }
-
- return(True);
-}
+ if (test && !lp_file_list_changed())
+ return(True);
+ lp_killunused(conn_snum_used);
+
+ ret = lp_load(dyn_CONFIGFILE, False, False, True);
-/****************************************************************************
- become the specified uid and gid
-****************************************************************************/
-static BOOL become_id(int uid,int gid)
-{
- return(become_gid(gid) && become_uid(uid));
-}
+ load_printers();
-/****************************************************************************
-become the guest user
-****************************************************************************/
-static BOOL become_guest(void)
-{
- BOOL ret;
- static struct passwd *pass=NULL;
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
- if (initial_uid != 0)
- return(True);
+ reopen_logs();
- if (!pass)
- pass = Get_Pwnam(lp_guestaccount(-1),True);
- if (!pass) return(False);
+ load_interfaces();
- ret = become_id(pass->pw_uid,pass->pw_gid);
+ {
+ if (smbd_server_fd() != -1) {
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+ }
+ }
- if (!ret)
- DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
+ mangle_reset_cache();
+ reset_stat_cache();
- last_user.cnum = -2;
+ /* this forces service parameters to be flushed */
+ set_current_service(NULL,True);
- return(ret);
+ return(ret);
}
+#if DUMP_CORE
/*******************************************************************
-check if a username is OK
+prepare to dump a core file - carefully!
********************************************************************/
-static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
-{
- int i;
- for (i=0;i<Connections[cnum].uid_cache.entries;i++)
- if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True);
-
- if (!user_ok(vuser->name,snum)) return(False);
-
- i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE;
- Connections[cnum].uid_cache.list[i] = vuser->uid;
-
- if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE)
- Connections[cnum].uid_cache.entries++;
-
- return(True);
-}
-
-
-/****************************************************************************
- become the user of a connection number
-****************************************************************************/
-BOOL become_user(int cnum, int uid)
-{
- int new_umask;
- user_struct *vuser;
- int snum,gid;
- int ngroups;
- gid_t *groups;
-
- if (last_user.cnum == cnum && last_user.uid == uid) {
- DEBUG(4,("Skipping become_user - already user\n"));
- return(True);
- }
-
- unbecome_user();
-
- if (!OPEN_CNUM(cnum)) {
- DEBUG(2,("Connection %d not open\n",cnum));
- return(False);
- }
-
- snum = Connections[cnum].service;
-
- if (Connections[cnum].force_user ||
- lp_security() == SEC_SHARE ||
- !(vuser = get_valid_user_struct(uid)) ||
- !check_user_ok(cnum,vuser,snum)) {
- uid = Connections[cnum].uid;
- gid = Connections[cnum].gid;
- groups = Connections[cnum].groups;
- ngroups = Connections[cnum].ngroups;
- } else {
- if (!vuser) {
- DEBUG(2,("Invalid vuid used %d\n",uid));
- return(False);
- }
- uid = vuser->uid;
- if(!*lp_force_group(snum))
- gid = vuser->gid;
- else
- gid = Connections[cnum].gid;
- groups = vuser->user_groups;
- ngroups = vuser->user_ngroups;
- }
-
- if (initial_uid == 0)
- {
- if (!become_gid(gid)) return(False);
-
-#ifndef NO_SETGROUPS
- if (!IS_IPC(cnum)) {
- /* groups stuff added by ih/wreu */
- if (ngroups > 0)
- if (setgroups(ngroups,groups)<0)
- DEBUG(0,("setgroups call failed!\n"));
- }
-#endif
-
- if (!Connections[cnum].admin_user && !become_uid(uid))
- return(False);
- }
-
- new_umask = 0777 & ~CREATE_MODE(cnum);
- old_umask = umask(new_umask);
-
- last_user.cnum = cnum;
- last_user.uid = uid;
-
- DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d) new_umask=0%o\n",
- getuid(),geteuid(),getgid(),getegid(),new_umask));
-
- return(True);
-}
-
-/****************************************************************************
- unbecome the user of a connection number
-****************************************************************************/
-BOOL unbecome_user(void )
+static BOOL dump_core(void)
{
- if (last_user.cnum == -1)
- return(False);
-
- ChDir(OriginalDir);
-
- umask(old_umask);
-
- if (initial_uid == 0)
- {
-#ifdef USE_SETRES
- setresuid(-1,getuid(),-1);
- setresgid(-1,getgid(),-1);
-#else
- if (seteuid(initial_uid) != 0)
- setuid(initial_uid);
- setgid(initial_gid);
+ char *p;
+ pstring dname;
+
+ pstrcpy(dname,lp_logfile());
+ if ((p=strrchr_m(dname,'/'))) *p=0;
+ pstrcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",
+ (int)rlp.rlim_cur,(int)rlp.rlim_max));
+ }
#endif
- }
-#ifdef NO_EID
- if (initial_uid == 0)
- DEBUG(2,("Running with no EID\n"));
- initial_uid = getuid();
- initial_gid = getgid();
-#else
- if (geteuid() != initial_uid)
- {
- DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
- initial_uid = geteuid();
- }
- if (getegid() != initial_gid)
- {
- DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
- initial_gid = getegid();
- }
#endif
-
- if (ChDir(OriginalDir) != 0)
- DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
- timestring(),OriginalDir));
- DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
- getuid(),geteuid(),getgid(),getegid()));
- last_user.cnum = -1;
-
- return(True);
+ DEBUG(0,("Dumping core in %s\n", dname));
+ abort();
+ return(True);
}
+#endif
/****************************************************************************
- find a service entry
+update the current smbd process count
****************************************************************************/
-int find_service(char *service)
-{
- int iService;
-
- string_sub(service,"\\","/");
-
- iService = lp_servicenumber(service);
-
- /* now handle the special case of a home directory */
- if (iService < 0)
- {
- char *phome_dir = get_home_dir(service);
- DEBUG(3,("checking for home directory %s gave %s\n",service,
- phome_dir?phome_dir:"(NULL)"));
- if (phome_dir)
- {
- int iHomeService;
- if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0)
- {
- lp_add_home(service,iHomeService,phome_dir);
- iService = lp_servicenumber(service);
- }
- }
- }
-
- /* If we still don't have a service, attempt to add it as a printer. */
- if (iService < 0)
- {
- int iPrinterService;
-
- if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
- {
- char *pszTemp;
-
- DEBUG(3,("checking whether %s is a valid printer name...\n", service));
- pszTemp = PRINTCAP;
- if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp))
- {
- DEBUG(3,("%s is a valid printer name\n", service));
- DEBUG(3,("adding %s as a printer service\n", service));
- lp_add_printer(service,iPrinterService);
- iService = lp_servicenumber(service);
- if (iService < 0)
- DEBUG(0,("failed to add %s as a printer service!\n", service));
- }
- else
- DEBUG(3,("%s is not a valid printer name\n", service));
- }
- }
-
- /* just possibly it's a default service? */
- if (iService < 0)
- {
- char *defservice = lp_defaultservice();
- if (defservice && *defservice && !strequal(defservice,service)) {
- iService = find_service(defservice);
- if (iService >= 0) {
- string_sub(service,"_","/");
- iService = lp_add_service(service,iService);
- }
- }
- }
-
- if (iService >= 0)
- if (!VALID_SNUM(iService))
- {
- DEBUG(0,("Invalid snum %d for %s\n",iService,service));
- iService = -1;
- }
-
- if (iService < 0)
- DEBUG(3,("find_service() failed to find service %s\n", service));
-
- return (iService);
-}
-
-/****************************************************************************
- create an error packet from a cached error.
-****************************************************************************/
-int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line)
+static void decrement_smbd_process_count(void)
{
- write_bmpx_struct *wbmpx = Files[fnum].wbmpx_ptr;
+ int32 total_smbds;
- int32 eclass = wbmpx->wr_errclass;
- int32 err = wbmpx->wr_error;
-
- /* We can now delete the auxiliary struct */
- free((char *)wbmpx);
- Files[fnum].wbmpx_ptr = NULL;
- return error_packet(inbuf,outbuf,eclass,err,line);
-}
-
-
-struct
-{
- int unixerror;
- int smbclass;
- int smbcode;
-} unix_smb_errmap[] =
-{
- {EPERM,ERRDOS,ERRnoaccess},
- {EACCES,ERRDOS,ERRnoaccess},
- {ENOENT,ERRDOS,ERRbadfile},
- {EIO,ERRHRD,ERRgeneral},
- {EBADF,ERRSRV,ERRsrverror},
- {EINVAL,ERRSRV,ERRsrverror},
- {EEXIST,ERRDOS,ERRfilexists},
- {ENFILE,ERRDOS,ERRnofids},
- {EMFILE,ERRDOS,ERRnofids},
- {ENOSPC,ERRHRD,ERRdiskfull},
-#ifdef EDQUOT
- {EDQUOT,ERRHRD,ERRdiskfull},
-#endif
-#ifdef ENOTEMPTY
- {ENOTEMPTY,ERRDOS,ERRnoaccess},
-#endif
-#ifdef EXDEV
- {EXDEV,ERRDOS,ERRdiffdevice},
-#endif
- {EROFS,ERRHRD,ERRnowrite},
- {0,0,0}
-};
-
-
-/****************************************************************************
- create an error packet from errno
-****************************************************************************/
-int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line)
-{
- int eclass=def_class;
- int ecode=def_code;
- int i=0;
-
- if (unix_ERR_class != SUCCESS)
- {
- eclass = unix_ERR_class;
- ecode = unix_ERR_code;
- unix_ERR_class = SUCCESS;
- unix_ERR_code = 0;
- }
- else
- {
- while (unix_smb_errmap[i].smbclass != 0)
- {
- if (unix_smb_errmap[i].unixerror == errno)
- {
- eclass = unix_smb_errmap[i].smbclass;
- ecode = unix_smb_errmap[i].smbcode;
- break;
- }
- i++;
+ if (lp_max_smbd_processes()) {
+ total_smbds = 0;
+ tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, -1);
}
- }
-
- return(error_packet(inbuf,outbuf,eclass,ecode,line));
}
-
/****************************************************************************
- create an error packet. Normally called using the ERROR() macro
+ Exit the server.
****************************************************************************/
-int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line)
-{
- int outsize = set_message(outbuf,0,0,True);
- int cmd;
- cmd = CVAL(inbuf,smb_com);
-
- CVAL(outbuf,smb_rcls) = error_class;
- SSVAL(outbuf,smb_err,error_code);
-
- DEBUG(3,("%s error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n",
- timestring(),
- line,
- (int)CVAL(inbuf,smb_com),
- smb_fn_name(CVAL(inbuf,smb_com)),
- error_class,
- error_code));
-
- if (errno != 0)
- DEBUG(3,("error string = %s\n",strerror(errno)));
-
- return(outsize);
-}
-
-#ifndef SIGCLD_IGNORE
-/****************************************************************************
-this prevents zombie child processes
-****************************************************************************/
-static int sig_cld()
+void exit_server(char *reason)
{
- static int depth = 0;
- if (depth != 0)
- {
- DEBUG(0,("ERROR: Recursion in sig_cld? Perhaps you need `#define USE_WAITPID'?\n"));
- depth=0;
- return(0);
- }
- depth++;
-
- BlockSignals(True);
- DEBUG(5,("got SIGCLD\n"));
-
-#ifdef USE_WAITPID
- while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0);
-#endif
+ static int firsttime=1;
+ extern char *last_inbuf;
+ extern struct auth_context *negprot_global_auth_context;
- /* Stop zombies */
- /* Stevens, Adv. Unix Prog. says that on system V you must call
- wait before reinstalling the signal handler, because the kernel
- calls the handler from within the signal-call when there is a
- child that has exited. This would lead to an infinite recursion
- if done vice versa. */
-
-#ifndef DONT_REINSTALL_SIG
-#ifdef SIGCLD_IGNORE
- signal(SIGCLD, SIG_IGN);
-#else
- signal(SIGCLD, SIGNAL_CAST sig_cld);
-#endif
-#endif
+ if (!firsttime)
+ exit(0);
+ firsttime = 0;
-#ifndef USE_WAITPID
- while (wait3(WAIT3_CAST1 NULL, WNOHANG, WAIT3_CAST2 NULL) > 0);
-#endif
- depth--;
- BlockSignals(False);
- return 0;
-}
-#endif
+ change_to_root_user();
+ DEBUG(2,("Closing connections\n"));
-/****************************************************************************
- this is called when the client exits abruptly
- **************************************************************************/
-static int sig_pipe()
-{
- exit_server("Got sigpipe\n");
- return(0);
-}
-
-/****************************************************************************
- open the socket communication
-****************************************************************************/
-static BOOL open_sockets(BOOL is_daemon,int port)
-{
- extern int Client;
-
- if (is_daemon)
- {
- int s;
- struct sockaddr addr;
- int in_addrlen = sizeof(addr);
-
- /* Stop zombies */
-#ifdef SIGCLD_IGNORE
- signal(SIGCLD, SIG_IGN);
-#else
- signal(SIGCLD, SIGNAL_CAST sig_cld);
-#endif
+ if (negprot_global_auth_context) {
+ (negprot_global_auth_context->free)(&negprot_global_auth_context);
+ }
- /* open an incoming socket */
- s = open_socket_in(SOCK_STREAM, port, 0);
- if (s == -1)
- return(False);
+ conn_close_all();
- /* ready to listen */
- if (listen(s, 5) == -1)
- {
- DEBUG(0,("listen: %s",strerror(errno)));
- close(s);
- return False;
- }
-
- /* now accept incoming connections - forking a new process
- for each incoming connection */
- DEBUG(2,("waiting for a connection\n"));
- while (1)
- {
- Client = accept(s,&addr,&in_addrlen);
+ invalidate_all_vuids();
- if (Client == -1 && errno == EINTR)
- continue;
+ /* delete our entry in the connections database. */
+ yield_connection(NULL,"");
- if (Client == -1)
- {
- DEBUG(0,("accept: %s",strerror(errno)));
- return False;
- }
+ respond_to_all_remaining_local_messages();
+ decrement_smbd_process_count();
-#ifdef NO_FORK_DEBUG
-#ifndef NO_SIGNAL_TEST
- signal(SIGPIPE, SIGNAL_CAST sig_pipe);
- signal(SIGCLD, SIGNAL_CAST SIG_DFL);
-#endif
- return True;
-#else
- if (Client != -1 && fork()==0)
- {
-#ifndef NO_SIGNAL_TEST
- signal(SIGPIPE, SIGNAL_CAST sig_pipe);
- signal(SIGCLD, SIGNAL_CAST SIG_DFL);
-#endif
- /* close our standard file descriptors */
- close_low_fds();
-
- set_socket_options(Client,"SO_KEEPALIVE");
- set_socket_options(Client,user_socket_options);
-
- return True;
- }
- close(Client); /* The parent doesn't need this socket */
-#endif
+#ifdef WITH_DFS
+ if (dcelogin_atmost_once) {
+ dfs_unlogin();
}
- }
- else
- {
- /* We will abort gracefully when the client or remote system
- goes away */
-#ifndef NO_SIGNAL_TEST
- signal(SIGPIPE, SIGNAL_CAST sig_pipe);
#endif
- Client = dup(0);
- /* close our standard file descriptors */
- close_low_fds();
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUG(0,("Last message was %s\n",smb_fn_name(last_message)));
+ if (last_inbuf)
+ show_msg(last_inbuf);
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
- set_socket_options(Client,"SO_KEEPALIVE");
- set_socket_options(Client,user_socket_options);
- }
+ locking_end();
- return True;
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
}
-
/****************************************************************************
-check if a snum is in use
+ Initialise connect, service and file structs.
****************************************************************************/
-BOOL snum_used(int snum)
-{
- int i;
- for (i=0;i<MAX_CONNECTIONS;i++)
- if (OPEN_CNUM(i) && (SNUM(i) == snum))
- return(True);
- return(False);
-}
-/****************************************************************************
- reload the services file
- **************************************************************************/
-BOOL reload_services(BOOL test)
+static void init_structs(void )
{
- BOOL ret;
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
- if (lp_loaded())
- {
- pstring fname;
- strcpy(fname,lp_configfile());
- if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
- {
- strcpy(servicesf,fname);
- test = False;
+ if (!*global_myname) {
+ char *p;
+ pstrcpy( global_myname, myhostname() );
+ p = strchr_m(global_myname, '.' );
+ if (p)
+ *p = 0;
}
- }
- reopen_logs();
+ strupper(global_myname);
- if (test && !lp_file_list_changed())
- return(True);
+ conn_init();
- lp_killunused(snum_used);
+ file_init();
- ret = lp_load(servicesf,False);
+ /* for RPC pipes */
+ init_rpc_pipe_hnd();
- /* perhaps the config filename is now set */
- if (!test)
- reload_services(True);
+ init_dptrs();
- reopen_logs();
+ secrets_init();
- {
- extern int Client;
- if (Client != -1) {
- set_socket_options(Client,"SO_KEEPALIVE");
- set_socket_options(Client,user_socket_options);
- }
- }
-
- create_mangled_stack(lp_mangledstack());
-
- /* this forces service parameters to be flushed */
- become_service(-1,True);
-
- return(ret);
-}
-
-
-
-/****************************************************************************
-this prevents zombie child processes
-****************************************************************************/
-static int sig_hup()
-{
- BlockSignals(True);
- DEBUG(0,("Got SIGHUP\n"));
- reload_services(False);
-#ifndef DONT_REINSTALL_SIG
- signal(SIGHUP,SIGNAL_CAST sig_hup);
-#endif
- BlockSignals(False);
- return(0);
}
/****************************************************************************
-Setup the groups a user belongs to.
+ Usage on the program.
****************************************************************************/
-int setup_groups(char *user, int uid, int gid, int *p_ngroups,
- int **p_igroups, gid_t **p_groups)
-{
- if (-1 == initgroups(user,gid))
- {
- if (getuid() == 0)
- {
- DEBUG(0,("Unable to initgroups!\n"));
- if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000)
- DEBUG(0,("This is probably a problem with the account %s\n",user));
- }
- }
- else
- {
- int i,ngroups;
- int *igroups;
- gid_t grp = 0;
- ngroups = getgroups(0,&grp);
- if (ngroups <= 0)
- ngroups = 32;
- igroups = (int *)malloc(sizeof(int)*ngroups);
- for (i=0;i<ngroups;i++)
- igroups[i] = 0x42424242;
- ngroups = getgroups(ngroups,(gid_t *)igroups);
-
- if (igroups[0] == 0x42424242)
- ngroups = 0;
-
- *p_ngroups = ngroups;
-
- /* The following bit of code is very strange. It is due to the
- fact that some OSes use int* and some use gid_t* for
- getgroups, and some (like SunOS) use both, one in prototypes,
- and one in man pages and the actual code. Thus we detect it
- dynamically using some very ugly code */
- if (ngroups > 0)
- {
- /* does getgroups return ints or gid_t ?? */
- static BOOL groups_use_ints = True;
-
- if (groups_use_ints &&
- ngroups == 1 &&
- SVAL(igroups,2) == 0x4242)
- groups_use_ints = False;
-
- for (i=0;groups_use_ints && i<ngroups;i++)
- if (igroups[i] == 0x42424242)
- groups_use_ints = False;
-
- if (groups_use_ints)
- {
- *p_igroups = igroups;
- *p_groups = (gid_t *)igroups;
- }
- else
- {
- gid_t *groups = (gid_t *)igroups;
- igroups = (int *)malloc(sizeof(int)*ngroups);
- for (i=0;i<ngroups;i++)
- igroups[i] = groups[i];
- *p_igroups = igroups;
- *p_groups = (gid_t *)groups;
- }
- }
- DEBUG(3,("%s is in %d groups\n",user,ngroups));
- for (i=0;i<ngroups;i++)
- DEBUG(3,("%d ",igroups[i]));
- DEBUG(3,("\n"));
- }
- return 0;
-}
-/****************************************************************************
- make a connection to a service
-****************************************************************************/
-int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid)
+static void usage(char *pname)
{
- int cnum;
- int snum;
- struct passwd *pass = NULL;
- connection_struct *pcon;
- BOOL guest = False;
- BOOL force = False;
- static BOOL first_connection = True;
-
- strlower(service);
-
- snum = find_service(service);
- if (snum < 0)
- {
- if (strequal(service,"IPC$"))
- {
- DEBUG(3,("%s refusing IPC connection\n",timestring()));
- return(-3);
- }
-
- DEBUG(0,("%s couldn't find service %s\n",timestring(),service));
- return(-2);
- }
-
- if (strequal(service,HOMES_NAME))
- {
- if (*user && Get_Pwnam(user,True))
- return(make_connection(user,user,password,pwlen,dev,vuid));
- if (validated_username(vuid))
- {
- strcpy(user,validated_username(vuid));
- return(make_connection(user,user,password,pwlen,dev,vuid));
- }
- }
-
- if (!lp_snum_ok(snum) || !check_access(snum)) {
- return(-4);
- }
-
- /* you can only connect to the IPC$ service as an ipc device */
- if (strequal(service,"IPC$"))
- strcpy(dev,"IPC");
-
- if (*dev == '?' || !*dev)
- {
- if (lp_print_ok(snum))
- strcpy(dev,"LPT1:");
- else
- strcpy(dev,"A:");
- }
-
- /* if the request is as a printer and you can't print then refuse */
- strupper(dev);
- if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) {
- DEBUG(1,("Attempt to connect to non-printer as a printer\n"));
- return(-6);
- }
-
- /* lowercase the user name */
- strlower(user);
-
- /* add it as a possible user name */
- add_session_user(service);
-
- /* shall we let them in? */
- if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid))
- {
- DEBUG(2,("%s invalid username/password for %s\n",timestring(),service));
- return(-1);
- }
-
- cnum = find_free_connection(str_checksum(service) + str_checksum(user));
- if (cnum < 0)
- {
- DEBUG(0,("%s couldn't find free connection\n",timestring()));
- return(-1);
- }
-
- pcon = &Connections[cnum];
- bzero((char *)pcon,sizeof(*pcon));
-
- /* find out some info about the user */
- pass = Get_Pwnam(user,True);
-
- if (pass == NULL)
- {
- DEBUG(0,("%s couldn't find account %s\n",timestring(),user));
- return(-7);
- }
-
- pcon->read_only = lp_readonly(snum);
-
- {
- pstring list;
- StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1);
- string_sub(list,"%S",service);
-
- if (user_in_list(user,list))
- pcon->read_only = True;
-
- StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
- string_sub(list,"%S",service);
-
- if (user_in_list(user,list))
- pcon->read_only = False;
- }
-
- /* admin user check */
- if (user_in_list(user,lp_admin_users(snum)) &&
- !pcon->read_only)
- {
- pcon->admin_user = True;
- DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
- }
- else
- pcon->admin_user = False;
-
- pcon->force_user = force;
- pcon->uid = pass->pw_uid;
- pcon->gid = pass->pw_gid;
- pcon->num_files_open = 0;
- pcon->lastused = time(NULL);
- pcon->service = snum;
- pcon->used = True;
- pcon->printer = (strncmp(dev,"LPT",3) == 0);
- pcon->ipc = (strncmp(dev,"IPC",3) == 0);
- pcon->dirptr = NULL;
- string_set(&pcon->dirpath,"");
- string_set(&pcon->user,user);
-
-#if HAVE_GETGRNAM
- if (*lp_force_group(snum))
- {
- struct group *gptr = (struct group *)getgrnam(lp_force_group(snum));
- if (gptr)
- {
- pcon->gid = gptr->gr_gid;
- DEBUG(3,("Forced group %s\n",lp_force_group(snum)));
- }
- else
- DEBUG(1,("Couldn't find group %s\n",lp_force_group(snum)));
- }
-#endif
-
- if (*lp_force_user(snum))
- {
- struct passwd *pass2;
- fstring fuser;
- strcpy(fuser,lp_force_user(snum));
- pass2 = (struct passwd *)Get_Pwnam(fuser,True);
- if (pass2)
- {
- pcon->uid = pass2->pw_uid;
- string_set(&pcon->user,fuser);
- strcpy(user,fuser);
- pcon->force_user = True;
- DEBUG(3,("Forced user %s\n",fuser));
- }
- else
- DEBUG(1,("Couldn't find user %s\n",fuser));
- }
-
- {
- pstring s;
- strcpy(s,lp_pathname(snum));
- standard_sub(cnum,s);
- string_set(&pcon->connectpath,s);
- DEBUG(3,("Connect path is %s\n",s));
- }
-
- /* groups stuff added by ih */
- pcon->ngroups = 0;
- pcon->groups = NULL;
-
- if (!IS_IPC(cnum))
- {
- /* Find all the groups this uid is in and store them. Used by become_user() */
- setup_groups(pcon->user,pcon->uid,pcon->gid,&pcon->ngroups,&pcon->igroups,&pcon->groups);
-
- /* check number of connections */
- if (!claim_connection(cnum,
- lp_servicename(SNUM(cnum)),
- lp_max_connections(SNUM(cnum)),False))
- {
- DEBUG(1,("too many connections - rejected\n"));
- return(-8);
- }
-
- if (lp_status(SNUM(cnum)))
- claim_connection(cnum,"STATUS.",MAXSTATUS,first_connection);
-
- first_connection = False;
- } /* IS_IPC */
-
- pcon->open = True;
-
- /* execute any "root preexec = " line */
- if (*lp_rootpreexec(SNUM(cnum)))
- {
- pstring cmd;
- strcpy(cmd,lp_rootpreexec(SNUM(cnum)));
- standard_sub(cnum,cmd);
- DEBUG(5,("cmd=%s\n",cmd));
- smbrun(cmd,NULL);
- }
-
- if (!become_user(cnum,pcon->uid))
- {
- DEBUG(0,("Can't become connected user!\n"));
- pcon->open = False;
- if (!IS_IPC(cnum)) {
- yield_connection(cnum,
- lp_servicename(SNUM(cnum)),
- lp_max_connections(SNUM(cnum)));
- if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS);
- }
- return(-1);
- }
-
- if (ChDir(pcon->connectpath) != 0)
- {
- DEBUG(0,("Can't change directory to %s\n",pcon->connectpath));
- pcon->open = False;
- unbecome_user();
- if (!IS_IPC(cnum)) {
- yield_connection(cnum,
- lp_servicename(SNUM(cnum)),
- lp_max_connections(SNUM(cnum)));
- if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS);
- }
- return(-5);
- }
-
- string_set(&pcon->origpath,pcon->connectpath);
-
-#if SOFTLINK_OPTIMISATION
- /* resolve any soft links early */
- {
- pstring s;
- strcpy(s,pcon->connectpath);
- GetWd(s);
- string_set(&pcon->connectpath,s);
- ChDir(pcon->connectpath);
- }
-#endif
-
- num_connections_open++;
- add_session_user(user);
-
- /* execute any "preexec = " line */
- if (*lp_preexec(SNUM(cnum)))
- {
- pstring cmd;
- strcpy(cmd,lp_preexec(SNUM(cnum)));
- standard_sub(cnum,cmd);
- smbrun(cmd,NULL);
- }
-
- /* we've finished with the sensitive stuff */
- unbecome_user();
-
- {
- extern struct from_host Client_info;
- DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) connect to service %s as user %s (uid=%d,gid=%d) (pid %d)\n",
- timestring(),
- Client_info.name,Client_info.addr,
- lp_servicename(SNUM(cnum)),user,
- pcon->uid,
- pcon->gid,
- (int)getpid()));
- }
-
- return(cnum);
+ d_printf("Usage: %s [-DaioPh?Vb] [-d debuglevel] [-l log basename] [-p port]\n", pname);
+ d_printf(" [-O socket options] [-s services file]\n");
+ d_printf("\t-D Become a daemon (default)\n");
+ d_printf("\t-a Append to log file (default)\n");
+ d_printf("\t-i Run interactive (not a daemon)\n" );
+ d_printf("\t-o Overwrite log file, don't append\n");
+ d_printf("\t-h Print usage\n");
+ d_printf("\t-? Print usage\n");
+ d_printf("\t-V Print version\n");
+ d_printf("\t-b Print build options\n");
+ d_printf("\t-d debuglevel Set the debuglevel\n");
+ d_printf("\t-l log basename. Basename for log/debug files\n");
+ d_printf("\t-p port Listen on the specified port\n");
+ d_printf("\t-O socket options Socket options\n");
+ d_printf("\t-s services file. Filename of services file\n");
+ d_printf("\n");
}
-
/****************************************************************************
- find first available file slot
+ main program.
****************************************************************************/
-int find_free_file(void )
-{
- int i;
- for (i=1;i<MAX_OPEN_FILES;i++)
- if (!Files[i].open)
- return(i);
- DEBUG(1,("ERROR! Out of file structures - perhaps increase MAX_OPEN_FILES?\n"));
- return(-1);
-}
-/****************************************************************************
- find first available connection slot, starting from a random position.
-The randomisation stops problems with the server dieing and clients
-thinking the server is still available.
-****************************************************************************/
-static int find_free_connection(int hash )
+ int main(int argc,char *argv[])
{
- int i;
- BOOL used=False;
- hash = (hash % (MAX_CONNECTIONS-2))+1;
+ extern BOOL append_log;
+ extern BOOL AllowDebugChange;
+ extern char *optarg;
+ /* shall I run as a daemon */
+ BOOL is_daemon = False;
+ BOOL interactive = False;
+ BOOL specified_logfile = False;
+ int port = SMB_PORT;
+ int opt;
+ pstring logfile;
- again:
+#ifdef HAVE_SET_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
- for (i=hash+1;i!=hash;)
- {
- if (!Connections[i].open && Connections[i].used == used)
- {
- DEBUG(3,("found free connection number %d\n",i));
- return(i);
+ /* this is for people who can't start the program correctly */
+ while (argc > 1 && (*argv[1] != '-')) {
+ argv++;
+ argc--;
}
- i++;
- if (i == MAX_CONNECTIONS)
- i = 1;
- }
-
- if (!used)
- {
- used = !used;
- goto again;
- }
-
- DEBUG(1,("ERROR! Out of connection structures\n"));
- return(-1);
-}
+ while ( EOF != (opt = getopt(argc, argv, "O:l:s:d:Dp:h?bVaiof:")) )
+ switch (opt) {
+ case 'O':
+ pstrcpy(user_socket_options,optarg);
+ break;
-/****************************************************************************
-reply for the core protocol
-****************************************************************************/
-int reply_corep(char *outbuf)
-{
- int outsize = set_message(outbuf,1,0,True);
+ case 's':
+ pstrcpy(dyn_CONFIGFILE,optarg);
+ break;
- Protocol = PROTOCOL_CORE;
-
- return outsize;
-}
+ case 'l':
+ specified_logfile = True;
+ pstr_sprintf(logfile, "%s/log.smbd", optarg);
+ lp_set_logfile(logfile);
+ break;
+ case 'a':
+ append_log = True;
+ break;
-/****************************************************************************
-reply for the coreplus protocol
-****************************************************************************/
-int reply_coreplus(char *outbuf)
-{
- int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
- int outsize = set_message(outbuf,13,0,True);
- SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
- readbraw and writebraw (possibly) */
- CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
- SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */
+ case 'i':
+ interactive = True;
+ break;
- Protocol = PROTOCOL_COREPLUS;
+ case 'o':
+ append_log = False;
+ break;
- return outsize;
-}
+ case 'D':
+ is_daemon = True;
+ break;
+ case 'd':
+ if (*optarg == 'A')
+ DEBUGLEVEL = 10000;
+ else
+ DEBUGLEVEL = atoi(optarg);
+ AllowDebugChange = False;
+ break;
-/****************************************************************************
-reply for the lanman 1.0 protocol
-****************************************************************************/
-int reply_lanman1(char *outbuf)
-{
- int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
- int secword=0;
- BOOL doencrypt = SMBENCRYPT();
- time_t t = time(NULL);
-
- if (lp_security()>=SEC_USER) secword |= 1;
- if (doencrypt) secword |= 2;
-
- set_message(outbuf,13,doencrypt?8:0,True);
- SSVAL(outbuf,smb_vwv1,secword);
-#ifdef SMB_PASSWD
- /* Create a token value and add it to the outgoing packet. */
- if (doencrypt)
- generate_next_challenge(smb_buf(outbuf));
-#endif
+ case 'p':
+ port = atoi(optarg);
+ break;
- Protocol = PROTOCOL_LANMAN1;
+ case 'h':
+ case '?':
+ usage(argv[0]);
+ exit(0);
+ break;
+
+ case 'V':
+ d_printf("Version %s\n",VERSION);
+ exit(0);
+ break;
+ case 'b':
+ build_options(True); /* Display output to screen as well as debug */
+ exit(0);
+ break;
+ default:
+ DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
+ usage(argv[0]);
+ exit(1);
+ }
- if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
- DEBUG(3,("using password server validation\n"));
-#ifdef SMB_PASSWD
- if (doencrypt) set_challenge(smb_buf(outbuf));
+#ifdef HAVE_SETLUID
+ /* needed for SecureWare on SCO */
+ setluid(0);
#endif
- }
- CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
- SSVAL(outbuf,smb_vwv2,maxxmit);
- SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
- SSVAL(outbuf,smb_vwv4,1);
- SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
- readbraw writebraw (possibly) */
- SIVAL(outbuf,smb_vwv6,getpid());
- SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+ sec_init();
- put_dos_date(outbuf,smb_vwv8,t);
+ load_case_tables();
- return (smb_len(outbuf)+4);
-}
+ append_log = True;
+ if(!specified_logfile) {
+ pstr_sprintf(logfile, "%s/log.smbd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+ }
-/****************************************************************************
-reply for the lanman 2.0 protocol
-****************************************************************************/
-int reply_lanman2(char *outbuf)
-{
- int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
- int secword=0;
- BOOL doencrypt = SMBENCRYPT();
- time_t t = time(NULL);
-
- if (lp_security()>=SEC_USER) secword |= 1;
- if (doencrypt) secword |= 2;
-
- set_message(outbuf,13,doencrypt?8:0,True);
- SSVAL(outbuf,smb_vwv1,secword);
-#ifdef SMB_PASSWD
- /* Create a token value and add it to the outgoing packet. */
- if (doencrypt)
- generate_next_challenge(smb_buf(outbuf));
-#endif
+ fstrcpy(remote_machine, "smbd");
- SIVAL(outbuf,smb_vwv6,getpid());
+ setup_logging(argv[0],interactive);
- Protocol = PROTOCOL_LANMAN2;
+ /* we want to re-seed early to prevent time delays causing
+ client problems at a later date. (tridge) */
+ generate_random_buffer(NULL, 0, False);
- if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
- DEBUG(3,("using password server validation\n"));
-#ifdef SMB_PASSWD
- if (doencrypt) set_challenge(smb_buf(outbuf));
-#endif
- }
+ /* make absolutely sure we run as root - to handle cases where people
+ are crazy enough to have it setuid */
- CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
- SSVAL(outbuf,smb_vwv2,maxxmit);
- SSVAL(outbuf,smb_vwv3,lp_maxmux());
- SSVAL(outbuf,smb_vwv4,1);
- SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
- SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
- put_dos_date(outbuf,smb_vwv8,t);
+ gain_root_privilege();
+ gain_root_group_privilege();
- return (smb_len(outbuf)+4);
-}
+ fault_setup((void (*)(void *))exit_server);
+ CatchSignal(SIGTERM , SIGNAL_CAST sig_term);
+ CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
-/****************************************************************************
-reply for the nt protocol
-****************************************************************************/
-int reply_nt1(char *outbuf)
-{
- int capabilities=0x300; /* has dual names + lock_and_read */
- int secword=0;
- BOOL doencrypt = SMBENCRYPT();
-
- if (lp_security()>=SEC_USER) secword |= 1;
- if (doencrypt) secword |= 2;
-
- set_message(outbuf,17,doencrypt?8:0,True);
- CVAL(outbuf,smb_vwv1) = secword;
-#ifdef SMB_PASSWD
- /* Create a token value and add it to the outgoing packet. */
- if (doencrypt) {
- generate_next_challenge(smb_buf(outbuf));
- /* Tell the nt machine how long the challenge is. */
- SSVALS(outbuf,smb_vwv16+1,8);
- }
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
#endif
- SIVAL(outbuf,smb_vwv7+1,getpid()); /* session key */
-
- Protocol = PROTOCOL_NT1;
-
- if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
- DEBUG(3,("using password server validation\n"));
-#ifdef SMB_PASSWD
- if (doencrypt) set_challenge(smb_buf(outbuf));
+#if defined(SIGUSR2)
+ /* We are no longer interested in USR2 */
+ BlockSignals(True,SIGUSR2);
#endif
- }
-
- if (lp_readraw() && lp_writeraw())
- capabilities |= 1;
-
- SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
- SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
- SIVAL(outbuf,smb_vwv3+1,0xFFFF); /* max buffer */
- SIVAL(outbuf,smb_vwv5+1,0xFFFF); /* raw size */
- SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
- put_long_date(outbuf+smb_vwv11+1,time(NULL));
- SSVALS(outbuf,smb_vwv15+1,TimeDiff(time(NULL))/60);
-
- return (smb_len(outbuf)+4);
-}
-
-
-/* these are the protocol lists used for auto architecture detection:
-
-WinNT 3.51:
-protocol [PC NETWORK PROGRAM 1.0]
-protocol [XENIX CORE]
-protocol [MICROSOFT NETWORKS 1.03]
-protocol [LANMAN1.0]
-protocol [Windows for Workgroups 3.1a]
-protocol [LM1.2X002]
-protocol [LANMAN2.1]
-protocol [NT LM 0.12]
-
-Win95:
-protocol [PC NETWORK PROGRAM 1.0]
-protocol [XENIX CORE]
-protocol [MICROSOFT NETWORKS 1.03]
-protocol [LANMAN1.0]
-protocol [Windows for Workgroups 3.1a]
-protocol [LM1.2X002]
-protocol [LANMAN2.1]
-protocol [NT LM 0.12]
-
-OS/2:
-protocol [PC NETWORK PROGRAM 1.0]
-protocol [XENIX CORE]
-protocol [LANMAN1.0]
-protocol [LM1.2X002]
-protocol [LANMAN2.1]
-*/
-
-/*
- * Modified to recognize the architecture of the remote machine better.
- *
- * This appears to be the matrix of which protocol is used by which
- * MS product.
- Protocol WfWg Win95 WinNT OS/2
- PC NETWORK PROGRAM 1.0 1 1 1 1
- XENIX CORE 2 2
- MICROSOFT NETWORKS 3.0 2 2
- DOS LM1.2X002 3 3
- MICROSOFT NETWORKS 1.03 3
- DOS LANMAN2.1 4 4
- LANMAN1.0 4 3
- Windows for Workgroups 3.1a 5 5 5
- LM1.2X002 6 4
- LANMAN2.1 7 5
- NT LM 0.12 6 8
- *
- * tim@fsg.com 09/29/95
- */
-
-#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */
-#define ARCH_WIN95 0x2
-#define ARCH_OS2 0xC /* Again OS/2 is like NT */
-#define ARCH_WINNT 0x8
-#define ARCH_SAMBA 0x10
-
-#define ARCH_ALL 0x1F
-
-/* List of supported protocols, most desired first */
-struct {
- char *proto_name;
- char *short_name;
- int (*proto_reply_fn)(char *);
- int protocol_level;
-} supported_protocols[] = {
- {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
- {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
- {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
- {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
- {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
- {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
- {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
- {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
- {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
- {NULL,NULL},
-};
-
-
-/****************************************************************************
- reply to a negprot
-****************************************************************************/
-static int reply_negprot(char *inbuf,char *outbuf)
-{
- extern fstring remote_arch;
- int outsize = set_message(outbuf,1,0,True);
- int Index=0;
- int choice= -1;
- int protocol;
- char *p;
- int bcc = SVAL(smb_buf(inbuf),-2);
- int arch = ARCH_ALL;
-
- p = smb_buf(inbuf)+1;
- while (p < (smb_buf(inbuf) + bcc))
- {
- Index++;
- DEBUG(3,("Requested protocol [%s]\n",p));
- if (strcsequal(p,"Windows for Workgroups 3.1a"))
- arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT );
- else if (strcsequal(p,"DOS LM1.2X002"))
- arch &= ( ARCH_WFWG | ARCH_WIN95 );
- else if (strcsequal(p,"DOS LANMAN2.1"))
- arch &= ( ARCH_WFWG | ARCH_WIN95 );
- else if (strcsequal(p,"NT LM 0.12"))
- arch &= ( ARCH_WIN95 | ARCH_WINNT );
- else if (strcsequal(p,"LANMAN2.1"))
- arch &= ( ARCH_WINNT | ARCH_OS2 );
- else if (strcsequal(p,"LM1.2X002"))
- arch &= ( ARCH_WINNT | ARCH_OS2 );
- else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
- arch &= ARCH_WINNT;
- else if (strcsequal(p,"XENIX CORE"))
- arch &= ( ARCH_WINNT | ARCH_OS2 );
- else if (strcsequal(p,"Samba")) {
- arch = ARCH_SAMBA;
- break;
- }
-
- p += strlen(p) + 2;
- }
-
- switch ( arch ) {
- case ARCH_SAMBA:
- strcpy(remote_arch,"Samba");
- break;
- case ARCH_WFWG:
- strcpy(remote_arch,"WfWg");
- break;
- case ARCH_WIN95:
- strcpy(remote_arch,"Win95");
- break;
- case ARCH_WINNT:
- strcpy(remote_arch,"WinNT");
- break;
- case ARCH_OS2:
- strcpy(remote_arch,"OS2");
- break;
- default:
- strcpy(remote_arch,"UNKNOWN");
- break;
- }
-
- /* possibly reload - change of architecture */
- reload_services(True);
-
- /* a special case to stop password server loops */
- if (Index == 1 && strequal(remote_machine,myhostname) &&
- lp_security()==SEC_SERVER)
- exit_server("Password server loop!");
-
- /* Check for protocols, most desirable first */
- for (protocol = 0; supported_protocols[protocol].proto_name; protocol++)
- {
- p = smb_buf(inbuf)+1;
- Index = 0;
- if (lp_maxprotocol() >= supported_protocols[protocol].protocol_level)
- while (p < (smb_buf(inbuf) + bcc))
- {
- if (strequal(p,supported_protocols[protocol].proto_name))
- choice = Index;
- Index++;
- p += strlen(p) + 2;
- }
- if(choice != -1)
- break;
- }
-
- SSVAL(outbuf,smb_vwv0,choice);
- if(choice != -1) {
- extern fstring remote_proto;
- strcpy(remote_proto,supported_protocols[protocol].short_name);
- reload_services(True);
- outsize = supported_protocols[protocol].proto_reply_fn(outbuf);
- DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
- }
- else {
- DEBUG(0,("No protocol supported !\n"));
- }
- SSVAL(outbuf,smb_vwv0,choice);
-
- DEBUG(5,("%s negprot index=%d\n",timestring(),choice));
-
- return(outsize);
-}
-
-
-/****************************************************************************
- parse a connect packet
-****************************************************************************/
-void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev)
-{
- char *p = smb_buf(buf) + 1;
- char *p2;
-
- DEBUG(4,("parsing connect string %s\n",p));
-
- p2 = strrchr(p,'\\');
- if (p2 == NULL)
- strcpy(service,p);
- else
- strcpy(service,p2+1);
-
- p += strlen(p) + 2;
-
- strcpy(password,p);
- *pwlen = strlen(password);
-
- p += strlen(p) + 2;
-
- strcpy(dev,p);
-
- *user = 0;
- p = strchr(service,'%');
- if (p != NULL)
- {
- *p = 0;
- strcpy(user,p+1);
- }
-}
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't recieve them. */
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGTERM);
-/****************************************************************************
-close all open files for a connection
-****************************************************************************/
-static void close_open_files(int cnum)
-{
- int i;
- for (i=0;i<MAX_OPEN_FILES;i++)
- if( Files[i].cnum == cnum && Files[i].open) {
- close_file(i);
- }
-}
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+ init_sec_ctx();
+ reopen_logs();
-/****************************************************************************
-close a cnum
-****************************************************************************/
-void close_cnum(int cnum, int uid)
-{
- extern struct from_host Client_info;
-
- DirCacheFlush(SNUM(cnum));
-
- unbecome_user();
-
- if (!OPEN_CNUM(cnum))
- {
- DEBUG(0,("Can't close cnum %d\n",cnum));
- return;
- }
-
- DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) closed connection to service %s\n",
- timestring(),
- Client_info.name,Client_info.addr,
- lp_servicename(SNUM(cnum))));
-
- yield_connection(cnum,
- lp_servicename(SNUM(cnum)),
- lp_max_connections(SNUM(cnum)));
-
- if (lp_status(SNUM(cnum)))
- yield_connection(cnum,"STATUS.",MAXSTATUS);
-
- close_open_files(cnum);
- dptr_closecnum(cnum);
-
- /* execute any "postexec = " line */
- if (*lp_postexec(SNUM(cnum)) && become_user(cnum,uid))
- {
- pstring cmd;
- strcpy(cmd,lp_postexec(SNUM(cnum)));
- standard_sub(cnum,cmd);
- smbrun(cmd,NULL);
- unbecome_user();
- }
-
- unbecome_user();
- /* execute any "root postexec = " line */
- if (*lp_rootpostexec(SNUM(cnum)))
- {
- pstring cmd;
- strcpy(cmd,lp_rootpostexec(SNUM(cnum)));
- standard_sub(cnum,cmd);
- smbrun(cmd,NULL);
- }
-
- Connections[cnum].open = False;
- num_connections_open--;
- if (Connections[cnum].ngroups && Connections[cnum].groups)
- {
- if (Connections[cnum].igroups != (int *)Connections[cnum].groups)
- free(Connections[cnum].groups);
- free(Connections[cnum].igroups);
- Connections[cnum].groups = NULL;
- Connections[cnum].igroups = NULL;
- Connections[cnum].ngroups = 0;
- }
-
- string_set(&Connections[cnum].user,"");
- string_set(&Connections[cnum].dirpath,"");
- string_set(&Connections[cnum].connectpath,"");
-}
+ DEBUG(0,( "smbd version %s started.\n", VERSION));
+ DEBUGADD(0,( "Copyright Andrew Tridgell and the Samba Team 1992-2002\n"));
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
-/****************************************************************************
-simple routines to do connection counting
-****************************************************************************/
-BOOL yield_connection(int cnum,char *name,int max_connections)
-{
- struct connect_record crec;
- pstring fname;
- FILE *f;
- int mypid = getpid();
- int i;
-
- DEBUG(3,("Yielding connection to %d %s\n",cnum,name));
-
- if (max_connections <= 0)
- return(True);
-
- bzero(&crec,sizeof(crec));
-
- strcpy(fname,lp_lockdir());
- standard_sub(cnum,fname);
- trim_string(fname,"","/");
+ /* Output the build options to the debug log */
+ build_options(False);
- strcat(fname,"/");
- strcat(fname,name);
- strcat(fname,".LCK");
-
- f = fopen(fname,"r+");
- if (!f)
- {
- DEBUG(2,("Coudn't open lock file %s (%s)\n",fname,strerror(errno)));
- return(False);
- }
-
- fseek(f,0,SEEK_SET);
-
- /* find a free spot */
- for (i=0;i<max_connections;i++)
- {
- if (fread(&crec,sizeof(crec),1,f) != 1)
- {
- DEBUG(2,("Entry not found in lock file %s\n",fname));
- fclose(f);
- return(False);
+ if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ exit(1);
}
- if (crec.pid == mypid && crec.cnum == cnum)
- break;
- }
-
- if (crec.pid != mypid || crec.cnum != cnum)
- {
- fclose(f);
- DEBUG(2,("Entry not found in lock file %s\n",fname));
- return(False);
- }
-
- bzero((void *)&crec,sizeof(crec));
-
- /* remove our mark */
- if (fseek(f,i*sizeof(crec),SEEK_SET) != 0 ||
- fwrite(&crec,sizeof(crec),1,f) != 1)
- {
- DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno)));
- fclose(f);
- return(False);
- }
-
- DEBUG(3,("Yield successful\n"));
-
- fclose(f);
- return(True);
-}
+ /*
+ * Do this before reload_services.
+ */
-/****************************************************************************
-simple routines to do connection counting
-****************************************************************************/
-BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear)
-{
- struct connect_record crec;
- pstring fname;
- FILE *f;
- int snum = SNUM(cnum);
- int i,foundi= -1;
- int total_recs;
-
- if (max_connections <= 0)
- return(True);
-
- DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections));
-
- strcpy(fname,lp_lockdir());
- standard_sub(cnum,fname);
- trim_string(fname,"","/");
-
- if (!directory_exist(fname,NULL))
- mkdir(fname,0755);
-
- strcat(fname,"/");
- strcat(fname,name);
- strcat(fname,".LCK");
-
- if (!file_exist(fname,NULL))
- {
- f = fopen(fname,"w");
- if (f) fclose(f);
- }
-
- total_recs = file_size(fname) / sizeof(crec);
+ if (!reload_services(False))
+ return(-1);
- f = fopen(fname,"r+");
+ init_structs();
- if (!f)
- {
- DEBUG(1,("couldn't open lock file %s\n",fname));
- return(False);
- }
-
- /* find a free spot */
- for (i=0;i<max_connections;i++)
- {
-
- if (i>=total_recs ||
- fseek(f,i*sizeof(crec),SEEK_SET) != 0 ||
- fread(&crec,sizeof(crec),1,f) != 1)
- {
- if (foundi < 0) foundi = i;
- break;
- }
-
- if (Clear && crec.pid && !process_exists(crec.pid))
- {
- fseek(f,i*sizeof(crec),SEEK_SET);
- bzero((void *)&crec,sizeof(crec));
- fwrite(&crec,sizeof(crec),1,f);
- if (foundi < 0) foundi = i;
- continue;
+ /* don't call winbind for our domain if we are the DC */
+ if (lp_domain_logons()) {
+ winbind_exclude_domain(lp_workgroup());
}
- if (foundi < 0 && (!crec.pid || !process_exists(crec.pid)))
- {
- foundi=i;
- if (!Clear) break;
+
+#ifdef WITH_PROFILE
+ if (!profile_setup(False)) {
+ DEBUG(0,("ERROR: failed to setup profiling\n"));
+ return -1;
}
- }
-
- if (foundi < 0)
- {
- DEBUG(3,("no free locks in %s\n",fname));
- fclose(f);
- return(False);
- }
-
- /* fill in the crec */
- bzero((void *)&crec,sizeof(crec));
- crec.magic = 0x280267;
- crec.pid = getpid();
- crec.cnum = cnum;
- crec.uid = Connections[cnum].uid;
- crec.gid = Connections[cnum].gid;
- StrnCpy(crec.name,lp_servicename(snum),sizeof(crec.name)-1);
- crec.start = time(NULL);
-
- {
- extern struct from_host Client_info;
- StrnCpy(crec.machine,Client_info.name,sizeof(crec.machine)-1);
- StrnCpy(crec.addr,Client_info.addr,sizeof(crec.addr)-1);
- }
-
- /* make our mark */
- if (fseek(f,foundi*sizeof(crec),SEEK_SET) != 0 ||
- fwrite(&crec,sizeof(crec),1,f) != 1)
- {
- fclose(f);
- return(False);
- }
-
- fclose(f);
- return(True);
-}
-
-#if DUMP_CORE
-/*******************************************************************
-prepare to dump a core file - carefully!
-********************************************************************/
-static BOOL dump_core(void)
-{
- char *p;
- pstring dname;
- strcpy(dname,debugf);
- if ((p=strrchr(dname,'/'))) *p=0;
- strcat(dname,"/corefiles");
- mkdir(dname,0700);
- sys_chown(dname,getuid(),getgid());
- chmod(dname,0700);
- if (chdir(dname)) return(False);
- umask(~(0700));
-
-#ifndef NO_GETRLIMIT
-#ifdef RLIMIT_CORE
- {
- struct rlimit rlp;
- getrlimit(RLIMIT_CORE, &rlp);
- rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
- setrlimit(RLIMIT_CORE, &rlp);
- getrlimit(RLIMIT_CORE, &rlp);
- DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max));
- }
-#endif
-#endif
-
-
- DEBUG(0,("Dumping core in %s\n",dname));
- return(True);
-}
-#endif
-
-/****************************************************************************
-exit the server
-****************************************************************************/
-void exit_server(char *reason)
-{
- static int firsttime=1;
- int i;
-
- if (!firsttime) exit(0);
- firsttime = 0;
-
- unbecome_user();
- DEBUG(2,("Closing connections\n"));
- for (i=0;i<MAX_CONNECTIONS;i++)
- if (Connections[i].open)
- close_cnum(i,-1);
-#ifdef DFS_AUTH
- if (dcelogin_atmost_once)
- dfs_unlogin();
-#endif
- if (!reason) {
- int oldlevel = DEBUGLEVEL;
- DEBUGLEVEL = 10;
- DEBUG(0,("Last message was %s\n",smb_fn_name(last_message)));
- if (last_inbuf)
- show_msg(last_inbuf);
- DEBUGLEVEL = oldlevel;
- DEBUG(0,("===============================================================\n"));
-#if DUMP_CORE
- if (dump_core()) return;
-#endif
- }
- DEBUG(3,("%s Server exit (%s)\n",timestring(),reason?reason:""));
- exit(0);
-}
-
-/****************************************************************************
-do some standard substitutions in a string
-****************************************************************************/
-void standard_sub(int cnum,char *s)
-{
- if (!strchr(s,'%')) return;
-
- if (VALID_CNUM(cnum))
- {
- string_sub(s,"%S",lp_servicename(Connections[cnum].service));
- string_sub(s,"%P",Connections[cnum].connectpath);
- string_sub(s,"%u",Connections[cnum].user);
- if (strstr(s,"%H")) {
- char *home = get_home_dir(Connections[cnum].user);
- if (home) string_sub(s,"%H",home);
- }
- string_sub(s,"%g",gidtoname(Connections[cnum].gid));
- }
- standard_sub_basic(s);
-}
-
-/*
-These flags determine some of the permissions required to do an operation
-
-Note that I don't set NEED_WRITE on some write operations because they
-are used by some brain-dead clients when printing, and I don't want to
-force write permissions on print services.
-*/
-#define AS_USER (1<<0)
-#define NEED_WRITE (1<<1)
-#define TIME_INIT (1<<2)
-#define CAN_IPC (1<<3)
-#define AS_GUEST (1<<5)
-
-
-/*
- define a list of possible SMB messages and their corresponding
- functions. Any message that has a NULL function is unimplemented -
- please feel free to contribute implementations!
-*/
-struct smb_message_struct
-{
- int code;
- char *name;
- int (*fn)();
- int flags;
-#if PROFILING
- unsigned long time;
-#endif
-}
- smb_messages[] = {
-
- /* CORE PROTOCOL */
-
- {SMBnegprot,"SMBnegprot",reply_negprot,0},
- {SMBtcon,"SMBtcon",reply_tcon,0},
- {SMBtdis,"SMBtdis",reply_tdis,0},
- {SMBexit,"SMBexit",reply_exit,0},
- {SMBioctl,"SMBioctl",reply_ioctl,0},
- {SMBecho,"SMBecho",reply_echo,0},
- {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0},
- {SMBtconX,"SMBtconX",reply_tcon_and_X,0},
- {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 0},
- {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER},
- {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
- {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER},
- {SMBsearch,"SMBsearch",reply_search,AS_USER},
- {SMBopen,"SMBopen",reply_open,AS_USER},
-
- /* note that SMBmknew and SMBcreate are deliberately overloaded */
- {SMBcreate,"SMBcreate",reply_mknew,AS_USER},
- {SMBmknew,"SMBmknew",reply_mknew,AS_USER},
-
- {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE},
- {SMBread,"SMBread",reply_read,AS_USER},
- {SMBwrite,"SMBwrite",reply_write,AS_USER},
- {SMBclose,"SMBclose",reply_close,AS_USER},
- {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
- {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
- {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER},
- {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE},
-
- /* this is a Pathworks specific call, allowing the
- changing of the root path */
- {pSETDIR,"pSETDIR",reply_setdir,AS_USER},
-
- {SMBlseek,"SMBlseek",reply_lseek,AS_USER},
- {SMBflush,"SMBflush",reply_flush,AS_USER},
- {SMBctemp,"SMBctemp",reply_ctemp,AS_USER},
- {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER},
- {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER},
- {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER},
- {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER},
- {SMBlock,"SMBlock",reply_lock,AS_USER},
- {SMBunlock,"SMBunlock",reply_unlock,AS_USER},
-
- /* CORE+ PROTOCOL FOLLOWS */
-
- {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER},
- {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER},
- {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER},
- {SMBlockread,"SMBlockread",reply_lockread,AS_USER},
- {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER},
-
- /* LANMAN1.0 PROTOCOL FOLLOWS */
-
- {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER},
- {SMBreadBs,"SMBreadBs",NULL,AS_USER},
- {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER},
- {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER},
- {SMBwritec,"SMBwritec",NULL,AS_USER},
- {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE},
- {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER},
- {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC},
- {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC},
- {SMBioctls,"SMBioctls",NULL,AS_USER},
- {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE},
- {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE},
-
- {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER},
- {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER},
- {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER},
- {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
-
- {SMBffirst,"SMBffirst",reply_search,AS_USER},
- {SMBfunique,"SMBfunique",reply_search,AS_USER},
- {SMBfclose,"SMBfclose",reply_fclose,AS_USER},
-
- /* LANMAN2.0 PROTOCOL FOLLOWS */
- {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER},
- {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER},
- {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER},
- {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER},
-
- /* messaging routines */
- {SMBsends,"SMBsends",reply_sends,AS_GUEST},
- {SMBsendstrt,"SMBsendstrt",reply_sendstrt,AS_GUEST},
- {SMBsendend,"SMBsendend",reply_sendend,AS_GUEST},
- {SMBsendtxt,"SMBsendtxt",reply_sendtxt,AS_GUEST},
-
- /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */
-
- {SMBsendb,"SMBsendb",NULL,AS_GUEST},
- {SMBfwdname,"SMBfwdname",NULL,AS_GUEST},
- {SMBcancelf,"SMBcancelf",NULL,AS_GUEST},
- {SMBgetmac,"SMBgetmac",NULL,AS_GUEST}
- };
-
-/****************************************************************************
-return a string containing the function name of a SMB command
-****************************************************************************/
-char *smb_fn_name(int type)
-{
- static char *unknown_name = "SMBunknown";
- static int num_smb_messages =
- sizeof(smb_messages) / sizeof(struct smb_message_struct);
- int match;
-
- for (match=0;match<num_smb_messages;match++)
- if (smb_messages[match].code == type)
- break;
-
- if (match == num_smb_messages)
- return(unknown_name);
-
- return(smb_messages[match].name);
-}
-
-
-/****************************************************************************
-do a switch on the message type, and return the response size
-****************************************************************************/
-static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
-{
- static int pid= -1;
- int outsize = 0;
- static int num_smb_messages =
- sizeof(smb_messages) / sizeof(struct smb_message_struct);
- int match;
-
-#if PROFILING
- struct timeval msg_start_time;
- struct timeval msg_end_time;
- static unsigned long total_time = 0;
-
- GetTimeOfDay(&msg_start_time);
#endif
- if (pid == -1)
- pid = getpid();
-
- errno = 0;
- last_message = type;
-
- /* make sure this is an SMB packet */
- if (strncmp(smb_base(inbuf),"\377SMB",4) != 0)
- {
- DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf)));
- return(-1);
- }
-
- for (match=0;match<num_smb_messages;match++)
- if (smb_messages[match].code == type)
- break;
-
- if (match == num_smb_messages)
- {
- DEBUG(0,("Unknown message type %d!\n",type));
- outsize = reply_unknown(inbuf,outbuf);
- }
- else
- {
- DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid));
- if (smb_messages[match].fn)
+#ifdef WITH_SSL
{
- int cnum = SVAL(inbuf,smb_tid);
- int flags = smb_messages[match].flags;
- int uid = SVAL(inbuf,smb_uid);
-
- /* does this protocol need to be run as root? */
- if (!(flags & AS_USER))
- unbecome_user();
-
- /* does this protocol need to be run as the connected user? */
- if ((flags & AS_USER) && !become_user(cnum,uid))
- return(ERROR(ERRSRV,ERRinvnid));
-
- /* does it need write permission? */
- if ((flags & NEED_WRITE) && !CAN_WRITE(cnum))
- return(ERROR(ERRSRV,ERRaccess));
-
- /* ipc services are limited */
- if (IS_IPC(cnum) && (flags & AS_USER) && !(flags & CAN_IPC))
- return(ERROR(ERRSRV,ERRaccess));
-
- /* load service specific parameters */
- if (OPEN_CNUM(cnum) && !become_service(cnum,(flags & AS_USER)?True:False))
- return(ERROR(ERRSRV,ERRaccess));
-
- /* does this protocol need to be run as guest? */
- if ((flags & AS_GUEST) && (!become_guest() || !check_access(-1)))
- return(ERROR(ERRSRV,ERRaccess));
-
- last_inbuf = inbuf;
-
- outsize = smb_messages[match].fn(inbuf,outbuf,size,bufsize);
- }
- else
- {
- outsize = reply_unknown(inbuf,outbuf);
+ extern BOOL sslEnabled;
+ sslEnabled = lp_ssl_enabled();
+ if(sslEnabled)
+ sslutil_init(True);
}
- }
-
-#if PROFILING
- GetTimeOfDay(&msg_end_time);
- if (!(smb_messages[match].flags & TIME_INIT))
- {
- smb_messages[match].time = 0;
- smb_messages[match].flags |= TIME_INIT;
- }
- {
- unsigned long this_time =
- (msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 +
- (msg_end_time.tv_usec - msg_start_time.tv_usec);
- smb_messages[match].time += this_time;
- total_time += this_time;
- }
- DEBUG(2,("TIME %s %d usecs %g pct\n",
- smb_fn_name(type),smb_messages[match].time,
- (100.0*smb_messages[match].time) / total_time));
-#endif
-
- return(outsize);
-}
-
-
-/****************************************************************************
-construct a chained reply and add it to the already made reply
-
-inbuf points to the original message start.
-inbuf2 points to the smb_wct part of the secondary message
-type is the type of the secondary message
-outbuf points to the original outbuffer
-outbuf2 points to the smb_wct field of the new outbuffer
-size is the total length of the incoming message (from inbuf1)
-bufsize is the total buffer size
-
-return how many bytes were added to the response
-****************************************************************************/
-int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize)
-{
- int outsize = 0;
- char *ibuf,*obuf;
- static BOOL in_chain = False;
- static char *last_outbuf=NULL;
- BOOL was_inchain = in_chain;
- int insize_remaining;
- static int insize_deleted;
-
-
- chain_size += PTR_DIFF(outbuf2,outbuf) - smb_wct;
- if (was_inchain)
- outbuf = last_outbuf;
- else
- insize_deleted = 0;
-
-
- insize_deleted = 0;
- inbuf2 -= insize_deleted;
- insize_remaining = size - PTR_DIFF(inbuf2,inbuf);
- insize_deleted += size - (insize_remaining + smb_wct);
-
- in_chain = True;
- last_outbuf = outbuf;
+#endif /* WITH_SSL */
+ fstrcpy(global_myworkgroup, lp_workgroup());
- /* allocate some space for the in and out buffers of the chained message */
- ibuf = (char *)malloc(size + SAFETY_MARGIN);
- obuf = (char *)malloc(bufsize + SAFETY_MARGIN);
+ DEBUG(3,( "loaded services\n"));
- if (!ibuf || !obuf)
- {
- DEBUG(0,("Out of memory in chain reply\n"));
- return(ERROR(ERRSRV,ERRnoresource));
- }
+ if (!is_daemon && !is_a_socket(0)) {
+ if (!interactive)
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
- ibuf += SMB_ALIGNMENT;
- obuf += SMB_ALIGNMENT;
+ /*
+ * Setting is_daemon here prevents us from eventually calling
+ * the open_sockets_inetd()
+ */
- /* create the in buffer */
- memcpy(ibuf,inbuf,smb_wct);
- memcpy(ibuf+smb_wct,inbuf2,insize_remaining);
- CVAL(ibuf,smb_com) = type;
-
- /* create the out buffer */
- bzero(obuf,smb_size);
-
- set_message(obuf,0,0,True);
- CVAL(obuf,smb_com) = CVAL(ibuf,smb_com);
-
- memcpy(obuf+4,ibuf+4,4);
- CVAL(obuf,smb_rcls) = SUCCESS;
- CVAL(obuf,smb_reh) = 0;
- CVAL(obuf,smb_flg) = 0x80 | (CVAL(ibuf,smb_flg) & 0x8); /* bit 7 set
- means a reply */
- SSVAL(obuf,smb_flg2,1); /* say we support long filenames */
- SSVAL(obuf,smb_err,SUCCESS);
- SSVAL(obuf,smb_tid,SVAL(inbuf,smb_tid));
- SSVAL(obuf,smb_pid,SVAL(inbuf,smb_pid));
- SSVAL(obuf,smb_uid,SVAL(inbuf,smb_uid));
- SSVAL(obuf,smb_mid,SVAL(inbuf,smb_mid));
-
- DEBUG(3,("Chained message\n"));
- show_msg(ibuf);
-
- /* process the request */
- outsize = switch_message(type,ibuf,obuf,smb_wct+insize_remaining,
- bufsize-chain_size);
-
- /* copy the new reply header over the old one, but preserve
- the smb_com field */
- memcpy(outbuf+smb_com+1,obuf+smb_com+1,smb_wct-(smb_com+1));
-
- /* and copy the data from the reply to the right spot */
- memcpy(outbuf2,obuf+smb_wct,outsize - smb_wct);
-
- /* free the allocated buffers */
- if (ibuf) free(ibuf-SMB_ALIGNMENT);
- if (obuf) free(obuf-SMB_ALIGNMENT);
-
- in_chain = was_inchain;
-
- /* return how much extra has been added to the packet */
- return(outsize - smb_wct);
-}
-
-
-
-/****************************************************************************
- construct a reply to the incoming packet
-****************************************************************************/
-int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
-{
- int type = CVAL(inbuf,smb_com);
- int outsize = 0;
- int msg_type = CVAL(inbuf,0);
-
- smb_last_time = time(NULL);
-
- chain_size = 0;
-
- bzero(outbuf,smb_size);
-
- if (msg_type != 0)
- return(reply_special(inbuf,outbuf));
-
- CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
- set_message(outbuf,0,0,True);
-
- memcpy(outbuf+4,inbuf+4,4);
- CVAL(outbuf,smb_rcls) = SUCCESS;
- CVAL(outbuf,smb_reh) = 0;
- CVAL(outbuf,smb_flg) = 0x80 | (CVAL(inbuf,smb_flg) & 0x8); /* bit 7 set
- means a reply */
- SSVAL(outbuf,smb_flg2,1); /* say we support long filenames */
- SSVAL(outbuf,smb_err,SUCCESS);
- SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
- SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
- SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
- SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
-
- outsize = switch_message(type,inbuf,outbuf,size,bufsize);
-
- if(outsize > 4)
- smb_setlen(outbuf,outsize - 4);
- return(outsize);
-}
-
-
-/****************************************************************************
- process commands from the client
-****************************************************************************/
-void process(void )
-{
- static int trans_num = 0;
- int nread;
- extern struct from_host Client_info;
- extern int Client;
-
- fromhost(Client,&Client_info);
-
- InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
- OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
- if ((InBuffer == NULL) || (OutBuffer == NULL))
- return;
-
- InBuffer += SMB_ALIGNMENT;
- OutBuffer += SMB_ALIGNMENT;
-
-#if PRIME_NMBD
- DEBUG(3,("priming nmbd\n"));
- {
- struct in_addr ip;
- ip = *interpret_addr2("localhost");
- if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1");
- *OutBuffer = 0;
- send_one_packet(OutBuffer,1,ip,137,SOCK_DGRAM);
- }
-#endif
-
- last_user.cnum = -1;
-
- while (True)
- {
- int32 len;
- int msg_type;
- int msg_flags;
- int type;
- int deadtime = lp_deadtime()*60;
- int counter;
- int last_keepalive=0;
-
- if (deadtime <= 0)
- deadtime = DEFAULT_SMBD_TIMEOUT;
-
- if (lp_readprediction())
- do_read_prediction();
-
- {
- extern pstring share_del_pending;
- if (*share_del_pending) {
- unbecome_user();
- if (!unlink(share_del_pending))
- DEBUG(3,("Share file deleted %s\n",share_del_pending));
- else
- DEBUG(2,("Share del failed of %s\n",share_del_pending));
- share_del_pending[0] = 0;
+ is_daemon = True;
}
- }
-
- if (share_mode_pending) {
- unbecome_user();
- check_share_modes();
- share_mode_pending=False;
- }
-
- errno = 0;
- for (counter=SMBD_SELECT_LOOP;
- !receive_smb(Client,InBuffer,SMBD_SELECT_LOOP*1000);
- counter += SMBD_SELECT_LOOP)
- {
- int i;
- time_t t;
- BOOL allidle = True;
- extern int keepalive;
-
- /* check for socket failure */
- if (errno == EBADF) {
- DEBUG(3,("%s Bad file descriptor - exiting\n",timestring()));
- return;
- }
-
- t = time(NULL);
-
- /* become root again if waiting */
- unbecome_user();
-
- /* check for smb.conf reload */
- if (!(counter%SMBD_RELOAD_CHECK))
- reload_services(True);
-
- /* check the share modes every 10 secs */
- if (!(counter%SHARE_MODES_CHECK))
- check_share_modes();
-
- /* clean the share modes every 5 minutes */
- if (!(counter%SHARE_MODES_CLEAN))
- clean_share_files();
-
- /* automatic timeout if all connections are closed */
- if (num_connections_open==0 && counter >= IDLE_CLOSED_TIMEOUT) {
- DEBUG(2,("%s Closing idle connection\n",timestring()));
- return;
- }
-
- if (keepalive && (counter-last_keepalive)>keepalive) {
- if (!send_keepalive(Client)) {
- DEBUG(2,("%s Keepalive failed - exiting\n",timestring()));
- return;
- }
- last_keepalive = counter;
- }
-
- /* check for connection timeouts */
- for (i=0;i<MAX_CONNECTIONS;i++)
- if (Connections[i].open)
- {
- /* close dirptrs on connections that are idle */
- if ((t-Connections[i].lastused)>DPTR_IDLE_TIMEOUT)
- dptr_idlecnum(i);
-
- if (Connections[i].num_files_open > 0 ||
- (t-Connections[i].lastused)<deadtime)
- allidle = False;
- }
-
- if (allidle && num_connections_open>0) {
- DEBUG(2,("%s Closing idle connection 2\n",timestring()));
- return;
- }
+ if (is_daemon && !interactive) {
+ DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ become_daemon();
}
- msg_type = CVAL(InBuffer,0);
- msg_flags = CVAL(InBuffer,1);
- type = CVAL(InBuffer,smb_com);
-
- len = smb_len(InBuffer);
-
- DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
-
- nread = len + 4;
-
- DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
-
-#ifdef WITH_VTP
- if(trans_num == 1 && VT_Check(InBuffer)) {
- VT_Process();
- return;
- }
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
#endif
+ if (!directory_exist(lp_lockdir(), NULL)) {
+ mkdir(lp_lockdir(), 0755);
+ }
- if (msg_type == 0)
- show_msg(InBuffer);
-
- nread = construct_reply(InBuffer,OutBuffer,nread,maxxmit);
-
- if(nread > 0) {
- if (CVAL(OutBuffer,0) == 0)
- show_msg(OutBuffer);
-
- if (nread != smb_len(OutBuffer) + 4)
- {
- DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
- nread,
- smb_len(OutBuffer)));
- }
- else
- send_smb(Client,OutBuffer);
- }
- trans_num++;
- }
-}
-
-
-/****************************************************************************
- initialise connect, service and file structs
-****************************************************************************/
-static void init_structs(void )
-{
- int i;
- get_myname(myhostname,&myip);
-
- for (i=0;i<MAX_CONNECTIONS;i++)
- {
- Connections[i].open = False;
- Connections[i].num_files_open=0;
- Connections[i].lastused=0;
- Connections[i].used=False;
- string_init(&Connections[i].user,"");
- string_init(&Connections[i].dirpath,"");
- string_init(&Connections[i].connectpath,"");
- string_init(&Connections[i].origpath,"");
- }
-
- for (i=0;i<MAX_OPEN_FILES;i++)
- {
- Files[i].open = False;
- string_init(&Files[i].name,"");
- }
-
- init_dptrs();
-}
-
-/****************************************************************************
-usage on the program
-****************************************************************************/
-void usage(char *pname)
-{
- DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
-
- printf("Usage: %s [-D] [-p port] [-d debuglevel] [-l log basename] [-s services file]\n",pname);
- printf("Version %s\n",VERSION);
- printf("\t-D become a daemon\n");
- printf("\t-p port listen on the specified port\n");
- printf("\t-d debuglevel set the debuglevel\n");
- printf("\t-l log basename. Basename for log/debug files\n");
- printf("\t-s services file. Filename of services file\n");
- printf("\t-P passive only\n");
- printf("\t-a overwrite log file, don't append\n");
- printf("\n");
-}
-
-
-/****************************************************************************
- main program
-****************************************************************************/
-int main(int argc,char *argv[])
-{
- extern BOOL append_log;
- /* shall I run as a daemon */
- BOOL is_daemon = False;
- int port = 139;
- int opt;
- extern char *optarg;
-
-#ifdef NEED_AUTH_PARAMETERS
- set_auth_parameters(argc,argv);
-#endif
+ if (is_daemon) {
+ pidfile_create("smbd");
+ }
-#ifdef SecureWare
- setluid(0);
-#endif
+ if (!message_init()) {
+ exit(1);
+ }
+ register_msg_pool_usage();
+ register_dmalloc_msgs();
- append_log = True;
+ /* Setup the main smbd so that we can get messages. */
+ claim_connection(NULL,"",0,True);
- TimeInit();
+ /*
+ DO NOT ENABLE THIS TILL YOU COPE WITH KILLING THESE TASKS AND INETD
+ THIS *killed* LOTS OF BUILD FARM MACHINES. IT CREATED HUNDREDS OF
+ smbd PROCESSES THAT NEVER DIE
+ start_background_queue();
+ */
- strcpy(debugf,SMBLOGFILE);
+ if (!open_sockets(is_daemon,port))
+ exit(1);
- setup_logging(argv[0],False);
+ /*
+ * everything after this point is run after the fork()
+ */
- charset_initialise();
+ if (!locking_init(0))
+ exit(1);
- /* make absolutely sure we run as root - to handle cases whre people
- are crazy enough to have it setuid */
-#ifdef USE_SETRES
- setresuid(0,0,0);
-#else
- setuid(0);
- seteuid(0);
- setuid(0);
- seteuid(0);
-#endif
+ if (!print_backend_init())
+ exit(1);
- fault_setup(exit_server);
+ if (!share_info_db_init())
+ exit(1);
- umask(0777 & ~DEF_CREATE_MASK);
+ if(!initialize_password_db(False))
+ exit(1);
- initial_uid = geteuid();
- initial_gid = getegid();
+ uni_group_cache_init(); /* Non-critical */
+
+ /* possibly reload the services file. */
+ reload_services(True);
- if (initial_gid != 0 && initial_uid == 0)
- {
-#ifdef HPUX
- setresgid(0,0,0);
-#else
- setgid(0);
- setegid(0);
-#endif
- }
-
- initial_uid = geteuid();
- initial_gid = getegid();
-
-
- /* this is for people who can't start the program correctly */
- while (argc > 1 && (*argv[1] != '-'))
- {
- argv++;
- argc--;
- }
-
- while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPa")) != EOF)
- switch (opt)
- {
- case 'O':
- strcpy(user_socket_options,optarg);
- break;
- case 'i':
- strcpy(scope,optarg);
- break;
- case 'P':
- {
- extern BOOL passive;
- passive = True;
- }
- break;
- case 's':
- strcpy(servicesf,optarg);
- break;
- case 'l':
- strcpy(debugf,optarg);
- break;
- case 'a':
- {
- extern BOOL append_log;
- append_log = !append_log;
+ if(!pdb_generate_sam_sid()) {
+ DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n"));
+ exit(1);
}
- break;
- case 'D':
- is_daemon = True;
- break;
- case 'd':
- if (*optarg == 'A')
- DEBUGLEVEL = 10000;
- else
- DEBUGLEVEL = atoi(optarg);
- break;
- case 'p':
- port = atoi(optarg);
- break;
- case 'h':
- usage(argv[0]);
- exit(0);
- break;
- default:
- usage(argv[0]);
- exit(1);
- }
-
- reopen_logs();
-
- DEBUG(2,("%s smbd version %s started\n",timestring(),VERSION));
- DEBUG(2,("Copyright Andrew Tridgell 1992-1995\n"));
-
- GetWd(OriginalDir);
-
-#ifndef NO_GETRLIMIT
-#ifdef RLIMIT_NOFILE
- {
- struct rlimit rlp;
- getrlimit(RLIMIT_NOFILE, &rlp);
- rlp.rlim_cur = (MAX_OPEN_FILES>rlp.rlim_max)? rlp.rlim_max:MAX_OPEN_FILES;
- setrlimit(RLIMIT_NOFILE, &rlp);
- getrlimit(RLIMIT_NOFILE, &rlp);
- DEBUG(3,("Maximum number of open files per session is %d\n",rlp.rlim_cur));
- }
-#endif
-#endif
-
-
- DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
- getuid(),getgid(),geteuid(),getegid()));
-
- if (sizeof(uint16) < 2 || sizeof(uint32) < 4)
- {
- DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
- exit(1);
- }
-
- init_structs();
-
- if (!reload_services(False))
- return(-1);
-
-#ifndef NO_SIGNAL_TEST
- signal(SIGHUP,SIGNAL_CAST sig_hup);
-#endif
-
- DEBUG(3,("%s loaded services\n",timestring()));
- if (!is_daemon && !is_a_socket(0))
- {
- DEBUG(0,("standard input is not a socket, assuming -D option\n"));
- is_daemon = True;
- }
-
- if (is_daemon)
- {
- DEBUG(3,("%s becoming a daemon\n",timestring()));
- become_daemon();
- }
-
- if (open_sockets(is_daemon,port))
- {
- /* possibly reload the services file. */
- reload_services(True);
-
- maxxmit = MIN(lp_maxxmit(),BUFFER_SIZE);
-
- if (*lp_rootdir())
- {
- if (sys_chroot(lp_rootdir()) == 0)
- DEBUG(2,("%s changed root to %s\n",timestring(),lp_rootdir()));
+ if (!init_account_policy()) {
+ DEBUG(0,("Could not open account policy tdb.\n"));
+ exit(1);
}
- process();
- close_sockets();
- }
- exit_server("normal exit");
- return(0);
-}
+ if (*lp_rootdir()) {
+ if (sys_chroot(lp_rootdir()) == 0)
+ DEBUG(2,("Changed root to %s\n", lp_rootdir()));
+ }
+ /* Setup oplocks */
+ if (!init_oplocks())
+ exit(1);
+
+ /* Setup change notify */
+ if (!init_change_notify())
+ exit(1);
+ smbd_process();
+
+ uni_group_cache_shutdown();
+ exit_server("normal exit");
+ return(0);
+}
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
new file mode 100644
index 0000000000..0ae49b7adf
--- /dev/null
+++ b/source3/smbd/service.c
@@ -0,0 +1,738 @@
+/*
+ Unix SMB/CIFS implementation.
+ service (connection) opening and closing
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern struct timeval smb_last_time;
+extern int case_default;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern BOOL case_mangle;
+extern BOOL case_sensitive;
+extern BOOL use_mangled_map;
+extern fstring remote_machine;
+extern userdom_struct current_user_info;
+extern fstring remote_machine;
+
+
+/****************************************************************************
+ Load parameters specific to a connection/service.
+****************************************************************************/
+
+BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
+{
+ extern char magic_char;
+ static connection_struct *last_conn;
+ int snum;
+
+ if (!conn) {
+ last_conn = NULL;
+ return(False);
+ }
+
+ conn->lastused = smb_last_time.tv_sec;
+
+ snum = SNUM(conn);
+
+ if (do_chdir &&
+ vfs_ChDir(conn,conn->connectpath) != 0 &&
+ vfs_ChDir(conn,conn->origpath) != 0) {
+ DEBUG(0,("chdir (%s) failed\n",
+ conn->connectpath));
+ return(False);
+ }
+
+ if (conn == last_conn)
+ return(True);
+
+ last_conn = conn;
+
+ case_default = lp_defaultcase(snum);
+ case_preserve = lp_preservecase(snum);
+ short_case_preserve = lp_shortpreservecase(snum);
+ case_mangle = lp_casemangle(snum);
+ case_sensitive = lp_casesensitive(snum);
+ magic_char = lp_magicchar(snum);
+ use_mangled_map = (*lp_mangled_map(snum) ? True:False);
+ return(True);
+}
+
+/****************************************************************************
+ Add a home service. Returns the new service number or -1 if fail.
+****************************************************************************/
+
+int add_home_service(const char *service, const char *homedir)
+{
+ int iHomeService;
+ int iService;
+ fstring new_service;
+ fstring domain;
+
+ if (!service || !homedir)
+ return -1;
+
+ if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
+ return -1;
+
+ /*
+ * If this is a winbindd provided username, remove
+ * the domain component before adding the service.
+ * Log a warning if the "path=" parameter does not
+ * include any macros.
+ */
+
+ split_domain_and_name(service, domain, new_service);
+ lp_add_home(new_service, iHomeService, homedir);
+ iService = lp_servicenumber(new_service);
+
+ return iService;
+}
+
+
+/**
+ * Find a service entry. service is always in dos codepage.
+ *
+ * @param service is modified (to canonical form??)
+ **/
+int find_service(fstring service)
+{
+ int iService;
+
+ all_string_sub(service,"\\","/",0);
+
+ iService = lp_servicenumber(service);
+
+ /* now handle the special case of a home directory */
+ if (iService < 0)
+ {
+ char *phome_dir = get_user_service_home_dir(service);
+
+ if(!phome_dir)
+ {
+ /*
+ * Try mapping the servicename, it may
+ * be a Windows to unix mapped user name.
+ */
+ if(map_username(service))
+ phome_dir = get_user_service_home_dir(service);
+ }
+
+ DEBUG(3,("checking for home directory %s gave %s\n",service,
+ phome_dir?phome_dir:"(NULL)"));
+
+ iService = add_home_service(service,phome_dir);
+ }
+
+ /* If we still don't have a service, attempt to add it as a printer. */
+ if (iService < 0)
+ {
+ int iPrinterService;
+
+ if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
+ {
+ char *pszTemp;
+
+ DEBUG(3,("checking whether %s is a valid printer name...\n", service));
+ pszTemp = PRINTCAP;
+ if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp))
+ {
+ DEBUG(3,("%s is a valid printer name\n", service));
+ DEBUG(3,("adding %s as a printer service\n", service));
+ lp_add_printer(service, iPrinterService);
+ iService = lp_servicenumber(service);
+ if (iService < 0)
+ DEBUG(0,("failed to add %s as a printer service!\n", service));
+ }
+ else
+ DEBUG(3,("%s is not a valid printer name\n", service));
+ }
+ }
+
+ /* Check for default vfs service? Unsure whether to implement this */
+ if (iService < 0)
+ {
+ }
+
+ /* just possibly it's a default service? */
+ if (iService < 0)
+ {
+ char *pdefservice = lp_defaultservice();
+ if (pdefservice && *pdefservice &&
+ !strequal(pdefservice,service) &&
+ !strstr(service,".."))
+ {
+ /*
+ * We need to do a local copy here as lp_defaultservice()
+ * returns one of the rotating lp_string buffers that
+ * could get overwritten by the recursive find_service() call
+ * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
+ */
+ pstring defservice;
+ pstrcpy(defservice, pdefservice);
+ iService = find_service(defservice);
+ if (iService >= 0)
+ {
+ all_string_sub(service, "_","/",0);
+ iService = lp_add_service(service, iService);
+ }
+ }
+ }
+
+ if (iService >= 0)
+ if (!VALID_SNUM(iService))
+ {
+ DEBUG(0,("Invalid snum %d for %s\n",iService, service));
+ iService = -1;
+ }
+
+ if (iService < 0)
+ DEBUG(3,("find_service() failed to find service %s\n", service));
+
+ return (iService);
+}
+
+
+/****************************************************************************
+ do some basic sainity checks on the share.
+ This function modifies dev, ecode.
+****************************************************************************/
+static NTSTATUS share_sanity_checks(int snum, const char* service, pstring dev)
+{
+
+ if (!lp_snum_ok(snum) ||
+ !check_access(smbd_server_fd(),
+ lp_hostsallow(snum), lp_hostsdeny(snum))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* you can only connect to the IPC$ service as an ipc device */
+ if (strequal(service,"IPC$") || strequal(service,"ADMIN$"))
+ pstrcpy(dev,"IPC");
+
+ if (dev[0] == '?' || !dev[0]) {
+ if (lp_print_ok(snum)) {
+ pstrcpy(dev,"LPT1:");
+ } else {
+ pstrcpy(dev,"A:");
+ }
+ }
+
+ /* if the request is as a printer and you can't print then refuse */
+ strupper(dev);
+ if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) {
+ DEBUG(1,("Attempt to connect to non-printer as a printer\n"));
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+
+ /* Behave as a printer if we are supposed to */
+ if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
+ pstrcpy(dev, "LPT1:");
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ readonly share?
+****************************************************************************/
+static void set_read_only(connection_struct *conn)
+{
+ char **list;
+ char *service = lp_servicename(conn->service);
+ conn->read_only = lp_readonly(conn->service);
+
+ if (!service) return;
+
+ lp_list_copy(&list, lp_readlist(conn->service));
+ if (list) {
+ if (!lp_list_substitute(list, "%S", service)) {
+ DEBUG(0, ("ERROR: read list substitution failed\n"));
+ }
+ if (user_in_list(conn->user, list))
+ conn->read_only = True;
+ lp_list_free(&list);
+ }
+
+ lp_list_copy(&list, lp_writelist(conn->service));
+ if (list) {
+ if (!lp_list_substitute(list, "%S", service)) {
+ DEBUG(0, ("ERROR: write list substitution failed\n"));
+ }
+ if (user_in_list(conn->user, list))
+ conn->read_only = False;
+ lp_list_free(&list);
+ }
+}
+
+
+/****************************************************************************
+ admin user check
+****************************************************************************/
+static void set_admin_user(connection_struct *conn)
+{
+ /* admin user check */
+
+ /* JRA - original code denied admin user if the share was
+ marked read_only. Changed as I don't think this is needed,
+ but old code left in case there is a problem here.
+ */
+ if (user_in_list(conn->user,lp_admin_users(conn->service))
+#if 0
+ && !conn->read_only
+#endif
+ ) {
+ conn->admin_user = True;
+ DEBUG(0,("%s logged in as admin user (root privileges)\n",conn->user));
+ } else {
+ conn->admin_user = False;
+ }
+
+#if 0 /* This done later, for now */
+ /* admin users always run as uid=0 */
+ if (conn->admin_user) {
+ conn->uid = 0;
+ }
+#endif
+}
+
+/****************************************************************************
+ Make a connection to a service.
+ *
+ * @param service (May be modified to canonical form???)
+****************************************************************************/
+
+connection_struct *make_connection(char *service, DATA_BLOB password,
+ char *dev, uint16 vuid, NTSTATUS *status)
+{
+ int snum;
+ struct passwd *pass = NULL;
+ BOOL guest = False;
+ BOOL force = False;
+ connection_struct *conn;
+ uid_t euid;
+
+ fstring user;
+ ZERO_STRUCT(user);
+
+ /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
+ if (!non_root_mode() && (euid = geteuid()) != 0) {
+ DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
+ smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
+ }
+
+ strlower(service);
+
+ snum = find_service(service);
+
+ if (snum < 0) {
+ if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
+ DEBUG(3,("refusing IPC connection\n"));
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+
+ DEBUG(0,("%s (%s) couldn't find service %s\n",
+ remote_machine, client_addr(), service));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+ if (strequal(service,HOMES_NAME)) {
+ if(lp_security() != SEC_SHARE) {
+ if (validated_username(vuid)) {
+ fstring unix_username;
+ fstrcpy(unix_username,validated_username(vuid));
+ return make_connection(unix_username,
+ password,dev,vuid,status);
+ }
+ } else {
+ /* Security = share. Try with current_user_info.smb_name
+ * as the username. */
+ if (* current_user_info.smb_name) {
+ fstring unix_username;
+ fstrcpy(unix_username,
+ current_user_info.smb_name);
+ map_username(unix_username);
+ return make_connection(unix_username,
+ password,dev,vuid,status);
+ }
+ }
+ }
+
+ if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, service, dev))) {
+ return NULL;
+ }
+
+ /* add it as a possible user name if we
+ are in share mode security */
+ if (lp_security() == SEC_SHARE) {
+ add_session_user(service);
+ }
+
+
+ /* shall we let them in? */
+ if (!authorise_login(snum,user,password,&guest,&force,vuid)) {
+ DEBUG( 2, ( "Invalid username/password for %s [%s]\n", service, user ) );
+ *status = NT_STATUS_WRONG_PASSWORD;
+ return NULL;
+ }
+
+ add_session_user(user);
+
+ conn = conn_new();
+ if (!conn) {
+ DEBUG(0,("Couldn't find free connection.\n"));
+ *status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ return NULL;
+ }
+
+ /* find out some info about the user */
+ pass = smb_getpwnam(user,True);
+
+ if (pass == NULL) {
+ DEBUG(0,( "Couldn't find account %s\n",user));
+ *status = NT_STATUS_NO_SUCH_USER;
+ conn_free(conn);
+ return NULL;
+ }
+
+ conn->force_user = force;
+ conn->vuid = vuid;
+ conn->uid = pass->pw_uid;
+ conn->gid = pass->pw_gid;
+ safe_strcpy(conn->client_address, client_addr(),
+ sizeof(conn->client_address)-1);
+ conn->num_files_open = 0;
+ conn->lastused = time(NULL);
+ conn->service = snum;
+ conn->used = True;
+ conn->printer = (strncmp(dev,"LPT",3) == 0);
+ conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
+ conn->dirptr = NULL;
+ conn->veto_list = NULL;
+ conn->hide_list = NULL;
+ conn->veto_oplock_list = NULL;
+ string_set(&conn->dirpath,"");
+ string_set(&conn->user,user);
+ conn->nt_user_token = NULL;
+
+ set_read_only(conn);
+
+ set_admin_user(conn);
+
+ /*
+ * If force user is true, then store the
+ * given userid and also the primary groupid
+ * of the user we're forcing.
+ */
+
+ if (*lp_force_user(snum)) {
+ struct passwd *pass2;
+ pstring fuser;
+ pstrcpy(fuser,lp_force_user(snum));
+
+ /* Allow %S to be used by force user. */
+ pstring_sub(fuser,"%S",service);
+
+ pass2 = (struct passwd *)Get_Pwnam_Modify(fuser);
+ if (pass2) {
+ conn->uid = pass2->pw_uid;
+ conn->gid = pass2->pw_gid;
+ string_set(&conn->user,fuser);
+ fstrcpy(user,fuser);
+ conn->force_user = True;
+ DEBUG(3,("Forced user %s\n",fuser));
+ } else {
+ DEBUG(1,("Couldn't find user %s\n",fuser));
+ }
+ }
+
+ /* admin users always run as uid=0 */
+ if (conn->admin_user) {
+ conn->uid = 0;
+ }
+
+#ifdef HAVE_GETGRNAM
+ /*
+ * If force group is true, then override
+ * any groupid stored for the connecting user.
+ */
+
+ if (*lp_force_group(snum)) {
+ gid_t gid;
+ pstring gname;
+ pstring tmp_gname;
+ BOOL user_must_be_member = False;
+
+ StrnCpy(tmp_gname,lp_force_group(snum),sizeof(pstring)-1);
+
+ if (tmp_gname[0] == '+') {
+ user_must_be_member = True;
+ StrnCpy(gname,&tmp_gname[1],sizeof(pstring)-2);
+ } else {
+ StrnCpy(gname,tmp_gname,sizeof(pstring)-1);
+ }
+ /* default service may be a group name */
+ pstring_sub(gname,"%S",service);
+ gid = nametogid(gname);
+
+ if (gid != (gid_t)-1) {
+ /*
+ * If the user has been forced and the forced group starts
+ * with a '+', then we only set the group to be the forced
+ * group if the forced user is a member of that group.
+ * Otherwise, the meaning of the '+' would be ignored.
+ */
+ if (conn->force_user && user_must_be_member) {
+ if (user_in_group_list( user, gname )) {
+ conn->gid = gid;
+ DEBUG(3,("Forced group %s for member %s\n",gname,user));
+ }
+ } else {
+ conn->gid = gid;
+ DEBUG(3,("Forced group %s\n",gname));
+ }
+ } else {
+ DEBUG(1,("Couldn't find group %s\n",gname));
+ }
+ }
+#endif /* HAVE_GETGRNAM */
+
+ {
+ pstring s;
+ pstrcpy(s,lp_pathname(snum));
+ standard_sub_conn(conn,s);
+ string_set(&conn->connectpath,s);
+ DEBUG(3,("Connect path is %s\n",s));
+ }
+
+ /* groups stuff added by ih */
+ conn->ngroups = 0;
+ conn->groups = NULL;
+
+ /* Find all the groups this uid is in and
+ store them. Used by change_to_user() */
+ initialise_groups(conn->user, conn->uid, conn->gid);
+ get_current_groups(&conn->ngroups,&conn->groups);
+
+ conn->nt_user_token = create_nt_token(conn->uid, conn->gid,
+ conn->ngroups, conn->groups,
+ guest, NULL);
+
+ /*
+ * New code to check if there's a share security descripter
+ * added from NT server manager. This is done after the
+ * smb.conf checks are done as we need a uid and token. JRA.
+ */
+
+ {
+ BOOL can_write = share_access_check(conn, snum, vuid, FILE_WRITE_DATA);
+
+ if (!can_write) {
+ if (!share_access_check(conn, snum, vuid, FILE_READ_DATA)) {
+ /* No access, read or write. */
+ *status = NT_STATUS_ACCESS_DENIED;
+ DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
+ service ));
+ conn_free(conn);
+ return NULL;
+ } else {
+ conn->read_only = True;
+ }
+ }
+ }
+ /* Initialise VFS function pointers */
+
+ if (!smbd_vfs_init(conn)) {
+ DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
+ conn_free(conn);
+ return NULL;
+ }
+
+/* ROOT Activities: */
+ /* check number of connections */
+ if (!claim_connection(conn,
+ lp_servicename(SNUM(conn)),
+ lp_max_connections(SNUM(conn)),
+ False)) {
+ DEBUG(1,("too many connections - rejected\n"));
+ *status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ conn_free(conn);
+ return NULL;
+ }
+
+ /* Preexecs are done here as they might make the dir we are to ChDir to below */
+ /* execute any "root preexec = " line */
+ if (*lp_rootpreexec(SNUM(conn))) {
+ int ret;
+ pstring cmd;
+ pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd);
+ DEBUG(5,("cmd=%s\n",cmd));
+ ret = smbrun(cmd,NULL);
+ if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
+ DEBUG(1,("root preexec gave %d - failing connection\n", ret));
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_UNSUCCESSFUL;
+ return NULL;
+ }
+ }
+
+/* USER Activites: */
+ if (!change_to_user(conn, conn->vuid)) {
+ /* No point continuing if they fail the basic checks */
+ DEBUG(0,("Can't become connected user!\n"));
+ conn_free(conn);
+ *status = NT_STATUS_LOGON_FAILURE;
+ return NULL;
+ }
+
+ /* Remember that a different vuid can connect later without these checks... */
+
+ /* Preexecs are done here as they might make the dir we are to ChDir to below */
+ /* execute any "preexec = " line */
+ if (*lp_preexec(SNUM(conn))) {
+ int ret;
+ pstring cmd;
+ pstrcpy(cmd,lp_preexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd);
+ ret = smbrun(cmd,NULL);
+ if (ret != 0 && lp_preexec_close(SNUM(conn))) {
+ DEBUG(1,("preexec gave %d - failing connection\n", ret));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_UNSUCCESSFUL;
+ return NULL;
+ }
+ }
+
+ if (vfs_ChDir(conn,conn->connectpath) != 0) {
+ DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
+ remote_machine, conn->client_address,
+ conn->connectpath,strerror(errno)));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+ string_set(&conn->origpath,conn->connectpath);
+
+#if SOFTLINK_OPTIMISATION
+ /* resolve any soft links early */
+ {
+ pstring s;
+ pstrcpy(s,conn->connectpath);
+ vfs_GetWd(conn,s);
+ string_set(&conn->connectpath,s);
+ vfs_ChDir(conn,conn->connectpath);
+ }
+#endif
+
+ /*
+ * Print out the 'connected as' stuff here as we need
+ * to know the effective uid and gid we will be using
+ * (at least initially).
+ */
+
+ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
+ dbgtext( "%s (%s) ", remote_machine, conn->client_address );
+ dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
+ dbgtext( "initially as user %s ", user );
+ dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
+ dbgtext( "(pid %d)\n", (int)sys_getpid() );
+ }
+
+ /* Add veto/hide lists */
+ if (!IS_IPC(conn) && !IS_PRINT(conn)) {
+ set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
+ set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
+ set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
+ }
+
+ /* Invoke VFS make connection hook */
+
+ if (conn->vfs_ops.connect) {
+ if (conn->vfs_ops.connect(conn, service, user) < 0) {
+ DEBUG(0,("make_connection: VFS make connection failed!\n"));
+ *status = NT_STATUS_UNSUCCESSFUL;
+ change_to_root_user();
+ conn_free(conn);
+ return NULL;
+ }
+ }
+
+ /* we've finished with the user stuff - go back to root */
+ change_to_root_user();
+
+ return(conn);
+}
+
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(connection_struct *conn, uint16 vuid)
+{
+ DirCacheFlush(SNUM(conn));
+
+ change_to_root_user();
+
+ DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
+ remote_machine,conn->client_address,
+ lp_servicename(SNUM(conn))));
+
+ if (conn->vfs_ops.disconnect != NULL) {
+
+ /* Call VFS disconnect hook */
+
+ conn->vfs_ops.disconnect(conn);
+
+ }
+
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+
+ file_close_conn(conn);
+ dptr_closecnum(conn);
+
+ /* execute any "postexec = " line */
+ if (*lp_postexec(SNUM(conn)) &&
+ change_to_user(conn, vuid)) {
+ pstring cmd;
+ pstrcpy(cmd,lp_postexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd);
+ smbrun(cmd,NULL);
+ change_to_root_user();
+ }
+
+ change_to_root_user();
+ /* execute any "root postexec = " line */
+ if (*lp_rootpostexec(SNUM(conn))) {
+ pstring cmd;
+ pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd);
+ smbrun(cmd,NULL);
+ }
+ conn_free(conn);
+}
diff --git a/source3/smbd/session.c b/source3/smbd/session.c
new file mode 100644
index 0000000000..05a7b24da2
--- /dev/null
+++ b/source3/smbd/session.c
@@ -0,0 +1,178 @@
+/*
+ Unix SMB/CIFS implementation.
+ session handling for utmp and PAM
+ Copyright (C) tridge@samba.org 2001
+ Copyright (C) abartlet@pcug.org.au 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+ and is yielded when the corresponding vuid is destroyed.
+
+ sessions are used to populate utmp and PAM session structures
+*/
+
+#include "includes.h"
+
+extern fstring remote_machine;
+
+static TDB_CONTEXT *tdb;
+/* called when a session is created */
+BOOL session_claim(user_struct *vuser)
+{
+ int i;
+ TDB_DATA data;
+ struct sessionid sessionid;
+ uint32 pid = (uint32)sys_getpid();
+ TDB_DATA key;
+ fstring keystr;
+ char * hostname;
+
+ vuser->session_id = 0;
+
+ /* don't register sessions for the guest user - its just too
+ expensive to go through pam session code for browsing etc */
+ if (vuser->guest) {
+ return True;
+ }
+
+ if (!tdb) {
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
+ if (!tdb) {
+ DEBUG(1,("session_claim: failed to open sessionid tdb\n"));
+ return False;
+ }
+ }
+
+ ZERO_STRUCT(sessionid);
+
+ data.dptr = NULL;
+ data.dsize = 0;
+
+ for (i=1;i<MAX_SESSION_ID;i++) {
+ slprintf(keystr, sizeof(keystr)-1, "ID/%d", i);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+
+ if (tdb_store(tdb, key, data, TDB_INSERT) == 0) break;
+ }
+
+ if (i == MAX_SESSION_ID) {
+ DEBUG(1,("session_claim: out of session IDs (max is %d)\n",
+ MAX_SESSION_ID));
+ return False;
+ }
+
+ /* If 'hostname lookup' == yes, then do the DNS lookup. This is
+ needed becouse utmp and PAM both expect DNS names
+
+ client_name() handles this case internally.
+ */
+
+ hostname = client_name();
+ if (strcmp(hostname, "UNKNOWN") == 0) {
+ hostname = client_addr();
+ }
+
+ fstrcpy(sessionid.username, vuser->user.unix_name);
+ fstrcpy(sessionid.hostname, hostname);
+ slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1, SESSION_TEMPLATE, i);
+ sessionid.id_num = i;
+ sessionid.pid = pid;
+ sessionid.uid = vuser->uid;
+ sessionid.gid = vuser->gid;
+ fstrcpy(sessionid.remote_machine, remote_machine);
+ fstrcpy(sessionid.ip_addr, client_addr());
+
+ if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, sessionid.hostname)) {
+ DEBUG(1,("pam_session rejected the session for %s [%s]\n",
+ sessionid.username, sessionid.id_str));
+ tdb_delete(tdb, key);
+ return False;
+ }
+
+ data.dptr = (char *)&sessionid;
+ data.dsize = sizeof(sessionid);
+ if (tdb_store(tdb, key, data, TDB_MODIFY) != 0) {
+ DEBUG(1,("session_claim: unable to create session id record\n"));
+ return False;
+ }
+
+#if WITH_UTMP
+ if (lp_utmp()) {
+ sys_utmp_claim(sessionid.username, sessionid.hostname,
+ sessionid.id_str, sessionid.id_num);
+ }
+#endif
+
+ vuser->session_id = i;
+ return True;
+}
+
+/* called when a session is destroyed */
+void session_yield(user_struct *vuser)
+{
+ TDB_DATA dbuf;
+ struct sessionid sessionid;
+ TDB_DATA key;
+ fstring keystr;
+
+ if (!tdb) return;
+
+ if (vuser->session_id == 0) {
+ return;
+ }
+
+ slprintf(keystr, sizeof(keystr)-1, "ID/%d", vuser->session_id);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ SAFE_FREE(dbuf.dptr);
+
+#if WITH_UTMP
+ if (lp_utmp()) {
+ sys_utmp_yield(sessionid.username, sessionid.hostname,
+ sessionid.id_str, sessionid.id_num);
+ }
+#endif
+
+ smb_pam_close_session(sessionid.username, sessionid.id_str, sessionid.hostname);
+
+ tdb_delete(tdb, key);
+}
+
+BOOL session_traverse(int (*fn)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *), void *state)
+{
+ if (!tdb) {
+ DEBUG(3, ("No tdb opened\n"));
+ return False;
+ }
+
+ tdb_traverse(tdb, fn, state);
+ return True;
+}
+
+
+
diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c
new file mode 100644
index 0000000000..899c9174b2
--- /dev/null
+++ b/source3/smbd/sesssetup.c
@@ -0,0 +1,808 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle SMBsessionsetup
+ Copyright (C) Andrew Tridgell 1998-2001
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+uint32 global_client_caps = 0;
+static struct auth_context *ntlmssp_auth_context = NULL;
+
+/*
+ on a logon error possibly map the error to success if "map to guest"
+ is set approriately
+*/
+static NTSTATUS do_map_to_guest(NTSTATUS status, auth_serversupplied_info **server_info,
+ const char *user, const char *domain)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) ||
+ (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) {
+ DEBUG(3,("No such user %s [%s] - using guest account\n",
+ user, domain));
+ make_server_info_guest(server_info);
+ status = NT_STATUS_OK;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) {
+ DEBUG(3,("Registered username %s for guest access\n",user));
+ make_server_info_guest(server_info);
+ status = NT_STATUS_OK;
+ }
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+ Add the standard 'Samba' signature to the end of the session setup.
+****************************************************************************/
+static void add_signature(char *outbuf)
+{
+ char *p;
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, "Samba", -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE);
+ set_message_end(outbuf,p);
+}
+
+/****************************************************************************
+ Do a 'guest' logon, getting back the
+****************************************************************************/
+static NTSTATUS check_guest_password(auth_serversupplied_info **server_info)
+{
+ struct auth_context *auth_context;
+ auth_usersupplied_info *user_info = NULL;
+
+ NTSTATUS nt_status;
+ unsigned char chal[8];
+
+ ZERO_STRUCT(chal);
+
+ DEBUG(3,("Got anonymous request\n"));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_fixed(&auth_context, chal))) {
+ return nt_status;
+ }
+
+ if (!make_user_info_guest(&user_info)) {
+ (auth_context->free)(&auth_context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = auth_context->check_ntlm_password(auth_context, user_info, server_info);
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+
+#ifdef HAVE_KRB5
+/****************************************************************************
+reply to a session setup spnego negotiate packet for kerberos
+****************************************************************************/
+static int reply_spnego_kerberos(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB *secblob)
+{
+ DATA_BLOB ticket;
+ char *client, *p;
+ const struct passwd *pw;
+ char *user;
+ int sess_vuid;
+ NTSTATUS ret;
+ DATA_BLOB auth_data;
+ auth_serversupplied_info *server_info = NULL;
+ ADS_STRUCT *ads;
+
+ if (!spnego_parse_krb5_wrap(*secblob, &ticket)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ ads = ads_init(NULL, NULL, NULL, NULL);
+
+ ret = ads_verify_ticket(ads, &ticket, &client, &auth_data);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("Failed to verify incoming ticket!\n"));
+ ads_destroy(&ads);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ DEBUG(3,("Ticket name is [%s]\n", client));
+
+ p = strchr_m(client, '@');
+ if (!p) {
+ DEBUG(3,("Doesn't look like a valid principal\n"));
+ ads_destroy(&ads);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ *p = 0;
+ if (strcasecmp(p+1, ads->realm) != 0) {
+ DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
+ if (!lp_allow_trusted_domains()) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+ /* this gives a fully qualified user name (ie. with full realm).
+ that leads to very long usernames, but what else can we do? */
+ asprintf(&user, "%s%s%s", p+1, lp_winbind_separator(), client);
+ } else {
+ user = strdup(client);
+ }
+ ads_destroy(&ads);
+
+ /* the password is good - let them in */
+ pw = smb_getpwnam(user,False);
+ if (!pw && !strstr(user, lp_winbind_separator())) {
+ char *user2;
+ /* try it with a winbind domain prefix */
+ asprintf(&user2, "%s%s%s", lp_workgroup(), lp_winbind_separator(), user);
+ pw = smb_getpwnam(user2,False);
+ if (pw) {
+ free(user);
+ user = user2;
+ }
+ }
+
+ if (!pw) {
+ DEBUG(1,("Username %s is invalid on this system\n",user));
+ return ERROR_NT(NT_STATUS_NO_SUCH_USER);
+ }
+
+ if (!make_server_info_pw(&server_info,pw)) {
+ DEBUG(1,("make_server_info_from_pw failed!\n"));
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ sess_vuid = register_vuid(server_info, user);
+
+ free(user);
+ free_server_info(&server_info);
+
+ if (sess_vuid == -1) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+ add_signature(outbuf);
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+ SSVAL(inbuf,smb_uid,sess_vuid);
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+#endif
+
+
+/****************************************************************************
+send a security blob via a session setup reply
+****************************************************************************/
+static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf,
+ DATA_BLOB blob)
+{
+ char *p;
+
+ set_message(outbuf,4,0,True);
+
+ /* we set NT_STATUS_MORE_PROCESSING_REQUIRED to tell the other end
+ that we aren't finished yet */
+
+ SIVAL(outbuf, smb_rcls, NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED));
+ SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */
+ SSVAL(outbuf, smb_vwv3, blob.length);
+ p = smb_buf(outbuf);
+ memcpy(p, blob.data, blob.length);
+ p += blob.length;
+ p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, "Samba", -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE);
+ set_message_end(outbuf,p);
+
+ return send_smb(smbd_server_fd(),outbuf);
+}
+
+/****************************************************************************
+reply to a session setup spnego negotiate packet
+****************************************************************************/
+static int reply_spnego_negotiate(connection_struct *conn,
+ char *inbuf,
+ char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB blob1)
+{
+ char *OIDs[ASN1_MAX_OIDS];
+ DATA_BLOB secblob;
+ int i;
+ uint32 ntlmssp_command, neg_flags;
+ DATA_BLOB sess_key, chal, spnego_chal;
+ const uint8 *cryptkey;
+ BOOL got_kerberos = False;
+ NTSTATUS nt_status;
+
+ /* parse out the OIDs and the first sec blob */
+ if (!parse_negTokenTarg(blob1, OIDs, &secblob)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("Got OID %s\n", OIDs[i]));
+ if (strcmp(OID_KERBEROS5, OIDs[i]) == 0 ||
+ strcmp(OID_KERBEROS5_OLD, OIDs[i]) == 0) {
+ got_kerberos = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("Got secblob of size %d\n", secblob.length));
+
+#ifdef HAVE_KRB5
+ if (got_kerberos) {
+ int ret = reply_spnego_kerberos(conn, inbuf, outbuf,
+ length, bufsize, &secblob);
+ data_blob_free(&secblob);
+ return ret;
+ }
+#endif
+
+ /* parse the NTLMSSP packet */
+#if 0
+ file_save("secblob.dat", secblob.data, secblob.length);
+#endif
+
+ if (!msrpc_parse(&secblob, "CddB",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &neg_flags,
+ &sess_key)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ data_blob_free(&secblob);
+ data_blob_free(&sess_key);
+
+ if (ntlmssp_command != NTLMSSP_NEGOTIATE) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ DEBUG(3,("Got neg_flags=%08x\n", neg_flags));
+
+ if (ntlmssp_auth_context) {
+ (ntlmssp_auth_context->free)(&ntlmssp_auth_context);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&ntlmssp_auth_context))) {
+ return ERROR_NT(nt_status);
+ }
+
+ cryptkey = ntlmssp_auth_context->get_ntlm_challenge(ntlmssp_auth_context);
+
+ /* Give them the challenge. For now, ignore neg_flags and just
+ return the flags we want. Obviously this is not correct */
+
+ neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_LM_KEY |
+ NTLMSSP_NEGOTIATE_NTLM;
+
+ msrpc_gen(&chal, "Cddddbdddd",
+ "NTLMSSP",
+ NTLMSSP_CHALLENGE,
+ 0,
+ 0x30, /* ?? */
+ neg_flags,
+ cryptkey, 8,
+ 0, 0, 0,
+ 0x3000); /* ?? */
+
+ if (!spnego_gen_challenge(&spnego_chal, &chal, &chal)) {
+ DEBUG(3,("Failed to generate challenge\n"));
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* now tell the client to send the auth packet */
+ reply_sesssetup_blob(conn, outbuf, spnego_chal);
+
+ data_blob_free(&chal);
+ data_blob_free(&spnego_chal);
+
+ /* and tell smbd that we have already replied to this packet */
+ return -1;
+}
+
+
+/****************************************************************************
+reply to a session setup spnego auth packet
+****************************************************************************/
+static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB blob1)
+{
+ DATA_BLOB auth;
+ char *workgroup = NULL, *user = NULL, *machine = NULL;
+ DATA_BLOB lmhash, nthash, sess_key;
+ DATA_BLOB plaintext_password = data_blob(NULL, 0);
+ uint32 ntlmssp_command, neg_flags;
+ NTSTATUS nt_status;
+ int sess_vuid;
+ BOOL as_guest;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ auth_usersupplied_info *user_info = NULL;
+ auth_serversupplied_info *server_info = NULL;
+
+ if (!spnego_parse_auth(blob1, &auth)) {
+#if 0
+ file_save("auth.dat", blob1.data, blob1.length);
+#endif
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&auth, "CdBBUUUBd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &lmhash,
+ &nthash,
+ &workgroup,
+ &user,
+ &machine,
+ &sess_key,
+ &neg_flags)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ data_blob_free(&auth);
+ data_blob_free(&sess_key);
+
+ DEBUG(3,("Got user=[%s] workgroup=[%s] machine=[%s] len1=%d len2=%d\n",
+ user, workgroup, machine, lmhash.length, nthash.length));
+
+#if 0
+ file_save("nthash1.dat", nthash.data, nthash.length);
+ file_save("lmhash1.dat", lmhash.data, lmhash.length);
+#endif
+
+ if (lmhash.length) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+
+ if (nthash.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (nthash.length > 24) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ if (!make_user_info_map(&user_info,
+ user, workgroup,
+ machine,
+ lmhash, nthash,
+ plaintext_password,
+ auth_flags, True)) {
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ nt_status = ntlmssp_auth_context->check_ntlm_password(ntlmssp_auth_context, user_info, &server_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ nt_status = do_map_to_guest(nt_status, &server_info, user, workgroup);
+ }
+
+ SAFE_FREE(workgroup);
+ SAFE_FREE(machine);
+
+ (ntlmssp_auth_context->free)(&ntlmssp_auth_context);
+
+ free_user_info(&user_info);
+
+ data_blob_free(&lmhash);
+
+ data_blob_free(&nthash);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ SAFE_FREE(user);
+ return ERROR_NT(nt_status_squash(nt_status));
+ }
+
+ as_guest = server_info->guest;
+
+ sess_vuid = register_vuid(server_info, user);
+ free_server_info(&server_info);
+
+ SAFE_FREE(user);
+
+ if (sess_vuid == -1) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+
+ if (as_guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ add_signature(outbuf);
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+ SSVAL(inbuf,smb_uid,sess_vuid);
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+
+/****************************************************************************
+reply to a session setup spnego anonymous packet
+****************************************************************************/
+static int reply_spnego_anonymous(connection_struct *conn, char *inbuf, char *outbuf,
+ int length, int bufsize)
+{
+ int sess_vuid;
+ auth_serversupplied_info *server_info = NULL;
+ NTSTATUS nt_status;
+
+ nt_status = check_guest_password(&server_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ERROR_NT(nt_status_squash(nt_status));
+ }
+
+ sess_vuid = register_vuid(server_info, lp_guestaccount());
+
+ free_server_info(&server_info);
+
+ if (sess_vuid == -1) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+ add_signature(outbuf);
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+ SSVAL(inbuf,smb_uid,sess_vuid);
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,char *outbuf,
+ int length,int bufsize)
+{
+ uint8 *p;
+ DATA_BLOB blob1;
+ int ret;
+
+ DEBUG(3,("Doing spnego session setup\n"));
+
+ if (global_client_caps == 0) {
+ global_client_caps = IVAL(inbuf,smb_vwv10);
+ }
+
+ p = (uint8 *)smb_buf(inbuf);
+
+ if (SVAL(inbuf, smb_vwv7) == 0) {
+ /* an anonymous request */
+ return reply_spnego_anonymous(conn, inbuf, outbuf, length, bufsize);
+ }
+
+ /* pull the spnego blob */
+ blob1 = data_blob(p, SVAL(inbuf, smb_vwv7));
+
+#if 0
+ file_save("negotiate.dat", blob1.data, blob1.length);
+#endif
+
+ if (blob1.data[0] == ASN1_APPLICATION(0)) {
+ /* its a negTokenTarg packet */
+ ret = reply_spnego_negotiate(conn, inbuf, outbuf, length, bufsize, blob1);
+ data_blob_free(&blob1);
+ return ret;
+ }
+
+ if (blob1.data[0] == ASN1_CONTEXT(1)) {
+ /* its a auth packet */
+ ret = reply_spnego_auth(conn, inbuf, outbuf, length, bufsize, blob1);
+ data_blob_free(&blob1);
+ return ret;
+ }
+
+ /* what sort of packet is this? */
+ DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n"));
+
+ data_blob_free(&blob1);
+
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
+ int length,int bufsize)
+{
+ int sess_vuid;
+ int smb_bufsize;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DATA_BLOB plaintext_password;
+ pstring user;
+ pstring sub_user; /* Sainitised username for substituion */
+ fstring domain;
+ fstring native_os;
+ fstring native_lanman;
+ static BOOL done_sesssetup = False;
+ extern BOOL global_encrypted_passwords_negotiated;
+ extern BOOL global_spnego_negotiated;
+ extern int Protocol;
+ extern fstring remote_machine;
+ extern userdom_struct current_user_info;
+ extern int max_send;
+
+ auth_usersupplied_info *user_info = NULL;
+ extern struct auth_context *negprot_global_auth_context;
+ auth_serversupplied_info *server_info = NULL;
+
+ NTSTATUS nt_status;
+
+ BOOL doencrypt = global_encrypted_passwords_negotiated;
+
+ START_PROFILE(SMBsesssetupX);
+
+ ZERO_STRUCT(lm_resp);
+ ZERO_STRUCT(nt_resp);
+ ZERO_STRUCT(plaintext_password);
+
+ DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2)));
+
+ /* a SPNEGO session setup has 12 command words, whereas a normal
+ NT1 session setup has 13. See the cifs spec. */
+ if (CVAL(inbuf, smb_wct) == 12 &&
+ (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+ if (!global_spnego_negotiated) {
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize);
+ }
+
+ smb_bufsize = SVAL(inbuf,smb_vwv2);
+
+ if (Protocol < PROTOCOL_NT1) {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ if (passlen1 > MAX_PASS_LEN) {
+ return ERROR_DOS(ERRDOS,ERRbuftoosmall);
+ }
+
+ if (doencrypt) {
+ lm_resp = data_blob(smb_buf(inbuf), passlen1);
+ } else {
+ plaintext_password = data_blob(smb_buf(inbuf), passlen1+1);
+ /* Ensure null termination */
+ plaintext_password.data[passlen1] = 0;
+ }
+
+ srvstr_pull(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), -1, STR_TERMINATE);
+ *domain = 0;
+
+ } else {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ uint16 passlen2 = SVAL(inbuf,smb_vwv8);
+ enum remote_arch_types ra_type = get_remote_arch();
+ char *p = smb_buf(inbuf);
+
+ if(global_client_caps == 0)
+ global_client_caps = IVAL(inbuf,smb_vwv11);
+
+ /* client_caps is used as final determination if client is NT or Win95.
+ This is needed to return the correct error codes in some
+ circumstances.
+ */
+
+ if(ra_type == RA_WINNT || ra_type == RA_WIN2K || ra_type == RA_WIN95) {
+ if(!(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32))) {
+ set_remote_arch( RA_WIN95);
+ }
+ }
+
+ if (passlen1 > MAX_PASS_LEN) {
+ return ERROR_DOS(ERRDOS,ERRbuftoosmall);
+ }
+
+ passlen1 = MIN(passlen1, MAX_PASS_LEN);
+ passlen2 = MIN(passlen2, MAX_PASS_LEN);
+
+ if (!doencrypt) {
+ /* both Win95 and WinNT stuff up the password lengths for
+ non-encrypting systems. Uggh.
+
+ if passlen1==24 its a win95 system, and its setting the
+ password length incorrectly. Luckily it still works with the
+ default code because Win95 will null terminate the password
+ anyway
+
+ if passlen1>0 and passlen2>0 then maybe its a NT box and its
+ setting passlen2 to some random value which really stuffs
+ things up. we need to fix that one. */
+
+ if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1)
+ passlen2 = 0;
+ }
+
+ /* Save the lanman2 password and the NT md4 password. */
+
+ if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) {
+ doencrypt = False;
+ }
+
+ if (doencrypt) {
+ lm_resp = data_blob(p, passlen1);
+ nt_resp = data_blob(p+passlen1, passlen2);
+ } else {
+ plaintext_password = data_blob(p, passlen1+1);
+ /* Ensure null termination */
+ plaintext_password.data[passlen1] = 0;
+ }
+
+ p += passlen1 + passlen2;
+ p += srvstr_pull(inbuf, user, p, sizeof(user), -1,
+ STR_TERMINATE);
+ p += srvstr_pull(inbuf, domain, p, sizeof(domain),
+ -1, STR_TERMINATE);
+ p += srvstr_pull(inbuf, native_os, p, sizeof(native_os),
+ -1, STR_TERMINATE);
+ p += srvstr_pull(inbuf, native_lanman, p, sizeof(native_lanman),
+ -1, STR_TERMINATE);
+ DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n",
+ domain,native_os,native_lanman));
+ }
+
+ /* don't allow for weird usernames or domains */
+ alpha_strcpy(user, user, ". _-$", sizeof(user));
+ alpha_strcpy(domain, domain, ". _-", sizeof(domain));
+ if (strstr(user, "..") || strstr(domain,"..")) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, remote_machine));
+
+ if (*user) {
+ if (global_spnego_negotiated) {
+
+ /* This has to be here, becouse this is a perfectly valid behaviour for guest logons :-( */
+
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ pstrcpy(sub_user, user);
+ } else {
+ pstrcpy(sub_user, lp_guestaccount());
+ }
+
+ pstrcpy(current_user_info.smb_name,sub_user);
+
+ reload_services(True);
+
+ if (lp_security() == SEC_SHARE) {
+ /* in share level we should ignore any passwords */
+
+ data_blob_free(&lm_resp);
+ data_blob_free(&nt_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ map_username(sub_user);
+ add_session_user(sub_user);
+ /* Then force it to null for the benfit of the code below */
+ *user = 0;
+ }
+
+ if (!*user) {
+
+ nt_status = check_guest_password(&server_info);
+
+ } else if (doencrypt) {
+ if (!make_user_info_for_reply_enc(&user_info,
+ user, domain,
+ lm_resp, nt_resp)) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ } else {
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info,
+ &server_info);
+ }
+ } else {
+ struct auth_context *plaintext_auth_context = NULL;
+ const uint8 *chal;
+ if (NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ user, domain, chal,
+ plaintext_password)) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info,
+ &server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
+ }
+ }
+ }
+
+ free_user_info(&user_info);
+
+ data_blob_free(&lm_resp);
+ data_blob_free(&nt_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ nt_status = do_map_to_guest(nt_status, &server_info, user, domain);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ERROR_NT(nt_status_squash(nt_status));
+ }
+
+ /* it's ok - setup a reply */
+ if (Protocol < PROTOCOL_NT1) {
+ set_message(outbuf,3,0,True);
+ } else {
+ set_message(outbuf,3,0,True);
+ add_signature(outbuf);
+ /* perhaps grab OS version here?? */
+ }
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ /* register the name and uid as being validated, so further connections
+ to a uid can get through without a password, on the same VC */
+
+ sess_vuid = register_vuid(server_info, sub_user);
+
+ free_server_info(&server_info);
+
+ if (sess_vuid == -1) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+ SSVAL(inbuf,smb_uid,sess_vuid);
+
+ if (!done_sesssetup)
+ max_send = MIN(max_send,smb_bufsize);
+
+ done_sesssetup = True;
+
+ END_PROFILE(SMBsesssetupX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
diff --git a/source3/smbd/smbrun.c b/source3/smbd/smbrun.c
deleted file mode 100644
index df12ae1f85..0000000000
--- a/source3/smbd/smbrun.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- Unix SMB/Netbios implementation.
- Version 1.9.
- external program running routine
- Copyright (C) Andrew Tridgell 1992-1995
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "includes.h"
-
-
-/*******************************************************************
-close the low 3 fd's and open dev/null in their place
-********************************************************************/
-static void close_fds(void)
-{
- int fd;
- int i;
- close(0); close(1); close(2);
- /* try and use up these file descriptors, so silly
- library routines writing to stdout etc won't cause havoc */
- for (i=0;i<3;i++) {
- fd = open("/dev/null",O_RDWR,0);
- if (fd < 0) fd = open("/dev/null",O_WRONLY,0);
- if (fd != i) return;
- }
-}
-
-
-/*
-This is a wrapper around the system call to allow commands to run correctly
-as non root from a program which is switching between root and non-root
-
-It takes one argument as argv[1] and runs it after becoming a non-root
-user
-*/
-int main(int argc,char *argv[])
-{
- close_fds();
-
- if (getuid() != geteuid())
- {
- int uid,gid;
-
- if (getuid() == 0)
- uid = geteuid();
- else
- uid = getuid();
-
- if (getgid() == 0)
- gid = getegid();
- else
- gid = getgid();
-
-#ifdef USE_SETRES
- setresgid(0,0,0);
- setresuid(0,0,0);
- setresgid(gid,gid,gid);
- setresuid(uid,uid,uid);
-#else
- setuid(0);
- seteuid(0);
- setgid(gid);
- setegid(gid);
- setuid(uid);
- seteuid(uid);
-#endif
-
- if (getuid() != uid)
- return(3);
- }
-
- if (geteuid() != getuid())
- return(1);
-
- if (argc < 2)
- return(2);
-
- /* this is to make sure that the system() call doesn't run forever */
- alarm(30);
-
- return(system(argv[1]));
-}
diff --git a/source3/smbd/srvstr.c b/source3/smbd/srvstr.c
new file mode 100644
index 0000000000..90da422f13
--- /dev/null
+++ b/source3/smbd/srvstr.c
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ server specific string routines
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+int srvstr_push(void *base_ptr, void *dest, const char *src, int dest_len, int flags)
+{
+ return push_string(base_ptr, dest, src, dest_len, flags);
+}
+
+int srvstr_pull(void *base_ptr, char *dest, const void *src, int dest_len, int src_len,
+ int flags)
+{
+ return pull_string(base_ptr, dest, src, dest_len, src_len, flags);
+}
diff --git a/source3/smbd/ssl.c b/source3/smbd/ssl.c
new file mode 100644
index 0000000000..7fcb48a954
--- /dev/null
+++ b/source3/smbd/ssl.c
@@ -0,0 +1,286 @@
+/*
+ Unix SMB/CIFS implementation.
+ SSLeay utility functions
+ Copyright (C) Christian Starkjohann <cs@obdev.at> 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * since includes.h pulls in config.h which is were WITH_SSL will be
+ * defined, we want to include includes.h before testing for WITH_SSL
+ * RJS 26-Jan-1999
+ */
+
+#include "includes.h"
+
+#ifdef WITH_SSL /* should always be defined if this module is compiled */
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+BOOL sslEnabled;
+SSL *ssl = NULL;
+int sslFd = -1;
+static SSL_CTX *sslContext = NULL;
+extern int DEBUGLEVEL;
+
+static int ssl_verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+char buffer[256];
+
+ X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),
+ buffer, sizeof(buffer));
+ if(ok){
+ DEBUG(0, ("SSL: Certificate OK: %s\n", buffer));
+ }else{
+ switch (ctx->error){
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ DEBUG(0, ("SSL: Cert error: CA not known: %s\n", buffer));
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ DEBUG(0, ("SSL: Cert error: Cert not yet valid: %s\n", buffer));
+ break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ DEBUG(0, ("SSL: Cert error: illegal \'not before\' field: %s\n",
+ buffer));
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ DEBUG(0, ("SSL: Cert error: Cert expired: %s\n", buffer));
+ break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ DEBUG(0, ("SSL: Cert error: invalid \'not after\' field: %s\n",
+ buffer));
+ break;
+ default:
+ DEBUG(0, ("SSL: Cert error: unknown error %d in %s\n", ctx->error,
+ buffer));
+ break;
+ }
+ }
+ return ok;
+}
+
+static RSA *ssl_temp_rsa_cb(SSL *ssl, int is_export, int keylength)
+{
+static RSA *rsa = NULL;
+
+ if(rsa == NULL)
+ rsa = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
+ return rsa;
+}
+
+/* This is called before we fork. It should ask the user for the pass phrase
+ * if necessary. Error output can still go to stderr because the process
+ * has a terminal.
+ */
+int sslutil_init(int isServer)
+{
+int err, entropybytes;
+char *certfile, *keyfile, *ciphers, *cacertDir, *cacertFile;
+char *egdsocket, *entropyfile;
+
+ SSL_load_error_strings();
+ SSLeay_add_ssl_algorithms();
+ egdsocket = lp_ssl_egdsocket();
+ if (egdsocket != NULL && *egdsocket != 0)
+ RAND_egd(egdsocket);
+ entropyfile = lp_ssl_entropyfile();
+ entropybytes = lp_ssl_entropybytes();
+ if (entropyfile != NULL && *entropyfile != 0)
+ RAND_load_file(entropyfile, entropybytes);
+ switch(lp_ssl_version()){
+ case SMB_SSL_V2: sslContext = SSL_CTX_new(SSLv2_method()); break;
+ case SMB_SSL_V3: sslContext = SSL_CTX_new(SSLv3_method()); break;
+ default:
+ case SMB_SSL_V23: sslContext = SSL_CTX_new(SSLv23_method()); break;
+ case SMB_SSL_TLS1: sslContext = SSL_CTX_new(TLSv1_method()); break;
+ }
+ if(sslContext == NULL){
+ err = ERR_get_error();
+ fprintf(stderr, "SSL: Error allocating context: %s\n",
+ ERR_error_string(err, NULL));
+ exit(1);
+ }
+ if(lp_ssl_compatibility()){
+ SSL_CTX_set_options(sslContext, SSL_OP_ALL);
+ }
+ certfile = isServer ? lp_ssl_server_cert() : lp_ssl_client_cert();
+ if((certfile == NULL || *certfile == 0) && isServer){
+ fprintf(stderr, "SSL: No cert file specified in config file!\n");
+ fprintf(stderr, "The server MUST have a certificate!\n");
+ exit(1);
+ }
+ keyfile = isServer ? lp_ssl_server_privkey() : lp_ssl_client_privkey();
+ if(keyfile == NULL || *keyfile == 0)
+ keyfile = certfile;
+ if(certfile != NULL && *certfile != 0){
+ if(!SSL_CTX_use_certificate_chain_file(sslContext, certfile)){
+ err = ERR_get_error();
+ fprintf(stderr, "SSL: error reading certificate from file %s: %s\n",
+ certfile, ERR_error_string(err, NULL));
+ exit(1);
+ }
+ if(!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)){
+ err = ERR_get_error();
+ fprintf(stderr, "SSL: error reading private key from file %s: %s\n",
+ keyfile, ERR_error_string(err, NULL));
+ exit(1);
+ }
+ if(!SSL_CTX_check_private_key(sslContext)){
+ err = ERR_get_error();
+ fprintf(stderr, "SSL: Private key does not match public key in cert!\n");
+ exit(1);
+ }
+ }
+ cacertDir = lp_ssl_cacertdir();
+ cacertFile = lp_ssl_cacertfile();
+ if(cacertDir != NULL && *cacertDir == 0)
+ cacertDir = NULL;
+ if(cacertFile != NULL && *cacertFile == 0)
+ cacertFile = NULL;
+ if(!SSL_CTX_load_verify_locations(sslContext, cacertFile, cacertDir)){
+ err = ERR_get_error();
+ if (cacertFile || cacertDir) {
+ fprintf(stderr, "SSL: Error error setting CA cert locations: %s\n",
+ ERR_error_string(err, NULL));
+ fprintf(stderr, "trying default locations.\n");
+ }
+ cacertFile = cacertDir = NULL;
+ if(!SSL_CTX_set_default_verify_paths(sslContext)){
+ err = ERR_get_error();
+ fprintf(stderr, "SSL: Error error setting default CA cert location: %s\n",
+ ERR_error_string(err, NULL));
+ exit(1);
+ }
+ }
+ SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
+ if((ciphers = lp_ssl_ciphers()) != NULL && *ciphers != 0)
+ SSL_CTX_set_cipher_list(sslContext, ciphers);
+ if((isServer && lp_ssl_reqClientCert()) || (!isServer && lp_ssl_reqServerCert())){
+ SSL_CTX_set_verify(sslContext,
+ SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb);
+ }else{
+ SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, ssl_verify_cb);
+ }
+#if 1 /* don't know what this is good for, but s_server in SSLeay does it, too */
+ if(isServer){
+ SSL_CTX_set_client_CA_list(sslContext, SSL_load_client_CA_file(certfile));
+ }
+#endif
+ return 0;
+}
+
+int sslutil_accept(int fd)
+{
+int err;
+
+ if(ssl != NULL){
+ DEBUG(0, ("SSL: internal error: more than one SSL connection (server)\n"));
+ return -1;
+ }
+ if((ssl = SSL_new(sslContext)) == NULL){
+ err = ERR_get_error();
+ DEBUG(0, ("SSL: Error allocating handle: %s\n",
+ ERR_error_string(err, NULL)));
+ return -1;
+ }
+ SSL_set_fd(ssl, fd);
+ sslFd = fd;
+ if(SSL_accept(ssl) <= 0){
+ err = ERR_get_error();
+ DEBUG(0, ("SSL: Error accepting on socket: %s\n",
+ ERR_error_string(err, NULL)));
+ return -1;
+ }
+ DEBUG(0, ("SSL: negotiated cipher: %s\n", SSL_get_cipher(ssl)));
+ return 0;
+}
+
+int sslutil_fd_is_ssl(int fd)
+{
+ return fd == sslFd;
+}
+
+int sslutil_connect(int fd)
+{
+int err;
+
+ if(ssl != NULL){
+ DEBUG(0, ("SSL: internal error: more than one SSL connection (client)\n"));
+ return -1;
+ }
+ if((ssl = SSL_new(sslContext)) == NULL){
+ err = ERR_get_error();
+ DEBUG(0, ("SSL: Error allocating handle: %s\n",
+ ERR_error_string(err, NULL)));
+ return -1;
+ }
+ SSL_set_fd(ssl, fd);
+ sslFd = fd;
+ if(SSL_connect(ssl) <= 0){
+ err = ERR_get_error();
+ DEBUG(0, ("SSL: Error conencting socket: %s\n",
+ ERR_error_string(err, NULL)));
+ return -1;
+ }
+ DEBUG(0, ("SSL: negotiated cipher: %s\n", SSL_get_cipher(ssl)));
+ return 0;
+}
+
+int sslutil_disconnect(int fd)
+{
+ if(fd == sslFd && ssl != NULL){
+ SSL_free(ssl);
+ ssl = NULL;
+ sslFd = -1;
+ }
+ return 0;
+}
+
+int sslutil_negotiate_ssl(int fd, int msg_type)
+{
+unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+char *reqHosts, *resignHosts;
+
+ reqHosts = lp_ssl_hosts();
+ resignHosts = lp_ssl_hosts_resign();
+ if(!allow_access(resignHosts, reqHosts, get_socket_name(fd), get_socket_addr(fd))){
+ sslEnabled = False;
+ return 0;
+ }
+ if(msg_type != 0x81){ /* first packet must be a session request */
+ DEBUG( 0, ( "Client %s did not use session setup; access denied\n",
+ client_addr() ) );
+ if (!send_smb(fd, (char *)buf))
+ DEBUG(0, ("sslutil_negotiate_ssl: send_smb failed.\n"));
+ return -1;
+ }
+ buf[4] = 0x8e; /* negative session response: use SSL */
+ if (!send_smb(fd, (char *)buf)) {
+ DEBUG(0,("sslutil_negotiate_ssl: send_smb failed.\n"));
+ return -1;
+ }
+ if(sslutil_accept(fd) != 0){
+ DEBUG( 0, ( "Client %s failed SSL negotiation!\n", client_addr() ) );
+ return -1;
+ }
+ return 1;
+}
+
+#else /* WITH_SSL */
+ void ssl_dummy(void);
+ void ssl_dummy(void) {;} /* So some compilers don't complain. */
+#endif /* WITH_SSL */
diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c
new file mode 100644
index 0000000000..93782b9bb0
--- /dev/null
+++ b/source3/smbd/statcache.c
@@ -0,0 +1,230 @@
+/*
+ Unix SMB/CIFS implementation.
+ stat cache code
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1999-2000
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern BOOL case_sensitive;
+
+
+/****************************************************************************
+ Stat cache code used in unix_convert.
+*****************************************************************************/
+
+typedef struct {
+ int name_len;
+ char names[2]; /* This is extended via malloc... */
+} stat_cache_entry;
+
+#define INIT_STAT_CACHE_SIZE 512
+static hash_table stat_cache;
+
+/****************************************************************************
+ Add an entry into the stat cache.
+*****************************************************************************/
+
+void stat_cache_add( char *full_orig_name, char *orig_translated_path)
+{
+ stat_cache_entry *scp;
+ stat_cache_entry *found_scp;
+ pstring orig_name;
+ pstring translated_path;
+ int namelen;
+ hash_element *hash_elem;
+
+ if (!lp_stat_cache()) return;
+
+ namelen = strlen(orig_translated_path);
+
+ /*
+ * Don't cache trivial valid directory entries.
+ */
+ if((*full_orig_name == '\0') || (strcmp(full_orig_name, ".") == 0) ||
+ (strcmp(full_orig_name, "..") == 0))
+ return;
+
+ /*
+ * If we are in case insentive mode, we need to
+ * store names that need no translation - else, it
+ * would be a waste.
+ */
+
+ if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0))
+ return;
+
+ /*
+ * Remove any trailing '/' characters from the
+ * translated path.
+ */
+
+ pstrcpy(translated_path, orig_translated_path);
+ if(translated_path[namelen-1] == '/') {
+ translated_path[namelen-1] = '\0';
+ namelen--;
+ }
+
+ /*
+ * We will only replace namelen characters
+ * of full_orig_name.
+ * StrnCpy always null terminates.
+ */
+
+ StrnCpy(orig_name, full_orig_name, namelen);
+ if(!case_sensitive)
+ strupper( orig_name );
+
+ /*
+ * Check this name doesn't exist in the cache before we
+ * add it.
+ */
+
+ if ((hash_elem = hash_lookup(&stat_cache, orig_name))) {
+ found_scp = (stat_cache_entry *)(hash_elem->value);
+ if (strcmp((found_scp->names+found_scp->name_len+1), translated_path) == 0) {
+ return;
+ } else {
+ hash_remove(&stat_cache, hash_elem);
+ if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) {
+ DEBUG(0,("stat_cache_add: Out of memory !\n"));
+ return;
+ }
+ pstrcpy(scp->names, orig_name);
+ pstrcpy((scp->names+namelen+1), translated_path);
+ scp->name_len = namelen;
+ hash_insert(&stat_cache, (char *)scp, orig_name);
+ }
+ return;
+ } else {
+
+ /*
+ * New entry.
+ */
+
+ if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) {
+ DEBUG(0,("stat_cache_add: Out of memory !\n"));
+ return;
+ }
+ pstrcpy(scp->names, orig_name);
+ pstrcpy(scp->names+namelen+1, translated_path);
+ scp->name_len = namelen;
+ hash_insert(&stat_cache, (char *)scp, orig_name);
+ }
+
+ DEBUG(5,("stat_cache_add: Added entry %s -> %s\n", scp->names, (scp->names+scp->name_len+1)));
+}
+
+/****************************************************************************
+ Look through the stat cache for an entry - promote it to the top if found.
+ Return True if we translated (and did a scuccessful stat on) the entire name.
+*****************************************************************************/
+
+BOOL stat_cache_lookup(connection_struct *conn, char *name, char *dirpath,
+ char **start, SMB_STRUCT_STAT *pst)
+{
+ stat_cache_entry *scp;
+ char *trans_name;
+ pstring chk_name;
+ int namelen;
+ hash_element *hash_elem;
+ char *sp;
+
+ if (!lp_stat_cache())
+ return False;
+
+ namelen = strlen(name);
+
+ *start = name;
+
+ DO_PROFILE_INC(statcache_lookups);
+
+ /*
+ * Don't lookup trivial valid directory entries.
+ */
+ if((*name == '\0') || (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
+ DO_PROFILE_INC(statcache_misses);
+ return False;
+ }
+
+ pstrcpy(chk_name, name);
+ if(!case_sensitive)
+ strupper( chk_name );
+
+ while (1) {
+ hash_elem = hash_lookup(&stat_cache, chk_name);
+ if(hash_elem == NULL) {
+ /*
+ * Didn't find it - remove last component for next try.
+ */
+ sp = strrchr_m(chk_name, '/');
+ if (sp) {
+ *sp = '\0';
+ } else {
+ /*
+ * We reached the end of the name - no match.
+ */
+ DO_PROFILE_INC(statcache_misses);
+ return False;
+ }
+ if((*chk_name == '\0') || (strcmp(chk_name, ".") == 0)
+ || (strcmp(chk_name, "..") == 0)) {
+ DO_PROFILE_INC(statcache_misses);
+ return False;
+ }
+ } else {
+ scp = (stat_cache_entry *)(hash_elem->value);
+ DO_PROFILE_INC(statcache_hits);
+ trans_name = scp->names+scp->name_len+1;
+ if(vfs_stat(conn,trans_name, pst) != 0) {
+ /* Discard this entry - it doesn't exist in the filesystem. */
+ hash_remove(&stat_cache, hash_elem);
+ return False;
+ }
+ memcpy(name, trans_name, scp->name_len);
+ *start = &name[scp->name_len];
+ if(**start == '/')
+ ++*start;
+ StrnCpy( dirpath, trans_name, name - (*start));
+ return (namelen == scp->name_len);
+ }
+ }
+}
+
+/*************************************************************************** **
+ * Initializes or clears the stat cache.
+ *
+ * Input: none.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+BOOL reset_stat_cache( void )
+{
+ static BOOL initialised;
+ if (!lp_stat_cache()) return True;
+
+ if (initialised) {
+ hash_clear(&stat_cache);
+ }
+
+ initialised = hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE,
+ (compare_function)(strcmp));
+ return initialised;
+} /* reset_stat_cache */
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 9d02123cf8..1972e9c8c8 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -1,8 +1,7 @@
/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
SMB transaction2 handling
- Copyright (C) Jeremy Allison 1994
+ Copyright (C) Jeremy Allison 1994-2001
Extensively modified by Andrew Tridgell, 1995
@@ -22,15 +21,14 @@
*/
#include "includes.h"
-#include "loadparm.h"
-#include "trans2.h"
-extern int DEBUGLEVEL;
extern int Protocol;
-extern connection_struct Connections[];
-extern files_struct Files[];
extern BOOL case_sensitive;
-extern int Client;
+extern int smb_read_error;
+extern fstring local_machine;
+extern int global_oplock_break;
+extern uint32 global_client_caps;
+extern pstring global_myname;
/****************************************************************************
Send the required number of replies back.
@@ -38,23 +36,25 @@ extern int Client;
set correctly for the type of call.
HACK ! Always assumes smb_setup field is zero.
****************************************************************************/
+
static int send_trans2_replies(char *outbuf, int bufsize, char *params,
- int paramsize, char *pdata, int datasize)
+ int paramsize, char *pdata, int datasize)
{
- /* As we are using a protocol > LANMAN1 then the maxxmit
+ /* As we are using a protocol > LANMAN1 then the max_send
variable must have been set in the sessetupX call.
This takes precedence over the max_xmit field in the
global struct. These different max_xmit variables should
be merged as this is now too confusing */
- extern int maxxmit;
+ extern int max_send;
int data_to_send = datasize;
int params_to_send = paramsize;
int useable_space;
char *pp = params;
char *pd = pdata;
int params_sent_thistime, data_sent_thistime, total_sent_thistime;
- int alignment_offset = 1;
+ int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */
+ int data_alignment_offset = 0;
/* Initially set the wcnt area to be 10 - this is true for all
trans2 replies */
@@ -63,107 +63,136 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params,
/* If there genuinely are no parameters or data to send just send
the empty packet */
if(params_to_send == 0 && data_to_send == 0)
- {
- send_smb(Client,outbuf);
- return 0;
- }
+ {
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans2_replies: send_smb failed.");
+ return 0;
+ }
+
+ /* When sending params and data ensure that both are nicely aligned */
+ /* Only do this alignment when there is also data to send - else
+ can cause NT redirector problems. */
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
/* Space is bufsize minus Netbios over TCP header minus SMB header */
- /* The + 1 is to align the param and data bytes on an even byte
+ /* The alignment_offset is to align the param bytes on an even byte
boundary. NT 4.0 Beta needs this to work correctly. */
- useable_space = bufsize - ((smb_buf(outbuf)+alignment_offset) - outbuf);
- useable_space = MIN(useable_space, maxxmit); /* XXX is this needed? correct? */
+ useable_space = bufsize - ((smb_buf(outbuf)+
+ alignment_offset+data_alignment_offset) -
+ outbuf);
- while( params_to_send || data_to_send)
- {
- /* Calculate whether we will totally or partially fill this packet */
- total_sent_thistime = params_to_send + data_to_send + alignment_offset;
- total_sent_thistime = MIN(total_sent_thistime, useable_space);
+ /* useable_space can never be more than max_send minus the
+ alignment offset. */
+ useable_space = MIN(useable_space,
+ max_send - (alignment_offset+data_alignment_offset));
- set_message(outbuf, 10, total_sent_thistime, True);
- /* Set total params and data to be sent */
- SSVAL(outbuf,smb_tprcnt,paramsize);
- SSVAL(outbuf,smb_tdrcnt,datasize);
+ while (params_to_send || data_to_send)
+ {
+ /* Calculate whether we will totally or partially fill this packet */
+ total_sent_thistime = params_to_send + data_to_send +
+ alignment_offset + data_alignment_offset;
+ /* We can never send more than useable_space */
+ /*
+ * Note that 'useable_space' does not include the alignment offsets,
+ * but we must include the alignment offsets in the calculation of
+ * the length of the data we send over the wire, as the alignment offsets
+ * are sent here. Fix from Marc_Jacobsen@hp.com.
+ */
+ total_sent_thistime = MIN(total_sent_thistime, useable_space+
+ alignment_offset + data_alignment_offset);
+
+ set_message(outbuf, 10, total_sent_thistime, True);
+
+ /* Set total params and data to be sent */
+ SSVAL(outbuf,smb_tprcnt,paramsize);
+ SSVAL(outbuf,smb_tdrcnt,datasize);
+
+ /* Calculate how many parameters and data we can fit into
+ this packet. Parameters get precedence */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SSVAL(outbuf,smb_prcnt, params_sent_thistime);
+
+ /* smb_proff is the offset from the start of the SMB header to the
+ parameter bytes, however the first 4 bytes of outbuf are
+ the Netbios over TCP header. Thus use smb_base() to subtract
+ them from the calculation */
+
+ SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+
+ if(params_sent_thistime == 0)
+ SSVAL(outbuf,smb_prdisp,0);
+ else
+ /* Absolute displacement of param bytes sent in this packet */
+ SSVAL(outbuf,smb_prdisp,pp - params);
+
+ SSVAL(outbuf,smb_drcnt, data_sent_thistime);
+ if(data_sent_thistime == 0)
+ {
+ SSVAL(outbuf,smb_droff,0);
+ SSVAL(outbuf,smb_drdisp, 0);
+ }
+ else
+ {
+ /* The offset of the data bytes is the offset of the
+ parameter bytes plus the number of parameters being sent this time */
+ SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime + data_alignment_offset);
+ SSVAL(outbuf,smb_drdisp, pd - pdata);
+ }
- /* Calculate how many parameters and data we can fit into
- this packet. Parameters get precedence */
+ /* Copy the param bytes into the packet */
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+ /* Copy in the data bytes */
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+
+ data_alignment_offset,pd,data_sent_thistime);
- params_sent_thistime = MIN(params_to_send,useable_space);
- data_sent_thistime = useable_space - params_sent_thistime;
- data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+ DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
- SSVAL(outbuf,smb_prcnt, params_sent_thistime);
- if(params_sent_thistime == 0)
- {
- SSVAL(outbuf,smb_proff,0);
- SSVAL(outbuf,smb_prdisp,0);
- } else {
- /* smb_proff is the offset from the start of the SMB header to the
- parameter bytes, however the first 4 bytes of outbuf are
- the Netbios over TCP header. Thus use smb_base() to subtract
- them from the calculation */
- SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
- /* Absolute displacement of param bytes sent in this packet */
- SSVAL(outbuf,smb_prdisp,pp - params);
- }
+ /* Send the packet */
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans2_replies: send_smb failed.");
- SSVAL(outbuf,smb_drcnt, data_sent_thistime);
- if(data_sent_thistime == 0)
- {
- SSVAL(outbuf,smb_droff,0);
- SSVAL(outbuf,smb_drdisp, 0);
- } else {
- /* The offset of the data bytes is the offset of the
- parameter bytes plus the number of parameters being sent this time */
- SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) -
- smb_base(outbuf)) + params_sent_thistime);
- SSVAL(outbuf,smb_drdisp, pd - pdata);
- }
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
- /* Copy the param bytes into the packet */
- if(params_sent_thistime)
- memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
- /* Copy in the data bytes */
- if(data_sent_thistime)
- memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime,pd,data_sent_thistime);
-
- DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
- params_sent_thistime, data_sent_thistime, useable_space));
- DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
- params_to_send, data_to_send, paramsize, datasize));
-
- /* Send the packet */
- send_smb(Client,outbuf);
-
- pp += params_sent_thistime;
- pd += data_sent_thistime;
-
- params_to_send -= params_sent_thistime;
- data_to_send -= data_sent_thistime;
-
- /* Sanity check */
- if(params_to_send < 0 || data_to_send < 0)
- {
- DEBUG(2,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
- params_to_send, data_to_send));
- return -1;
- }
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /* Sanity check */
+ if(params_to_send < 0 || data_to_send < 0)
+ {
+ DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
}
+ }
return 0;
}
-
/****************************************************************************
- reply to a TRANSACT2_OPEN
+ Reply to a TRANSACT2_OPEN.
****************************************************************************/
-static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum,
- char **pparams, char **ppdata)
+
+static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf,
+ int bufsize,
+ char **pparams, char **ppdata)
{
char *params = *pparams;
int16 open_mode = SVAL(params, 2);
int16 open_attr = SVAL(params,6);
+ BOOL oplock_request = (((SVAL(params,0)|(1<<1))>>1) | ((SVAL(params,0)|(1<<2))>>1));
#if 0
BOOL return_additional_info = BITSETW(params,0);
int16 open_sattr = SVAL(params, 4);
@@ -172,68 +201,85 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum,
int16 open_ofun = SVAL(params,12);
int32 open_size = IVAL(params,14);
char *pname = &params[28];
- int16 namelen = strlen(pname)+1;
-
pstring fname;
- int fnum = -1;
- int unixmode;
- int size=0,fmode=0,mtime=0,rmode;
- int32 inode = 0;
- struct stat sbuf;
+ mode_t unixmode;
+ SMB_OFF_T size=0;
+ int fmode=0,mtime=0,rmode;
+ SMB_INO_T inode = 0;
+ SMB_STRUCT_STAT sbuf;
int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+
+ srvstr_pull(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE);
- StrnCpy(fname,pname,namelen);
+ DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n",
+ fname,open_mode, open_attr, open_ofun, open_size));
- DEBUG(3,("trans2open %s cnum=%d mode=%d attr=%d ofun=%d size=%d\n",
- fname,cnum,open_mode, open_attr, open_ofun, open_size));
+ if (IS_IPC(conn)) {
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
/* XXXX we need to handle passed times, sattr and flags */
- unix_convert(fname,cnum);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
- fnum = find_free_file();
- if (fnum < 0)
- return(ERROR(ERRSRV,ERRnofids));
-
- if (!check_name(fname,cnum))
+ if (!check_name(fname,conn))
+ {
+ if((errno == ENOENT) && bad_path)
+ {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
- unixmode = unix_mode(cnum,open_attr | aARCH);
-
+ unixmode = unix_mode(conn,open_attr | aARCH, fname);
- open_file_shared(fnum,cnum,fname,open_mode,open_ofun,unixmode,
- &rmode,&smb_action);
+ fsp = open_file_shared(conn,fname,&sbuf,open_mode,open_ofun,unixmode,
+ oplock_request, &rmode,&smb_action);
- if (!Files[fnum].open)
+ if (!fsp)
+ {
+ if((errno == ENOENT) && bad_path)
+ {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
return(UNIXERROR(ERRDOS,ERRnoaccess));
-
- if (fstat(Files[fnum].fd,&sbuf) != 0) {
- close_file(fnum);
- return(ERROR(ERRDOS,ERRnoaccess));
}
-
+
size = sbuf.st_size;
- fmode = dos_mode(cnum,fname,&sbuf);
+ fmode = dos_mode(conn,fname,&sbuf);
mtime = sbuf.st_mtime;
inode = sbuf.st_ino;
if (fmode & aDIR) {
- close_file(fnum);
- return(ERROR(ERRDOS,ERRnoaccess));
+ close_file(fsp,False);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
}
/* Realloc the size of parameters and data we will return */
- params = *pparams = Realloc(*pparams, 28);
- if(params == NULL)
- return(ERROR(ERRDOS,ERRnomem));
+ params = Realloc(*pparams, 28);
+ if( params == NULL ) {
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ *pparams = params;
- bzero(params,28);
- SSVAL(params,0,fnum);
+ memset((char *)params,'\0',28);
+ SSVAL(params,0,fsp->fnum);
SSVAL(params,2,fmode);
put_dos_date2(params,4, mtime);
- SIVAL(params,8, size);
+ SIVAL(params,8, (uint32)size);
SSVAL(params,12,rmode);
+ if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+ }
+
SSVAL(params,18,smb_action);
+ /*
+ * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes.
+ */
SIVAL(params,20,inode);
/* Send the required number of replies */
@@ -242,292 +288,503 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum,
return -1;
}
+/*********************************************************
+ Routine to check if a given string matches exactly.
+ as a special case a mask of "." does NOT match. That
+ is required for correct wildcard semantics
+ Case can be significant or not.
+**********************************************************/
+
+static BOOL exact_match(char *str,char *mask, BOOL case_sig)
+{
+ if (mask[0] == '.' && mask[1] == 0)
+ return False;
+ if (case_sig)
+ return strcmp(str,mask)==0;
+ return strcasecmp(str,mask) == 0;
+}
+
+#if 0
+
+Not finished yet - jra.
+
/****************************************************************************
- get a level dependent lanman2 dir entry.
+ Return the filetype for UNIX extensions.
****************************************************************************/
-static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_level,
- int requires_resume_key,
- BOOL dont_descend,char **ppdata,
- char *base_data, int space_remaining,
- BOOL *out_of_space,
- int *last_name_off)
+
+static uint32 unix_filetype(mode_t mode)
{
- char *dname;
- BOOL found = False;
- struct stat sbuf;
- pstring mask;
- pstring pathreal;
- pstring fname;
- BOOL matched;
- char *p, *pdata = *ppdata;
- int reskey=0, prev_dirpos=0;
- int mode=0;
- uint32 size=0,len;
- uint32 mdate=0, adate=0, cdate=0;
- char *name_ptr;
- BOOL isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
- strequal(Connections[cnum].dirpath,".") ||
- strequal(Connections[cnum].dirpath,"/"));
- BOOL was_8_3;
-
- *fname = 0;
- *out_of_space = False;
-
- if (!Connections[cnum].dirptr)
- return(False);
-
- p = strrchr(path_mask,'/');
- if(p != NULL)
- {
- if(p[1] == '\0')
- strcpy(mask,"*.*");
- else
- strcpy(mask, p+1);
- }
- else
- strcpy(mask, path_mask);
+ if(S_ISREG(mode))
+ return UNIX_TYPE_FILE;
+ else if(S_ISDIR(mode))
+ return UNIX_TYPE_DIR;
+#ifdef S_ISLNK
+ else if(S_ISLNK(mode))
+ return UNIX_TYPE_SYMLINK;
+#endif
+#ifdef S_ISCHR
+ else if(S_ISCHR(mode))
+ return UNIX_TYPE_CHARDEV;
+#endif
+#ifdef S_ISBLK
+ else if(S_ISBLK(mode))
+ return UNIX_TYPE_BLKDEV;
+#endif
+#ifdef S_ISFIFO
+ else if(S_ISFIFO(mode))
+ return UNIX_TYPE_FIFO;
+#endif
+#ifdef S_ISSOCK
+ else if(S_ISSOCK(mode))
+ return UNIX_TYPE_SOCKET;
+#endif
- while (!found)
- {
- /* Needed if we run out of space */
- prev_dirpos = TellDir(Connections[cnum].dirptr);
- dname = ReadDirName(Connections[cnum].dirptr);
+ DEBUG(0,("unix_filetype: unknown filetype %u", (unsigned)mode));
+ return UNIX_TYPE_UNKNOWN;
+}
- reskey = TellDir(Connections[cnum].dirptr);
+/****************************************************************************
+ Return the major devicenumber for UNIX extensions.
+****************************************************************************/
- DEBUG(6,("get_lanman2_dir_entry:readdir on dirptr 0x%x now at offset %d\n",
- Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
-
- if (!dname)
- return(False);
+static uint32 unix_dev_major(SMB_DEV_T dev)
+{
+#if defined(HAVE_DEVICE_MAJOR_FN)
+ return (uint32)major(dev);
+#else
+ return (uint32)(dev >> 8);
+#endif
+}
- matched = False;
+/****************************************************************************
+ Return the minor devicenumber for UNIX extensions.
+****************************************************************************/
- strcpy(fname,dname);
+static uint32 unix_dev_minor(SMB_DEV_T dev)
+{
+#if defined(HAVE_DEVICE_MINOR_FN)
+ return (uint32)minor(dev);
+#else
+ return (uint32)(dev & 0xff);
+#endif
+}
- if(mask_match(fname, mask, case_sensitive, True))
- {
- BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
- if (dont_descend && !isdots)
- continue;
-
- if (isrootdir && isdots)
- continue;
-
- strcpy(pathreal,Connections[cnum].dirpath);
- strcat(pathreal,"/");
- strcat(pathreal,fname);
- if (sys_stat(pathreal,&sbuf) != 0)
- {
- DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno)));
- continue;
- }
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+
+static uint32 unix_perms_to_wire(mode_t perms)
+{
+ uint ret = 0;
+
+ ret |= ((perms & S_IXOTH) ? UNIX_X_OTH : 0);
+ ret |= ((perms & S_IWOTH) ? UNIX_W_OTH : 0);
+ ret |= ((perms & S_IROTH) ? UNIX_R_OTH : 0);
+ ret |= ((perms & S_IXGRP) ? UNIX_X_GRP : 0);
+ ret |= ((perms & S_IWGRP) ? UNIX_W_GRP : 0);
+ ret |= ((perms & S_IRGRP) ? UNIX_R_GRP : 0);
+ ret |= ((perms & S_IXUSR) ? UNIX_X_USR : 0);
+ ret |= ((perms & S_IWUSR) ? UNIX_W_USR : 0);
+ ret |= ((perms & S_IRUSR) ? UNIX_R_USR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & S_ISVTX) ? UNIX_STICKY : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & S_ISGID) ? UNIX_SET_GID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & S_ISVTX) ? UNIX_SET_UID : 0);
+#endif
+ return ret;
+}
- mode = dos_mode(cnum,pathreal,&sbuf);
+#endif
- if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
- {
- DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
- continue;
- }
- size = sbuf.st_size;
- mdate = sbuf.st_mtime;
- adate = sbuf.st_atime;
- cdate = sbuf.st_ctime;
- if(mode & aDIR)
- size = 0;
-
- DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
+/****************************************************************************
+ Get a level dependent lanman2 dir entry.
+****************************************************************************/
+
+static BOOL get_lanman2_dir_entry(connection_struct *conn,
+ void *inbuf, void *outbuf,
+ char *path_mask,int dirtype,int info_level,
+ int requires_resume_key,
+ BOOL dont_descend,char **ppdata,
+ char *base_data, int space_remaining,
+ BOOL *out_of_space, BOOL *got_exact_match,
+ int *last_name_off)
+{
+ char *dname;
+ BOOL found = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring mask;
+ pstring pathreal;
+ pstring fname;
+ char *p, *q, *pdata = *ppdata;
+ uint32 reskey=0;
+ int prev_dirpos=0;
+ int mode=0;
+ SMB_OFF_T size = 0;
+ SMB_OFF_T allocation_size = 0;
+ uint32 len;
+ time_t mdate=0, adate=0, cdate=0;
+ char *nameptr;
+ BOOL was_8_3;
+ int nt_extmode; /* Used for NT connections instead of mode */
+ BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
+
+ *fname = 0;
+ *out_of_space = False;
+ *got_exact_match = False;
+
+ if (!conn->dirptr)
+ return(False);
+
+ p = strrchr_m(path_mask,'/');
+ if(p != NULL) {
+ if(p[1] == '\0')
+ pstrcpy(mask,"*.*");
+ else
+ pstrcpy(mask, p+1);
+ } else
+ pstrcpy(mask, path_mask);
+
+ while (!found) {
+ BOOL got_match;
+
+ /* Needed if we run out of space */
+ prev_dirpos = TellDir(conn->dirptr);
+ dname = ReadDirName(conn->dirptr);
+
+ /*
+ * Due to bugs in NT client redirectors we are not using
+ * resume keys any more - set them to zero.
+ * Check out the related comments in findfirst/findnext.
+ * JRA.
+ */
+
+ reskey = 0;
+
+ DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n",
+ (long)conn->dirptr,TellDir(conn->dirptr)));
+
+ if (!dname)
+ return(False);
+
+ pstrcpy(fname,dname);
+
+ if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive)))
+ got_match = mask_match(fname, mask, case_sensitive);
+
+ if(!got_match && !mangle_is_8_3(fname, False)) {
+
+ /*
+ * It turns out that NT matches wildcards against
+ * both long *and* short names. This may explain some
+ * of the wildcard wierdness from old DOS clients
+ * that some people have been seeing.... JRA.
+ */
+
+ pstring newname;
+ pstrcpy( newname, fname);
+ mangle_map( newname, True, False, SNUM(conn));
+ if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive)))
+ got_match = mask_match(newname, mask, case_sensitive);
+ }
+
+ if(got_match) {
+ BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
+ if (dont_descend && !isdots)
+ continue;
- found = True;
+ pstrcpy(pathreal,conn->dirpath);
+ if(needslash)
+ pstrcat(pathreal,"/");
+ pstrcat(pathreal,dname);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (vfs_lstat(conn,pathreal,&sbuf) != 0) {
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't lstat [%s] (%s)\n",
+ pathreal,strerror(errno)));
+ continue;
+ }
+ } else if (vfs_stat(conn,pathreal,&sbuf) != 0) {
+ /* Needed to show the msdfs symlinks as directories */
+ if(!lp_host_msdfs() || !lp_msdfs_root(SNUM(conn))
+ || !is_msdfs_link(conn, pathreal)) {
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",
+ pathreal,strerror(errno)));
+ continue;
+ } else {
+ DEBUG(5,("get_lanman2_dir_entry: Masquerading msdfs link %s as a directory\n",
+ pathreal));
+ sbuf.st_mode = (sbuf.st_mode & 0xFFF) | S_IFDIR;
+ }
+ }
+
+ mode = dos_mode(conn,pathreal,&sbuf);
+
+ if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) {
+ DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
+ continue;
+ }
+
+ size = sbuf.st_size;
+ allocation_size = SMB_ROUNDUP_ALLOCATION(sbuf.st_size);
+ mdate = sbuf.st_mtime;
+ adate = sbuf.st_atime;
+ cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ cdate &= ~1;
+ mdate &= ~1;
+ adate &= ~1;
+ }
+
+ if(mode & aDIR)
+ size = 0;
+
+ DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
+
+ found = True;
+ }
}
- }
-
-#ifndef KANJI
- unix2dos_format(fname, True);
+ mangle_map(fname,False,True,SNUM(conn));
+
+ p = pdata;
+ nameptr = p;
+
+ nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL;
+
+ switch (info_level) {
+ case 1:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l1_fdateCreation,cdate);
+ put_dos_date2(p,l1_fdateLastAccess,adate);
+ put_dos_date2(p,l1_fdateLastWrite,mdate);
+ SIVAL(p,l1_cbFile,(uint32)size);
+ SIVAL(p,l1_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(p,l1_attrFile,mode);
+ p += l1_achName;
+ nameptr = p;
+ p += align_string(outbuf, p, 0);
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+ SCVAL(nameptr, -1, len);
+ p += len;
+ break;
+
+ case 2:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l2_fdateCreation,cdate);
+ put_dos_date2(p,l2_fdateLastAccess,adate);
+ put_dos_date2(p,l2_fdateLastWrite,mdate);
+ SIVAL(p,l2_cbFile,(uint32)size);
+ SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(p,l2_attrFile,mode);
+ SIVAL(p,l2_cbList,0); /* No extended attributes */
+ p += l2_achName;
+ nameptr = p;
+ len = srvstr_push(outbuf, p, fname, -1, STR_NOALIGN);
+ SCVAL(p, -1, len);
+ p += len;
+ *p++ = 0; /* craig from unisys pointed out we need this */
+ break;
+
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ was_8_3 = mangle_is_8_3(fname, True);
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ q = p; p += 4;
+ SIVAL(p,0,0); p += 4;
+ if (!was_8_3) {
+ pstring mangled_name;
+ pstrcpy(mangled_name, fname);
+ mangle_map(mangled_name,True,True,SNUM(conn));
+ mangled_name[12] = 0;
+ len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER);
+ SSVAL(p, 0, len);
+ } else {
+ SSVAL(p,0,0);
+ *(p+2) = 0;
+ }
+ p += 2 + 24;
+ len = srvstr_push(outbuf, p, fname, -1, 0);
+ SIVAL(q,0,len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ p += 4;
+ len = srvstr_push(outbuf, p, fname, -1, 0);
+ SIVAL(p, -4, len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ p += 4;
+ SIVAL(p,0,0); p += 4;
+
+ len = srvstr_push(outbuf, p, fname, -1, 0);
+ SIVAL(p, -4, len);
+ p += len;
+
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_NAMES_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ p += 4;
+ /* this must *not* be null terminated or w2k gets in a loop trying to set an
+ acl on a dir (tridge) */
+ len = srvstr_push(outbuf, p, fname, -1, 0);
+ SIVAL(p, -4, len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ /* CIFS UNIX Extension. */
+
+#if 0 /* JRA - FIXME - NEEDS UNICODE CONVERSION !!! */
+ case SMB_FIND_FILE_UNIX:
+
+ len = 108+strlen(fname)+1; /* (length of SMB_QUERY_FILE_UNIX_BASIC = 100)+4+4+strlen(fname)*/
+ /* +1 to be sure to transmit the termination of fname */
+ len = (len + 3) & ~3;
+
+ SIVAL(p,0,len); p+= 4; /* Offset from this structure to the beginning of the next one */
+ SIVAL(p,0,reskey); p+= 4; /* Used for continuing search. */
+
+ /* Begin of SMB_QUERY_FILE_UNIX_BASIC */
+ SOFF_T(p,0,sbuf.st_size); /* File size 64 Bit */
+ p+= 8;
+
+#if defined(HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
+ SOFF_T(p,0,sbuf.st_blocks*STAT_ST_BLOCKSIZE); /* Number of bytes used on disk - 64 Bit */
+#else
+ /* Can't get the value - fake it using size. */
+ SOFF_T(p,0,sbuf.st_size); /* Number of bytes used on disk - 64 Bit */
#endif
+ p+= 8;
- p = pdata;
- name_ptr = p;
+ put_long_date(p,sbuf.st_ctime); /* Creation Time 64 Bit */
+ put_long_date(p+8,sbuf.st_atime); /* Last access time 64 Bit */
+ put_long_date(p+16,sbuf.st_mtime); /* Last modification time 64 Bit */
+ p+= 24;
- name_map_mangle(fname,False,SNUM(cnum));
+ SIVAL(p,0,sbuf.st_uid); /* user id for the owner */
+ SIVAL(p,4,0);
+ p+= 8;
- switch (info_level)
- {
- case 1:
- if(requires_resume_key) {
- SIVAL(p,0,reskey);
- p += 4;
- }
- put_dos_date2(p,l1_fdateCreation,cdate);
- put_dos_date2(p,l1_fdateLastAccess,adate);
- put_dos_date2(p,l1_fdateLastWrite,mdate);
- SIVAL(p,l1_cbFile,size);
- SIVAL(p,l1_cbFileAlloc,ROUNDUP(size,1024));
- SSVAL(p,l1_attrFile,mode);
- SCVAL(p,l1_cchName,strlen(fname));
- strcpy(p + l1_achName, fname);
- name_ptr = p + l1_achName;
- p += l1_achName + strlen(fname) + 1;
- break;
+ SIVAL(p,0,sbuf.st_gid); /* group id of owner */
+ SIVAL(p,4,0);
+ p+= 8;
- case 2:
- /* info_level 2 */
- if(requires_resume_key) {
- SIVAL(p,0,reskey);
- p += 4;
- }
- put_dos_date2(p,l2_fdateCreation,cdate);
- put_dos_date2(p,l2_fdateLastAccess,adate);
- put_dos_date2(p,l2_fdateLastWrite,mdate);
- SIVAL(p,l2_cbFile,size);
- SIVAL(p,l2_cbFileAlloc,ROUNDUP(size,1024));
- SSVAL(p,l2_attrFile,mode);
- SIVAL(p,l2_cbList,0); /* No extended attributes */
- SCVAL(p,l2_cchName,strlen(fname));
- strcpy(p + l2_achName, fname);
- name_ptr = p + l2_achName;
- p += l2_achName + strlen(fname) + 1;
- break;
+ SIVAL(p,0,unix_filetype(sbuf.st_mode));
+ p+= 4;
- case 3:
- SIVAL(p,0,reskey);
- put_dos_date2(p,4,cdate);
- put_dos_date2(p,8,adate);
- put_dos_date2(p,12,mdate);
- SIVAL(p,16,size);
- SIVAL(p,20,ROUNDUP(size,1024));
- SSVAL(p,24,mode);
- SIVAL(p,26,4);
- CVAL(p,30) = strlen(fname);
- strcpy(p+31, fname);
- name_ptr = p+31;
- p += 31 + strlen(fname) + 1;
- break;
+ SIVAL(p,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */
+ SIVAL(p,4,0);
+ p+= 8;
- case 4:
- if(requires_resume_key) {
- SIVAL(p,0,reskey);
- p += 4;
- }
- SIVAL(p,0,33+strlen(fname)+1);
- put_dos_date2(p,4,cdate);
- put_dos_date2(p,8,adate);
- put_dos_date2(p,12,mdate);
- SIVAL(p,16,size);
- SIVAL(p,20,ROUNDUP(size,1024));
- SSVAL(p,24,mode);
- CVAL(p,32) = strlen(fname);
- strcpy(p + 33, fname);
- name_ptr = p+33;
- p += 33 + strlen(fname) + 1;
- break;
+ SIVAL(p,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */
+ SIVAL(p,4,0);
+ p+= 8;
- case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
- was_8_3 = is_8_3(fname);
- len = 94+strlen(fname);
- len = (len + 3) & ~3;
- SIVAL(p,0,len); p += 4;
- SIVAL(p,0,reskey); p += 4;
- put_long_date(p,cdate); p += 8;
- put_long_date(p,adate); p += 8;
- put_long_date(p,mdate); p += 8;
- put_long_date(p,mdate); p += 8;
- SIVAL(p,0,size); p += 8;
- SIVAL(p,0,size); p += 8;
- SIVAL(p,0,mode); p += 4;
- SIVAL(p,0,strlen(fname)); p += 4;
- SIVAL(p,0,0); p += 4;
- if (!was_8_3) {
-#ifndef KANJI
- strcpy(p+2,unix2dos_format(fname,False));
-#else
- strcpy(p+2,fname);
-#endif
- if (!name_map_mangle(p+2,True,SNUM(cnum)))
- (p+2)[12] = 0;
- } else
- *(p+2) = 0;
- strupper(p+2);
- SSVAL(p,0,strlen(p+2));
- p += 2 + 24;
- /* name_ptr = p; */
- strcpy(p,fname); p += strlen(p);
- p = pdata + len;
- break;
+ SINO_T(p,0,(SMB_INO_T)sbuf.st_ino); /* inode number */
+ p+= 8;
- case SMB_FIND_FILE_DIRECTORY_INFO:
- len = 64+strlen(fname);
- len = (len + 3) & ~3;
- SIVAL(p,0,len); p += 4;
- SIVAL(p,0,reskey); p += 4;
- put_long_date(p,cdate); p += 8;
- put_long_date(p,adate); p += 8;
- put_long_date(p,mdate); p += 8;
- put_long_date(p,mdate); p += 8;
- SIVAL(p,0,size); p += 8;
- SIVAL(p,0,size); p += 8;
- SIVAL(p,0,mode); p += 4;
- SIVAL(p,0,strlen(fname)); p += 4;
- strcpy(p,fname);
- p = pdata + len;
- break;
-
-
- case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
- len = 68+strlen(fname);
- len = (len + 3) & ~3;
- SIVAL(p,0,len); p += 4;
- SIVAL(p,0,reskey); p += 4;
- put_long_date(p,cdate); p += 8;
- put_long_date(p,adate); p += 8;
- put_long_date(p,mdate); p += 8;
- put_long_date(p,mdate); p += 8;
- SIVAL(p,0,size); p += 8;
- SIVAL(p,0,size); p += 8;
- SIVAL(p,0,mode); p += 4;
- SIVAL(p,0,strlen(fname)); p += 4;
- SIVAL(p,0,0); p += 4;
- strcpy(p,fname);
- p = pdata + len;
- break;
+ SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */
+ SIVAL(p,4,0);
+ p+= 8;
- case SMB_FIND_FILE_NAMES_INFO:
- len = 12+strlen(fname);
- len = (len + 3) & ~3;
- SIVAL(p,0,len); p += 4;
- SIVAL(p,0,reskey); p += 4;
- SIVAL(p,0,strlen(fname)); p += 4;
- strcpy(p,fname);
- p = pdata + len;
- break;
+ SIVAL(p,0,sbuf.st_nlink); /* number of hard links */
+ SIVAL(p,4,0);
+ p+= 8;
- default:
- return(False);
- }
+ /* End of SMB_QUERY_FILE_UNIX_BASIC */
+ pstrcpy(p,fname);
+ p=pdata+len;
+ break;
+#endif
- if (PTR_DIFF(p,pdata) > space_remaining) {
- /* Move the dirptr back to prev_dirpos */
- SeekDir(Connections[cnum].dirptr, prev_dirpos);
- *out_of_space = True;
- DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
- return False; /* Not finished - just out of space */
- }
+ default:
+ return(False);
+ }
+
+
+ if (PTR_DIFF(p,pdata) > space_remaining) {
+ /* Move the dirptr back to prev_dirpos */
+ SeekDir(conn->dirptr, prev_dirpos);
+ *out_of_space = True;
+ DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+ return False; /* Not finished - just out of space */
+ }
+
+ /* Setup the last_filename pointer, as an offset from base_data */
+ *last_name_off = PTR_DIFF(nameptr,base_data);
+ /* Advance the data pointer to the next slot */
+ *ppdata = p;
- /* Setup the last_filename pointer, as an offset from base_data */
- *last_name_off = PTR_DIFF(name_ptr,base_data);
- /* Advance the data pointer to the next slot */
- *ppdata = p;
- return(found);
+ return(found);
}
-
+
/****************************************************************************
- reply to a TRANS2_FINDFIRST
+ Reply to a TRANS2_FINDFIRST.
****************************************************************************/
-static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum,
- char **pparams, char **ppdata)
+
+static int call_trans2findfirst(connection_struct *conn,
+ char *inbuf, char *outbuf, int bufsize,
+ char **pparams, char **ppdata)
{
/* We must be careful here that we don't return more than the
allowed number of data bytes. If this means returning fewer than
@@ -554,6 +811,8 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum
BOOL dont_descend = False;
BOOL out_of_space = False;
int space_remaining;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
*directory = *mask = 0;
@@ -573,68 +832,69 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
break;
default:
- return(ERROR(ERRDOS,ERRunknownlevel));
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
}
- strcpy(directory, params + 12); /* Complete directory path with
- wildcard mask appended */
+ srvstr_pull(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE);
- DEBUG(5,("path=%s\n",directory));
+ RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf);
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if(!check_name(directory,conn)) {
+ if((errno == ENOENT) && bad_path)
+ {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+
+#if 0
+ /* Ugly - NT specific hack - maybe not needed ? (JRA) */
+ if((errno == ENOTDIR) && (Protocol >= PROTOCOL_NT1) &&
+ (get_remote_arch() == RA_WINNT))
+ {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbaddirectory;
+ }
+#endif
- unix_convert(directory,cnum);
- if(!check_name(directory,cnum)) {
- return(ERROR(ERRDOS,ERRbadpath));
+ return(UNIXERROR(ERRDOS,ERRbadpath));
}
- p = strrchr(directory,'/');
+ p = strrchr_m(directory,'/');
if(p == NULL) {
- strcpy(mask,directory);
- strcpy(directory,"./");
+ pstrcpy(mask,directory);
+ pstrcpy(directory,"./");
} else {
- strcpy(mask,p+1);
+ pstrcpy(mask,p+1);
*p = 0;
}
DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
- pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024);
- if(!*ppdata)
- return(ERROR(ERRDOS,ERRnomem));
- bzero(pdata,max_data_bytes);
+ pdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if( pdata == NULL ) {
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
/* Realloc the params space */
- params = *pparams = Realloc(*pparams, 10);
- if(params == NULL)
- return(ERROR(ERRDOS,ERRnomem));
-
- dptr_num = dptr_create(cnum,directory, True ,SVAL(inbuf,smb_pid));
- if (dptr_num < 0)
- return(ERROR(ERRDOS,ERRbadpath));
-
- /* convert the formatted masks */
- {
- p = mask;
- while (*p) {
- if (*p == '<') *p = '*';
- if (*p == '>') *p = '?';
- if (*p == '"') *p = '.';
- p++;
- }
+ params = Realloc(*pparams, 10);
+ if (params == NULL) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
}
-
- /* a special case for 16 bit apps */
- if (strequal(mask,"????????.???")) strcpy(mask,"*");
+ *pparams = params;
- /* handle broken clients that send us old 8.3 format */
- string_sub(mask,"????????","*");
- string_sub(mask,".???",".*");
+ dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0)
+ return(UNIXERROR(ERRDOS,ERRbadfile));
/* Save the wildcard match and attribs we are using on this directory -
needed as lanman2 assumes these are being saved between calls */
if(!(wcard = strdup(mask))) {
- dptr_close(dptr_num);
- return(ERROR(ERRDOS,ERRnomem));
+ dptr_close(&dptr_num);
+ return ERROR_DOS(ERRDOS,ERRnomem);
}
dptr_set_wcard(dptr_num, wcard);
@@ -646,8 +906,8 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum
a different TRANS2 call. */
DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
- Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
- if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive))
+ conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
dont_descend = True;
p = pdata;
@@ -655,39 +915,61 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum
out_of_space = False;
for (i=0;(i<maxentries) && !finished && !out_of_space;i++)
+ {
+ BOOL got_exact_match = False;
+
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0)
+ {
+ out_of_space = True;
+ finished = False;
+ }
+ else
{
+ finished = !get_lanman2_dir_entry(conn,
+ inbuf, outbuf,
+ mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+ &last_name_off);
+ }
- /* this is a heuristic to avoid seeking the dirptr except when
- absolutely necessary. It allows for a filename of about 40 chars */
- if (space_remaining < DIRLEN_GUESS && numentries > 0)
- {
- out_of_space = True;
- finished = False;
- }
- else
- {
- finished =
- !get_lanman2_dir_entry(cnum,mask,dirtype,info_level,
- requires_resume_key,dont_descend,
- &p,pdata,space_remaining, &out_of_space,
- &last_name_off);
- }
+ if (finished && out_of_space)
+ finished = False;
- if (finished && out_of_space)
- finished = False;
+ if (!finished && !out_of_space)
+ numentries++;
- if (!finished && !out_of_space)
- numentries++;
- space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
- }
+ /*
+ * As an optimisation if we know we aren't looking
+ * for a wildcard name (ie. the name matches the wildcard exactly)
+ * then we can finish on any (first) match.
+ * This speeds up large directory searches. JRA.
+ */
+
+ if(got_exact_match)
+ finished = True;
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
/* Check if we can close the dirptr */
if(close_after_first || (finished && close_if_end))
- {
- dptr_close(dptr_num);
- DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
- dptr_num = -1;
- }
+ {
+ DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+ dptr_close(&dptr_num);
+ }
+
+ /*
+ * If there are no matching entries we must return ERRDOS/ERRbadfile -
+ * from observation of NT.
+ */
+
+ if(numentries == 0) {
+ dptr_close(&dptr_num);
+ return ERROR_DOS(ERRDOS,ERRbadfile);
+ }
/* At this point pdata points to numentries directory entries. */
@@ -701,22 +983,34 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum
send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata));
if ((! *directory) && dptr_path(dptr_num))
- sprintf(directory,"(%s)",dptr_path(dptr_num));
+ slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries ) );
- DEBUG(4,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n",
- timestring(),
- smb_fn_name(CVAL(inbuf,smb_com)),
- mask,directory,cnum,dirtype,numentries));
+ /*
+ * Force a name mangle here to ensure that the
+ * mask as an 8.3 name is top of the mangled cache.
+ * The reasons for this are subtle. Don't remove
+ * this code unless you know what you are doing
+ * (see PR#13758). JRA.
+ */
+
+ if(!mangle_is_8_3( mask, False))
+ mangle_map(mask, True, True, SNUM(conn));
return(-1);
}
-
/****************************************************************************
- reply to a TRANS2_FINDNEXT
+ Reply to a TRANS2_FINDNEXT.
****************************************************************************/
-static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsize,
- int cnum, char **pparams, char **ppdata)
+
+static int call_trans2findnext(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **pparams, char **ppdata)
{
/* We must be careful here that we don't return more than the
allowed number of data bytes. If this means returning fewer than
@@ -726,7 +1020,7 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz
int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
char *params = *pparams;
char *pdata = *ppdata;
- int16 dptr_num = SVAL(params,0);
+ int dptr_num = SVAL(params,0);
int maxentries = SVAL(params,2);
uint16 info_level = SVAL(params,4);
uint32 resume_key = IVAL(params,6);
@@ -734,6 +1028,7 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz
BOOL close_if_end = BITSETW(params+10,1);
BOOL requires_resume_key = BITSETW(params+10,2);
BOOL continue_bit = BITSETW(params+10,3);
+ pstring resume_name;
pstring mask;
pstring directory;
char *p;
@@ -745,11 +1040,15 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz
BOOL out_of_space = False;
int space_remaining;
- *mask = *directory = 0;
+ *mask = *directory = *resume_name = 0;
+
+ srvstr_pull(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE);
- DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, close_after_request=%d, close_if_end = %d requires_resume_key = %d resume_key = %d continue=%d level = %d\n",
+ DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \
+close_after_request=%d, close_if_end = %d requires_resume_key = %d \
+resume_key = %d resume name = %s continue=%d level = %d\n",
dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end,
- requires_resume_key, resume_key, continue_bit, info_level));
+ requires_resume_key, resume_key, resume_name, continue_bit, info_level));
switch (info_level)
{
@@ -763,89 +1062,179 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
break;
default:
- return(ERROR(ERRDOS,ERRunknownlevel));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
}
- pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024);
- if(!*ppdata)
- return(ERROR(ERRDOS,ERRnomem));
- bzero(pdata,max_data_bytes);
+ pdata = Realloc( *ppdata, max_data_bytes + 1024);
+ if(pdata == NULL) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
/* Realloc the params space */
- params = *pparams = Realloc(*pparams, 6*SIZEOFWORD);
- if(!params)
- return(ERROR(ERRDOS,ERRnomem));
+ params = Realloc(*pparams, 6*SIZEOFWORD);
+ if( params == NULL ) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *pparams = params;
/* Check that the dptr is valid */
- if(!(Connections[cnum].dirptr = dptr_fetch_lanman2(params, dptr_num)))
- return(ERROR(ERRDOS,ERRnofiles));
+ if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num)))
+ return ERROR_DOS(ERRDOS,ERRnofiles);
- string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
+ string_set(&conn->dirpath,dptr_path(dptr_num));
/* Get the wildcard mask from the dptr */
if((p = dptr_wcard(dptr_num))== NULL) {
DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
- return (ERROR(ERRDOS,ERRnofiles));
+ return ERROR_DOS(ERRDOS,ERRnofiles);
}
- strcpy(mask, p);
- strcpy(directory,Connections[cnum].dirpath);
+ pstrcpy(mask, p);
+ pstrcpy(directory,conn->dirpath);
/* Get the attr mask from the dptr */
dirtype = dptr_attr(dptr_num);
- DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%X,%d)\n",
+ DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n",
dptr_num, mask, dirtype,
- Connections[cnum].dirptr,
- TellDir(Connections[cnum].dirptr)));
+ (long)conn->dirptr,
+ TellDir(conn->dirptr)));
/* We don't need to check for VOL here as this is returned by
a different TRANS2 call. */
- DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
- if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive))
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
dont_descend = True;
p = pdata;
space_remaining = max_data_bytes;
out_of_space = False;
- /* If we have a resume key - seek to the correct position. */
- if(requires_resume_key && !continue_bit)
- SeekDir(Connections[cnum].dirptr, resume_key);
+ /*
+ * Seek to the correct position. We no longer use the resume key but
+ * depend on the last file name instead.
+ */
+ if(requires_resume_key && *resume_name && !continue_bit)
+ {
+ /*
+ * Fix for NT redirector problem triggered by resume key indexes
+ * changing between directory scans. We now return a resume key of 0
+ * and instead look for the filename to continue from (also given
+ * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
+ * findfirst/findnext (as is usual) then the directory pointer
+ * should already be at the correct place. Check this by scanning
+ * backwards looking for an exact (ie. case sensitive) filename match.
+ * If we get to the beginning of the directory and haven't found it then scan
+ * forwards again looking for a match. JRA.
+ */
+
+ int current_pos, start_pos;
+ char *dname = NULL;
+ void *dirptr = conn->dirptr;
+ start_pos = TellDir(dirptr);
+ for(current_pos = start_pos; current_pos >= 0; current_pos--)
+ {
+ DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos));
- for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++)
+ SeekDir(dirptr, current_pos);
+ dname = ReadDirName(dirptr);
+
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we do the same
+ * here.
+ */
+
+ if(dname != NULL)
+ mangle_map( dname, False, True, SNUM(conn));
+
+ if(dname && strcsequal( resume_name, dname))
+ {
+ SeekDir(dirptr, current_pos+1);
+ DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
+ break;
+ }
+ }
+
+ /*
+ * Scan forward from start if not found going backwards.
+ */
+
+ if(current_pos < 0)
{
- /* this is a heuristic to avoid seeking the dirptr except when
- absolutely necessary. It allows for a filename of about 40 chars */
- if (space_remaining < DIRLEN_GUESS && numentries > 0)
- {
- out_of_space = True;
- finished = False;
- }
- else
- {
- finished =
- !get_lanman2_dir_entry(cnum,mask,dirtype,info_level,
- requires_resume_key,dont_descend,
- &p,pdata,space_remaining, &out_of_space,
- &last_name_off);
- }
+ DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos));
+ SeekDir(dirptr, start_pos);
+ for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; SeekDir(dirptr,++current_pos))
+ {
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we do the same
+ * here.
+ */
+
+ if(dname != NULL)
+ mangle_map( dname, False, True, SNUM(conn));
+
+ if(dname && strcsequal( resume_name, dname))
+ {
+ SeekDir(dirptr, current_pos+1);
+ DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
+ break;
+ }
+ } /* end for */
+ } /* end if current_pos */
+ } /* end if requires_resume_key && !continue_bit */
- if (finished && out_of_space)
- finished = False;
+ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++)
+ {
+ BOOL got_exact_match = False;
- if (!finished && !out_of_space)
- numentries++;
- space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0)
+ {
+ out_of_space = True;
+ finished = False;
+ }
+ else
+ {
+ finished = !get_lanman2_dir_entry(conn,
+ inbuf, outbuf,
+ mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+ &last_name_off);
}
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+
+ /*
+ * As an optimisation if we know we aren't looking
+ * for a wildcard name (ie. the name matches the wildcard exactly)
+ * then we can finish on any (first) match.
+ * This speeds up large directory searches. JRA.
+ */
+
+ if(got_exact_match)
+ finished = True;
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
/* Check if we can close the dirptr */
if(close_after_request || (finished && close_if_end))
- {
- dptr_close(dptr_num); /* This frees up the saved mask */
- DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
- dptr_num = -1;
- }
+ {
+ DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
+ dptr_close(&dptr_num); /* This frees up the saved mask */
+ }
/* Set up the return parameter block */
@@ -857,124 +1246,166 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz
send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata));
if ((! *directory) && dptr_path(dptr_num))
- sprintf(directory,"(%s)",dptr_path(dptr_num));
+ slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
- DEBUG(3,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n",
- timestring(),
- smb_fn_name(CVAL(inbuf,smb_com)),
- mask,directory,cnum,dirtype,numentries));
+ DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries ) );
return(-1);
}
/****************************************************************************
- reply to a TRANS2_QFSINFO (query filesystem info)
+ Reply to a TRANS2_QFSINFO (query filesystem info).
****************************************************************************/
-static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize,
- int cnum, char **pparams, char **ppdata)
+
+static int call_trans2qfsinfo(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **pparams, char **ppdata)
{
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
char *pdata = *ppdata;
char *params = *pparams;
uint16 info_level = SVAL(params,0);
- int data_len;
- struct stat st;
- char *vname = volume_label(SNUM(cnum));
-
- DEBUG(3,("call_trans2qfsinfo: cnum = %d, level = %d\n", cnum, info_level));
+ int data_len, len;
+ SMB_STRUCT_STAT st;
+ char *vname = volume_label(SNUM(conn));
+ int snum = SNUM(conn);
+ char *fstype = lp_fstype(SNUM(conn));
- if(sys_stat(".",&st)!=0) {
+ DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
+
+ if(vfs_stat(conn,".",&st)!=0) {
DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
- return (ERROR(ERRSRV,ERRinvdevice));
+ return ERROR_DOS(ERRSRV,ERRinvdevice);
}
- pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024);
+ pdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if ( pdata == NULL ) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
switch (info_level)
- {
+ {
case 1:
- {
- int dfree,dsize,bsize;
- data_len = 18;
- sys_disk_free(".",&bsize,&dfree,&dsize);
- SIVAL(pdata,l1_idFileSystem,st.st_dev);
- SIVAL(pdata,l1_cSectorUnit,bsize/512);
- SIVAL(pdata,l1_cUnit,dsize);
- SIVAL(pdata,l1_cUnitAvail,dfree);
- SSVAL(pdata,l1_cbSector,512);
- DEBUG(5,("call_trans2qfsinfo : bsize=%d, id=%x, cSectorUnit=%d, cUnit=%d, cUnitAvail=%d, cbSector=%d\n",
- bsize, st.st_dev, bsize/512, dsize, dfree, 512));
- break;
- }
- case 2:
- {
- /* Return volume name */
- int volname_len = MIN(strlen(vname),11);
- data_len = l2_vol_szVolLabel + volname_len + 1;
- put_dos_date2(pdata,l2_vol_fdateCreation,st.st_ctime);
- SCVAL(pdata,l2_vol_cch,volname_len);
- StrnCpy(pdata+l2_vol_szVolLabel,vname,volname_len);
- DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",st.st_ctime,volname_len,
- pdata+l2_vol_szVolLabel));
+ {
+ SMB_BIG_UINT dfree,dsize,bsize;
+ data_len = 18;
+ conn->vfs_ops.disk_free(conn,".",False,&bsize,&dfree,&dsize);
+ SIVAL(pdata,l1_idFileSystem,st.st_dev);
+ SIVAL(pdata,l1_cSectorUnit,bsize/512);
+ SIVAL(pdata,l1_cUnit,dsize);
+ SIVAL(pdata,l1_cUnitAvail,dfree);
+ SSVAL(pdata,l1_cbSector,512);
+ DEBUG(5,("call_trans2qfsinfo : bsize=%u, id=%x, cSectorUnit=%u, cUnit=%u, cUnitAvail=%u, cbSector=%d\n",
+ (unsigned int)bsize, (unsigned int)st.st_dev, ((unsigned int)bsize)/512, (unsigned int)dsize,
+ (unsigned int)dfree, 512));
break;
}
+ case 2:
+ /* Return volume name */
+ /*
+ * Add volume serial number - hash of a combination of
+ * the called hostname and the service name.
+ */
+ SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(local_machine)<<16) );
+ len = srvstr_push(outbuf, pdata+l2_vol_szVolLabel, vname, -1,
+ STR_TERMINATE);
+ SCVAL(pdata,l2_vol_cch,len);
+ data_len = l2_vol_szVolLabel + len;
+ DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",
+ (unsigned)st.st_ctime, len, vname));
+ break;
+
case SMB_QUERY_FS_ATTRIBUTE_INFO:
- data_len = 12 + 2*strlen(FSTYPE_STRING);
- SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */
- SIVAL(pdata,4,128); /* Max filename component length */
- SIVAL(pdata,8,2*strlen(FSTYPE_STRING));
- PutUniCode(pdata+12,FSTYPE_STRING);
- break;
+ SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
+ (lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)); /* FS ATTRIBUTES */
+ SIVAL(pdata,4,255); /* Max filename component length */
+ /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it
+ and will think we can't do long filenames */
+ len = srvstr_push(outbuf, pdata+12, fstype, -1, 0);
+ SIVAL(pdata,8,len);
+ data_len = 12 + len;
+ break;
+
case SMB_QUERY_FS_LABEL_INFO:
- data_len = 4 + strlen(vname);
- SIVAL(pdata,0,strlen(vname));
- strcpy(pdata+4,vname);
- break;
+ len = srvstr_push(outbuf, pdata+4, vname, -1, STR_TERMINATE);
+ data_len = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
case SMB_QUERY_FS_VOLUME_INFO:
- data_len = 17 + strlen(vname);
- SIVAL(pdata,12,strlen(vname));
- strcpy(pdata+17,vname);
- break;
+ /*
+ * Add volume serial number - hash of a combination of
+ * the called hostname and the service name.
+ */
+ SIVAL(pdata,8,str_checksum(lp_servicename(snum)) ^
+ (str_checksum(local_machine)<<16));
+
+ len = srvstr_push(outbuf, pdata+18, vname, -1, STR_TERMINATE);
+ SIVAL(pdata,12,len);
+ data_len = 18+len;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n",
+ (int)strlen(vname),vname, lp_servicename(snum)));
+ break;
case SMB_QUERY_FS_SIZE_INFO:
- {
- int dfree,dsize,bsize;
- data_len = 24;
- sys_disk_free(".",&bsize,&dfree,&dsize);
- SIVAL(pdata,0,dsize);
- SIVAL(pdata,8,dfree);
- SIVAL(pdata,16,bsize/512);
- SIVAL(pdata,20,512);
- }
+ {
+ SMB_BIG_UINT dfree,dsize,bsize;
+ data_len = 24;
+ conn->vfs_ops.disk_free(conn,".",False,&bsize,&dfree,&dsize);
+ SBIG_UINT(pdata,0,dsize);
+ SBIG_UINT(pdata,8,dfree);
+ SIVAL(pdata,16,bsize/512);
+ SIVAL(pdata,20,512);
break;
+ }
case SMB_QUERY_FS_DEVICE_INFO:
data_len = 8;
SIVAL(pdata,0,0); /* dev type */
SIVAL(pdata,4,0); /* characteristics */
break;
- default:
- return(ERROR(ERRDOS,ERRunknownlevel));
- }
+ case SMB_MAC_QUERY_FS_INFO:
+ /*
+ * Thursby MAC extension... ONLY on NTFS filesystems
+ * once we do streams then we don't need this
+ */
+ if (strequal(lp_fstype(SNUM(conn)),"NTFS")) {
+ data_len = 88;
+ SIVAL(pdata,84,0x100); /* Don't support mac... */
+ break;
+ }
+ /* drop through */
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len);
- DEBUG(4,("%s %s info_level =%d\n",timestring(),smb_fn_name(CVAL(inbuf,smb_com)), info_level));
+ DEBUG( 4, ( "%s info_level = %d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)), info_level) );
return -1;
}
/****************************************************************************
- reply to a TRANS2_SETFSINFO (set filesystem info)
+ Reply to a TRANS2_SETFSINFO (set filesystem info).
****************************************************************************/
-static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsize,
- int cnum, char **pparams, char **ppdata)
+
+static int call_trans2setfsinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
+ char **pparams, char **ppdata)
{
/* Just say yes we did it - there is nothing that
can be set here so it doesn't matter. */
int outsize;
DEBUG(3,("call_trans2setfsinfo\n"));
- if (!CAN_WRITE(cnum))
- return(ERROR(ERRSRV,ERRaccess));
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
outsize = set_message(outbuf,10,0,True);
@@ -982,388 +1413,972 @@ static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsi
}
/****************************************************************************
- reply to a TRANS2_QFILEINFO (query file info by fileid)
+ * Utility function to set bad path error.
+ ****************************************************************************/
+
+NTSTATUS set_bad_path_error(int err, BOOL bad_path)
+{
+ if((err == ENOENT) && bad_path) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by
+ file name or file id).
****************************************************************************/
-static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length,
- int bufsize,int cnum,
+
+static int call_trans2qfilepathinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
char **pparams,char **ppdata,
int total_data)
{
- char *params = *pparams;
- char *pdata = *ppdata;
- uint16 tran_call = SVAL(inbuf, smb_setup0);
- uint16 info_level;
- int mode=0;
- int size=0;
- unsigned int data_size;
- struct stat sbuf;
- pstring fname1;
- char *fname;
- char *p;
- int l,pos;
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int mode=0;
+ SMB_OFF_T size=0;
+ SMB_OFF_T allocation_size=0;
+ unsigned int data_size;
+ SMB_STRUCT_STAT sbuf;
+ pstring fname, dos_fname;
+ char *base_name;
+ char *p;
+ SMB_OFF_T pos = 0;
+ BOOL bad_path = False;
+ BOOL delete_pending = False;
+ int len;
+ time_t c_time;
+
+ if (!params) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+ if (tran_call == TRANSACT2_QFILEINFO) {
+ files_struct *fsp = file_fsp(params,0);
+ info_level = SVAL(params,2);
+
+ DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level));
+
+ if(fsp && (fsp->is_directory || fsp->fd == -1)) {
+ /*
+ * This is actually a QFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn) ||
+ (!VALID_STAT(sbuf) && vfs_stat(conn,fname,&sbuf))) {
+ DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ if((errno == ENOENT) && bad_path) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+
+ delete_pending = fsp->directory_delete_on_close;
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ CHECK_FSP(fsp,conn);
+
+ pstrcpy(fname, fsp->fsp_name);
+ if (vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n", fsp->fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ if((pos = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_CUR)) == -1)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ delete_pending = fsp->delete_on_close;
+ }
+ } else {
+ /* qpathinfo */
+ info_level = SVAL(params,0);
- if (tran_call == TRANSACT2_QFILEINFO) {
- int16 fnum = SVAL(params,0);
- info_level = SVAL(params,2);
+ DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
+ srvstr_pull(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE);
- fname = Files[fnum].name;
- if (fstat(Files[fnum].fd,&sbuf) != 0) {
- DEBUG(3,("fstat of fnum %d failed (%s)\n",fnum, strerror(errno)));
- return(UNIXERROR(ERRDOS,ERRbadfid));
- }
- pos = lseek(Files[fnum].fd,0,SEEK_CUR);
- } else {
- /* qpathinfo */
- info_level = SVAL(params,0);
- fname = &fname1[0];
- strcpy(fname,&params[6]);
- unix_convert(fname,cnum);
- if (!check_name(fname,cnum) || sys_stat(fname,&sbuf)) {
- DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
- return(UNIXERROR(ERRDOS,ERRbadpath));
- }
- pos = 0;
- }
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn) ||
+ (!VALID_STAT(sbuf) && vfs_stat(conn,fname,&sbuf))) {
+ DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ if((errno == ENOENT) && bad_path) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+ }
- DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n",
- fname,info_level,tran_call,total_data));
+ DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n",
+ fname,info_level,tran_call,total_data));
+
+ p = strrchr_m(fname,'/');
+ if (!p)
+ base_name = fname;
+ else
+ base_name = p+1;
+
+ mode = dos_mode(conn,fname,&sbuf);
+ size = sbuf.st_size;
+ allocation_size = SMB_ROUNDUP_ALLOCATION(sbuf.st_size);
+
+ if (mode & aDIR)
+ size = 0;
+
+ params = Realloc(*pparams,2);
+ if (params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+ memset((char *)params,'\0',2);
+ data_size = max_data_bytes + 1024;
+ pdata = Realloc(*ppdata, data_size);
+ if ( pdata == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *ppdata = pdata;
+
+ if (total_data > 0 && IVAL(pdata,0) == total_data) {
+ /* uggh, EAs for OS2 */
+ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+ return ERROR_DOS(ERRDOS,ERReasnotsupported);
+ }
- p = strrchr(fname,'/');
- if (!p)
- p = fname;
- else
- p++;
- l = strlen(p);
- mode = dos_mode(cnum,fname,&sbuf);
- size = sbuf.st_size;
- if (mode & aDIR) size = 0;
-
- params = *pparams = Realloc(*pparams,2); bzero(params,2);
- data_size = 1024;
- pdata = *ppdata = Realloc(*ppdata, data_size);
+ memset((char *)pdata,'\0',data_size);
+
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ /* NT expects the name to be in an exact form */
+ if (strequal(fname,".")) {
+ pstrcpy(dos_fname, "\\");
+ } else {
+ snprintf(dos_fname, sizeof(dos_fname), "\\%s", fname);
+ string_replace( dos_fname, '/', '\\');
+ }
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ data_size = (info_level==1?22:26);
+ put_dos_date2(pdata,l1_fdateCreation,c_time);
+ put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
+ put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */
+ SIVAL(pdata,l1_cbFile,(uint32)size);
+ SIVAL(pdata,l1_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(pdata,l1_attrFile,mode);
+ SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */
+ break;
+
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ data_size = 24;
+ put_dos_date2(pdata,0,c_time);
+ put_dos_date2(pdata,4,sbuf.st_atime);
+ put_dos_date2(pdata,8,sbuf.st_mtime);
+ SIVAL(pdata,12,(uint32)size);
+ SIVAL(pdata,16,(uint32)allocation_size);
+ SIVAL(pdata,20,mode);
+ break;
+
+ case SMB_INFO_QUERY_ALL_EAS:
+ data_size = 4;
+ SIVAL(pdata,0,data_size);
+ break;
+
+ case 6:
+ return ERROR_DOS(ERRDOS,ERRbadfunc); /* os/2 needs this */
+
+ case SMB_FILE_BASIC_INFORMATION:
+ case SMB_QUERY_FILE_BASIC_INFO:
+
+ if (info_level == SMB_QUERY_FILE_BASIC_INFO)
+ data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */
+ else {
+ data_size = 40;
+ SIVAL(pdata,36,0);
+ }
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+
+ DEBUG(5,("SMB_QFBI - "));
+ {
+ time_t create_time = c_time;
+ DEBUG(5,("create: %s ", ctime(&create_time)));
+ }
+ DEBUG(5,("access: %s ", ctime(&sbuf.st_atime)));
+ DEBUG(5,("write: %s ", ctime(&sbuf.st_mtime)));
+ DEBUG(5,("change: %s ", ctime(&sbuf.st_mtime)));
+ DEBUG(5,("mode: %x\n", mode));
+
+ break;
+
+ case SMB_FILE_STANDARD_INFORMATION:
+ case SMB_QUERY_FILE_STANDARD_INFO:
+ data_size = 24;
+ /* Fake up allocation size. */
+ SOFF_T(pdata,0,allocation_size);
+ SOFF_T(pdata,8,size);
+ SIVAL(pdata,16,sbuf.st_nlink);
+ SCVAL(pdata,20,0);
+ SCVAL(pdata,21,(mode&aDIR)?1:0);
+ break;
+
+ case SMB_FILE_EA_INFORMATION:
+ case SMB_QUERY_FILE_EA_INFO:
+ data_size = 4;
+ break;
+
+ /* Get the 8.3 name - used if NT SMB was negotiated. */
+ case SMB_QUERY_FILE_ALT_NAME_INFO:
+ {
+ pstring short_name;
+
+ pstrcpy(short_name,base_name);
+ /* Mangle if not already 8.3 */
+ if(!mangle_is_8_3(short_name, True)) {
+ if(!mangle_map(short_name,True,True,SNUM(conn)))
+ *short_name = '\0';
+ }
+ len = srvstr_push(outbuf, pdata+4, short_name, -1, STR_TERMINATE|STR_UPPER);
+ data_size = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+ }
+
+ case SMB_QUERY_FILE_NAME_INFO:
+ /*
+ this must be *exactly* right for ACLs on mapped drives to work
+ */
+ len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
+ data_size = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+
+ case SMB_FILE_END_OF_FILE_INFORMATION:
+ case SMB_QUERY_FILE_END_OF_FILEINFO:
+ data_size = 8;
+ SOFF_T(pdata,0,size);
+ break;
+
+ case SMB_FILE_ALLOCATION_INFORMATION:
+ case SMB_QUERY_FILE_ALLOCATION_INFO:
+ data_size = 8;
+ SOFF_T(pdata,0,allocation_size);
+ break;
+
+ case SMB_QUERY_FILE_ALL_INFO:
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+ pdata += 40;
+ SOFF_T(pdata,0,allocation_size);
+ SOFF_T(pdata,8,size);
+ SIVAL(pdata,16,sbuf.st_nlink);
+ SCVAL(pdata,20,delete_pending);
+ SCVAL(pdata,21,(mode&aDIR)?1:0);
+ pdata += 24;
+ SINO_T(pdata,0,(SMB_INO_T)sbuf.st_ino);
+ pdata += 8; /* index number */
+ pdata += 4; /* EA info */
+ if (mode & aRONLY)
+ SIVAL(pdata,0,0xA9);
+ else
+ SIVAL(pdata,0,0xd01BF);
+ pdata += 4;
+ SOFF_T(pdata,0,pos); /* current offset */
+ pdata += 8;
+ SIVAL(pdata,0,mode); /* is this the right sort of mode info? */
+ pdata += 4;
+ pdata += 4; /* alignment */
+ len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_TERMINATE);
+ SIVAL(pdata,0,len);
+ pdata += 4 + len;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+ break;
+
+ case SMB_FILE_INTERNAL_INFORMATION:
+ /* This should be an index number - looks like dev/ino to me :-) */
+ SIVAL(pdata,0,sbuf.st_dev);
+ SIVAL(pdata,4,sbuf.st_ino);
+ data_size = 8;
+ break;
+
+ case SMB_FILE_ACCESS_INFORMATION:
+ SIVAL(pdata,0,0x12019F); /* ??? */
+ data_size = 4;
+ break;
+
+ case SMB_FILE_NAME_INFORMATION:
+ /* Pathname with leading '\'. */
+ {
+ size_t byte_len;
+ byte_len = dos_PutUniCode(pdata+4,dos_fname,max_data_bytes,False);
+ SIVAL(pdata,0,byte_len);
+ data_size = 4 + byte_len;
+ break;
+ }
+
+ case SMB_FILE_DISPOSITION_INFORMATION:
+ data_size = 1;
+ SCVAL(pdata,0,delete_pending);
+ break;
+
+ case SMB_FILE_POSITION_INFORMATION:
+ data_size = 8;
+ SOFF_T(pdata,0,pos);
+ break;
+
+ case SMB_FILE_MODE_INFORMATION:
+ SIVAL(pdata,0,mode);
+ data_size = 4;
+ break;
+
+ case SMB_FILE_ALIGNMENT_INFORMATION:
+ SIVAL(pdata,0,0); /* No alignment needed. */
+ data_size = 4;
+ break;
- if (total_data > 0 && IVAL(pdata,0) == total_data) {
- /* uggh, EAs for OS2 */
- DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
#if 0
- SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED);
- send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
- return(-1);
-#else
- return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED));
+ /* Not yet finished... JRA */
+ case 1018:
+ {
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+ SIVAL(pdata,36,0); /* ??? */
+ SIVAL(pdata,40,0x20); /* ??? */
+ SIVAL(pdata,44,0); /* ??? */
+ SOFF_T(pdata,48,size);
+ SIVAL(pdata,56,0x1); /* ??? */
+ SIVAL(pdata,60,0); /* ??? */
+ SIVAL(pdata,64,0); /* ??? */
+ SIVAL(pdata,68,length); /* Following string length in bytes. */
+ dos_PutUniCode(pdata+72,,False);
+ break;
+ }
#endif
- }
- bzero(pdata,data_size);
+ case SMB_FILE_ALTERNATE_NAME_INFORMATION:
+ /* Last component of pathname. */
+ {
+ size_t byte_len = dos_PutUniCode(pdata+4,fname,max_data_bytes,False);
+ SIVAL(pdata,0,byte_len);
+ data_size = 4 + byte_len;
+ break;
+ }
+
+ case SMB_FILE_STREAM_INFORMATION:
+ if (mode & aDIR) {
+ data_size = 0;
+ } else {
+ size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
+ SIVAL(pdata,0,0); /* ??? */
+ SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
+ SOFF_T(pdata,8,size);
+ SIVAL(pdata,16,allocation_size);
+ SIVAL(pdata,20,0); /* ??? */
+ data_size = 24 + byte_len;
+ }
+ break;
+
+ case SMB_FILE_COMPRESSION_INFORMATION:
+ SOFF_T(pdata,0,size);
+ SIVAL(pdata,8,0); /* ??? */
+ SIVAL(pdata,12,0); /* ??? */
+ data_size = 16;
+ break;
+
+ case SMB_FILE_NETWORK_OPEN_INFORMATION:
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,allocation_size);
+ SOFF_T(pdata,40,size);
+ SIVAL(pdata,48,mode);
+ SIVAL(pdata,52,0); /* ??? */
+ data_size = 56;
+ break;
+
+ case SMB_FILE_ATTRIBUTE_TAG_INFORMATION:
+ SIVAL(pdata,0,mode);
+ SIVAL(pdata,4,0);
+ data_size = 8;
+ break;
- switch (info_level)
- {
- case 1:
- case 2:
- data_size = (info_level==1?22:26);
- put_dos_date2(pdata,l1_fdateCreation,sbuf.st_ctime);
- put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
- put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime);
- SIVAL(pdata,l1_cbFile,size);
- SIVAL(pdata,l1_cbFileAlloc,ROUNDUP(size,1024));
- SSVAL(pdata,l1_attrFile,mode);
- SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */
- break;
+#if 0
+ /* NT4 server just returns "invalid query" to this - if we try to answer
+ it then NTws gets a BSOD! (tridge) */
+ case SMB_QUERY_FILE_STREAM_INFO:
+ SIVAL(pdata,0,pos);
+ SIVAL(pdata,4,(uint32)size);
+ SIVAL(pdata,12,(uint32)allocation_size);
+ len = srvstr_push(outbuf, pdata+24, fname, -1, STR_TERMINATE);
+ SIVAL(pdata,20,len);
+ data_size = 24 + len;
+ break;
+#endif
- case 3:
- data_size = 24;
- put_dos_date2(pdata,0,sbuf.st_ctime);
- put_dos_date2(pdata,4,sbuf.st_atime);
- put_dos_date2(pdata,8,sbuf.st_mtime);
- SIVAL(pdata,12,size);
- SIVAL(pdata,16,ROUNDUP(size,1024));
- SIVAL(pdata,20,mode);
- break;
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
- case 4:
- data_size = 4;
- SIVAL(pdata,0,data_size);
- break;
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, data_size);
- case 6:
- return(ERROR(ERRDOS,ERRbadfunc)); /* os/2 needs this */
+ return(-1);
+}
- case SMB_QUERY_FILE_BASIC_INFO:
- data_size = 36;
- put_long_date(pdata,sbuf.st_ctime);
- put_long_date(pdata+8,sbuf.st_atime);
- put_long_date(pdata+16,sbuf.st_mtime);
- put_long_date(pdata+24,sbuf.st_mtime);
- SIVAL(pdata,32,mode);
- break;
+/****************************************************************************
+ Deal with the internal needs of setting the delete on close flag. Note that
+ as the tdb locking is recursive, it is safe to call this from within
+ open_file_shared. JRA.
+****************************************************************************/
- case SMB_QUERY_FILE_STANDARD_INFO:
- data_size = 22;
- SIVAL(pdata,0,size);
- SIVAL(pdata,8,size);
- SIVAL(pdata,16,sbuf.st_nlink);
- CVAL(pdata,20) = 0;
- CVAL(pdata,21) = (mode&aDIR)?1:0;
- break;
+NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close)
+{
+ /*
+ * Only allow delete on close for writable shares.
+ */
+
+ if (delete_on_close && !CAN_WRITE(fsp->conn)) {
+ DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but write access denied on share.\n",
+ fsp->fsp_name ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /*
+ * Only allow delete on close for files/directories opened with delete intent.
+ */
+
+ if (delete_on_close && !(fsp->desired_access & DELETE_ACCESS)) {
+ DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but delete access denied.\n",
+ fsp->fsp_name ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
- case SMB_QUERY_FILE_EA_INFO:
- data_size = 4;
- break;
+ if(fsp->is_directory) {
+ fsp->directory_delete_on_close = delete_on_close;
+ DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, directory %s\n",
+ delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+ } else {
- case SMB_QUERY_FILE_NAME_INFO:
- case SMB_QUERY_FILE_ALT_NAME_INFO:
- data_size = 4 + l;
- SIVAL(pdata,0,l);
- strcpy(pdata+4,fname);
- break;
- case SMB_QUERY_FILE_ALLOCATION_INFO:
- case SMB_QUERY_FILE_END_OF_FILEINFO:
- data_size = 8;
- SIVAL(pdata,0,size);
- break;
+ files_struct *iterate_fsp;
- case SMB_QUERY_FILE_ALL_INFO:
- put_long_date(pdata,sbuf.st_ctime);
- put_long_date(pdata+8,sbuf.st_atime);
- put_long_date(pdata+16,sbuf.st_mtime);
- put_long_date(pdata+24,sbuf.st_mtime);
- SIVAL(pdata,32,mode);
- pdata += 40;
- SIVAL(pdata,0,size);
- SIVAL(pdata,8,size);
- SIVAL(pdata,16,sbuf.st_nlink);
- CVAL(pdata,20) = 0;
- CVAL(pdata,21) = (mode&aDIR)?1:0;
- pdata += 24;
- pdata += 8; /* index number */
- pdata += 4; /* EA info */
- if (mode & aRONLY)
- SIVAL(pdata,0,0xA9);
- else
- SIVAL(pdata,0,0xd01BF);
- pdata += 4;
- SIVAL(pdata,0,pos); /* current offset */
- pdata += 8;
- SIVAL(pdata,0,mode); /* is this the right sort of mode info? */
- pdata += 4;
- pdata += 4; /* alignment */
- SIVAL(pdata,0,l);
- strcpy(pdata+4,fname);
- pdata += 4 + l;
- data_size = PTR_DIFF(pdata,(*ppdata));
- break;
+ /*
+ * Modify the share mode entry for all files open
+ * on this device and inode to tell other smbds we have
+ * changed the delete on close flag. This will be noticed
+ * in the close code, the last closer will delete the file
+ * if flag is set.
+ */
- case SMB_QUERY_FILE_STREAM_INFO:
- data_size = 24 + l;
- SIVAL(pdata,0,pos);
- SIVAL(pdata,4,size);
- SIVAL(pdata,12,size);
- SIVAL(pdata,20,l);
- strcpy(pdata+24,fname);
- break;
- default:
- return(ERROR(ERRDOS,ERRunknownlevel));
- }
+ DEBUG(10,("set_delete_on_close_internal: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
- send_trans2_replies( outbuf, bufsize, params, 2, *ppdata, data_size);
+ if (lock_share_entry_fsp(fsp) == False)
+ return NT_STATUS_ACCESS_DENIED;
- return(-1);
+ if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) {
+ DEBUG(0,("set_delete_on_close_internal: failed to change delete on close flag for file %s\n",
+ fsp->fsp_name ));
+ unlock_share_entry_fsp(fsp);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * Release the lock.
+ */
+
+ unlock_share_entry_fsp(fsp);
+
+ /*
+ * Go through all files we have open on the same device and
+ * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG.
+ * Other smbd's that have this file open will look in the share_mode on close.
+ * take care of this (rare) case in close_file(). See the comment there.
+ * NB. JRA. We don't really need to do this anymore - all should be taken
+ * care of in the share_mode changes in the tdb.
+ */
+
+ for(iterate_fsp = file_find_di_first(fsp->dev, fsp->inode);
+ iterate_fsp; iterate_fsp = file_find_di_next(iterate_fsp))
+ fsp->delete_on_close = delete_on_close;
+
+ /*
+ * Set the delete on close flag in the fsp.
+ */
+ fsp->delete_on_close = delete_on_close;
+
+ DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+
+ }
+
+ return NT_STATUS_OK;
}
/****************************************************************************
- reply to a TRANS2_SETFILEINFO (set file info by fileid)
+ Reply to a TRANS2_SETFILEINFO (set file info by fileid).
****************************************************************************/
-static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length,
- int bufsize, int cnum, char **pparams,
+
+static int call_trans2setfilepathinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize, char **pparams,
char **ppdata, int total_data)
{
- char *params = *pparams;
- char *pdata = *ppdata;
- uint16 tran_call = SVAL(inbuf, smb_setup0);
- uint16 info_level;
- int mode=0;
- int size=0;
- struct utimbuf tvs;
- struct stat st;
- pstring fname1;
- char *fname;
- int fd = -1;
-
- if (!CAN_WRITE(cnum))
- return(ERROR(ERRSRV,ERRaccess));
-
- if (tran_call == TRANSACT2_SETFILEINFO) {
- int16 fnum = SVAL(params,0);
- info_level = SVAL(params,2);
-
- CHECK_FNUM(fnum,cnum);
- CHECK_ERROR(fnum);
-
- fname = Files[fnum].name;
- fd = Files[fnum].fd;
-
- if(fstat(fd,&st)!=0) {
- DEBUG(3,("fstat of %s failed (%s)\n", fname, strerror(errno)));
- return(ERROR(ERRDOS,ERRbadpath));
- }
- } else {
- /* set path info */
- info_level = SVAL(params,0);
- fname = fname1;
- strcpy(fname,&params[6]);
- unix_convert(fname,cnum);
- if(!check_name(fname, cnum))
- return(ERROR(ERRDOS,ERRbadpath));
-
- if(sys_stat(fname,&st)!=0) {
- DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno)));
- return(ERROR(ERRDOS,ERRbadpath));
- }
- }
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int mode=0;
+ SMB_OFF_T size=0;
+ struct utimbuf tvs;
+ SMB_STRUCT_STAT sbuf;
+ pstring fname;
+ int fd = -1;
+ BOOL bad_path = False;
+ files_struct *fsp = NULL;
+
+ if (tran_call == TRANSACT2_SETFILEINFO) {
+ fsp = file_fsp(params,0);
+ info_level = SVAL(params,2);
+
+ if(fsp && (fsp->is_directory || fsp->fd == -1)) {
+ /*
+ * This is actually a SETFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) {
+ DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ if((errno == ENOENT) && bad_path) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+ } else if (fsp && fsp->print_file) {
+ /*
+ * Doing a DELETE_ON_CLOSE should cancel a print job.
+ */
+ if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) {
+ fsp->share_mode = FILE_DELETE_ON_CLOSE;
+
+ DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name ));
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ CHECK_FSP(fsp,conn);
+
+ pstrcpy(fname, fsp->fsp_name);
+ fd = fsp->fd;
+
+ if (vfs_fstat(fsp,fd,&sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ }
+ } else {
+ /* set path info */
+ info_level = SVAL(params,0);
+ srvstr_pull(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if(!check_name(fname, conn)) {
+ if((errno == ENOENT) && bad_path) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+
+ if(!VALID_STAT(sbuf)) {
+ DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno)));
+ if((errno == ENOENT) && bad_path) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+ }
- DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n",
- tran_call,fname,info_level,total_data));
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
- /* Realloc the parameter and data sizes */
- params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0);
- if(params == NULL)
- return(ERROR(ERRDOS,ERRnomem));
+ DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n",
+ tran_call,fname,info_level,total_data));
- size = st.st_size;
- tvs.modtime = st.st_mtime;
- tvs.actime = st.st_atime;
- mode = dos_mode(cnum,fname,&st);
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,2);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
- if (total_data > 0 && IVAL(pdata,0) == total_data) {
- /* uggh, EAs for OS2 */
- DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
- SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED);
+ SSVAL(params,0,0);
- send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
-
- return(-1);
- }
+ size = sbuf.st_size;
+ tvs.modtime = sbuf.st_mtime;
+ tvs.actime = sbuf.st_atime;
+ mode = dos_mode(conn,fname,&sbuf);
- switch (info_level)
- {
- case 1:
- tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
- tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
- mode = SVAL(pdata,l1_attrFile);
- size = IVAL(pdata,l1_cbFile);
- break;
+ if (total_data > 4 && IVAL(pdata,0) == total_data) {
+ /* uggh, EAs for OS2 */
+ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+ return ERROR_DOS(ERRDOS,ERReasnotsupported);
+ }
- case 2:
- tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
- tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
- mode = SVAL(pdata,l1_attrFile);
- size = IVAL(pdata,l1_cbFile);
- break;
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ {
+ /* access time */
+ tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+
+ /* write time */
+ tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+
+ mode = SVAL(pdata,l1_attrFile);
+ size = IVAL(pdata,l1_cbFile);
+ break;
+ }
+
+ /* XXXX um, i don't think this is right.
+ it's also not in the cifs6.txt spec.
+ */
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ mode = IVAL(pdata,24);
+ break;
+
+ /* XXXX nor this. not in cifs6.txt, either. */
+ case SMB_INFO_QUERY_ALL_EAS:
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ mode = IVAL(pdata,24);
+ break;
+
+ case SMB_SET_FILE_BASIC_INFO:
+ case SMB_FILE_BASIC_INFORMATION:
+ {
+ /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */
+ time_t write_time;
+ time_t changed_time;
+
+ /* Ignore create time at offset pdata. */
+
+ /* access time */
+ tvs.actime = interpret_long_date(pdata+8);
+
+ write_time = interpret_long_date(pdata+16);
+ changed_time = interpret_long_date(pdata+24);
+
+ tvs.modtime = MIN(write_time, changed_time);
+
+ /* Prefer a defined time to an undefined one. */
+ if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+ tvs.modtime = (write_time == (time_t)0 || write_time == (time_t)-1
+ ? changed_time : write_time);
+
+ /* attributes */
+ mode = IVAL(pdata,32);
+ break;
+ }
+
+ case SMB_FILE_ALLOCATION_INFORMATION:
+ case SMB_SET_FILE_ALLOCATION_INFO:
+ {
+ int ret = -1;
+ SMB_OFF_T allocation_size = IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ allocation_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);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n",
+ fname, (double)allocation_size ));
+
+ if(allocation_size != sbuf.st_size) {
+ SMB_STRUCT_STAT new_sbuf;
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new allocation size to %.0f\n",
+ fname, (double)allocation_size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared1(conn, fname, &sbuf,FILE_WRITE_DATA,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ 0, 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_allocate_file_space(new_fsp, allocation_size);
+ if (vfs_fstat(new_fsp,new_fsp->fd,&new_sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n",new_fsp->fnum, strerror(errno)));
+ ret = -1;
+ }
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_allocate_file_space(fsp, allocation_size);
+ if (vfs_fstat(fsp,fd,&new_sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
+ ret = -1;
+ }
+ }
+ if (ret == -1)
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+
+ /* Allocate can trucate size... */
+ size = new_sbuf.st_size;
+ }
+
+ break;
+ }
+
+ case SMB_FILE_END_OF_FILE_INFORMATION:
+ case SMB_SET_FILE_END_OF_FILE_INFO:
+ {
+ size = IVAL(pdata,0);
+#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);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size ));
+ break;
+ }
+
+ case SMB_FILE_DISPOSITION_INFORMATION:
+ case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
+ {
+ BOOL delete_on_close = (CVAL(pdata,0) ? True : False);
+ NTSTATUS status;
+
+ if (tran_call != TRANSACT2_SETFILEINFO)
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+
+ if (fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+
+ status = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
- case 3:
- tvs.actime = make_unix_date2(pdata+8);
- tvs.modtime = make_unix_date2(pdata+12);
- size = IVAL(pdata,16);
- mode = IVAL(pdata,24);
- break;
+ break;
+ }
- case 4:
- tvs.actime = make_unix_date2(pdata+8);
- tvs.modtime = make_unix_date2(pdata+12);
- size = IVAL(pdata,16);
- mode = IVAL(pdata,24);
- break;
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
- case SMB_SET_FILE_BASIC_INFO:
- pdata += 8; /* create time */
- tvs.actime = interpret_long_date(pdata); pdata += 8;
- tvs.modtime=MAX(interpret_long_date(pdata),interpret_long_date(pdata+8));
- pdata += 16;
- mode = IVAL(pdata,0);
- break;
+ /* get some defaults (no modifications) if any info is zero or -1. */
+ if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1)
+ tvs.actime = sbuf.st_atime;
+
+ if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+ tvs.modtime = sbuf.st_mtime;
+
+ DEBUG(6,("actime: %s " , ctime(&tvs.actime)));
+ DEBUG(6,("modtime: %s ", ctime(&tvs.modtime)));
+ DEBUG(6,("size: %.0f ", (double)size));
+ DEBUG(6,("mode: %x\n" , mode));
+
+ if (S_ISDIR(sbuf.st_mode))
+ mode |= aDIR;
+ else
+ mode &= ~aDIR;
+
+ if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) ||
+ (info_level == SMB_SET_FILE_ALLOCATION_INFO) ||
+ (info_level == SMB_FILE_ALLOCATION_INFORMATION) ||
+ (info_level == SMB_FILE_END_OF_FILE_INFORMATION))) {
+
+ /*
+ * Only do this test if we are not explicitly
+ * changing the size of a file.
+ */
+ if (!size)
+ size = sbuf.st_size;
+ }
- case SMB_SET_FILE_END_OF_FILE_INFO:
- if (IVAL(pdata,4) != 0) /* more than 32 bits? */
- return(ERROR(ERRDOS,ERRunknownlevel));
- size = IVAL(pdata,0);
- break;
+ /*
+ * Try and set the times, size and mode of this file -
+ * if they are different from the current values
+ */
+ if (sbuf.st_mtime != tvs.modtime || sbuf.st_atime != tvs.actime) {
+ if(fsp != NULL) {
+ /*
+ * This was a setfileinfo on an open file.
+ * NT does this a lot. It's actually pointless
+ * setting the time here, as it will be overwritten
+ * on the next write, so we save the request
+ * away and will set it on file code. JRA.
+ */
+
+ if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) {
+ DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", ctime(&tvs.modtime) ));
+ fsp->pending_modtime = tvs.modtime;
+ }
+
+ } else {
+
+ DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n"));
+
+ if(file_utime(conn, fname, &tvs)!=0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
- case SMB_SET_FILE_DISPOSITION_INFO: /* not supported yet */
- case SMB_SET_FILE_ALLOCATION_INFO: /* not supported yet */
- default:
- return(ERROR(ERRDOS,ERRunknownlevel));
- }
+ /* check the mode isn't different, before changing it */
+ if ((mode != 0) && (mode != dos_mode(conn, fname, &sbuf))) {
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", fname, mode ));
- if (!tvs.actime) tvs.actime = st.st_atime;
- if (!tvs.modtime) tvs.modtime = st.st_mtime;
- if (!size) size = st.st_size;
+ if(file_chmod(conn, fname, mode, NULL)) {
+ DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
- /* Try and set the times, size and mode of this file - if they are different
- from the current values */
- if(st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) {
- if(sys_utime(fname, &tvs)!=0)
- return(ERROR(ERRDOS,ERRnoaccess));
- }
- if(mode != dos_mode(cnum,fname,&st) && dos_chmod(cnum,fname,mode,NULL)) {
- DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
- return(ERROR(ERRDOS,ERRnoaccess));
- }
- if(size != st.st_size) {
- if (fd == -1) {
- fd = sys_open(fname,O_RDWR,0);
- if (fd == -1)
- return(ERROR(ERRDOS,ERRbadpath));
- set_filelen(fd, size);
- close(fd);
- } else {
- set_filelen(fd, size);
- }
- }
+ if(size != sbuf.st_size) {
- SSVAL(params,0,0);
+ int ret;
- send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n",
+ fname, (double)size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared(conn, fname, &sbuf,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ 0, 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_set_filelen(new_fsp, size);
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_set_filelen(fsp, size);
+ }
+
+ if (ret == -1)
+ return (UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ SSVAL(params,0,0);
+
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
- return(-1);
+ return(-1);
}
/****************************************************************************
- reply to a TRANS2_MKDIR (make directory with extended attributes).
+ Reply to a TRANS2_MKDIR (make directory with extended attributes).
****************************************************************************/
-static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize,
- int cnum, char **pparams, char **ppdata)
+
+static int call_trans2mkdir(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, char **ppdata)
{
char *params = *pparams;
pstring directory;
int ret = -1;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
- if (!CAN_WRITE(cnum))
- return(ERROR(ERRSRV,ERRaccess));
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
- strcpy(directory, &params[4]);
+ srvstr_pull(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE);
DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
- unix_convert(directory,cnum);
- if (check_name(directory,cnum))
- ret = sys_mkdir(directory,unix_mode(cnum,aDIR));
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if (check_name(directory,conn))
+ ret = vfs_mkdir(conn,directory,unix_mode(conn,aDIR,directory));
if(ret < 0)
{
DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno)));
+ if((errno == ENOENT) && bad_path)
+ {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
return(UNIXERROR(ERRDOS,ERRnoaccess));
}
/* Realloc the parameter and data sizes */
- params = *pparams = Realloc(*pparams,2);
- if(params == NULL)
- return(ERROR(ERRDOS,ERRnomem));
+ params = Realloc(*pparams,2);
+ if(params == NULL) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *pparams = params;
SSVAL(params,0,0);
@@ -1373,11 +2388,14 @@ static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize,
}
/****************************************************************************
- reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes)
- We don't actually do this - we just send a null response.
+ Reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes).
+ We don't actually do this - we just send a null response.
****************************************************************************/
-static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int bufsize,
- int cnum, char **pparams, char **ppdata)
+
+static int call_trans2findnotifyfirst(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **pparams, char **ppdata)
{
static uint16 fnf_handle = 257;
char *params = *pparams;
@@ -1391,13 +2409,15 @@ static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int
case 2:
break;
default:
- return(ERROR(ERRDOS,ERRunknownlevel));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
}
/* Realloc the parameter and data sizes */
- params = *pparams = Realloc(*pparams,6);
- if(params == NULL)
- return(ERROR(ERRDOS,ERRnomem));
+ params = Realloc(*pparams,6);
+ if(params == NULL) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *pparams = params;
SSVAL(params,0,fnf_handle);
SSVAL(params,2,0); /* No changes */
@@ -1414,20 +2434,25 @@ static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int
}
/****************************************************************************
- reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for
- changes). Currently this does nothing.
+ Reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for
+ changes). Currently this does nothing.
****************************************************************************/
-static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int bufsize,
- int cnum, char **pparams, char **ppdata)
+
+static int call_trans2findnotifynext(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **pparams, char **ppdata)
{
char *params = *pparams;
DEBUG(3,("call_trans2findnotifynext\n"));
/* Realloc the parameter and data sizes */
- params = *pparams = Realloc(*pparams,4);
- if(params == NULL)
- return(ERROR(ERRDOS,ERRnomem));
+ params = Realloc(*pparams,4);
+ if(params == NULL) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *pparams = params;
SSVAL(params,0,0); /* No changes */
SSVAL(params,2,0); /* No EA errors */
@@ -1438,209 +2463,388 @@ static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int
}
/****************************************************************************
- reply to a SMBfindclose (stop trans2 directory search)
+ Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>.
****************************************************************************/
-int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize)
+
+static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf,
+ char* outbuf, int length, int bufsize,
+ char** pparams, char** ppdata)
{
- int cnum;
- int outsize = 0;
- uint16 dptr_num=SVAL(inbuf,smb_vwv0);
+ char *params = *pparams;
+ pstring pathname;
+ int reply_size = 0;
+ int max_referral_level = SVAL(params,0);
- cnum = SVAL(inbuf,smb_tid);
- DEBUG(3,("reply_findclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num));
+ DEBUG(10,("call_trans2getdfsreferral\n"));
- dptr_close(dptr_num);
+ if(!lp_host_msdfs())
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
- outsize = set_message(outbuf,0,0,True);
+ srvstr_pull(inbuf, pathname, &params[2], sizeof(pathname), -1, STR_TERMINATE);
- DEBUG(3,("%s SMBfindclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num));
+ if((reply_size = setup_dfs_referral(pathname,max_referral_level,ppdata)) < 0)
+ return ERROR_DOS(ERRDOS,ERRbadfile);
+
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES);
+ send_trans2_replies(outbuf,bufsize,0,0,*ppdata,reply_size);
- return(outsize);
+ return(-1);
}
+#define LMCAT_SPL 0x53
+#define LMFUNC_GETJOBID 0x60
+
/****************************************************************************
- reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search)
+ Reply to a TRANS2_IOCTL - used for OS/2 printing.
****************************************************************************/
-int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize)
+
+static int call_trans2ioctl(connection_struct *conn, char* inbuf,
+ char* outbuf, int length, int bufsize,
+ char** pparams, char** ppdata)
{
- int cnum;
- int outsize = 0;
- int dptr_num= -1;
+ char *pdata = *ppdata;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv15);
- cnum = SVAL(inbuf,smb_tid);
- dptr_num = SVAL(inbuf,smb_vwv0);
+ if ((SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) &&
+ (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) {
+ pdata = Realloc(*ppdata, 32);
+ if(pdata == NULL) {
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+ *ppdata = pdata;
- DEBUG(3,("reply_findnclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num));
+ /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2
+ CAN ACCEPT THIS IN UNICODE. JRA. */
- /* We never give out valid handles for a
- findnotifyfirst - so any dptr_num is ok here.
- Just ignore it. */
+ SSVAL(pdata,0,fsp->print_jobid); /* Job number */
+ srvstr_push( outbuf, pdata + 2, global_myname, 15, STR_ASCII|STR_TERMINATE); /* Our NetBIOS name */
+ srvstr_push( outbuf, pdata+18, lp_servicename(SNUM(conn)), 13, STR_ASCII|STR_TERMINATE); /* Service name */
+ send_trans2_replies(outbuf,bufsize,*pparams,0,*ppdata,32);
+ return(-1);
+ } else {
+ DEBUG(2,("Unknown TRANS2_IOCTL\n"));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+}
- outsize = set_message(outbuf,0,0,True);
+/****************************************************************************
+ Reply to a SMBfindclose (stop trans2 directory search).
+****************************************************************************/
+
+int reply_findclose(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int dptr_num=SVALS(inbuf,smb_vwv0);
+ START_PROFILE(SMBfindclose);
+
+ DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num));
+
+ dptr_close(&dptr_num);
+
+ outsize = set_message(outbuf,0,0,True);
- DEBUG(3,("%s SMB_findnclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num));
+ DEBUG(3,("SMBfindclose dptr_num = %d\n", dptr_num));
- return(outsize);
+ END_PROFILE(SMBfindclose);
+ return(outsize);
}
+/****************************************************************************
+ Reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search).
+****************************************************************************/
+
+int reply_findnclose(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int dptr_num= -1;
+ START_PROFILE(SMBfindnclose);
+
+ dptr_num = SVAL(inbuf,smb_vwv0);
+
+ DEBUG(3,("reply_findnclose, dptr_num = %d\n", dptr_num));
+
+ /* We never give out valid handles for a
+ findnotifyfirst - so any dptr_num is ok here.
+ Just ignore it. */
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMB_findnclose dptr_num = %d\n", dptr_num));
+
+ END_PROFILE(SMBfindnclose);
+ return(outsize);
+}
/****************************************************************************
- reply to a SMBtranss2 - just ignore it!
+ Reply to a SMBtranss2 - just ignore it!
****************************************************************************/
-int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize)
+
+int reply_transs2(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
{
- DEBUG(4,("Ignoring transs2 of length %d\n",length));
- return(-1);
+ START_PROFILE(SMBtranss2);
+ DEBUG(4,("Ignoring transs2 of length %d\n",length));
+ END_PROFILE(SMBtranss2);
+ return(-1);
}
/****************************************************************************
- reply to a SMBtrans2
+ Reply to a SMBtrans2.
****************************************************************************/
-int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize)
+
+int reply_trans2(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
{
- int outsize = 0;
- int cnum = SVAL(inbuf,smb_tid);
- unsigned int total_params = SVAL(inbuf, smb_tpscnt);
- unsigned int total_data =SVAL(inbuf, smb_tdscnt);
+ int outsize = 0;
+ unsigned int total_params = SVAL(inbuf, smb_tpscnt);
+ unsigned int total_data =SVAL(inbuf, smb_tdscnt);
#if 0
- unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
- unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
- 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);
+ unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
+ unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
+ 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;
- int num_params, num_params_sofar, num_data, num_data_sofar;
-
- outsize = set_message(outbuf,0,0,True);
+ unsigned int suwcnt = SVAL(inbuf, smb_suwcnt);
+ unsigned int tran_call = SVAL(inbuf, smb_setup0);
+ char *params = NULL, *data = NULL;
+ int num_params, num_params_sofar, num_data, num_data_sofar;
+ START_PROFILE(SMBtrans2);
+
+ if(global_oplock_break && (tran_call == TRANSACT2_OPEN)) {
+ /* Queue this open message as we are the process of an
+ * oplock break. */
+
+ DEBUG(2,("reply_trans2: queueing message trans2open due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ END_PROFILE(SMBtrans2);
+ return -1;
+ }
+
+ if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN)
+ && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) {
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
- /* All trans2 messages we handle have smb_sucnt == 1 - ensure this
- is so as a sanity check */
- if(suwcnt != 1 )
- {
- DEBUG(2,("Invalid smb_sucnt in trans2 call\n"));
- return(ERROR(ERRSRV,ERRerror));
- }
+ 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(%d)\n",suwcnt));
+ DEBUG(2,("Transaction is %d\n",tran_call));
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+ }
- /* Allocate the space for the maximum needed parameters and data */
- if (total_params > 0)
- params = (char *)malloc(total_params);
- if (total_data > 0)
- data = (char *)malloc(total_data);
+ /* Allocate the space for the maximum needed parameters and data */
+ if (total_params > 0)
+ params = (char *)malloc(total_params);
+ if (total_data > 0)
+ data = (char *)malloc(total_data);
- if ((total_params && !params) || (total_data && !data))
- {
- DEBUG(2,("Out of memory in reply_trans2\n"));
- return(ERROR(ERRDOS,ERRnomem));
- }
-
- /* 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);
-
- memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params);
- memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_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_DOS(ERRDOS,ERRnomem);
+ }
- 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);
- send_smb(Client,outbuf);
-
- while( num_data_sofar < total_data || num_params_sofar < total_params)
- {
- receive_smb(Client,inbuf, 0);
-
- /* Ensure this is still a trans2 packet (sanity check) */
- if(CVAL(inbuf, smb_com) != SMBtranss2)
- {
- outsize = set_message(outbuf,0,0,True);
- DEBUG(2,("Invalid secondary trans2 packet\n"));
- free(params);
- free(data);
- return(ERROR(ERRSRV,ERRerror));
- }
+ /* 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)
+ memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params);
+ if(data)
+ memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_data);
+
+ 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);
+ 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;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ 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" ));
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
- /* Revise total_params and total_data in case they have changed downwards */
- total_params = SVAL(inbuf, smb_tpscnt);
- total_data = SVAL(inbuf, smb_tdscnt);
- num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt));
- num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt));
- memcpy( &params[ SVAL(inbuf, smb_spsdisp)],
- smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params);
- memcpy( &data[SVAL(inbuf, smb_sdsdisp)],
- smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data);
+ /* Revise total_params and total_data in case
+ they have changed downwards */
+ total_params = SVAL(inbuf, smb_tpscnt);
+ total_data = SVAL(inbuf, smb_tdscnt);
+ num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt));
+ num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt));
+ if (num_params_sofar > total_params || num_data_sofar > total_data)
+ exit_server("data overflow in trans2");
+
+ memcpy( &params[ SVAL(inbuf, smb_spsdisp)],
+ smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params);
+ memcpy( &data[SVAL(inbuf, smb_sdsdisp)],
+ smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data);
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1) {
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */
}
- }
-
- if (Protocol >= PROTOCOL_NT1) {
- uint16 flg2 = SVAL(outbuf,smb_flg2);
- SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
- }
-
- /* Now we must call the relevant TRANS2 function */
- switch(tran_call)
- {
- case TRANSACT2_OPEN:
- outsize = call_trans2open(inbuf, outbuf, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_FINDFIRST:
- outsize = call_trans2findfirst(inbuf, outbuf, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_FINDNEXT:
- outsize = call_trans2findnext(inbuf, outbuf, length, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_QFSINFO:
- outsize = call_trans2qfsinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_SETFSINFO:
- outsize = call_trans2setfsinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_QPATHINFO:
- case TRANSACT2_QFILEINFO:
- outsize = call_trans2qfilepathinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data, total_data);
- break;
- case TRANSACT2_SETPATHINFO:
- case TRANSACT2_SETFILEINFO:
- outsize = call_trans2setfilepathinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data, total_data);
- break;
- case TRANSACT2_FINDNOTIFYFIRST:
- outsize = call_trans2findnotifyfirst(inbuf, outbuf, length, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_FINDNOTIFYNEXT:
- outsize = call_trans2findnotifynext(inbuf, outbuf, length, bufsize, cnum, &params, &data);
- break;
- case TRANSACT2_MKDIR:
- outsize = call_trans2mkdir(inbuf, outbuf, length, bufsize, cnum, &params, &data);
- break;
- default:
- /* Error in request */
- DEBUG(2,("%s Unknown request %d in trans2 call\n",timestring(), tran_call));
- if(params)
- free(params);
- if(data)
- free(data);
- return (ERROR(ERRSRV,ERRerror));
- }
- /* 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.
- */
-
- if(params)
- free(params);
- if(data)
- free(data);
- return outsize; /* If a correct response was needed the call_trans2xxx
- calls have already sent it. If outsize != -1 then it is
- returning an error packet. */
+ /* Now we must call the relevant TRANS2 function */
+ switch(tran_call) {
+ case TRANSACT2_OPEN:
+ START_PROFILE_NESTED(Trans2_open);
+ outsize = call_trans2open(conn,
+ inbuf, outbuf, bufsize,
+ &params, &data);
+ END_PROFILE_NESTED(Trans2_open);
+ break;
+
+ case TRANSACT2_FINDFIRST:
+ START_PROFILE_NESTED(Trans2_findfirst);
+ outsize = call_trans2findfirst(conn, inbuf, outbuf,
+ bufsize, &params, &data);
+ END_PROFILE_NESTED(Trans2_findfirst);
+ break;
+
+ case TRANSACT2_FINDNEXT:
+ START_PROFILE_NESTED(Trans2_findnext);
+ outsize = call_trans2findnext(conn, inbuf, outbuf,
+ length, bufsize,
+ &params, &data);
+ END_PROFILE_NESTED(Trans2_findnext);
+ break;
+
+ case TRANSACT2_QFSINFO:
+ START_PROFILE_NESTED(Trans2_qfsinfo);
+ outsize = call_trans2qfsinfo(conn, inbuf, outbuf,
+ length, bufsize, &params,
+ &data);
+ END_PROFILE_NESTED(Trans2_qfsinfo);
+ break;
+
+ case TRANSACT2_SETFSINFO:
+ START_PROFILE_NESTED(Trans2_setfsinfo);
+ outsize = call_trans2setfsinfo(conn, inbuf, outbuf,
+ length, bufsize,
+ &params, &data);
+ 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,
+ &params, &data, total_data);
+ 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,
+ &params, &data,
+ total_data);
+ END_PROFILE_NESTED(Trans2_setpathinfo);
+ break;
+
+ case TRANSACT2_FINDNOTIFYFIRST:
+ START_PROFILE_NESTED(Trans2_findnotifyfirst);
+ outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf,
+ length, bufsize,
+ &params, &data);
+ END_PROFILE_NESTED(Trans2_findnotifyfirst);
+ break;
+
+ case TRANSACT2_FINDNOTIFYNEXT:
+ START_PROFILE_NESTED(Trans2_findnotifynext);
+ outsize = call_trans2findnotifynext(conn, inbuf, outbuf,
+ length, bufsize,
+ &params, &data);
+ END_PROFILE_NESTED(Trans2_findnotifynext);
+ break;
+ case TRANSACT2_MKDIR:
+ START_PROFILE_NESTED(Trans2_mkdir);
+ outsize = call_trans2mkdir(conn, inbuf, outbuf, length,
+ bufsize, &params, &data);
+ 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, &params, &data);
+ END_PROFILE_NESTED(Trans2_get_dfs_referral);
+ break;
+ case TRANSACT2_IOCTL:
+ START_PROFILE_NESTED(Trans2_ioctl);
+ outsize = call_trans2ioctl(conn,inbuf,outbuf,length,
+ bufsize,&params,&data);
+ 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);
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* 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.
+ */
+
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ 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 */
}
diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c
new file mode 100644
index 0000000000..864d3d6c66
--- /dev/null
+++ b/source3/smbd/uid.c
@@ -0,0 +1,709 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* what user is current? */
+extern struct current_user current_user;
+
+/****************************************************************************
+ Become the guest user without changing the security context stack.
+****************************************************************************/
+
+BOOL change_to_guest(void)
+{
+ static struct passwd *pass=NULL;
+
+ if (!pass) {
+ /* Don't need to free() this as its stored in a static */
+ pass = getpwnam_alloc(lp_guestaccount());
+ if (!pass)
+ return(False);
+ }
+
+#ifdef AIX
+ /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before
+ setting IDs */
+ initgroups(pass->pw_name, pass->pw_gid);
+#endif
+
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+
+ return True;
+}
+
+/*******************************************************************
+ Check if a username is OK.
+********************************************************************/
+
+static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
+{
+ int i;
+ for (i=0;i<conn->uid_cache.entries;i++)
+ if (conn->uid_cache.list[i] == vuser->uid)
+ return(True);
+
+ if (!user_ok(vuser->user.unix_name,snum))
+ return(False);
+
+ i = conn->uid_cache.entries % UID_CACHE_SIZE;
+ conn->uid_cache.list[i] = vuser->uid;
+
+ if (conn->uid_cache.entries < UID_CACHE_SIZE)
+ conn->uid_cache.entries++;
+
+ return(True);
+}
+
+/****************************************************************************
+ Become the user of a connection number without changing the security context
+ stack, but modify the currnet_user entries.
+****************************************************************************/
+
+BOOL change_to_user(connection_struct *conn, uint16 vuid)
+{
+ user_struct *vuser = get_valid_user_struct(vuid);
+ int snum;
+ gid_t gid;
+ uid_t uid;
+ char group_c;
+ BOOL must_free_token = False;
+ NT_USER_TOKEN *token = NULL;
+
+ if (!conn) {
+ DEBUG(2,("change_to_user: Connection not open\n"));
+ return(False);
+ }
+
+ /*
+ * We need a separate check in security=share mode due to vuid
+ * always being UID_FIELD_INVALID. If we don't do this then
+ * in share mode security we are *always* changing uid's between
+ * SMB's - this hurts performance - Badly.
+ */
+
+ if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
+ (current_user.uid == conn->uid)) {
+ DEBUG(4,("change_to_user: Skipping user change - already user\n"));
+ return(True);
+ } else if ((current_user.conn == conn) &&
+ (vuser != 0) && (current_user.vuid == vuid) &&
+ (current_user.uid == vuser->uid)) {
+ DEBUG(4,("change_to_user: Skipping user change - already user\n"));
+ return(True);
+ }
+
+ snum = SNUM(conn);
+
+ if((vuser != NULL) && !check_user_ok(conn, vuser, snum))
+ return False;
+
+ if (conn->force_user ||
+ conn->admin_user ||
+ (lp_security() == SEC_SHARE)) {
+ uid = conn->uid;
+ gid = conn->gid;
+ current_user.groups = conn->groups;
+ current_user.ngroups = conn->ngroups;
+ token = conn->nt_user_token;
+ } else {
+ if (!vuser) {
+ DEBUG(2,("change_to_user: Invalid vuid used %d\n",vuid));
+ return(False);
+ }
+ uid = vuser->uid;
+ gid = vuser->gid;
+ current_user.ngroups = vuser->n_groups;
+ current_user.groups = vuser->groups;
+ token = vuser->nt_user_token;
+ }
+
+ /*
+ * See if we should force group for this service.
+ * If so this overrides any group set in the force
+ * user code.
+ */
+
+ if((group_c = *lp_force_group(snum))) {
+ BOOL is_guest = False;
+
+ if(group_c == '+') {
+
+ /*
+ * Only force group if the user is a member of
+ * the service group. Check the group memberships for
+ * this user (we already have this) to
+ * see if we should force the group.
+ */
+
+ int i;
+ for (i = 0; i < current_user.ngroups; i++) {
+ if (current_user.groups[i] == conn->gid) {
+ gid = conn->gid;
+ break;
+ }
+ }
+ } else {
+ gid = conn->gid;
+ }
+
+ /*
+ * We've changed the group list in the token - we must
+ * re-create it.
+ */
+
+ if (vuser && vuser->guest)
+ is_guest = True;
+
+ token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest, NULL);
+ must_free_token = True;
+ }
+
+ set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token);
+
+ /*
+ * Free the new token (as set_sec_ctx copies it).
+ */
+
+ if (must_free_token)
+ delete_nt_token(&token);
+
+ current_user.conn = conn;
+ current_user.vuid = vuid;
+
+ DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
+ (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
+
+ return(True);
+}
+
+/****************************************************************************
+ Go back to being root without changing the security context stack,
+ but modify the current_user entries.
+****************************************************************************/
+
+BOOL change_to_root_user(void)
+{
+ set_root_sec_ctx();
+
+ DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
+ (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+
+ return(True);
+}
+
+/****************************************************************************
+ Become the user of an authenticated connected named pipe.
+ When this is called we are currently running as the connection
+ user. Doesn't modify current_user.
+****************************************************************************/
+
+BOOL become_authenticated_pipe_user(pipes_struct *p)
+{
+ if (!push_sec_ctx())
+ return False;
+
+ set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid,
+ p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token);
+
+ return True;
+}
+
+/****************************************************************************
+ Unbecome the user of an authenticated connected named pipe.
+ When this is called we are running as the authenticated pipe
+ user and need to go back to being the connection user. Doesn't modify
+ current_user.
+****************************************************************************/
+
+BOOL unbecome_authenticated_pipe_user(void)
+{
+ return pop_sec_ctx();
+}
+
+/****************************************************************************
+ Utility functions used by become_xxx/unbecome_xxx.
+****************************************************************************/
+
+struct conn_ctx {
+ connection_struct *conn;
+ uint16 vuid;
+};
+
+/* A stack of current_user connection contexts. */
+
+static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
+static int conn_ctx_stack_ndx;
+
+static void push_conn_ctx(void)
+{
+ struct conn_ctx *ctx_p;
+
+ /* Check we don't overflow our stack */
+
+ if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Connection context stack overflow!\n"));
+ smb_panic("Connection context stack overflow!\n");
+ }
+
+ /* Store previous user context */
+ ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
+
+ ctx_p->conn = current_user.conn;
+ ctx_p->vuid = current_user.vuid;
+
+ DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
+
+ conn_ctx_stack_ndx++;
+}
+
+static void pop_conn_ctx(void)
+{
+ struct conn_ctx *ctx_p;
+
+ /* Check for stack underflow. */
+
+ if (conn_ctx_stack_ndx == 0) {
+ DEBUG(0, ("Connection context stack underflow!\n"));
+ smb_panic("Connection context stack underflow!\n");
+ }
+
+ conn_ctx_stack_ndx--;
+ ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
+
+ current_user.conn = ctx_p->conn;
+ current_user.vuid = ctx_p->vuid;
+
+ ctx_p->conn = NULL;
+ ctx_p->vuid = UID_FIELD_INVALID;
+}
+
+void init_conn_ctx(void)
+{
+ int i;
+
+ /* Initialise connection context stack */
+ for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
+ conn_ctx_stack[i].conn = NULL;
+ conn_ctx_stack[i].vuid = UID_FIELD_INVALID;
+ }
+}
+
+/****************************************************************************
+ Temporarily become a root user. Must match with unbecome_root(). Saves and
+ restores the connection context.
+****************************************************************************/
+
+void become_root(void)
+{
+ push_sec_ctx();
+ push_conn_ctx();
+ set_root_sec_ctx();
+}
+
+/* Unbecome the root user */
+
+void unbecome_root(void)
+{
+ pop_sec_ctx();
+ pop_conn_ctx();
+}
+
+/****************************************************************************
+ Push the current security context then force a change via change_to_user().
+ Saves and restores the connection context.
+****************************************************************************/
+
+BOOL become_user(connection_struct *conn, uint16 vuid)
+{
+ if (!push_sec_ctx())
+ return False;
+
+ push_conn_ctx();
+
+ if (!change_to_user(conn, vuid)) {
+ pop_sec_ctx();
+ pop_conn_ctx();
+ return False;
+ }
+
+ return True;
+}
+
+BOOL unbecome_user(void)
+{
+ pop_sec_ctx();
+ pop_conn_ctx();
+ return True;
+}
+
+/*****************************************************************
+ Convert the suplimentary SIDs returned in a netlogon into UNIX
+ group gid_t's. Add to the total group array.
+*****************************************************************/
+
+void add_supplementary_nt_login_groups(int *n_groups, gid_t **pp_groups, NT_USER_TOKEN **pptok)
+{
+ int total_groups;
+ int current_n_groups = *n_groups;
+ gid_t *final_groups = NULL;
+ size_t i;
+ NT_USER_TOKEN *ptok = *pptok;
+ NT_USER_TOKEN *new_tok = NULL;
+
+ if (!ptok || (ptok->num_sids == 0))
+ return;
+
+ new_tok = dup_nt_token(ptok);
+ if (!new_tok) {
+ DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new token\n"));
+ return;
+ }
+ /* Leave the allocated space but empty the number of SIDs. */
+ new_tok->num_sids = 0;
+
+ total_groups = current_n_groups + ptok->num_sids;
+
+ final_groups = (gid_t *)malloc(total_groups * sizeof(gid_t));
+ if (!final_groups) {
+ DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new groups.\n"));
+ delete_nt_token(&new_tok);
+ return;
+ }
+
+ memcpy(final_groups, *pp_groups, current_n_groups * sizeof(gid_t));
+ for (i = 0; i < ptok->num_sids; i++) {
+ enum SID_NAME_USE sid_type;
+ gid_t new_grp;
+
+ if (sid_to_gid(&ptok->user_sids[i], &new_grp, &sid_type)) {
+ /*
+ * Don't add the gid_t if it is already in the current group
+ * list. Some UNIXen don't like the same group more than once.
+ */
+ int j;
+
+ for (j = 0; j < current_n_groups; j++)
+ if (final_groups[j] == new_grp)
+ break;
+
+ if ( j == current_n_groups) {
+ /* Group not already present. */
+ final_groups[current_n_groups++] = new_grp;
+ }
+ } else {
+ /* SID didn't map. Copy to the new token to be saved. */
+ sid_copy(&new_tok->user_sids[new_tok->num_sids++], &ptok->user_sids[i]);
+ }
+ }
+
+ SAFE_FREE(*pp_groups);
+ *pp_groups = final_groups;
+ *n_groups = current_n_groups;
+
+ /* Replace the old token with the truncated one. */
+ delete_nt_token(&ptok);
+ *pptok = new_tok;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert name to SID function.
+ Tries local lookup first - for local domains - then uses winbind.
+*****************************************************************/
+
+BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ extern pstring global_myname;
+ extern fstring global_myworkgroup;
+ fstring sid;
+ BOOL ret = False;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* If we are looking up a domain user, make sure it is
+ for the local machine only */
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ if (strequal(domain, global_myworkgroup)) {
+ ret = local_lookup_name(name, psid, name_type);
+ }
+ /* No break is deliberate here. JRA. */
+ default:
+ if (ret) {
+ } else if (strequal(global_myname, domain)) {
+ ret = local_lookup_name(name, psid, name_type);
+ } else {
+ DEBUG(5, ("lookup_name: domain %s is not local\n", domain));
+ }
+ }
+
+ if (ret) {
+ DEBUG(10,
+ ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %u)\n",
+ domain, name, sid_to_string(sid,psid),
+ (unsigned int)*name_type ));
+ return True;
+ } else if (winbind_lookup_name(domain, name, psid, name_type) || (*name_type != SID_NAME_USER) ) {
+
+ DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
+ domain, name, sid_to_string(sid, psid),
+ (unsigned int)*name_type));
+ return True;
+ }
+
+ DEBUG(10, ("lookup_name: winbind and local lookups for [%s]\\[%s] failed\n", domain, name));
+
+ return False;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to name function.
+ Tries local lookup first - for local sids, then tries winbind.
+*****************************************************************/
+
+BOOL lookup_sid(DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type)
+{
+ if (!name_type)
+ return False;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* Check if this is our own sid. This should perhaps be done by
+ winbind? For the moment handle it here. */
+
+ if (sid->num_auths == 5) {
+ DOM_SID tmp_sid;
+ uint32 rid;
+
+ sid_copy(&tmp_sid, sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ if (sid_equal(&global_sam_sid, &tmp_sid)) {
+
+ return map_domain_sid_to_name(&tmp_sid, dom_name) &&
+ local_lookup_sid(sid, name, name_type);
+ }
+ }
+
+ if (!winbind_lookup_sid(sid, dom_name, name, name_type)) {
+ fstring sid_str;
+ DOM_SID tmp_sid;
+ uint32 rid;
+
+ DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying local.\n", sid_to_string(sid_str, sid) ));
+
+ sid_copy(&tmp_sid, sid);
+ sid_split_rid(&tmp_sid, &rid);
+ return map_domain_sid_to_name(&tmp_sid, dom_name) &&
+ lookup_known_rid(&tmp_sid, rid, name, name_type);
+ }
+ return True;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert uid_t to SID function.
+ Tries winbind first - then uses local lookup.
+ Returns SID pointer.
+*****************************************************************/
+
+DOM_SID *uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ uid_t low, high;
+ fstring sid;
+
+ if (lp_winbind_uid(&low, &high) && uid >= low && uid <= high) {
+ if (winbind_uid_to_sid(psid, uid)) {
+
+ DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
+ (unsigned int)uid, sid_to_string(sid, psid)));
+
+ return psid;
+ }
+ }
+
+ local_uid_to_sid(psid, uid);
+
+ DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
+
+ return psid;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert gid_t to SID function.
+ Tries winbind first - then uses local lookup.
+ Returns SID pointer.
+*****************************************************************/
+
+DOM_SID *gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ gid_t low, high;
+ fstring sid;
+
+ if (lp_winbind_gid(&low, &high) && gid >= low && gid <= high) {
+ if (winbind_gid_to_sid(psid, gid)) {
+
+ DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
+ (unsigned int)gid, sid_to_string(sid, psid)));
+
+ return psid;
+ }
+ }
+
+ local_gid_to_sid(psid, gid);
+
+ DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
+
+ return psid;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to uid function.
+ Tries winbind first - then uses local lookup.
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not. sidtype is set by this function.
+*****************************************************************/
+
+BOOL sid_to_uid(DOM_SID *psid, uid_t *puid, enum SID_NAME_USE *sidtype)
+{
+ fstring sid_str;
+
+ /* if we know its local then don't try winbindd */
+ if (sid_compare_domain(&global_sam_sid, psid) == 0) {
+ return local_sid_to_uid(puid, psid, sidtype);
+ }
+
+/* (tridge) I commented out the slab of code below in order to support foreign SIDs
+ Do we really need to validate the type of SID we have in this case?
+*/
+#if 0
+ fstring dom_name, name;
+ enum SID_NAME_USE name_type;
+
+ *sidtype = SID_NAME_UNKNOWN;
+ /*
+ * First we must look up the name and decide if this is a user sid.
+ */
+
+ if ( (!winbind_lookup_sid(psid, dom_name, name, &name_type)) || (name_type != SID_NAME_USER) ) {
+ DEBUG(10,("sid_to_uid: winbind lookup for sid %s failed - trying local.\n",
+ sid_to_string(sid_str, psid) ));
+
+ return local_sid_to_uid(puid, psid, sidtype);
+ }
+
+ /*
+ * Ensure this is a user sid.
+ */
+
+ if (name_type != SID_NAME_USER) {
+ DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a uid (%u)\n",
+ (unsigned int)name_type ));
+ return False;
+ }
+#endif
+ *sidtype = SID_NAME_USER;
+
+ /*
+ * Get the uid for this SID.
+ */
+
+ if (!winbind_sid_to_uid(puid, psid)) {
+ DEBUG(10,("sid_to_uid: winbind lookup for sid %s failed.\n",
+ sid_to_string(sid_str, psid) ));
+ return local_sid_to_uid(puid, psid, sidtype);
+ }
+
+ DEBUG(10,("sid_to_uid: winbindd %s -> %u\n",
+ sid_to_string(sid_str, psid),
+ (unsigned int)*puid ));
+
+ return True;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ Tries winbind first - then uses local lookup.
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+BOOL sid_to_gid(DOM_SID *psid, gid_t *pgid, enum SID_NAME_USE *sidtype)
+{
+ fstring dom_name, name, sid_str;
+ enum SID_NAME_USE name_type;
+
+ *sidtype = SID_NAME_UNKNOWN;
+
+ /*
+ * First we must look up the name and decide if this is a group sid.
+ */
+
+ if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
+ DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed - trying local.\n",
+ sid_to_string(sid_str, psid) ));
+
+ if (!local_sid_to_gid(pgid, psid, sidtype)) {
+ /* this was probably a foreign sid - assume its a group rid
+ and continue */
+ name_type = SID_NAME_DOM_GRP;
+ } else {
+ return True;
+ }
+ }
+
+ /*
+ * Ensure this is a group sid.
+ */
+
+ if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_WKN_GRP)) {
+ DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
+ (unsigned int)name_type ));
+
+ return local_sid_to_gid(pgid, psid, sidtype);
+ }
+
+ *sidtype = name_type;
+
+ /*
+ * Get the gid for this SID.
+ */
+
+ if (!winbind_sid_to_gid(pgid, psid)) {
+ DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed.\n",
+ sid_to_string(sid_str, psid) ));
+ return False;
+ }
+
+ DEBUG(10,("sid_to_gid: winbindd %s -> %u\n",
+ sid_to_string(sid_str, psid),
+ (unsigned int)*pgid ));
+
+ return True;
+}
+
diff --git a/source3/smbd/utmp.c b/source3/smbd/utmp.c
new file mode 100644
index 0000000000..6b7b0f3ad1
--- /dev/null
+++ b/source3/smbd/utmp.c
@@ -0,0 +1,606 @@
+/*
+ Unix SMB/CIFS implementation.
+ utmp routines
+ Copyright (C) T.D.Lee@durham.ac.uk 1999
+ Heavily modified by Andrew Bartlett and Tridge, April 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef WITH_UTMP
+
+/****************************************************************************
+Reflect connection status in utmp/wtmp files.
+ T.D.Lee@durham.ac.uk September 1999
+
+ With grateful thanks since then to many who have helped port it to
+ different operating systems. The variety of OS quirks thereby
+ uncovered is amazing...
+
+Hints for porting:
+ o Always attempt to use programmatic interface (pututline() etc.)
+ Indeed, at present only programmatic use is supported.
+ o The only currently supported programmatic interface to "wtmp{,x}"
+ is through "updwtmp*()" routines.
+ o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
+ o The HAVE_* items should identify supported features.
+ o If at all possible, avoid "if defined(MY-OS)" constructions.
+
+OS observations and status:
+ Almost every OS seems to have its own quirks.
+
+ Solaris 2.x:
+ Tested on 2.6 and 2.7; should be OK on other flavours.
+ AIX:
+ Apparently has utmpx.h but doesn't implement.
+ OSF:
+ Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?)
+ Redhat 6:
+ utmpx.h seems not to set default filenames. non-x better.
+ IRIX 6.5:
+ Not tested. Appears to have "x".
+ HP-UX 9.x:
+ Not tested. Appears to lack "x".
+ HP-UX 10.x:
+ Not tested.
+ "updwtmp*()" routines seem absent, so no current wtmp* support.
+ Has "ut_addr": probably trivial to implement (although remember
+ that IPv6 is coming...).
+
+ FreeBSD:
+ No "putut*()" type of interface.
+ No "ut_type" and associated defines.
+ Write files directly. Alternatively use its login(3)/logout(3).
+ SunOS 4:
+ Not tested. Resembles FreeBSD, but no login()/logout().
+
+lastlog:
+ Should "lastlog" files, if any, be updated?
+ BSD systems (SunOS 4, FreeBSD):
+ o Prominent mention on man pages.
+ System-V (e.g. Solaris 2):
+ o No mention on man pages, even under "man -k".
+ o Has a "/var/adm/lastlog" file, but pututxline() etc. seem
+ not to touch it.
+ o Despite downplaying (above), nevertheless has <lastlog.h>.
+ So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
+
+Notes:
+ Each connection requires a small number (starting at 0, working up)
+ to represent the line (unum). This must be unique within and across
+ all smbd processes.
+
+ The 4 byte 'ut_id' component is vital to distinguish connections,
+ of which there could be several hundered or even thousand.
+ Entries seem to be printable characters, with optional NULL pads.
+
+ We need to be distinct from other entries in utmp/wtmp.
+
+ Observed things: therefore avoid them. Add to this list please.
+ From Solaris 2.x (because that's what I have):
+ 'sN' : run-levels; N: [0-9]
+ 'co' : console
+ 'CC' : arbitrary things; C: [a-z]
+ 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z]
+ 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z]
+ '/NNN' : Solaris CDE
+ 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff)
+ Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
+ but differences have been seen.
+
+ Arbitrarily I have chosen to use a distinctive 'SM' for the
+ first two bytes.
+
+ The remaining two encode the "unum" (see above).
+
+ For "utmp consolidate" the suggestion was made to encode the pid into
+ those remaining two bytes (16 bits). But recent UNIX (e.g Solaris 8)
+ is migrating to pids > 16 bits, so we ought not to do this.
+
+****************************************************************************/
+
+#include <utmp.h>
+
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+/* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
+/* Some System-V systems (e.g. Solaris 2) declare this too. */
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#endif
+
+/****************************************************************************
+ Obtain/release a small number (0 upwards) unique within and across smbds.
+****************************************************************************/
+/*
+ * Need a "small" number to represent this connection, unique within this
+ * smbd and across all smbds.
+ *
+ * claim:
+ * Start at 0, hunt up for free, unique number "unum" by attempting to
+ * store it as a key in a tdb database:
+ * key: unum data: pid+conn
+ * Also store its inverse, ready for yield function:
+ * key: pid+conn data: unum
+ *
+ * yield:
+ * Find key: pid+conn; data is unum; delete record
+ * Find key: unum ; delete record.
+ *
+ * Comment:
+ * The claim algorithm (a "for" loop attempting to store numbers in a tdb
+ * database) will be increasingly inefficient with larger numbers of
+ * connections. Is it possible to write a suitable primitive within tdb?
+ *
+ * However, by also storing the inverse key/data pair, we at least make
+ * the yield algorithm efficient.
+ */
+
+/****************************************************************************
+ Default paths to various {u,w}tmp{,x} files.
+****************************************************************************/
+
+#ifdef HAVE_UTMPX_H
+
+static const char *ux_pathname =
+# if defined (UTMPX_FILE)
+ UTMPX_FILE ;
+# elif defined (_UTMPX_FILE)
+ _UTMPX_FILE ;
+# elif defined (_PATH_UTMPX)
+ _PATH_UTMPX ;
+# else
+ "" ;
+# endif
+
+static const char *wx_pathname =
+# if defined (WTMPX_FILE)
+ WTMPX_FILE ;
+# elif defined (_WTMPX_FILE)
+ _WTMPX_FILE ;
+# elif defined (_PATH_WTMPX)
+ _PATH_WTMPX ;
+# else
+ "" ;
+# endif
+
+#endif /* HAVE_UTMPX_H */
+
+static const char *ut_pathname =
+# if defined (UTMP_FILE)
+ UTMP_FILE ;
+# elif defined (_UTMP_FILE)
+ _UTMP_FILE ;
+# elif defined (_PATH_UTMP)
+ _PATH_UTMP ;
+# else
+ "" ;
+# endif
+
+static const char *wt_pathname =
+# if defined (WTMP_FILE)
+ WTMP_FILE ;
+# elif defined (_WTMP_FILE)
+ _WTMP_FILE ;
+# elif defined (_PATH_WTMP)
+ _PATH_WTMP ;
+# else
+ "" ;
+# endif
+
+/* BSD-like systems might want "lastlog" support. */
+/* *** Not yet implemented */
+#ifndef HAVE_PUTUTLINE /* see "pututline_my()" */
+static const char *ll_pathname =
+# if defined (_PATH_LASTLOG) /* what other names (if any?) */
+ _PATH_LASTLOG ;
+# else
+ "" ;
+# endif /* _PATH_LASTLOG */
+#endif /* HAVE_PUTUTLINE */
+
+/*
+ * Get name of {u,w}tmp{,x} file.
+ * return: fname contains filename
+ * Possibly empty if this code not yet ported to this system.
+ *
+ * utmp{,x}: try "utmp dir", then default (a define)
+ * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define)
+ */
+static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
+{
+ pstring dirname;
+
+ pstrcpy(dirname, "");
+
+ /* For w-files, first look for explicit "wtmp dir" */
+ if (uw_name[0] == 'w') {
+ pstrcpy(dirname,lp_wtmpdir());
+ trim_string(dirname,"","/");
+ }
+
+ /* For u-files and non-explicit w-dir, look for "utmp dir" */
+ if (dirname == 0 || strlen(dirname) == 0) {
+ pstrcpy(dirname,lp_utmpdir());
+ trim_string(dirname,"","/");
+ }
+
+ /* If explicit directory above, use it */
+ if (dirname != 0 && strlen(dirname) != 0) {
+ pstrcpy(fname, dirname);
+ pstrcat(fname, "/");
+ pstrcat(fname, uw_name);
+ return;
+ }
+
+ /* No explicit directory: attempt to use default paths */
+ if (strlen(uw_default) == 0) {
+ /* No explicit setting, no known default.
+ * Has it yet been ported to this OS?
+ */
+ DEBUG(2,("uw_pathname: unable to determine pathname\n"));
+ }
+ pstrcpy(fname, uw_default);
+}
+
+#ifndef HAVE_PUTUTLINE
+
+/****************************************************************************
+ Update utmp file directly. No subroutine interface: probably a BSD system.
+****************************************************************************/
+
+static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
+{
+ DEBUG(1,("pututline_my: not yet implemented\n"));
+ /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
+}
+#endif /* HAVE_PUTUTLINE */
+
+#ifndef HAVE_UPDWTMP
+
+/****************************************************************************
+ Update wtmp file directly. No subroutine interface: probably a BSD system.
+ Credit: Michail Vidiassov <master@iaas.msu.ru>
+****************************************************************************/
+
+static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
+{
+ int fd;
+ struct stat buf;
+
+ if (! claim) {
+ /*
+ * BSD-like systems:
+ * may use empty ut_name to distinguish a logout record.
+ *
+ * May need "if defined(SUNOS4)" etc. around some of these,
+ * but try to avoid if possible.
+ *
+ * SunOS 4:
+ * man page indicates ut_name and ut_host both NULL
+ * FreeBSD 4.0:
+ * man page appears not to specify (hints non-NULL)
+ * A correspondent suggest at least ut_name should be NULL
+ */
+ memset((char *)&u->ut_name, '\0', sizeof(u->ut_name));
+ memset((char *)&u->ut_host, '\0', sizeof(u->ut_host));
+ }
+ /* Stolen from logwtmp function in libutil.
+ * May be more locking/blocking is needed?
+ */
+ if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (fstat(fd, &buf) == 0) {
+ if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
+ (void) ftruncate(fd, buf.st_size);
+ }
+ (void) close(fd);
+}
+#endif /* HAVE_UPDWTMP */
+
+/****************************************************************************
+ Update via utmp/wtmp (not utmpx/wtmpx).
+****************************************************************************/
+
+static void utmp_nox_update(struct utmp *u, BOOL claim)
+{
+ pstring uname, wname;
+#if defined(PUTUTLINE_RETURNS_UTMP)
+ struct utmp *urc;
+#endif /* PUTUTLINE_RETURNS_UTMP */
+
+ uw_pathname(uname, "utmp", ut_pathname);
+ DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
+
+#ifdef HAVE_PUTUTLINE
+ if (strlen(uname) != 0) {
+ utmpname(uname);
+ }
+
+# if defined(PUTUTLINE_RETURNS_UTMP)
+ setutent();
+ urc = pututline(u);
+ endutent();
+ if (urc == NULL) {
+ DEBUG(2,("utmp_nox_update: pututline() failed\n"));
+ return;
+ }
+# else /* PUTUTLINE_RETURNS_UTMP */
+ setutent();
+ pututline(u);
+ endutent();
+# endif /* PUTUTLINE_RETURNS_UTMP */
+
+#else /* HAVE_PUTUTLINE */
+ if (strlen(uname) != 0) {
+ pututline_my(uname, u, claim);
+ }
+#endif /* HAVE_PUTUTLINE */
+
+ uw_pathname(wname, "wtmp", wt_pathname);
+ DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
+ if (strlen(wname) != 0) {
+#ifdef HAVE_UPDWTMP
+ updwtmp(wname, u);
+ /*
+ * updwtmp() and the newer updwtmpx() may be unsymmetrical.
+ * At least one OS, Solaris 2.x declares the former in the
+ * "utmpx" (latter) file and context.
+ * In the Solaris case this is irrelevant: it has both and
+ * we always prefer the "x" case, so doesn't come here.
+ * But are there other systems, with no "x", which lack
+ * updwtmp() perhaps?
+ */
+#else
+ updwtmp_my(wname, u, claim);
+#endif /* HAVE_UPDWTMP */
+ }
+}
+
+/****************************************************************************
+ Copy a string in the utmp structure.
+****************************************************************************/
+
+static void utmp_strcpy(char *dest, const char *src, size_t n)
+{
+ size_t len = 0;
+
+ memset(dest, '\0', n);
+ if (src)
+ len = strlen(src);
+ if (len >= n) {
+ memcpy(dest, src, n);
+ } else {
+ if (len)
+ memcpy(dest, src, len);
+ }
+}
+
+/****************************************************************************
+ Update via utmpx/wtmpx (preferred) or via utmp/wtmp.
+****************************************************************************/
+
+static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
+{
+#if !defined(HAVE_UTMPX_H)
+ /* No utmpx stuff. Drop to non-x stuff */
+ utmp_nox_update(u, claim);
+#elif !defined(HAVE_PUTUTXLINE)
+ /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
+ utmp_nox_update(u, claim);
+#elif !defined(HAVE_GETUTMPX)
+ /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
+ utmp_nox_update(u, claim);
+#else
+ pstring uname, wname;
+ struct utmpx ux, *uxrc;
+
+ getutmpx(u, &ux);
+
+#if defined(HAVE_UX_UT_SYSLEN)
+ if (hostname)
+ ux.ut_syslen = strlen(hostname) + 1; /* include end NULL */
+ else
+ ux.ut_syslen = 0;
+#endif
+ utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host));
+
+ uw_pathname(uname, "utmpx", ux_pathname);
+ uw_pathname(wname, "wtmpx", wx_pathname);
+ DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
+ /*
+ * Check for either uname or wname being empty.
+ * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
+ * define default filenames.
+ * Also, our local installation has not provided an override.
+ * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".)
+ */
+ if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
+ utmp_nox_update(u, claim);
+ } else {
+ utmpxname(uname);
+ setutxent();
+ uxrc = pututxline(&ux);
+ endutxent();
+ if (uxrc == NULL) {
+ DEBUG(2,("utmp_update: pututxline() failed\n"));
+ return;
+ }
+ updwtmpx(wname, &ux);
+ }
+#endif /* HAVE_UTMPX_H */
+}
+
+#if defined(HAVE_UT_UT_ID)
+/****************************************************************************
+ Encode the unique connection number into "ut_id".
+****************************************************************************/
+
+static int ut_id_encode(int i, char *fourbyte)
+{
+ int nbase;
+ char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ fourbyte[0] = 'S';
+ fourbyte[1] = 'M';
+
+/*
+ * Encode remaining 2 bytes from 'i'.
+ * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
+ * Example: digits would produce the base-10 numbers from '001'.
+ */
+ nbase = strlen(ut_id_encstr);
+
+ fourbyte[3] = ut_id_encstr[i % nbase];
+ i /= nbase;
+ fourbyte[2] = ut_id_encstr[i % nbase];
+ i /= nbase;
+
+ return(i); /* 0: good; else overflow */
+}
+#endif /* defined(HAVE_UT_UT_ID) */
+
+
+/*
+ fill a system utmp structure given all the info we can gather
+*/
+static BOOL sys_utmp_fill(struct utmp *u,
+ const char *username, const char *hostname,
+ const char *id_str, int id_num)
+{
+ struct timeval timeval;
+
+ /*
+ * ut_name, ut_user:
+ * Several (all?) systems seems to define one as the other.
+ * It is easier and clearer simply to let the following take its course,
+ * rather than to try to detect and optimise.
+ */
+#if defined(HAVE_UT_UT_USER)
+ utmp_strcpy(u->ut_user, username, sizeof(u->ut_user));
+#elif defined(HAVE_UT_UT_NAME)
+ utmp_strcpy(u->ut_name, username, sizeof(u->ut_name));
+#endif
+
+ /*
+ * ut_line:
+ * If size limit proves troublesome, then perhaps use "ut_id_encode()".
+ *
+ * Temporary variable "line_tmp" avoids trouble:
+ * o with unwanted trailing NULL if ut_line full;
+ * o with overflow if ut_line would be more than full.
+ */
+ if (strlen(id_str) > sizeof(u->ut_line)) {
+ DEBUG(1,("id_str [%s] is too long for %d char utmp field\n",
+ id_str, sizeof(u->ut_line)));
+ return False;
+ }
+ utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line));
+
+#if defined(HAVE_UT_UT_PID)
+ u->ut_pid = sys_getpid();
+#endif
+
+/*
+ * ut_time, ut_tv:
+ * Some have one, some the other. Many have both, but defined (aliased).
+ * It is easier and clearer simply to let the following take its course.
+ * But note that we do the more precise ut_tv as the final assignment.
+ */
+#if defined(HAVE_UT_UT_TIME)
+ gettimeofday(&timeval, NULL);
+ u->ut_time = timeval.tv_sec;
+#elif defined(HAVE_UT_UT_TV)
+ gettimeofday(&timeval, NULL);
+ u->ut_tv = timeval;
+#else
+#error "with-utmp must have UT_TIME or UT_TV"
+#endif
+
+#if defined(HAVE_UT_UT_HOST)
+ utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host));
+#endif
+
+#if defined(HAVE_UT_UT_ADDR)
+ /*
+ * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
+ * Volunteer to implement, please ...
+ */
+#endif
+
+#if defined(HAVE_UT_UT_ID)
+ if (ut_id_encode(id_num, u->ut_id) != 0) {
+ DEBUG(1,("utmp_fill: cannot encode id %d\n", id_num));
+ return False;
+ }
+#endif
+
+ return True;
+}
+
+/****************************************************************************
+ Close a connection.
+****************************************************************************/
+
+void sys_utmp_yield(const char *username, const char *hostname,
+ const char *id_str, int id_num)
+{
+ struct utmp u;
+
+ ZERO_STRUCT(u);
+
+#if defined(HAVE_UT_UT_EXIT)
+ u.ut_exit.e_termination = 0;
+ u.ut_exit.e_exit = 0;
+#endif
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = DEAD_PROCESS;
+#endif
+
+ if (!sys_utmp_fill(&u, username, hostname, id_str, id_num)) return;
+
+ sys_utmp_update(&u, NULL, False);
+}
+
+/****************************************************************************
+ Claim a entry in whatever utmp system the OS uses.
+****************************************************************************/
+
+void sys_utmp_claim(const char *username, const char *hostname,
+ const char *id_str, int id_num)
+{
+ struct utmp u;
+
+ ZERO_STRUCT(u);
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = USER_PROCESS;
+#endif
+
+ if (!sys_utmp_fill(&u, username, hostname, id_str, id_num)) return;
+
+ sys_utmp_update(&u, hostname, True);
+}
+
+#else /* WITH_UTMP */
+ void dummy_utmp(void) {}
+#endif
diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c
new file mode 100644
index 0000000000..fadc435a2f
--- /dev/null
+++ b/source3/smbd/vfs-wrap.c
@@ -0,0 +1,726 @@
+/*
+ Unix SMB/CIFS implementation.
+ Wrap disk only vfs functions to sidestep dodgy compilers.
+ Copyright (C) Tim Potter 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Check for NULL pointer parameters in vfswrap_* functions */
+
+/* We don't want to have NULL function pointers lying around. Someone
+ is sure to try and execute them. These stubs are used to prevent
+ this possibility. */
+
+int vfswrap_dummy_connect(connection_struct *conn, const char *service, const char *user)
+{
+ return 0; /* Return >= 0 for success */
+}
+
+void vfswrap_dummy_disconnect(connection_struct *conn)
+{
+}
+
+/* Disk operations */
+
+SMB_BIG_UINT vfswrap_disk_free(connection_struct *conn, const char *path, BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ SMB_BIG_UINT result;
+
+ result = sys_disk_free(path, small_query, bsize, dfree, dsize);
+ return result;
+}
+
+/* Directory operations */
+
+DIR *vfswrap_opendir(connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ START_PROFILE(syscall_opendir);
+ result = opendir(fname);
+ END_PROFILE(syscall_opendir);
+ return result;
+}
+
+struct dirent *vfswrap_readdir(connection_struct *conn, DIR *dirp)
+{
+ struct dirent *result;
+
+ START_PROFILE(syscall_readdir);
+ result = readdir(dirp);
+ END_PROFILE(syscall_readdir);
+ return result;
+}
+
+int vfswrap_mkdir(connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+ BOOL has_dacl = False;
+
+ START_PROFILE(syscall_mkdir);
+
+ if (lp_inherit_acls(SNUM(conn)) && (has_dacl = directory_has_default_acl(conn, parent_dirname(path))))
+ mode = 0777;
+
+ result = mkdir(path, mode);
+
+ if (result == 0 && !has_dacl) {
+ /*
+ * We need to do this as the default behavior of POSIX ACLs
+ * is to set the mask to be the requested group permission
+ * bits, not the group permission bits to be the requested
+ * group permission bits. This is not what we want, as it will
+ * mess up any inherited ACL bits that were set. JRA.
+ */
+ int saved_errno = errno; /* We may get ENOSYS */
+ if (conn->vfs_ops.chmod_acl != NULL) {
+ if ((conn->vfs_ops.chmod_acl(conn, path, mode) == -1) && (errno == ENOSYS))
+ errno = saved_errno;
+ }
+ }
+
+ END_PROFILE(syscall_mkdir);
+ return result;
+}
+
+int vfswrap_rmdir(connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_rmdir);
+ result = rmdir(path);
+ END_PROFILE(syscall_rmdir);
+ return result;
+}
+
+int vfswrap_closedir(connection_struct *conn, DIR *dirp)
+{
+ int result;
+
+ START_PROFILE(syscall_closedir);
+ result = closedir(dirp);
+ END_PROFILE(syscall_closedir);
+ return result;
+}
+
+/* File operations */
+
+int vfswrap_open(connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_open);
+ result = sys_open(fname, flags, mode);
+ END_PROFILE(syscall_open);
+ return result;
+}
+
+int vfswrap_close(files_struct *fsp, int fd)
+{
+ int result;
+
+ START_PROFILE(syscall_close);
+
+ result = close(fd);
+ END_PROFILE(syscall_close);
+ return result;
+}
+
+ssize_t vfswrap_read(files_struct *fsp, int fd, void *data, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_read, n);
+ result = read(fd, data, n);
+ END_PROFILE(syscall_read);
+ return result;
+}
+
+ssize_t vfswrap_write(files_struct *fsp, int fd, const void *data, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_write, n);
+ result = write(fd, data, n);
+ END_PROFILE(syscall_write);
+ return result;
+}
+
+SMB_OFF_T vfswrap_lseek(files_struct *fsp, int filedes, SMB_OFF_T offset, int whence)
+{
+ SMB_OFF_T result = 0;
+
+ START_PROFILE(syscall_lseek);
+
+ /* Cope with 'stat' file opens. */
+ if (filedes != -1)
+ result = sys_lseek(filedes, offset, whence);
+
+ /*
+ * We want to maintain the fiction that we can seek
+ * on a fifo for file system purposes. This allows
+ * people to set up UNIX fifo's that feed data to Windows
+ * applications. JRA.
+ */
+
+ if((result == -1) && (errno == ESPIPE)) {
+ result = 0;
+ errno = 0;
+ }
+
+ END_PROFILE(syscall_lseek);
+ return result;
+}
+
+int vfswrap_rename(connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ START_PROFILE(syscall_rename);
+ result = rename(old, new);
+ END_PROFILE(syscall_rename);
+ return result;
+}
+
+int vfswrap_fsync(files_struct *fsp, int fd)
+{
+#ifdef HAVE_FSYNC
+ int result;
+
+ START_PROFILE(syscall_fsync);
+
+ result = fsync(fd);
+ END_PROFILE(syscall_fsync);
+ return result;
+#else
+ return 0;
+#endif
+}
+
+int vfswrap_stat(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_stat);
+ result = sys_stat(fname, sbuf);
+ END_PROFILE(syscall_stat);
+ return result;
+}
+
+int vfswrap_fstat(files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_fstat);
+ result = sys_fstat(fd, sbuf);
+ END_PROFILE(syscall_fstat);
+ return result;
+}
+
+int vfswrap_lstat(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_lstat);
+ result = sys_lstat(path, sbuf);
+ END_PROFILE(syscall_lstat);
+ return result;
+}
+
+int vfswrap_unlink(connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_unlink);
+ result = unlink(path);
+ END_PROFILE(syscall_unlink);
+ return result;
+}
+
+int vfswrap_chmod(connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_chmod);
+
+ /*
+ * We need to do this due to the fact that the default POSIX ACL
+ * chmod modifies the ACL *mask* for the group owner, not the
+ * group owner bits directly. JRA.
+ */
+
+
+ if (conn->vfs_ops.chmod_acl != NULL) {
+ int saved_errno = errno; /* We might get ENOSYS */
+ if ((result = conn->vfs_ops.chmod_acl(conn, path, mode)) == 0) {
+ END_PROFILE(syscall_chmod);
+ return result;
+ }
+ /* Error - return the old errno. */
+ errno = saved_errno;
+ }
+
+ result = chmod(path, mode);
+ END_PROFILE(syscall_chmod);
+ return result;
+}
+
+int vfswrap_fchmod(files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+ struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
+
+ START_PROFILE(syscall_fchmod);
+
+ /*
+ * We need to do this due to the fact that the default POSIX ACL
+ * chmod modifies the ACL *mask* for the group owner, not the
+ * group owner bits directly. JRA.
+ */
+
+ if (vfs_ops->fchmod_acl != NULL) {
+ int saved_errno = errno; /* We might get ENOSYS */
+ if ((result = vfs_ops->fchmod_acl(fsp, fd, mode)) == 0) {
+ END_PROFILE(syscall_chmod);
+ return result;
+ }
+ /* Error - return the old errno. */
+ errno = saved_errno;
+ }
+
+ result = fchmod(fd, mode);
+ END_PROFILE(syscall_fchmod);
+ return result;
+}
+
+int vfswrap_chown(connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ int result;
+
+ START_PROFILE(syscall_chown);
+ result = sys_chown(path, uid, gid);
+ END_PROFILE(syscall_chown);
+ return result;
+}
+
+int vfswrap_fchown(files_struct *fsp, int fd, uid_t uid, gid_t gid)
+{
+ int result;
+
+ START_PROFILE(syscall_fchown);
+
+ result = fchown(fd, uid, gid);
+ END_PROFILE(syscall_fchown);
+ return result;
+}
+
+int vfswrap_chdir(connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_chdir);
+ result = chdir(path);
+ END_PROFILE(syscall_chdir);
+ return result;
+}
+
+char *vfswrap_getwd(connection_struct *conn, char *path)
+{
+ char *result;
+
+ START_PROFILE(syscall_getwd);
+ result = sys_getwd(path);
+ END_PROFILE(syscall_getwd);
+ return result;
+}
+
+int vfswrap_utime(connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ int result;
+
+ START_PROFILE(syscall_utime);
+ result = utime(path, times);
+ END_PROFILE(syscall_utime);
+ return result;
+}
+
+/*********************************************************************
+ A version of ftruncate that will write the space on disk if strict
+ allocate is set.
+**********************************************************************/
+
+static int strict_allocate_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
+ SMB_STRUCT_STAT st;
+ SMB_OFF_T currpos = vfs_ops->lseek(fsp, fd, 0, SEEK_CUR);
+ unsigned char zero_space[4096];
+ SMB_OFF_T space_to_write;
+
+ if (currpos == -1)
+ return -1;
+
+ if (vfs_ops->fstat(fsp, fd, &st) == -1)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode))
+ return 0;
+#endif
+
+ if (st.st_size == len)
+ return 0;
+
+ /* Shrink - just ftruncate. */
+ if (st.st_size > len)
+ return sys_ftruncate(fd, len);
+
+ /* Write out the real space on disk. */
+ if (vfs_ops->lseek(fsp, fd, st.st_size, SEEK_SET) != st.st_size)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+ memset(zero_space, '\0', sizeof(zero_space));
+ while ( space_to_write > 0) {
+ SMB_OFF_T retlen;
+ SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),space_to_write);
+
+ retlen = vfs_ops->write(fsp,fsp->fd,(char *)zero_space,current_len_to_write);
+ if (retlen <= 0)
+ return -1;
+
+ space_to_write -= retlen;
+ }
+
+ /* Seek to where we were */
+ if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos)
+ return -1;
+
+ return 0;
+}
+
+int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ int result = -1;
+ struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
+ SMB_STRUCT_STAT st;
+ char c = 0;
+ SMB_OFF_T currpos;
+
+ START_PROFILE(syscall_ftruncate);
+
+ if (lp_strict_allocate(SNUM(fsp->conn))) {
+ result = strict_allocate_ftruncate(fsp, fd, len);
+ END_PROFILE(syscall_ftruncate);
+ return result;
+ }
+
+ /* we used to just check HAVE_FTRUNCATE_EXTEND and only use
+ sys_ftruncate if the system supports it. Then I discovered that
+ you can have some filesystems that support ftruncate
+ expansion and some that don't! On Linux fat can't do
+ ftruncate extend but ext2 can. */
+
+ result = sys_ftruncate(fd, len);
+ if (result == 0)
+ goto done;
+
+ /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
+ extend a file with ftruncate. Provide alternate implementation
+ for this */
+ currpos = vfs_ops->lseek(fsp, fd, 0, SEEK_CUR);
+ if (currpos == -1) {
+ goto done;
+ }
+
+ /* Do an fstat to see if the file is longer than the requested
+ size in which case the ftruncate above should have
+ succeeded or shorter, in which case seek to len - 1 and
+ write 1 byte of zero */
+ if (vfs_ops->fstat(fsp, fd, &st) == -1) {
+ goto done;
+ }
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode)) {
+ result = 0;
+ goto done;
+ }
+#endif
+
+ if (st.st_size == len) {
+ result = 0;
+ goto done;
+ }
+
+ if (st.st_size > len) {
+ /* the sys_ftruncate should have worked */
+ goto done;
+ }
+
+ if (vfs_ops->lseek(fsp, fd, len-1, SEEK_SET) != len -1)
+ goto done;
+
+ if (vfs_ops->write(fsp, fd, &c, 1)!=1)
+ goto done;
+
+ /* Seek to where we were */
+ if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos)
+ goto done;
+ result = 0;
+
+ done:
+
+ END_PROFILE(syscall_ftruncate);
+ return result;
+}
+
+BOOL vfswrap_lock(files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ BOOL result;
+
+ START_PROFILE(syscall_fcntl_lock);
+
+ result = fcntl_lock(fd, op, offset, count,type);
+ END_PROFILE(syscall_fcntl_lock);
+ return result;
+}
+
+int vfswrap_symlink(connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ int result;
+
+ START_PROFILE(syscall_symlink);
+ result = sys_symlink(oldpath, newpath);
+ END_PROFILE(syscall_symlink);
+ return result;
+}
+
+int vfswrap_readlink(connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ int result;
+
+ START_PROFILE(syscall_readlink);
+ result = sys_readlink(path, buf, bufsiz);
+ END_PROFILE(syscall_readlink);
+ return result;
+}
+
+int vfswrap_link(connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ int result;
+
+ START_PROFILE(syscall_link);
+ result = sys_link(oldpath, newpath);
+ END_PROFILE(syscall_link);
+ return result;
+}
+
+int vfswrap_mknod(connection_struct *conn, const char *pathname, mode_t mode, SMB_DEV_T dev)
+{
+ int result;
+
+ START_PROFILE(syscall_mknod);
+ result = sys_mknod(pathname, mode, dev);
+ END_PROFILE(syscall_mknod);
+ return result;
+}
+
+char *vfswrap_realpath(connection_struct *conn, const char *path, char *resolved_path)
+{
+ char *result;
+
+ START_PROFILE(syscall_realpath);
+ result = sys_realpath(path, resolved_path);
+ END_PROFILE(syscall_realpath);
+ return result;
+}
+
+size_t vfswrap_fget_nt_acl(files_struct *fsp, int fd, SEC_DESC **ppdesc)
+{
+ size_t result;
+
+ START_PROFILE(fget_nt_acl);
+ result = get_nt_acl(fsp, ppdesc);
+ END_PROFILE(fget_nt_acl);
+ return result;
+}
+
+size_t vfswrap_get_nt_acl(files_struct *fsp, const char *name, SEC_DESC **ppdesc)
+{
+ size_t result;
+
+ START_PROFILE(get_nt_acl);
+ result = get_nt_acl(fsp, ppdesc);
+ END_PROFILE(get_nt_acl);
+ return result;
+}
+
+BOOL vfswrap_fset_nt_acl(files_struct *fsp, int fd, uint32 security_info_sent, SEC_DESC *psd)
+{
+ BOOL result;
+
+ START_PROFILE(fset_nt_acl);
+ result = set_nt_acl(fsp, security_info_sent, psd);
+ END_PROFILE(fset_nt_acl);
+ return result;
+}
+
+BOOL vfswrap_set_nt_acl(files_struct *fsp, const char *name, uint32 security_info_sent, SEC_DESC *psd)
+{
+ BOOL result;
+
+ START_PROFILE(set_nt_acl);
+ result = set_nt_acl(fsp, security_info_sent, psd);
+ END_PROFILE(set_nt_acl);
+ return result;
+}
+
+int vfswrap_chmod_acl(connection_struct *conn, const char *name, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(chmod_acl);
+ result = chmod_acl(conn, name, mode);
+ END_PROFILE(chmod_acl);
+ return result;
+}
+
+int vfswrap_fchmod_acl(files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(fchmod_acl);
+ result = fchmod_acl(fsp, fd, mode);
+ END_PROFILE(fchmod_acl);
+ return result;
+}
+
+int vfswrap_sys_acl_get_entry(struct connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return sys_acl_get_entry(theacl, entry_id, entry_p);
+}
+
+int vfswrap_sys_acl_get_tag_type(struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return sys_acl_get_tag_type(entry_d, tag_type_p);
+}
+
+int vfswrap_sys_acl_get_permset(struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return sys_acl_get_permset(entry_d, permset_p);
+}
+
+void * vfswrap_sys_acl_get_qualifier(struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d)
+{
+ return sys_acl_get_qualifier(entry_d);
+}
+
+SMB_ACL_T vfswrap_sys_acl_get_file(struct connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return sys_acl_get_file(path_p, type);
+}
+
+SMB_ACL_T vfswrap_sys_acl_get_fd(struct files_struct *fsp, int fd)
+{
+ return sys_acl_get_fd(fd);
+}
+
+int vfswrap_sys_acl_clear_perms(struct connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ return sys_acl_clear_perms(permset);
+}
+
+int vfswrap_sys_acl_add_perm(struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return sys_acl_add_perm(permset, perm);
+}
+
+char * vfswrap_sys_acl_to_text(struct connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen)
+{
+ return sys_acl_to_text(theacl, plen);
+}
+
+SMB_ACL_T vfswrap_sys_acl_init(struct connection_struct *conn, int count)
+{
+ return sys_acl_init(count);
+}
+
+int vfswrap_sys_acl_create_entry(struct connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return sys_acl_create_entry(pacl, pentry);
+}
+
+int vfswrap_sys_acl_set_tag_type(struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return sys_acl_set_tag_type(entry, tagtype);
+}
+
+int vfswrap_sys_acl_set_qualifier(struct connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return sys_acl_set_qualifier(entry, qual);
+}
+
+int vfswrap_sys_acl_set_permset(struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return sys_acl_set_permset(entry, permset);
+}
+
+int vfswrap_sys_acl_valid(struct connection_struct *conn, SMB_ACL_T theacl )
+{
+ return sys_acl_valid(theacl );
+}
+
+int vfswrap_sys_acl_set_file(struct connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return sys_acl_set_file(name, acltype, theacl);
+}
+
+int vfswrap_sys_acl_set_fd(struct files_struct *fsp, int fd, SMB_ACL_T theacl)
+{
+ return sys_acl_set_fd(fd, theacl);
+}
+
+int vfswrap_sys_acl_delete_def_file(struct connection_struct *conn, const char *path)
+{
+ return sys_acl_delete_def_file(path);
+}
+
+int vfswrap_sys_acl_get_perm(struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return sys_acl_get_perm(permset, perm);
+}
+
+int vfswrap_sys_acl_free_text(struct connection_struct *conn, char *text)
+{
+ return sys_acl_free_text(text);
+}
+
+int vfswrap_sys_acl_free_acl(struct connection_struct *conn, SMB_ACL_T posix_acl)
+{
+ return sys_acl_free_acl(posix_acl);
+}
+
+int vfswrap_sys_acl_free_qualifier(struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype)
+{
+ return sys_acl_free_qualifier(qualifier, tagtype);
+}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
new file mode 100644
index 0000000000..4e2e353ed8
--- /dev/null
+++ b/source3/smbd/vfs.c
@@ -0,0 +1,802 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ VFS initialisation and support functions
+ Copyright (C) Tim Potter 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Some structures to help us initialise the vfs operations table */
+
+struct vfs_syminfo {
+ char *name;
+ void *fptr;
+};
+
+/* Default vfs hooks. WARNING: The order of these initialisers is
+ very important. They must be in the same order as defined in
+ vfs.h. Change at your own peril. */
+
+struct vfs_ops default_vfs_ops = {
+
+ /* Disk operations */
+
+ vfswrap_dummy_connect,
+ vfswrap_dummy_disconnect,
+ vfswrap_disk_free,
+
+ /* Directory operations */
+
+ vfswrap_opendir,
+ vfswrap_readdir,
+ vfswrap_mkdir,
+ vfswrap_rmdir,
+ vfswrap_closedir,
+
+ /* File operations */
+
+ vfswrap_open,
+ vfswrap_close,
+ vfswrap_read,
+ vfswrap_write,
+ vfswrap_lseek,
+ vfswrap_rename,
+ vfswrap_fsync,
+ vfswrap_stat,
+ vfswrap_fstat,
+ vfswrap_lstat,
+ vfswrap_unlink,
+ vfswrap_chmod,
+ vfswrap_fchmod,
+ vfswrap_chown,
+ vfswrap_fchown,
+ vfswrap_chdir,
+ vfswrap_getwd,
+ vfswrap_utime,
+ vfswrap_ftruncate,
+ vfswrap_lock,
+ vfswrap_symlink,
+ vfswrap_readlink,
+ vfswrap_link,
+ vfswrap_mknod,
+ vfswrap_realpath,
+
+ vfswrap_fget_nt_acl,
+ vfswrap_get_nt_acl,
+ vfswrap_fset_nt_acl,
+ vfswrap_set_nt_acl,
+
+ /* POSIX ACL operations. */
+#if defined(HAVE_NO_ACLS)
+ NULL,
+ NULL,
+#else
+ vfswrap_chmod_acl,
+ vfswrap_fchmod_acl,
+#endif
+ vfswrap_sys_acl_get_entry,
+ vfswrap_sys_acl_get_tag_type,
+ vfswrap_sys_acl_get_permset,
+ vfswrap_sys_acl_get_qualifier,
+ vfswrap_sys_acl_get_file,
+ vfswrap_sys_acl_get_fd,
+ vfswrap_sys_acl_clear_perms,
+ vfswrap_sys_acl_add_perm,
+ vfswrap_sys_acl_to_text,
+ vfswrap_sys_acl_init,
+ vfswrap_sys_acl_create_entry,
+ vfswrap_sys_acl_set_tag_type,
+ vfswrap_sys_acl_set_qualifier,
+ vfswrap_sys_acl_set_permset,
+ vfswrap_sys_acl_valid,
+ vfswrap_sys_acl_set_file,
+ vfswrap_sys_acl_set_fd,
+ vfswrap_sys_acl_delete_def_file,
+ vfswrap_sys_acl_get_perm,
+ vfswrap_sys_acl_free_text,
+ vfswrap_sys_acl_free_acl,
+ vfswrap_sys_acl_free_qualifier
+};
+
+/****************************************************************************
+ initialise default vfs hooks
+****************************************************************************/
+
+static BOOL vfs_init_default(connection_struct *conn)
+{
+ DEBUG(3, ("Initialising default vfs hooks\n"));
+
+ memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
+ return True;
+}
+
+/****************************************************************************
+ initialise custom vfs hooks
+****************************************************************************/
+
+static BOOL vfs_init_custom(connection_struct *conn)
+{
+ int vfs_version = -1;
+ struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *);
+
+ DEBUG(3, ("Initialising custom vfs hooks from %s\n", lp_vfsobj(SNUM(conn))));
+
+ /* Open object file */
+
+ if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL)) == NULL) {
+ DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), dlerror()));
+ return False;
+ }
+
+ /* Get handle on vfs_init() symbol */
+
+ init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init");
+
+ if (init_fptr == NULL) {
+ DEBUG(0, ("No vfs_init() symbol found in %s\n", lp_vfsobj(SNUM(conn))));
+ return False;
+ }
+
+ /* Initialise vfs_ops structure */
+
+ conn->vfs_ops = default_vfs_ops;
+
+ if ((ops = init_fptr(&vfs_version, &default_vfs_ops)) == NULL) {
+ DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
+ return False;
+ }
+
+ if (vfs_version != SMB_VFS_INTERFACE_VERSION) {
+ DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
+ vfs_version, SMB_VFS_INTERFACE_VERSION ));
+ return False;
+ }
+
+ if (ops != &conn->vfs_ops) {
+ memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
+ }
+
+ return True;
+}
+
+/*****************************************************************
+ Generic VFS init.
+******************************************************************/
+
+BOOL smbd_vfs_init(connection_struct *conn)
+{
+ if (*lp_vfsobj(SNUM(conn))) {
+
+ /* Loadable object file */
+
+ if (!vfs_init_custom(conn)) {
+ DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
+ return False;
+ }
+
+ return True;
+ }
+
+ /* Normal share - initialise with disk access functions */
+
+ return vfs_init_default(conn);
+}
+
+/*******************************************************************
+ Check if directory exists.
+********************************************************************/
+
+BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ BOOL ret;
+
+ if (!st)
+ st = &st2;
+
+ if (vfs_stat(conn,dname,st) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_mode);
+ if(!ret)
+ errno = ENOTDIR;
+
+ return ret;
+}
+
+/*******************************************************************
+ vfs getwd wrapper
+********************************************************************/
+char *vfs_getwd(connection_struct *conn, char *path)
+{
+ return conn->vfs_ops.getwd(conn,path);
+}
+
+/*******************************************************************
+ vfs mkdir wrapper
+********************************************************************/
+
+int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
+{
+ int ret;
+ SMB_STRUCT_STAT sbuf;
+
+ if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
+ /*
+ * Check if high bits should have been set,
+ * then (if bits are missing): add them.
+ * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
+ */
+ if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
+ !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
+ vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Check if an object exists in the vfs.
+********************************************************************/
+
+BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+
+ if (!sbuf)
+ sbuf = &st;
+
+ ZERO_STRUCTP(sbuf);
+
+ if (vfs_stat(conn,fname,sbuf) == -1)
+ return(False);
+ return True;
+}
+
+/*******************************************************************
+ Check if a file exists in the vfs.
+********************************************************************/
+
+BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+
+ if (!sbuf)
+ sbuf = &st;
+
+ ZERO_STRUCTP(sbuf);
+
+ if (vfs_stat(conn,fname,sbuf) == -1)
+ return False;
+ return(S_ISREG(sbuf->st_mode));
+}
+
+/****************************************************************************
+ Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
+****************************************************************************/
+
+ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
+{
+ size_t total=0;
+
+ while (total < byte_count)
+ {
+ ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
+ byte_count - total);
+
+ if (ret == 0) return total;
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a fd on the vfs.
+****************************************************************************/
+
+ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
+
+ if (ret == -1)
+ return -1;
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ An allocate file space call using the vfs interface.
+ Allocates space for a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
+{
+ int ret;
+ SMB_STRUCT_STAT st;
+ connection_struct *conn = fsp->conn;
+ struct vfs_ops *vfs_ops = &conn->vfs_ops;
+ SMB_OFF_T space_avail;
+ SMB_BIG_UINT bsize,dfree,dsize;
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /*
+ * Actually try and commit the space on disk....
+ */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
+
+ ret = vfs_fstat(fsp,fsp->fd,&st);
+ if (ret == -1)
+ return ret;
+
+ if (len == st.st_size)
+ return 0;
+
+ if (len < st.st_size) {
+ /* Shrink - use ftruncate. */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
+ fsp->fsp_name, (double)st.st_size ));
+
+ flush_write_cache(fsp, SIZECHANGE_FLUSH);
+ if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
+ set_filelen_write_cache(fsp, len);
+ }
+ return ret;
+ }
+
+ /* Grow - we need to test if we have enough space. */
+
+ if (!lp_strict_allocate(SNUM(fsp->conn)))
+ return 0;
+
+ len -= st.st_size;
+ len /= 1024; /* Len is now number of 1k blocks needed. */
+ space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
+ fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
+
+ if (len > space_avail) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ A vfs set_filelen call.
+ set the length of a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
+{
+ int ret;
+
+ release_level_2_oplocks_on_change(fsp);
+ DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
+ flush_write_cache(fsp, SIZECHANGE_FLUSH);
+ if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
+ set_filelen_write_cache(fsp, len);
+
+ return ret;
+}
+
+/****************************************************************************
+ Transfer some data (n bytes) between two file_struct's.
+****************************************************************************/
+
+static files_struct *in_fsp;
+static files_struct *out_fsp;
+
+static ssize_t read_fn(int fd, void *buf, size_t len)
+{
+ return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
+}
+
+static ssize_t write_fn(int fd, const void *buf, size_t len)
+{
+ return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
+}
+
+SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
+{
+ in_fsp = in;
+ out_fsp = out;
+
+ return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
+}
+
+/*******************************************************************
+ A vfs_readdir wrapper which just returns the file name.
+********************************************************************/
+
+char *vfs_readdirname(connection_struct *conn, void *p)
+{
+ struct dirent *ptr;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
+ if (!ptr)
+ return(NULL);
+
+ dname = ptr->d_name;
+
+#ifdef NEXT2
+ if (telldir(p) < 0)
+ return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+ /* using /usr/ucb/cc is BAD */
+ dname = dname - 2;
+#endif
+
+ return(dname);
+}
+
+/* VFS options not quite working yet */
+
+#if 0
+
+/***************************************************************************
+ handle the interpretation of the vfs option parameter
+ *************************************************************************/
+static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
+{
+ struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
+ int i;
+
+ /* Create new vfs option */
+
+ new_option = (struct vfs_options *)malloc(sizeof(*new_option));
+ if (new_option == NULL) {
+ return False;
+ }
+
+ ZERO_STRUCTP(new_option);
+
+ /* Get name and value */
+
+ new_option->name = strtok(pszParmValue, "=");
+
+ if (new_option->name == NULL) {
+ return False;
+ }
+
+ while(isspace(*new_option->name)) {
+ new_option->name++;
+ }
+
+ for (i = strlen(new_option->name); i > 0; i--) {
+ if (!isspace(new_option->name[i - 1])) break;
+ }
+
+ new_option->name[i] = '\0';
+ new_option->name = strdup(new_option->name);
+
+ new_option->value = strtok(NULL, "=");
+
+ if (new_option->value != NULL) {
+
+ while(isspace(*new_option->value)) {
+ new_option->value++;
+ }
+
+ for (i = strlen(new_option->value); i > 0; i--) {
+ if (!isspace(new_option->value[i - 1])) break;
+ }
+
+ new_option->value[i] = '\0';
+ new_option->value = strdup(new_option->value);
+ }
+
+ /* Add to list */
+
+ DLIST_ADD(*options, new_option);
+
+ return True;
+}
+
+#endif
+
+
+/*******************************************************************
+ A wrapper for vfs_chdir().
+********************************************************************/
+
+int vfs_ChDir(connection_struct *conn, char *path)
+{
+ int res;
+ static pstring LastDir="";
+
+ if (strcsequal(path,"."))
+ return(0);
+
+ if (*path == '/' && strcsequal(LastDir,path))
+ return(0);
+
+ DEBUG(3,("vfs_ChDir to %s\n",path));
+
+ res = vfs_chdir(conn,path);
+ if (!res)
+ pstrcpy(LastDir,path);
+ return(res);
+}
+
+/* number of list structures for a caching GetWd function. */
+#define MAX_GETWDCACHE (50)
+
+struct {
+ SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
+ SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
+ char *dos_path; /* The pathname in DOS format. */
+ BOOL valid;
+} ino_list[MAX_GETWDCACHE];
+
+extern BOOL use_getwd_cache;
+
+/****************************************************************************
+ Prompte a ptr (to make it recently used)
+****************************************************************************/
+
+static void array_promote(char *array,int elsize,int element)
+{
+ char *p;
+ if (element == 0)
+ return;
+
+ p = (char *)malloc(elsize);
+
+ if (!p) {
+ DEBUG(5,("array_promote: malloc fail\n"));
+ return;
+ }
+
+ memcpy(p,array + element * elsize, elsize);
+ memmove(array + elsize,array,elsize*element);
+ memcpy(array,p,elsize);
+ SAFE_FREE(p);
+}
+
+/*******************************************************************
+ Return the absolute current directory path - given a UNIX pathname.
+ Note that this path is returned in DOS format, not UNIX
+ format. Note this can be called with conn == NULL.
+********************************************************************/
+
+char *vfs_GetWd(connection_struct *conn, char *path)
+{
+ pstring s;
+ static BOOL getwd_cache_init = False;
+ SMB_STRUCT_STAT st, st2;
+ int i;
+
+ *s = 0;
+
+ if (!use_getwd_cache)
+ return(vfs_getwd(conn,path));
+
+ /* init the cache */
+ if (!getwd_cache_init) {
+ getwd_cache_init = True;
+ for (i=0;i<MAX_GETWDCACHE;i++) {
+ string_set(&ino_list[i].dos_path,"");
+ ino_list[i].valid = False;
+ }
+ }
+
+ /* Get the inode of the current directory, if this doesn't work we're
+ in trouble :-) */
+
+ if (vfs_stat(conn, ".",&st) == -1) {
+ DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
+ return(vfs_getwd(conn,path));
+ }
+
+
+ for (i=0; i<MAX_GETWDCACHE; i++) {
+ if (ino_list[i].valid) {
+
+ /* If we have found an entry with a matching inode and dev number
+ then find the inode number for the directory in the cached string.
+ If this agrees with that returned by the stat for the current
+ directory then all is o.k. (but make sure it is a directory all
+ the same...) */
+
+ if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
+ if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
+ if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
+ (st2.st_mode & S_IFMT) == S_IFDIR) {
+ pstrcpy (path, ino_list[i].dos_path);
+
+ /* promote it for future use */
+ array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+ return (path);
+ } else {
+ /* If the inode is different then something's changed,
+ scrub the entry and start from scratch. */
+ ino_list[i].valid = False;
+ }
+ }
+ }
+ }
+ }
+
+ /* We don't have the information to hand so rely on traditional methods.
+ The very slow getcwd, which spawns a process on some systems, or the
+ not quite so bad getwd. */
+
+ if (!vfs_getwd(conn,s)) {
+ DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
+ return (NULL);
+ }
+
+ pstrcpy(path,s);
+
+ DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
+
+ /* add it to the cache */
+ i = MAX_GETWDCACHE - 1;
+ string_set(&ino_list[i].dos_path,s);
+ ino_list[i].dev = st.st_dev;
+ ino_list[i].inode = st.st_ino;
+ ino_list[i].valid = True;
+
+ /* put it at the top of the list */
+ array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+
+ return (path);
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements and checking that
+ it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
+ on the system that has the referenced file system.
+ Widelinks are allowed if widelinks is true.
+********************************************************************/
+
+BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
+{
+#ifndef REDUCE_PATHS
+ return True;
+#else
+ pstring dir2;
+ pstring wd;
+ pstring base_name;
+ pstring newname;
+ char *p=NULL;
+ BOOL relative = (*s != '/');
+
+ *dir2 = *wd = *base_name = *newname = 0;
+
+ if (widelinks) {
+ unix_clean_name(s);
+ /* can't have a leading .. */
+ if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
+ DEBUG(3,("Illegal file name? (%s)\n",s));
+ return(False);
+ }
+
+ if (strlen(s) == 0)
+ pstrcpy(s,"./");
+
+ return(True);
+ }
+
+ DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
+
+ /* remove any double slashes */
+ all_string_sub(s,"//","/",0);
+
+ pstrcpy(base_name,s);
+ p = strrchr_m(base_name,'/');
+
+ if (!p)
+ return(True);
+
+ if (!vfs_GetWd(conn,wd)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
+ return(False);
+ }
+
+ if (vfs_ChDir(conn,dir) != 0) {
+ DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
+ return(False);
+ }
+
+ if (!vfs_GetWd(conn,dir2)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
+ vfs_ChDir(conn,wd);
+ return(False);
+ }
+
+ if (p && (p != base_name)) {
+ *p = 0;
+ if (strcmp(p+1,".")==0)
+ p[1]=0;
+ if (strcmp(p+1,"..")==0)
+ *p = '/';
+ }
+
+ if (vfs_ChDir(conn,base_name) != 0) {
+ vfs_ChDir(conn,wd);
+ DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
+ return(False);
+ }
+
+ if (!vfs_GetWd(conn,newname)) {
+ vfs_ChDir(conn,wd);
+ DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
+ return(False);
+ }
+
+ if (p && (p != base_name)) {
+ pstrcat(newname,"/");
+ pstrcat(newname,p+1);
+ }
+
+ {
+ size_t l = strlen(dir2);
+ if (dir2[l-1] == '/')
+ l--;
+
+ if (strncmp(newname,dir2,l) != 0) {
+ vfs_ChDir(conn,wd);
+ DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
+ return(False);
+ }
+
+ if (relative) {
+ if (newname[l] == '/')
+ pstrcpy(s,newname + l + 1);
+ else
+ pstrcpy(s,newname+l);
+ } else
+ pstrcpy(s,newname);
+ }
+
+ vfs_ChDir(conn,wd);
+
+ if (strlen(s) == 0)
+ pstrcpy(s,"./");
+
+ DEBUG(3,("reduced to %s\n",s));
+ return(True);
+#endif
+}
diff --git a/source3/smbd/vt_mode.c b/source3/smbd/vt_mode.c
deleted file mode 100644
index 83b62a38ac..0000000000
--- a/source3/smbd/vt_mode.c
+++ /dev/null
@@ -1,496 +0,0 @@
-/* vt_mode.c */
-/*
-support vtp-sessions
-
-written by Christian A. Lademann <cal@zls.com>
-*/
-
-/*
-02.05.95:cal:ported to samba-1.9.13
-*/
-
-#define __vt_mode_c__
-
-
-/* #include <stdio.h> */
-/* #include <fcntl.h> */
-/* #include <sys/types.h> */
-/* #include <unistd.h> */
-/* #include <signal.h> */
-/* #include <errno.h> */
-/* #include <ctype.h> */
-/* #include <utmp.h> */
-/* #include <sys/param.h> */
-/* #include <sys/ioctl.h> */
-/* #include <stdlib.h> */
-/* #include <string.h> */
-
-#include "includes.h"
-#include "vt_mode.h"
-#include <utmp.h>
-
-#ifdef SCO
- extern char *strdup();
-#endif
-
-extern int Client;
-
-#ifdef LINUX
-# define HAS_VTY
-#endif
-
-#ifdef SCO
-# define HAS_PTY
-# define HAS_VTY
-
-# include <sys/tty.h>
-#endif
-
-extern int DEBUGLEVEL;
-extern char *InBuffer, *OutBuffer;
-extern int done_become_user;
-
-char master_name [64], slave_name [64];
-int master, slave, i, o, e;
-
-int ms_type = MS_NONE,
- ms_poll = 0;
-
-
-/*
-VT_Check: test incoming packet for "vtp" or "iVT1\0"
-*/
-int VT_Check(buffer)
-char *buffer;
-{
- DEBUG(3,("Checking packet: <%10s...>\n", buffer+4));
- if((strncmp(buffer+4, "vtp", 3) == 0 && smb_len(buffer) == 3) || (strncmp(buffer+4, "iVT1\0", 5) == 0 && smb_len(buffer) == 5))
- return(1);
- else
- return(0);
-}
-
-
-/*
-VT_Start_utmp: prepare /etc/utmp for /bin/login
-*/
-VT_Start_utmp()
-{
- struct utmp u, *v;
- char *tt;
-
-
- setutent();
-
- strcpy(u.ut_line, VT_Line);
-
- if((v = getutline(&u)) == NULL) {
- if(strncmp(VT_Line, "tty", 3) == 0)
- tt = VT_Line + 3;
- else if(strlen(VT_Line) > 4)
- tt = VT_Line + strlen(VT_Line) - 4;
- else
- tt = VT_Line;
-
- strcpy(u.ut_id, tt);
- u.ut_time = time((time_t*)0);
- }
-
- strcpy(u.ut_user, "LOGIN");
- strcpy(u.ut_line, VT_Line);
- u.ut_pid = getpid();
- u.ut_type = LOGIN_PROCESS;
- pututline(&u);
-
- endutent();
-
- return(0);
-}
-
-
-/*
-VT_Stop_utmp: prepare /etc/utmp for other processes
-*/
-VT_Stop_utmp()
-{
- struct utmp u, *v;
-
-
- if(VT_Line != NULL) {
- setutent();
-
- strcpy(u.ut_line, VT_Line);
-
- if((v = getutline(&u)) != NULL) {
- strcpy(v->ut_user, "");
- v->ut_type = DEAD_PROCESS;
- v->ut_time = time((time_t*)0);
- pututline(v);
- }
-
- endutent();
- }
-
- return(0);
-}
-
-
-/*
-VT_AtExit: Things to do when the program exits
-*/
-void VT_AtExit()
-{
- if(VT_ChildPID > 0) {
- kill(VT_ChildPID, SIGHUP);
- (void)wait(NULL);
- }
-
- VT_Stop_utmp();
-}
-
-
-/*
-VT_SigCLD: signalhandler for SIGCLD: set flag if child-process died
-*/
-void VT_SigCLD(sig)
-int sig;
-{
- if(wait(NULL) == VT_ChildPID)
- VT_ChildDied = True;
- else
- signal(SIGCLD, VT_SigCLD);
-}
-
-
-/*
-VT_SigEXIT: signalhandler for signals that cause the process to exit
-*/
-void VT_SigEXIT(sig)
-int sig;
-{
- VT_AtExit();
-
- exit(1);
-}
-
-
-/*
-VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK
-*/
-int VT_Start()
-{
- char OutBuf [64], *X, *Y;
-
-
- ms_type = MS_NONE;
- master = slave = -1;
-
-#ifdef HAS_VTY
-#ifdef LINUX
-# define MASTER_TMPL "/dev/pty "
-# define SLAVE_TMPL "/dev/tty "
-# define LETTER1 "pqrs"
-# define POS1 8
-# define LETTER2 "0123456789abcdef"
-# define POS2 9
-#endif
-
-#ifdef SCO
-# define MASTER_TMPL "/dev/ptyp_ "
-# define SLAVE_TMPL "/dev/ttyp_ "
-# define LETTER1 "0123456"
-# define POS1 10
-# define LETTER2 "0123456789abcdef"
-# define POS2 11
-#endif
-
- if(ms_poll == MS_VTY || ms_poll == 0) {
- strcpy(master_name, MASTER_TMPL);
- strcpy(slave_name, SLAVE_TMPL);
-
- for(X = LETTER1; *X && master < 0; X++)
- for(Y = LETTER2; *Y && master < 0; Y++) {
- master_name [POS1] = *X;
- master_name [POS2] = *Y;
- if((master = open(master_name, O_RDWR)) >= 0) {
- slave_name [POS1] = *X;
- slave_name [POS2] = *Y;
- if((slave = open(slave_name, O_RDWR)) < 0)
- close(master);
- }
- }
-
- if(master >= 0 && slave >= 0)
- ms_type = MS_VTY;
- }
-
-# undef MASTER_TMPL
-# undef SLAVE_TMPL
-# undef LETTER1
-# undef LETTER2
-# undef POS1
-# undef POS2
-#endif
-
-
-#ifdef HAS_PTY
-#ifdef SCO
-# define MASTER_TMPL "/dev/ptyp%d"
-# define SLAVE_TMPL "/dev/ttyp%d"
-# define MIN_I 0
-# define MAX_I 63
-#endif
-
- if(ms_poll == MS_PTY || ms_poll == 0) {
- int i;
-
- for(i = MIN_I; i <= MAX_I && master < 0; i++) {
- sprintf(master_name, MASTER_TMPL, i);
- if((master = open(master_name, O_RDWR)) >= 0) {
- sprintf(slave_name, SLAVE_TMPL, i);
- if((slave = open(slave_name, O_RDWR)) < 0)
- close(master);
- }
- }
-
- if(master >= 0 && slave >= 0)
- ms_type = MS_PTY;
- }
-
-# undef MASTER_TMPL
-# undef SLAVE_TMPL
-# undef MIN_I
-# undef MAX_I
-#endif
-
-
- if(! ms_type)
- return(-1);
-
- VT_Line = strdup(strrchr(slave_name, '/') + 1);
-
- switch((VT_ChildPID = fork())) {
- case -1:
- return(-1);
- break;
-
- case 0:
-#ifdef SCO
- setsid();
-#endif
- close(0);
- close(1);
- close(2);
-
- i = open(slave_name, O_RDWR);
- o = open(slave_name, O_RDWR);
- e = open(slave_name, O_RDWR);
-
-#ifdef LINUX
- setsid();
- if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1)
- exit(1);
-#endif
-#ifdef SCO
- tcsetpgrp(0, getpid());
-#endif
-
- VT_Start_utmp();
-
- system("stty sane");
- execlp("/bin/login", "login", "-c", (char*)0);
- exit(1);
- break;
-
- default:
- VT_Mode = True;
- VT_Status = VT_OPEN;
- VT_ChildDied = False;
- VT_Fd = master;
-
- signal(SIGCLD, VT_SigCLD);
-
- signal(SIGHUP, VT_SigEXIT);
- signal(SIGTERM, VT_SigEXIT);
- signal(SIGINT, VT_SigEXIT);
- signal(SIGQUIT, VT_SigEXIT);
-
- memset(OutBuf, 0, sizeof(OutBuf));
- OutBuf [4] = 0x06;
- _smb_setlen(OutBuf, 1);
-
- send_smb(Client,OutBuf);
-
- return(0);
- break;
- }
-}
-
-
-/*
-VT_Output: transport data from socket to pty
-*/
-int VT_Output(Buffer)
-char *Buffer;
-{
- int i, len, nb;
-
-
- if(VT_Status != VT_OPEN)
- return(-1);
-
- len = smb_len(Buffer);
-
- nb = write(VT_Fd, Buffer + 4, len);
-
- return((nb == len) ? 0 : -1);
-}
-
-
-/*
-VT_Input: transport data from pty to socket
-*/
-int VT_Input(Buffer, Size)
-char *Buffer;
-int Size;
-{
- int len;
-
-
- if(VT_Status != VT_OPEN)
- return(-1);
-
- memset(Buffer, 0, Size);
- len = read(VT_Fd, Buffer + 4, MIN(VT_MAXREAD, Size));
-
- _smb_setlen(Buffer, len);
-
- return(len + 4);
-}
-
-
-/*
-VT_Process: main loop while in vt-mode
-*/
-void VT_Process()
-{
- static int trans_num = 0;
- extern int Client;
- int nread;
-
-
- VT_Start();
-
- atexit(VT_AtExit);
-
- while (True) {
- int32 len;
- int msg_type;
- int msg_flags;
- int counter;
- int last_keepalive=0;
- struct fd_set si;
- struct timeval to, *top;
- int n, ret, t;
-
-
- errno = 0;
- t = SMBD_SELECT_LOOP*1000;
-
-
- FD_ZERO(&si);
- FD_SET(Client, &si);
-
- FD_SET(VT_Fd, &si);
-
- if(t >= 0) {
- to.tv_sec = t / 1000;
- to.tv_usec = t - (to.tv_sec * 1000);
-
- top = &to;
- } else
- top = NULL;
-
- if(VT_ChildDied)
- goto leave_VT_Process;
-
- n = select(MAX(VT_Fd, Client) + 1, &si, NULL, NULL, top);
-
- if(VT_ChildDied)
- goto leave_VT_Process;
-
- if(n == 0) {
- int i;
- time_t t;
- BOOL allidle = True;
- extern int keepalive;
-
- counter += SMBD_SELECT_LOOP;
-
- t = time(NULL);
-
- if (keepalive && (counter-last_keepalive)>keepalive) {
- if (!send_keepalive(Client))
- goto leave_VT_Process;
- last_keepalive = counter;
- }
- } else if(n > 0) {
- counter = 0;
-
- if(FD_ISSET(VT_Fd, &si)) {
- /* got input from vt */
- nread = VT_Input(OutBuffer, MIN(BUFFER_SIZE,lp_maxxmit()));
-
- if(nread > 0)
- send_smb(Client,OutBuffer);
- }
-
- if(FD_ISSET(Client, &si)) {
- /* got input from socket */
-
- if(receive_smb(Client,InBuffer, 0)) {
- msg_type = CVAL(InBuffer,0);
- msg_flags = CVAL(InBuffer,1);
-
- len = smb_len(InBuffer);
-
- DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
-
- nread = len + 4;
-
- DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
-
- if(msg_type == 0)
- VT_Output(InBuffer);
- else {
- nread = construct_reply(InBuffer,OutBuffer,nread,MIN(BUFFER_SIZE,lp_maxxmit()));
-
- if(nread > 0) {
- if (nread != smb_len(OutBuffer) + 4) {
- DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
- nread,
- smb_len(OutBuffer)));
- } else
- send_smb(Client,OutBuffer);
- }
- }
- } else
- if(errno == EBADF)
- goto leave_VT_Process;
- }
- }
-
- trans_num++;
- }
-
- leave_VT_Process:
-/*
- if(VT_ChildPID > 0)
- kill(VT_ChildPID, SIGHUP);
-
- VT_Stop_utmp(VT_Line);
- return;
-*/
- close_sockets();
- exit(0);
-}