local.h: Removed ununsed SHARE_MODES_XXX defines. Upped SMBD_RELOAD_CHECK
[kai/samba.git] / source3 / smbd / server.c
index 5ef92777b2432d0a9109d3853a25ed0f82a818f4..41f23ed02f16adb3d6e5a94bee7f150d0d03e7f1 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    Main SMB server routines
-   Copyright (C) Andrew Tridgell 1992-1995
+   Copyright (C) Andrew Tridgell 1992-1997
    
    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
 */
 
 #include "includes.h"
-#include "loadparm.h"
-#include "pcap.h"
 #include "trans2.h"
-#include "reply.h"
 
 pstring servicesf = CONFIGFILE;
-pstring OriginalDir ="/";
 extern pstring debugf;
 extern pstring sesssetup_user;
+extern fstring myworkgroup;
 
 char *InBuffer = NULL;
 char *OutBuffer = NULL;
 char *last_inbuf = NULL;
 
-int initial_uid = 0;
-int initial_gid = 0;
-
-BOOL share_mode_pending = False;
-
-/* have I done a become_user? */
-static struct {
-  int cnum, uid;
-} last_user;
+int am_parent = 1;
+int atexit_set = 0;
 
 /* the last message the was processed */
 int last_message = -1;
@@ -60,16 +50,33 @@ extern BOOL short_case_preserve;
 extern BOOL case_mangle;
 extern time_t smb_last_time;
 
+extern int smb_read_error;
+
 extern pstring user_socket_options;
 
 connection_struct Connections[MAX_CONNECTIONS];
 files_struct Files[MAX_OPEN_FILES];
 
-extern int Protocol;
+/*
+ * Indirection for file fd's. Needed as POSIX locking
+ * is based on file/process, not fd/process.
+ */
+file_fd_struct FileFd[MAX_OPEN_FILES];
+int max_file_fd_used = 0;
 
-int maxxmit = BUFFER_SIZE;
+extern int Protocol;
 
-int chain_size = 0;
+/* 
+ * Size of data we can send to client. Set
+ *  by the client for all protocols above CORE.
+ *  Set by us for CORE protocol.
+ */
+int max_send = BUFFER_SIZE;
+/*
+ * Size of the data we can receive. Set by us.
+ * Can be modified by the max xmit parameter.
+ */
+int max_recv = BUFFER_SIZE;
 
 /* a fnum to use when chaining */
 int chain_fnum = -1;
@@ -79,6 +86,7 @@ static int num_connections_open = 0;
 
 extern fstring remote_machine;
 
+pstring OriginalDir;
 
 /* these can be set by some functions to override the error codes */
 int unix_ERR_class=SUCCESS;
@@ -88,16 +96,9 @@ int unix_ERR_code=0;
 extern int extra_time_offset;
 
 extern pstring myhostname;
-extern struct in_addr myip;
-
 
 static int find_free_connection(int hash);
 
-#ifdef SMB_PASSWD
-extern void generate_next_challenge(char *challenge);
-extern void set_challenge(char *challenge);
-#endif
-
 /* for readability... */
 #define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0)
 #define IS_DOS_DIR(test_mode) (((test_mode) & aDIR) != 0)
@@ -105,7 +106,21 @@ extern void set_challenge(char *challenge);
 #define IS_DOS_SYSTEM(test_mode) (((test_mode) & aSYSTEM) != 0)
 #define IS_DOS_HIDDEN(test_mode) (((test_mode) & aHIDDEN) != 0)
 
-
+/****************************************************************************
+  when exiting, take the whole family
+****************************************************************************/
+void  *dflt_sig(void)
+{
+  exit_server("caught signal");
+  return 0; /* Keep -Wall happy :-) */
+}
+/****************************************************************************
+  Send a SIGTERM to our process group.
+*****************************************************************************/
+void  killkids(void)
+{
+  if(am_parent) kill(0,SIGTERM);
+}
 
 /****************************************************************************
   change a dos mode to a unix mode
@@ -115,8 +130,12 @@ extern void set_challenge(char *challenge);
          dos archive is represented in unix by the user's execute bit
          dos system is represented in unix by the group's execute bit
          dos hidden is represented in unix by the other's execute bit
+         Then apply create mask,
+         then add force bits.
     base permission for directories:
          dos directory is represented in unix by unix's dir bit and the exec bit
+         Then apply create mask,
+         then add force bits.
 ****************************************************************************/
 mode_t unix_mode(int cnum,int dosmode)
 {
@@ -125,19 +144,29 @@ mode_t unix_mode(int cnum,int dosmode)
   if ( !IS_DOS_READONLY(dosmode) )
     result |= (S_IWUSR | S_IWGRP | S_IWOTH);
  
-  if (IS_DOS_DIR(dosmode))
+  if (IS_DOS_DIR(dosmode)) {
+    /* We never make directories read only for the owner as under DOS a user
+       can always create a file in a read-only directory. */
     result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR);
+    /* Apply directory mask */
+    result &= lp_dir_mode(SNUM(cnum));
+    /* Add in force bits */
+    result |= lp_force_dir_mode(SNUM(cnum));
+  } else { 
+    if (MAP_ARCHIVE(cnum) && IS_DOS_ARCHIVE(dosmode))
+      result |= S_IXUSR;
+
+    if (MAP_SYSTEM(cnum) && IS_DOS_SYSTEM(dosmode))
+      result |= S_IXGRP;
  
-  if (MAP_ARCHIVE(cnum) && IS_DOS_ARCHIVE(dosmode))
-    result |= S_IXUSR;
-
-  if (MAP_SYSTEM(cnum) && IS_DOS_SYSTEM(dosmode))
-    result |= S_IXGRP;
-  if (MAP_HIDDEN(cnum) && IS_DOS_HIDDEN(dosmode))
-    result |= S_IXOTH;  
+    if (MAP_HIDDEN(cnum) && IS_DOS_HIDDEN(dosmode))
+      result |= S_IXOTH;  
  
-  result &= CREATE_MODE(cnum);
+    /* Apply mode mask */
+    result &= lp_create_mode(SNUM(cnum));
+    /* Add in force bits */
+    result |= lp_force_create_mode(SNUM(cnum));
+  }
   return(result);
 }
 
@@ -148,31 +177,22 @@ mode_t unix_mode(int cnum,int dosmode)
 int dos_mode(int cnum,char *path,struct stat *sbuf)
 {
   int result = 0;
+  extern struct current_user current_user;
+
+  DEBUG(5,("dos_mode: %d %s\n", cnum, path));
 
-#if OLD_DOS_MODE
-  if (!CAN_WRITE(cnum) || !((sbuf->st_mode & S_IWOTH) ||
-                           Connections[cnum].admin_user ||
-                           ((sbuf->st_mode & S_IWUSR) && 
-                            Connections[cnum].uid==sbuf->st_uid) ||
-                           ((sbuf->st_mode & S_IWGRP) && 
-                            in_group(sbuf->st_gid,Connections[cnum].gid,
-                                     Connections[cnum].ngroups,
-                                     Connections[cnum].igroups))))
-    result |= aRONLY;
-#else
   if (CAN_WRITE(cnum) && !lp_alternate_permissions(SNUM(cnum))) {
     if (!((sbuf->st_mode & S_IWOTH) ||
          Connections[cnum].admin_user ||
-         ((sbuf->st_mode & S_IWUSR) && Connections[cnum].uid==sbuf->st_uid) ||
+         ((sbuf->st_mode & S_IWUSR) && current_user.uid==sbuf->st_uid) ||
          ((sbuf->st_mode & S_IWGRP) && 
-          in_group(sbuf->st_gid,Connections[cnum].gid,
-                   Connections[cnum].ngroups,Connections[cnum].igroups))))
+          in_group(sbuf->st_gid,current_user.gid,
+                   current_user.ngroups,current_user.igroups))))
       result |= aRONLY;
   } else {
     if ((sbuf->st_mode & S_IWUSR) == 0)
       result |= aRONLY;
   }
-#endif
 
   if ((sbuf->st_mode & S_IXUSR) != 0)
     result |= aARCH;
@@ -204,6 +224,23 @@ int dos_mode(int cnum,char *path,struct stat *sbuf)
        result |= aHIDDEN;
     }
 
+  /* Optimization : Only call is_hidden_path if it's not already
+     hidden. */
+  if (!(result & aHIDDEN) && is_hidden_path(SNUM(cnum), path))
+  {
+    result |= aHIDDEN;
+  }
+
+  DEBUG(5,("dos_mode returning "));
+
+  if (result & aHIDDEN) DEBUG(5, ("h"));
+  if (result & aRONLY ) DEBUG(5, ("r"));
+  if (result & aSYSTEM) DEBUG(5, ("s"));
+  if (result & aDIR   ) DEBUG(5, ("d"));
+  if (result & aARCH  ) DEBUG(5, ("a"));
+
+  DEBUG(5,("\n"));
+
   return(result);
 }
 
@@ -258,7 +295,7 @@ int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st)
     unixmode |= tmp;
   }
 
-  return(chmod(fname,unixmode));
+  return(sys_chmod(fname,unixmode));
 }
 
 
