Ok - fixed a bug in our levelII oplock code. We need to break a level II on
authorJeremy Allison <jra@samba.org>
Thu, 16 Nov 2000 00:59:18 +0000 (00:59 +0000)
committerJeremy Allison <jra@samba.org>
Thu, 16 Nov 2000 00:59:18 +0000 (00:59 +0000)
a byte range lock (write lock only, but Win2k breaks on read lock also so I
do the same) - if you think about why, this is obvious. Also fixed our client
code to do level II oplocks, if requested, and fixed the code where we would
assume the client wanted level II if it advertised itself as being level II
capable - it may not want that.
Jeremy.
(This used to be commit 213cd0b5192307cd4b0026cae94b2f52fb1b0c02)

14 files changed:
source3/include/client.h
source3/include/proto.h
source3/lib/util.c
source3/libsmb/cliconnect.c
source3/libsmb/clientgen.c
source3/libsmb/clifile.c
source3/smbd/fileio.c
source3/smbd/negprot.c
source3/smbd/oplock.c
source3/smbd/reply.c
source3/smbd/trans2.c
source3/smbd/vfs-wrap.c
source3/smbd/vfs.c
source3/utils/torture.c

index 748c7375e1123cc06f9a02cefb5e9f8831aa7db5..ae7229b516cbd6b5be35d0a46296f762bd407877 100644 (file)
@@ -130,6 +130,7 @@ struct cli_state {
        uint32 ntlmssp_flags;
 
        BOOL use_oplocks; /* should we use oplocks? */
+       BOOL use_level_II_oplocks; /* should we use level II oplocks? */
 };
 
 #endif /* _CLIENT_H */
index 57d972cf364f69ff07f47ca0e4fa36afe7609605..16545b156d84ca9d53a961130652604185545ee4 100644 (file)
@@ -346,7 +346,6 @@ SMB_OFF_T transfer_file(int infd,int outfd,SMB_OFF_T n,char *header,int headlen,
 void msleep(int t);
 void become_daemon(void);
 BOOL yesno(char *p);
-int set_filelen(int fd, SMB_OFF_T len);
 void *Realloc(void *p,size_t size);
 void safe_free(void *p);
 BOOL get_myname(char *my_name);
@@ -3599,13 +3598,14 @@ int32 get_number_of_exclusive_open_oplocks(void);
 BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeout);
 BOOL set_file_oplock(files_struct *fsp, int oplock_type);
 void release_file_oplock(files_struct *fsp);
-BOOL remove_oplock(files_struct *fsp);
+BOOL remove_oplock(files_struct *fsp, BOOL break_to_none);
 int setup_oplock_select_set( fd_set *fds);
 BOOL process_local_message(char *buffer, int buf_size);
 BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token);
 BOOL request_oplock_break(share_mode_entry *share_entry, 
                           SMB_DEV_T dev, SMB_INO_T inode);
 BOOL attempt_close_oplocked_file(files_struct *fsp);
+void release_level_2_oplocks_on_change(files_struct *fsp);
 BOOL init_oplocks(void);
 #endif
 
@@ -3867,7 +3867,7 @@ int vfswrap_chown(connection_struct *conn, char *path, uid_t uid, gid_t gid);
 int vfswrap_chdir(connection_struct *conn, char *path);
 char *vfswrap_getwd(connection_struct *conn, char *path);
 int vfswrap_utime(connection_struct *conn, char *path, struct utimbuf *times);
-int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T offset);
+int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len);
 BOOL vfswrap_lock(files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type);
 size_t vfswrap_fget_nt_acl(files_struct *fsp, int fd, SEC_DESC **ppdesc);
 size_t vfswrap_get_nt_acl(files_struct *fsp, char *name, SEC_DESC **ppdesc);
@@ -3884,6 +3884,7 @@ char *vfs_getwd(connection_struct *conn, char *unix_path);
 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf);
 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count);
 ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N);
+int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len);
 SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp,
                            int out_fd, files_struct *out_fsp,
                            SMB_OFF_T n, char *header, int headlen, int align);
index 0aef60082f397c712154802b62149108045abaab..2d922aab7023f7cd03cfdad7a6fe22aa7b544117 100644 (file)
@@ -649,57 +649,6 @@ BOOL yesno(char *p)
   return(False);
 }
 
