diff options
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/fileio.c | 81 | ||||
-rw-r--r-- | source3/smbd/negprot.c | 2 | ||||
-rw-r--r-- | source3/smbd/oplock.c | 99 | ||||
-rw-r--r-- | source3/smbd/reply.c | 25 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 31 | ||||
-rw-r--r-- | source3/smbd/vfs-wrap.c | 68 | ||||
-rw-r--r-- | source3/smbd/vfs.c | 18 |
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. ****************************************************************************/ |