@@ -306,7 +343,7 @@ static BOOL mangled_equal(char *name1, char *name2)
 {
   pstring tmpname;
 
-  if (is_8_3(name2))
+  if (is_8_3(name2, True))
     return(False);
 
   strcpy(tmpname,name2);
@@ -343,7 +380,7 @@ static BOOL scan_directory(char *path, char *name,int snum,BOOL docache)
     check_mangled_stack(name);
 
   /* open the directory */
-  if (!(cur_dir = OpenDir(path))) 
+  if (!(cur_dir = OpenDir(snum, path, True))) 
     {
       DEBUG(3,("scan dir didn't open dir [%s]\n",path));
       return(False);
@@ -360,7 +397,7 @@ static BOOL scan_directory(char *path, char *name,int snum,BOOL docache)
       if (!name_map_mangle(name2,False,snum)) continue;
 
       if ((mangled && mangled_equal(name,name2))
-         || fname_equal(name, name2))
+         || fname_equal(name, dname))
        {
          /* we've found the file, change it's name and return */
          if (docache) DirCacheAdd(path,name,dname,snum);
@@ -384,31 +421,49 @@ for this service.
 
 The function will return False if some part of the name except for the last
 part cannot be resolved
+
+If the saved_last_component != 0, then the unmodified last component
+of the pathname is returned there. This is used in an exceptional
+case in reply_mv (so far). If saved_last_component == 0 then nothing
+is returned there.
 ****************************************************************************/
-BOOL unix_convert(char *name,int cnum)
+BOOL unix_convert(char *name,int cnum,pstring saved_last_component)
 {
   struct stat st;
   char *start, *end;
   pstring dirpath;
 
   *dirpath = 0;
+  if(saved_last_component)
+    *saved_last_component = 0;
 
   /* convert to basic unix format - removing \ chars and cleaning it up */
   unix_format(name);
   unix_clean_name(name);
 
-  if (!case_sensitive && 
-      (!case_preserve || (is_8_3(name) && !short_case_preserve)))
-    strnorm(name);
-
   /* names must be relative to the root of the service - trim any leading /.
    also trim trailing /'s */
   trim_string(name,"/","/");
 
+  /*
+   * Ensure saved_last_component is valid even if file exists.
+   */
+  if(saved_last_component) {
+    end = strrchr(name, '/');
+    if(end)
+      strcpy(saved_last_component, end + 1);
+    else
+      strcpy(saved_last_component, name);
+  }
+
+  if (!case_sensitive && 
+      (!case_preserve || (is_8_3(name, False) && !short_case_preserve)))
+    strnorm(name);
+
   /* check if it's a printer file */
   if (Connections[cnum].printer)
     {
-      if ((! *name) || strchr(name,'/') || !is_8_3(name))
+      if ((! *name) || strchr(name,'/') || !is_8_3(name, True))
        {
          char *s;
          fstring name2;
@@ -448,7 +503,10 @@ BOOL unix_convert(char *name,int cnum)
       end = strchr(start, '/');
 
       /* chop the name at this point */
-      if (end) *end = 0;
+      if (end)         *end = 0;
+
+      if(saved_last_component != 0)
+       strcpy(saved_last_component, end ? end + 1 : start);
 
       /* check if the name exists up to this point */
       if (sys_stat(name, &st) == 0) 
@@ -473,7 +531,6 @@ BOOL unix_convert(char *name,int cnum)
             later */
          if (end) strcpy(rest,end+1);
 
-
          /* try to find this part of the path in the directory */
          if (strchr(start,'?') || strchr(start,'*') ||
              !scan_directory(dirpath, start, SNUM(cnum), end?True:False))
@@ -565,6 +622,14 @@ static void disk_norm(int *bsize,int *dfree,int *dsize)
 int disk_free(char *path,int *bsize,int *dfree,int *dsize)
 {
   char *df_command = lp_dfree_command();
+  int dfree_retval;
+#ifdef QUOTAS
+  int dfreeq_retval;
+  int dfreeq = 0;
+  int bsizeq = *bsize;
+  int dsizeq = *dsize;
+#endif
+
 #ifndef NO_STATFS
 #ifdef USE_STATVFS
   struct statvfs fs;
@@ -577,15 +642,6 @@ int disk_free(char *path,int *bsize,int *dfree,int *dsize)
 #endif
 #endif
 
-#ifdef QUOTAS
-  if (disk_quotas(path, bsize, dfree, dsize))
-    {
-      disk_norm(bsize,dfree,dsize);
-      return(((*bsize)/1024)*(*dfree));
-    }
-#endif
-
-
   /* possibly use system() to get the result */
   if (df_command && *df_command)
     {
@@ -593,30 +649,50 @@ int disk_free(char *path,int *bsize,int *dfree,int *dsize)
       pstring syscmd;
       pstring outfile;
          
-      sprintf(outfile,"/tmp/dfree.smb.%d",(int)getpid());
+      sprintf(outfile,"%s/dfree.smb.%d",tmpdir(),(int)getpid());
       sprintf(syscmd,"%s %s",df_command,path);
       standard_sub_basic(syscmd);
 
-      ret = smbrun(syscmd,outfile);
+      ret = smbrun(syscmd,outfile,False);
       DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
          
       {
-       FILE *f = fopen(outfile,"r");   
-       *dsize = 0;
-       *dfree = 0;
-       *bsize = 1024;
-       if (f)
-         {
-           fscanf(f,"%d %d %d",dsize,dfree,bsize);
-           fclose(f);
-         }
-       else
-         DEBUG(0,("Can't open %s\n",outfile));
+        FILE *f = fopen(outfile,"r");  
+        *dsize = 0;
+        *dfree = 0;
+        *bsize = 1024;
+        if (f)
+          {
+            fscanf(f,"%d %d %d",dsize,dfree,bsize);
+            fclose(f);
+          }
+        else
+          DEBUG(0,("Can't open %s\n",outfile));
       }
          
       unlink(outfile);
       disk_norm(bsize,dfree,dsize);
-      return(((*bsize)/1024)*(*dfree));
+      dfree_retval = ((*bsize)/1024)*(*dfree);
+#ifdef QUOTAS
+      /* Ensure we return the min value between the users quota and
+         what's free on the disk. Thanks to Albrecht Gebhardt 
+         <albrecht.gebhardt@uni-klu.ac.at> for this fix.
+      */
+      if (disk_quotas(path, &bsizeq, &dfreeq, &dsizeq))
+        {
+          disk_norm(&bsizeq, &dfreeq, &dsizeq);
+          dfreeq_retval = ((bsizeq)/1024)*(dfreeq);
+          dfree_retval =  ( dfree_retval < dfreeq_retval ) ? 
+                           dfree_retval : dfreeq_retval ;
+          /* maybe dfree and dfreeq are calculated using different bsizes 
+             so convert dfree from bsize into bsizeq */
+          *dfree = ((*dfree) * (*bsize)) / (bsizeq);
+          *dfree = ( *dfree < dfreeq ) ? *dfree : dfreeq ; 
+          *bsize = bsizeq;
+          *dsize = dsizeq;
+        }
+#endif
+      return(dfree_retval);
     }
 
 #ifdef NO_STATFS
@@ -686,7 +762,27 @@ if ((*bsize) < 512 || (*bsize)>0xFFFF) *bsize = 1024;
       *dsize = 20*1024*1024/(*bsize);
       *dfree = MAX(1,*dfree);
     }
-  return(((*bsize)/1024)*(*dfree));
+  dfree_retval = ((*bsize)/1024)*(*dfree);
+#ifdef QUOTAS
+  /* Ensure we return the min value between the users quota and
+     what's free on the disk. Thanks to Albrecht Gebhardt 
+     <albrecht.gebhardt@uni-klu.ac.at> for this fix.
+  */
+  if (disk_quotas(path, &bsizeq, &dfreeq, &dsizeq))
+    {
+      disk_norm(&bsizeq, &dfreeq, &dsizeq);
+      dfreeq_retval = ((bsizeq)/1024)*(dfreeq);
+      dfree_retval = ( dfree_retval < dfreeq_retval ) ? 
+                       dfree_retval : dfreeq_retval ;
+      /* maybe dfree and dfreeq are calculated using different bsizes 
+         so convert dfree from bsize into bsizeq */
+      *dfree = ((*dfree) * (*bsize)) / (bsizeq);
+      *dfree = ( *dfree < dfreeq ) ? *dfree : dfreeq ;
+      *bsize = bsizeq;
+      *dsize = dsizeq;
+    }
+#endif
+  return(dfree_retval);
 #endif
 }
 
@@ -714,6 +810,12 @@ BOOL check_name(char *name,int cnum)
 
   errno = 0;
 
+  if( is_vetoed_name(SNUM(cnum), name)) 
+    {
+      DEBUG(5,("file path name %s vetoed\n",name));
+      return(0);
+    }
+
   ret = reduce_name(name,Connections[cnum].connectpath,lp_widelinks(SNUM(cnum)));
   if (!ret)
     DEBUG(5,("check_name on %s failed\n",name));
@@ -738,16 +840,182 @@ static void check_for_pipe(char *fname)
     }
 }
 
+/****************************************************************************
+fd support routines - attempt to do a sys_open
+****************************************************************************/
+
+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))
+    {
+      strcat(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;
+}
+
+/****************************************************************************
+fd support routines - attempt to find an already open file by dev
+and inode - increments the ref_count of the returned file_fd_struct *.
+****************************************************************************/
+file_fd_struct *fd_get_already_open(struct stat *sbuf)
+{
+  int i;
+  file_fd_struct *fd_ptr;
+
+  if(sbuf == 0)
+    return 0;
+
+  for(i = 0; i <= max_file_fd_used; i++) {
+    fd_ptr = &FileFd[i];
+    if((fd_ptr->ref_count > 0) &&
+       (((uint32)sbuf->st_dev) == fd_ptr->dev) &&
+       (((uint32)sbuf->st_ino) == fd_ptr->inode)) {
+      fd_ptr->ref_count++;
+      DEBUG(3,
+       ("Re-used file_fd_struct %d, dev = %x, inode = %x, ref_count = %d\n",
+        i, fd_ptr->dev, fd_ptr->inode, fd_ptr->ref_count));
+      return fd_ptr;
+    }
+  }
+  return 0;
+}
+
+/****************************************************************************
+fd support routines - attempt to find a empty slot in the FileFd array.
+Increments the ref_count of the returned entry.
+****************************************************************************/
+file_fd_struct *fd_get_new()
+{
+  int i;
+  file_fd_struct *fd_ptr;
+
+  for(i = 0; i < MAX_OPEN_FILES; i++) {
+    fd_ptr = &FileFd[i];
+    if(fd_ptr->ref_count == 0) {
+      fd_ptr->dev = (uint32)-1;
+      fd_ptr->inode = (uint32)-1;
+      fd_ptr->fd = -1;
+      fd_ptr->fd_readonly = -1;
+      fd_ptr->fd_writeonly = -1;
+      fd_ptr->real_open_flags = -1;
+      fd_ptr->ref_count++;
+      /* Increment max used counter if neccessary, cuts down
+        on search time when re-using */
+      if(i > max_file_fd_used)
+        max_file_fd_used = i;
+      DEBUG(3,("Allocated new file_fd_struct %d, dev = %x, inode = %x\n",
+               i, fd_ptr->dev, fd_ptr->inode));
+      return fd_ptr;
+    }
+  }
+  DEBUG(1,("ERROR! Out of file_fd structures - perhaps increase MAX_OPEN_FILES?\
+n"));
+  return 0;
+}
+
+/****************************************************************************
+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.
+****************************************************************************/
+
+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)
+{
+  DEBUG(3,("fd_attempt_close on file_fd_struct %d, fd = %d, dev = %x, inode = %x, open_flags = %d, ref_count = %d.\n",
+          fd_ptr - &FileFd[0],
+          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;
+    }
+  } 
+ return fd_ptr->ref_count;
+}
 
 /****************************************************************************
 open a file
 ****************************************************************************/