-/****************************************************************************
-set the length of a file from a filedescriptor.
-Returns 0 on success, -1 on failure.
-****************************************************************************/
-
-/* tpot vfs need to recode this function */
-
-int set_filelen(int fd, SMB_OFF_T len)
-{
-/* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
-   extend a file with ftruncate. Provide alternate implementation
-   for this */
-
-#ifdef HAVE_FTRUNCATE_EXTEND
-  return sys_ftruncate(fd, len);
-#else
-  SMB_STRUCT_STAT st;
-  char c = 0;
-  SMB_OFF_T currpos = sys_lseek(fd, (SMB_OFF_T)0, SEEK_CUR);
-
-  if(currpos == -1)
-    return -1;
-  /* Do an fstat to see if the file is longer than
-     the requested size (call ftruncate),
-     or shorter, in which case seek to len - 1 and write 1
-     byte of zero */
-  if(sys_fstat(fd, &st)<0)
-    return -1;
-
-#ifdef S_ISFIFO
-  if (S_ISFIFO(st.st_mode))
-    return 0;
-#endif
-
-  if(st.st_size == len)
-    return 0;
-  if(st.st_size > len)
-    return sys_ftruncate(fd, len);
-
-  if(sys_lseek(fd, len-1, SEEK_SET) != len -1)
-    return -1;
-  if(write(fd, &c, 1)!=1)
-    return -1;
-  /* Seek to where we were */
-  if(sys_lseek(fd, currpos, SEEK_SET) != currpos)
-    return -1;
-  return 0;
-#endif
-}
-
-
 #ifdef HPUX
 /****************************************************************************
 this is a version of setbuffer() for those machines that only have setvbuf
index 3292b9e1d6693cb55232272aa21eae3839726794..ff81d886b0d1de23b7fe5d1f47d0078a9300892c 100644 (file)
@@ -146,7 +146,7 @@ BOOL cli_session_setup(struct cli_state *cli,
                SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
                SSVAL(cli->outbuf,smb_vwv7,passlen);
                SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
-               SSVAL(cli->outbuf,smb_vwv11,0);
+               SSVAL(cli->outbuf,smb_vwv11,CAP_NT_SMBS|(cli->use_level_II_oplocks ? CAP_LEVEL_II_OPLOCKS : 0));
                p = smb_buf(cli->outbuf);
                memcpy(p,pword,passlen); 
                p += SVAL(cli->outbuf,smb_vwv7);
index 8d9fcb61d6e2fc9584fbee0e0580bd6762faa736..8d9e2f034f2d8bb50d336a6f86b074eb9f898142 100644 (file)
@@ -121,14 +121,20 @@ static void cli_process_oplock(struct cli_state *cli)
        char *oldbuf = cli->outbuf;
        pstring buf;
        int fnum;
+       unsigned char level;
 
        fnum = SVAL(cli->inbuf,smb_vwv2);
+       level = CVAL(cli->inbuf,smb_vwv3+1);
 
        /* damn, we really need to keep a record of open files so we
           can detect a oplock break and a close crossing on the
           wire. for now this swallows the errors */
        if (fnum == 0) return;
 
+       /* Ignore level II break to none's. */
+       if (level == OPLOCKLEVEL_NONE)
+               return;
+
        cli->outbuf = buf;
 
         memset(buf,'\0',smb_size);
@@ -140,7 +146,10 @@ static void cli_process_oplock(struct cli_state *cli)
        SSVAL(buf,smb_vwv0,0xFF);
        SSVAL(buf,smb_vwv1,0);
        SSVAL(buf,smb_vwv2,fnum);
-       SSVAL(buf,smb_vwv3,2); /* oplock break ack */
+       if (cli->use_level_II_oplocks)
+               SSVAL(buf,smb_vwv3,0x102); /* levelII oplock break ack */
+       else
+               SSVAL(buf,smb_vwv3,2); /* exclusive oplock break ack */
        SIVAL(buf,smb_vwv4,0); /* timoeut */
        SSVAL(buf,smb_vwv6,0); /* unlockcount */
        SSVAL(buf,smb_vwv7,0); /* lockcount */
