summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/client.h1
-rw-r--r--source3/include/proto.h7
-rw-r--r--source3/lib/util.c51
-rw-r--r--source3/libsmb/cliconnect.c2
-rw-r--r--source3/libsmb/clientgen.c11
-rw-r--r--source3/libsmb/clifile.c5
-rw-r--r--source3/smbd/fileio.c81
-rw-r--r--source3/smbd/negprot.c2
-rw-r--r--source3/smbd/oplock.c99
-rw-r--r--source3/smbd/reply.c25
-rw-r--r--source3/smbd/trans2.c31
-rw-r--r--source3/smbd/vfs-wrap.c68
-rw-r--r--source3/smbd/vfs.c18
-rw-r--r--source3/utils/torture.c124
14 files changed, 360 insertions, 165 deletions
diff --git a/source3/include/client.h b/source3/include/client.h
index 748c7375e1..ae7229b516 100644
--- a/source3/include/client.h
+++ b/source3/include/client.h
@@ -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 */
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 57d972cf36..16545b156d 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -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);
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 0aef60082f..2d922aab70 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -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
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 3292b9e1d6..ff81d886b0 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -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);
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
index 8d9fcb61d6..8d9e2f034f 100644
--- a/source3/libsmb/clientgen.c
+++ b/source3/libsmb/clientgen.c
@@ -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 */
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index 63f6f8cc6c..2f183ea135 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -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);
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index bb7ab46baa..35e2f1455e 100644
--- a/source3/smbd/fileio.c
+++ b/source3/smbd/fileio.c
@@ -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.
diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c
index 1029c8db62..41e95b816d 100644
--- a/source3/smbd/negprot.c
+++ b/source3/smbd/negprot.c
@@ -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);
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 4a8260421c..366b4d0fec 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -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
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 0b3b5bbe27..dd53eb46e7 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -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++) {
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 17a362fb5c..5b6759aec0 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -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);
diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c
index 3b8d5eebcc..ff150b4e40 100644
--- a/source3/smbd/vfs-wrap.c
+++ b/source3/smbd/vfs-wrap.c
@@ -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;
}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 77b42361ed..3db150ab6f 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -385,6 +385,24 @@ ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
}
/****************************************************************************
+ 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.
****************************************************************************/
diff --git a/source3/utils/torture.c b/source3/utils/torture.c
index 4267aab118..d9aa6bfca0 100644
--- a/source3/utils/torture.c
+++ b/source3/utils/torture.c
@@ -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},