-void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
+static void open_file(int fnum,int cnum,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;
 
   Files[fnum].open = False;
-  Files[fnum].fd = -1;
+  Files[fnum].fd_ptr = 0;
   errno = EPERM;
 
   strcpy(fname,fname1);
@@ -777,10 +1045,104 @@ void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
     sys_unlink(fname);
 #endif
 
+  /*
+   * Ensure we have a valid struct stat so we can search the
+   * open fd table.
+   */
+  if(sbuf == 0) {
+    if(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;
+    }
+  }
 
-  Files[fnum].fd = sys_open(fname,flags,mode);
+  /*
+   * 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) {
+
+    int accmode = (flags & (O_RDONLY | O_WRONLY | O_RDWR));
+
+    /* File was already open. */
+    if((flags & O_CREAT) && (flags & O_EXCL)) {
+      fd_ptr->ref_count--;
+      errno = EEXIST;
+      return;
+    }
 
-  if ((Files[fnum].fd>=0) && 
+    /* 
+     * 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_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(flags & O_WRONLY) {
+        fd_ptr->fd = fd_attempt_open(fname, open_flags|O_WRONLY, mode);
+        fd_ptr->real_open_flags = O_WRONLY;
+      } else {
+       fd_ptr->fd = fd_attempt_open(fname, open_flags|O_RDONLY, mode);
+        fd_ptr->real_open_flags = O_RDONLY;
+      }
+    }
+  }
+
+  if ((fd_ptr->fd >=0) && 
       Connections[cnum].printer && lp_minprintspace(SNUM(cnum))) {
     pstring dname;
     int dum1,dum2,dum3;
@@ -790,72 +1152,48 @@ void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
     if (p) *p = 0;
     if (sys_disk_free(dname,&dum1,&dum2,&dum3) < 
        lp_minprintspace(SNUM(cnum))) {
-      close(Files[fnum].fd);
-      Files[fnum].fd = -1;
-      sys_unlink(fname);
+      fd_attempt_close(fd_ptr);
+      Files[fnum].fd_ptr = 0;
+      if(fd_ptr->ref_count == 0)
+        sys_unlink(fname);
       errno = ENOSPC;
       return;
     }
   }
     
-
-  /* Fix for files ending in '.' */
-  if((Files[fnum].fd == -1) && (errno == ENOENT) && 
-     (strchr(fname,'.')==NULL))
-    {
-      strcat(fname,".");
-      Files[fnum].fd = sys_open(fname,flags,mode);
-    }
-
-#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF))
-  if ((Files[fnum].fd == -1) && (errno == ENAMETOOLONG))
-    {
-      int max_len;
-      char *p = strrchr(fname, '/');
-
-      if (p == fname)  /* name is "/xxx" */
-       {
-         max_len = pathconf("/", _PC_NAME_MAX);
-         p++;
-       }
-      else if ((p == NULL) || (p == fname))
-       {
-         p = fname;
-         max_len = pathconf(".", _PC_NAME_MAX);
-       }
-      else
-       {
-         *p = '\0';
-         max_len = pathconf(fname, _PC_NAME_MAX);
-         *p = '/';
-         p++;
-       }
-      if (strlen(p) > max_len)
-       {
-         char tmp = p[max_len];
-
-         p[max_len] = '\0';
-         if ((Files[fnum].fd = sys_open(fname,flags,mode)) == -1)
-           p[max_len] = tmp;
-       }
-    }
-#endif
-
-  if (Files[fnum].fd < 0)
+  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 (Files[fnum].fd >= 0)
+  if (fd_ptr->fd >= 0)
     {
-      struct stat st;
+      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;
+
+      Files[fnum].fd_ptr = fd_ptr;
       Connections[cnum].num_files_open++;
-      fstat(Files[fnum].fd,&st);
-      Files[fnum].mode = st.st_mode;
-      Files[fnum].open_time = time(NULL);
+      Files[fnum].mode = sbuf->st_mode;
+      GetTimeOfDay(&Files[fnum].open_time);
+      Files[fnum].uid = current_user.id;
       Files[fnum].size = 0;
       Files[fnum].pos = -1;
       Files[fnum].open = True;
@@ -865,11 +1203,10 @@ void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
       Files[fnum].can_read = ((flags & O_WRONLY)==0);
       Files[fnum].can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
       Files[fnum].share_mode = 0;
-      Files[fnum].share_pending = False;
       Files[fnum].print_file = Connections[cnum].printer;
       Files[fnum].modified = False;
       Files[fnum].cnum = cnum;
-      string_set(&Files[fnum].name,fname);
+      string_set(&Files[fnum].name,dos_to_unix(fname,False));
       Files[fnum].wbmpx_ptr = NULL;      
 
       /*
@@ -899,7 +1236,7 @@ void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
     {
       Files[fnum].mmap_size = file_size(fname);
       Files[fnum].mmap_ptr = (char *)mmap(NULL,Files[fnum].mmap_size,
-                                         PROT_READ,MAP_SHARED,Files[fnum].fd,0);
+                                         PROT_READ,MAP_SHARED,Files[fnum].fd_ptr->fd,0);
 
       if (Files[fnum].mmap_ptr == (char *)-1 || !Files[fnum].mmap_ptr)
        {
@@ -916,7 +1253,7 @@ sync a file
 void sync_file(int fnum)
 {
 #ifndef NO_FSYNC
-  fsync(Files[fnum].fd);
+  fsync(Files[fnum].fd_ptr->fd);
 #endif
 }
 
@@ -953,7 +1290,7 @@ static void check_magic(int fnum,int cnum)
       sprintf(magic_output,"%s.out",fname);
 
     chmod(fname,0755);
-    ret = smbrun(fname,magic_output);
+    ret = smbrun(fname,magic_output,False);
     DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
     unlink(fname);
   }
@@ -965,38 +1302,49 @@ close a file - possibly invalidating the read prediction
 ****************************************************************************/
 void close_file(int fnum)
 {
-  int cnum = Files[fnum].cnum;
-  invalidate_read_prediction(Files[fnum].fd);
-  Files[fnum].open = False;
+  files_struct *fs_p = &Files[fnum];
+  int cnum = fs_p->cnum;
+  uint32 dev = fs_p->fd_ptr->dev;
+  uint32 inode = fs_p->fd_ptr->inode;
+  share_lock_token token;
+
+  invalidate_read_prediction(fs_p->fd_ptr->fd);
+  fs_p->open = False;
   Connections[cnum].num_files_open--;
-  if(Files[fnum].wbmpx_ptr) 
+  if(fs_p->wbmpx_ptr) 
     {
-      free((char *)Files[fnum].wbmpx_ptr);
-      Files[fnum].wbmpx_ptr = NULL;
+      free((char *)fs_p->wbmpx_ptr);
+      fs_p->wbmpx_ptr = NULL;
     }
 
 #if USE_MMAP
-  if(Files[fnum].mmap_ptr) 
+  if(fs_p->mmap_ptr) 
     {
-      munmap(Files[fnum].mmap_ptr,Files[fnum].mmap_size);
-      Files[fnum].mmap_ptr = NULL;
+      munmap(fs_p->mmap_ptr,fs_p->mmap_size);
+      fs_p->mmap_ptr = NULL;
     }
 #endif
 
   if (lp_share_modes(SNUM(cnum)))
-    del_share_mode(fnum);
+  {
+    lock_share_entry( cnum, dev, inode, &token);
+    del_share_mode(token, fnum);
+  }
+
+  fd_attempt_close(fs_p->fd_ptr);
 
-  close(Files[fnum].fd);
+  if (lp_share_modes(SNUM(cnum)))
+    unlock_share_entry( cnum, dev, inode, token);
 
   /* NT uses smbclose to start a print - weird */
-  if (Files[fnum].print_file)
+  if (fs_p->print_file)
     print_file(fnum);
 
   /* check for magic scripts */
   check_magic(fnum,cnum);
 
   DEBUG(2,("%s %s closed file %s (numopen=%d)\n",
-          timestring(),Connections[cnum].user,Files[fnum].name,
+          timestring(),Connections[cnum].user,fs_p->name,
           Connections[cnum].num_files_open));
 }
 
@@ -1058,17 +1406,44 @@ return True if sharing doesn't prevent the operation
 ********************************************************************/
 BOOL check_file_sharing(int cnum,char *fname)
 {
-  int pid=0;
-  int share_mode = get_share_mode_byname(cnum,fname,&pid);
+  int i;
+  int ret = False;
+  min_share_mode_entry *old_shares = 0;
+  int num_share_modes;
+  struct stat sbuf;
+  share_lock_token token;
+  int pid = getpid();
 
-  if (!pid || !share_mode) return(True);
-  if (share_mode == DENY_DOS)
-    return(pid == getpid());
+  if(!lp_share_modes(SNUM(cnum)))
+    return True;
+
+  if (stat(fname,&sbuf) == -1) return(True);
+
+  lock_share_entry(cnum, (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, &token);
+  num_share_modes = get_share_modes(cnum, token, 
+                     (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, &old_shares);
+
+  for( i = 0; i < num_share_modes; i++)
+  {
+    if (old_shares[i].share_mode != DENY_DOS)
+      goto free_and_exit;
+
+    if(old_shares[i].pid != pid)
+      goto free_and_exit;
+  }
 
   /* XXXX exactly what share mode combinations should be allowed for
      deleting/renaming? */
-  return(False);
+  /* 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(cnum, (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, token);
+  if(old_shares != NULL)
+    free((char *)old_shares);
+  return(ret);
 }
 
 /****************************************************************************
@@ -1076,17 +1451,25 @@ BOOL check_file_sharing(int cnum,char *fname)
   Helper for open_file_shared. 
   Truncate a file after checking locking; close file if locked.
   **************************************************************************/
-static void truncate_unless_locked(int fnum, int cnum)
+static void truncate_unless_locked(int fnum, int cnum, share_lock_token token, 
+       BOOL *share_locked)
 {
   if (Files[fnum].can_write){
     if (is_locked(fnum,cnum,0x3FFFFFFF,0)){
+      /* 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(cnum)))
+        unlock_share_entry( cnum, Files[fnum].fd_ptr->dev, 
+                            Files[fnum].fd_ptr->inode, token);
       close_file(fnum);   
+      /* Share mode no longer locked. */
+      *share_locked = False;
       errno = EACCES;
       unix_ERR_class = ERRDOS;
       unix_ERR_code = ERRlock;
     }
     else
-      ftruncate(Files[fnum].fd,0); 
+      ftruncate(Files[fnum].fd_ptr->fd,0); 
   }
 }
 
@@ -1097,25 +1480,31 @@ open a file with a share mode
 void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
                      int mode,int *Access,int *action)
 {
+  files_struct *fs_p = &Files[fnum];
   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 share_pid=0;
+  share_lock_token token;
+  uint32 dev = 0;
+  uint32 inode = 0;
 
-  Files[fnum].open = False;
-  Files[fnum].fd = -1;
+  fs_p->open = False;
+  fs_p->fd_ptr = 0;
 
   /* this is for OS/2 EAs - try and say we don't support them */
-  if (strstr(fname,".+,;=[].")) {
+  if (strstr(fname,".+,;=[].")) 
+  {
     unix_ERR_class = ERRDOS;
     unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
     return;
   }
 
-  if ((ofun & 0x3) == 0 && file_existed) {
+  if ((ofun & 0x3) == 0 && file_existed)  
+  {
     errno = EEXIST;
     return;
   }
@@ -1129,7 +1518,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
      append does not mean the same thing under dos and unix */
 
   switch (share_mode&0xF)
-    {
+  {
     case 1: 
       flags = O_WRONLY; 
       break;
@@ -1143,18 +1532,21 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
     default:
       flags = O_RDONLY;
       break;
-    }
+  }
   
   if (flags != O_RDONLY && file_existed && 
-      (!CAN_WRITE(cnum) || IS_DOS_READONLY(dos_mode(cnum,fname,&sbuf)))) {
-    if (!fcbopen) {
+      (!CAN_WRITE(cnum) || IS_DOS_READONLY(dos_mode(cnum,fname,&sbuf)))) 
+  {
+    if (!fcbopen) 
+    {
       errno = EACCES;
       return;
     }
     flags = O_RDONLY;
   }
 
-  if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
+  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;
@@ -1162,21 +1554,36 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
 
   if (deny_mode == DENY_FCB) deny_mode = DENY_DOS;
 
-  if (lp_share_modes(SNUM(cnum))) {
-    int old_share=0;
+  if (lp_share_modes(SNUM(cnum))) 
+  {
+    int num_shares = 0;
+    int i;
+    min_share_mode_entry *old_shares = 0;
+
 
     if (file_existed)
-      old_share = get_share_mode(cnum,&sbuf,&share_pid);
+    {
+      dev = (uint32)sbuf.st_dev;
+      inode = (uint32)sbuf.st_ino;
+      lock_share_entry(cnum, dev, inode, &token);
+      share_locked = True;
+      num_shares = get_share_modes(cnum, token, dev, inode, &old_shares);
+    }
 
-    if (share_pid) {
+    for(i = 0; i < num_shares; i++)
+    {
       /* someone else has a share lock on it, check to see 
         if we can too */
-      int old_open_mode = old_share&0xF;
-      int old_deny_mode = (old_share>>4)&7;
+      int old_open_mode = old_shares[i].share_mode &0xF;
+      int old_deny_mode = (old_shares[i].share_mode >>4)&7;
 
-      if (deny_mode > 4 || old_deny_mode > 4 || old_open_mode > 2) {
+      if (deny_mode > 4 || old_deny_mode > 4 || old_open_mode > 2) 
+      {
        DEBUG(2,("Invalid share mode (%d,%d,%d) on file %s\n",
                 deny_mode,old_deny_mode,old_open_mode,fname));
+        free((char *)old_shares);
+        if(share_locked)
+          unlock_share_entry(cnum, dev, inode, token);
        errno = EACCES;
        unix_ERR_class = ERRDOS;
        unix_ERR_code = ERRbadshare;
@@ -1185,20 +1592,25 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
 
       {
        int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
-                                         share_pid,fname);
+                                         old_shares[i].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)) {
+           (access_allowed == AWRITE && flags == O_RDONLY)) 
+        {
          DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s) = %d\n",
                   deny_mode,old_deny_mode,old_open_mode,
-                  share_pid,fname,
+                  old_shares[i].pid,fname,
                   access_allowed));
+          free((char *)old_shares);
+          if(share_locked)
+            unlock_share_entry(cnum, dev, inode, token);
          errno = EACCES;
          unix_ERR_class = ERRDOS;
          unix_ERR_code = ERRbadshare;
          return;
-       }
+        }
        
        if (access_allowed == AREAD)
          flags = O_RDONLY;
@@ -1207,76 +1619,72 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
          flags = O_WRONLY;
       }
     }
+    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(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode);
-  if (!Files[fnum].open && flags==O_RDWR && errno!=ENOENT && fcbopen) {
+  open_file(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode,file_existed ? &sbuf : 0);
+  if (!fs_p->open && flags==O_RDWR && errno!=ENOENT && fcbopen) 
+  {
     flags = O_RDONLY;
-    open_file(fnum,cnum,fname,flags,mode);
+    open_file(fnum,cnum,fname,flags,mode,file_existed ? &sbuf : 0 );
   }
 
-  if (Files[fnum].open) {
+  if (fs_p->open) 
+  {
     int open_mode=0;
-    switch (flags) {
-    case O_RDONLY:
-      open_mode = 0;
-      break;
-    case O_RDWR:
-      open_mode = 2;
-      break;
-    case O_WRONLY:
-      open_mode = 1;
-      break;
+
+    if((share_locked == False) && lp_share_modes(SNUM(cnum)))
+    {
+      /* We created the file - thus we must now lock the share entry before creating it. */
+      dev = fs_p->fd_ptr->dev;
+      inode = fs_p->fd_ptr->inode;
+      lock_share_entry(cnum, dev, inode, &token);
+      share_locked = True;
     }
 
-    Files[fnum].share_mode = (deny_mode<<4) | open_mode;
-    Files[fnum].share_pending = True;
+    switch (flags) 
+    {
+      case O_RDONLY:
+        open_mode = 0;
+        break;
+      case O_RDWR:
+        open_mode = 2;
+        break;
+      case O_WRONLY:
+        open_mode = 1;
+        break;
+    }
+
+    fs_p->share_mode = (deny_mode<<4) | open_mode;
 
-    if (Access) {
+    if (Access)
       (*Access) = open_mode;
-    }
-    
-    if (action) {
+
+    if (action) 
+    {
       if (file_existed && !(flags2 & O_TRUNC)) *action = 1;
       if (!file_existed) *action = 2;
       if (file_existed && (flags2 & O_TRUNC)) *action = 3;
     }
-
-    if (!share_pid)
-      share_mode_pending = True;
+    /* 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(cnum)))
+      set_share_mode(token, fnum);
 
     if ((flags2&O_TRUNC) && file_existed)
-      truncate_unless_locked(fnum,cnum);
+      truncate_unless_locked(fnum,cnum,token,&share_locked);
   }
-}
-
 
-
-/*******************************************************************
-check for files that we should now set our share modes on
-********************************************************************/
-static void check_share_modes(void)
-{
-  int i;
-  for (i=0;i<MAX_OPEN_FILES;i++)
-    if(Files[i].open && Files[i].share_pending) {
-      if (lp_share_modes(SNUM(Files[i].cnum))) {
-       int pid=0;
-       get_share_mode_by_fnum(Files[i].cnum,i,&pid);
-       if (!pid) {
-         set_share_mode(i,Files[i].share_mode);
-         Files[i].share_pending = False;
-       }
-      } else {
-       Files[i].share_pending = False; 
-      }
-    }
+  if (share_locked && lp_share_modes(SNUM(cnum)))
+    unlock_share_entry( cnum, dev, inode, token);
 }
 
-
 /****************************************************************************
 seek a file. Try to avoid the seek if possible
 ****************************************************************************/
@@ -1286,48 +1694,42 @@ int seek_file(int fnum,int pos)
   if (Files[fnum].print_file && POSTSCRIPT(Files[fnum].cnum))
     offset = 3;
 
-  Files[fnum].pos = lseek(Files[fnum].fd,pos+offset,SEEK_SET) - offset;
+  Files[fnum].pos = lseek(Files[fnum].fd_ptr->fd,pos+offset,SEEK_SET) - offset;
   return(Files[fnum].pos);
 }
 
 /****************************************************************************
 read from a file
 ****************************************************************************/
-int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact)
+int read_file(int fnum,char *data,int pos,int n)
 {
-  int ret=0;
+  int ret=0,readret;
 
   if (!Files[fnum].can_write)
     {
-      ret = read_predict(Files[fnum].fd,
-                        pos,
-                        data,
-                        NULL,
-                        maxcnt);
+      ret = read_predict(Files[fnum].fd_ptr->fd,pos,data,NULL,n);
 
       data += ret;
-      maxcnt -= ret;
-      mincnt = MAX(mincnt-ret,0);
+      n -= ret;
       pos += ret;
     }
 
 #if USE_MMAP
   if (Files[fnum].mmap_ptr)
     {
-      int num = MIN(maxcnt,Files[fnum].mmap_size-pos);
+      int num = MIN(n,Files[fnum].mmap_size-pos);
       if (num > 0)
        {
          memcpy(data,Files[fnum].mmap_ptr+pos,num);
          data += num;
          pos += num;
-         maxcnt -= num;
-         mincnt = MAX(mincnt-num,0);
+         n -= num;
          ret += num;
        }
     }
 #endif
 
-  if (maxcnt <= 0)
+  if (n <= 0)
     return(ret);
 
   if (seek_file(fnum,pos) != pos)
@@ -1336,13 +1738,10 @@ int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL
       return(ret);
     }
   
-  if (maxcnt > 0)
-    ret += read_with_timeout(Files[fnum].fd,
-                            data,
-                            mincnt,
-                            maxcnt,
-                            timeout,
-                            exact);
+  if (n > 0) {
+    readret = read(Files[fnum].fd_ptr->fd,data,n);
+    if (readret > 0) ret += readret;
+  }
 
   return(ret);
 }
@@ -1361,7 +1760,7 @@ int write_file(int fnum,char *data,int n)
   if (!Files[fnum].modified) {
     struct stat st;
     Files[fnum].modified = True;
-    if (fstat(Files[fnum].fd,&st) == 0) {
+    if (fstat(Files[fnum].fd_ptr->fd,&st) == 0) {
       int dosmode = dos_mode(Files[fnum].cnum,Files[fnum].name,&st);
       if (MAP_ARCHIVE(Files[fnum].cnum) && !IS_DOS_ARCHIVE(dosmode)) { 
        dos_chmod(Files[fnum].cnum,Files[fnum].name,dosmode | aARCH,&st);
@@ -1369,12 +1768,10 @@ int write_file(int fnum,char *data,int n)
     }  
   }
 
-  return(write_data(Files[fnum].fd,data,n));
+  return(write_data(Files[fnum].fd_ptr->fd,data,n));
 }
 
 
-static int old_umask = 022;
-
 /****************************************************************************
 load parameters specific to a connection/service
 ****************************************************************************/
@@ -1419,261 +1816,6 @@ BOOL become_service(int cnum,BOOL do_chdir)
 }
 
 
-/****************************************************************************
-  become the specified uid 
-****************************************************************************/
-static BOOL become_uid(int uid)
-{
-  if (initial_uid != 0)
-    return(True);
-
-#ifdef AIX
-  {
-    /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
-    priv_t priv;
-
-    priv.pv_priv[0] = 0;
-    priv.pv_priv[1] = 0;
-    if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
-               &priv, sizeof(priv_t)) < 0 ||
-       setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
-       seteuid((uid_t)uid) < 0) 
-      DEBUG(1,("Can't set uid (AIX3)"));
-  }
-#endif
-
-#ifdef USE_SETRES
-  if (setresuid(-1,uid,-1) != 0)
-#else
-    if ((seteuid(uid) != 0) && 
-       (setuid(uid) != 0))
-#endif
-      {
-       DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
-                uid,getuid(), geteuid()));
-       if (uid > 32000)
-         DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
-       return(False);
-      }
-
-  if (((uid == -1) || (uid == 65535)) && geteuid() != uid)
-    {
-      DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
-      return(False);
-    }
-
-  return(True);
-}
-
-
-/****************************************************************************
-  become the specified gid
-****************************************************************************/
-static BOOL become_gid(int gid)
-{
-  if (initial_uid != 0)
-    return(True);
-  
-#ifdef USE_SETRES 
-  if (setresgid(-1,gid,-1) != 0)
-#else
-    if (setgid(gid) != 0)
-#endif
-      {
-       DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
-                gid,getgid(),getegid()));
-       if (gid > 32000)
-         DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
-       return(False);
-      }
-
-  return(True);
-}
-
-
-/****************************************************************************
-  become the specified uid and gid
-****************************************************************************/
-static BOOL become_id(int uid,int gid)
-{
-  return(become_gid(gid) && become_uid(uid));
-}
-
-/****************************************************************************
-become the guest user
-****************************************************************************/
-static BOOL become_guest(void)
-{
-  BOOL ret;
-  static struct passwd *pass=NULL;
-
-  if (initial_uid != 0) 
-    return(True);
-
-  if (!pass)
-    pass = Get_Pwnam(lp_guestaccount(-1),True);
-  if (!pass) return(False);
-
-  ret = become_id(pass->pw_uid,pass->pw_gid);
-
-  if (!ret)
-    DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
-
-  last_user.cnum = -2;
-
-  return(ret);
-}
-
-/*******************************************************************
-check if a username is OK
-********************************************************************/
-static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
-{
-  int i;
-  for (i=0;i<Connections[cnum].uid_cache.entries;i++)
-    if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True);
-
-  if (!user_ok(vuser->name,snum)) return(False);
-
-  i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE;
-  Connections[cnum].uid_cache.list[i] = vuser->uid;
-
-  if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE)
-    Connections[cnum].uid_cache.entries++;
-
-  return(True);
-}
-
-
-/****************************************************************************
-  become the user of a connection number
-****************************************************************************/
-BOOL become_user(int cnum, int uid)
-{
-  int new_umask;
-  user_struct *vuser;
-  int snum,gid;
-  int ngroups;
-  gid_t *groups;
-
-  if (last_user.cnum == cnum && last_user.uid == uid) {
-    DEBUG(4,("Skipping become_user - already user\n"));
-    return(True);
-  }
-
-  unbecome_user();
-
-  if (!OPEN_CNUM(cnum)) {
-    DEBUG(2,("Connection %d not open\n",cnum));
-    return(False);
-  }
-
-  snum = Connections[cnum].service;
-
-  if (Connections[cnum].force_user || 
-      lp_security() == SEC_SHARE ||
-      !(vuser = get_valid_user_struct(uid)) ||
-      !check_user_ok(cnum,vuser,snum)) {
-    uid = Connections[cnum].uid;
-    gid = Connections[cnum].gid;
-    groups = Connections[cnum].groups;
-    ngroups = Connections[cnum].ngroups;
-  } else {
-    if (!vuser) {
-      DEBUG(2,("Invalid vuid used %d\n",uid));
-      return(False);
-    }
-    uid = vuser->uid;
-    if(!*lp_force_group(snum))
-      gid = vuser->gid;
-    else
-      gid = Connections[cnum].gid;
-    groups = vuser->user_groups;
-    ngroups = vuser->user_ngroups;
-  }
-
-  if (initial_uid == 0)
-    {
-      if (!become_gid(gid)) return(False);
-
-#ifndef NO_SETGROUPS      
-      if (!IS_IPC(cnum)) {
-       /* groups stuff added by ih/wreu */
-       if (ngroups > 0)
-         if (setgroups(ngroups,groups)<0)
-           DEBUG(0,("setgroups call failed!\n"));
-      }
-#endif
-
-      if (!Connections[cnum].admin_user && !become_uid(uid))
-       return(False);
-    }
-
-  new_umask = 0777 & ~CREATE_MODE(cnum);
-  old_umask = umask(new_umask);
-
-  last_user.cnum = cnum;
-  last_user.uid = uid;
-  
-  DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d) new_umask=0%o\n",
-          getuid(),geteuid(),getgid(),getegid(),new_umask));
-  
-  return(True);
-}
-
-/****************************************************************************
-  unbecome the user of a connection number
-****************************************************************************/
-BOOL unbecome_user(void )
-{
-  if (last_user.cnum == -1)
-    return(False);
-
-  ChDir(OriginalDir);
-
-  umask(old_umask);
-
-  if (initial_uid == 0)
-    {
-#ifdef USE_SETRES
-      setresuid(-1,getuid(),-1);
-      setresgid(-1,getgid(),-1);
-#else
-      if (seteuid(initial_uid) != 0) 
-       setuid(initial_uid);
-      setgid(initial_gid);
-#endif
-    }
-#ifdef NO_EID
-  if (initial_uid == 0)
-    DEBUG(2,("Running with no EID\n"));
-  initial_uid = getuid();
-  initial_gid = getgid();
-#else
-  if (geteuid() != initial_uid)
-    {
-      DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
-      initial_uid = geteuid();
-    }
-  if (getegid() != initial_gid)
-    {
-      DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
-      initial_gid = getegid();
-    }
-#endif
-  
-  if (ChDir(OriginalDir) != 0)
-    DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
-            timestring(),OriginalDir));  
-
-  DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
-       getuid(),geteuid(),getgid(),getegid()));
-
-  last_user.cnum = -1;
-
-  return(True);
-}
-
 /****************************************************************************
   find a service entry
 ****************************************************************************/
