this completes the splitup of server.c.
authorAndrew Tridgell <tridge@samba.org>
Mon, 17 Aug 1998 13:11:34 +0000 (13:11 +0000)
committerAndrew Tridgell <tridge@samba.org>
Mon, 17 Aug 1998 13:11:34 +0000 (13:11 +0000)
the splitup was done with an axe, not a scalpel, so there are some
rough edges. I mostly wanted to get the general form right with fine
tuning of what goes where to come later. Still, this is better than
what we had before where server.c was a general repository for
anything that didn't fit elsewhere.

source/Makefile.in
source/include/proto.h
source/smbd/close.c [new file with mode: 0644]
source/smbd/dosmode.c
source/smbd/error.c [new file with mode: 0644]
source/smbd/open.c [new file with mode: 0644]
source/smbd/oplock.c [new file with mode: 0644]
source/smbd/process.c [new file with mode: 0644]
source/smbd/server.c
source/smbd/service.c [new file with mode: 0644]

index 2fd50bdbcbab0e9007188e57f6edf2022a093e3e..dfb2d0e80b51e8b0e69225f1358cbec0d3cc49a1 100644 (file)
@@ -126,7 +126,8 @@ SMBD_OBJ1 = smbd/server.o smbd/files.o smbd/chgpasswd.o smbd/connection.o \
             smbd/groupname.o smbd/ipc.o smbd/mangle.o smbd/negprot.o \
             smbd/message.o smbd/nttrans.o smbd/pipes.o smbd/predict.o \
             smbd/quotas.o smbd/reply.o smbd/ssl.o smbd/trans2.o smbd/uid.o \
-           smbd/dosmode.o smbd/filename.o
+           smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o \
+           smbd/process.o smbd/oplock.o smbd/service.o smbd/error.o
 
 PRINTING_OBJ = printing/pcap.o printing/print_svid.o printing/printing.o
 
index f154b8f4699ed73cc1aa3dec8b0569da5ad4df46..56f888dbdd6dad57dce14d35b0da30e3eb950272 100644 (file)
@@ -1912,6 +1912,11 @@ BOOL check_oem_password(char *user, unsigned char *data,
                         int new_passwd_size);
 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd, BOOL override);
 
+/*The following definitions come from  smbd/close.c  */
+
+void close_file(files_struct *fsp, BOOL normal_close);
+void close_directory(files_struct *fsp);
+
 /*The following definitions come from  smbd/conn.c  */
 
 void conn_init(void);
@@ -1965,6 +1970,14 @@ void DirCacheFlush(int snum);
 mode_t unix_mode(connection_struct *conn,int dosmode);
 int dos_mode(connection_struct *conn,char *path,struct stat *sbuf);
 int dos_chmod(connection_struct *conn,char *fname,int dosmode,struct stat *st);
+int file_utime(connection_struct *conn, char *fname, struct utimbuf *times);
+BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime);
+
+/*The following definitions come from  smbd/error.c  */
+
+int cached_error_packet(char *inbuf,char *outbuf,files_struct *fsp,int line);
+int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line);
+int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line);
 
 /*The following definitions come from  smbd/fileio.c  */
 
@@ -2046,6 +2059,27 @@ void process_pending_change_notify_queue(time_t t);
 int reply_nttrans(connection_struct *conn,
                  char *inbuf,char *outbuf,int length,int bufsize);
 
+/*The following definitions come from  smbd/open.c  */
+
+void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u);
+int fd_attempt_close(file_fd_struct *fd_ptr);
+void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun,
+                     int mode,int oplock_request, int *Access,int *action);
+int open_directory(files_struct *fsp,connection_struct *conn,
+                  char *fname, int smb_ofun, int unixmode, int *action);
+BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op);
+int check_share_mode( share_mode_entry *share, int deny_mode, char *fname,
+                      BOOL fcbopen, int *flags);
+
+/*The following definitions come from  smbd/oplock.c  */
+
+BOOL open_oplock_ipc(void);
+BOOL process_local_message(int sock, char *buffer, int buf_size);
+BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval);
+BOOL request_oplock_break(share_mode_entry *share_entry, 
+                          uint32 dev, uint32 inode);
+BOOL attempt_close_oplocked_file(files_struct *fsp);
+
 /*The following definitions come from  smbd/password.c  */
 
 void generate_next_challenge(char *challenge);
@@ -2088,6 +2122,16 @@ int read_predict(int fd,int offset,char *buf,char **ptr,int num);
 void do_read_prediction(void);
 void invalidate_read_prediction(int fd);
 
+/*The following definitions come from  smbd/process.c  */
+
+BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout);
+void process_smb(char *inbuf, char *outbuf);
+char *smb_fn_name(int type);
+int chain_reply(char *inbuf,char *outbuf,int size,int bufsize);
+void construct_reply_common(char *inbuf,char *outbuf);
+int construct_reply(char *inbuf,char *outbuf,int size,int bufsize);
+void smbd_process(void);
+
 /*The following definitions come from  smbd/quotas.c  */
 
 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize);
@@ -2168,36 +2212,15 @@ int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int dum_si
 
 void  *dflt_sig(void);
 void  killkids(void);
-int file_utime(connection_struct *conn, char *fname, struct utimbuf *times);
-BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime);
-void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u);
-void close_file(files_struct *fsp, BOOL normal_close);
-void close_directory(files_struct *fsp);
-int open_directory(files_struct *fsp,connection_struct *conn,
-                  char *fname, int smb_ofun, int unixmode, int *action);
-BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op);
-int check_share_mode( share_mode_entry *share, int deny_mode, char *fname,
-                      BOOL fcbopen, int *flags);
-void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun,
-                     int mode,int oplock_request, int *Access,int *action);
+BOOL reload_services(BOOL test);
+void exit_server(char *reason);
+
+/*The following definitions come from  smbd/service.c  */
+
 BOOL become_service(connection_struct *conn,BOOL do_chdir);
 int find_service(char *service);
-int cached_error_packet(char *inbuf,char *outbuf,files_struct *fsp,int line);
-int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line);
-int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line);
-BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval);
-BOOL request_oplock_break(share_mode_entry *share_entry, 
-                          uint32 dev, uint32 inode);
-BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout);
-BOOL reload_services(BOOL test);
 connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode);
-BOOL attempt_close_oplocked_file(files_struct *fsp);
 void close_cnum(connection_struct *conn, uint16 vuid);
-void exit_server(char *reason);
-char *smb_fn_name(int type);
-int chain_reply(char *inbuf,char *outbuf,int size,int bufsize);
-void construct_reply_common(char *inbuf,char *outbuf);
-int construct_reply(char *inbuf,char *outbuf,int size,int bufsize);
 
 /*The following definitions come from  smbd/ssl.c  */
 
diff --git a/source/smbd/close.c b/source/smbd/close.c
new file mode 100644 (file)
index 0000000..9bc0a89
--- /dev/null
@@ -0,0 +1,170 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   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"
+
+extern int DEBUGLEVEL;
+
+extern int32 global_oplocks_open;
+
+
+/****************************************************************************
+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(fsp->fsp_name,'/')))
+      p = fsp->fsp_name;
+    else
+      p++;
+
+    if (!strequal(lp_magicscript(SNUM(conn)),p))
+      return;
+  }
+
+  {
+    int ret;
+    pstring magic_output;
+    pstring fname;
+    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,magic_output,False);
+    DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
+    unlink(fname);
+  }
+}
+
+/****************************************************************************
+  Common code to close a file or a directory.
+****************************************************************************/
+static void close_filestruct(files_struct *fsp)
+{   
+       connection_struct *conn = fsp->conn;
+    
+       fsp->open = False;
+       fsp->is_directory = False; 
+    
+       conn->num_files_open--;
+       if(fsp->wbmpx_ptr) {  
+               free((char *)fsp->wbmpx_ptr);
+               fsp->wbmpx_ptr = NULL; 
+       }  
+     
+#if WITH_MMAP
+       if(fsp->mmap_ptr) {
+               munmap(fsp->mmap_ptr,fsp->mmap_size);
+               fsp->mmap_ptr = NULL;
+       }  
+#endif 
+}    
+
+/****************************************************************************
+ Close a file - possibly invalidating the read prediction.
+
+ 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.
+****************************************************************************/
+void close_file(files_struct *fsp, BOOL normal_close)
+{
+       uint32 dev = fsp->fd_ptr->dev;
+       uint32 inode = fsp->fd_ptr->inode;
+       int token;
+       connection_struct *conn = fsp->conn;
+
+       close_filestruct(fsp);
+
+#if USE_READ_PREDICTION
+       invalidate_read_prediction(fsp->fd_ptr->fd);
+#endif
+
+       if (lp_share_modes(SNUM(conn))) {
+               lock_share_entry(conn, dev, inode, &token);
+               del_share_mode(token, fsp);
+       }
+
+       fd_attempt_close(fsp->fd_ptr);
+
+       if (lp_share_modes(SNUM(conn)))
+               unlock_share_entry(conn, dev, inode, token);
+
+       /* NT uses smbclose to start a print - weird */
+       if (normal_close && fsp->print_file)
+               print_file(conn, fsp);
+
+       /* check for magic scripts */
+       if (normal_close) {
+               check_magic(fsp,conn);
+       }
+
+       if(fsp->granted_oplock == True)
+               global_oplocks_open--;
+
+       fsp->sent_oplock_break = False;
+
+       DEBUG(2,("%s closed file %s (numopen=%d)\n",
+                conn->user,fsp->fsp_name,
+                conn->num_files_open));
+
+       if (fsp->fsp_name) {
+               string_free(&fsp->fsp_name);
+       }
+
+       file_free(fsp);
+}
+
+/****************************************************************************
+ Close a directory opened by an NT SMB call. 
+****************************************************************************/
+  
+void close_directory(files_struct *fsp)
+{
+       /* TODO - walk the list of pending
+          change notify requests and free
+          any pertaining to this fsp. */
+
+       remove_pending_change_notify_requests_by_fid(fsp);
+
+       /*
+        * Do the code common to files and directories.
+        */
+       close_filestruct(fsp);
+       
+       if (fsp->fsp_name)
+               string_free(&fsp->fsp_name);
+       
+       file_free(fsp);
+}
+
index c1af18d80bade4dc598195381ce91e44cd0f3d0c..9db3e208b5e17fa7419936500e9c5927174dd763 100644 (file)
@@ -200,3 +200,71 @@ int dos_chmod(connection_struct *conn,char *fname,int dosmode,struct stat *st)
   return(sys_chmod(fname,unixmode));
 }
 