index 63f6f8cc6c65ef580eadea61da115c671a182224..2f183ea1354b8eab2e0c93a95f8cc08d7c0ebfa0 100644 (file)
@@ -182,7 +182,10 @@ int cli_nt_create(struct cli_state *cli, char *fname)
        cli_setup_packet(cli);
 
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
-       SIVAL(cli->outbuf,smb_ntcreate_Flags, 0x06);
+       if (cli->use_oplocks)
+               SIVAL(cli->outbuf,smb_ntcreate_Flags, REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK);
+       else
+               SIVAL(cli->outbuf,smb_ntcreate_Flags, 0);
        SIVAL(cli->outbuf,smb_ntcreate_RootDirectoryFid, 0x0);
        SIVAL(cli->outbuf,smb_ntcreate_DesiredAccess, 0x2019f);
        SIVAL(cli->outbuf,smb_ntcreate_FileAttributes, 0x0);
index bb7ab46baa93a0627dace4296adbc83676721804..35e2f1455e95cddffb99ff591777646724715fc9 100644 (file)
@@ -195,78 +195,7 @@ ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
    * the shared memory area whilst doing this.
    */
 
-  if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
-    share_mode_entry *share_list = NULL;
-    pid_t pid = sys_getpid();
-    int token = -1;
-    int num_share_modes = 0;
-    int i;
-
-    if (lock_share_entry_fsp(fsp) == False) {
-      DEBUG(0,("write_file: failed to lock share mode entry for file %s.\n", fsp->fsp_name ));
-    }
-
-    num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list);
-
-    for(i = 0; i < num_share_modes; i++) {
-      share_mode_entry *share_entry = &share_list[i];
-
-      /*
-       * As there could have been multiple writes waiting at the lock_share_entry
-       * gate we may not be the first to enter. Hence the state of the op_types
-       * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II
-       * oplock. It will do no harm to re-send break messages to those smbd's
-       * that are still waiting their turn to remove their LEVEL_II state, and
-       * also no harm to ignore existing NO_OPLOCK states. JRA.
-       */
-
-      if (share_entry->op_type == NO_OPLOCK)
-        continue;
-
-      /* Paranoia .... */
-      if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
-        DEBUG(0,("write_file: PANIC. share mode entry %d is an exlusive oplock !\n", i ));
-        unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
-        abort();
-      }
-
-      /*
-       * Check if this is a file we have open (including the
-       * file we've been called to do write_file on. If so
-       * then break it directly without releasing the lock.
-       */
-
-      if (pid == share_entry->pid) {
-        files_struct *new_fsp = file_find_dit(fsp->dev, fsp->inode, &share_entry->time);
-
-        /* Paranoia check... */
-        if(new_fsp == NULL) {
-          DEBUG(0,("write_file: PANIC. share mode entry %d is not a local file !\n", i ));
-          unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
-          abort();
-        }
-        oplock_break_level2(new_fsp, True, token);
-
-      } else {
-
-        /*
-         * This is a remote file and so we send an asynchronous
-         * message.
-         */
-
-        request_oplock_break(share_entry, fsp->dev, fsp->inode);
-      }
-    }
-    free((char *)share_list);
-    unlock_share_entry_fsp(fsp);
-  }
-
-  /* Paranoia check... */
-  if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
-    DEBUG(0,("write_file: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name));
-    abort();
-  }
+  release_level_2_oplocks_on_change(fsp);
 
 #ifdef WITH_PROFILE
   if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
@@ -425,14 +354,6 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
 
       wcp->file_size = wcp->offset + wcp->data_size;
 
-#if 0
-      if (set_filelen(fsp->fd, wcp->file_size) == -1) {
-        DEBUG(0,("write_file: error %s in setting file to length %.0f\n",
-          strerror(errno), (double)wcp->file_size ));
-        return -1;
-      }
-#endif
-
       /*
        * If we used all the data then
        * return here.
index 1029c8db6207d8e365628819c419ec1e68201c35..41e95b816df3d90c5fdbf4a074639e5b334b16fc 100644 (file)
@@ -160,7 +160,7 @@ reply for the nt protocol
 static int reply_nt1(char *outbuf)
 {
   /* dual names + lock_and_read + nt SMBs + remote API calls */
-  int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ|
+  int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ|CAP_LEVEL_II_OPLOCKS|
                      (lp_nt_smb_support() ? CAP_NT_SMBS | CAP_RPC_REMOTE_APIS : 0) |
                      (SMB_OFF_T_BITS == 64 ? CAP_LARGE_FILES : 0);
 
index 4a8260421c4ecea7e2ca7b45d6073b3a6a24436b..366b4d0fec6cf79cbbe5f90a5d51c20c1ad9ef00 100644 (file)
@@ -208,10 +208,11 @@ static void downgrade_file_oplock(files_struct *fsp)
 
 /****************************************************************************
  Remove a file oplock. Copes with level II and exclusive.
- Locks then unlocks the share mode lock.
+ Locks then unlocks the share mode lock. Client can decide to go directly
+ to none even if a "break-to-level II" was sent.
 ****************************************************************************/
 
-BOOL remove_oplock(files_struct *fsp)
+BOOL remove_oplock(files_struct *fsp, BOOL break_to_none)
 {
        SMB_DEV_T dev = fsp->dev;
        SMB_INO_T inode = fsp->inode;
@@ -224,7 +225,7 @@ BOOL remove_oplock(files_struct *fsp)
                ret = False;
        }
 
-       if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT) {
+       if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT || break_to_none) {
                /*
                 * Deal with a reply when a break-to-none was sent.
                 */
@@ -837,7 +838,7 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval, B
         OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
   {
     DEBUG(0,("oplock_break: client failure in oplock break in file %s\n", fsp->fsp_name));
-    remove_oplock(fsp);
+    remove_oplock(fsp,True);
     global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */
   }
 
@@ -1096,6 +1097,96 @@ BOOL attempt_close_oplocked_file(files_struct *fsp)
   return False;
 }
 