@@ -1878,11 +2020,11 @@ static int sig_cld()
     }
   depth++;
 
-  BlockSignals(True);
+  BlockSignals(True,SIGCLD);
   DEBUG(5,("got SIGCLD\n"));
 
 #ifdef USE_WAITPID
-  while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0);
+  while (sys_waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0);
 #endif
 
   /* Stop zombies */
@@ -1904,7 +2046,7 @@ static int sig_cld()
   while (wait3(WAIT3_CAST1 NULL, WNOHANG, WAIT3_CAST2 NULL) > 0);
 #endif
   depth--;
-  BlockSignals(False);
+  BlockSignals(False,SIGCLD);
   return 0;
 }
 #endif
@@ -1914,6 +2056,20 @@ static int sig_cld()
   **************************************************************************/
 static int sig_pipe()
 {
+  extern int password_client;
+  BlockSignals(True,SIGPIPE);
+
+  if (password_client != -1) {
+    DEBUG(3,("lost connection to password server\n"));
+    close(password_client);
+    password_client = -1;
+#ifndef DONT_REINSTALL_SIG
+    signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+#endif
+    BlockSignals(False,SIGPIPE);
+    return 0;
+  }
+
   exit_server("Got sigpipe\n");
   return(0);
 }
@@ -1939,18 +2095,21 @@ static BOOL open_sockets(BOOL is_daemon,int port)
 #endif
 
       /* open an incoming socket */