+
+/*******************************************************************
+Wrapper around sys_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;
+  struct stat sb;
+  int ret = -1;
+
+  errno = 0;
+
+  if(sys_utime(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(sys_stat(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(False);
+                 ret = sys_utime(fname, times);
+                 unbecome_root(False);
+         }
+  }
+
+  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(True);
+} 
+
+
diff --git a/source/smbd/error.c b/source/smbd/error.c
new file mode 100644 (file)
index 0000000..ea23d8b
--- /dev/null
@@ -0,0 +1,142 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   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"
+
+extern int DEBUGLEVEL;
+
+
+/****************************************************************************
+  create an error packet from a cached error.
+****************************************************************************/
+int cached_error_packet(char *inbuf,char *outbuf,files_struct *fsp,int line)
+{
+  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(inbuf,outbuf,eclass,err,line);
+}
+
+
+struct
+{
+  int unixerror;
+  int smbclass;
+  int smbcode;
+} unix_smb_errmap[] =
+{
+  {EPERM,ERRDOS,ERRnoaccess},
+  {EACCES,ERRDOS,ERRnoaccess},
+  {ENOENT,ERRDOS,ERRbadfile},
+  {ENOTDIR,ERRDOS,ERRbadpath},
+  {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 != SMB_SUCCESS)
+    {
+      eclass = unix_ERR_class;
+      ecode = unix_ERR_code;
+      unix_ERR_class = SMB_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++;
+      }
+    }
+
+  return(error_packet(inbuf,outbuf,eclass,ecode,line));
+}
+
+
+/****************************************************************************
+  create an error packet. Normally called using the ERROR() macro
+****************************************************************************/
+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 = CVAL(inbuf,smb_com);
+  int flgs2 = SVAL(outbuf,smb_flg2); 
+
+  if ((flgs2 & FLAGS2_32_BIT_ERROR_CODES) == FLAGS2_32_BIT_ERROR_CODES)
+  {
+    SIVAL(outbuf,smb_rcls,error_code);
+    
+    DEBUG( 3, ( "32 bit error packet at line %d cmd=%d (%s) eclass=%08x [%s]\n",
+              line, cmd, smb_fn_name(cmd), error_code, smb_errstr(outbuf) ) );
+  }
+  else
+  {
+    CVAL(outbuf,smb_rcls) = error_class;
+    SSVAL(outbuf,smb_err,error_code);  
+    DEBUG( 3, ( "error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n",
+             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);
+}
diff --git a/source/smbd/open.c b/source/smbd/open.c
new file mode 100644 (file)
index 0000000..311eac0
--- /dev/null
@@ -0,0 +1,1150 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   file opening and share modes
+   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 DEBUGLEVEL;
+
+extern pstring sesssetup_user;
+extern int global_oplocks_open;
+extern uint16 oplock_port;
+
+
+/****************************************************************************
+fd support routines - attempt to do a sys_open
+****************************************************************************/
+static int fd_attempt_open(char *fname, int flags, int mode)
+{
+  int fd = sys_open(fname,flags,mode);
+
+  /* Fix for files ending in '.' */
+  if((fd == -1) && (errno == ENOENT) &&
+     (strchr(fname,'.')==NULL))
+    {
+      pstrcat(fname,".");
+      fd = sys_open(fname,flags,mode);
+    }
+
+#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF))
+  if ((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];
+
+          p[max_len] = '\0';
+          if ((fd = sys_open(fname,flags,mode)) == -1)
+            p[max_len] = tmp;
+        }
+    }
+#endif
+  return fd;
+}
+
+/****************************************************************************
+Cache a uid_t currently with this file open. This is an optimization only
+used when multiple sessionsetup's have been done to one smbd.
+****************************************************************************/
+void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u)
+{
+  if(fd_ptr->uid_cache_count >= sizeof(fd_ptr->uid_users_cache)/sizeof(uid_t))
+    return;
+  fd_ptr->uid_users_cache[fd_ptr->uid_cache_count++] = u;
+}
+
+/****************************************************************************
+Remove a uid_t that currently has this file open. This is an optimization only
+used when multiple sessionsetup's have been done to one smbd.
+****************************************************************************/
+static void fd_remove_from_uid_cache(file_fd_struct *fd_ptr, uid_t u)
+{
+  int i;
+  for(i = 0; i < fd_ptr->uid_cache_count; i++)
+    if(fd_ptr->uid_users_cache[i] == u) {
+      if(i < (fd_ptr->uid_cache_count-1))
+        memmove((char *)&fd_ptr->uid_users_cache[i], (char *)&fd_ptr->uid_users_cache[i+1],
+               sizeof(uid_t)*(fd_ptr->uid_cache_count-1-i) );
+      fd_ptr->uid_cache_count--;
+    }
+  return;
+}
+
+/****************************************************************************
+Check if a uid_t that currently has this file open is present. This is an
+optimization only used when multiple sessionsetup's have been done to one smbd.
+****************************************************************************/
+static BOOL fd_is_in_uid_cache(file_fd_struct *fd_ptr, uid_t u)
+{
+  int i;
+  for(i = 0; i < fd_ptr->uid_cache_count; i++)
+    if(fd_ptr->uid_users_cache[i] == u)
+      return True;
+  return False;
+}
+
+
+/****************************************************************************
+fd support routines - attempt to re-open an already open fd as O_RDWR.
+Save the already open fd (we cannot close due to POSIX file locking braindamage.
+****************************************************************************/
+static void fd_attempt_reopen(char *fname, int mode, file_fd_struct *fd_ptr)
+{
+  int fd = sys_open( fname, O_RDWR, mode);
+
+  if(fd == -1)
+    return;
+
+  if(fd_ptr->real_open_flags == O_RDONLY)
+    fd_ptr->fd_readonly = fd_ptr->fd;
+  if(fd_ptr->real_open_flags == O_WRONLY)
+    fd_ptr->fd_writeonly = fd_ptr->fd;
+
+  fd_ptr->fd = fd;
+  fd_ptr->real_open_flags = O_RDWR;
+}
+
+/****************************************************************************
+fd support routines - attempt to close the file referenced by this fd.
+Decrements the ref_count and returns it.
+****************************************************************************/
+int fd_attempt_close(file_fd_struct *fd_ptr)
+{
+  extern struct current_user current_user;
+
+  DEBUG(3,("fd_attempt_close fd = %d, dev = %x, inode = %x, open_flags = %d, ref_count = %d.\n",
+          fd_ptr->fd, fd_ptr->dev, fd_ptr->inode,
+          fd_ptr->real_open_flags,
+          fd_ptr->ref_count));
+  if(fd_ptr->ref_count > 0) {
+    fd_ptr->ref_count--;
+    if(fd_ptr->ref_count == 0) {
+      if(fd_ptr->fd != -1)
+        close(fd_ptr->fd);
+      if(fd_ptr->fd_readonly != -1)
+        close(fd_ptr->fd_readonly);
+      if(fd_ptr->fd_writeonly != -1)
+        close(fd_ptr->fd_writeonly);
+      fd_ptr->fd = -1;
+      fd_ptr->fd_readonly = -1;
+      fd_ptr->fd_writeonly = -1;
+      fd_ptr->real_open_flags = -1;
+      fd_ptr->dev = (uint32)-1;
+      fd_ptr->inode = (uint32)-1;
+      fd_ptr->uid_cache_count = 0;
+    } else {
+      fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
+    }
+  } 
+ return fd_ptr->ref_count;
+}
+
+/****************************************************************************
+fd support routines - check that current user has permissions
+to open this file. Used when uid not found in optimization cache.
+This is really ugly code, as due to POSIX locking braindamage we must
+fork and then attempt to open the file, and return success or failure
+via an exit code.
+****************************************************************************/
+static BOOL check_access_allowed_for_current_user( char *fname, int accmode )
+{
+  pid_t child_pid;
+
+  if((child_pid = fork()) < 0) {
+    DEBUG(0,("check_access_allowed_for_current_user: fork failed.\n"));
+    return False;
+  }
+
+  if(child_pid) {
+    /*
+     * Parent.
+     */
+    pid_t wpid;
+    int status_code;
+    if ((wpid = sys_waitpid(child_pid, &status_code, 0)) < 0) {
+      DEBUG(0,("check_access_allowed_for_current_user: The process is no longer waiting!\n"));
+      return(False);
+    }
+
+    if (child_pid != wpid) {
+      DEBUG(0,("check_access_allowed_for_current_user: We were waiting for the wrong process ID\n"));
+      return(False);
+    }
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+    if (WIFEXITED(status_code) == 0) {
+      DEBUG(0,("check_access_allowed_for_current_user: The process exited while we were waiting\n"));
+      return(False);
+    }
+    if (WEXITSTATUS(status_code) != 0) {
+      DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access denied.\n", status_code));
+      return(False);
+    }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+    if(status_code != 0) {
+      DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access denied.\n", status_code));
+      return(False);
+    }
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+    /*
+     * Success - the child could open the file.
+     */
+    DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access allowed.\n", status_code));
+    return True;
+  } else {
+    /*
+     * Child.
+     */
+    int fd;
+    DEBUG(9,("check_access_allowed_for_current_user: Child - attempting to open %s with mode %d.\n", fname, accmode ));
+    if((fd = fd_attempt_open( fname, accmode, 0)) < 0) {
+      /* Access denied. */
+      _exit(EACCES);
+    }
+    close(fd);
+    DEBUG(9,("check_access_allowed_for_current_user: Child - returning ok.\n"));
+    _exit(0);
+  }
+
+  return False;
+}
+
+/****************************************************************************
+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,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;
+       }
+}
+
+/****************************************************************************
+open a file
+****************************************************************************/
+static void open_file(files_struct *fsp,connection_struct *conn,
+                     char *fname1,int flags,int mode, struct stat *sbuf)
+{
+  extern struct current_user current_user;
+  pstring fname;
+  struct stat statbuf;
+  file_fd_struct *fd_ptr;
+  int accmode = (flags & (O_RDONLY | O_WRONLY | O_RDWR));
+
+  fsp->open = False;
+  fsp->fd_ptr = 0;
+  fsp->granted_oplock = False;
+  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 (conn->read_only && !conn->printer) {
+    /* 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;
+    } 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 handles a bug in Win95 - it doesn't say to create the file when it 
+     should */
+  if (conn->printer) {
+         flags |= O_CREAT;
+  }
+
+/*
+  if (flags == O_WRONLY)
+    DEBUG(3,("Bug in client? Set O_WRONLY without O_CREAT\n"));
+*/
+
+  /*
+   * Ensure we have a valid struct stat so we can search the
+   * open fd table.
+   */
+  if(sbuf == 0) {
+    if(sys_stat(fname, &statbuf) < 0) {
+      if(errno != ENOENT) {
+        DEBUG(3,("Error doing stat on file %s (%s)\n",
+                 fname,strerror(errno)));
+
+        check_for_pipe(fname);
+        return;
+      }
+      sbuf = 0;
+    } else {
+      sbuf = &statbuf;
+    }
+  }
+
+  /*
+   * Check to see if we have this file already
+   * open. If we do, just use the already open fd and increment the
+   * reference count (fd_get_already_open increments the ref_count).
+   */
+  if((fd_ptr = fd_get_already_open(sbuf))!= 0) {
+    /*
+     * File was already open.
+     */
+
+    /* 
+     * Check it wasn't open for exclusive use.
+     */
+    if((flags & O_CREAT) && (flags & O_EXCL)) {
+      fd_ptr->ref_count--;
+      errno = EEXIST;
+      return;
+    }
+
+    /*
+     * Ensure that the user attempting to open
+     * this file has permissions to do so, if
+     * the user who originally opened the file wasn't
+     * the same as the current user.
+     */
+
+    if(!fd_is_in_uid_cache(fd_ptr, (uid_t)current_user.uid)) {
+      if(!check_access_allowed_for_current_user( fname, accmode )) {
+        /* Error - permission denied. */
+        DEBUG(3,("Permission denied opening file %s (flags=%d, accmode = %d)\n",
+              fname, flags, accmode));
+        /* Ensure the ref_count is decremented. */
+        fd_ptr->ref_count--;
+        fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
+        errno = EACCES;
+        return;
+      }
+    }
+
+    fd_add_to_uid_cache(fd_ptr, (uid_t)current_user.uid);
+
+    /* 
+     * If not opened O_RDWR try
+     * and do that here - a chmod may have been done
+     * between the last open and now. 
+     */
+    if(fd_ptr->real_open_flags != O_RDWR)
+      fd_attempt_reopen(fname, mode, fd_ptr);
+
+    /*
+     * Ensure that if we wanted write access
+     * it has been opened for write, and if we wanted read it
+     * was open for read. 
+     */
+    if(((accmode == O_WRONLY) && (fd_ptr->real_open_flags == O_RDONLY)) ||
+       ((accmode == O_RDONLY) && (fd_ptr->real_open_flags == O_WRONLY)) ||
+       ((accmode == O_RDWR) && (fd_ptr->real_open_flags != O_RDWR))) {
+      DEBUG(3,("Error opening (already open for flags=%d) file %s (%s) (flags=%d)\n",
+               fd_ptr->real_open_flags, fname,strerror(EACCES),flags));
+      check_for_pipe(fname);
+      fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
+      fd_ptr->ref_count--;
+      return;
+    }
+
+  } else {
+    int open_flags;
+    /* We need to allocate a new file_fd_struct (this increments the
+       ref_count). */
+    if((fd_ptr = fd_get_new()) == 0)
+      return;
+    /*
+     * Whatever the requested flags, attempt read/write access,
+     * as we don't know what flags future file opens may require.
+     * If this fails, try again with the required flags. 
+     * Even if we open read/write when only read access was 
+     * requested the setting of the can_write flag in
+     * the file_struct will protect us from errant
+     * write requests. We never need to worry about O_APPEND
+     * as this is not set anywhere in Samba.
+     */
+    fd_ptr->real_open_flags = O_RDWR;
+    /* Set the flags as needed without the read/write modes. */
+    open_flags = flags & ~(O_RDWR|O_WRONLY|O_RDONLY);
+    fd_ptr->fd = fd_attempt_open(fname, open_flags|O_RDWR, mode);
+    /*
+     * On some systems opening a file for R/W access on a read only
+     * filesystems sets errno to EROFS.
+     */
+#ifdef EROFS
+    if((fd_ptr->fd == -1) && ((errno == EACCES) || (errno == EROFS))) {
+#else /* No EROFS */
+    if((fd_ptr->fd == -1) && (errno == EACCES)) {
+#endif /* EROFS */
+      if(accmode != O_RDWR) {
+        fd_ptr->fd = fd_attempt_open(fname, open_flags|accmode, mode);
+        fd_ptr->real_open_flags = accmode;
+      }
+    }
+  }
+
+  if ((fd_ptr->fd >=0) && 
+      conn->printer && lp_minprintspace(SNUM(conn))) {
+    pstring dname;
+    int dum1,dum2,dum3;
+    char *p;
+    pstrcpy(dname,fname);
+    p = strrchr(dname,'/');
+    if (p) *p = 0;
+    if (sys_disk_free(dname,&dum1,&dum2,&dum3) < 
+       lp_minprintspace(SNUM(conn))) {
+      fd_attempt_close(fd_ptr);
+      fsp->fd_ptr = 0;
+      if(fd_ptr->ref_count == 0)
+        sys_unlink(fname);
+      errno = ENOSPC;
+      return;
+    }
+  }
+    
+  if (fd_ptr->fd < 0)
+  {
+    DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
+      fname,strerror(errno),flags));
+    /* Ensure the ref_count is decremented. */
+    fd_attempt_close(fd_ptr);
+    check_for_pipe(fname);
+    return;
+  }
+
+  if (fd_ptr->fd >= 0)
+  {
+    if(sbuf == 0) {
+      /* Do the fstat */
+      if(fstat(fd_ptr->fd, &statbuf) == -1) {
+        /* Error - backout !! */
+        DEBUG(3,("Error doing fstat on fd %d, file %s (%s)\n",
+                 fd_ptr->fd, fname,strerror(errno)));
+        /* Ensure the ref_count is decremented. */
+        fd_attempt_close(fd_ptr);
+        return;
+      }
+      sbuf = &statbuf;
+    }
+
+    /* Set the correct entries in fd_ptr. */
+    fd_ptr->dev = (uint32)sbuf->st_dev;
+    fd_ptr->inode = (uint32)sbuf->st_ino;
+
+    fsp->fd_ptr = fd_ptr;
+    conn->num_files_open++;
+    fsp->mode = sbuf->st_mode;
+    GetTimeOfDay(&fsp->open_time);
+    fsp->vuid = current_user.vuid;
+    fsp->size = 0;
+    fsp->pos = -1;
+    fsp->open = True;
+    fsp->mmap_ptr = NULL;
+    fsp->mmap_size = 0;
+    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->print_file = conn->printer;
+    fsp->modified = False;
+    fsp->granted_oplock = False;
+    fsp->sent_oplock_break = False;
+    fsp->is_directory = False;
+    fsp->conn = conn;
+    /*
+     * Note that the file name here is the *untranslated* name
+     * ie. it is still in the DOS codepage sent from the client.
+     * All use of this filename will pass though the sys_xxxx
+     * functions which will do the dos_to_unix translation before
+     * mapping into a UNIX filename. JRA.
+     */
+    string_set(&fsp->fsp_name,fname);
+    fsp->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 (fsp->print_file && lp_postscript(SNUM(conn)) && fsp->can_write) {
+           DEBUG(3,("Writing postscript line\n"));
+           write_file(fsp,"%!\n",3);
+    }
+      
+    DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
+            *sesssetup_user ? sesssetup_user : conn->user,fname,
+            BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+            conn->num_files_open));
+
+  }
+
+#if WITH_MMAP
+  /* mmap it if read-only */
+  if (!fsp->can_write) {
+         fsp->mmap_size = file_size(fname);
+         fsp->mmap_ptr = (char *)mmap(NULL,fsp->mmap_size,
+                                      PROT_READ,MAP_SHARED,fsp->fd_ptr->fd,0);
+
+         if (fsp->mmap_ptr == (char *)-1 || !fsp->mmap_ptr) {
+                 DEBUG(3,("Failed to mmap() %s - %s\n",
+                          fname,strerror(errno)));
+                 fsp->mmap_ptr = NULL;
+         }
+  }
+#endif
+}
+
+
+/****************************************************************************
+  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(files_struct *fsp, connection_struct *conn, int token, 
+                                  BOOL *share_locked)
+{
+  if (fsp->can_write){
+    if (is_locked(fsp,conn,0x3FFFFFFF,0,F_WRLCK)){
+      /* If share modes are in force for this connection we
+         have the share entry locked. Unlock it before closing. */
+      if (*share_locked && lp_share_modes(SNUM(conn)))
+        unlock_share_entry( conn, fsp->fd_ptr->dev, 
+                            fsp->fd_ptr->inode, token);
+      close_file(fsp,False);   
+      /* Share mode no longer locked. */
+      *share_locked = False;
+      errno = EACCES;
+      unix_ERR_class = ERRDOS;
+      unix_ERR_code = ERRlock;
+    }
+    else
+      ftruncate(fsp->fd_ptr->fd,0); 
+  }
+}
+
+
+
+/****************************************************************************
+open a file with a share mode
+****************************************************************************/
+void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun,
+                     int mode,int oplock_request, int *Access,int *action)
+{
+  int flags=0;
+  int flags2=0;
+  int deny_mode = (share_mode>>4)&7;
+  struct stat sbuf;
+  BOOL file_existed = file_exist(fname,&sbuf);
+  BOOL share_locked = False;
+  BOOL fcbopen = False;
+  int token;
+  uint32 dev = 0;
+  uint32 inode = 0;
+  int num_share_modes = 0;
+
+  fsp->open = False;
+  fsp->fd_ptr = 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 */
+
+    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 defined(O_SYNC)
+  if (share_mode&(1<<14)) {
+         flags2 |= O_SYNC;
+  }
+#endif /* O_SYNC */
+  
+  if (flags != O_RDONLY && file_existed && 
+      (!CAN_WRITE(conn) || IS_DOS_READONLY(dos_mode(conn,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(conn))) 
+  {
+    int i;
+    share_mode_entry *old_shares = 0;
+
+    if (file_existed)
+    {
+      dev = (uint32)sbuf.st_dev;
+      inode = (uint32)sbuf.st_ino;
+      lock_share_entry(conn, dev, inode, &token);
+      share_locked = True;
+      num_share_modes = get_share_modes(conn, token, dev, inode, &old_shares);
+    }
+
+    /*
+     * Check if the share modes will give us access.
+     */
+
+    if(share_locked && (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];
+
+          /* 
+           * 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(share_entry->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+          {
+
+            DEBUG(5,("open_file_shared: breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
+
+            /* Oplock break.... */
+            unlock_share_entry(conn, dev, inode, token);
+            if(request_oplock_break(share_entry, dev, inode) == False)
+            {
+              free((char *)old_shares);
+              DEBUG(0,("open_file_shared: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
+              errno = EACCES;
+              unix_ERR_class = ERRDOS;
+              unix_ERR_code = ERRbadshare;
+              return;
+            }
+            lock_share_entry(conn, dev, inode, &token);
+            broke_oplock = True;
+            break;
+          }
+
+          /* someone else has a share lock on it, check to see 
+             if we can too */
+          if(check_share_mode(share_entry, deny_mode, fname, fcbopen, &flags) == False)
+          {
+            free((char *)old_shares);
+            unlock_share_entry(conn, dev, inode, token);
+            errno = EACCES;
+            unix_ERR_class = ERRDOS;
+            unix_ERR_code = ERRbadshare;
+            return;
+          }
+
+        } /* end for */
+
+        if(broke_oplock)
+        {
+          free((char *)old_shares);
+          num_share_modes = get_share_modes(conn, token, dev, inode, &old_shares);
+        }
+      } while(broke_oplock);
+    }
+
+    if(old_shares != 0)
+      free((char *)old_shares);
+  }
+
+  DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
+          flags,flags2,mode));
+
+  open_file(fsp,conn,fname,flags|(flags2&~(O_TRUNC)),mode,file_existed ? &sbuf : 0);
+  if (!fsp->open && flags==O_RDWR && errno!=ENOENT && fcbopen) 
+  {
+    flags = O_RDONLY;
+    open_file(fsp,conn,fname,flags,mode,file_existed ? &sbuf : 0 );
+  }
+
+  if (fsp->open) 
+  {
+    int open_mode=0;
+
+    if((share_locked == False) && lp_share_modes(SNUM(conn)))
+    {
+      /* We created the file - thus we must now lock the share entry before creating it. */
+      dev = fsp->fd_ptr->dev;
+      inode = fsp->fd_ptr->inode;
+      lock_share_entry(conn, dev, inode, &token);
+      share_locked = True;
+    }
+
+    switch (flags) 
+    {
+      case O_RDONLY:
+        open_mode = 0;
+        break;
+      case O_RDWR:
+        open_mode = 2;
+        break;
+      case O_WRONLY:
+        open_mode = 1;
+        break;
+    }
+
+    fsp->share_mode = (deny_mode<<4) | open_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;
+    }
+    /* We must create the share mode entry before truncate as
+       truncate can fail due to locking and have to close the
+       file (which expects the share_mode_entry to be there).
+     */
+    if (lp_share_modes(SNUM(conn)))
+    {
+      uint16 port = 0;
+      /* JRA. Currently this only services Exlcusive and batch
+         oplocks (no other opens on this file). This needs to
+         be extended to level II oplocks (multiple reader
+         oplocks). */
+
+      if(oplock_request && (num_share_modes == 0) && lp_oplocks(SNUM(conn)) && 
+             !IS_VETO_OPLOCK_PATH(conn,fname))
+      {
+        fsp->granted_oplock = True;
+        fsp->sent_oplock_break = False;
+        global_oplocks_open++;
+        port = oplock_port;
+
+        DEBUG(5,("open_file_shared: granted oplock (%x) on file %s, \
+dev = %x, inode = %x\n", oplock_request, fname, dev, inode));
+
+      }
+      else
+      {
+        port = 0;
+        oplock_request = 0;
+      }
+      set_share_mode(token, fsp, port, oplock_request);
+    }
+
+    if ((flags2&O_TRUNC) && file_existed)
+      truncate_unless_locked(fsp,conn,token,&share_locked);
+  }
+
+  if (share_locked && lp_share_modes(SNUM(conn)))
+    unlock_share_entry( conn, dev, inode, token);
+}
+
+
+
+/****************************************************************************
+ Open a directory from an NT SMB call.
+****************************************************************************/
+int open_directory(files_struct *fsp,connection_struct *conn,
+                  char *fname, int smb_ofun, int unixmode, int *action)
+{
+       extern struct current_user current_user;
+       struct stat st;
+
+       if (smb_ofun & 0x10) {
+               /*
+                * Create the directory.
+                */
+
+               if(sys_mkdir(fname, unixmode) < 0) {
+                       DEBUG(0,("open_directory: unable to create %s. Error was %s\n",
+                                fname, strerror(errno) ));
+                       return -1;
+               }
+
+               *action = FILE_WAS_CREATED;
+       } else {
+               /*
+                * Check that it *was* a directory.
+                */
+
+               if(sys_stat(fname, &st) < 0) {
+                       DEBUG(0,("open_directory: unable to stat name = %s. Error was %s\n",
+                                fname, strerror(errno) ));
+                       return -1;
+               }
+
+               if(!S_ISDIR(st.st_mode)) {
+                       DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+                       return -1;
+               }
+               *action = FILE_WAS_OPENED;
+       }
+       
+       DEBUG(5,("open_directory: opening directory %s\n",
+                fname));
+
+       /*
+        * Setup the files_struct for it.
+        */
+       
+       fsp->fd_ptr = NULL;
+       conn->num_files_open++;
+       fsp->mode = 0;
+       GetTimeOfDay(&fsp->open_time);
+       fsp->vuid = current_user.vuid;
+       fsp->size = 0;
+       fsp->pos = -1;
+       fsp->open = True;
+       fsp->mmap_ptr = NULL;
+       fsp->mmap_size = 0;
+       fsp->can_lock = True;
+       fsp->can_read = False;
+       fsp->can_write = False;
+       fsp->share_mode = 0;
+       fsp->print_file = False;
+       fsp->modified = False;
+       fsp->granted_oplock = False;
+       fsp->sent_oplock_break = False;
+       fsp->is_directory = True;
+       fsp->conn = conn;
+       /*
+        * Note that the file name here is the *untranslated* name
+        * ie. it is still in the DOS codepage sent from the client.
+        * All use of this filename will pass though the sys_xxxx
+        * functions which will do the dos_to_unix translation before
+        * mapping into a UNIX filename. JRA.
+        */
+       string_set(&fsp->fsp_name,fname);
+       fsp->wbmpx_ptr = NULL;
+
+       return 0;
+}
+
+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) {
+    int pid = getpid();
+    if (old_deny == new_deny && share_pid == pid) 
+       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(connection_struct *conn,char *fname, BOOL rename_op)
+{
+  int i;
+  int ret = False;
+  share_mode_entry *old_shares = 0;
+  int num_share_modes;
+  struct stat sbuf;
+  int token;
+  int pid = getpid();
+  uint32 dev, inode;
+
+  if(!lp_share_modes(SNUM(conn)))
+    return True;
+
+  if (sys_stat(fname,&sbuf) == -1) return(True);
+
+  dev = (uint32)sbuf.st_dev;
+  inode = (uint32)sbuf.st_ino;
+
+  lock_share_entry(conn, dev, inode, &token);
+  num_share_modes = get_share_modes(conn, token, 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(share_entry->op_type & BATCH_OPLOCK)
+        {
+
+          /*
+           * It appears that the NT redirector may have a bug, in that
+           * it tries to do an SMBmv on a file that it has open with a
+           * batch oplock, and then fails to respond to the oplock break
+           * request. This only seems to occur when the client is doing an
+           * SMBmv to the smbd it is using - thus we try and detect this
+           * condition by checking if the file being moved is open and oplocked by
+           * this smbd process, and then not sending the oplock break in this
+           * special case. If the file was open with a deny mode that 
+           * prevents the move the SMBmv will fail anyway with a share
+           * violation error. JRA.
+           */
+          if(rename_op && (share_entry->pid == pid))
+          {
+            DEBUG(0,("check_file_sharing: NT redirector workaround - rename attempted on \
+batch oplocked file %s, dev = %x, inode = %x\n", fname, dev, inode));
+            /* 
+             * This next line is a test that allows the deny-mode
+             * processing to be skipped. This seems to be needed as
+             * NT insists on the rename succeeding (in Office 9x no less !).
+             * This should be removed as soon as (a) MS fix the redirector
+             * bug or (b) NT SMB support in Samba makes NT not issue the
+             * call (as is my fervent hope). JRA.
+             */ 
+            continue;
+          }
+          else
+          {
+            DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
+
+            /* Oplock break.... */
+            unlock_share_entry(conn, dev, inode, token);
+            if(request_oplock_break(share_entry, dev, inode) == False)
+            {
+              free((char *)old_shares);
+              DEBUG(0,("check_file_sharing: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
+              return False;
+            }
+            lock_share_entry(conn, dev, inode, &token);
+            broke_oplock = True;
+            break;
+          }
+        }
+
+        /* someone else has a share lock on it, check to see 
+           if we can too */
+        if ((share_entry->share_mode != DENY_DOS) || (share_entry->pid != pid))
+          goto free_and_exit;
+
+      } /* end for */
+
+      if(broke_oplock)
+      {
+        free((char *)old_shares);
+        num_share_modes = get_share_modes(conn, token, 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() */
+  ret = True;
+
+free_and_exit:
+
+  unlock_share_entry(conn, dev, inode, token);
+  if(old_shares != NULL)
+    free((char *)old_shares);
+  return(ret);
+}
+
+/****************************************************************************
+check if we can open a file with a share mode
+****************************************************************************/
+int check_share_mode( share_mode_entry *share, int deny_mode, char *fname,
+                      BOOL fcbopen, int *flags)
+{
+  int old_open_mode = share->share_mode &0xF;
+  int old_deny_mode = (share->share_mode >>4)&7;
+
+  if (old_deny_mode > 4 || old_open_mode > 2)
+  {
+    DEBUG(0,("Invalid share mode found (%d,%d,%d) on file %s\n",
+               deny_mode,old_deny_mode,old_open_mode,fname));
+    return False;
+  }
+
+  {
+    int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
+                                share->pid,fname);
+
+    if ((access_allowed == AFAIL) ||
+        (!fcbopen && (access_allowed == AREAD && *flags == O_RDWR)) ||
+        (access_allowed == AREAD && *flags == O_WRONLY) ||
+        (access_allowed == AWRITE && *flags == O_RDONLY))
+    {
+      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,
+                share->pid,fname, fcbopen, *flags, access_allowed));
+      return False;
+    }
+
+    if (access_allowed == AREAD)
+      *flags = O_RDONLY;
+
+    if (access_allowed == AWRITE)
+      *flags = O_WRONLY;
+
+  }
+  return True;
+}
+
diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c
new file mode 100644 (file)
index 0000000..1b49e2c
--- /dev/null
@@ -0,0 +1,639 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   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"
+
+extern int DEBUGLEVEL;
+
+extern int oplock_sock;
+extern uint16 oplock_port;
+extern int32 global_oplocks_open;
+extern int32 global_oplocks_open;
+extern int global_oplock_break;
+extern int smb_read_error;
+
+
+/****************************************************************************
+  open the oplock IPC socket communication
+****************************************************************************/
+BOOL open_oplock_ipc(void)
+{
+  struct sockaddr_in sock_name;
+  int 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));
+  if (oplock_sock == -1)
+  {
+    DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
+address %x. Error was %s\n", htonl(INADDR_LOOPBACK), strerror(errno)));
+    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;
+    oplock_port = 0;
+    return False;
+  }
+  oplock_port = ntohs(sock_name.sin_port);
+
+  DEBUG(3,("open_oplock ipc: pid = %d, oplock_port = %u\n", 
+            (int)getpid(), oplock_port));
+
+  return True;
+}
+
+/****************************************************************************
+  process an oplock break message.
+****************************************************************************/
+BOOL process_local_message(int sock, char *buffer, int buf_size)
+{
+  int32 msg_len;
+  uint16 from_port;
+  char *msg_start;
+
+  msg_len = IVAL(buffer,UDP_CMD_LEN_OFFSET);
+  from_port = SVAL(buffer,UDP_CMD_PORT_OFFSET);
+
+  msg_start = &buffer[UDP_CMD_HEADER_LEN];
+
+  DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n", 
+            msg_len, from_port));
+
+  /* Switch on message command - currently OPLOCK_BREAK_CMD is the
+     only valid request. */
+
+  switch(SVAL(msg_start,UDP_MESSAGE_CMD_OFFSET))
+  {
+    case 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", msg_len, OPLOCK_BREAK_MSG_LEN));
+        return False;
+      }
+      {
+        uint32 remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET);
+        uint32 dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
+        uint32 inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET);
+        struct timeval tval;
+        struct sockaddr_in toaddr;
+
+        tval.tv_sec = IVAL(msg_start, OPLOCK_BREAK_SEC_OFFSET);
+        tval.tv_usec = IVAL(msg_start, OPLOCK_BREAK_USEC_OFFSET);
+
+        DEBUG(5,("process_local_message: oplock break request from \
+pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
+
+        /*
+         * 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 return success in this case.
+         */
+
+        if(global_oplocks_open != 0)
+        {
+          if(oplock_break(dev, inode, &tval) == False)
+          {
+            DEBUG(0,("process_local_message: oplock break failed - \
+not returning udp message.\n"));
+            return False;
+          }
+        }
+        else
+        {
+          DEBUG(3,("process_local_message: oplock break requested with no outstanding \
+oplocks. Returning success.\n"));
+        }
+
+        /* Send the message back after OR'ing in the 'REPLY' bit. */
+        SSVAL(msg_start,UDP_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY);
+  
+        bzero((char *)&toaddr,sizeof(toaddr));
+        toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+        toaddr.sin_port = htons(from_port);
+        toaddr.sin_family = AF_INET;
+
+        if(sendto( 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",
+                    remotepid, strerror(errno)));
+          return False;
+        }
+
+        DEBUG(5,("process_local_message: oplock break reply sent to \
+pid %d, port %d, for file dev = %x, inode = %x\n", remotepid, 
+                from_port, dev, inode));
+
+      }
+      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", msg_len, OPLOCK_BREAK_MSG_LEN));
+        return False;
+      }
+
+      {
+        uint32 remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET);
+        uint32 dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
+        uint32 inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET);
+
+        DEBUG(0,("process_local_message: unsolicited oplock break reply from \
+pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
+
+       }
+       return False;
+
+    default:
+      DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n",
+                (unsigned int)SVAL(msg_start,0)));
+      return False;
+  }
+  return True;
+}
+
+/****************************************************************************
+ Process an oplock break directly.
+****************************************************************************/
+BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval)
+{
+  extern struct current_user current_user;
+  extern int Client;
+  char *inbuf = NULL;
+  char *outbuf = NULL;
+  files_struct *fsp = NULL;
+  time_t start_time;
+  BOOL shutdown_server = False;
+  connection_struct *saved_conn;
+  int saved_vuid;
+  pstring saved_dir; 
+
+  if( DEBUGLVL( 3 ) )
+    {
+    dbgtext( "oplock_break: called for dev = %x, inode = %x.\n", dev, inode );
+    dbgtext( "Current global_oplocks_open = %d\n", global_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_dit(dev, inode, tval);
+
+  if(fsp == NULL)
+  {
+    /* The file could have been closed in the meantime - return success. */
+    if( DEBUGLVL( 0 ) )
+      {
+      dbgtext( "oplock_break: cannot find open file with " );
+      dbgtext( "dev = %x, inode = %x ", dev, inode);
+      dbgtext( "allowing break to succeed.\n" );
+      }
+    return True;
+  }
+
+  /* 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->granted_oplock)
+  {
+    if( DEBUGLVL( 0 ) )
+      {
+      dbgtext( "oplock_break: file %s ", fsp->fsp_name );
+      dbgtext( "(dev = %x, inode = %x) has no oplock.\n", dev, inode );
+      dbgtext( "Allowing break to succeed regardless.\n" );
+      }
+    return True;
+  }
+
+  /* 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 = %x)\n", dev, inode );
+      }
+
+    /* 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;
+  }
+
+  /* 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 + SAFETY_MARGIN))==NULL)
+  {
+    DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
+    return False;
+  }
+
+  if((outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN))==NULL)
+  {
+    DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
+    free(inbuf);
+    inbuf = NULL;
+    return False;
+  }
+
+  /* Prepare the SMBlockingX message. */
+  bzero(outbuf,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);
+  /* Change this when we have level II oplocks. */
+  SCVAL(outbuf,smb_vwv3+1,OPLOCKLEVEL_NONE);
+  send_smb(Client, outbuf);
+
+  /* Remember we just sent an oplock break on this file. */
+  fsp->sent_oplock_break = True;
+
+  /* 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_conn = fsp->conn;
+  saved_vuid = current_user.vuid;
+  GetWd(saved_dir);
+  unbecome_user();
+
+  while(OPEN_FSP(fsp) && fsp->granted_oplock)
+  {
+    if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
+    {
+      /*
+       * Die if we got an error.
+       */
+
+      if (smb_read_error == READ_EOF)
+        DEBUG( 0, ( "oplock_break: end of file from client\n" ) );
+      if (smb_read_error == READ_ERROR)
+        DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
+
+      if (smb_read_error == READ_TIMEOUT)
+        DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n",
+                     OPLOCK_BREAK_TIMEOUT ) );
+
+      DEBUGADD( 0, ( "oplock_break failed for file %s ", fsp->fsp_name ) );
+      DEBUGADD( 0, ( "(dev = %x, inode = %x).\n", dev, inode));
+      shutdown_server = True;
+      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 = %x).\n", dev, inode );
+        }
+      shutdown_server = True;
+      break;
+    }
+  }
+
+  /*
+   * Go back to being the user who requested the oplock
+   * break.
+   */
+  if(!become_user(saved_conn, saved_vuid))
+  {
+    DEBUG( 0, ( "oplock_break: unable to re-become user!" ) );
+    DEBUGADD( 0, ( "Shutting down server\n" ) );
+    close_sockets();
+    close(oplock_sock);
+    exit_server("unable to re-become user");
+  }
+  /* Including the directory. */
+  ChDir(saved_dir);
+
+  /* Free the buffers we've been using to recurse. */
+  free(inbuf);
+  free(outbuf);
+
+  /* We need this in case a readraw crossed on the wire. */
+  if(global_oplock_break)
+    global_oplock_break = False;
+
+  /*
+   * If the client did not respond we must die.
+   */
+
+  if(shutdown_server)
+  {
+    DEBUG( 0, ( "oplock_break: client failure in break - " ) );
+    DEBUGADD( 0, ( "shutting down this smbd.\n" ) );
+    close_sockets();
+    close(oplock_sock);
+    exit_server("oplock break failure");
+  }
+
+  if(OPEN_FSP(fsp))
+  {
+    /* The lockingX reply will have removed the oplock flag 
+       from the sharemode. */
+    /* Paranoia.... */
+    fsp->granted_oplock = False;
+    fsp->sent_oplock_break = False;
+    global_oplocks_open--;
+  }
+
+  /* Santity check - remove this later. JRA */
+  if(global_oplocks_open < 0)
+  {
+    DEBUG(0,("oplock_break: global_oplocks_open < 0 (%d). PANIC ERROR\n",
+              global_oplocks_open));
+    exit_server("oplock_break: global_oplocks_open < 0");
+  }
+
+  if( DEBUGLVL( 3 ) )
+    {
+    dbgtext( "oplock_break: returning success for " );
+    dbgtext( "dev = %x, inode = %x.\n", dev, inode );
+    dbgtext( "Current global_oplocks_open = %d\n", global_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, 
+                          uint32 dev, uint32 inode)
+{
+  char op_break_msg[OPLOCK_BREAK_MSG_LEN];
+  struct sockaddr_in addr_out;
+  int pid = getpid();
+  time_t start_time;
+  int time_left;
+
+  if(pid == share_entry->pid)
+  {
+    /* We are breaking our own oplock, make sure it's us. */
+    if(share_entry->op_port != oplock_port)
+    {
+      DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
+should be %d\n", pid, share_entry->op_port, oplock_port));
+      return False;
+    }
+
+    DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
+
+    /* Call oplock break direct. */
+    return oplock_break(dev, inode, &share_entry->time);
+  }
+
+  /* We need to send a OPLOCK_BREAK_CMD message to the
+     port in the share mode entry. */
+
+  SSVAL(op_break_msg,UDP_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD);
+  SIVAL(op_break_msg,OPLOCK_BREAK_PID_OFFSET,pid);
+  SIVAL(op_break_msg,OPLOCK_BREAK_DEV_OFFSET,dev);
+  SIVAL(op_break_msg,OPLOCK_BREAK_INODE_OFFSET,inode);
+  SIVAL(op_break_msg,OPLOCK_BREAK_SEC_OFFSET,(uint32)share_entry->time.tv_sec);
+  SIVAL(op_break_msg,OPLOCK_BREAK_USEC_OFFSET,(uint32)share_entry->time.tv_usec);
+
+  /* set the address and port */
+  bzero((char *)&addr_out,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 ", share_entry->pid, share_entry->op_port );
+    dbgtext( "for dev = %x, inode = %x\n", dev, inode );
+    }
+
+  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 ", share_entry->pid );
+      dbgtext( "on port %d ", share_entry->op_port );
+      dbgtext( "for dev = %x, inode = %x.\n", dev, inode );
+      dbgtext( "Error was %s\n", strerror(errno) );
+      }
+    return False;
+  }
+
+  /*
+   * 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[UDP_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
+    int32 reply_msg_len;
+    uint16 reply_from_port;
+    char *reply_msg_start;
+
+    if(receive_local_message(oplock_sock, 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 ", share_entry->pid );
+          dbgtext( "on port %d ", share_entry->op_port );
+          dbgtext( "for dev = %x, inode = %x\n", dev, inode );
+          }
+        /*
+         * 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 ", share_entry->pid );
+          dbgtext( "on port %d ", share_entry->op_port );
+          dbgtext( "for dev = %x, inode = %x.\n", dev, inode );
+          dbgtext( "Error was (%s).\n", strerror(errno) );
+          }
+      return False;
+    }
+
+    reply_msg_len = IVAL(op_break_reply,UDP_CMD_LEN_OFFSET);
+    reply_from_port = SVAL(op_break_reply,UDP_CMD_PORT_OFFSET);
+
+    reply_msg_start = &op_break_reply[UDP_CMD_HEADER_LEN];
+
+    if(reply_msg_len != OPLOCK_BREAK_MSG_LEN)
+    {
+      /* Ignore it. */
+      DEBUG( 0, ( "request_oplock_break: invalid message length received." ) );
+      DEBUGADD( 0, ( "  Ignoring.\n" ) );
+      continue;
+    }
+
+    /*
+     * Test to see if this is the reply we are awaiting.
+     */
+
+    if((SVAL(reply_msg_start,UDP_MESSAGE_CMD_OFFSET) & CMD_REPLY) &&
+       (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 - probably a break request.
+       * 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(oplock_sock, 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 (fsp->open && fsp->granted_oplock && !fsp->sent_oplock_break) {
+
+    /* Try and break the oplock. */
+    file_fd_struct *fd_ptr = fsp->fd_ptr;
+    if(oplock_break( fd_ptr->dev, fd_ptr->inode, &fsp->open_time)) {
+      if(!fsp->open) /* Did the oplock break close the file ? */
+        return True;
+    }
+  }
+
+  return False;
+}
+
diff --git a/source/smbd/process.c b/source/smbd/process.c
new file mode 100644 (file)
index 0000000..30abfdb
--- /dev/null
@@ -0,0 +1,810 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   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"
+
+extern int DEBUGLEVEL;
+
+extern int last_message;
+extern int global_oplock_break;
+extern pstring sesssetup_user;
+extern char *last_inbuf;
+extern time_t smb_last_time;
+extern char *InBuffer;
+extern char *OutBuffer;
+extern int oplock_sock;
+extern int smb_read_error;
+extern BOOL reload_after_sighup;
+extern BOOL global_machine_pasword_needs_changing;
+extern fstring global_myworkgroup;
+extern pstring global_myname;
+extern int max_send;
+
+
+/****************************************************************************
+Get the next SMB packet, doing the local message processing automatically.
+****************************************************************************/
+BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout)
+{
+  BOOL got_smb = False;
+  BOOL ret;
+
+  do
+  {
+    ret = receive_message_or_smb(smbfd,oplockfd,inbuf,bufsize,
+                                 timeout,&got_smb);
+
+    if(ret && !got_smb)
+    {
+      /* Deal with oplock break requests from other smbd's. */
+      process_local_message(oplock_sock, inbuf, bufsize);
+      continue;
+    }
+
+    if(ret && (CVAL(inbuf,0) == 0x85))
+    {
+      /* Keepalive packet. */
+      got_smb = False;
+    }
+
+  }
+  while(ret && !got_smb);
+
+  return ret;
+}
+
+
+
+/****************************************************************************
+  process an smb from the client - split out from the process() code so
+  it can be used by the oplock break code.
+****************************************************************************/
+void process_smb(char *inbuf, char *outbuf)
+{
+  extern int Client;
+#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;
+
+  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 (!check_access(Client, lp_hostsallow(-1), lp_hostsdeny(-1))) {
+                 /* send a negative session response "not listining on calling
+                  name" */
+                 static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+                 DEBUG( 1, ( "Connection denied from %s\n",
+                             client_addr(Client) ) );
+                 send_smb(Client,(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(Client, msg_type);
+        if(sslConnected < 0){   /* an error occured */
+            exit_server("SSL negotiation failed");
+        }else if(sslConnected){
+            trans_num++;
+            return;
+        }
+    }
+#endif  /* WITH_SSL */
+
+#ifdef WITH_VTP
+  if(trans_num == 1 && VT_Check(inbuf)) 
+  {
+    VT_Process();
+    return;
+  }
+#endif
+
+  if (msg_type == 0)
+    show_msg(inbuf);
+  else if(msg_type == 0x85)
+    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
+      send_smb(Client,outbuf);
+  }
+  trans_num++;
+}
+
+
+/*
+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!
+*/
+struct smb_message_struct
+{
+  int code;
+  char *name;
+  int (*fn)(connection_struct *conn, char *, char *, int, int);
+  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}, /* ulogoff doesn't give a valid TID */
+   {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 | QUEUE_IN_OPLOCK },
+
+   /* 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 | QUEUE_IN_OPLOCK},
+   {SMBread,"SMBread",reply_read,AS_USER},
+   {SMBwrite,"SMBwrite",reply_write,AS_USER},
+   {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC},
+   {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 | QUEUE_IN_OPLOCK},
+
+   /* 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 | QUEUE_IN_OPLOCK },
+   {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
+   {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 | QUEUE_IN_OPLOCK },
+   {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+   
+   {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+   {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+   {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},
+
+   /* NT PROTOCOL FOLLOWS */
+   {SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+   {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
+   {SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
+   {SMBntcancel, "SMBntcancel", reply_ntcancel, 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;
+  extern int Client;
+
+#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(global_oplock_break && (smb_messages[match].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;
+    }          
+
+    if (smb_messages[match].fn)
+    {
+      int flags = smb_messages[match].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));
+
+
+      /* Ensure this value is replaced in the incoming packet. */
+      SSVAL(inbuf,smb_uid,session_tag);
+
+      /*
+       * Ensure the correct username is in sesssetup_user.
+       * 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)
+          pstrcpy( sesssetup_user, vuser->requested_name);
+      }
+
+      /* 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(conn,session_tag)) {
+        if (flags & AS_GUEST) 
+          flags &= ~AS_USER;
+        else
+          return(ERROR(ERRSRV,ERRinvnid));
+      }
+      /* 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(ERRSRV,ERRaccess));
+
+      /* ipc services are limited */
+      if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) {
+        return(ERROR(ERRSRV,ERRaccess));           
+      }
+
+      /* load service specific parameters */
+      if (conn && 
+         !become_service(conn,(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(Client, lp_hostsallow(-1), lp_hostsdeny(-1)))) {
+        return(ERROR(ERRSRV,ERRaccess));
+      }
+
+      last_inbuf = inbuf;
+
+      outsize = smb_messages[match].fn(conn, inbuf,outbuf,size,bufsize);
+    }
+    else
+    {
+      outsize = reply_unknown(inbuf,outbuf);
+    }
+  }
+
+#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
+  **************************************************************************/
+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];
+  extern int chain_size;
+  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) {
+    CVAL(outbuf,smb_vwv0) = 0xFF;
+    return outsize;
+  }
+
+  if (chain_size == 0) {
+    /* this is the first part of the chain */
+    orig_inbuf = inbuf;
+    orig_outbuf = outbuf;
+  }
+
+  /* we need to tell the client where the next part of the reply will be */
+  SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
+  CVAL(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 */
+  CVAL(inbuf2,smb_com) = smb_com2;
+
+  /* create the out buffer */
+  bzero(outbuf2,smb_size);
+  set_message(outbuf2,0,0,True);
+  CVAL(outbuf2,smb_com) = CVAL(inbuf2,smb_com);
+  
+  memcpy(outbuf2+4,inbuf2+4,4);
+  CVAL(outbuf2,smb_rcls) = SMB_SUCCESS;
+  CVAL(outbuf2,smb_reh) = 0;
+  CVAL(outbuf2,smb_flg) = 0x80 | (CVAL(inbuf2,smb_flg) & 0x8); /* bit 7 set 
+                                                                 means a reply */
+  SSVAL(outbuf2,smb_flg2,1); /* say we support long filenames */
+  SSVAL(outbuf2,smb_err,SMB_SUCCESS);
+  SSVAL(outbuf2,smb_tid,SVAL(inbuf2,smb_tid));
+  SSVAL(outbuf2,smb_pid,SVAL(inbuf2,smb_pid));
+  SSVAL(outbuf2,smb_uid,SVAL(inbuf2,smb_uid));
+  SSVAL(outbuf2,smb_mid,SVAL(inbuf2,smb_mid));
+
+  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);
+  CVAL(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;
+}
+
+
+/****************************************************************************
+ Helper function for contruct_reply.
+****************************************************************************/
+
+void construct_reply_common(char *inbuf,char *outbuf)
+{
+  bzero(outbuf,smb_size);
+
+  CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
+  set_message(outbuf,0,0,True);
+
+  memcpy(outbuf+4,inbuf+4,4);
+  CVAL(outbuf,smb_rcls) = SMB_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,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 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);
+  extern int chain_size;
+
+  smb_last_time = time(NULL);
+
+  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);
+}
+
+/****************************************************************************
+  process commands from the client
+****************************************************************************/
+void smbd_process(void)
+{
+  extern int Client;
+
+  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,NMB_PORT,SOCK_DGRAM);
+  }
+#endif    
+
+  /* re-initialise the timezone */
+  TimeInit();
+
+  while (True)
+  {
+    int deadtime = lp_deadtime()*60;
+    int counter;
+    int last_keepalive=0;
+    int service_load_counter = 0;
+    BOOL got_smb = False;
+
+    if (deadtime <= 0)
+      deadtime = DEFAULT_SMBD_TIMEOUT;
+
+#if USE_READ_PREDICTION
+    if (lp_readprediction())
+      do_read_prediction();
+#endif
+
+    errno = 0;      
+
+    for (counter=SMBD_SELECT_LOOP; 
+          !receive_message_or_smb(Client,oplock_sock,
+                      InBuffer,BUFFER_SIZE,SMBD_SELECT_LOOP*1000,&got_smb); 
+          counter += SMBD_SELECT_LOOP)
+    {
+      time_t t;
+      BOOL allidle = True;
+      extern int keepalive;
+
+      if (counter > 365 * 3600) /* big number of seconds. */
+      {
+        counter = 0;
+        service_load_counter = 0;
+      }
+
+      if (smb_read_error == READ_EOF) 
+      {
+        DEBUG(3,("end of file from client\n"));
+        return;
+      }
+
+      if (smb_read_error == READ_ERROR) 
+      {
+        DEBUG(3,("receive_smb error (%s) exiting\n",
+                  strerror(errno)));
+        return;
+      }
+
+      t = time(NULL);
+
+      /* become root again if waiting */
+      unbecome_user();
+
+      /* check for smb.conf reload */
+      if (counter >= service_load_counter + SMBD_RELOAD_CHECK)
+      {
+        service_load_counter = counter;
+
+        /* reload services, if files have changed. */
+        reload_services(True);
+      }
+
+      /*
+       * If reload_after_sighup == True then we got a SIGHUP
+       * and are being asked to reload. Fix from <branko.cibej@hermes.si>
+       */
+
+      if (reload_after_sighup)
+      {
+        DEBUG(0,("Reloading services after SIGHUP\n"));
+        reload_services(False);
+        reload_after_sighup = False;
+      }
+
+      /* automatic timeout if all connections are closed */      
+      if (conn_num_open()==0 && counter >= IDLE_CLOSED_TIMEOUT) 
+      {
+        DEBUG( 2, ( "Closing idle connection\n" ) );
+        return;
+      }
+
+      if (keepalive && (counter-last_keepalive)>keepalive) 
+      {
+             struct cli_state *cli = server_client();
+             if (!send_keepalive(Client)) {
+                     DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+                     return;
+             }     
+             /* also send a keepalive to the password server if its still
+                connected */
+             if (cli && cli->initialised)
+                     send_keepalive(cli->fd);
+             last_keepalive = counter;
+      }
+
+      /* check for connection timeouts */
+      allidle = conn_idle_all(t, deadtime);
+
+      if (allidle && conn_num_open()>0) {
+             DEBUG(2,("Closing idle connection 2.\n"));
+             return;
+      }
+
+      if(global_machine_pasword_needs_changing)
+      {
+        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(!trust_password_lock( global_myworkgroup, global_myname, True)) {
+          DEBUG(0,("process: unable to open the machine account password file for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+          continue;
+        }
+
+        if(!get_trust_account_password( 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 ));
+          trust_password_unlock();
+          continue;
+        }
+
+        /*
+         * Make sure someone else hasn't already done this.
+         */
+
+        if(t < lct + lp_machine_password_timeout()) {
+          trust_password_unlock();
+          global_machine_pasword_needs_changing = False;
+          continue;
+        }
+
+        pstrcpy(remote_machine_list, lp_passwordserver());
+
+        change_trust_account_password( global_myworkgroup, remote_machine_list);
+        trust_password_unlock();
+        global_machine_pasword_needs_changing = False;
+      }
+
+      /*
+       * Check to see if we have any change notifies 
+       * outstanding on the queue.
+       */
+      process_pending_change_notify_queue(t);
+    }
+
+    if(got_smb)
+      process_smb(InBuffer, OutBuffer);
+    else
+      process_local_message(oplock_sock, InBuffer, BUFFER_SIZE);
+  }
+}
index d81d80047bc5b5a3dd36e9da98c3ef847329724e..276f5a8075274bb32d4572315c2a8bb90b348fa7 100644 (file)
@@ -114,2493 +114,216 @@ void  killkids(void)
   if(am_parent) kill(0,SIGTERM);
 }
 
