first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[kai/samba.git] / source3 / smbd / open.c
index 108ef814ee1b02f2eb0ddf0066ab7f3735e57f56..4491082b2c54fbf347900ef9ecc4177c09623d1f 100644 (file)
 extern int DEBUGLEVEL;
 
 extern pstring sesssetup_user;
-extern int global_oplocks_open;
-extern uint16 oplock_port;
-
+extern uint16 global_oplock_port;
+extern BOOL global_client_failed_oplock_break;
 
 /****************************************************************************
 fd support routines - attempt to do a dos_open
 ****************************************************************************/
+
 static int fd_attempt_open(char *fname, int flags, mode_t mode)
 {
   int fd = dos_open(fname,flags,mode);
@@ -83,6 +83,7 @@ static int fd_attempt_open(char *fname, int flags, mode_t mode)
 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))
@@ -94,6 +95,7 @@ void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t 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;
@@ -111,6 +113,7 @@ static void fd_remove_from_uid_cache(file_fd_struct *fd_ptr, uid_t u)
 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;
@@ -120,11 +123,11 @@ static BOOL fd_is_in_uid_cache(file_fd_struct *fd_ptr, uid_t u)
   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, mode_t mode, file_fd_struct *fd_ptr)
 {
   int fd = dos_open( fname, O_RDWR, mode);
@@ -145,11 +148,14 @@ static void fd_attempt_reopen(char *fname, mode_t mode, file_fd_struct *fd_ptr)
 fd support routines - attempt to close the file referenced by this fd.
 Decrements the ref_count and returns it.
 ****************************************************************************/
-uint16 fd_attempt_close(file_fd_struct *fd_ptr)
+
+uint16 fd_attempt_close(file_fd_struct *fd_ptr, int *err_ret)
 {
   extern struct current_user current_user;
   uint16 ret_ref = fd_ptr->ref_count;
 
+  *err_ret = 0;
+
   DEBUG(3,("fd_attempt_close fd = %d, dev = %x, inode = %.0f, open_flags = %d, ref_count = %d.\n",
           fd_ptr->fd, (unsigned int)fd_ptr->dev, (double)fd_ptr->inode,
           fd_ptr->real_open_flags,
@@ -161,12 +167,26 @@ uint16 fd_attempt_close(file_fd_struct *fd_ptr)
   ret_ref = 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);
+
+    if(fd_ptr->fd != -1) {
+      if(close(fd_ptr->fd) < 0)
+        *err_ret = errno;
+       }
+
+    if(fd_ptr->fd_readonly != -1) {
+      if(close(fd_ptr->fd_readonly) < 0) {
+        if(*err_ret == 0)
+          *err_ret = errno;
+      }
+       }
+
+    if(fd_ptr->fd_writeonly != -1) {
+      if( close(fd_ptr->fd_writeonly) < 0) {
+        if(*err_ret == 0)
+          *err_ret = errno;
+      }
+       }
+
     /*
      * Delete this fd_ptr.
      */
@@ -175,7 +195,7 @@ uint16 fd_attempt_close(file_fd_struct *fd_ptr)
     fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
   }
 
- return ret_ref;
 return ret_ref;
 }
 
 /****************************************************************************
@@ -185,12 +205,21 @@ 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;
 
+  /*
+   * We need to temporarily stop CatchChild from eating 
+   * SIGCLD signals as it also eats the exit status code. JRA.
+   */
+
+  CatchChildLeaveStatus();
+
   if((child_pid = fork()) < 0) {
     DEBUG(0,("check_access_allowed_for_current_user: fork failed.\n"));
+    CatchChild();
     return False;
   }
 
@@ -200,11 +229,23 @@ static BOOL check_access_allowed_for_current_user( char *fname, int accmode )
      */
     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"));
+
+    while ((wpid = sys_waitpid(child_pid, &status_code, 0)) < 0) {
+      if(errno == EINTR) {
+        errno = 0;
+        continue;
+      }
+      DEBUG(0,("check_access_allowed_for_current_user: The process \
+is no longer waiting ! Error = %s\n", strerror(errno) ));
+      CatchChild();
       return(False);
     }
 
+       /*
+        * Go back to ignoring children.
+        */
+       CatchChild();
+
     if (child_pid != wpid) {
       DEBUG(0,("check_access_allowed_for_current_user: We were waiting for the wrong process ID\n"));
       return(False);
@@ -251,11 +292,12 @@ static BOOL check_access_allowed_for_current_user( char *fname, int accmode )
 /****************************************************************************
 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);
+       StrnCpy(s,fname,sizeof(s)-1);
        strlower(s);
        if (strstr(s,"pipe/")) {
                DEBUG(3,("Rejecting named pipe open for %s\n",fname));
@@ -267,6 +309,7 @@ static void check_for_pipe(char *fname)
 /****************************************************************************
 open a file
 ****************************************************************************/
+
 static void open_file(files_struct *fsp,connection_struct *conn,
                      char *fname1,int flags,mode_t mode, SMB_STRUCT_STAT *sbuf)
 {
@@ -274,11 +317,11 @@ static void open_file(files_struct *fsp,connection_struct *conn,
   pstring fname;
   SMB_STRUCT_STAT statbuf;
   file_fd_struct *fd_ptr;
-  int accmode = (flags & (O_RDONLY | O_WRONLY | O_RDWR));
+  int accmode = (flags & O_ACCMODE);
 
   fsp->open = False;
   fsp->fd_ptr = 0;
-  fsp->granted_oplock = False;
+  fsp->oplock_type = NO_OPLOCK;
   errno = EPERM;
 
   pstrcpy(fname,fname1);
@@ -295,7 +338,7 @@ static void open_file(files_struct *fsp,connection_struct *conn,
    * JRA.
    */
 
-  if (conn->read_only && !conn->printer) {
+  if (!CAN_WRITE(conn) && !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));
@@ -313,7 +356,7 @@ static void open_file(files_struct *fsp,connection_struct *conn,
   /* this handles a bug in Win95 - it doesn't say to create the file when it 
      should */
   if (conn->printer) {
-         flags |= O_CREAT;
+         flags |= (O_CREAT|O_EXCL);
   }
 
 /*
@@ -449,8 +492,9 @@ static void open_file(files_struct *fsp,connection_struct *conn,
     pstrcpy(dname,fname);
     p = strrchr(dname,'/');
     if (p) *p = 0;
-    if (sys_disk_free(dname,&dum1,&dum2,&dum3) < (SMB_BIG_UINT)lp_minprintspace(SNUM(conn))) {
-      if(fd_attempt_close(fd_ptr) == 0)
+    if (sys_disk_free(dname,False,&dum1,&dum2,&dum3) < (SMB_BIG_UINT)lp_minprintspace(SNUM(conn))) {
+      int err;
+      if(fd_attempt_close(fd_ptr, &err) == 0)
         dos_unlink(fname);
       fsp->fd_ptr = 0;
       errno = ENOSPC;
@@ -460,10 +504,11 @@ static void open_file(files_struct *fsp,connection_struct *conn,
     
   if (fd_ptr->fd < 0)
   {
+    int err;
     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);
+    fd_attempt_close(fd_ptr,&err);
     check_for_pipe(fname);
     return;
   }
@@ -473,11 +518,12 @@ static void open_file(files_struct *fsp,connection_struct *conn,
     if(sbuf == 0) {
       /* Do the fstat */
       if(sys_fstat(fd_ptr->fd, &statbuf) == -1) {
+        int err;
         /* 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);
+        fd_attempt_close(fd_ptr,&err);
         return;
       }
       sbuf = &statbuf;
@@ -495,18 +541,17 @@ static void open_file(files_struct *fsp,connection_struct *conn,
     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->oplock_type = NO_OPLOCK;
+    fsp->sent_oplock_break = NO_BREAK_SENT;
     fsp->is_directory = False;
-    fsp->delete_on_close = False;
+    fsp->stat_open = False;
+    fsp->directory_delete_on_close = False;
     fsp->conn = conn;
     /*
      * Note that the file name here is the *untranslated* name
@@ -517,6 +562,7 @@ static void open_file(files_struct *fsp,connection_struct *conn,
      */
     string_set(&fsp->fsp_name,fname);
     fsp->wbmpx_ptr = NULL;      
+    fsp->wcp = NULL; /* Write cache pointer. */
 
     /*
      * If the printer is marked as postscript output a leading
@@ -527,7 +573,7 @@ static void open_file(files_struct *fsp,connection_struct *conn,
      */
     if (fsp->print_file && lp_postscript(SNUM(conn)) && fsp->can_write) {
            DEBUG(3,("Writing postscript line\n"));
-           write_file(fsp,"%!\n",3);
+           write_file(fsp,"%!\n",-1,3);
     }
       
     DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
@@ -536,68 +582,52 @@ static void open_file(files_struct *fsp,connection_struct *conn,
             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){
-#ifdef LARGE_SMB_OFF_T
-    if (is_locked(fsp,conn,0x3FFFFFFFFFFFFFFFLL,0,F_WRLCK)){
-#else
-    if (is_locked(fsp,conn,0x3FFFFFFF,0,F_WRLCK)){
-#endif
-      /* 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
-      sys_ftruncate(fsp->fd_ptr->fd,0); 
-  }
+       if (fsp->can_write){
+               SMB_OFF_T mask2 = ((SMB_OFF_T)0x3) << (SMB_OFF_T_BITS-4);
+               SMB_OFF_T mask = (mask2<<2);
+               
+               if (is_locked(fsp,conn,~mask,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 {
+                       sys_ftruncate(fsp->fd_ptr->fd,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)
+                       pid_t 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();
+    pid_t pid = getpid();
     if (old_deny == new_deny && share_pid == pid) 
        return(AALL);    
 
@@ -639,21 +669,39 @@ static int access_table(int new_deny,int old_deny,int old_mode,
   return(AFAIL);      
 }
 
-
 /****************************************************************************
 check if we can open a file with a share mode
 ****************************************************************************/
+
 static 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;
+  int old_open_mode = GET_OPEN_MODE(share->share_mode);
+  int old_deny_mode = GET_DENY_MODE(share->share_mode);
+
+  /*
+   * Don't allow any open once the delete on close flag has been
+   * set.
+   */
+
+  if(GET_DELETE_ON_CLOSE_FLAG(share->share_mode))
+  {
+    DEBUG(5,("check_share_mode: Failing open on file %s as delete on close flag is set.\n",
+          fname ));
+    unix_ERR_class = ERRDOS;
+    unix_ERR_code = ERRnoaccess;
+    return False;
+  }
 
   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));
+
+    unix_ERR_class = ERRDOS;
+    unix_ERR_code = ERRbadshare;
+
     return False;
   }
 
@@ -668,7 +716,11 @@ static int check_share_mode( share_mode_entry *share, int deny_mode,
     {
       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));
+                (int)share->pid,fname, fcbopen, *flags, access_allowed));
+
+      unix_ERR_class = ERRDOS;
+      unix_ERR_code = ERRbadshare;
+
       return False;
     }
 
@@ -679,31 +731,43 @@ static int check_share_mode( share_mode_entry *share, int deny_mode,
       *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,
                      mode_t mode,int oplock_request, int *Access,int *action)
 {
   int flags=0;
   int flags2=0;
-  int deny_mode = (share_mode>>4)&7;
+  int deny_mode = GET_DENY_MODE(share_mode);
+  BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode);
   SMB_STRUCT_STAT sbuf;
-  BOOL file_existed = file_exist(fname,&sbuf);
+  BOOL file_existed = dos_file_exist(fname,&sbuf);
   BOOL share_locked = False;
   BOOL fcbopen = False;
-  int token;
+  int token = 0;
   SMB_DEV_T dev = 0;
   SMB_INO_T inode = 0;
   int num_share_modes = 0;
-
+  int oplock_contention_count = 0;
+  BOOL all_current_opens_are_level_II = False;
   fsp->open = False;
   fsp->fd_ptr = 0;
 
+  DEBUG(10,("open_file_shared: fname = %s, share_mode = %x, ofun = %x, mode = %o, oplock request = %d\n",
+        fname, share_mode, ofun, (int)mode,  oplock_request ));
+
+
+  /* ignore any oplock requests if oplocks are disabled */
+  if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) {
+         oplock_request = 0;
+  }
+
   /* this is for OS/2 EAs - try and say we don't support them */
   if (strstr(fname,".+,;=[].")) 
   {
@@ -715,33 +779,40 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int
     unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
 #endif /* OS2_WPS_FIX */
 
+    DEBUG(5,("open_file_shared: OS/2 EA's are not supported.\n"));
     return;
   }
 
-  if ((ofun & 0x3) == 0 && file_existed)  
+  if ((GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) && file_existed)  
   {
+    DEBUG(5,("open_file_shared: create new requested for file %s and file already exists.\n",
+          fname ));
     errno = EEXIST;
     return;
   }
       
-  if (ofun & 0x10)
+  if (GET_FILE_CREATE_DISPOSITION(ofun) == FILE_CREATE_IF_NOT_EXIST)
     flags2 |= O_CREAT;
-  if ((ofun & 0x3) == 2)
+
+  if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE)
     flags2 |= O_TRUNC;
 
+  if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL)
+    flags2 |= O_EXCL;
+
   /* note that we ignore the append flag as 
      append does not mean the same thing under dos and unix */
 
-  switch (share_mode&0xF)
+  switch (GET_OPEN_MODE(share_mode))
   {
-    case 1
+    case DOS_OPEN_WRONLY
       flags = O_WRONLY; 
       break;
-    case 0xF
+    case DOS_OPEN_FCB
       fcbopen = True;
       flags = O_RDWR; 
       break;
-    case 2
+    case DOS_OPEN_RDWR
       flags = O_RDWR; 
       break;
     default:
@@ -750,7 +821,7 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int
   }
 
 #if defined(O_SYNC)
-  if (share_mode&(1<<14)) {
+  if (GET_FILE_SYNC_OPENMODE(share_mode)) {
          flags2 |= O_SYNC;
   }
 #endif /* O_SYNC */
@@ -760,6 +831,8 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int
   {
     if (!fcbopen) 
     {
+      DEBUG(5,("open_file_shared: read/write access requested for file %s on read only %s\n",
+            fname, !CAN_WRITE(conn) ? "share" : "file" ));
       errno = EACCES;
       return;
     }
@@ -773,7 +846,8 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int
     return;
   }
 
-  if (deny_mode == DENY_FCB) deny_mode = DENY_DOS;
+  if (deny_mode == DENY_FCB)
+    deny_mode = DENY_DOS;
 
   if (lp_share_modes(SNUM(conn))) 
   {
@@ -801,6 +875,8 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int
       {
 
         broke_oplock = False;
+        all_current_opens_are_level_II = True;
+
         for(i = 0; i < num_share_modes; i++)
         {
           share_mode_entry *share_entry = &old_shares[i];
@@ -812,7 +888,8 @@ void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int
            * 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))
+          if((oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) ||
+             (!oplock_request && (share_entry->op_type != NO_OPLOCK)))
           {
 
             DEBUG(5,("open_file_shared: breaking oplock (%x) on file %s, \
@@ -834,7 +911,10 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
             }
             lock_share_entry(conn, dev, inode, &token);
             broke_oplock = True;
+            all_current_opens_are_level_II = False;
             break;
+          } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+            all_current_opens_are_level_II = False;
           }
 
           /* someone else has a share lock on it, check to see 
@@ -844,8 +924,6 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
             free((char *)old_shares);
             unlock_share_entry(conn, dev, inode, token);
             errno = EACCES;
-            unix_ERR_class = ERRDOS;
-            unix_ERR_code = ERRbadshare;
             return;
           }
 
@@ -855,6 +933,7 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
         {
           free((char *)old_shares);
           num_share_modes = get_share_modes(conn, token, dev, inode, &old_shares);
+          oplock_contention_count++;
         }
       } while(broke_oplock);
     }
@@ -863,6 +942,18 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
       free((char *)old_shares);
   }
 
+  /*
+   * Refuse to grant an oplock in case the contention limit is
+   * reached when going through the lock list multiple times.
+   */
+
+  if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn)))
+  {
+    oplock_request = 0;
+    DEBUG(4,("open_file_shared: oplock contention = %d. Not granting oplock.\n",
+          oplock_contention_count ));
+  }
+
   DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
           flags,flags2,(int)mode));
 