+/****************************************************************************
+ This function is called on any file modification or lock request. If a file
+ is level 2 oplocked then it must tell all other level 2 holders to break to none.
+****************************************************************************/
+
+void release_level_2_oplocks_on_change(files_struct *fsp)
+{
+       share_mode_entry *share_list = NULL;
+       pid_t pid = sys_getpid();
+       int token = -1;
+       int num_share_modes = 0;
+       int i;
+
+       /*
+        * If this file is level II oplocked then we need
+        * to grab the shared memory lock and inform all
+        * other files with a level II lock that they need
+        * to flush their read caches. We keep the lock over
+        * the shared memory area whilst doing this.
+        */
+
+       if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+               return;
+
+       if (lock_share_entry_fsp(fsp) == False) {
+               DEBUG(0,("release_level_2_oplocks_on_change: failed to lock share mode entry for file %s.\n", fsp->fsp_name ));
+       }
+
+       num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list);
+
+       for(i = 0; i < num_share_modes; i++) {
+               share_mode_entry *share_entry = &share_list[i];
+
+               /*
+                * As there could have been multiple writes waiting at the lock_share_entry
+                * gate we may not be the first to enter. Hence the state of the op_types
+                * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II
+                * oplock. It will do no harm to re-send break messages to those smbd's
+                * that are still waiting their turn to remove their LEVEL_II state, and
+                * also no harm to ignore existing NO_OPLOCK states. JRA.
+                */
+
+               if (share_entry->op_type == NO_OPLOCK)
+                       continue;
+
+               /* Paranoia .... */
+               if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+                       DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is an exlusive oplock !\n", i ));
+                       unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+                       abort();
+               }
+
+               /*
+                * Check if this is a file we have open (including the
+                * file we've been called to do write_file on. If so
+                * then break it directly without releasing the lock.
+                */
+
+               if (pid == share_entry->pid) {
+                       files_struct *new_fsp = file_find_dit(fsp->dev, fsp->inode, &share_entry->time);
+
+                       /* Paranoia check... */
+                       if(new_fsp == NULL) {
+                               DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is not a local file !\n", i ));
+                               unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+                               abort();
+                       }
+
+                       oplock_break_level2(new_fsp, True, token);
+
+               } else {
+
+                       /*
+                        * This is a remote file and so we send an asynchronous
+                        * message.
+                        */
+
+                       request_oplock_break(share_entry, fsp->dev, fsp->inode);
+               }
+       }
+
+       free((char *)share_list);
+       unlock_share_entry_fsp(fsp);
+
+       /* Paranoia check... */
+       if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+               DEBUG(0,("release_level_2_oplocks_on_change: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name));
+               abort();
+       }
+}
 
 /****************************************************************************
 setup oplocks for this process
index 0b3b5bbe276d8fa73d8dec8acc3ced40541a966b..dd53eb46e76db1587d7d38a8e74fcf609e8e6dee 100644 (file)
@@ -2192,6 +2192,8 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length
   CHECK_READ(fsp);
   CHECK_ERROR(fsp);
 
+  release_level_2_oplocks_on_change(fsp);
+
   numtoread = SVAL(inbuf,smb_vwv1);
   startpos = IVAL(inbuf,smb_vwv2);
   
@@ -2572,8 +2574,7 @@ int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int d
      zero then the file size should be extended or
      truncated to the size given in smb_vwv[2-3] */
   if(numtowrite == 0) {
-      if((nwritten = set_filelen(fsp->fd, (SMB_OFF_T)startpos)) >= 0) /* tpot vfs */
-      set_filelen_write_cache(fsp, startpos); 
+      nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos);
   } else
     nwritten = write_file(fsp,data,startpos,numtowrite);
   
