diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/client.h | 1 | ||||
-rw-r--r-- | source3/include/proto.h | 7 | ||||
-rw-r--r-- | source3/lib/util.c | 51 | ||||
-rw-r--r-- | source3/libsmb/cliconnect.c | 2 | ||||
-rw-r--r-- | source3/libsmb/clientgen.c | 11 | ||||
-rw-r--r-- | source3/libsmb/clifile.c | 5 | ||||
-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 | ||||
-rw-r--r-- | source3/utils/torture.c | 124 |
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}, |