@@ -889,17 +980,19 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
     switch (flags) 
     {
       case O_RDONLY:
-        open_mode = 0;
+        open_mode = DOS_OPEN_RDONLY;
         break;
       case O_RDWR:
-        open_mode = 2;
+        open_mode = DOS_OPEN_RDWR;
         break;
       case O_WRONLY:
-        open_mode = 1;
+        open_mode = DOS_OPEN_WRONLY;
         break;
     }
 
-    fsp->share_mode = (deny_mode<<4) | open_mode;
+    fsp->share_mode = SET_DENY_MODE(deny_mode) | 
+                      SET_OPEN_MODE(open_mode) | 
+                      SET_ALLOW_SHARE_DELETE(allow_share_delete);
 
     if (Access)
       (*Access) = open_mode;
@@ -917,28 +1010,24 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
     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 = %.0f\n", oplock_request, fname, (unsigned int)dev, (double)inode));
+      /* 
+       * Setup the oplock info in both the shared memory and
+       * file structs.
+       */
 
-      }
-      else
-      {
+      if(oplock_request && (num_share_modes == 0) && 
+             !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp, oplock_request) ) {
+        port = global_oplock_port;
+      } else if (oplock_request && all_current_opens_are_level_II) {
+        port = global_oplock_port;
+        oplock_request = LEVEL_II_OPLOCK;
+        set_file_oplock(fsp, oplock_request);
+      } else {
         port = 0;
         oplock_request = 0;
       }