-/*******************************************************************
-Wrapper around sys_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;
-  struct stat sb;
-  int ret = -1;
-
-  errno = 0;
-
-  if(sys_utime(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(sys_stat(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(False);
-                 ret = sys_utime(fname, times);
-                 unbecome_root(False);
-         }
-  }
-
-  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(True);
-} 
-
-
-/****************************************************************************
-fd support routines - attempt to do a sys_open
-****************************************************************************/
-static int fd_attempt_open(char *fname, int flags, int mode)
-{
-  int fd = sys_open(fname,flags,mode);
-
-  /* Fix for files ending in '.' */
-  if((fd == -1) && (errno == ENOENT) &&
-     (strchr(fname,'.')==NULL))
-    {
-      pstrcat(fname,".");
-      fd = sys_open(fname,flags,mode);
-    }
-
-#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF))
-  if ((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];
-
-          p[max_len] = '\0';
-          if ((fd = sys_open(fname,flags,mode)) == -1)
-            p[max_len] = tmp;
-        }
-    }
-#endif
-  return fd;
-}
-
-/****************************************************************************
-Cache a uid_t currently with this file open. This is an optimization only
-used when multiple sessionsetup's have been done to one smbd.
-****************************************************************************/
-void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u)
-{
-  if(fd_ptr->uid_cache_count >= sizeof(fd_ptr->uid_users_cache)/sizeof(uid_t))
-    return;
-  fd_ptr->uid_users_cache[fd_ptr->uid_cache_count++] = u;
-}
-
-/****************************************************************************
-Remove a uid_t that currently has this file open. This is an optimization only
-used when multiple sessionsetup's have been done to one smbd.
-****************************************************************************/
-static void fd_remove_from_uid_cache(file_fd_struct *fd_ptr, uid_t u)
-{
-  int i;
-  for(i = 0; i < fd_ptr->uid_cache_count; i++)
-    if(fd_ptr->uid_users_cache[i] == u) {
-      if(i < (fd_ptr->uid_cache_count-1))
-        memmove((char *)&fd_ptr->uid_users_cache[i], (char *)&fd_ptr->uid_users_cache[i+1],
-               sizeof(uid_t)*(fd_ptr->uid_cache_count-1-i) );
-      fd_ptr->uid_cache_count--;
-    }
-  return;
-}
-
-/****************************************************************************
-Check if a uid_t that currently has this file open is present. This is an
-optimization only used when multiple sessionsetup's have been done to one smbd.
-****************************************************************************/
-static BOOL fd_is_in_uid_cache(file_fd_struct *fd_ptr, uid_t u)
-{
-  int i;
-  for(i = 0; i < fd_ptr->uid_cache_count; i++)
-    if(fd_ptr->uid_users_cache[i] == u)
-      return True;
-  return False;
-}
-
-
-/****************************************************************************
-fd support routines - attempt to re-open an already open fd as O_RDWR.
-Save the already open fd (we cannot close due to POSIX file locking braindamage.
-****************************************************************************/
-static void fd_attempt_reopen(char *fname, int mode, file_fd_struct *fd_ptr)
-{
-  int fd = sys_open( fname, O_RDWR, mode);
-
-  if(fd == -1)
-    return;
-
-  if(fd_ptr->real_open_flags == O_RDONLY)
-    fd_ptr->fd_readonly = fd_ptr->fd;
-  if(fd_ptr->real_open_flags == O_WRONLY)
-    fd_ptr->fd_writeonly = fd_ptr->fd;
-
-  fd_ptr->fd = fd;
-  fd_ptr->real_open_flags = O_RDWR;
-}
-
-/****************************************************************************
-fd support routines - attempt to close the file referenced by this fd.
-Decrements the ref_count and returns it.
-****************************************************************************/
-static int fd_attempt_close(file_fd_struct *fd_ptr)
-{
-  extern struct current_user current_user;
-
-  DEBUG(3,("fd_attempt_close fd = %d, dev = %x, inode = %x, open_flags = %d, ref_count = %d.\n",
-          fd_ptr->fd, fd_ptr->dev, fd_ptr->inode,
-          fd_ptr->real_open_flags,
-          fd_ptr->ref_count));
-  if(fd_ptr->ref_count > 0) {
-    fd_ptr->ref_count--;
-    if(fd_ptr->ref_count == 0) {
-      if(fd_ptr->fd != -1)
-        close(fd_ptr->fd);
-      if(fd_ptr->fd_readonly != -1)
-        close(fd_ptr->fd_readonly);
-      if(fd_ptr->fd_writeonly != -1)
-        close(fd_ptr->fd_writeonly);
-      fd_ptr->fd = -1;
-      fd_ptr->fd_readonly = -1;
-      fd_ptr->fd_writeonly = -1;
-      fd_ptr->real_open_flags = -1;
-      fd_ptr->dev = (uint32)-1;
-      fd_ptr->inode = (uint32)-1;
-      fd_ptr->uid_cache_count = 0;
-    } else {
-      fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
-    }
-  } 
- return fd_ptr->ref_count;
-}
-
-/****************************************************************************
-fd support routines - check that current user has permissions
-to open this file. Used when uid not found in optimization cache.
-This is really ugly code, as due to POSIX locking braindamage we must
-fork and then attempt to open the file, and return success or failure
-via an exit code.
-****************************************************************************/
-static BOOL check_access_allowed_for_current_user( char *fname, int accmode )
-{
-  pid_t child_pid;
-
-  if((child_pid = fork()) < 0) {
-    DEBUG(0,("check_access_allowed_for_current_user: fork failed.\n"));
-    return False;
-  }
-
-  if(child_pid) {
-    /*
-     * Parent.
-     */
-    pid_t wpid;
-    int status_code;
-    if ((wpid = sys_waitpid(child_pid, &status_code, 0)) < 0) {
-      DEBUG(0,("check_access_allowed_for_current_user: The process is no longer waiting!\n"));
-      return(False);
-    }
-
-    if (child_pid != wpid) {
-      DEBUG(0,("check_access_allowed_for_current_user: We were waiting for the wrong process ID\n"));
-      return(False);
-    }
-#if defined(WIFEXITED) && defined(WEXITSTATUS)
-    if (WIFEXITED(status_code) == 0) {
-      DEBUG(0,("check_access_allowed_for_current_user: The process exited while we were waiting\n"));
-      return(False);
-    }
-    if (WEXITSTATUS(status_code) != 0) {
-      DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access denied.\n", status_code));
-      return(False);
-    }
-#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
-    if(status_code != 0) {
-      DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access denied.\n", status_code));
-      return(False);
-    }
-#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
-
-    /*
-     * Success - the child could open the file.
-     */
-    DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access allowed.\n", status_code));
-    return True;
-  } else {
-    /*
-     * Child.
-     */
-    int fd;
-    DEBUG(9,("check_access_allowed_for_current_user: Child - attempting to open %s with mode %d.\n", fname, accmode ));
-    if((fd = fd_attempt_open( fname, accmode, 0)) < 0) {
-      /* Access denied. */
-      _exit(EACCES);
-    }
-    close(fd);
-    DEBUG(9,("check_access_allowed_for_current_user: Child - returning ok.\n"));
-    _exit(0);
-  }
-
-  return False;
-}
-
-/****************************************************************************
-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,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;
-       }
-}
-
-/****************************************************************************
-open a file
-****************************************************************************/
-static void open_file(files_struct *fsp,connection_struct *conn,
-                     char *fname1,int flags,int mode, struct stat *sbuf)
-{
-  extern struct current_user current_user;
-  pstring fname;
-  struct stat statbuf;
-  file_fd_struct *fd_ptr;
-  int accmode = (flags & (O_RDONLY | O_WRONLY | O_RDWR));
-
-  fsp->open = False;
-  fsp->fd_ptr = 0;
-  fsp->granted_oplock = False;
-  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 (conn->read_only && !conn->printer) {
-    /* 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;
-    } 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 handles a bug in Win95 - it doesn't say to create the file when it 
-     should */
-  if (conn->printer) {
-         flags |= O_CREAT;
-  }
-
-/*
-  if (flags == O_WRONLY)
-    DEBUG(3,("Bug in client? Set O_WRONLY without O_CREAT\n"));
-*/
-
-  /*
-   * Ensure we have a valid struct stat so we can search the
-   * open fd table.
-   */
-  if(sbuf == 0) {
-    if(sys_stat(fname, &statbuf) < 0) {
-      if(errno != ENOENT) {
-        DEBUG(3,("Error doing stat on file %s (%s)\n",
-                 fname,strerror(errno)));
-
-        check_for_pipe(fname);
-        return;
-      }
-      sbuf = 0;
-    } else {
-      sbuf = &statbuf;
-    }
-  }
-
-  /*
-   * Check to see if we have this file already
-   * open. If we do, just use the already open fd and increment the
-   * reference count (fd_get_already_open increments the ref_count).
-   */
-  if((fd_ptr = fd_get_already_open(sbuf))!= 0) {
-    /*
-     * File was already open.
-     */
-
-    /* 
-     * Check it wasn't open for exclusive use.
-     */
-    if((flags & O_CREAT) && (flags & O_EXCL)) {
-      fd_ptr->ref_count--;
-      errno = EEXIST;
-      return;
-    }
-
-    /*
-     * Ensure that the user attempting to open
-     * this file has permissions to do so, if
-     * the user who originally opened the file wasn't
-     * the same as the current user.
-     */
-
-    if(!fd_is_in_uid_cache(fd_ptr, (uid_t)current_user.uid)) {
-      if(!check_access_allowed_for_current_user( fname, accmode )) {
-        /* Error - permission denied. */
-        DEBUG(3,("Permission denied opening file %s (flags=%d, accmode = %d)\n",
-              fname, flags, accmode));
-        /* Ensure the ref_count is decremented. */
-        fd_ptr->ref_count--;
-        fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
-        errno = EACCES;
-        return;
-      }
-    }
-
-    fd_add_to_uid_cache(fd_ptr, (uid_t)current_user.uid);
-
-    /* 
-     * If not opened O_RDWR try
-     * and do that here - a chmod may have been done
-     * between the last open and now. 
-     */
-    if(fd_ptr->real_open_flags != O_RDWR)
-      fd_attempt_reopen(fname, mode, fd_ptr);
-
-    /*
-     * Ensure that if we wanted write access
-     * it has been opened for write, and if we wanted read it
-     * was open for read. 
-     */
-    if(((accmode == O_WRONLY) && (fd_ptr->real_open_flags == O_RDONLY)) ||
-       ((accmode == O_RDONLY) && (fd_ptr->real_open_flags == O_WRONLY)) ||
-       ((accmode == O_RDWR) && (fd_ptr->real_open_flags != O_RDWR))) {
-      DEBUG(3,("Error opening (already open for flags=%d) file %s (%s) (flags=%d)\n",
-               fd_ptr->real_open_flags, fname,strerror(EACCES),flags));
-      check_for_pipe(fname);
-      fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
-      fd_ptr->ref_count--;
-      return;
-    }
-
-  } else {
-    int open_flags;
-    /* We need to allocate a new file_fd_struct (this increments the
-       ref_count). */
-    if((fd_ptr = fd_get_new()) == 0)
-      return;
-    /*
-     * Whatever the requested flags, attempt read/write access,
-     * as we don't know what flags future file opens may require.
-     * If this fails, try again with the required flags. 
-     * Even if we open read/write when only read access was 
-     * requested the setting of the can_write flag in
-     * the file_struct will protect us from errant
-     * write requests. We never need to worry about O_APPEND
-     * as this is not set anywhere in Samba.
-     */
-    fd_ptr->real_open_flags = O_RDWR;
-    /* Set the flags as needed without the read/write modes. */
-    open_flags = flags & ~(O_RDWR|O_WRONLY|O_RDONLY);
-    fd_ptr->fd = fd_attempt_open(fname, open_flags|O_RDWR, mode);
-    /*
-     * On some systems opening a file for R/W access on a read only
-     * filesystems sets errno to EROFS.
-     */
-#ifdef EROFS
-    if((fd_ptr->fd == -1) && ((errno == EACCES) || (errno == EROFS))) {
-#else /* No EROFS */
-    if((fd_ptr->fd == -1) && (errno == EACCES)) {
-#endif /* EROFS */
-      if(accmode != O_RDWR) {
-        fd_ptr->fd = fd_attempt_open(fname, open_flags|accmode, mode);
-        fd_ptr->real_open_flags = accmode;
-      }
-    }
-  }
-
-  if ((fd_ptr->fd >=0) && 
-      conn->printer && lp_minprintspace(SNUM(conn))) {
-    pstring dname;
-    int dum1,dum2,dum3;
-    char *p;
-    pstrcpy(dname,fname);
-    p = strrchr(dname,'/');
-    if (p) *p = 0;
-    if (sys_disk_free(dname,&dum1,&dum2,&dum3) < 
-       lp_minprintspace(SNUM(conn))) {
-      fd_attempt_close(fd_ptr);
-      fsp->fd_ptr = 0;
-      if(fd_ptr->ref_count == 0)
-        sys_unlink(fname);
-      errno = ENOSPC;
-      return;
-    }
-  }
-    
-  if (fd_ptr->fd < 0)
-  {
-    DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
-      fname,strerror(errno),flags));
-    /* Ensure the ref_count is decremented. */
-    fd_attempt_close(fd_ptr);
-    check_for_pipe(fname);
-    return;
-  }
-
-  if (fd_ptr->fd >= 0)
-  {
-    if(sbuf == 0) {
-      /* Do the fstat */
-      if(fstat(fd_ptr->fd, &statbuf) == -1) {
-        /* Error - backout !! */
-        DEBUG(3,("Error doing fstat on fd %d, file %s (%s)\n",
-                 fd_ptr->fd, fname,strerror(errno)));
-        /* Ensure the ref_count is decremented. */
-        fd_attempt_close(fd_ptr);
-        return;
-      }
-      sbuf = &statbuf;
-    }
-
-    /* Set the correct entries in fd_ptr. */
-    fd_ptr->dev = (uint32)sbuf->st_dev;
-    fd_ptr->inode = (uint32)sbuf->st_ino;
-
-    fsp->fd_ptr = fd_ptr;
-    conn->num_files_open++;
-    fsp->mode = sbuf->st_mode;
-    GetTimeOfDay(&fsp->open_time);
-    fsp->vuid = current_user.vuid;
-    fsp->size = 0;
-    fsp->pos = -1;
-    fsp->open = True;
-    fsp->mmap_ptr = NULL;
-    fsp->mmap_size = 0;
-    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->print_file = conn->printer;
-    fsp->modified = False;
-    fsp->granted_oplock = False;
-    fsp->sent_oplock_break = False;
-    fsp->is_directory = False;
-    fsp->conn = conn;
-    /*
-     * Note that the file name here is the *untranslated* name
-     * ie. it is still in the DOS codepage sent from the client.
-     * All use of this filename will pass though the sys_xxxx
-     * functions which will do the dos_to_unix translation before
-     * mapping into a UNIX filename. JRA.
-     */
-    string_set(&fsp->fsp_name,fname);
-    fsp->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 (fsp->print_file && lp_postscript(SNUM(conn)) && fsp->can_write) {
-           DEBUG(3,("Writing postscript line\n"));
-           write_file(fsp,"%!\n",3);
-    }
-      
-    DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
-            *sesssetup_user ? sesssetup_user : conn->user,fname,
-            BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
-            conn->num_files_open));
-
-  }
-
-#if WITH_MMAP
-  /* mmap it if read-only */
-  if (!fsp->can_write) {
-         fsp->mmap_size = file_size(fname);
-         fsp->mmap_ptr = (char *)mmap(NULL,fsp->mmap_size,
-                                      PROT_READ,MAP_SHARED,fsp->fd_ptr->fd,0);
-
-         if (fsp->mmap_ptr == (char *)-1 || !fsp->mmap_ptr) {
-                 DEBUG(3,("Failed to mmap() %s - %s\n",
-                          fname,strerror(errno)));
-                 fsp->mmap_ptr = NULL;
-         }
-  }
-#endif
-}
-
-/****************************************************************************
-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(fsp->fsp_name,'/')))
-      p = fsp->fsp_name;
-    else
-      p++;
-
-    if (!strequal(lp_magicscript(SNUM(conn)),p))
-      return;
-  }
-
-  {
-    int ret;
-    pstring magic_output;
-    pstring fname;
-    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,magic_output,False);
-    DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
-    unlink(fname);
-  }
-}
-
-/****************************************************************************
-  Common code to close a file or a directory.
-****************************************************************************/
-static void close_filestruct(files_struct *fsp)
-{   
-       connection_struct *conn = fsp->conn;
-    
-       fsp->open = False;
-       fsp->is_directory = False; 
-    
-       conn->num_files_open--;
-       if(fsp->wbmpx_ptr) {  
-               free((char *)fsp->wbmpx_ptr);
-               fsp->wbmpx_ptr = NULL; 
-       }  
-     
-#if WITH_MMAP
-       if(fsp->mmap_ptr) {
-               munmap(fsp->mmap_ptr,fsp->mmap_size);
-               fsp->mmap_ptr = NULL;
-       }  
-#endif 
-}    
-
-/****************************************************************************
- Close a file - possibly invalidating the read prediction.
-
- 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.
-****************************************************************************/
-void close_file(files_struct *fsp, BOOL normal_close)
-{
-       uint32 dev = fsp->fd_ptr->dev;
-       uint32 inode = fsp->fd_ptr->inode;
-       int token;
-       connection_struct *conn = fsp->conn;
-
-       close_filestruct(fsp);
-
-#if USE_READ_PREDICTION
-       invalidate_read_prediction(fsp->fd_ptr->fd);
-#endif
-
-       if (lp_share_modes(SNUM(conn))) {
-               lock_share_entry(conn, dev, inode, &token);
-               del_share_mode(token, fsp);
-       }
-
-       fd_attempt_close(fsp->fd_ptr);
-
-       if (lp_share_modes(SNUM(conn)))
-               unlock_share_entry(conn, dev, inode, token);
-
-       /* NT uses smbclose to start a print - weird */
-       if (normal_close && fsp->print_file)
-               print_file(conn, fsp);
-
-       /* check for magic scripts */
-       if (normal_close) {
-               check_magic(fsp,conn);
-       }
-
-       if(fsp->granted_oplock == True)
-               global_oplocks_open--;
-
-       fsp->sent_oplock_break = False;
-
-       DEBUG(2,("%s closed file %s (numopen=%d)\n",
-                conn->user,fsp->fsp_name,
-                conn->num_files_open));
-
-       if (fsp->fsp_name) {
-               string_free(&fsp->fsp_name);
-       }
-
-       file_free(fsp);
-}
-
-/****************************************************************************
- Close a directory opened by an NT SMB call. 
-****************************************************************************/
-  
-void close_directory(files_struct *fsp)
-{
-       /* TODO - walk the list of pending
-          change notify requests and free
-          any pertaining to this fsp. */
-
-       remove_pending_change_notify_requests_by_fid(fsp);
-
-       /*
-        * Do the code common to files and directories.
-        */
-       close_filestruct(fsp);
-       
-       if (fsp->fsp_name)
-               string_free(&fsp->fsp_name);
-       
-       file_free(fsp);
-}
-
-/****************************************************************************
- Open a directory from an NT SMB call.
-****************************************************************************/
-int open_directory(files_struct *fsp,connection_struct *conn,
-                  char *fname, int smb_ofun, int unixmode, int *action)
-{
-       extern struct current_user current_user;
-       struct stat st;
-
-       if (smb_ofun & 0x10) {
-               /*
-                * Create the directory.
-                */
-
-               if(sys_mkdir(fname, unixmode) < 0) {
-                       DEBUG(0,("open_directory: unable to create %s. Error was %s\n",
-                                fname, strerror(errno) ));
-                       return -1;
-               }
-
-               *action = FILE_WAS_CREATED;
-       } else {
-               /*
-                * Check that it *was* a directory.
-                */
-
-               if(sys_stat(fname, &st) < 0) {
-                       DEBUG(0,("open_directory: unable to stat name = %s. Error was %s\n",
-                                fname, strerror(errno) ));
-                       return -1;
-               }
-
-               if(!S_ISDIR(st.st_mode)) {
-                       DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
-                       return -1;
-               }
-               *action = FILE_WAS_OPENED;
-       }
-       
-       DEBUG(5,("open_directory: opening directory %s\n",
-                fname));
-
-       /*
-        * Setup the files_struct for it.
-        */
-       
-       fsp->fd_ptr = NULL;
-       conn->num_files_open++;
-       fsp->mode = 0;
-       GetTimeOfDay(&fsp->open_time);
-       fsp->vuid = current_user.vuid;
-       fsp->size = 0;
-       fsp->pos = -1;
-       fsp->open = True;
-       fsp->mmap_ptr = NULL;
-       fsp->mmap_size = 0;
-       fsp->can_lock = True;
-       fsp->can_read = False;
-       fsp->can_write = False;
-       fsp->share_mode = 0;
-       fsp->print_file = False;
-       fsp->modified = False;
-       fsp->granted_oplock = False;
-       fsp->sent_oplock_break = False;
-       fsp->is_directory = True;
-       fsp->conn = conn;
-       /*
-        * Note that the file name here is the *untranslated* name
-        * ie. it is still in the DOS codepage sent from the client.
-        * All use of this filename will pass though the sys_xxxx
-        * functions which will do the dos_to_unix translation before
-        * mapping into a UNIX filename. JRA.
-        */
-       string_set(&fsp->fsp_name,fname);
-       fsp->wbmpx_ptr = NULL;
-
-       return 0;
-}
-
-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) {
-    int pid = getpid();
-    if (old_deny == new_deny && share_pid == pid) 
-       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(connection_struct *conn,char *fname, BOOL rename_op)
-{
-  int i;
-  int ret = False;
-  share_mode_entry *old_shares = 0;
-  int num_share_modes;
-  struct stat sbuf;
-  int token;
-  int pid = getpid();
-  uint32 dev, inode;
-
-  if(!lp_share_modes(SNUM(conn)))
-    return True;
-
-  if (sys_stat(fname,&sbuf) == -1) return(True);
-
-  dev = (uint32)sbuf.st_dev;
-  inode = (uint32)sbuf.st_ino;
-
-  lock_share_entry(conn, dev, inode, &token);
-  num_share_modes = get_share_modes(conn, token, 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(share_entry->op_type & BATCH_OPLOCK)
-        {
-
-          /*
-           * It appears that the NT redirector may have a bug, in that
-           * it tries to do an SMBmv on a file that it has open with a
-           * batch oplock, and then fails to respond to the oplock break
-           * request. This only seems to occur when the client is doing an
-           * SMBmv to the smbd it is using - thus we try and detect this
-           * condition by checking if the file being moved is open and oplocked by
-           * this smbd process, and then not sending the oplock break in this
-           * special case. If the file was open with a deny mode that 
-           * prevents the move the SMBmv will fail anyway with a share
-           * violation error. JRA.
-           */
-          if(rename_op && (share_entry->pid == pid))
-          {
-            DEBUG(0,("check_file_sharing: NT redirector workaround - rename attempted on \
-batch oplocked file %s, dev = %x, inode = %x\n", fname, dev, inode));
-            /* 
-             * This next line is a test that allows the deny-mode
-             * processing to be skipped. This seems to be needed as
-             * NT insists on the rename succeeding (in Office 9x no less !).
-             * This should be removed as soon as (a) MS fix the redirector
-             * bug or (b) NT SMB support in Samba makes NT not issue the
-             * call (as is my fervent hope). JRA.
-             */ 
-            continue;
-          }
-          else
-          {
-            DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \
-dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
-
-            /* Oplock break.... */
-            unlock_share_entry(conn, dev, inode, token);
-            if(request_oplock_break(share_entry, dev, inode) == False)
-            {
-              free((char *)old_shares);
-              DEBUG(0,("check_file_sharing: FAILED when breaking oplock (%x) on file %s, \
-dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
-              return False;
-            }
-            lock_share_entry(conn, dev, inode, &token);
-            broke_oplock = True;
-            break;
-          }
-        }
-
-        /* someone else has a share lock on it, check to see 
-           if we can too */
-        if ((share_entry->share_mode != DENY_DOS) || (share_entry->pid != pid))
-          goto free_and_exit;
-
-      } /* end for */
-
-      if(broke_oplock)
-      {
-        free((char *)old_shares);
-        num_share_modes = get_share_modes(conn, token, 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() */
-  ret = True;
-
-free_and_exit:
-
-  unlock_share_entry(conn, dev, inode, token);
-  if(old_shares != NULL)
-    free((char *)old_shares);
-  return(ret);
-}
-
-/****************************************************************************
-  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(files_struct *fsp, connection_struct *conn, int token, 
-                                  BOOL *share_locked)
-{
-  if (fsp->can_write){
-    if (is_locked(fsp,conn,0x3FFFFFFF,0,F_WRLCK)){
-      /* If share modes are in force for this connection we
-         have the share entry locked. Unlock it before closing. */
-      if (*share_locked && lp_share_modes(SNUM(conn)))
-        unlock_share_entry( conn, fsp->fd_ptr->dev, 
-                            fsp->fd_ptr->inode, token);
-      close_file(fsp,False);   
-      /* Share mode no longer locked. */
-      *share_locked = False;
-      errno = EACCES;
-      unix_ERR_class = ERRDOS;
-      unix_ERR_code = ERRlock;
-    }
-    else
-      ftruncate(fsp->fd_ptr->fd,0); 
-  }
-}
-
-/****************************************************************************
-check if we can open a file with a share mode
-****************************************************************************/
-int check_share_mode( share_mode_entry *share, int deny_mode, char *fname,
-                      BOOL fcbopen, int *flags)
-{
-  int old_open_mode = share->share_mode &0xF;
-  int old_deny_mode = (share->share_mode >>4)&7;
-
-  if (old_deny_mode > 4 || old_open_mode > 2)
-  {
-    DEBUG(0,("Invalid share mode found (%d,%d,%d) on file %s\n",
-               deny_mode,old_deny_mode,old_open_mode,fname));
-    return False;
-  }
-
-  {
-    int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
-                                share->pid,fname);
-
-    if ((access_allowed == AFAIL) ||
-        (!fcbopen && (access_allowed == AREAD && *flags == O_RDWR)) ||
-        (access_allowed == AREAD && *flags == O_WRONLY) ||
-        (access_allowed == AWRITE && *flags == O_RDONLY))
-    {
-      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,
-                share->pid,fname, fcbopen, *flags, access_allowed));
-      return False;
-    }
-
-    if (access_allowed == AREAD)
-      *flags = O_RDONLY;
-
-    if (access_allowed == AWRITE)
-      *flags = O_WRONLY;
-
-  }
-  return True;
-}
-
-/****************************************************************************
-open a file with a share mode
-****************************************************************************/
-void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun,
-                     int mode,int oplock_request, int *Access,int *action)
-{
-  int flags=0;
-  int flags2=0;
-  int deny_mode = (share_mode>>4)&7;
-  struct stat sbuf;
-  BOOL file_existed = file_exist(fname,&sbuf);
-  BOOL share_locked = False;
-  BOOL fcbopen = False;
-  int token;
-  uint32 dev = 0;
-  uint32 inode = 0;
-  int num_share_modes = 0;
-
-  fsp->open = False;
-  fsp->fd_ptr = 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 */
-
-    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 defined(O_SYNC)
-  if (share_mode&(1<<14)) {
-         flags2 |= O_SYNC;
-  }
-#endif /* O_SYNC */
-  
-  if (flags != O_RDONLY && file_existed && 
-      (!CAN_WRITE(conn) || IS_DOS_READONLY(dos_mode(conn,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(conn))) 
-  {
-    int i;
-    share_mode_entry *old_shares = 0;
-
-    if (file_existed)
-    {
-      dev = (uint32)sbuf.st_dev;
-      inode = (uint32)sbuf.st_ino;
-      lock_share_entry(conn, dev, inode, &token);
-      share_locked = True;
-      num_share_modes = get_share_modes(conn, token, dev, inode, &old_shares);
-    }
-
-    /*
-     * Check if the share modes will give us access.
-     */
-
-    if(share_locked && (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];
-
-          /* 
-           * 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(share_entry->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
-          {
-
-            DEBUG(5,("open_file_shared: breaking oplock (%x) on file %s, \
-dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
-
-            /* Oplock break.... */
-            unlock_share_entry(conn, dev, inode, token);
-            if(request_oplock_break(share_entry, dev, inode) == False)
-            {
-              free((char *)old_shares);
-              DEBUG(0,("open_file_shared: FAILED when breaking oplock (%x) on file %s, \
-dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
-              errno = EACCES;
-              unix_ERR_class = ERRDOS;
-              unix_ERR_code = ERRbadshare;
-              return;
-            }
-            lock_share_entry(conn, dev, inode, &token);
-            broke_oplock = True;
-            break;
-          }
-
-          /* someone else has a share lock on it, check to see 
-             if we can too */
-          if(check_share_mode(share_entry, deny_mode, fname, fcbopen, &flags) == False)
-          {
-            free((char *)old_shares);
-            unlock_share_entry(conn, dev, inode, token);
-            errno = EACCES;
-            unix_ERR_class = ERRDOS;
-            unix_ERR_code = ERRbadshare;
-            return;
-          }
-
-        } /* end for */
-
-        if(broke_oplock)
-        {
-          free((char *)old_shares);
-          num_share_modes = get_share_modes(conn, token, dev, inode, &old_shares);
-        }
-      } while(broke_oplock);
-    }
-
-    if(old_shares != 0)
-      free((char *)old_shares);
-  }
-
-  DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
-          flags,flags2,mode));
-
-  open_file(fsp,conn,fname,flags|(flags2&~(O_TRUNC)),mode,file_existed ? &sbuf : 0);
-  if (!fsp->open && flags==O_RDWR && errno!=ENOENT && fcbopen) 
-  {
-    flags = O_RDONLY;
-    open_file(fsp,conn,fname,flags,mode,file_existed ? &sbuf : 0 );
-  }
-
-  if (fsp->open) 
-  {
-    int open_mode=0;
-
-    if((share_locked == False) && lp_share_modes(SNUM(conn)))
-    {
-      /* We created the file - thus we must now lock the share entry before creating it. */
-      dev = fsp->fd_ptr->dev;
-      inode = fsp->fd_ptr->inode;
-      lock_share_entry(conn, dev, inode, &token);
-      share_locked = True;
-    }
-
-    switch (flags) 
-    {
-      case O_RDONLY:
-        open_mode = 0;
-        break;
-      case O_RDWR:
-        open_mode = 2;
-        break;
-      case O_WRONLY:
-        open_mode = 1;
-        break;
-    }
-
-    fsp->share_mode = (deny_mode<<4) | open_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;
-    }
-    /* We must create the share mode entry before truncate as
-       truncate can fail due to locking and have to close the
-       file (which expects the share_mode_entry to be there).
-     */
-    if (lp_share_modes(SNUM(conn)))
-    {
-      uint16 port = 0;
-      /* JRA. Currently this only services Exlcusive and batch
-         oplocks (no other opens on this file). This needs to
-         be extended to level II oplocks (multiple reader
-         oplocks). */
-
-      if(oplock_request && (num_share_modes == 0) && lp_oplocks(SNUM(conn)) && 
-             !IS_VETO_OPLOCK_PATH(conn,fname))
-      {
-        fsp->granted_oplock = True;
-        fsp->sent_oplock_break = False;
-        global_oplocks_open++;
-        port = oplock_port;
-
-        DEBUG(5,("open_file_shared: granted oplock (%x) on file %s, \
-dev = %x, inode = %x\n", oplock_request, fname, dev, inode));
-
-      }
-      else
-      {
-        port = 0;
-        oplock_request = 0;
-      }
-      set_share_mode(token, fsp, port, oplock_request);
-    }
-
-    if ((flags2&O_TRUNC) && file_existed)
-      truncate_unless_locked(fsp,conn,token,&share_locked);
-  }
-
-  if (share_locked && lp_share_modes(SNUM(conn)))
-    unlock_share_entry( conn, dev, inode, token);
-}
-
-
-/****************************************************************************
-load parameters specific to a connection/service
-****************************************************************************/
-BOOL become_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;
-
-       snum = SNUM(conn);
-  
-       if (do_chdir &&
-           ChDir(conn->connectpath) != 0 &&
-           ChDir(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);
-}
-
-
-/****************************************************************************
-  find a service entry
-****************************************************************************/
-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);
-
-      if(!phome_dir)
-      {
-        /*
-         * Try mapping the servicename, it may
-         * be a Windows to unix mapped user name.
-         */
-        if(map_username(service))
-          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 *pdefservice = lp_defaultservice();
-     if (pdefservice && *pdefservice && !strequal(pdefservice,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)
-       {
-         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,files_struct *fsp,int line)
-{
-  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(inbuf,outbuf,eclass,err,line);
-}
-
-
-struct
-{
-  int unixerror;
-  int smbclass;
-  int smbcode;
-} unix_smb_errmap[] =
-{
-  {EPERM,ERRDOS,ERRnoaccess},
-  {EACCES,ERRDOS,ERRnoaccess},
-  {ENOENT,ERRDOS,ERRbadfile},
-  {ENOTDIR,ERRDOS,ERRbadpath},
-  {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 != SMB_SUCCESS)
-    {
-      eclass = unix_ERR_class;
-      ecode = unix_ERR_code;
-      unix_ERR_class = SMB_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++;
-      }
-    }
-
-  return(error_packet(inbuf,outbuf,eclass,ecode,line));
-}
-
-
-/****************************************************************************
-  create an error packet. Normally called using the ERROR() macro
-****************************************************************************/
-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 = CVAL(inbuf,smb_com);
-  int flgs2 = SVAL(outbuf,smb_flg2); 
-
-  if ((flgs2 & FLAGS2_32_BIT_ERROR_CODES) == FLAGS2_32_BIT_ERROR_CODES)
-  {
-    SIVAL(outbuf,smb_rcls,error_code);
-    
-    DEBUG( 3, ( "32 bit error packet at line %d cmd=%d (%s) eclass=%08x [%s]\n",
-              line, cmd, smb_fn_name(cmd), error_code, smb_errstr(outbuf) ) );
-  }
-  else
-  {
-    CVAL(outbuf,smb_rcls) = error_class;
-    SSVAL(outbuf,smb_err,error_code);  
-    DEBUG( 3, ( "error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n",
-             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);
-}
-
-
-/****************************************************************************
-  this is called when the client exits abruptly
-  **************************************************************************/
-static void sig_pipe(int sig)
-{
-       struct cli_state *cli;
-       BlockSignals(True,SIGPIPE);
-
-       if ((cli = server_client()) && cli->initialised) {
-               DEBUG(3,("lost connection to password server\n"));
-               cli_shutdown(cli);
-               BlockSignals(False,SIGPIPE);
-               return;
-       }
-
-       exit_server("Got sigpipe\n");
-}
-
-/****************************************************************************
-  open the socket communication
-****************************************************************************/
-static BOOL open_sockets(BOOL is_daemon,int port)
-{
-  extern int Client;
-
-  if (is_daemon)
-  {
-    int num_interfaces = iface_count();
-    int fd_listenset[FD_SETSIZE];
-    fd_set listen_set;
-    int s;
-    int i;
-
-#ifdef HAVE_ATEXIT
-    static int atexit_set;
-    if(atexit_set == 0) {
-           atexit_set=1;
-           atexit(killkids);
-    }
-#endif
-
-    /* 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);
-        if(s == -1)
-          return False;
-        /* ready to listen */
-        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()));
-      if (s == -1)
-        return(False);
-
-      /* ready to listen */
-      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);
-    }      
-
-    /* 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;
-
-      memcpy((char *)&lfds, (char *)&listen_set, sizeof(listen_set));
-
-      num = sys_select(&lfds,NULL);
-
-      if (num == -1 && errno == EINTR)
-        continue;
-
-      /* Find the sockets that are read-ready - accept on these. */
-      for( ; num > 0; num--)
-      {
-        struct sockaddr addr;
-        int 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;
-          }
-        }
-
-        Client = accept(s,&addr,&in_addrlen);
-
-        if (Client == -1 && errno == EINTR)
-          continue;
-
-        if (Client == -1)
-        {
-          DEBUG(0,("open_sockets: accept: %s\n",strerror(errno)));
-          continue;
-        }
-
-        if (Client != -1 && fork()==0)
-        {
-          /* Child code ... */
-
-          CatchSignal(SIGPIPE, SIGNAL_CAST sig_pipe);
-
-          /* 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(Client,"SO_KEEPALIVE");
-          set_socket_options(Client,user_socket_options);
-
-          /* Reset global variables in util.c so that
-             client substitutions will be done correctly
-             in the process.
-           */
-          reset_globals_after_fork();
-          return True; 
-        }
-        close(Client); /* The parent doesn't need this socket */
-
-       /*
-        * 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();
-      } /* end for num */
-    } /* end while 1 */
-  } /* end if is_daemon */
-  else
-  {
-    /* Started from inetd. fd 0 is the socket. */
-    /* We will abort gracefully when the client or remote system 
-       goes away */
-    CatchSignal(SIGPIPE, SIGNAL_CAST sig_pipe);
-    Client = dup(0);
-
-    /* close our standard file descriptors */
-    close_low_fds();
-
-    set_socket_options(Client,"SO_KEEPALIVE");
-    set_socket_options(Client,user_socket_options);
-  }
-
-  return True;
-}
-
-/****************************************************************************
-  process an smb from the client - split out from the process() code so
-  it can be used by the oplock break code.
-****************************************************************************/
-
-static void process_smb(char *inbuf, char *outbuf)
-{
-  extern int Client;
-#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;
-
-  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 (!check_access(Client, lp_hostsallow(-1), lp_hostsdeny(-1))) {
-                 /* send a negative session response "not listining on calling
-                  name" */
-                 static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
-                 DEBUG( 1, ( "Connection denied from %s\n",
-                             client_addr(Client) ) );
-                 send_smb(Client,(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(Client, msg_type);
-        if(sslConnected < 0){   /* an error occured */
-            exit_server("SSL negotiation failed");
-        }else if(sslConnected){
-            trans_num++;
-            return;
-        }
-    }
-#endif  /* WITH_SSL */
-
-#ifdef WITH_VTP
-  if(trans_num == 1 && VT_Check(inbuf)) 
-  {
-    VT_Process();
-    return;
-  }
-#endif
-
-  if (msg_type == 0)
-    show_msg(inbuf);
-  else if(msg_type == 0x85)
-    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
-      send_smb(Client,outbuf);
-  }
-  trans_num++;
-}
-
-/****************************************************************************
-  open the oplock IPC socket communication
-****************************************************************************/
-static BOOL open_oplock_ipc(void)
-{
-  struct sockaddr_in sock_name;
-  int 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));
-  if (oplock_sock == -1)
-  {
-    DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
-address %x. Error was %s\n", htonl(INADDR_LOOPBACK), strerror(errno)));
-    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;
-    oplock_port = 0;
-    return False;
-  }
-  oplock_port = ntohs(sock_name.sin_port);
 
