From 5864551aef50295addd1c8aa690a52870f70626d Mon Sep 17 00:00:00 2001 From: Jeremy Allison <jra@samba.org> Date: Wed, 1 Oct 1997 23:32:22 +0000 Subject: OPLOCK CHECK-IN - oplocks are now *OPERATIONAL* !!!! Yipeee. At least as far as I can check in a short time :-). local.h: Changed OPLOCK_BREAK_TIMEOUT to 30 seconds. locking.c: Big changes to delete oplocks on a share mode entry. proto.h: updated. reply.c: Added oplock break code in lockingX reply & readbraw reply. server.c: Add batch oplock code. Force server shutdown if client fails to respond to oplock break. smb.h: Fix silly slow share mode oplock define bug. status.c: Add oplock status info. Jeremy (jallison@whistle.com) (This used to be commit 4c83d37239f15f855fc10f01d7b4bf4217fb9eda) --- source3/smbd/reply.c | 57 ++++++++++++++- source3/smbd/server.c | 199 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 226 insertions(+), 30 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 8987e7c0c2..1a896aa02a 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1254,10 +1254,13 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) if (oplock_request && lp_fake_oplocks(SNUM(cnum))) { smb_action |= EXTENDED_OPLOCK_GRANTED; + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; } - if(fsp->granted_oplock) + if(fsp->granted_oplock) { smb_action |= EXTENDED_OPLOCK_GRANTED; + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + } set_message(outbuf,15,0,True); SSVAL(outbuf,smb_vwv2,fnum); @@ -1609,6 +1612,22 @@ int reply_readbraw(char *inbuf, char *outbuf) int fd; char *fname; +#ifdef USE_OPLOCKS + /* + * Special check if an oplock break has been issued + * and the readraw request croses on the wire, we must + * return a zero length response here. + */ + + if(global_oplock_break) + { + _smb_setlen(header,0); + transfer_file(0,Client,0,header,4,0); + DEBUG(5,("readbraw - oplock break finished\n")); + return -1; + } +#endif + cnum = SVAL(inbuf,smb_tid); fnum = GETFNUM(inbuf,smb_vwv0); @@ -3342,7 +3361,10 @@ int reply_setdir(char *inbuf,char *outbuf) int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) { int fnum = GETFNUM(inbuf,smb_vwv2); - uint16 locktype = SVAL(inbuf,smb_vwv3); + unsigned char locktype = CVAL(inbuf,smb_vwv3); +#if 0 + unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1); +#endif /* USE_OPLOCKS */ uint16 num_ulocks = SVAL(inbuf,smb_vwv6); uint16 num_locks = SVAL(inbuf,smb_vwv7); uint32 count, offset; @@ -3359,6 +3381,35 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) CHECK_ERROR(fnum); data = smb_buf(inbuf); + +#ifdef USE_OPLOCKS + /* Check if this is an oplock break on a file + we have granted an oplock on. + */ + if((locktype == LOCKING_ANDX_OPLOCK_RELEASE) && + (num_ulocks == 0) && (num_locks == 0) && + (CVAL(inbuf,smb_vwv0) == 0xFF)) + { + DEBUG(5,("reply_lockingX: oplock break reply from client for fnum = %d\n", + fnum)); + /* + * Make sure we have granted an oplock on this file. + */ + if(!Files[fnum].granted_oplock) + { + DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ +oplock granted on this file.\n", fnum)); + return ERROR(ERRDOS,ERRlock); + } + + /* Just clear the granted flag and return. oplock_break() + will handle changing the share_mode_entry. */ + + Files[fnum].granted_oplock = 0; + return -1; + } +#endif /* USE_OPLOCKS */ + /* Data now points at the beginning of the list of smb_unlkrng structs */ for(i = 0; i < (int)num_ulocks; i++) { @@ -3393,7 +3444,7 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) set_message(outbuf,2,0,True); DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n", - timestring(),fnum,cnum,locktype,num_locks,num_ulocks)); + timestring(),fnum,cnum,(unsigned int)locktype,num_locks,num_ulocks)); chain_fnum = fnum; diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 708a2c272b..5f59af1bf1 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -1458,23 +1458,74 @@ BOOL check_file_sharing(int cnum,char *fname) struct stat sbuf; share_lock_token token; int pid = getpid(); + uint32 dev, inode; if(!lp_share_modes(SNUM(cnum))) return True; if (stat(fname,&sbuf) == -1) return(True); - lock_share_entry(cnum, (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, &token); - num_share_modes = get_share_modes(cnum, token, - (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, &old_shares); + dev = (uint32)sbuf.st_dev; + inode = (uint32)sbuf.st_ino; - for( i = 0; i < num_share_modes; i++) + lock_share_entry(cnum, dev, inode, &token); + num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares); + + /* + * Check if the share modes will give us access. + */ + + if(num_share_modes != 0) { - if (old_shares[i].share_mode != DENY_DOS) - goto free_and_exit; + BOOL broke_oplock; + + do + { + + broke_oplock = False; + for(i = 0; i < num_share_modes; i++) + { + min_share_mode_entry *share_entry = &old_shares[i]; + + /* someone else has a share lock on it, check to see + if we can too */ + if ((share_entry->share_mode != DENY_DOS) || (share_entry->pid != pid)) + goto free_and_exit; + +#ifdef USE_OPLOCKS + /* + * The share modes would give us access. Check if someone + * has an oplock on this file. If so we must break it before + * continuing. + */ + if(share_entry->op_type & BATCH_OPLOCK) + { + + DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \ +dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode)); - if(old_shares[i].pid != pid) - goto free_and_exit; + /* Oplock break.... */ + unlock_share_entry(cnum, dev, inode, token); + if(request_oplock_break(share_entry, dev, inode) == False) + { + free((char *)old_shares); + DEBUG(0,("check_file_sharing: FAILED when breaking oplock (%x) on file %s, \ +dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode)); + return False; + } + lock_share_entry(cnum, dev, inode, &token); + broke_oplock = True; + break; + } +#endif /* USE_OPLOCKS */ + } /* end for */ + + if(broke_oplock) + { + free((char *)old_shares); + num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares); + } + } while(broke_oplock); } /* XXXX exactly what share mode combinations should be allowed for @@ -1485,7 +1536,7 @@ BOOL check_file_sharing(int cnum,char *fname) free_and_exit: - unlock_share_entry(cnum, (uint32)sbuf.st_dev, (uint32)sbuf.st_ino, token); + unlock_share_entry(cnum, dev, inode, token); if(old_shares != NULL) free((char *)old_shares); return(ret); @@ -1576,6 +1627,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, share_lock_token token; uint32 dev = 0; uint32 inode = 0; + int num_share_modes = 0; fs_p->open = False; fs_p->fd_ptr = 0; @@ -1647,7 +1699,6 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, if (lp_share_modes(SNUM(cnum))) { - int num_shares = 0; int i; min_share_mode_entry *old_shares = 0; @@ -1657,14 +1708,14 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, inode = (uint32)sbuf.st_ino; lock_share_entry(cnum, dev, inode, &token); share_locked = True; - num_shares = get_share_modes(cnum, token, dev, inode, &old_shares); + num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares); } /* * Check if the share modes will give us access. */ - if(share_locked && (num_shares != 0)) + if(share_locked && (num_share_modes != 0)) { BOOL broke_oplock; @@ -1672,7 +1723,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, { broke_oplock = False; - for(i = 0; i < num_shares; i++) + for(i = 0; i < num_share_modes; i++) { min_share_mode_entry *share_entry = &old_shares[i]; @@ -1696,7 +1747,7 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, if(share_entry->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) { - DEBUG(5,("open file shared: breaking oplock (%x) on file %s, \ + DEBUG(5,("open_file_shared: breaking oplock (%x) on file %s, \ dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode)); /* Oplock break.... */ @@ -1704,7 +1755,7 @@ dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode)); if(request_oplock_break(share_entry, dev, inode) == False) { free((char *)old_shares); - DEBUG(0,("open file shared: FAILED when breaking oplock (%x) on file %s, \ + DEBUG(0,("open_file_shared: FAILED when breaking oplock (%x) on file %s, \ dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode)); errno = EACCES; unix_ERR_class = ERRDOS; @@ -1721,7 +1772,7 @@ dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode)); if(broke_oplock) { free((char *)old_shares); - num_shares = get_share_modes(cnum, token, dev, inode, &old_shares); + num_share_modes = get_share_modes(cnum, token, dev, inode, &old_shares); } } while(broke_oplock); } @@ -1782,7 +1833,27 @@ dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode)); file (which expects the share_mode_entry to be there). */ if (lp_share_modes(SNUM(cnum))) - set_share_mode(token, fnum, 0, 0); + { + uint16 port = 0; +#ifdef USE_OPLOCKS + /* JRA. Currently this only services Exlcusive and batch + oplocks (no other opens on this file). This needs to + be extended to level II oplocks (multiple reader + oplocks). */ + + if(oplock_request && (num_share_modes == 0)) + { + fs_p->granted_oplock = True; + global_oplocks_open++; + port = oplock_port; + + DEBUG(5,("open_file_shared: granted oplock (%x) on file %s, \ +dev = %x, inode = %x\n", oplock_request, fname, dev, inode)); + } + +#endif /* USE_OPLOCKS */ + set_share_mode(token, fnum, port, oplock_request); + } if ((flags2&O_TRUNC) && file_existed) truncate_unless_locked(fnum,cnum,token,&share_locked); @@ -2444,7 +2515,7 @@ should be %d).\n", msg_len, OPLOCK_BREAK_MSG_LEN)); struct sockaddr_in toaddr; DEBUG(5,("process_local_message: oplock break request from \ -pid %d, dev %d, inode %d\n", remotepid, dev, inode)); +pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode)); /* * If we have no record of any currently open oplocks, @@ -2483,6 +2554,11 @@ oplocks. Returning success.\n")); remotepid, strerror(errno))); return False; } + + DEBUG(5,("process_local_message: oplock break reply sent to \ +pid %d, port %d, for file dev = %x, inode = %x\n", remotepid, + from_port, dev, inode)); + } break; default: @@ -2503,6 +2579,9 @@ BOOL oplock_break(uint32 dev, uint32 inode) static char *outbuf = NULL; files_struct *fsp = NULL; int fnum; + share_lock_token token; + time_t start_time; + BOOL shutdown_server = False; if(inbuf == NULL) { @@ -2579,24 +2658,90 @@ allowing break to succeed.\n", dev, inode, fnum)); global_oplock_break = True; /* Process incoming messages. */ - while(global_oplock_break && OPEN_FNUM(fnum)) + + /* JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT + seconds we should just die.... */ + + start_time = time(NULL); + + while(OPEN_FNUM(fnum) && fsp->granted_oplock) { if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False) { + /* + * Die if we got an error. + */ + if (smb_read_error == READ_EOF) - { - DEBUG(3,("oplock_break: end of file from client\n")); - return False; - } + DEBUG(0,("oplock_break: end of file from client\n")); if (smb_read_error == READ_ERROR) - { - DEBUG(3,("oplock_break: receive_smb error (%s)\n", + DEBUG(0,("oplock_break: receive_smb error (%s)\n", strerror(errno))); - return False; - } + + if (smb_read_error == READ_TIMEOUT) + DEBUG(0,("oplock_break: receive_smb timed out after %d seconds.\n", + OPLOCK_BREAK_TIMEOUT)); + + DEBUG(0,("oplock_break failed for file %s (fnum = %d, dev = %x, \ +inode = %x).\n", fsp->name, fnum, dev, inode)); + shutdown_server = True; + break; } process_smb(inbuf, outbuf); + + /* We only need this in case a readraw crossed on the wire. */ + global_oplock_break = False; + + /* + * Die if we go over the time limit. + */ + + if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) + { + DEBUG(0,("oplock_break: no break received from client within \ +%d seconds.\n", OPLOCK_BREAK_TIMEOUT)); + DEBUG(0,("oplock_break failed for file %s (fnum = %d, dev = %x, \ +inode = %x).\n", fsp->name, fnum, dev, inode)); + shutdown_server = True; + break; + } + } + + /* + * If the client did not respond we must die. + */ + + if(shutdown_server) + { + DEBUG(0,("oplock_break: client failure in break - shutting down this smbd.\n")); + close_sockets(); + close(oplock_sock); + exit_server("oplock break failure"); + } + + if(OPEN_FNUM(fnum)) + { + /* Remove the oplock flag from the sharemode. */ + lock_share_entry(fsp->cnum, dev, inode, &token); + if(remove_share_oplock( fnum, token)==False) + { + DEBUG(0,("oplock_break: failed to remove share oplock for fnum %d, \ +dev = %x, inode = %x\n", fnum, dev, inode)); + unlock_share_entry(fsp->cnum, dev, inode, token); + return False; + } + unlock_share_entry(fsp->cnum, dev, inode, token); + } + + global_oplocks_open--; + + /* Santity check - remove this later. JRA */ + if(global_oplocks_open < 0) + { + DEBUG(0,("oplock_break: global_oplocks_open < 0 (%d). PANIC ERROR\n", + global_oplocks_open)); + abort(); } return True; -- cgit