summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
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.
****************************************************************************/