-      s = open_socket_in(SOCK_STREAM, port, 0);
+      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,("listen: %s",strerror(errno)));
+         DEBUG(0,("listen: %s\n",strerror(errno)));
          close(s);
          return False;
        }
       
+      if(atexit_set == 0)
+        atexit(killkids);
+
       /* now accept incoming connections - forking a new process
         for each incoming connection */
       DEBUG(2,("waiting for a connection\n"));
@@ -1963,8 +2122,8 @@ static BOOL open_sockets(BOOL is_daemon,int port)
 
          if (Client == -1)
            {
-             DEBUG(0,("accept: %s",strerror(errno)));
-             return False;
+             DEBUG(0,("accept: %s\n",strerror(errno)));
+             continue;
            }
 
 #ifdef NO_FORK_DEBUG
@@ -1976,6 +2135,7 @@ static BOOL open_sockets(BOOL is_daemon,int port)
 #else
          if (Client != -1 && fork()==0)
            {
+              /* Child code ... */
 #ifndef NO_SIGNAL_TEST
              signal(SIGPIPE, SIGNAL_CAST sig_pipe);
              signal(SIGCLD, SIGNAL_CAST SIG_DFL);
@@ -1985,10 +2145,16 @@ static BOOL open_sockets(BOOL is_daemon,int port)
 
              /* 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 */
@@ -2060,6 +2226,8 @@ BOOL reload_services(BOOL test)
 
   reopen_logs();
 
+  load_interfaces();
+
   {
     extern int Client;
     if (Client != -1) {      
@@ -2083,13 +2251,13 @@ this prevents zombie child processes
 ****************************************************************************/
 static int sig_hup()
 {
-  BlockSignals(True);
+  BlockSignals(True,SIGHUP);
   DEBUG(0,("Got SIGHUP\n"));
   reload_services(False);
 #ifndef DONT_REINSTALL_SIG
   signal(SIGHUP,SIGNAL_CAST sig_hup);
 #endif
-  BlockSignals(False);
+  BlockSignals(False,SIGHUP);
   return(0);
 }
 
@@ -2171,7 +2339,7 @@ int setup_groups(char *user, int uid, int gid, int *p_ngroups,
 /****************************************************************************
   make a connection to a service
 ****************************************************************************/
-int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid)
+int make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid)
 {
   int cnum;
   int snum;
@@ -2291,6 +2459,7 @@ int make_connection(char *service,char *user,char *password, int pwlen, char *de
     pcon->admin_user = False;
     
   pcon->force_user = force;
+  pcon->vuid = vuid;
   pcon->uid = pass->pw_uid;
   pcon->gid = pass->pw_gid;
   pcon->num_files_open = 0;
@@ -2306,14 +2475,21 @@ int make_connection(char *service,char *user,char *password, int pwlen, char *de
 #if HAVE_GETGRNAM 
   if (*lp_force_group(snum))
     {
-      struct group *gptr = (struct group *)getgrnam(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)
        {
          pcon->gid = gptr->gr_gid;
-         DEBUG(3,("Forced group %s\n",lp_force_group(snum)));
+         DEBUG(3,("Forced group %s\n",gname));
        }
       else
-       DEBUG(1,("Couldn't find group %s\n",lp_force_group(snum)));
+       DEBUG(1,("Couldn't find group %s\n",gname));
     }
 #endif
 
@@ -2376,10 +2552,10 @@ int make_connection(char *service,char *user,char *password, int pwlen, char *de
       strcpy(cmd,lp_rootpreexec(SNUM(cnum)));
       standard_sub(cnum,cmd);
       DEBUG(5,("cmd=%s\n",cmd));
-      smbrun(cmd,NULL);
+      smbrun(cmd,NULL,False);
     }
 
-  if (!become_user(cnum,pcon->uid))
+  if (!become_user(cnum,pcon->vuid))
     {
       DEBUG(0,("Can't become connected user!\n"));
       pcon->open = False;
@@ -2429,17 +2605,17 @@ int make_connection(char *service,char *user,char *password, int pwlen, char *de
       pstring cmd;
       strcpy(cmd,lp_preexec(SNUM(cnum)));
       standard_sub(cnum,cmd);
-      smbrun(cmd,NULL);
+      smbrun(cmd,NULL,False);
     }
   
   /* we've finished with the sensitive stuff */
   unbecome_user();
 
   {
-    extern struct from_host Client_info;
     DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) connect to service %s as user %s (uid=%d,gid=%d) (pid %d)\n",
                            timestring(),
-                           Client_info.name,Client_info.addr,
+                           remote_machine,
+                           client_addr(),
                            lp_servicename(SNUM(cnum)),user,
                            pcon->uid,
                            pcon->gid,
@@ -2456,6 +2632,8 @@ int make_connection(char *service,char *user,char *password, int pwlen, char *de
 int find_free_file(void )
 {
   int i;
+  /* we start at 1 here for an obscure reason I can't now remember,
+     but I think is important :-) */
   for (i=1;i<MAX_OPEN_FILES;i++)
     if (!Files[i].open)
       return(i);
@@ -2539,6 +2717,11 @@ int reply_lanman1(char *outbuf)
   int secword=0;
   BOOL doencrypt = SMBENCRYPT();
   time_t t = time(NULL);
+  /* We need to save and restore this as it can be destroyed
+     if we call another server if security=server
+     Thanks to Paul Nelson @ Thursby for pointing this out.
+   */
+  uint16 mid = SVAL(outbuf, smb_mid);
 
   if (lp_security()>=SEC_USER) secword |= 1;
   if (doencrypt) secword |= 2;
@@ -2561,7 +2744,8 @@ int reply_lanman1(char *outbuf)
   }
 
   CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
-  SSVAL(outbuf,smb_vwv2,maxxmit);
+  SSVAL(outbuf,smb_mid,mid); /* Restore possibly corrupted mid */
+  SSVAL(outbuf,smb_vwv2,max_recv);
   SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
   SSVAL(outbuf,smb_vwv4,1);
   SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
@@ -2584,6 +2768,11 @@ int reply_lanman2(char *outbuf)
   int secword=0;
   BOOL doencrypt = SMBENCRYPT();
   time_t t = time(NULL);
+  /* We need to save and restore this as it can be destroyed
+     if we call another server if security=server
+     Thanks to Paul Nelson @ Thursby for pointing this out.
+   */
+  uint16 mid = SVAL(outbuf, smb_mid);
 
   if (lp_security()>=SEC_USER) secword |= 1;
   if (doencrypt) secword |= 2;
@@ -2608,7 +2797,8 @@ int reply_lanman2(char *outbuf)
   }
 
   CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
-  SSVAL(outbuf,smb_vwv2,maxxmit);
+  SSVAL(outbuf,smb_mid,mid); /* Restore possibly corrupted mid */
+  SSVAL(outbuf,smb_vwv2,max_recv);
   SSVAL(outbuf,smb_vwv3,lp_maxmux()); 
   SSVAL(outbuf,smb_vwv4,1);
   SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
@@ -2618,32 +2808,72 @@ int reply_lanman2(char *outbuf)
   return (smb_len(outbuf)+4);
 }
 
+
 /****************************************************************************
 reply for the nt protocol
 ****************************************************************************/
 int reply_nt1(char *outbuf)
 {
-  int capabilities=0x300; /* has dual names + lock_and_read */
+  /* dual names + lock_and_read + nt SMBs + remote API calls */
+  int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ;
+/*
+  other valid capabilities which we may support at some time...
+                     CAP_LARGE_FILES|CAP_NT_SMBS|CAP_RPC_REMOTE_APIS;
+                     CAP_LARGE_FILES|CAP_LARGE_READX|
+                     CAP_STATUS32|CAP_LEVEL_II_OPLOCKS;
+ */
+
   int secword=0;
   BOOL doencrypt = SMBENCRYPT();
   time_t t = time(NULL);
+  int data_len;
+  int encrypt_len;
+  char challenge_len = 8;
+  /* We need to save and restore this as it can be destroyed
+     if we call another server if security=server
+     Thanks to Paul Nelson @ Thursby for pointing this out.
+   */
+  uint16 mid = SVAL(outbuf, smb_mid);
+
+  if (lp_readraw() && lp_writeraw())
+  {
+    capabilities |= CAP_RAW_MODE;
+  }
 
   if (lp_security()>=SEC_USER) secword |= 1;
   if (doencrypt) secword |= 2;
 
-  set_message(outbuf,17,doencrypt?8:0,True);
+  /* decide where (if) to put the encryption challenge, and
+     follow it with the OEM'd domain name
+   */
+  encrypt_len = doencrypt?challenge_len:0;
+#if UNICODE
+  data_len = encrypt_len + 2*(strlen(myworkgroup)+1);
+#else
+  data_len = encrypt_len + strlen(myworkgroup) + 1;
+#endif
+
+  set_message(outbuf,17,data_len,True);
+
+#if UNICODE
+  /* put the OEM'd domain name */
+  PutUniCode(smb_buf(outbuf)+encrypt_len,myworkgroup);
+#else
+  strcpy(smb_buf(outbuf)+encrypt_len, myworkgroup);
+#endif
+
   CVAL(outbuf,smb_vwv1) = secword;
 #ifdef SMB_PASSWD
   /* Create a token value and add it to the outgoing packet. */
-  if (doencrypt) {
+  if (doencrypt)
+  {
     generate_next_challenge(smb_buf(outbuf));
+
     /* Tell the nt machine how long the challenge is. */
-    SSVALS(outbuf,smb_vwv16+1,8);
+    SSVALS(outbuf,smb_vwv16+1,challenge_len);
   }
 #endif
 
-  SIVAL(outbuf,smb_vwv7+1,getpid()); /* session key */
-
   Protocol = PROTOCOL_NT1;
 
   if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
@@ -2653,21 +2883,20 @@ int reply_nt1(char *outbuf)
 #endif
   }
 
-  if (lp_readraw() && lp_writeraw())
-    capabilities |= 1;
-
+  SSVAL(outbuf,smb_mid,mid); /* Restore possibly corrupted mid */
   SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
   SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
-  SIVAL(outbuf,smb_vwv3+1,0xFFFF); /* max buffer */
-  SIVAL(outbuf,smb_vwv5+1,0xFFFF); /* raw size */
+  SIVAL(outbuf,smb_vwv3+1,0xffff); /* max buffer. LOTS! */
+  SIVAL(outbuf,smb_vwv5+1,0xffff); /* raw size. LOTS! */
+  SIVAL(outbuf,smb_vwv7+1,getpid()); /* session key */
   SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
   put_long_date(outbuf+smb_vwv11+1,t);
   SSVALS(outbuf,smb_vwv15+1,TimeDiff(t)/60);
+  SSVAL(outbuf,smb_vwv17,data_len); /* length of challenge+domain strings */
 
   return (smb_len(outbuf)+4);
 }
 
-
 /* these are the protocol lists used for auto architecture detection:
 
 WinNT 3.51:
@@ -2855,41 +3084,6 @@ static int reply_negprot(char *inbuf,char *outbuf)
 }
 
 
-/****************************************************************************
-  parse a connect packet
-****************************************************************************/
-void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev)
-{
-  char *p = smb_buf(buf) + 1;
-  char *p2;
-
-  DEBUG(4,("parsing connect string %s\n",p));
-    
-  p2 = strrchr(p,'\\');
-  if (p2 == NULL)
-    strcpy(service,p);
-  else
-    strcpy(service,p2+1);
-  
-  p += strlen(p) + 2;
-  
-  strcpy(password,p);
-  *pwlen = strlen(password);
-
-  p += strlen(p) + 2;
-
-  strcpy(dev,p);
-  
-  *user = 0;
-  p = strchr(service,'%');
-  if (p != NULL)
-    {
-      *p = 0;
-      strcpy(user,p+1);
-    }
-}
-
-
 /****************************************************************************
 close all open files for a connection
 ****************************************************************************/
@@ -2907,10 +3101,8 @@ static void close_open_files(int cnum)
 /****************************************************************************
 close a cnum
 ****************************************************************************/
-void close_cnum(int cnum, int uid)
+void close_cnum(int cnum, uint16 vuid)
 {
-  extern struct from_host Client_info;
-
   DirCacheFlush(SNUM(cnum));
 
   unbecome_user();
@@ -2923,7 +3115,7 @@ void close_cnum(int cnum, int uid)
 
   DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) closed connection to service %s\n",
                          timestring(),
-                         Client_info.name,Client_info.addr,
+                         remote_machine,client_addr(),
                          lp_servicename(SNUM(cnum))));
 
   yield_connection(cnum,
@@ -2937,12 +3129,12 @@ void close_cnum(int cnum, int uid)
   dptr_closecnum(cnum);
 
   /* execute any "postexec = " line */
-  if (*lp_postexec(SNUM(cnum)) && become_user(cnum,uid))
+  if (*lp_postexec(SNUM(cnum)) && become_user(cnum,vuid))
     {
       pstring cmd;
       strcpy(cmd,lp_postexec(SNUM(cnum)));
       standard_sub(cnum,cmd);
-      smbrun(cmd,NULL);
+      smbrun(cmd,NULL,False);
       unbecome_user();
     }
 
@@ -2953,7 +3145,7 @@ void close_cnum(int cnum, int uid)
       pstring cmd;
       strcpy(cmd,lp_rootpostexec(SNUM(cnum)));
       standard_sub(cnum,cmd);
-      smbrun(cmd,NULL);
+      smbrun(cmd,NULL,False);
     }
 
   Connections[cnum].open = False;
@@ -3003,7 +3195,7 @@ BOOL yield_connection(int cnum,char *name,int max_connections)
   f = fopen(fname,"r+");
   if (!f)
     {
-      DEBUG(2,("Coudn't open lock file %s (%s)\n",fname,strerror(errno)));
+      DEBUG(2,("Couldn't open lock file %s (%s)\n",fname,strerror(errno)));
       return(False);
     }
 
@@ -3077,8 +3269,10 @@ BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear)
 
   if (!file_exist(fname,NULL))
     {
+      int oldmask = umask(022);
       f = fopen(fname,"w");
       if (f) fclose(f);
+      umask(oldmask);
     }
 
   total_recs = file_size(fname) / sizeof(crec);
@@ -3135,11 +3329,8 @@ BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear)
   StrnCpy(crec.name,lp_servicename(snum),sizeof(crec.name)-1);
   crec.start = time(NULL);
 
