X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source%2Fsmbd%2Foplock.c;h=9ac9d7628400c7bf9671975f22b408e40579ae88;hb=5c6c8e1fe93;hp=c17e63384f28e28e4831d73d0bb26b61a9a91511;hpb=a95943fde0ad89ae3f2deca2f7ba9cb5ab612b74;p=samba.git diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c index c17e63384f2..9ac9d762840 100644 --- a/source/smbd/oplock.c +++ b/source/smbd/oplock.c @@ -1,12 +1,13 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. oplock processing Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1998 - 2001 + Copyright (C) Volker Lendecke 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -15,30 +16,22 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ +#define DBGC_CLASS DBGC_LOCKING #include "includes.h" -extern int DEBUGLEVEL; - -/* Oplock ipc UDP socket. */ -static int oplock_sock = -1; -uint16 global_oplock_port = 0; - /* Current number of oplocks we have outstanding. */ static int32 exclusive_oplocks_open = 0; static int32 level_II_oplocks_open = 0; BOOL global_client_failed_oplock_break = False; -BOOL global_oplock_break = False; +extern uint32 global_client_caps; extern int smb_read_error; static struct kernel_oplocks *koplocks; -static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval, BOOL local); - /**************************************************************************** Get the number of current exclusive oplocks. ****************************************************************************/ @@ -54,134 +47,81 @@ int32 get_number_of_exclusive_open_oplocks(void) BOOL oplock_message_waiting(fd_set *fds) { - if (koplocks && koplocks->msg_waiting(fds)) - return True; - - if (FD_ISSET(oplock_sock, fds)) + if (koplocks && koplocks->msg_waiting(fds)) { return True; + } return False; } /**************************************************************************** - Read an oplock break message from either the oplock UDP fd or the - kernel (if kernel oplocks are supported). - - If timeout is zero then *fds contains the file descriptors that - are ready to be read and acted upon. If timeout is non-zero then - *fds contains the file descriptors to be selected on for read. - The timeout is in milliseconds - + Find out if there are any kernel oplock messages waiting and process them + if so. pfds is the fd_set from the main select loop (which contains any + kernel oplock fd if that's what the system uses (IRIX). If may be NULL if + we're calling this in a shutting down state. ****************************************************************************/ -BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeout) +void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds) { - struct sockaddr_in from; - int fromlen = sizeof(from); - int32 msg_len = 0; - - smb_read_error = 0; - - if(timeout != 0) { - struct timeval to; - int selrtn; - int maxfd = oplock_sock; - - if (koplocks && koplocks->notification_fd != -1) { - FD_SET(koplocks->notification_fd, fds); - maxfd = MAX(maxfd, koplocks->notification_fd); - } - - to.tv_sec = timeout / 1000; - to.tv_usec = (timeout % 1000) * 1000; - - selrtn = sys_select(maxfd+1,fds,&to); - - if (selrtn == -1 && errno == EINTR) { - /* could be a kernel oplock interrupt */ - if (koplocks && koplocks->msg_waiting(fds)) { - return koplocks->receive_message(fds, buffer, buffer_len); - } - } - - /* Check if error */ - if(selrtn == -1) { - /* something is wrong. Maybe the socket is dead? */ - smb_read_error = READ_ERROR; - return False; - } - - /* Did we timeout ? */ - if (selrtn == 0) { - smb_read_error = READ_TIMEOUT; - return False; - } - } - - if (koplocks && koplocks->msg_waiting(fds)) { - return koplocks->receive_message(fds, buffer, buffer_len); - } - - if (!FD_ISSET(oplock_sock, fds)) return False; - - /* - * From here down we deal with the smbd <--> smbd - * oplock break protocol only. - */ - - /* - * Read a loopback udp message. - */ - msg_len = recvfrom(oplock_sock, &buffer[OPBRK_CMD_HEADER_LEN], - buffer_len - OPBRK_CMD_HEADER_LEN, 0, - (struct sockaddr *)&from, &fromlen); - - if(msg_len < 0) { - DEBUG(0,("receive_local_message. Error in recvfrom. (%s).\n",strerror(errno))); - return False; - } - - /* Validate message length. */ - if(msg_len > (buffer_len - OPBRK_CMD_HEADER_LEN)) { - DEBUG(0,("receive_local_message: invalid msg_len (%d) max can be %d\n", - msg_len, - buffer_len - OPBRK_CMD_HEADER_LEN)); - return False; - } - - /* Validate message from address (must be localhost). */ - if(from.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { - DEBUG(0,("receive_local_message: invalid 'from' address \ -(was %lx should be 127.0.0.1\n", (long)from.sin_addr.s_addr)); - return False; - } - - /* Setup the message header */ - SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,msg_len); - SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,ntohs(from.sin_port)); - - return True; + /* + * We need to check for kernel oplocks before going into the select + * here, as the EINTR generated by the linux kernel oplock may have + * already been eaten. JRA. + */ + + if (!koplocks) { + return; + } + + while (koplocks->msg_waiting(pfds)) { + files_struct *fsp; + char msg[MSG_SMB_KERNEL_BREAK_SIZE]; + + fsp = koplocks->receive_message(pfds); + + if (fsp == NULL) { + DEBUG(3, ("Kernel oplock message announced, but none " + "received\n")); + return; + } + + /* Put the kernel break info into the message. */ + push_file_id_16(msg, &fsp->file_id); + SIVAL(msg,16,fsp->fh->gen_id); + + /* Don't need to be root here as we're only ever + sending to ourselves. */ + + messaging_send_buf(msg_ctx, procid_self(), + MSG_SMB_KERNEL_BREAK, + (uint8 *)&msg, MSG_SMB_KERNEL_BREAK_SIZE); + } } /**************************************************************************** Attempt to set an oplock on a file. Always succeeds if kernel oplocks are disabled (just sets flags). Returns True if oplock set. ****************************************************************************/ + BOOL set_file_oplock(files_struct *fsp, int oplock_type) { - if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) + if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) { return False; + } fsp->oplock_type = oplock_type; fsp->sent_oplock_break = NO_BREAK_SENT; - if (oplock_type == LEVEL_II_OPLOCK) + if (oplock_type == LEVEL_II_OPLOCK) { level_II_oplocks_open++; - else + } else { exclusive_oplocks_open++; + } - DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", - fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, - (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec )); + DEBUG(5,("set_file_oplock: granted oplock on file %s, %s/%lu, " + "tv_sec = %x, tv_usec = %x\n", + fsp->fsp_name, file_id_string_tos(&fsp->file_id), + fsp->fh->gen_id, (int)fsp->open_time.tv_sec, + (int)fsp->open_time.tv_usec )); return True; } @@ -192,17 +132,27 @@ BOOL set_file_oplock(files_struct *fsp, int oplock_type) void release_file_oplock(files_struct *fsp) { - if (koplocks) koplocks->release_oplock(fsp); + if ((fsp->oplock_type != NO_OPLOCK) && + (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) && + koplocks) { + koplocks->release_oplock(fsp); + } - if (fsp->oplock_type == LEVEL_II_OPLOCK) + if (fsp->oplock_type == LEVEL_II_OPLOCK) { level_II_oplocks_open--; - else + } else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { exclusive_oplocks_open--; + } + + SMB_ASSERT(exclusive_oplocks_open>=0); + SMB_ASSERT(level_II_oplocks_open>=0); fsp->oplock_type = NO_OPLOCK; fsp->sent_oplock_break = NO_BREAK_SENT; flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH); + + TALLOC_FREE(fsp->oplock_timeout); } /**************************************************************************** @@ -211,7 +161,9 @@ void release_file_oplock(files_struct *fsp) static void downgrade_file_oplock(files_struct *fsp) { - if (koplocks) koplocks->release_oplock(fsp); + if (koplocks) { + koplocks->release_oplock(fsp); + } fsp->oplock_type = LEVEL_II_OPLOCK; exclusive_oplocks_open--; level_II_oplocks_open++; @@ -224,920 +176,570 @@ static void downgrade_file_oplock(files_struct *fsp) to none even if a "break-to-level II" was sent. ****************************************************************************/ -BOOL remove_oplock(files_struct *fsp, BOOL break_to_none) +BOOL remove_oplock(files_struct *fsp) { - SMB_DEV_T dev = fsp->dev; - SMB_INO_T inode = fsp->inode; - BOOL ret = True; + BOOL ret; + struct share_mode_lock *lck; /* Remove the oplock flag from the sharemode. */ - if (lock_share_entry_fsp(fsp) == False) { - DEBUG(0,("remove_oplock: failed to lock share entry for file %s\n", - fsp->fsp_name )); - ret = False; + lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + if (lck == NULL) { + DEBUG(0,("remove_oplock: failed to lock share entry for " + "file %s\n", fsp->fsp_name )); + return False; } + ret = remove_share_oplock(lck, fsp); + if (!ret) { + DEBUG(0,("remove_oplock: failed to remove share oplock for " + "file %s fnum %d, %s\n", + fsp->fsp_name, fsp->fnum, file_id_string_tos(&fsp->file_id))); + } + release_file_oplock(fsp); + TALLOC_FREE(lck); + return ret; +} - if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT || break_to_none) { - /* - * Deal with a reply when a break-to-none was sent. - */ - - if(remove_share_oplock(fsp)==False) { - DEBUG(0,("remove_oplock: failed to remove share oplock for file %s fnum %d, \ -dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); - ret = False; - } +/* + * Deal with a reply when a break-to-level II was sent. + */ +BOOL downgrade_oplock(files_struct *fsp) +{ + BOOL ret; + struct share_mode_lock *lck; - release_file_oplock(fsp); - } else { - /* - * Deal with a reply when a break-to-level II was sent. - */ - if(downgrade_share_oplock(fsp)==False) { - DEBUG(0,("remove_oplock: failed to downgrade share oplock for file %s fnum %d, \ -dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); - ret = False; - } - - downgrade_file_oplock(fsp); + lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + if (lck == NULL) { + DEBUG(0,("downgrade_oplock: failed to lock share entry for " + "file %s\n", fsp->fsp_name )); + return False; + } + ret = downgrade_share_oplock(lck, fsp); + if (!ret) { + DEBUG(0,("downgrade_oplock: failed to downgrade share oplock " + "for file %s fnum %d, file_id %s\n", + fsp->fsp_name, fsp->fnum, file_id_string_tos(&fsp->file_id))); } - unlock_share_entry_fsp(fsp); + downgrade_file_oplock(fsp); + TALLOC_FREE(lck); return ret; } /**************************************************************************** - Setup the listening set of file descriptors for an oplock break - message either from the UDP socket or from the kernel. Returns the maximum - fd used. + Return the fd (if any) used for receiving oplock notifications. ****************************************************************************/ -int setup_oplock_select_set( fd_set *fds) +int oplock_notify_fd(void) { - int maxfd = oplock_sock; - - if(oplock_sock == -1) - return 0; - - FD_SET(oplock_sock,fds); - - if (koplocks && koplocks->notification_fd != -1) { - FD_SET(koplocks->notification_fd, fds); - maxfd = MAX(maxfd, koplocks->notification_fd); + if (koplocks) { + return koplocks->notification_fd; } - return maxfd; + return -1; } /**************************************************************************** - Process an oplock break message - whether it came from the UDP socket - or from the kernel. + Set up an oplock break message. ****************************************************************************/ -BOOL process_local_message(char *buffer, int buf_size) + +static char *new_break_smb_message(TALLOC_CTX *mem_ctx, + files_struct *fsp, uint8 cmd) { - int32 msg_len; - uint16 from_port; - char *msg_start; - SMB_DEV_T dev; - SMB_INO_T inode; - pid_t remotepid; - struct timeval tval; - struct timeval *ptval = NULL; - uint16 break_cmd_type; - - msg_len = IVAL(buffer,OPBRK_CMD_LEN_OFFSET); - from_port = SVAL(buffer,OPBRK_CMD_PORT_OFFSET); - - msg_start = &buffer[OPBRK_CMD_HEADER_LEN]; - - DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n", - msg_len, from_port)); - - /* - * Pull the info out of the requesting packet. - */ - - break_cmd_type = SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET); - - switch(break_cmd_type) - { - case KERNEL_OPLOCK_BREAK_CMD: - if (!koplocks) { - DEBUG(0,("unexpected kernel oplock break!\n")); - break; - } - if (!koplocks->parse_message(msg_start, msg_len, &inode, &dev)) { - DEBUG(0,("kernel oplock break parse failure!\n")); - } - break; - - case OPLOCK_BREAK_CMD: - case LEVEL_II_OPLOCK_BREAK_CMD: - - /* Ensure that the msg length is correct. */ - if(msg_len != OPLOCK_BREAK_MSG_LEN) - { - DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d).\n", - (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN)); - return False; - } - { - long usec; - time_t sec; - - memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); - memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); - memcpy((char *)&sec, msg_start+OPLOCK_BREAK_SEC_OFFSET,sizeof(sec)); - tval.tv_sec = sec; - memcpy((char *)&usec, msg_start+OPLOCK_BREAK_USEC_OFFSET, sizeof(usec)); - tval.tv_usec = usec; - - ptval = &tval; - - memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); - - DEBUG(5,("process_local_message: (%s) oplock break request from \ -pid %d, port %d, dev = %x, inode = %.0f\n", - (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II", - (int)remotepid, from_port, (unsigned int)dev, (double)inode)); - } - break; - - /* - * Keep this as a debug case - eventually we can remove it. - */ - case 0x8001: - DEBUG(0,("process_local_message: Received unsolicited break \ -reply - dumping info.\n")); - - if(msg_len != OPLOCK_BREAK_MSG_LEN) - { - DEBUG(0,("process_local_message: ubr: incorrect length for reply \ -(was %d, should be %d).\n", (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN)); - return False; - } - - { - memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); - memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); - memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); - - DEBUG(0,("process_local_message: unsolicited oplock break reply from \ -pid %d, port %d, dev = %x, inode = %.0f\n", (int)remotepid, from_port, (unsigned int)dev, (double)inode)); - - } - return False; - - default: - DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n", - (unsigned int)SVAL(msg_start,0))); - return False; - } - - /* - * Now actually process the break request. - */ - - if((exclusive_oplocks_open + level_II_oplocks_open) != 0) - { - if (oplock_break(dev, inode, ptval, False) == False) - { - DEBUG(0,("process_local_message: oplock break failed.\n")); - return False; - } - } - else - { - /* - * If we have no record of any currently open oplocks, - * it's not an error, as a close command may have - * just been issued on the file that was oplocked. - * Just log a message and return success in this case. - */ - DEBUG(3,("process_local_message: oplock break requested with no outstanding \ -oplocks. Returning success.\n")); - } - - /* - * Do the appropriate reply - none in the kernel or level II case. - */ - - if(SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET) == OPLOCK_BREAK_CMD) - { - struct sockaddr_in toaddr; - - /* Send the message back after OR'ing in the 'REPLY' bit. */ - SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY); - - memset((char *)&toaddr,'\0',sizeof(toaddr)); - toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - toaddr.sin_port = htons(from_port); - toaddr.sin_family = AF_INET; - - if(sendto( oplock_sock, msg_start, OPLOCK_BREAK_MSG_LEN, 0, - (struct sockaddr *)&toaddr, sizeof(toaddr)) < 0) - { - DEBUG(0,("process_local_message: sendto process %d failed. Errno was %s\n", - (int)remotepid, strerror(errno))); - return False; - } - - DEBUG(5,("process_local_message: oplock break reply sent to \ -pid %d, port %d, for file dev = %x, inode = %.0f\n", - (int)remotepid, from_port, (unsigned int)dev, (double)inode)); - } - - return True; + char *result = TALLOC_ARRAY(mem_ctx, char, smb_size + 8*2 + 0); + + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + memset(result,'\0',smb_size); + set_message(result,8,0,True); + SCVAL(result,smb_com,SMBlockingX); + SSVAL(result,smb_tid,fsp->conn->cnum); + SSVAL(result,smb_pid,0xFFFF); + SSVAL(result,smb_uid,0); + SSVAL(result,smb_mid,0xFFFF); + SCVAL(result,smb_vwv0,0xFF); + SSVAL(result,smb_vwv2,fsp->fnum); + SCVAL(result,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE); + SCVAL(result,smb_vwv3+1,cmd); + return result; } /**************************************************************************** - Set up an oplock break message. + Function to do the waiting before sending a local break. ****************************************************************************/ -static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2) +static void wait_before_sending_break(void) { - memset(outbuf,'\0',smb_size); - set_message(outbuf,8,0,True); - - SCVAL(outbuf,smb_com,SMBlockingX); - SSVAL(outbuf,smb_tid,fsp->conn->cnum); - SSVAL(outbuf,smb_pid,0xFFFF); - SSVAL(outbuf,smb_uid,0); - SSVAL(outbuf,smb_mid,0xFFFF); - SCVAL(outbuf,smb_vwv0,0xFF); - SSVAL(outbuf,smb_vwv2,fsp->fnum); - SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE); - SCVAL(outbuf,smb_vwv3+1,level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); + long wait_time = (long)lp_oplock_break_wait_time(); + + if (wait_time) { + smb_msleep(wait_time); + } } /**************************************************************************** - Function to do the waiting before sending a local break. + Ensure that we have a valid oplock. ****************************************************************************/ -static void wait_before_sending_break(BOOL local_request) +static files_struct *initial_break_processing(struct file_id id, unsigned long file_id) { - extern struct timeval smb_last_time; + files_struct *fsp = NULL; - if(local_request) { - struct timeval cur_tv; - long wait_left = (long)lp_oplock_break_wait_time(); + if( DEBUGLVL( 3 ) ) { + dbgtext( "initial_break_processing: called for %s/%u\n", + file_id_string_tos(&id), (int)file_id); + dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n", + exclusive_oplocks_open, level_II_oplocks_open ); + } - if (wait_left == 0) - return; + /* + * We need to search the file open table for the + * entry containing this dev and inode, and ensure + * we have an oplock on it. + */ - GetTimeOfDay(&cur_tv); + fsp = file_find_dif(id, file_id); - wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) + - ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000); + if(fsp == NULL) { + /* The file could have been closed in the meantime - return success. */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "initial_break_processing: cannot find open file with " ); + dbgtext( "file_id %s gen_id = %lu", file_id_string_tos(&id), file_id); + dbgtext( "allowing break to succeed.\n" ); + } + return NULL; + } - if(wait_left > 0) { - wait_left = MIN(wait_left, 1000); - sys_usleep(wait_left * 1000); - } - } + /* Ensure we have an oplock on the file */ + + /* + * There is a potential race condition in that an oplock could + * have been broken due to another udp request, and yet there are + * still oplock break messages being sent in the udp message + * queue for this file. So return true if we don't have an oplock, + * as we may have just freed it. + */ + + if(fsp->oplock_type == NO_OPLOCK) { + if( DEBUGLVL( 3 ) ) { + dbgtext( "initial_break_processing: file %s ", fsp->fsp_name ); + dbgtext( "(file_id = %s gen_id = %lu) has no oplock.\n", + file_id_string_tos(&id), fsp->fh->gen_id ); + dbgtext( "Allowing break to succeed regardless.\n" ); + } + return NULL; + } + + return fsp; } -/**************************************************************************** - Ensure that we have a valid oplock. -****************************************************************************/ -static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) +static void oplock_timeout_handler(struct event_context *ctx, + struct timed_event *te, + const struct timeval *now, + void *private_data) { - files_struct *fsp = NULL; - - if( DEBUGLVL( 3 ) ) - { - dbgtext( "initial_break_processing: called for dev = %x, inode = %.0f tv_sec = %x, tv_usec = %x.\n", - (unsigned int)dev, (double)inode, tval ? (int)tval->tv_sec : 0, - tval ? (int)tval->tv_usec : 0); - dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n", - exclusive_oplocks_open, level_II_oplocks_open ); - } - - /* We need to search the file open table for the - entry containing this dev and inode, and ensure - we have an oplock on it. */ - fsp = file_find_dit(dev, inode, tval); - - if(fsp == NULL) - { - /* The file could have been closed in the meantime - return success. */ - if( DEBUGLVL( 3 ) ) - { - dbgtext( "initial_break_processing: cannot find open file with " ); - dbgtext( "dev = %x, inode = %.0f ", (unsigned int)dev, (double)inode); - dbgtext( "allowing break to succeed.\n" ); - } - return NULL; - } - - /* Ensure we have an oplock on the file */ - - /* There is a potential race condition in that an oplock could - have been broken due to another udp request, and yet there are - still oplock break messages being sent in the udp message - queue for this file. So return true if we don't have an oplock, - as we may have just freed it. - */ - - if(fsp->oplock_type == NO_OPLOCK) - { - if( DEBUGLVL( 3 ) ) - { - dbgtext( "initial_break_processing: file %s ", fsp->fsp_name ); - dbgtext( "(dev = %x, inode = %.0f) has no oplock.\n", (unsigned int)dev, (double)inode ); - dbgtext( "Allowing break to succeed regardless.\n" ); - } - return NULL; - } - - return fsp; + files_struct *fsp = (files_struct *)private_data; + + /* Remove the timed event handler. */ + TALLOC_FREE(fsp->oplock_timeout); + DEBUG(0, ("Oplock break failed for file %s -- replying anyway\n", fsp->fsp_name)); + global_client_failed_oplock_break = True; + remove_oplock(fsp); + reply_to_oplock_break_requests(fsp); } -/**************************************************************************** - Process a level II oplock break directly. -****************************************************************************/ +/******************************************************************* + Add a timeout handler waiting for the client reply. +*******************************************************************/ -BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token) +static void add_oplock_timeout_handler(files_struct *fsp) { - extern uint32 global_client_caps; - char outbuf[128]; - BOOL got_lock = False; - SMB_DEV_T dev = fsp->dev; - SMB_INO_T inode = fsp->inode; - - /* - * We can have a level II oplock even if the client is not - * level II oplock aware. In this case just remove the - * flags and don't send the break-to-none message to - * the client. - */ - - if (global_client_caps & CAP_LEVEL_II_OPLOCKS) { - /* - * If we are sending an oplock break due to an SMB sent - * by our own client we ensure that we wait at leat - * lp_oplock_break_wait_time() milliseconds before sending - * the packet. Sending the packet sooner can break Win9x - * and has reported to cause problems on NT. JRA. - */ - - wait_before_sending_break(local_request); - - /* Prepare the SMBlockingX message. */ - - prepare_break_message( outbuf, fsp, False); - if (!send_smb(smbd_server_fd(), outbuf)) - exit_server("oplock_break_level2: send_smb failed.\n"); - } - - /* - * Now we must update the shared memory structure to tell - * everyone else we no longer have a level II oplock on - * this open file. If local_request is true then token is - * the existing lock on the shared memory area. - */ - - if(!local_request && lock_share_entry_fsp(fsp) == False) { - DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name )); - } else { - got_lock = True; - } - - if(remove_share_oplock(fsp)==False) { - DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name )); - } - - if (!local_request && got_lock) - unlock_share_entry_fsp(fsp); - - fsp->oplock_type = NO_OPLOCK; - level_II_oplocks_open--; - - if(level_II_oplocks_open < 0) - { - DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n", - level_II_oplocks_open)); - abort(); - } - - if( DEBUGLVL( 3 ) ) - { - dbgtext( "oplock_break_level2: returning success for " ); - dbgtext( "dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); - dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open ); - } - - return True; + if (fsp->oplock_timeout != NULL) { + DEBUG(0, ("Logic problem -- have an oplock event hanging " + "around\n")); + } + + fsp->oplock_timeout = + event_add_timed(smbd_event_context(), NULL, + timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0), + "oplock_timeout_handler", + oplock_timeout_handler, fsp); + + if (fsp->oplock_timeout == NULL) { + DEBUG(0, ("Could not add oplock timeout handler\n")); + } } -/**************************************************************************** - Process an oplock break directly. -****************************************************************************/ +/******************************************************************* + This handles the case of a write triggering a break to none + message on a level2 oplock. + When we get this message we may be in any of three states : + NO_OPLOCK, LEVEL_II, FAKE_LEVEL2. We only send a message to + the client for LEVEL2. +*******************************************************************/ + +static void process_oplock_async_level2_break_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) +{ + struct share_mode_entry msg; + files_struct *fsp; + char *break_msg; + BOOL sign_state; + + if (data->data == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; + } + + if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { + DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); + return; + } + + /* De-linearize incoming message. */ + message_to_share_mode_entry(&msg, (char *)data->data); + + DEBUG(10, ("Got oplock async level 2 break message from pid %d: %s/%lu\n", + (int)procid_to_pid(&src), file_id_string_tos(&msg.id), msg.share_file_id)); + + fsp = initial_break_processing(msg.id, msg.share_file_id); + + if (fsp == NULL) { + /* We hit a race here. Break messages are sent, and before we + * get to process this message, we have closed the file. + * No need to reply as this is an async message. */ + DEBUG(3, ("process_oplock_async_level2_break_message: Did not find fsp, ignoring\n")); + return; + } + + if (fsp->oplock_type == NO_OPLOCK) { + /* We already got a "break to none" message and we've handled it. + * just ignore. */ + DEBUG(3, ("process_oplock_async_level2_break_message: already broken to none, ignoring.\n")); + return; + } + + if (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) { + /* Don't tell the client, just downgrade. */ + DEBUG(3, ("process_oplock_async_level2_break_message: downgrading fake level 2 oplock.\n")); + remove_oplock(fsp); + return; + } + + /* Ensure we're really at level2 state. */ + SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK); + + /* Now send a break to none message to our client. */ + + break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE); + if (break_msg == NULL) { + exit_server("Could not talloc break_msg\n"); + } + + /* Need to wait before sending a break message if we sent ourselves this message. */ + if (procid_to_pid(&src) == sys_getpid()) { + wait_before_sending_break(); + } + + /* Save the server smb signing state. */ + sign_state = srv_oplock_set_signing(False); + + show_msg(break_msg); + if (!send_smb(smbd_server_fd(), break_msg)) { + exit_server_cleanly("oplock_break: send_smb failed."); + } + + /* Restore the sign state to what it was. */ + srv_oplock_set_signing(sign_state); + + TALLOC_FREE(break_msg); + + /* Async level2 request, don't send a reply, just remove the oplock. */ + remove_oplock(fsp); +} + +/******************************************************************* + This handles the generic oplock break message from another smbd. +*******************************************************************/ + +static void process_oplock_break_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) +{ + struct share_mode_entry msg; + files_struct *fsp; + char *break_msg; + BOOL break_to_level2 = False; + BOOL sign_state; + + if (data->data == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; + } + + if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { + DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); + return; + } + + /* De-linearize incoming message. */ + message_to_share_mode_entry(&msg, (char *)data->data); + + DEBUG(10, ("Got oplock break message from pid %d: %s/%lu\n", + (int)procid_to_pid(&src), file_id_string_tos(&msg.id), msg.share_file_id)); + + fsp = initial_break_processing(msg.id, msg.share_file_id); + + if (fsp == NULL) { + /* a We hit race here. Break messages are sent, and before we + * get to process this message, we have closed the file. Reply + * with 'ok, oplock broken' */ + DEBUG(3, ("Did not find fsp\n")); + + /* We just send the same message back. */ + messaging_send_buf(msg_ctx, src, MSG_SMB_BREAK_RESPONSE, + (uint8 *)data->data, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + return; + } + + if (fsp->sent_oplock_break != NO_BREAK_SENT) { + /* Remember we have to inform the requesting PID when the + * client replies */ + msg.pid = src; + ADD_TO_ARRAY(NULL, struct share_mode_entry, msg, + &fsp->pending_break_messages, + &fsp->num_pending_break_messages); + return; + } + + if (EXCLUSIVE_OPLOCK_TYPE(msg.op_type) && + !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(3, ("Already downgraded oplock on %s: %s\n", + file_id_string_tos(&fsp->file_id), + fsp->fsp_name)); + /* We just send the same message back. */ + messaging_send_buf(msg_ctx, src, MSG_SMB_BREAK_RESPONSE, + (uint8 *)data->data, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + return; + } + + if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) && + !(msg.op_type & FORCE_OPLOCK_BREAK_TO_NONE) && + !koplocks && /* NOTE: we force levelII off for kernel oplocks - + * this will change when it is supported */ + lp_level2_oplocks(SNUM(fsp->conn))) { + break_to_level2 = True; + } + + break_msg = new_break_smb_message(NULL, fsp, break_to_level2 ? + OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); + if (break_msg == NULL) { + exit_server("Could not talloc break_msg\n"); + } + + /* Need to wait before sending a break message if we sent ourselves this message. */ + if (procid_to_pid(&src) == sys_getpid()) { + wait_before_sending_break(); + } + + /* Save the server smb signing state. */ + sign_state = srv_oplock_set_signing(False); + + show_msg(break_msg); + if (!send_smb(smbd_server_fd(), break_msg)) { + exit_server_cleanly("oplock_break: send_smb failed."); + } + + /* Restore the sign state to what it was. */ + srv_oplock_set_signing(sign_state); + + TALLOC_FREE(break_msg); -static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval, BOOL local_request) + fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT; + + msg.pid = src; + ADD_TO_ARRAY(NULL, struct share_mode_entry, msg, + &fsp->pending_break_messages, + &fsp->num_pending_break_messages); + + add_oplock_timeout_handler(fsp); +} + +/******************************************************************* + This handles the kernel oplock break message. +*******************************************************************/ + +static void process_kernel_oplock_break(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) { - extern uint32 global_client_caps; - extern struct current_user current_user; - char *inbuf = NULL; - char *outbuf = NULL; - files_struct *fsp = NULL; - time_t start_time; - BOOL shutdown_server = False; - BOOL oplock_timeout = False; - connection_struct *saved_user_conn; - connection_struct *saved_fsp_conn; - int saved_vuid; - pstring saved_dir; - int timeout = (OPLOCK_BREAK_TIMEOUT * 1000); - pstring file_name; - BOOL using_levelII; - - if((fsp = initial_break_processing(dev, inode, tval)) == NULL) - return True; - - /* - * Deal with a level II oplock going break to none separately. - */ - - if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) - return oplock_break_level2(fsp, local_request, -1); - - /* Mark the oplock break as sent - we don't want to send twice! */ - if (fsp->sent_oplock_break) - { - if( DEBUGLVL( 0 ) ) - { - dbgtext( "oplock_break: ERROR: oplock_break already sent for " ); - dbgtext( "file %s ", fsp->fsp_name); - dbgtext( "(dev = %x, inode = %.0f)\n", (unsigned int)dev, (double)inode ); - } - - /* We have to fail the open here as we cannot send another oplock break on - this file whilst we are awaiting a response from the client - neither - can we allow another open to succeed while we are waiting for the - client. - */ - return False; - } - - if(global_oplock_break) { - DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n")); - abort(); - } - - /* Now comes the horrid part. We must send an oplock break to the client, - and then process incoming messages until we get a close or oplock release. - At this point we know we need a new inbuf/outbuf buffer pair. - We cannot use these staticaly as we may recurse into here due to - messages crossing on the wire. - */ - - if((inbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) - { - DEBUG(0,("oplock_break: malloc fail for input buffer.\n")); - return False; - } - - if((outbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) - { - DEBUG(0,("oplock_break: malloc fail for output buffer.\n")); - SAFE_FREE(inbuf); - return False; - } - - /* - * If we are sending an oplock break due to an SMB sent - * by our own client we ensure that we wait at leat - * lp_oplock_break_wait_time() milliseconds before sending - * the packet. Sending the packet sooner can break Win9x - * and has reported to cause problems on NT. JRA. - */ - - wait_before_sending_break(local_request); - - /* Prepare the SMBlockingX message. */ - - if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) && - !koplocks && /* NOTE: we force levelII off for kernel oplocks - - this will change when it is supported */ - lp_level2_oplocks(SNUM(fsp->conn))) { - using_levelII = True; - } else { - using_levelII = False; - } - - prepare_break_message( outbuf, fsp, using_levelII); - /* Remember if we just sent a break to level II on this file. */ - fsp->sent_oplock_break = using_levelII? - LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT; - - if (!send_smb(smbd_server_fd(), outbuf)) - exit_server("oplock_break: send_smb failed.\n"); - - /* We need this in case a readraw crosses on the wire. */ - global_oplock_break = True; - - /* Process incoming messages. */ - - /* JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT - seconds we should just die.... */ - - start_time = time(NULL); - - /* - * Save the information we need to re-become the - * user, then unbecome the user whilst we're doing this. - */ - saved_user_conn = current_user.conn; - saved_vuid = current_user.vuid; - saved_fsp_conn = fsp->conn; - vfs_GetWd(saved_fsp_conn,saved_dir); - unbecome_user(); - /* Save the chain fnum. */ - file_chain_save(); - - /* - * From Charles Hoch . If the break processing - * code closes the file (as it often does), then the fsp pointer here - * points to free()'d memory. We *must* revalidate fsp each time - * around the loop. - */ - - pstrcpy(file_name, fsp->fsp_name); - - while((fsp = initial_break_processing(dev, inode, tval)) && - OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) - { - if(receive_smb(smbd_server_fd(),inbuf, timeout) == False) - { - /* - * Die if we got an error. - */ - - if (smb_read_error == READ_EOF) { - DEBUG( 0, ( "oplock_break: end of file from client\n" ) ); - shutdown_server = True; - } else if (smb_read_error == READ_ERROR) { - DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) ); - shutdown_server = True; - } else if (smb_read_error == READ_TIMEOUT) { - DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", - OPLOCK_BREAK_TIMEOUT ) ); - oplock_timeout = True; - } - - DEBUGADD( 0, ( "oplock_break failed for file %s ", file_name ) ); - DEBUGADD( 0, ( "(dev = %x, inode = %.0f).\n", (unsigned int)dev, (double)inode)); - - break; - } - - /* - * There are certain SMB requests that we shouldn't allow - * to recurse. opens, renames and deletes are the obvious - * ones. This is handled in the switch_message() function. - * If global_oplock_break is set they will push the packet onto - * the pending smb queue and return -1 (no reply). - * JRA. - */ - - process_smb(inbuf, outbuf); - - /* - * Die if we go over the time limit. - */ - - if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) - { - if( DEBUGLVL( 0 ) ) - { - dbgtext( "oplock_break: no break received from client " ); - dbgtext( "within %d seconds.\n", OPLOCK_BREAK_TIMEOUT ); - dbgtext( "oplock_break failed for file %s ", fsp->fsp_name ); - dbgtext( "(dev = %x, inode = %.0f).\n", (unsigned int)dev, (double)inode ); - } - oplock_timeout = True; - break; - } - } - - /* - * Go back to being the user who requested the oplock - * break. - */ - if((saved_user_conn != NULL) && (saved_vuid != UID_FIELD_INVALID) && !become_user(saved_user_conn, saved_vuid)) - { - DEBUG( 0, ( "oplock_break: unable to re-become user!" ) ); - DEBUGADD( 0, ( "Shutting down server\n" ) ); - close(oplock_sock); - exit_server("unable to re-become user"); - } - /* Including the directory. */ - vfs_ChDir(saved_fsp_conn,saved_dir); - - /* Restore the chain fnum. */ - file_chain_restore(); - - /* Free the buffers we've been using to recurse. */ - SAFE_FREE(inbuf); - SAFE_FREE(outbuf); - - /* We need this in case a readraw crossed on the wire. */ - if(global_oplock_break) - global_oplock_break = False; - - /* - * If the client timed out then clear the oplock (or go to level II) - * and continue. This seems to be what NT does and is better than dropping - * the connection. - */ - - if(oplock_timeout && (fsp = initial_break_processing(dev, inode, tval)) && - 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,True); - global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */ - } - - /* - * If the client had an error we must die. - */ - - if(shutdown_server) - { - DEBUG( 0, ( "oplock_break: client failure in break - " ) ); - DEBUGADD( 0, ( "shutting down this smbd.\n" ) ); - close(oplock_sock); - exit_server("oplock break failure"); - } - - /* Santity check - remove this later. JRA */ - if(exclusive_oplocks_open < 0) - { - DEBUG(0,("oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR\n", - exclusive_oplocks_open)); - abort(); - } - - if( DEBUGLVL( 3 ) ) - { - dbgtext( "oplock_break: returning success for " ); - dbgtext( "dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); - dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open ); - } - - return True; + struct file_id id; + unsigned long file_id; + files_struct *fsp; + char *break_msg; + BOOL sign_state; + + if (data->data == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; + } + + if (data->length != MSG_SMB_KERNEL_BREAK_SIZE) { + DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); + return; + } + + /* Pull the data from the message. */ + pull_file_id_16((char *)data->data, &id); + file_id = (unsigned long)IVAL(data->data, 16); + + DEBUG(10, ("Got kernel oplock break message from pid %d: %s/%u\n", + (int)procid_to_pid(&src), file_id_string_tos(&id), + (unsigned int)file_id)); + + fsp = initial_break_processing(id, file_id); + + if (fsp == NULL) { + DEBUG(3, ("Got a kernel oplock break message for a file " + "I don't know about\n")); + return; + } + + if (fsp->sent_oplock_break != NO_BREAK_SENT) { + /* This is ok, kernel oplocks come in completely async */ + DEBUG(3, ("Got a kernel oplock request while waiting for a " + "break reply\n")); + return; + } + + break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE); + if (break_msg == NULL) { + exit_server("Could not talloc break_msg\n"); + } + + /* Save the server smb signing state. */ + sign_state = srv_oplock_set_signing(False); + + show_msg(break_msg); + if (!send_smb(smbd_server_fd(), break_msg)) { + exit_server_cleanly("oplock_break: send_smb failed."); + } + + /* Restore the sign state to what it was. */ + srv_oplock_set_signing(sign_state); + + TALLOC_FREE(break_msg); + + fsp->sent_oplock_break = BREAK_TO_NONE_SENT; + + add_oplock_timeout_handler(fsp); } -/**************************************************************************** -Send an oplock break message to another smbd process. If the oplock is held -by the local smbd then call the oplock break function directly. -****************************************************************************/ +void reply_to_oplock_break_requests(files_struct *fsp) +{ + int i; + + for (i=0; inum_pending_break_messages; i++) { + struct share_mode_entry *e = &fsp->pending_break_messages[i]; + char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; -BOOL request_oplock_break(share_mode_entry *share_entry, - SMB_DEV_T dev, SMB_INO_T inode) + share_mode_entry_to_message(msg, e); + + messaging_send_buf(smbd_messaging_context(), e->pid, + MSG_SMB_BREAK_RESPONSE, + (uint8 *)msg, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + } + + SAFE_FREE(fsp->pending_break_messages); + fsp->num_pending_break_messages = 0; + if (fsp->oplock_timeout != NULL) { + /* Remove the timed event handler. */ + TALLOC_FREE(fsp->oplock_timeout); + fsp->oplock_timeout = NULL; + } + return; +} + +static void process_oplock_break_response(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) { - char op_break_msg[OPLOCK_BREAK_MSG_LEN]; - struct sockaddr_in addr_out; - pid_t pid = sys_getpid(); - time_t start_time; - int time_left; - long usec; - time_t sec; - - if(pid == share_entry->pid) - { - /* We are breaking our own oplock, make sure it's us. */ - if(share_entry->op_port != global_oplock_port) - { - DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \ -should be %d\n", (int)pid, share_entry->op_port, global_oplock_port)); - return False; - } - - DEBUG(5,("request_oplock_break: breaking our own oplock\n")); - -#if 1 /* JRA PARANOIA TEST.... */ - { - files_struct *fsp = file_find_dit(dev, inode, &share_entry->time); - if (!fsp) { - DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \ -dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x and no fsp found !\n", - (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, - (int)share_entry->time.tv_usec )); - smb_panic("request_oplock_break: no fsp found for our own oplock\n"); - } - } -#endif /* END JRA PARANOIA TEST... */ - - /* Call oplock break direct. */ - return oplock_break(dev, inode, &share_entry->time, True); - } - - /* We need to send a OPLOCK_BREAK_CMD message to the - port in the share mode entry. */ - - if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { - SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,LEVEL_II_OPLOCK_BREAK_CMD); - } else { - SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD); - } - memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid)); - sec = (time_t)share_entry->time.tv_sec; - memcpy(op_break_msg+OPLOCK_BREAK_SEC_OFFSET,(char *)&sec,sizeof(sec)); - usec = (long)share_entry->time.tv_usec; - memcpy(op_break_msg+OPLOCK_BREAK_USEC_OFFSET,(char *)&usec,sizeof(usec)); - memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev)); - memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode)); - - /* set the address and port */ - memset((char *)&addr_out,'\0',sizeof(addr_out)); - addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr_out.sin_port = htons( share_entry->op_port ); - addr_out.sin_family = AF_INET; - - if( DEBUGLVL( 3 ) ) - { - dbgtext( "request_oplock_break: sending a oplock break message to " ); - dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", - (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, - (int)share_entry->time.tv_usec ); - - } - - if(sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0, - (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) - { - if( DEBUGLVL( 0 ) ) - { - dbgtext( "request_oplock_break: failed when sending a oplock " ); - dbgtext( "break message to pid %d ", (int)share_entry->pid ); - dbgtext( "on port %d ", share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", - (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, - (int)share_entry->time.tv_usec ); - dbgtext( "Error was %s\n", strerror(errno) ); - } - return False; - } - - /* - * If we just sent a message to a level II oplock share entry then - * we are done and may return. - */ - - if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { - DEBUG(3,("request_oplock_break: sent break message to level II entry.\n")); - return True; - } - - /* - * Now we must await the oplock broken message coming back - * from the target smbd process. Timeout if it fails to - * return in (OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) seconds. - * While we get messages that aren't ours, loop. - */ - - start_time = time(NULL); - time_left = OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR; - - while(time_left >= 0) - { - char op_break_reply[OPBRK_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN]; - uint16 reply_from_port; - char *reply_msg_start; - fd_set fds; - - FD_ZERO(&fds); - FD_SET(oplock_sock,&fds); - - if (koplocks && koplocks->notification_fd != -1) { - FD_SET(koplocks->notification_fd, &fds); - } - - if(receive_local_message(&fds, op_break_reply, sizeof(op_break_reply), - time_left ? time_left * 1000 : 1) == False) - { - if(smb_read_error == READ_TIMEOUT) - { - if( DEBUGLVL( 0 ) ) - { - dbgtext( "request_oplock_break: no response received to oplock " ); - dbgtext( "break request to pid %d ", (int)share_entry->pid ); - dbgtext( "on port %d ", share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); - dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", - (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, - (int)share_entry->time.tv_usec ); - } - - /* - * This is a hack to make handling of failing clients more robust. - * If a oplock break response message is not received in the timeout - * period we may assume that the smbd servicing that client holding - * the oplock has died and the client changes were lost anyway, so - * we should continue to try and open the file. - */ - break; - } - else - if( DEBUGLVL( 0 ) ) - { - dbgtext( "request_oplock_break: error in response received " ); - dbgtext( "to oplock break request to pid %d ", (int)share_entry->pid ); - dbgtext( "on port %d ", share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", - (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, - (int)share_entry->time.tv_usec ); - dbgtext( "Error was (%s).\n", strerror(errno) ); - } - return False; - } - - reply_from_port = SVAL(op_break_reply,OPBRK_CMD_PORT_OFFSET); - - reply_msg_start = &op_break_reply[OPBRK_CMD_HEADER_LEN]; - - - /* - * Test to see if this is the reply we are awaiting. - */ - if((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & CMD_REPLY) && - ((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & ~CMD_REPLY) == OPLOCK_BREAK_CMD) && - (reply_from_port == share_entry->op_port) && - (memcmp(&reply_msg_start[OPLOCK_BREAK_PID_OFFSET], - &op_break_msg[OPLOCK_BREAK_PID_OFFSET], - OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET) == 0)) - { - /* - * This is the reply we've been waiting for. - */ - break; - } - else - { - /* - * This is another message - a break request. - * Note that both kernel oplock break requests - * and UDP inter-smbd oplock break requests will - * be processed here. - * - * Process it to prevent potential deadlock. - * Note that the code in switch_message() prevents - * us from recursing into here as any SMB requests - * we might process that would cause another oplock - * break request to be made will be queued. - * JRA. - */ - - process_local_message(op_break_reply, sizeof(op_break_reply)); - } - - time_left -= (time(NULL) - start_time); - } - - DEBUG(3,("request_oplock_break: broke oplock.\n")); - - return True; + struct share_mode_entry msg; + + if (data->data == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; + } + + if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { + DEBUG(0, ("Got invalid msg len %u\n", + (unsigned int)data->length)); + return; + } + + /* De-linearize incoming message. */ + message_to_share_mode_entry(&msg, (char *)data->data); + + DEBUG(10, ("Got oplock break response from pid %d: %s/%lu mid %u\n", + (int)procid_to_pid(&src), file_id_string_tos(&msg.id), msg.share_file_id, + (unsigned int)msg.op_mid)); + + /* Here's the hack from open.c, store the mid in the 'port' field */ + schedule_deferred_open_smb_message(msg.op_mid); } -/**************************************************************************** - Attempt to break an oplock on a file (if oplocked). - Returns True if the file was closed as a result of - the oplock break, False otherwise. - Used as a last ditch attempt to free a space in the - file table when we have run out. -****************************************************************************/ -BOOL attempt_close_oplocked_file(files_struct *fsp) +static void process_open_retry_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) { + struct share_mode_entry msg; + + if (data->data == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; + } - DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name)); + if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { + DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); + return; + } + + /* De-linearize incoming message. */ + message_to_share_mode_entry(&msg, (char *)data->data); - if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fd != -1)) { - /* Try and break the oplock. */ - if (oplock_break(fsp->dev, fsp->inode, &fsp->open_time, True)) { - if(file_find_fsp(fsp) == NULL) /* Did the oplock break close the file ? */ - return True; - } - } + DEBUG(10, ("Got open retry msg from pid %d: %s mid %u\n", + (int)procid_to_pid(&src), file_id_string_tos(&msg.id), + (unsigned int)msg.op_mid)); - return False; + schedule_deferred_open_smb_message(msg.op_mid); } /**************************************************************************** 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. + 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; + struct share_mode_lock *lck; /* * If this file is level II oplocked then we need @@ -1150,111 +752,130 @@ void release_level_2_oplocks_on_change(files_struct *fsp) 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 )); + lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + if (lck == NULL) { + DEBUG(0,("release_level_2_oplocks_on_change: failed to lock " + "share mode entry for file %s.\n", fsp->fsp_name )); + return; } - num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list); - DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n", - num_share_modes )); + lck->num_share_modes )); + + for(i = 0; i < lck->num_share_modes; i++) { + struct share_mode_entry *share_entry = &lck->share_modes[i]; + char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &share_list[i]; + if (!is_valid_share_mode_entry(share_entry)) { + continue; + } /* - * 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. + * 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 or FAKE_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. */ - DEBUG(10,("release_level_2_oplocks_on_change: share_entry[%i]->op_type == %d\n", - i, share_entry->op_type )); + DEBUG(10,("release_level_2_oplocks_on_change: " + "share_entry[%i]->op_type == %d\n", + i, share_entry->op_type )); - if (share_entry->op_type == NO_OPLOCK) + 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); + DEBUG(0,("release_level_2_oplocks_on_change: PANIC. " + "share mode entry %d is an exlusive " + "oplock !\n", i )); + TALLOC_FREE(lck); 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); + share_mode_entry_to_message(msg, share_entry); - /* 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(); - } - - DEBUG(10,("release_level_2_oplocks_on_change: breaking our own oplock.\n")); + messaging_send_buf(smbd_messaging_context(), share_entry->pid, + MSG_SMB_ASYNC_LEVEL2_BREAK, + (uint8 *)msg, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + } - oplock_break_level2(new_fsp, True, token); + /* We let the message receivers handle removing the oplock state + in the share mode lock db. */ - } else { + TALLOC_FREE(lck); +} - /* - * This is a remote file and so we send an asynchronous - * message. - */ +/**************************************************************************** + Linearize a share mode entry struct to an internal oplock break message. +****************************************************************************/ - DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock.\n")); - request_oplock_break(share_entry, fsp->dev, fsp->inode); - } - } +void share_mode_entry_to_message(char *msg, struct share_mode_entry *e) +{ + SIVAL(msg,0,(uint32)e->pid.pid); + SSVAL(msg,4,e->op_mid); + SSVAL(msg,6,e->op_type); + SIVAL(msg,8,e->access_mask); + SIVAL(msg,12,e->share_access); + SIVAL(msg,16,e->private_options); + SIVAL(msg,20,(uint32)e->time.tv_sec); + SIVAL(msg,24,(uint32)e->time.tv_usec); + push_file_id_16(msg+28, &e->id); + SIVAL(msg,44,e->share_file_id); + SIVAL(msg,48,e->uid); + SSVAL(msg,52,e->flags); +#ifdef CLUSTER_SUPPORT + SIVAL(msg,54,e->pid.vnn); +#endif +} - SAFE_FREE(share_list); - unlock_share_entry_fsp(fsp); +/**************************************************************************** + De-linearize an internal oplock break message to a share mode entry struct. +****************************************************************************/ - /* 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)); - smb_panic("release_level_2_oplocks_on_change"); - } +void message_to_share_mode_entry(struct share_mode_entry *e, char *msg) +{ + e->pid.pid = (pid_t)IVAL(msg,0); + e->op_mid = SVAL(msg,4); + e->op_type = SVAL(msg,6); + e->access_mask = IVAL(msg,8); + e->share_access = IVAL(msg,12); + e->private_options = IVAL(msg,16); + e->time.tv_sec = (time_t)IVAL(msg,20); + e->time.tv_usec = (int)IVAL(msg,24); + pull_file_id_16(msg+28, &e->id); + e->share_file_id = (unsigned long)IVAL(msg,44); + e->uid = (uint32)IVAL(msg,48); + e->flags = (uint16)SVAL(msg,52); +#ifdef CLUSTER_SUPPORT + e->pid.vnn = IVAL(msg,54); +#endif } /**************************************************************************** -setup oplocks for this process + Setup oplocks for this process. ****************************************************************************/ -BOOL init_oplocks(void) -{ - struct sockaddr_in sock_name; - socklen_t len = sizeof(sock_name); - - DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n")); - - /* Open a lookback UDP socket on a random port. */ - oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK),False); - if (oplock_sock == -1) { - DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \ -address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno))); - global_oplock_port = 0; - return(False); - } - /* Find out the transient UDP port we have been allocated. */ - if(getsockname(oplock_sock, (struct sockaddr *)&sock_name, &len)<0) { - DEBUG(0,("open_oplock_ipc: Failed to get local UDP port. Error was %s\n", - strerror(errno))); - close(oplock_sock); - oplock_sock = -1; - global_oplock_port = 0; - return False; - } - global_oplock_port = ntohs(sock_name.sin_port); +BOOL init_oplocks(struct messaging_context *msg_ctx) +{ + DEBUG(3,("init_oplocks: initializing messages.\n")); + + messaging_register(msg_ctx, NULL, MSG_SMB_BREAK_REQUEST, + process_oplock_break_message); + messaging_register(msg_ctx, NULL, MSG_SMB_ASYNC_LEVEL2_BREAK, + process_oplock_async_level2_break_message); + messaging_register(msg_ctx, NULL, MSG_SMB_BREAK_RESPONSE, + process_oplock_break_response); + messaging_register(msg_ctx, NULL, MSG_SMB_KERNEL_BREAK, + process_kernel_oplock_break); + messaging_register(msg_ctx, NULL, MSG_SMB_OPEN_RETRY, + process_open_retry_message); if (lp_kernel_oplocks()) { #if HAVE_KERNEL_OPLOCKS_IRIX @@ -1264,8 +885,5 @@ address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno))); #endif } - DEBUG(3,("open_oplock ipc: pid = %d, global_oplock_port = %u\n", - (int)sys_getpid(), global_oplock_port)); - return True; }