X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Foplock.c;h=e43ed6b9a6adb8fcd993cd40cfc04cb1762d2a00;hb=8c24ebf371d8aff4742a2ba063a8789e20eaaabc;hp=e552cb545e150bdb7d382d462e4efa41d321ab25;hpb=5e54558c6dea67b56bbfaba5698f3a434d3dffb6;p=amitay%2Fsamba.git diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index e552cb545e1..e43ed6b9a6a 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -21,16 +21,9 @@ #define DBGC_CLASS DBGC_LOCKING #include "includes.h" - -/* 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; - -extern uint32 global_client_caps; -extern int smb_read_error; - -static struct kernel_oplocks *koplocks; +#include "smbd/smbd.h" +#include "smbd/globals.h" +#include "librpc/gen_ndr/messaging.h" /**************************************************************************** Get the number of current exclusive oplocks. @@ -41,71 +34,46 @@ int32 get_number_of_exclusive_open_oplocks(void) return exclusive_oplocks_open; } -/**************************************************************************** - Return True if an oplock message is pending. -****************************************************************************/ - -BOOL oplock_message_waiting(fd_set *fds) +/* + * helper function used by the kernel oplock backends to post the break message + */ +void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp) { - if (koplocks && koplocks->msg_waiting(fds)) { - return True; - } + uint8_t msg[MSG_SMB_KERNEL_BREAK_SIZE]; + + /* Put the kernel break info into the message. */ + push_file_id_24((char *)msg, &fsp->file_id); + SIVAL(msg,24,fsp->fh->gen_id); + + /* Don't need to be root here as we're only ever + sending to ourselves. */ - return False; + messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx), + MSG_SMB_KERNEL_BREAK, + msg, MSG_SMB_KERNEL_BREAK_SIZE); } /**************************************************************************** - 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. + Attempt to set an oplock on a file. Succeeds if kernel oplocks are + disabled (just sets flags) and no byte-range locks in the file. Returns True + if oplock set. ****************************************************************************/ -void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds) +bool set_file_oplock(files_struct *fsp, int oplock_type) { - /* - * 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; + if (fsp->oplock_type == LEVEL_II_OPLOCK) { + if (koplocks && + !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) { + DEBUG(10, ("Refusing level2 oplock, kernel oplocks " + "don't support them\n")); + return false; } - - /* 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 ((fsp->oplock_type != NO_OPLOCK) && + (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) && + koplocks && + !koplocks->ops->set_oplock(koplocks, fsp, oplock_type)) { return False; } @@ -113,13 +81,13 @@ BOOL set_file_oplock(files_struct *fsp, int oplock_type) fsp->sent_oplock_break = NO_BREAK_SENT; if (oplock_type == LEVEL_II_OPLOCK) { level_II_oplocks_open++; - } else { + } else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { exclusive_oplocks_open++; } DEBUG(5,("set_file_oplock: granted oplock on file %s, %s/%lu, " "tv_sec = %x, tv_usec = %x\n", - fsp->fsp_name, file_id_static_string(&fsp->file_id), + fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec )); @@ -135,7 +103,7 @@ void release_file_oplock(files_struct *fsp) if ((fsp->oplock_type != NO_OPLOCK) && (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) && koplocks) { - koplocks->release_oplock(fsp); + koplocks->ops->release_oplock(koplocks, fsp, NO_OPLOCK); } if (fsp->oplock_type == LEVEL_II_OPLOCK) { @@ -146,11 +114,17 @@ void release_file_oplock(files_struct *fsp) SMB_ASSERT(exclusive_oplocks_open>=0); SMB_ASSERT(level_II_oplocks_open>=0); - - fsp->oplock_type = NO_OPLOCK; + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + /* This doesn't matter for close. */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } else { + fsp->oplock_type = NO_OPLOCK; + } fsp->sent_oplock_break = NO_BREAK_SENT; - + flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH); + delete_write_cache(fsp); TALLOC_FREE(fsp->oplock_timeout); } @@ -161,8 +135,13 @@ void release_file_oplock(files_struct *fsp) static void downgrade_file_oplock(files_struct *fsp) { + if (!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0, ("trying to downgrade an already-downgraded oplock!\n")); + return; + } + if (koplocks) { - koplocks->release_oplock(fsp); + koplocks->ops->release_oplock(koplocks, fsp, LEVEL_II_OPLOCK); } fsp->oplock_type = LEVEL_II_OPLOCK; exclusive_oplocks_open--; @@ -176,23 +155,25 @@ 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 remove_oplock(files_struct *fsp) { - BOOL ret; + bool ret; struct share_mode_lock *lck; /* Remove the oplock flag from the sharemode. */ - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, + NULL); if (lck == NULL) { DEBUG(0,("remove_oplock: failed to lock share entry for " - "file %s\n", fsp->fsp_name )); + "file %s\n", fsp_str_dbg(fsp))); 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_static_string(&fsp->file_id))); + fsp_str_dbg(fsp), fsp->fnum, + file_id_string_tos(&fsp->file_id))); } release_file_oplock(fsp); TALLOC_FREE(lck); @@ -202,22 +183,24 @@ BOOL remove_oplock(files_struct *fsp) /* * Deal with a reply when a break-to-level II was sent. */ -BOOL downgrade_oplock(files_struct *fsp) +bool downgrade_oplock(files_struct *fsp) { - BOOL ret; + bool ret; struct share_mode_lock *lck; - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, + NULL); if (lck == NULL) { DEBUG(0,("downgrade_oplock: failed to lock share entry for " - "file %s\n", fsp->fsp_name )); + "file %s\n", fsp_str_dbg(fsp))); 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_static_string(&fsp->file_id))); + fsp_str_dbg(fsp), fsp->fnum, + file_id_string_tos(&fsp->file_id))); } downgrade_file_oplock(fsp); @@ -225,25 +208,21 @@ BOOL downgrade_oplock(files_struct *fsp) return ret; } -/**************************************************************************** - Return the fd (if any) used for receiving oplock notifications. -****************************************************************************/ - -int oplock_notify_fd(void) +/* + * Some kernel oplock implementations handle the notification themselves. + */ +bool should_notify_deferred_opens() { - if (koplocks) { - return koplocks->notification_fd; - } - - return -1; + return !(koplocks && + (koplocks->flags & KOPLOCKS_DEFERRED_OPEN_NOTIFICATION)); } /**************************************************************************** Set up an oplock break message. ****************************************************************************/ -static char *new_break_smb_message(TALLOC_CTX *mem_ctx, - files_struct *fsp, uint8 cmd) +static char *new_break_message_smb1(TALLOC_CTX *mem_ctx, + files_struct *fsp, int cmd) { char *result = TALLOC_ARRAY(mem_ctx, char, smb_size + 8*2 + 0); @@ -253,7 +232,7 @@ static char *new_break_smb_message(TALLOC_CTX *mem_ctx, } memset(result,'\0',smb_size); - set_message(NULL,result,8,0,True); + srv_set_message(result,8,0,true); SCVAL(result,smb_com,SMBlockingX); SSVAL(result,smb_tid,fsp->conn->cnum); SSVAL(result,smb_pid,0xFFFF); @@ -283,13 +262,15 @@ static void wait_before_sending_break(void) Ensure that we have a valid oplock. ****************************************************************************/ -static files_struct *initial_break_processing(struct file_id id, unsigned long file_id) +static files_struct *initial_break_processing( + struct smbd_server_connection *sconn, struct file_id id, + unsigned long file_id) { files_struct *fsp = NULL; if( DEBUGLVL( 3 ) ) { dbgtext( "initial_break_processing: called for %s/%u\n", - file_id_static_string(&id), (int)file_id); + file_id_string_tos(&id), (int)file_id); dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n", exclusive_oplocks_open, level_II_oplocks_open ); } @@ -300,13 +281,13 @@ static files_struct *initial_break_processing(struct file_id id, unsigned long f * we have an oplock on it. */ - fsp = file_find_dif(id, file_id); + fsp = file_find_dif(sconn, id, file_id); 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_static_string(&id), file_id); + dbgtext( "file_id %s gen_id = %lu", file_id_string_tos(&id), file_id); dbgtext( "allowing break to succeed.\n" ); } return NULL; @@ -324,9 +305,10 @@ static files_struct *initial_break_processing(struct file_id id, unsigned long f if(fsp->oplock_type == NO_OPLOCK) { if( DEBUGLVL( 3 ) ) { - dbgtext( "initial_break_processing: file %s ", fsp->fsp_name ); + dbgtext( "initial_break_processing: file %s ", + fsp_str_dbg(fsp)); dbgtext( "(file_id = %s gen_id = %lu) has no oplock.\n", - file_id_static_string(&id), fsp->fh->gen_id ); + file_id_string_tos(&id), fsp->fh->gen_id ); dbgtext( "Allowing break to succeed regardless.\n" ); } return NULL; @@ -337,15 +319,15 @@ static files_struct *initial_break_processing(struct file_id id, unsigned long f static void oplock_timeout_handler(struct event_context *ctx, struct timed_event *te, - const struct timeval *now, + struct timeval now, void *private_data) { 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; + DEBUG(0, ("Oplock break failed for file %s -- replying anyway\n", + fsp_str_dbg(fsp))); remove_oplock(fsp); reply_to_oplock_break_requests(fsp); } @@ -356,15 +338,23 @@ static void oplock_timeout_handler(struct event_context *ctx, static void add_oplock_timeout_handler(files_struct *fsp) { + /* + * If kernel oplocks already notifies smbds when an oplock break times + * out, just return. + */ + if (koplocks && + (koplocks->flags & KOPLOCKS_TIMEOUT_NOTIFICATION)) { + return; + } + 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, + event_add_timed(smbd_event_context(), fsp, timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0), - "oplock_timeout_handler", oplock_timeout_handler, fsp); if (fsp->oplock_timeout == NULL) { @@ -372,6 +362,65 @@ static void add_oplock_timeout_handler(files_struct *fsp) } } +static void send_break_message_smb1(files_struct *fsp, int level) +{ + char *break_msg = new_break_message_smb1(talloc_tos(), + fsp, + level); + if (break_msg == NULL) { + exit_server("Could not talloc break_msg\n"); + } + + show_msg(break_msg); + if (!srv_send_smb(fsp->conn->sconn, + break_msg, false, 0, + IS_CONN_ENCRYPTED(fsp->conn), + NULL)) { + exit_server_cleanly("send_break_message_smb1: " + "srv_send_smb failed."); + } + + TALLOC_FREE(break_msg); +} + +void break_level2_to_none_async(files_struct *fsp) +{ + struct smbd_server_connection *sconn = fsp->conn->sconn; + + 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); + + DEBUG(10,("process_oplock_async_level2_break_message: sending break " + "to none message for fid %d, file %s\n", fsp->fnum, + fsp_str_dbg(fsp))); + + /* Now send a break to none message to our client. */ + if (sconn->using_smb2) { + send_break_message_smb2(fsp, OPLOCKLEVEL_NONE); + } else { + send_break_message_smb1(fsp, OPLOCKLEVEL_NONE); + } + + /* Async level2 request, don't send a reply, just remove the oplock. */ + remove_oplock(fsp); +} + /******************************************************************* This handles the case of a write triggering a break to none message on a level2 oplock. @@ -380,22 +429,27 @@ static void add_oplock_timeout_handler(files_struct *fsp) the client for LEVEL2. *******************************************************************/ -static void process_oplock_async_level2_break_message(struct messaging_context *msg_ctx, +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 smbd_server_connection *sconn; 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; } + sconn = msg_ctx_to_sconn(msg_ctx); + if (sconn == NULL) { + DEBUG(1, ("could not find sconn\n")); + return; + } + if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); return; @@ -404,10 +458,11 @@ static void process_oplock_async_level2_break_message(struct messaging_context * /* 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_static_string(&msg.id), msg.share_file_id)); + DEBUG(10, ("Got oplock async level 2 break message from pid %s: " + "%s/%lu\n", procid_str(talloc_tos(), &src), + file_id_string_tos(&msg.id), msg.share_file_id)); - fsp = initial_break_processing(msg.id, msg.share_file_id); + fsp = initial_break_processing(sconn, msg.id, msg.share_file_id); if (fsp == NULL) { /* We hit a race here. Break messages are sent, and before we @@ -417,50 +472,7 @@ static void process_oplock_async_level2_break_message(struct messaging_context * 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); + break_level2_to_none_async(fsp); } /******************************************************************* @@ -473,17 +485,22 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, struct server_id src, DATA_BLOB *data) { + struct smbd_server_connection *sconn; struct share_mode_entry msg; files_struct *fsp; - char *break_msg; - BOOL break_to_level2 = False; - BOOL sign_state; + bool break_to_level2 = False; if (data->data == NULL) { DEBUG(0, ("Got NULL buffer\n")); return; } + sconn = msg_ctx_to_sconn(msg_ctx); + if (sconn == NULL) { + DEBUG(1, ("could not find sconn\n")); + return; + } + if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); return; @@ -492,13 +509,14 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, /* 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_static_string(&msg.id), msg.share_file_id)); + DEBUG(10, ("Got oplock break message from pid %s: %s/%lu\n", + procid_str(talloc_tos(), &src), file_id_string_tos(&msg.id), + msg.share_file_id)); - fsp = initial_break_processing(msg.id, msg.share_file_id); + fsp = initial_break_processing(sconn, msg.id, msg.share_file_id); if (fsp == NULL) { - /* a We hit race here. Break messages are sent, and before we + /* We hit a 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")); @@ -523,8 +541,8 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, if (EXCLUSIVE_OPLOCK_TYPE(msg.op_type) && !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { DEBUG(3, ("Already downgraded oplock on %s: %s\n", - file_id_static_string(&fsp->file_id), - fsp->fsp_name)); + file_id_string_tos(&fsp->file_id), + fsp_str_dbg(fsp))); /* We just send the same message back. */ messaging_send_buf(msg_ctx, src, MSG_SMB_BREAK_RESPONSE, (uint8 *)data->data, @@ -534,36 +552,25 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, 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 */ + !(koplocks && !(koplocks->flags & KOPLOCKS_LEVEL2_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()) { + /* Need to wait before sending a break + message if we sent ourselves this message. */ + if (procid_is_me(&src)) { 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."); + if (sconn->using_smb2) { + send_break_message_smb2(fsp, break_to_level2 ? + OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); + } else { + send_break_message_smb1(fsp, break_to_level2 ? + OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); } - /* Restore the sign state to what it was. */ - srv_oplock_set_signing(sign_state); - - TALLOC_FREE(break_msg); - fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT; msg.pid = src; @@ -584,11 +591,10 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, struct server_id src, DATA_BLOB *data) { + struct smbd_server_connection *sconn; 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")); @@ -600,15 +606,21 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, return; } + sconn = msg_ctx_to_sconn(msg_ctx); + if (sconn == NULL) { + DEBUG(1, ("could not find sconn\n")); + return; + } + /* Pull the data from the message. */ - pull_file_id_16((char *)data->data, &id); - file_id = (unsigned long)IVAL(data->data, 16); + pull_file_id_24((char *)data->data, &id); + file_id = (unsigned long)IVAL(data->data, 24); - DEBUG(10, ("Got kernel oplock break message from pid %d: %s/%u\n", - (int)procid_to_pid(&src), file_id_static_string(&id), + DEBUG(10, ("Got kernel oplock break message from pid %s: %s/%u\n", + procid_str(talloc_tos(), &src), file_id_string_tos(&id), (unsigned int)file_id)); - fsp = initial_break_processing(id, file_id); + fsp = initial_break_processing(sconn, id, file_id); if (fsp == NULL) { DEBUG(3, ("Got a kernel oplock break message for a file " @@ -623,24 +635,12 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, 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."); + if (sconn->using_smb2) { + send_break_message_smb2(fsp, OPLOCKLEVEL_NONE); + } else { + send_break_message_smb1(fsp, OPLOCKLEVEL_NONE); } - /* 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); @@ -650,13 +650,22 @@ void reply_to_oplock_break_requests(files_struct *fsp) { int i; + /* + * If kernel oplocks already notifies smbds when oplocks are + * broken/removed, just return. + */ + if (koplocks && + (koplocks->flags & KOPLOCKS_OPLOCK_BROKEN_NOTIFICATION)) { + return; + } + 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]; share_mode_entry_to_message(msg, e); - messaging_send_buf(smbd_messaging_context(), e->pid, + messaging_send_buf(fsp->conn->sconn->msg_ctx, e->pid, MSG_SMB_BREAK_RESPONSE, (uint8 *)msg, MSG_SMB_SHARE_MODE_ENTRY_SIZE); @@ -694,12 +703,11 @@ static void process_oplock_break_response(struct messaging_context *msg_ctx, /* 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_static_string(&msg.id), msg.share_file_id, - (unsigned int)msg.op_mid)); + DEBUG(10, ("Got oplock break response from pid %s: %s/%lu mid %llu\n", + procid_str(talloc_tos(), &src), file_id_string_tos(&msg.id), + msg.share_file_id, (unsigned long long)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); + schedule_deferred_open_message_smb(msg.op_mid); } static void process_open_retry_message(struct messaging_context *msg_ctx, @@ -723,11 +731,11 @@ static void process_open_retry_message(struct messaging_context *msg_ctx, /* De-linearize incoming message. */ message_to_share_mode_entry(&msg, (char *)data->data); - DEBUG(10, ("Got open retry msg from pid %d: %s mid %u\n", - (int)procid_to_pid(&src), file_id_static_string(&msg.id), - (unsigned int)msg.op_mid)); + DEBUG(10, ("Got open retry msg from pid %s: %s mid %llu\n", + procid_str(talloc_tos(), &src), file_id_string_tos(&msg.id), + (unsigned long long)msg.op_mid)); - schedule_deferred_open_smb_message(msg.op_mid); + schedule_deferred_open_message_smb(msg.op_mid); } /**************************************************************************** @@ -736,7 +744,8 @@ static void process_open_retry_message(struct messaging_context *msg_ctx, none. ****************************************************************************/ -void release_level_2_oplocks_on_change(files_struct *fsp) +static void contend_level2_oplocks_begin_default(files_struct *fsp, + enum level2_contention_type type) { int i; struct share_mode_lock *lck; @@ -752,10 +761,11 @@ void release_level_2_oplocks_on_change(files_struct *fsp) if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) return; - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, 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 )); + "share mode entry for file %s.\n", fsp_str_dbg(fsp))); return; } @@ -800,10 +810,37 @@ void release_level_2_oplocks_on_change(files_struct *fsp) share_mode_entry_to_message(msg, share_entry); - messaging_send_buf(smbd_messaging_context(), share_entry->pid, - MSG_SMB_ASYNC_LEVEL2_BREAK, - (uint8 *)msg, - MSG_SMB_SHARE_MODE_ENTRY_SIZE); + /* + * Deal with a race condition when breaking level2 + * oplocks. Don't send all the messages and release + * the lock, this allows someone else to come in and + * get a level2 lock before any of the messages are + * processed, and thus miss getting a break message. + * Ensure at least one entry (the one we're breaking) + * is processed immediately under the lock and becomes + * set as NO_OPLOCK to stop any waiter getting a level2. + * Bugid #5980. + */ + + if (procid_is_me(&share_entry->pid)) { + struct files_struct *cur_fsp = + initial_break_processing(fsp->conn->sconn, + share_entry->id, + share_entry->share_file_id); + wait_before_sending_break(); + if (cur_fsp != NULL) { + break_level2_to_none_async(cur_fsp); + } else { + DEBUG(3, ("release_level_2_oplocks_on_change: " + "Did not find fsp, ignoring\n")); + } + } else { + messaging_send_buf(fsp->conn->sconn->msg_ctx, + share_entry->pid, + MSG_SMB_ASYNC_LEVEL2_BREAK, + (uint8 *)msg, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + } } /* We let the message receivers handle removing the oplock state @@ -812,27 +849,46 @@ void release_level_2_oplocks_on_change(files_struct *fsp) TALLOC_FREE(lck); } +void contend_level2_oplocks_begin(files_struct *fsp, + enum level2_contention_type type) +{ + if (koplocks && koplocks->ops->contend_level2_oplocks_begin) { + koplocks->ops->contend_level2_oplocks_begin(fsp, type); + return; + } + + contend_level2_oplocks_begin_default(fsp, type); +} + +void contend_level2_oplocks_end(files_struct *fsp, + enum level2_contention_type type) +{ + /* Only kernel oplocks implement this so far */ + if (koplocks && koplocks->ops->contend_level2_oplocks_end) { + koplocks->ops->contend_level2_oplocks_end(fsp, type); + } +} + /**************************************************************************** Linearize a share mode entry struct to an internal oplock break message. ****************************************************************************/ -void share_mode_entry_to_message(char *msg, struct share_mode_entry *e) +void share_mode_entry_to_message(char *msg, const 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 + SIVAL(msg,OP_BREAK_MSG_PID_OFFSET,(uint32)e->pid.pid); + SBVAL(msg,OP_BREAK_MSG_MID_OFFSET,e->op_mid); + SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET,e->op_type); + SIVAL(msg,OP_BREAK_MSG_ACCESS_MASK_OFFSET,e->access_mask); + SIVAL(msg,OP_BREAK_MSG_SHARE_ACCESS_OFFSET,e->share_access); + SIVAL(msg,OP_BREAK_MSG_PRIV_OFFSET,e->private_options); + SIVAL(msg,OP_BREAK_MSG_TIME_SEC_OFFSET,(uint32_t)e->time.tv_sec); + SIVAL(msg,OP_BREAK_MSG_TIME_USEC_OFFSET,(uint32_t)e->time.tv_usec); + push_file_id_24(msg+OP_BREAK_MSG_DEV_OFFSET, &e->id); + SIVAL(msg,OP_BREAK_MSG_FILE_ID_OFFSET,e->share_file_id); + SIVAL(msg,OP_BREAK_MSG_UID_OFFSET,e->uid); + SSVAL(msg,OP_BREAK_MSG_FLAGS_OFFSET,e->flags); + SIVAL(msg,OP_BREAK_MSG_NAME_HASH_OFFSET,e->name_hash); + SIVAL(msg,OP_BREAK_MSG_VNN_OFFSET,e->pid.vnn); } /**************************************************************************** @@ -841,28 +897,27 @@ void share_mode_entry_to_message(char *msg, struct share_mode_entry *e) 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 + e->pid.pid = (pid_t)IVAL(msg,OP_BREAK_MSG_PID_OFFSET); + e->op_mid = BVAL(msg,OP_BREAK_MSG_MID_OFFSET); + e->op_type = SVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET); + e->access_mask = IVAL(msg,OP_BREAK_MSG_ACCESS_MASK_OFFSET); + e->share_access = IVAL(msg,OP_BREAK_MSG_SHARE_ACCESS_OFFSET); + e->private_options = IVAL(msg,OP_BREAK_MSG_PRIV_OFFSET); + e->time.tv_sec = (time_t)IVAL(msg,OP_BREAK_MSG_TIME_SEC_OFFSET); + e->time.tv_usec = (int)IVAL(msg,OP_BREAK_MSG_TIME_USEC_OFFSET); + pull_file_id_24(msg+OP_BREAK_MSG_DEV_OFFSET, &e->id); + e->share_file_id = (unsigned long)IVAL(msg,OP_BREAK_MSG_FILE_ID_OFFSET); + e->uid = (uint32)IVAL(msg,OP_BREAK_MSG_UID_OFFSET); + e->flags = (uint16)SVAL(msg,OP_BREAK_MSG_FLAGS_OFFSET); + e->name_hash = IVAL(msg,OP_BREAK_MSG_NAME_HASH_OFFSET); + e->pid.vnn = IVAL(msg,OP_BREAK_MSG_VNN_OFFSET); } /**************************************************************************** Setup oplocks for this process. ****************************************************************************/ -BOOL init_oplocks(struct messaging_context *msg_ctx) +bool init_oplocks(struct messaging_context *msg_ctx) { DEBUG(3,("init_oplocks: initializing messages.\n")); @@ -879,9 +934,12 @@ BOOL init_oplocks(struct messaging_context *msg_ctx) if (lp_kernel_oplocks()) { #if HAVE_KERNEL_OPLOCKS_IRIX - koplocks = irix_init_kernel_oplocks(); + koplocks = irix_init_kernel_oplocks(NULL); #elif HAVE_KERNEL_OPLOCKS_LINUX - koplocks = linux_init_kernel_oplocks(); + koplocks = linux_init_kernel_oplocks(NULL); +#elif HAVE_ONEFS +#error Isilon, please check if the NULL context is okay here. Thanks! + koplocks = onefs_init_kernel_oplocks(NULL); #endif }