@@ -2989,6 +2990,8 @@ int reply_lock(connection_struct *conn,
        CHECK_FSP(fsp,conn);
        CHECK_ERROR(fsp);
 
+       release_level_2_oplocks_on_change(fsp);
+
        count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
        offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
 
@@ -4243,9 +4246,7 @@ int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,
 {
   files_struct *fsp = file_fsp(inbuf,smb_vwv2);
   unsigned char locktype = CVAL(inbuf,smb_vwv3);
-#if 0
   unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1);
-#endif
   uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
   uint16 num_locks = SVAL(inbuf,smb_vwv7);
   SMB_BIG_UINT count = 0, offset = 0;
@@ -4268,8 +4269,11 @@ int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,
    */
   if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE))
   {
-    DEBUG(5,("reply_lockingX: oplock break reply from client for fnum = %d\n",
-              fsp->fnum));
+       /* Client can insist on breaking to none. */
+       BOOL break_to_none = (oplocklevel == 0);
+
+    DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n",
+              fsp->fnum, (unsigned int)oplocklevel ));
 
     /*
      * Make sure we have granted an exclusive or batch oplock on this file.
@@ -4290,7 +4294,7 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name));
       }
     }
 
-    if (remove_oplock(fsp) == False) {
+    if (remove_oplock(fsp, break_to_none) == False) {
       DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n",
             fsp->fsp_name ));
     }
@@ -4308,6 +4312,13 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name));
     }
   }
 
+  /*
+   * We do this check *after* we have checked this is not a oplock break
+   * response message. JRA.
+   */
+
+  release_level_2_oplocks_on_change(fsp);
+
   /* Data now points at the beginning of the list
      of smb_unlkrng structs */
   for(i = 0; i < (int)num_ulocks; i++) {
index 17a362fb5c0e95b4eab31c69d2601f42eef84890..5b6759aec0c972b3b57790cb830c3f9148465c05 100644 (file)
@@ -1969,17 +1969,32 @@ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode))
           fname, (double)size ));
 
     if (fd == -1) {
-      fd = conn->vfs_ops.open(conn,dos_to_unix(fname,False),O_RDWR,0);
-      if (fd == -1)
+      files_struct *new_fsp = NULL;
+      int access_mode = 0;
+      int action = 0;
+
+      if(global_oplock_break) {
+        /* Queue this file modify as we are the process of an oplock break.  */
+
+        DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+        DEBUGADD(2,( "in oplock break state.\n"));
+
+        push_oplock_pending_smb_message(inbuf, length);
+        return -1;
+      }
+
+      new_fsp = open_file_shared(conn, fname, &sbuf,
+                           SET_OPEN_MODE(DOS_OPEN_RDWR),
+                           (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                           0, 0, &access_mode, &action);
+       
+      if (new_fsp == NULL)
         return(UNIXERROR(ERRDOS,ERRbadpath));
-      set_filelen(fd, size); /* tpot vfs */
-      conn->vfs_ops.close(fsp,fd);
+      vfs_set_filelen(new_fsp, size);
+      close_file(new_fsp,True);
     } else {
-        set_filelen(fd, size); /* tpot vfs */
+        vfs_set_filelen(fsp, size);
     }
-
-    if(fsp)
-      set_filelen_write_cache(fsp, size);
   }
 
   SSVAL(params,0,0);
