summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2000-11-16 00:59:18 +0000
committerJeremy Allison <jra@samba.org>2000-11-16 00:59:18 +0000
commit6f58dd587124c8b85fc62177b26129aaea5819b0 (patch)
tree40c926a6f3e5b5db7e913e13ea6571b97dacf362 /source3/smbd
parent14355a6434952071e862af3a1e7bcfbbf640a6a3 (diff)
downloadsamba-6f58dd587124c8b85fc62177b26129aaea5819b0.tar.gz
samba-6f58dd587124c8b85fc62177b26129aaea5819b0.tar.bz2
samba-6f58dd587124c8b85fc62177b26129aaea5819b0.zip
Ok - fixed a bug in our levelII oplock code. We need to break a level II on
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)
Diffstat (limited to 'source3/smbd')
-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
7 files changed, 220 insertions, 104 deletions
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.
****************************************************************************/