-  DEBUG(3,("open_oplock ipc: pid = %d, oplock_port = %u\n", 
-            (int)getpid(), oplock_port));
-
-  return True;
-}
 
 /****************************************************************************
-  process an oplock break message.
-****************************************************************************/
-static BOOL process_local_message(int sock, char *buffer, int buf_size)
+  this is called when the client exits abruptly
+  **************************************************************************/
+static void sig_pipe(int sig)
 {
-  int32 msg_len;
-  uint16 from_port;
-  char *msg_start;
-
-  msg_len = IVAL(buffer,UDP_CMD_LEN_OFFSET);
-  from_port = SVAL(buffer,UDP_CMD_PORT_OFFSET);
-
-  msg_start = &buffer[UDP_CMD_HEADER_LEN];
-
-  DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n", 
-            msg_len, from_port));
-
-  /* Switch on message command - currently OPLOCK_BREAK_CMD is the
-     only valid request. */
-
-  switch(SVAL(msg_start,UDP_MESSAGE_CMD_OFFSET))
-  {
-    case 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", msg_len, OPLOCK_BREAK_MSG_LEN));
-        return False;
-      }
-      {
-        uint32 remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET);
-        uint32 dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
-        uint32 inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET);
-        struct timeval tval;
-        struct sockaddr_in toaddr;
-
-        tval.tv_sec = IVAL(msg_start, OPLOCK_BREAK_SEC_OFFSET);
-        tval.tv_usec = IVAL(msg_start, OPLOCK_BREAK_USEC_OFFSET);
-
-        DEBUG(5,("process_local_message: oplock break request from \
-pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
-
-        /*
-         * 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 return success in this case.
-         */
-
-        if(global_oplocks_open != 0)
-        {
-          if(oplock_break(dev, inode, &tval) == False)
-          {
-            DEBUG(0,("process_local_message: oplock break failed - \
-not returning udp message.\n"));
-            return False;
-          }
-        }
-        else
-        {
-          DEBUG(3,("process_local_message: oplock break requested with no outstanding \
-oplocks. Returning success.\n"));
-        }
-
-        /* Send the message back after OR'ing in the 'REPLY' bit. */
-        SSVAL(msg_start,UDP_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY);
-  
-        bzero((char *)&toaddr,sizeof(toaddr));
-        toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-        toaddr.sin_port = htons(from_port);
-        toaddr.sin_family = AF_INET;
-
-        if(sendto( 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",
-                    remotepid, strerror(errno)));
-          return False;
-        }
-
-        DEBUG(5,("process_local_message: oplock break reply sent to \
-pid %d, port %d, for file dev = %x, inode = %x\n", remotepid, 
-                from_port, dev, inode));
-
-      }
-      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", msg_len, OPLOCK_BREAK_MSG_LEN));
-        return False;
-      }
-
-      {
-        uint32 remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET);
-        uint32 dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
-        uint32 inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET);
-
-        DEBUG(0,("process_local_message: unsolicited oplock break reply from \
-pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
+       struct cli_state *cli;
+       BlockSignals(True,SIGPIPE);
 
-       }
-       return False;
+       if ((cli = server_client()) && cli->initialised) {
+               DEBUG(3,("lost connection to password server\n"));
+               cli_shutdown(cli);
+               BlockSignals(False,SIGPIPE);
+               return;
+       }
 
-    default:
-      DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n",
-                (unsigned int)SVAL(msg_start,0)));
-      return False;
-  }
-  return True;
+       exit_server("Got sigpipe\n");
 }
 
 /****************************************************************************
- Process an oplock break directly.
+  open the socket communication
 ****************************************************************************/
-BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval)
+static BOOL open_sockets(BOOL is_daemon,int port)
 {
-  extern struct current_user current_user;
   extern int Client;
-  char *inbuf = NULL;
-  char *outbuf = NULL;
-  files_struct *fsp = NULL;
-  time_t start_time;
-  BOOL shutdown_server = False;
-  connection_struct *saved_conn;
-  int saved_vuid;
-  pstring saved_dir; 
-
-  if( DEBUGLVL( 3 ) )
-    {
-    dbgtext( "oplock_break: called for dev = %x, inode = %x.\n", dev, inode );
-    dbgtext( "Current global_oplocks_open = %d\n", global_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_dit(dev, inode, tval);
-
-  if(fsp == NULL)
-  {
-    /* The file could have been closed in the meantime - return success. */
-    if( DEBUGLVL( 0 ) )
-      {
-      dbgtext( "oplock_break: cannot find open file with " );
-      dbgtext( "dev = %x, inode = %x ", dev, inode);
-      dbgtext( "allowing break to succeed.\n" );
-      }
-    return True;
-  }
-
-  /* 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->granted_oplock)
-  {
-    if( DEBUGLVL( 0 ) )
-      {
-      dbgtext( "oplock_break: file %s ", fsp->fsp_name );
-      dbgtext( "(dev = %x, inode = %x) has no oplock.\n", dev, inode );
-      dbgtext( "Allowing break to succeed regardless.\n" );
-      }
-    return True;
-  }
-
-  /* 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 = %x)\n", dev, inode );
-      }
-
-    /* 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;
-  }
-
-  /* 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 + SAFETY_MARGIN))==NULL)
-  {
-    DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
-    return False;
-  }
-
-  if((outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN))==NULL)
-  {
-    DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
-    free(inbuf);
-    inbuf = NULL;
-    return False;
-  }
-
-  /* Prepare the SMBlockingX message. */
-  bzero(outbuf,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);
-  /* Change this when we have level II oplocks. */
-  SCVAL(outbuf,smb_vwv3+1,OPLOCKLEVEL_NONE);
-  send_smb(Client, outbuf);
-
-  /* Remember we just sent an oplock break on this file. */
-  fsp->sent_oplock_break = True;
-
-  /* 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_conn = fsp->conn;
-  saved_vuid = current_user.vuid;
-  GetWd(saved_dir);
-  unbecome_user();
-
-  while(OPEN_FSP(fsp) && fsp->granted_oplock)
-  {
-    if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
-    {
-      /*
-       * Die if we got an error.
-       */
-
-      if (smb_read_error == READ_EOF)
-        DEBUG( 0, ( "oplock_break: end of file from client\n" ) );
-      if (smb_read_error == READ_ERROR)
-        DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
-
-      if (smb_read_error == READ_TIMEOUT)
-        DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n",
-                     OPLOCK_BREAK_TIMEOUT ) );
-
-      DEBUGADD( 0, ( "oplock_break failed for file %s ", fsp->fsp_name ) );
-      DEBUGADD( 0, ( "(dev = %x, inode = %x).\n", dev, inode));
-      shutdown_server = True;
-      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 = %x).\n", dev, inode );
-        }
-      shutdown_server = True;
-      break;
-    }
-  }
-
-  /*
-   * Go back to being the user who requested the oplock
-   * break.
-   */
-  if(!become_user(saved_conn, saved_vuid))
-  {
-    DEBUG( 0, ( "oplock_break: unable to re-become user!" ) );
-    DEBUGADD( 0, ( "Shutting down server\n" ) );
-    close_sockets();
-    close(oplock_sock);
-    exit_server("unable to re-become user");
-  }
-  /* Including the directory. */
-  ChDir(saved_dir);
-
-  /* Free the buffers we've been using to recurse. */
-  free(inbuf);
-  free(outbuf);
-
-  /* We need this in case a readraw crossed on the wire. */
-  if(global_oplock_break)
-    global_oplock_break = False;
-
-  /*
-   * If the client did not respond we must die.
-   */
-
-  if(shutdown_server)
-  {
-    DEBUG( 0, ( "oplock_break: client failure in break - " ) );
-    DEBUGADD( 0, ( "shutting down this smbd.\n" ) );
-    close_sockets();
-    close(oplock_sock);
-    exit_server("oplock break failure");
-  }
-
-  if(OPEN_FSP(fsp))
-  {
-    /* The lockingX reply will have removed the oplock flag 
-       from the sharemode. */
-    /* Paranoia.... */
-    fsp->granted_oplock = False;
-    fsp->sent_oplock_break = False;
-    global_oplocks_open--;
-  }
 