index 3b8d5eebcc91df4866f30ab4ef27edac1e984c63..ff150b4e40ba79216e4129947a1a651c8b3c2db3 100644 (file)
@@ -404,13 +404,73 @@ int vfswrap_utime(connection_struct *conn, char *path, struct utimbuf *times)
     return result;
 }
 
-int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T offset)
+int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
 {
-    int result;
-
+       int result = -1;
     START_PROFILE(syscall_ftruncate);
 
-    result = sys_ftruncate(fd, offset);
+#ifdef HAVE_FTRUNCATE_EXTEND
+       result = sys_ftruncate(fd, len);
+    END_PROFILE(syscall_ftruncate);
+    return result;
+#else
+
+       /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
+               extend a file with ftruncate. Provide alternate implementation
+               for this */
+
+       struct vfs_ops *vfs_ops = fsp->conn->vfs_ops;
+       SMB_STRUCT_STAT st;
+       char c = 0;
+       SMB_OFF_T currpos;
+
+       currpos = vfs_ops->lseek(fsp, (SMB_OFF_T)0, SEEK_CUR);
+       if(currpos == -1) {
+               goto done;
+       }
+
+       /* Do an fstat to see if the file is longer than
+               the requested size (call ftruncate),
+               or shorter, in which case seek to len - 1 and write 1
+               byte of zero */
+       if(vfs_ops->fstat(fsp, &st)<0) {
+               goto done;
+       }
+
+#ifdef S_ISFIFO
+       if (S_ISFIFO(st.st_mode)) {
+               result = 0;
+               goto done;
+       }
+#endif
+
+       if(st.st_size == len) {
+               result = 0;
+               goto done;
+       }
+
+       if(st.st_size > len) {
+               /* Yes this is *deliberately* sys_ftruncate ! JRA */
+               result = sys_ftruncate(fd, len);
+               goto done;
+       }
+
+       if(vfs_ops->lseek(fsp, len-1, SEEK_SET) != len -1) {
+               goto done;
+       }
+
+       if(vfs_ops->write(fsp, &c, 1)!=1) {
+               goto done;
+       }
+
+       /* Seek to where we were */
+       if(vfs_ops->lseek(fsp, currpos, SEEK_SET) != currpos) {
+               goto done;
+       }
+#endif
+
+  done:
+
     END_PROFILE(syscall_ftruncate);
     return result;
 }
index 77b42361ed280b4060bc1cb772dbae117d55499c..3db150ab6fc71ad5faca7d3076c247b3927e390d 100644 (file)
@@ -384,6 +384,24 @@ ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
   return (ssize_t)total;
 }
 
+/****************************************************************************
+ A vfs set_filelen call.
+ set the length of a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
+{
+       int ret;
+
+       release_level_2_oplocks_on_change(fsp);
+       if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1) {
+               set_filelen_write_cache(fsp, len);
+       }
+
+       return ret;
+}
+
 /****************************************************************************
  Transfer some data between two file_struct's.
 ****************************************************************************/
index 4267aab118c5d0734272d67d793b3d01b20beede..d9aa6bfca05f6d31f3a8cefe3fb6f235cafce644 100644 (file)
@@ -33,6 +33,7 @@ static int procnum; /* records process count number when forking */
 static struct cli_state current_cli;
 static fstring randomfname;
 static BOOL use_oplocks;
+static BOOL use_level_II_oplocks;
 
 static double create_procs(void (*fn)(int));
 
@@ -109,6 +110,7 @@ static BOOL open_nbt_connection(struct cli_state *c)
 
        c->timeout = 120000; /* set a really long timeout (2 minutes) */
        if (use_oplocks) c->use_oplocks = True;