-  {
-    extern struct from_host Client_info;
-    StrnCpy(crec.machine,Client_info.name,sizeof(crec.machine)-1);
-    StrnCpy(crec.addr,Client_info.addr,sizeof(crec.addr)-1);
-  }
+  StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1);
+  StrnCpy(crec.addr,client_addr(),sizeof(crec.addr)-1);
   
   /* make our mark */
   if (fseek(f,foundi*sizeof(crec),SEEK_SET) != 0 ||
@@ -3221,6 +3412,11 @@ void exit_server(char *reason)
     if (dump_core()) return;
 #endif
   }    
+
+#ifdef FAST_SHARE_MODES
+  stop_share_mode_mgmt();
+#endif /* FAST_SHARE_MODES */
+
   DEBUG(3,("%s Server exit  (%s)\n",timestring(),reason?reason:""));
   exit(0);
 }
@@ -3287,7 +3483,7 @@ struct smb_message_struct
    {SMBecho,"SMBecho",reply_echo,0},
    {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0},
    {SMBtconX,"SMBtconX",reply_tcon_and_X,0},
-   {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 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},
@@ -3301,7 +3497,7 @@ struct smb_message_struct
    {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE},
    {SMBread,"SMBread",reply_read,AS_USER},
    {SMBwrite,"SMBwrite",reply_write,AS_USER},