-  /* Santity check - remove this later. JRA */
-  if(global_oplocks_open < 0)
+  if (is_daemon)
   {
-    DEBUG(0,("oplock_break: global_oplocks_open < 0 (%d). PANIC ERROR\n",
-              global_oplocks_open));
-    exit_server("oplock_break: global_oplocks_open < 0");
-  }
+    int num_interfaces = iface_count();
+    int fd_listenset[FD_SETSIZE];
+    fd_set listen_set;
+    int s;
+    int i;
 
-  if( DEBUGLVL( 3 ) )
-    {
-    dbgtext( "oplock_break: returning success for " );
-    dbgtext( "dev = %x, inode = %x.\n", dev, inode );
-    dbgtext( "Current global_oplocks_open = %d\n", global_oplocks_open );
+#ifdef HAVE_ATEXIT
+    static int atexit_set;
+    if(atexit_set == 0) {
+           atexit_set=1;
+           atexit(killkids);
     }
+#endif
 
-  return True;
-}
+    /* Stop zombies */
+    CatchChild();
 
-/****************************************************************************
-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, 
-                          uint32 dev, uint32 inode)
-{
-  char op_break_msg[OPLOCK_BREAK_MSG_LEN];
-  struct sockaddr_in addr_out;
-  int pid = getpid();
-  time_t start_time;
-  int time_left;
+    FD_ZERO(&listen_set);
 
-  if(pid == share_entry->pid)
-  {
-    /* We are breaking our own oplock, make sure it's us. */
-    if(share_entry->op_port != oplock_port)
+    if(lp_interfaces() && lp_bind_interfaces_only())
     {
-      DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
-should be %d\n", pid, share_entry->op_port, oplock_port));
-      return False;
-    }
+       /* 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.
+        */
 
-    DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
+      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;
+      }
 
-    /* Call oplock break direct. */
-    return oplock_break(dev, inode, &share_entry->time);
-  }
+      /* 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);
 
-  /* We need to send a OPLOCK_BREAK_CMD message to the
-     port in the share mode entry. */
-
-  SSVAL(op_break_msg,UDP_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD);
-  SIVAL(op_break_msg,OPLOCK_BREAK_PID_OFFSET,pid);
-  SIVAL(op_break_msg,OPLOCK_BREAK_DEV_OFFSET,dev);
-  SIVAL(op_break_msg,OPLOCK_BREAK_INODE_OFFSET,inode);
-  SIVAL(op_break_msg,OPLOCK_BREAK_SEC_OFFSET,(uint32)share_entry->time.tv_sec);
-  SIVAL(op_break_msg,OPLOCK_BREAK_USEC_OFFSET,(uint32)share_entry->time.tv_usec);
-
-  /* set the address and port */
-  bzero((char *)&addr_out,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 ", share_entry->pid, share_entry->op_port );
-    dbgtext( "for dev = %x, inode = %x\n", dev, inode );
+        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);
+        if(s == -1)
+          return False;
+        /* ready to listen */
+        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;
 