+       if (use_level_II_oplocks) c->use_level_II_oplocks = True;
 
        if (!cli_session_request(c, &calling, &called)) {
                printf("%s rejected the session\n",host);
@@ -1885,13 +1887,13 @@ static void run_trans2test(int dummy)
 /*
   this is a harness for some oplock tests
  */
-static void run_oplock(int dummy)
+static void run_oplock1(int dummy)
 {
        static struct cli_state cli1;
        char *fname = "\\lockt1.lck";
        int fnum1;
 
-       printf("starting oplock test\n");
+       printf("starting oplock test 1\n");
 
        if (!open_connection(&cli1)) {
                return;
@@ -1927,7 +1929,120 @@ static void run_oplock(int dummy)
 
        close_connection(&cli1);
 
-       printf("finished oplock test\n");
+       printf("finished oplock test 1\n");
+}
+
+static void run_oplock2(int dummy)
+{
+       static struct cli_state cli1, cli2;
+       char *fname = "\\lockt2.lck";
+       int fnum1, fnum2;
+       int saved_use_oplocks = use_oplocks;
+       char buf[4];
+
+       use_level_II_oplocks = True;
+       use_oplocks = True;
+
+       printf("starting oplock test 2\n");
+
+       if (!open_connection(&cli1)) {
+               use_level_II_oplocks = False;
+               use_oplocks = saved_use_oplocks;
+               return;
+       }
+
+       cli1.use_oplocks = True;
+       cli1.use_level_II_oplocks = True;
+
+       if (!open_connection(&cli2)) {
+               use_level_II_oplocks = False;
+               use_oplocks = saved_use_oplocks;
+               return;
+       }
+
+       cli2.use_oplocks = True;
+       cli2.use_level_II_oplocks = True;
+
+       cli_unlink(&cli1, fname);
+
+       cli_sockopt(&cli1, sockops);
+       cli_sockopt(&cli2, sockops);
+
+       fnum1 = cli_open(&cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(&cli1));
+               return;
+       }
+
+       /* Don't need the globals any more. */
+       use_level_II_oplocks = False;
+       use_oplocks = saved_use_oplocks;
+
+       if (fork() == 0) {
+               /* Child code */
+               fnum2 = cli_open(&cli2, fname, O_RDWR, DENY_NONE);
+               if (fnum2 == -1) {
+                       printf("second open of %s failed (%s)\n", fname, cli_errstr(&cli1));
+                       exit(0);
+               }
+
+               sleep(2);
+
+               if (!cli_close(&cli2, fnum2)) {
+                       printf("close2 failed (%s)\n", cli_errstr(&cli1));
+               }
+
+               exit(0);
+       }
+
+       sleep(2);
+
+       /* Ensure cli1 processes the break. */
+
+       if (cli_read(&cli1, fnum1, buf, 0, 4) != 4) {
+               printf("read on fnum1 failed (%s)\n", cli_errstr(&cli1));
+       }
+
+       /* Should now be at level II. */
+       /* Test if sending a write locks causes a break to none. */
+
+       if (!cli_lock(&cli1, fnum1, 0, 4, 0, READ_LOCK)) {
+               printf("lock failed (%s)\n", cli_errstr(&cli1));
+       }
+
+       cli_unlock(&cli, fnum1, 0, 4);
+
+       sleep(2);
+
+       if (!cli_lock(&cli1, fnum1, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock failed (%s)\n", cli_errstr(&cli1));
+       }
+
+       cli_unlock(&cli, fnum1, 0, 4);
+
+       sleep(2);
+
+       cli_read(&cli1, fnum1, buf, 0, 4);
+
+#if 0
+       if (cli_write(&cli1, fnum1, 0, buf, 0, 4) != 4) {
+               printf("write on fnum1 failed (%s)\n", cli_errstr(&cli1));
+       }
+#endif
+
+       if (!cli_close(&cli1, fnum1)) {
+               printf("close1 failed (%s)\n", cli_errstr(&cli1));
+       }
+
+       sleep(4);
+
+       if (!cli_unlink(&cli1, fname)) {
+               printf("unlink failed (%s)\n", cli_errstr(&cli1));
+       }
+
+       close_connection(&cli1);
+
+       printf("finished oplock test 2\n");
 }
 
 
@@ -2089,7 +2204,8 @@ static struct {
        {"NEGNOWAIT", run_negprot_nowait, 0},
        {"NBW95",  run_nbw95, 0},
        {"NBWNT",  run_nbwnt, 0},
-       {"OPLOCK",  run_oplock, 0},
+       {"OPLOCK1",  run_oplock1, 0},
+       {"OPLOCK2",  run_oplock2, 0},
        {"DIR",  run_dirtest, 0},
        {"DENY1",  run_denytest1, 0},
        {"DENY2",  run_denytest2, 0},