-   {SMBclose,"SMBclose",reply_close,AS_USER},
+   {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},
@@ -3316,7 +3512,7 @@ struct smb_message_struct
    {SMBctemp,"SMBctemp",reply_ctemp,AS_USER},
    {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER},
    {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER},
-   {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER},
+   {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER|AS_GUEST},
    {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER},
    {SMBlock,"SMBlock",reply_lock,AS_USER},
    {SMBunlock,"SMBunlock",reply_unlock,AS_USER},
@@ -3344,7 +3540,7 @@ struct smb_message_struct
    {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE},
    {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE},
    
-   {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER},
+   {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC},
    {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER},
    {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER},
    {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
@@ -3442,15 +3638,24 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize
        {
          int cnum = SVAL(inbuf,smb_tid);
          int flags = smb_messages[match].flags;
-         int uid = SVAL(inbuf,smb_uid);
+         uint16 session_tag = SVAL(inbuf,smb_uid);
 
          /* does this protocol need to be run as root? */
          if (!(flags & AS_USER))
            unbecome_user();
 
          /* does this protocol need to be run as the connected user? */
-         if ((flags & AS_USER) && !become_user(cnum,uid))
-           return(ERROR(ERRSRV,ERRinvnid));
+         if ((flags & AS_USER) && !become_user(cnum,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(cnum))
@@ -3502,103 +3707,99 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize
 
 
 /****************************************************************************
-construct a chained reply and add it to the already made reply
-
-inbuf points to the original message start.
-inbuf2 points to the smb_wct part of the secondary message
-type is the type of the secondary message
-outbuf points to the original outbuffer
-outbuf2 points to the smb_wct field of the new outbuffer
-size is the total length of the incoming message (from inbuf1)
-bufsize is the total buffer size
-
-return how many bytes were added to the response
-****************************************************************************/
-int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize)
+  construct a chained reply and add it to the already made reply
+  **************************************************************************/
+int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
 {
-  int outsize = 0;
-  char *ibuf,*obuf;
-  static BOOL in_chain = False;
-  static char *last_outbuf=NULL;
-  BOOL was_inchain = in_chain;
-  int insize_remaining;
-  static int insize_deleted;
-
-  chain_size += PTR_DIFF(outbuf2,outbuf) - smb_wct;
-  if (was_inchain)
-    outbuf = last_outbuf;
-  else
-    insize_deleted = 0;
+  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;
+  }
 
-  insize_deleted = 0;
-  inbuf2 -= insize_deleted;
-  insize_remaining = size - PTR_DIFF(inbuf2,inbuf);
-  insize_deleted += size - (insize_remaining + smb_wct);
+  /* 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;
 
-  in_chain = True;
-  last_outbuf = outbuf;
+  /* 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;
 
-  /* allocate some space for the in and out buffers of the chained message */
-  ibuf = (char *)malloc(size + SAFETY_MARGIN);
-  obuf = (char *)malloc(bufsize + SAFETY_MARGIN);
+  /* remember the original command type */
+  smb_com1 = CVAL(orig_inbuf,smb_com);
 
-  if (!ibuf || !obuf)
-    {
-      DEBUG(0,("Out of memory in chain reply\n"));
-      return(ERROR(ERRSRV,ERRnoresource));
-    }
+  /* save the data which will be overwritten by the new headers */
+  memcpy(inbuf_saved,inbuf2,smb_wct);
+  memcpy(outbuf_saved,outbuf2,smb_wct);
 
-  ibuf += SMB_ALIGNMENT;
-  obuf += SMB_ALIGNMENT;
+  /* give the new packet the same header as the last part of the SMB */
+  memmove(inbuf2,inbuf,smb_wct);
 
   /* create the in buffer */
-  memcpy(ibuf,inbuf,smb_wct);
-  memcpy(ibuf+smb_wct,inbuf2,insize_remaining);
-  CVAL(ibuf,smb_com) = type;
+  CVAL(inbuf2,smb_com) = smb_com2;
 
   /* create the out buffer */
-  bzero(obuf,smb_size);
-
-  set_message(obuf,0,0,True);
-  CVAL(obuf,smb_com) = CVAL(ibuf,smb_com);
+  bzero(outbuf2,smb_size);
+  set_message(outbuf2,0,0,True);
+  CVAL(outbuf2,smb_com) = CVAL(inbuf2,smb_com);
   
-  memcpy(obuf+4,ibuf+4,4);
-  CVAL(obuf,smb_rcls) = SUCCESS;
-  CVAL(obuf,smb_reh) = 0;
-  CVAL(obuf,smb_flg) = 0x80 | (CVAL(ibuf,smb_flg) & 0x8); /* bit 7 set 
-                                                            means a reply */
-  SSVAL(obuf,smb_flg2,1); /* say we support long filenames */
-  SSVAL(obuf,smb_err,SUCCESS);
-  SSVAL(obuf,smb_tid,SVAL(inbuf,smb_tid));
-  SSVAL(obuf,smb_pid,SVAL(inbuf,smb_pid));
-  SSVAL(obuf,smb_uid,SVAL(inbuf,smb_uid));
-  SSVAL(obuf,smb_mid,SVAL(inbuf,smb_mid));
+  memcpy(outbuf2+4,inbuf2+4,4);
+  CVAL(outbuf2,smb_rcls) = 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,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(ibuf);
+  show_msg(inbuf2);
 
   /* process the request */
-  outsize = switch_message(type,ibuf,obuf,smb_wct+insize_remaining,
-                          bufsize-chain_size);
+  outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size,
+                           bufsize-chain_size);
 
-  /* copy the new reply header over the old one, but preserve 
-     the smb_com field */
-  memcpy(outbuf+smb_com+1,obuf+smb_com+1,smb_wct-(smb_com+1));
+  /* 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;
 
-  /* and copy the data from the reply to the right spot */
-  memcpy(outbuf2,obuf+smb_wct,outsize - smb_wct);
-
-  /* free the allocated buffers */
-  if (ibuf) free(ibuf-SMB_ALIGNMENT);
-  if (obuf) free(obuf-SMB_ALIGNMENT);
-
-  in_chain = was_inchain;
+  /* 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 how much extra has been added to the packet */
-  return(outsize - smb_wct);
+  return outsize2;
 }
 
 