-  if(sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0,
-         (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0)
-  {
-    if( DEBUGLVL( 0 ) )
+      /* open an incoming socket */
+      s = open_socket_in(SOCK_STREAM, port, 0,interpret_addr(lp_socket_address()));
+      if (s == -1)
+        return(False);
+
+      /* ready to listen */
+      if (listen(s, 5) == -1) 
       {
-      dbgtext( "request_oplock_break: failed when sending a oplock " );
-      dbgtext( "break message to pid %d ", share_entry->pid );
-      dbgtext( "on port %d ", share_entry->op_port );
-      dbgtext( "for dev = %x, inode = %x.\n", dev, inode );
-      dbgtext( "Error was %s\n", strerror(errno) );
+        DEBUG(0,("open_sockets: listen: %s\n",strerror(errno)));
+        close(s);
+        return False;
       }
-    return False;
-  }
 
-  /*
-   * 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.
-   */
+      fd_listenset[0] = s;
+      FD_SET(s,&listen_set);
+    }      
 
-  start_time = time(NULL);
-  time_left = OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR;
+    /* 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;
 
-  while(time_left >= 0)
-  {
-    char op_break_reply[UDP_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
-    int32 reply_msg_len;
-    uint16 reply_from_port;
-    char *reply_msg_start;
+      memcpy((char *)&lfds, (char *)&listen_set, sizeof(listen_set));
 
-    if(receive_local_message(oplock_sock, op_break_reply, sizeof(op_break_reply),
-               time_left ? time_left * 1000 : 1) == False)
-    {
-      if(smb_read_error == READ_TIMEOUT)
+      num = sys_select(&lfds,NULL);
+
+      if (num == -1 && errno == EINTR)
+        continue;
+
+      /* Find the sockets that are read-ready - accept on these. */
+      for( ; num > 0; num--)
       {
-        if( DEBUGLVL( 0 ) )
-          {
-          dbgtext( "request_oplock_break: no response received to oplock " );
-          dbgtext( "break request to pid %d ", share_entry->pid );
-          dbgtext( "on port %d ", share_entry->op_port );
-          dbgtext( "for dev = %x, inode = %x\n", dev, inode );
-          }
-        /*
-         * 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 ) )
+        struct sockaddr addr;
+        int in_addrlen = sizeof(addr);
+
+        s = -1;
+        for(i = 0; i < num_interfaces; i++)
+        {
+          if(FD_ISSET(fd_listenset[i],&lfds))
           {
-          dbgtext( "request_oplock_break: error in response received " );
-          dbgtext( "to oplock break request to pid %d ", share_entry->pid );
-          dbgtext( "on port %d ", share_entry->op_port );
-          dbgtext( "for dev = %x, inode = %x.\n", dev, inode );
-          dbgtext( "Error was (%s).\n", strerror(errno) );
+            s = fd_listenset[i];
+            /* Clear this so we don't look at it again. */
+            FD_CLR(fd_listenset[i],&lfds);
+            break;
           }
-      return False;
-    }
-
-    reply_msg_len = IVAL(op_break_reply,UDP_CMD_LEN_OFFSET);
-    reply_from_port = SVAL(op_break_reply,UDP_CMD_PORT_OFFSET);
-
-    reply_msg_start = &op_break_reply[UDP_CMD_HEADER_LEN];
+        }
 
-    if(reply_msg_len != OPLOCK_BREAK_MSG_LEN)
-    {
-      /* Ignore it. */
-      DEBUG( 0, ( "request_oplock_break: invalid message length received." ) );
-      DEBUGADD( 0, ( "  Ignoring.\n" ) );
-      continue;
-    }
+        Client = accept(s,&addr,&in_addrlen);
 
-    /*
-     * Test to see if this is the reply we are awaiting.
-     */
+        if (Client == -1 && errno == EINTR)
+          continue;
 
-    if((SVAL(reply_msg_start,UDP_MESSAGE_CMD_OFFSET) & CMD_REPLY) &&
-       (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 - probably a break request.
-       * 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(oplock_sock, op_break_reply, sizeof(op_break_reply));
-    }
+        if (Client == -1)
+        {
+          DEBUG(0,("open_sockets: accept: %s\n",strerror(errno)));
+          continue;
+        }
 
-    time_left -= (time(NULL) - start_time);
-  }
+        if (Client != -1 && fork()==0)
+        {
+          /* Child code ... */
 
-  DEBUG(3,("request_oplock_break: broke oplock.\n"));
+          CatchSignal(SIGPIPE, SIGNAL_CAST sig_pipe);
 
-  return True;
-}
+          /* close the listening socket(s) */
+          for(i = 0; i < num_interfaces; i++)
+            close(fd_listenset[i]);
 
-/****************************************************************************
-Get the next SMB packet, doing the local message processing automatically.
-****************************************************************************/
+          /* close our standard file descriptors */
+          close_low_fds();
+          am_parent = 0;
+  
+          set_socket_options(Client,"SO_KEEPALIVE");
+          set_socket_options(Client,user_socket_options);
 
-BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout)
-{
-  BOOL got_smb = False;
-  BOOL ret;
+          /* Reset global variables in util.c so that
+             client substitutions will be done correctly
+             in the process.
+           */
+          reset_globals_after_fork();
+          return True; 
+        }
+        close(Client); /* The parent doesn't need this socket */
 
-  do
+       /*
+        * 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();
+      } /* end for num */
+    } /* end while 1 */
+  } /* end if is_daemon */
+  else
   {
-    ret = receive_message_or_smb(smbfd,oplockfd,inbuf,bufsize,
-                                 timeout,&got_smb);
-
-    if(ret && !got_smb)
-    {
-      /* Deal with oplock break requests from other smbd's. */
-      process_local_message(oplock_sock, inbuf, bufsize);
-      continue;
-    }
+    /* Started from inetd. fd 0 is the socket. */
+    /* We will abort gracefully when the client or remote system 
+       goes away */
+    CatchSignal(SIGPIPE, SIGNAL_CAST sig_pipe);
+    Client = dup(0);
 
-    if(ret && (CVAL(inbuf,0) == 0x85))
-    {
-      /* Keepalive packet. */
-      got_smb = False;
-    }
+    /* close our standard file descriptors */
+    close_low_fds();
 
+    set_socket_options(Client,"SO_KEEPALIVE");
+    set_socket_options(Client,user_socket_options);
   }
-  while(ret && !got_smb);
 
-  return ret;
+  return True;
 }
 
 /****************************************************************************
@@ -2613,454 +336,67 @@ BOOL reload_services(BOOL test)
        if (lp_loaded()) {
                pstring fname;
                pstrcpy(fname,lp_configfile());
-               if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) {
-                       pstrcpy(servicesf,fname);
-                       test = False;
-               }
-       }
-
-       reopen_logs();
-
-       if (test && !lp_file_list_changed())
-               return(True);
-
-       lp_killunused(conn_snum_used);
-
-       ret = lp_load(servicesf,False,False,True);
-
-       load_printers();
-
-       /* perhaps the config filename is now set */
-       if (!test)
-               reload_services(True);
-
-       reopen_logs();
-
-       load_interfaces();
-
-       {
-               extern int Client;
-               if (Client != -1) {      
-                       set_socket_options(Client,"SO_KEEPALIVE");
-                       set_socket_options(Client,user_socket_options);
-               }
-       }
-
-       reset_mangled_cache();
-
-       /* this forces service parameters to be flushed */
-       become_service(NULL,True);
-
-       return(ret);
-}
-
-
-
-/****************************************************************************
-this prevents zombie child processes
-****************************************************************************/
-static BOOL reload_after_sighup = False;
-
-static void sig_hup(int sig)
-{
-  BlockSignals(True,SIGHUP);
-  DEBUG(0,("Got SIGHUP\n"));
-
-  /*
-   * Fix from <branko.cibej@hermes.si> here.
-   * We used to reload in the signal handler - this
-   * is a *BIG* no-no.
-   */
-
-  reload_after_sighup = True;
-  BlockSignals(False,SIGHUP);
-}
-
-
-/****************************************************************************
-  make a connection to a service
-****************************************************************************/
-connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode)
-{
-       int snum;
-       struct passwd *pass = NULL;
-       BOOL guest = False;
-       BOOL force = False;
-       extern int Client;
-       connection_struct *conn;
-
-       strlower(service);
-
-       snum = find_service(service);
-       if (snum < 0) {
-               extern int Client;
-               if (strequal(service,"IPC$")) {
-                       DEBUG(3,("refusing IPC connection\n"));
-                       *ecode = ERRnoipc;
-                       return NULL;
-               }
-
-               DEBUG(0,("%s (%s) couldn't find service %s\n",
-                        remote_machine, client_addr(Client), service));
-               *ecode = ERRinvnetname;
-               return NULL;
-       }
-
-       if (strequal(service,HOMES_NAME)) {
-               if (*user && Get_Pwnam(user,True))
-                       return(make_connection(user,user,password,
-                                              pwlen,dev,vuid,ecode));
-
-               if(lp_security() != SEC_SHARE) {
-                       if (validated_username(vuid)) {
-                               pstrcpy(user,validated_username(vuid));
-                               return(make_connection(user,user,password,pwlen,dev,vuid,ecode));
-                       }
-               } else {
-                       /* Security = share. Try with sesssetup_user
-                        * as the username.  */
-                       if(*sesssetup_user) {
-                               pstrcpy(user,sesssetup_user);
-                               return(make_connection(user,user,password,pwlen,dev,vuid,ecode));
-                       }
-               }
-       }
-
-       if (!lp_snum_ok(snum) || 
-           !check_access(Client, 
-                         lp_hostsallow(snum), lp_hostsdeny(snum))) {    
-               *ecode = ERRaccess;
-               return NULL;
-       }
-
-       /* you can only connect to the IPC$ service as an ipc device */
-       if (strequal(service,"IPC$"))
-               pstrcpy(dev,"IPC");
-       
-       if (*dev == '?' || !*dev) {
-               if (lp_print_ok(snum)) {
-                       pstrcpy(dev,"LPT1:");
-               } else {
-                       pstrcpy(dev,"A:");
+               if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) {
+                       pstrcpy(servicesf,fname);
+                       test = False;
                }
        }
 
-       /* 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"));
-               *ecode = ERRinvdevice;
-               return NULL;
-       }
-
-       /* lowercase the user name */
-       strlower(user);
-
-       /* add it as a possible user name */
-       add_session_user(service);
+       reopen_logs();
 
-       /* shall we let them in? */
-       if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) {
-               DEBUG( 2, ( "Invalid username/password for %s\n", service ) );
-               *ecode = ERRbadpw;
-               return NULL;
-       }
-  
-       conn = conn_new();
-       if (!conn) {
-               DEBUG(0,("Couldn't find free connection.\n"));
-               *ecode = ERRnoresource;
-               conn_free(conn);
-               return NULL;
-       }
+       if (test && !lp_file_list_changed())
+               return(True);
 
-       /* find out some info about the user */
-       pass = Get_Pwnam(user,True);
+       lp_killunused(conn_snum_used);
 
-       if (pass == NULL) {
-               DEBUG(0,( "Couldn't find account %s\n",user));
-               *ecode = ERRbaduid;
-               conn_free(conn);
-               return NULL;
-       }
+       ret = lp_load(servicesf,False,False,True);
 
-       conn->read_only = lp_readonly(snum);
+       load_printers();
 
-       {
-               pstring list;
-               StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1);
-               string_sub(list,"%S",service);
-
-               if (user_in_list(user,list))
-                       conn->read_only = True;
-               
-               StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
-               string_sub(list,"%S",service);
-               
-               if (user_in_list(user,list))
-                       conn->read_only = False;    
-       }
+       /* perhaps the config filename is now set */
+       if (!test)
+               reload_services(True);
 
-       /* 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(user,lp_admin_users(snum)) 
-#if 0
-           && !conn->read_only
-#endif
-           ) {
-               conn->admin_user = True;
-               DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
-       } else {
-               conn->admin_user = False;
-       }
-    
-       conn->force_user = force;
-       conn->vuid = vuid;
-       conn->uid = pass->pw_uid;
-       conn->gid = pass->pw_gid;
-       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);
-       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);
-       
-#ifdef HAVE_GETGRNAM 
-       if (*lp_force_group(snum)) {
-               struct group *gptr;
-               pstring gname;
-               
-               StrnCpy(gname,lp_force_group(snum),sizeof(pstring)-1);
-               /* default service may be a group name          */
-               string_sub(gname,"%S",service);
-               gptr = (struct group *)getgrnam(gname);
-               
-               if (gptr) {
-                       conn->gid = gptr->gr_gid;
-                       DEBUG(3,("Forced group %s\n",gname));
-               } else {
-                       DEBUG(1,("Couldn't find group %s\n",gname));
-               }
-       }
-#endif
-       
-       if (*lp_force_user(snum)) {
-               struct passwd *pass2;
-               fstring fuser;
-               fstrcpy(fuser,lp_force_user(snum));
-               pass2 = (struct passwd *)Get_Pwnam(fuser,True);
-               if (pass2) {
-                       conn->uid = pass2->pw_uid;
-                       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));
-               }
-       }
+       reopen_logs();
 
-       {
-               pstring s;
-               pstrcpy(s,lp_pathname(snum));
-               standard_sub(conn,s);
-               string_set(&conn->connectpath,s);
-               DEBUG(3,("Connect path is %s\n",s));
-       }
+       load_interfaces();
 
-       /* groups stuff added by ih */
-       conn->ngroups = 0;
-       conn->groups = NULL;
-       
-       if (!IS_IPC(conn)) {
-               /* Find all the groups this uid is in and
-                  store them. Used by become_user() */
-               setup_groups(conn->user,conn->uid,conn->gid,
-                            &conn->ngroups,&conn->groups);
-               
-               /* 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"));
-                       *ecode = ERRnoresource;
-                       conn_free(conn);
-                       return NULL;
-               }  
-               
-               if (lp_status(SNUM(conn)))
-                       claim_connection(conn,"STATUS.",
-                                        MAXSTATUS,False);
-       } /* IS_IPC */
-       
-       /* execute any "root preexec = " line */
-       if (*lp_rootpreexec(SNUM(conn))) {
-               pstring cmd;
-               pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
-               standard_sub(conn,cmd);
-               DEBUG(5,("cmd=%s\n",cmd));
-               smbrun(cmd,NULL,False);
-       }
-       
-       if (!become_user(conn, conn->vuid)) {
-               DEBUG(0,("Can't become connected user!\n"));
-               if (!IS_IPC(conn)) {
-                       yield_connection(conn,
-                                        lp_servicename(SNUM(conn)),
-                                        lp_max_connections(SNUM(conn)));
-                       if (lp_status(SNUM(conn))) {
-                               yield_connection(conn,"STATUS.",MAXSTATUS);
-                       }
-               }
-               conn_free(conn);
-               *ecode = ERRbadpw;
-               return NULL;
-       }
-       
-       if (ChDir(conn->connectpath) != 0) {
-               DEBUG(0,("Can't change directory to %s (%s)\n",
-                        conn->connectpath,strerror(errno)));
-               unbecome_user();
-               if (!IS_IPC(conn)) {
-                       yield_connection(conn,
-                                        lp_servicename(SNUM(conn)),
-                                        lp_max_connections(SNUM(conn)));
-                       if (lp_status(SNUM(conn))) 
-                               yield_connection(conn,"STATUS.",MAXSTATUS);
-               }
-               conn_free(conn);
-               *ecode = ERRinvnetname;
-               return NULL;
-       }
-       
-       string_set(&conn->origpath,conn->connectpath);
-       
-#if SOFTLINK_OPTIMISATION
-       /* resolve any soft links early */
        {
-               pstring s;
-               pstrcpy(s,conn->connectpath);
-               GetWd(s);
-               string_set(&conn->connectpath,s);
-               ChDir(conn->connectpath);
-       }
-#endif
-       
-       add_session_user(user);
-               
-       /* execute any "preexec = " line */
-       if (*lp_preexec(SNUM(conn))) {
-               pstring cmd;
-               pstrcpy(cmd,lp_preexec(SNUM(conn)));
-               standard_sub(conn,cmd);
-               smbrun(cmd,NULL,False);
-       }
-       
-       /* we've finished with the sensitive stuff */
-       unbecome_user();
-       
-       /* 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)));
-       }
-       
-       if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
                extern int Client;
-               
-               dbgtext( "%s (%s) ", remote_machine, client_addr(Client) );
-               dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
-               dbgtext( "as user %s ", user );
-               dbgtext( "(uid=%d, gid=%d) ", conn->uid, conn->gid );
-               dbgtext( "(pid %d)\n", (int)getpid() );
+               if (Client != -1) {      
+                       set_socket_options(Client,"SO_KEEPALIVE");
+                       set_socket_options(Client,user_socket_options);
+               }
        }
-       
-       return(conn);
-}
-
-/****************************************************************************
-  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 (fsp->open && fsp->granted_oplock && !fsp->sent_oplock_break) {
+       reset_mangled_cache();
 
-    /* Try and break the oplock. */
-    file_fd_struct *fd_ptr = fsp->fd_ptr;
-    if(oplock_break( fd_ptr->dev, fd_ptr->inode, &fsp->open_time)) {
-      if(!fsp->open) /* Did the oplock break close the file ? */
-        return True;
-    }
-  }
+       /* this forces service parameters to be flushed */
+       become_service(NULL,True);
 
-  return False;
+       return(ret);
 }
 
 
 
 /****************************************************************************
-close a cnum
+this prevents zombie child processes
 ****************************************************************************/
-void close_cnum(connection_struct *conn, uint16 vuid)
-{
-       extern int Client;
-       DirCacheFlush(SNUM(conn));
-
-       unbecome_user();
+BOOL reload_after_sighup = False;
 
-       DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
-                                remote_machine,client_addr(Client),
-                                lp_servicename(SNUM(conn))));
-
-       yield_connection(conn,
-                        lp_servicename(SNUM(conn)),
-                        lp_max_connections(SNUM(conn)));
-
-       if (lp_status(SNUM(conn)))
-               yield_connection(conn,"STATUS.",MAXSTATUS);
-
-       file_close_conn(conn);
-       dptr_closecnum(conn);
+static void sig_hup(int sig)
+{
+  BlockSignals(True,SIGHUP);
+  DEBUG(0,("Got SIGHUP\n"));
 
-       /* execute any "postexec = " line */
-       if (*lp_postexec(SNUM(conn)) && 
-           become_user(conn, vuid))  {
-               pstring cmd;
-               pstrcpy(cmd,lp_postexec(SNUM(conn)));
-               standard_sub(conn,cmd);
-               smbrun(cmd,NULL,False);
-               unbecome_user();
-       }
+  /*
+   * Fix from <branko.cibej@hermes.si> here.
+   * We used to reload in the signal handler - this
+   * is a *BIG* no-no.
+   */
 
-       unbecome_user();
-       /* execute any "root postexec = " line */
-       if (*lp_rootpostexec(SNUM(conn)))  {
-               pstring cmd;
-               pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
-               standard_sub(conn,cmd);
-               smbrun(cmd,NULL,False);
-       }
-       
-       conn_free(conn);
+  reload_after_sighup = True;
+  BlockSignals(False,SIGHUP);
 }
 
 