+
       set_share_mode(token, fsp, port, oplock_request);
     }
 
@@ -950,35 +1039,127 @@ dev = %x, inode = %.0f\n", oplock_request, fname, (unsigned int)dev, (double)ino
     unlock_share_entry( conn, dev, inode, token);
 }
 
+/****************************************************************************
+ Open a file for permissions read only. Return a pseudo file entry
+ with the 'stat_open' flag set and a fd_ptr of NULL.
+****************************************************************************/
+
+int open_file_stat(files_struct *fsp,connection_struct *conn,
+                  char *fname, int smb_ofun, SMB_STRUCT_STAT *pst, int *action)
+{
+       extern struct current_user current_user;
+
+       if(dos_stat(fname, pst) < 0) {
+               DEBUG(0,("open_file_stat: unable to stat name = %s. Error was %s\n",
+                        fname, strerror(errno) ));
+               return -1;
+       }
+
+       if(S_ISDIR(pst->st_mode)) {
+               DEBUG(0,("open_file_stat: %s is a directory !\n", fname ));
+               return -1;
+       }
+
+       *action = FILE_WAS_OPENED;
+       
+       DEBUG(5,("open_file_stat: opening file %s as a stat entry\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->can_lock = False;
+       fsp->can_read = False;
+       fsp->can_write = False;
+       fsp->share_mode = 0;
+       fsp->print_file = False;
+       fsp->modified = False;
+       fsp->oplock_type = NO_OPLOCK;
+       fsp->sent_oplock_break = NO_BREAK_SENT;
+       fsp->is_directory = False;
+       fsp->stat_open = True;
+       fsp->directory_delete_on_close = 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;
+    fsp->wcp = NULL; /* Write cache pointer. */
 
+       return 0;
+}
 
 /****************************************************************************
  Open a directory from an NT SMB call.
 ****************************************************************************/
+
 int open_directory(files_struct *fsp,connection_struct *conn,
                   char *fname, int smb_ofun, mode_t unixmode, int *action)
 {
        extern struct current_user current_user;
        SMB_STRUCT_STAT st;
+       BOOL got_stat = False;
 
-       if (smb_ofun & 0x10) {
-               /*
-                * Create the directory.
-                */
+       if(dos_stat(fname, &st) == 0) {
+               got_stat = True;
+       }
 
-               if(dos_mkdir(fname, unixmode) < 0) {
-                       DEBUG(0,("open_directory: unable to create %s. Error was %s\n",
-                                fname, strerror(errno) ));
-                       return -1;
-               }
+       if (got_stat && (GET_FILE_OPEN_DISPOSITION(smb_ofun) == FILE_EXISTS_FAIL)) {
+               errno = EEXIST; /* Setup so correct error is returned to client. */
+               return -1;
+       }
+
+       if (GET_FILE_CREATE_DISPOSITION(smb_ofun) == FILE_CREATE_IF_NOT_EXIST) {
+
+               if (got_stat) {
+
+                       if(!S_ISDIR(st.st_mode)) {
+                               DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+                               errno = EACCES;
+                               return -1;
+                       }
+                       *action = FILE_WAS_OPENED;
+
+               } else {
+
+                       /*
+                        * Try and create the directory.
+                        */
+
+                       if(!CAN_WRITE(conn)) {
+                               DEBUG(2,("open_directory: failing create on read-only share\n"));
+                               errno = EACCES;
+                               return -1;
+                       }
 
-               *action = FILE_WAS_CREATED;
+                       if(dos_mkdir(fname, unix_mode(conn,aDIR)) < 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.
+                * Don't create - just check that it *was* a directory.
                 */
 
-               if(dos_stat(fname, &st) < 0) {
+               if(!got_stat) {
                        DEBUG(0,("open_directory: unable to stat name = %s. Error was %s\n",
                                 fname, strerror(errno) ));
                        return -1;
@@ -988,6 +1169,7 @@ int open_directory(files_struct *fsp,connection_struct *conn,
                        DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
                        return -1;
                }
+
                *action = FILE_WAS_OPENED;
        }
        
@@ -1006,17 +1188,16 @@ int open_directory(files_struct *fsp,connection_struct *conn,
        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->oplock_type = NO_OPLOCK;
+       fsp->sent_oplock_break = NO_BREAK_SENT;
        fsp->is_directory = True;
+       fsp->directory_delete_on_close = False;
        fsp->conn = conn;
        /*
         * Note that the file name here is the *untranslated* name
@@ -1031,11 +1212,11 @@ int open_directory(files_struct *fsp,connection_struct *conn,
        return 0;
 }
 
-
 /*******************************************************************
-check if the share mode on a file allows it to be deleted or unlinked
-return True if sharing doesn't prevent the operation
+ 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;
@@ -1044,7 +1225,7 @@ BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op)
   int num_share_modes;
   SMB_STRUCT_STAT sbuf;
   int token;
-  int pid = getpid();
+  pid_t pid = getpid();
   SMB_DEV_T dev;
   SMB_INO_T inode;
 
@@ -1081,9 +1262,16 @@ BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op)
          * Check if someone has an oplock on this file. If so we must 
          * break it before continuing. 
          */
-        if(share_entry->op_type & BATCH_OPLOCK)
+        if(BATCH_OPLOCK_TYPE(share_entry->op_type))
         {
 
+#if 0
+
+/* JRA. Try removing this code to see if the new oplock changes
+   fix the problem. I'm dubious, but Andrew is recommending we
+   try this....
+*/
+
           /*
            * 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
@@ -1113,6 +1301,7 @@ batch oplocked file %s, dev = %x, inode = %.0f\n", fname, (unsigned int)dev, (do
             continue;
           }
           else
+#endif /* 0 */
           {
 
             DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \
@@ -1135,9 +1324,20 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
           }
         }
 
-        /* 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))
+        /* 
+         * If this is a delete request and ALLOW_SHARE_DELETE is set then allow 
+         * this to proceed. This takes precedence over share modes.
+         */
+
+        if(!rename_op && GET_ALLOW_SHARE_DELETE(share_entry->share_mode))
+          continue;
+
+        /* 
+         * Someone else has a share lock on it, check to see 
+         * if we can too.
+         */
+
+        if ((GET_DENY_MODE(share_entry->share_mode) != DENY_DOS) || (share_entry->pid != pid))
           goto free_and_exit;
 
       } /* end for */
@@ -1152,8 +1352,14 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
 
   /* 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() */
+  /* 
+   * If we got here then either there were no share modes or
+   * all share modes were DENY_DOS and the pid == getpid() or
+   * delete access was requested and all share modes had the
+   * ALLOW_SHARE_DELETE bit set (takes precedence over other
+   * share modes).
+   */
+
   ret = True;
 
 free_and_exit:
@@ -1163,5 +1369,3 @@ free_and_exit:
     free((char *)old_shares);
   return(ret);
 }
-
-