diff options
author | Jeremy Allison <jra@samba.org> | 2000-11-16 00:59:18 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2000-11-16 00:59:18 +0000 |
commit | 6f58dd587124c8b85fc62177b26129aaea5819b0 (patch) | |
tree | 40c926a6f3e5b5db7e913e13ea6571b97dacf362 /source3 | |
parent | 14355a6434952071e862af3a1e7bcfbbf640a6a3 (diff) | |
download | samba-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')
-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}, |