@@ -3611,10 +3812,12 @@ 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;
+  chain_fnum = -1;
 
   bzero(outbuf,smb_size);
 
@@ -3638,6 +3841,8 @@ int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
 
   outsize = switch_message(type,inbuf,outbuf,size,bufsize);
 
+  outsize += chain_size;
+
   if(outsize > 4)
     smb_setlen(outbuf,outsize - 4);
   return(outsize);
@@ -3647,15 +3852,12 @@ int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
 /****************************************************************************
   process commands from the client
 ****************************************************************************/
-void process(void )
+static void process(void)
 {
   static int trans_num = 0;
   int nread;
-  extern struct from_host Client_info;
   extern int Client;
 
-  fromhost(Client,&Client_info);
-  
   InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
   OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
   if ((InBuffer == NULL) || (OutBuffer == NULL)) 
@@ -3671,12 +3873,10 @@ void process(void )
     ip = *interpret_addr2("localhost");
     if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1");
     *OutBuffer = 0;
-    send_one_packet(OutBuffer,1,ip,137,SOCK_DGRAM);
+    send_one_packet(OutBuffer,1,ip,NMB_PORT,SOCK_DGRAM);
   }
 #endif    
 
-  last_user.cnum = -1;
-  
   while (True)
     {
       int32 len;      
@@ -3686,6 +3886,7 @@ void process(void )
       int deadtime = lp_deadtime()*60;
       int counter;
       int last_keepalive=0;
+      int service_load_counter = 0;
 
       if (deadtime <= 0)
        deadtime = DEFAULT_SMBD_TIMEOUT;
@@ -3693,24 +3894,6 @@ void process(void )
       if (lp_readprediction())
        do_read_prediction();
 
-      {
-       extern pstring share_del_pending;
-       if (*share_del_pending) {
-         unbecome_user();
-         if (!unlink(share_del_pending))
-           DEBUG(3,("Share file deleted %s\n",share_del_pending));
-         else
-           DEBUG(2,("Share del failed of %s\n",share_del_pending));
-         share_del_pending[0] = 0;
-       }
-      }
-
-      if (share_mode_pending) {
-       unbecome_user();
-       check_share_modes();
-       share_mode_pending=False;
-      }
-
       errno = 0;      
 
       for (counter=SMBD_SELECT_LOOP; 
@@ -3722,9 +3905,20 @@ void process(void )
          BOOL allidle = True;
          extern int keepalive;
 
-         /* check for socket failure */
-         if (errno) {
-           DEBUG(3,("receive_smb error (%s) exiting\n",strerror(errno)));
+      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;
          }
 
@@ -3734,16 +3928,16 @@ void process(void )
          unbecome_user();
 
          /* check for smb.conf reload */
-         if (!(counter%SMBD_RELOAD_CHECK))
-           reload_services(True);
+         if (counter >= service_load_counter + SMBD_RELOAD_CHECK)
+      {
+        service_load_counter = counter;
 
-         /* check the share modes every 10 secs */
-         if (!(counter%SHARE_MODES_CHECK))
-           check_share_modes();
+        /* remove all unused services.  reduce some of that memory overhead. */
+        lp_killunused(snum_used);
 
-         /* clean the share modes every 5 minutes */
-         if (!(counter%SHARE_MODES_CLEAN))
-           clean_share_files();
+        /* reload services, if files have changed. */
+           reload_services(True);
+      }
 
          /* automatic timeout if all connections are closed */      
          if (num_connections_open==0 && counter >= IDLE_CLOSED_TIMEOUT) {
@@ -3806,7 +4000,7 @@ void process(void )
       if (msg_type == 0)
        show_msg(InBuffer);
 
-      nread = construct_reply(InBuffer,OutBuffer,nread,maxxmit);
+      nread = construct_reply(InBuffer,OutBuffer,nread,max_send);
       
       if(nread > 0) {
         if (CVAL(OutBuffer,0) == 0)
@@ -3832,7 +4026,7 @@ void process(void )
 static void init_structs(void )
 {
   int i;
-  get_myname(myhostname,&myip);
+  get_myname(myhostname,NULL);
 
   for (i=0;i<MAX_CONNECTIONS;i++)
     {
@@ -3850,6 +4044,19 @@ static void init_structs(void )
     {
       Files[i].open = False;
       string_init(&Files[i].name,"");
+
+    }
+
+  for (i=0;i<MAX_OPEN_FILES;i++)
+    {
+      file_fd_struct *fd_ptr = &FileFd[i];
+      fd_ptr->ref_count = 0;
+      fd_ptr->dev = (int32)-1;
+      fd_ptr->inode = (int32)-1;
+      fd_ptr->fd = -1;
+      fd_ptr->fd_readonly = -1;
+      fd_ptr->fd_writeonly = -1;
+      fd_ptr->real_open_flags = -1;
     }
 
   init_dptrs();
@@ -3858,7 +4065,7 @@ static void init_structs(void )
 /****************************************************************************
 usage on the program
 ****************************************************************************/
-void usage(char *pname)
+static void usage(char *pname)
 {
   DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
 
@@ -3878,14 +4085,15 @@ void usage(char *pname)
 /****************************************************************************
   main program
 ****************************************************************************/
-int main(int argc,char *argv[])
+ int main(int argc,char *argv[])
 {
   extern BOOL append_log;
   /* shall I run as a daemon */
   BOOL is_daemon = False;
-  int port = 139;
+  int port = SMB_PORT;
   int opt;
   extern char *optarg;
+  char pidFile[100] = { 0 };
 
 #ifdef NEED_AUTH_PARAMETERS
   set_auth_parameters(argc,argv);
@@ -3903,7 +4111,7 @@ int main(int argc,char *argv[])
 
   setup_logging(argv[0],False);
 
-  charset_initialise();
+  charset_initialise(-1);
 
   /* make absolutely sure we run as root - to handle cases whre people
      are crazy enough to have it setuid */
@@ -3917,25 +4125,15 @@ int main(int argc,char *argv[])
 #endif
 
   fault_setup(exit_server);
+  signal(SIGTERM , SIGNAL_CAST dflt_sig);
 
-  umask(0777 & ~DEF_CREATE_MASK);
-
-  initial_uid = geteuid();
-  initial_gid = getegid();
-
-  if (initial_gid != 0 && initial_uid == 0)
-    {
-#ifdef HPUX
-      setresgid(0,0,0);
-#else
-      setgid(0);
-      setegid(0);
-#endif
-    }
+  /* we want total control over the permissions on created files,
+     so set our umask to 0 */
+  umask(0);
 
-  initial_uid = geteuid();
-  initial_gid = getegid();
+  GetWd(OriginalDir);
 
+  init_uid();
 
   /* this is for people who can't start the program correctly */
   while (argc > 1 && (*argv[1] != '-'))
@@ -3944,9 +4142,12 @@ int main(int argc,char *argv[])
       argc--;
     }
 
-  while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPa")) != EOF)
+  while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPaf:")) != EOF)
     switch (opt)
       {
+      case 'f':
+        strncpy(pidFile, optarg, sizeof(pidFile));
+        break;
       case 'O':
        strcpy(user_socket_options,optarg);
        break;
@@ -3995,9 +4196,7 @@ int main(int argc,char *argv[])
   reopen_logs();
 
   DEBUG(2,("%s smbd version %s started\n",timestring(),VERSION));
-  DEBUG(2,("Copyright Andrew Tridgell 1992-1995\n"));
-
-  GetWd(OriginalDir);
+  DEBUG(2,("Copyright Andrew Tridgell 1992-1997\n"));
 
 #ifndef NO_GETRLIMIT
 #ifdef RLIMIT_NOFILE
@@ -4027,6 +4226,10 @@ int main(int argc,char *argv[])
   if (!reload_services(False))
     return(-1);        
 
+  charset_initialise(lp_client_code_page());
+
+  strcpy(myworkgroup, lp_workgroup());
+
 #ifndef NO_SIGNAL_TEST
   signal(SIGHUP,SIGNAL_CAST sig_hup);
 #endif
@@ -4045,13 +4248,43 @@ int main(int argc,char *argv[])
       become_daemon();
     }
 
+  if (*pidFile)
+    {
+      int     fd;
+      char    buf[20];
+
+      if ((fd = open(pidFile,
+         O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
+        {
+           DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno)));
+           exit(1);
+        }
+      if(fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False)
+        {
+          DEBUG(0,("ERROR: smbd is already running\n"));
+          exit(1);
+        }
+      sprintf(buf, "%u\n", (unsigned int) getpid());
+      if (write(fd, buf, strlen(buf)) < 0)
+        {
+          DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno)));
+          exit(1);
+        }
+      /* Leave pid file open & locked for the duration... */
+    }
+
   if (!open_sockets(is_daemon,port))
     exit(1);
 
+#ifdef FAST_SHARE_MODES
+  if (!start_share_mode_mgmt())
+    exit(1);
+#endif /* FAST_SHARE_MODES */
+
   /* possibly reload the services file. */
   reload_services(True);
 
-  maxxmit = MIN(lp_maxxmit(),BUFFER_SIZE);
+  max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
 
   if (*lp_rootdir())
     {