@@ -3141,662 +477,6 @@ void exit_server(char *reason)
   exit(0);
 }
 
-/*
-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!
-*/
-struct smb_message_struct
-{
-  int code;
-  char *name;
-  int (*fn)(connection_struct *conn, char *, char *, int, int);
-  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}, /* ulogoff doesn't give a valid TID */
-   {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 | QUEUE_IN_OPLOCK },
-
-   /* 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 | QUEUE_IN_OPLOCK},
-   {SMBread,"SMBread",reply_read,AS_USER},
-   {SMBwrite,"SMBwrite",reply_write,AS_USER},
-   {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC},
-   {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 | QUEUE_IN_OPLOCK},
-
-   /* 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 | QUEUE_IN_OPLOCK },
-   {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
-   {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 | QUEUE_IN_OPLOCK },
-   {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
-   
-   {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
-   {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
-   {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},
-
-   /* NT PROTOCOL FOLLOWS */
-   {SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
-   {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
-   {SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
-   {SMBntcancel, "SMBntcancel", reply_ntcancel, 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;
-  extern int Client;
-
-#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(global_oplock_break && (smb_messages[match].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;
-    }          
-
-    if (smb_messages[match].fn)
-    {
-      int flags = smb_messages[match].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));
-
-
-      /* Ensure this value is replaced in the incoming packet. */
-      SSVAL(inbuf,smb_uid,session_tag);
-
-      /*
-       * Ensure the correct username is in sesssetup_user.
-       * 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)
-          pstrcpy( sesssetup_user, vuser->requested_name);
-      }
-
-      /* 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(conn,session_tag)) {
-        if (flags & AS_GUEST) 
-          flags &= ~AS_USER;
-        else
-          return(ERROR(ERRSRV,ERRinvnid));
-      }
-      /* 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(ERRSRV,ERRaccess));
-
-      /* ipc services are limited */
-      if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) {
-        return(ERROR(ERRSRV,ERRaccess));           
-      }
-
-      /* load service specific parameters */
-      if (conn && 
-         !become_service(conn,(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(Client, lp_hostsallow(-1), lp_hostsdeny(-1)))) {
-        return(ERROR(ERRSRV,ERRaccess));
-      }
-
-      last_inbuf = inbuf;
-
-      outsize = smb_messages[match].fn(conn, inbuf,outbuf,size,bufsize);
-    }
-    else
-    {
-      outsize = reply_unknown(inbuf,outbuf);
-    }
-  }
-
-#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
-  **************************************************************************/
-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];
-  extern int chain_size;
-  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) {
-    CVAL(outbuf,smb_vwv0) = 0xFF;
-    return outsize;
-  }
-
-  if (chain_size == 0) {
-    /* this is the first part of the chain */
-    orig_inbuf = inbuf;
-    orig_outbuf = outbuf;
-  }
-
-  /* we need to tell the client where the next part of the reply will be */
-  SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
-  CVAL(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 */
-  CVAL(inbuf2,smb_com) = smb_com2;
-
-  /* create the out buffer */
-  bzero(outbuf2,smb_size);
-  set_message(outbuf2,0,0,True);
-  CVAL(outbuf2,smb_com) = CVAL(inbuf2,smb_com);
-  
-  memcpy(outbuf2+4,inbuf2+4,4);
-  CVAL(outbuf2,smb_rcls) = SMB_SUCCESS;
-  CVAL(outbuf2,smb_reh) = 0;
-  CVAL(outbuf2,smb_flg) = 0x80 | (CVAL(inbuf2,smb_flg) & 0x8); /* bit 7 set 
-                                                                 means a reply */
-  SSVAL(outbuf2,smb_flg2,1); /* say we support long filenames */
-  SSVAL(outbuf2,smb_err,SMB_SUCCESS);
-  SSVAL(outbuf2,smb_tid,SVAL(inbuf2,smb_tid));
-  SSVAL(outbuf2,smb_pid,SVAL(inbuf2,smb_pid));
-  SSVAL(outbuf2,smb_uid,SVAL(inbuf2,smb_uid));
-  SSVAL(outbuf2,smb_mid,SVAL(inbuf2,smb_mid));
-
-  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);
-  CVAL(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;
-}
-
-
-/****************************************************************************
- Helper function for contruct_reply.
-****************************************************************************/
-
-void construct_reply_common(char *inbuf,char *outbuf)
-{
-  bzero(outbuf,smb_size);
-
-  CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
-  set_message(outbuf,0,0,True);
-
-  memcpy(outbuf+4,inbuf+4,4);
-  CVAL(outbuf,smb_rcls) = SMB_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,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 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);
-  extern int chain_size;
-
-  smb_last_time = time(NULL);
-
-  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);
-}
-
-/****************************************************************************
-  process commands from the client
-****************************************************************************/
-static void process(void)
-{
-  extern int Client;
-
-  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,NMB_PORT,SOCK_DGRAM);
-  }
-#endif    
-
-  /* re-initialise the timezone */
-  TimeInit();
-
-  while (True)
-  {
-    int deadtime = lp_deadtime()*60;
-    int counter;
-    int last_keepalive=0;
-    int service_load_counter = 0;
-    BOOL got_smb = False;
-
-    if (deadtime <= 0)
-      deadtime = DEFAULT_SMBD_TIMEOUT;
-
-#if USE_READ_PREDICTION
-    if (lp_readprediction())
-      do_read_prediction();
-#endif
-
-    errno = 0;      
-
-    for (counter=SMBD_SELECT_LOOP; 
-          !receive_message_or_smb(Client,oplock_sock,
-                      InBuffer,BUFFER_SIZE,SMBD_SELECT_LOOP*1000,&got_smb); 
-          counter += SMBD_SELECT_LOOP)
-    {
-      time_t t;
-      BOOL allidle = True;
-      extern int keepalive;
-
-      if (counter > 365 * 3600) /* big number of seconds. */
-      {
-        counter = 0;
-        service_load_counter = 0;
-      }
-
-      if (smb_read_error == READ_EOF) 
-      {
-        DEBUG(3,("end of file from client\n"));
-        return;
-      }
-
-      if (smb_read_error == READ_ERROR) 
-      {
-        DEBUG(3,("receive_smb error (%s) exiting\n",
-                  strerror(errno)));
-        return;
-      }
-
-      t = time(NULL);
-
-      /* become root again if waiting */
-      unbecome_user();
-
-      /* check for smb.conf reload */
-      if (counter >= service_load_counter + SMBD_RELOAD_CHECK)
-      {
-        service_load_counter = counter;
-
-        /* reload services, if files have changed. */
-        reload_services(True);
-      }
-
-      /*
-       * If reload_after_sighup == True then we got a SIGHUP
-       * and are being asked to reload. Fix from <branko.cibej@hermes.si>
-       */
-
-      if (reload_after_sighup)
-      {
-        DEBUG(0,("Reloading services after SIGHUP\n"));
-        reload_services(False);
-        reload_after_sighup = False;
-      }
-
-      /* automatic timeout if all connections are closed */      
-      if (conn_num_open()==0 && counter >= IDLE_CLOSED_TIMEOUT) 
-      {
-        DEBUG( 2, ( "Closing idle connection\n" ) );
-        return;
-      }
-
-      if (keepalive && (counter-last_keepalive)>keepalive) 
-      {
-             struct cli_state *cli = server_client();
-             if (!send_keepalive(Client)) {
-                     DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
-                     return;
-             }     
-             /* also send a keepalive to the password server if its still
-                connected */
-             if (cli && cli->initialised)
-                     send_keepalive(cli->fd);
-             last_keepalive = counter;
-      }
-
-      /* check for connection timeouts */
-      allidle = conn_idle_all(t, deadtime);
-
-      if (allidle && conn_num_open()>0) {
-             DEBUG(2,("Closing idle connection 2.\n"));
-             return;
-      }
-
-      if(global_machine_pasword_needs_changing)
-      {
-        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(!trust_password_lock( global_myworkgroup, global_myname, True)) {
-          DEBUG(0,("process: unable to open the machine account password file for \
-machine %s in domain %s.\n", global_myname, global_myworkgroup ));
-          continue;
-        }
-
-        if(!get_trust_account_password( 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 ));
-          trust_password_unlock();
-          continue;
-        }
-
-        /*
-         * Make sure someone else hasn't already done this.
-         */
-
-        if(t < lct + lp_machine_password_timeout()) {
-          trust_password_unlock();
-          global_machine_pasword_needs_changing = False;
-          continue;
-        }
-
-        pstrcpy(remote_machine_list, lp_passwordserver());
-
-        change_trust_account_password( global_myworkgroup, remote_machine_list);
-        trust_password_unlock();
-        global_machine_pasword_needs_changing = False;
-      }
-
-      /*
-       * Check to see if we have any change notifies 
-       * outstanding on the queue.
-       */
-      process_pending_change_notify_queue(t);
-    }
-
-    if(got_smb)
-      process_smb(InBuffer, OutBuffer);
-    else
-      process_local_message(oplock_sock, InBuffer, BUFFER_SIZE);
-  }
-}
 
 
 /****************************************************************************
@@ -4065,7 +745,7 @@ static void usage(char *pname)
   if( !open_oplock_ipc() )
     exit(1);
 
-  process();
+  smbd_process();
   close_sockets();
 
   exit_server("normal exit");
diff --git a/source/smbd/service.c b/source/smbd/service.c
new file mode 100644 (file)
index 0000000..b0c74aa
--- /dev/null
@@ -0,0 +1,542 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   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 int DEBUGLEVEL;
+
+extern time_t 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 pstring sesssetup_user;
+extern fstring remote_machine;
+
+
+/****************************************************************************
+load parameters specific to a connection/service
+****************************************************************************/
+BOOL become_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;
+
+       snum = SNUM(conn);
+  
+       if (do_chdir &&
+           ChDir(conn->connectpath) != 0 &&
+           ChDir(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);
+}
+
+
+/****************************************************************************
+  find a service entry
+****************************************************************************/
+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);
+
+      if(!phome_dir)
+      {
+        /*
+         * Try mapping the servicename, it may
+         * be a Windows to unix mapped user name.
+         */
+        if(map_username(service))
+          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 *pdefservice = lp_defaultservice();
+     if (pdefservice && *pdefservice && !strequal(pdefservice,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)
+       {
+         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);
+}
+
+
+/****************************************************************************
+  make a connection to a service
+****************************************************************************/
+connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode)
+{
+       int snum;
+       struct passwd *pass = NULL;
+       BOOL guest = False;
+       BOOL force = False;
+       extern int Client;
+       connection_struct *conn;
+
+       strlower(service);
+
+       snum = find_service(service);
+       if (snum < 0) {
+               extern int Client;
+               if (strequal(service,"IPC$")) {
+                       DEBUG(3,("refusing IPC connection\n"));
+                       *ecode = ERRnoipc;
+                       return NULL;
+               }
+
+               DEBUG(0,("%s (%s) couldn't find service %s\n",
+                        remote_machine, client_addr(Client), service));
+               *ecode = ERRinvnetname;
+               return NULL;
+       }
+
+       if (strequal(service,HOMES_NAME)) {
+               if (*user && Get_Pwnam(user,True))
+                       return(make_connection(user,user,password,
+                                              pwlen,dev,vuid,ecode));
+
+               if(lp_security() != SEC_SHARE) {
+                       if (validated_username(vuid)) {
+                               pstrcpy(user,validated_username(vuid));
+                               return(make_connection(user,user,password,pwlen,dev,vuid,ecode));
+                       }
+               } else {
+                       /* Security = share. Try with sesssetup_user
+                        * as the username.  */
+                       if(*sesssetup_user) {
+                               pstrcpy(user,sesssetup_user);
+                               return(make_connection(user,user,password,pwlen,dev,vuid,ecode));
+                       }
+               }
+       }
+
+       if (!lp_snum_ok(snum) || 
+           !check_access(Client, 
+                         lp_hostsallow(snum), lp_hostsdeny(snum))) {    
+               *ecode = ERRaccess;
+               return NULL;
+       }
+
+       /* you can only connect to the IPC$ service as an ipc device */
+       if (strequal(service,"IPC$"))
+               pstrcpy(dev,"IPC");
+       
+       if (*dev == '?' || !*dev) {
+               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"));
+               *ecode = ERRinvdevice;
+               return NULL;
+       }
+
+       /* 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, ( "Invalid username/password for %s\n", service ) );
+               *ecode = ERRbadpw;
+               return NULL;
+       }
+  
+       conn = conn_new();
+       if (!conn) {
+               DEBUG(0,("Couldn't find free connection.\n"));
+               *ecode = ERRnoresource;
+               conn_free(conn);
+               return NULL;
+       }
+
+       /* find out some info about the user */
+       pass = Get_Pwnam(user,True);
+
+       if (pass == NULL) {
+               DEBUG(0,( "Couldn't find account %s\n",user));
+               *ecode = ERRbaduid;
+               conn_free(conn);
+               return NULL;
+       }
+
+       conn->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))
+                       conn->read_only = True;
+               
+               StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
+               string_sub(list,"%S",service);
+               
+               if (user_in_list(user,list))
+                       conn->read_only = False;    
+       }
+
+       /* 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(user,lp_admin_users(snum)) 
+#if 0
+           && !conn->read_only
+#endif
+           ) {
+               conn->admin_user = True;
+               DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
+       } else {
+               conn->admin_user = False;
+       }
+    
+       conn->force_user = force;
+       conn->vuid = vuid;
+       conn->uid = pass->pw_uid;
+       conn->gid = pass->pw_gid;
+       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);
+       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);
+       
+#ifdef HAVE_GETGRNAM 
+       if (*lp_force_group(snum)) {
+               struct group *gptr;
+               pstring gname;
+               
+               StrnCpy(gname,lp_force_group(snum),sizeof(pstring)-1);
+               /* default service may be a group name          */
+               string_sub(gname,"%S",service);
+               gptr = (struct group *)getgrnam(gname);
+               
+               if (gptr) {
+                       conn->gid = gptr->gr_gid;
+                       DEBUG(3,("Forced group %s\n",gname));
+               } else {
+                       DEBUG(1,("Couldn't find group %s\n",gname));
+               }
+       }
+#endif
+       
+       if (*lp_force_user(snum)) {
+               struct passwd *pass2;
+               fstring fuser;
+               fstrcpy(fuser,lp_force_user(snum));
+               pass2 = (struct passwd *)Get_Pwnam(fuser,True);
+               if (pass2) {
+                       conn->uid = pass2->pw_uid;
+                       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));
+               }
+       }
+
+       {
+               pstring s;
+               pstrcpy(s,lp_pathname(snum));
+               standard_sub(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;
+       
+       if (!IS_IPC(conn)) {
+               /* Find all the groups this uid is in and
+                  store them. Used by become_user() */
+               setup_groups(conn->user,conn->uid,conn->gid,
+                            &conn->ngroups,&conn->groups);
+               
+               /* 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"));
+                       *ecode = ERRnoresource;
+                       conn_free(conn);
+                       return NULL;
+               }  
+               
+               if (lp_status(SNUM(conn)))
+                       claim_connection(conn,"STATUS.",
+                                        MAXSTATUS,False);
+       } /* IS_IPC */
+       
+       /* execute any "root preexec = " line */
+       if (*lp_rootpreexec(SNUM(conn))) {
+               pstring cmd;
+               pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
+               standard_sub(conn,cmd);
+               DEBUG(5,("cmd=%s\n",cmd));
+               smbrun(cmd,NULL,False);
+       }
+       
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("Can't become connected user!\n"));
+               if (!IS_IPC(conn)) {
+                       yield_connection(conn,
+                                        lp_servicename(SNUM(conn)),
+                                        lp_max_connections(SNUM(conn)));
+                       if (lp_status(SNUM(conn))) {
+                               yield_connection(conn,"STATUS.",MAXSTATUS);
+                       }
+               }
+               conn_free(conn);
+               *ecode = ERRbadpw;
+               return NULL;
+       }
+       
+       if (ChDir(conn->connectpath) != 0) {
+               DEBUG(0,("Can't change directory to %s (%s)\n",
+                        conn->connectpath,strerror(errno)));
+               unbecome_user();
+               if (!IS_IPC(conn)) {
+                       yield_connection(conn,
+                                        lp_servicename(SNUM(conn)),
+                                        lp_max_connections(SNUM(conn)));
+                       if (lp_status(SNUM(conn))) 
+                               yield_connection(conn,"STATUS.",MAXSTATUS);
+               }
+               conn_free(conn);
+               *ecode = ERRinvnetname;
+               return NULL;
+       }
+       
+       string_set(&conn->origpath,conn->connectpath);
+       
+#if SOFTLINK_OPTIMISATION
+       /* resolve any soft links early */
+       {
+               pstring s;
+               pstrcpy(s,conn->connectpath);
+               GetWd(s);
+               string_set(&conn->connectpath,s);
+               ChDir(conn->connectpath);
+       }
+#endif
+       
+       add_session_user(user);
+               
+       /* execute any "preexec = " line */
+       if (*lp_preexec(SNUM(conn))) {
+               pstring cmd;
+               pstrcpy(cmd,lp_preexec(SNUM(conn)));
+               standard_sub(conn,cmd);
+               smbrun(cmd,NULL,False);
+       }
+       
+       /* we've finished with the sensitive stuff */
+       unbecome_user();
+       
+       /* 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)));
+       }
+       
+       if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
+               extern int Client;
+               
+               dbgtext( "%s (%s) ", remote_machine, client_addr(Client) );
+               dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
+               dbgtext( "as user %s ", user );
+               dbgtext( "(uid=%d, gid=%d) ", conn->uid, conn->gid );
+               dbgtext( "(pid %d)\n", (int)getpid() );
+       }
+       
+       return(conn);
+}
+
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(connection_struct *conn, uint16 vuid)
+{
+       extern int Client;
+       DirCacheFlush(SNUM(conn));
+
+       unbecome_user();
+
+       DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
+                                remote_machine,client_addr(Client),
+                                lp_servicename(SNUM(conn))));
+
+       yield_connection(conn,
+                        lp_servicename(SNUM(conn)),
+                        lp_max_connections(SNUM(conn)));
+
+       if (lp_status(SNUM(conn)))
+               yield_connection(conn,"STATUS.",MAXSTATUS);
+
+       file_close_conn(conn);
+       dptr_closecnum(conn);
+
+       /* execute any "postexec = " line */
+       if (*lp_postexec(SNUM(conn)) && 
+           become_user(conn, vuid))  {
+               pstring cmd;
+               pstrcpy(cmd,lp_postexec(SNUM(conn)));
+               standard_sub(conn,cmd);
+               smbrun(cmd,NULL,False);
+               unbecome_user();
+       }
+
+       unbecome_user();
+       /* execute any "root postexec = " line */
+       if (*lp_rootpostexec(SNUM(conn)))  {
+               pstring cmd;
+               pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
+               standard_sub(conn,cmd);
+               smbrun(cmd,NULL,False);
+       }
+       
+       conn_free(conn);
+}
+
+