oplock.c
来自「samba-3.0.22.tar.gz 编译smb服务器的源码」· C语言 代码 · 共 894 行 · 第 1/2 页
C
894 行
/* 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 (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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.*/#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 struct timeval smb_last_time;extern uint32 global_client_caps;extern int smb_read_error;static struct kernel_oplocks *koplocks;/**************************************************************************** Get the number of current exclusive oplocks.****************************************************************************/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){ if (koplocks && koplocks->msg_waiting(fds)) { return True; } return False;}/**************************************************************************** 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.****************************************************************************/void process_kernel_oplocks(fd_set *pfds){ /* * 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. */ SDEV_T_VAL(msg,0,fsp->dev); SINO_T_VAL(msg,8,fsp->inode); SIVAL(msg,16,fsp->file_id); /* Don't need to be root here as we're only ever sending to ourselves. */ message_send_pid(pid_to_procid(sys_getpid()), MSG_SMB_KERNEL_BREAK, &msg, MSG_SMB_KERNEL_BREAK_SIZE, True); }}/**************************************************************************** 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)) { return False; } fsp->oplock_type = oplock_type; fsp->sent_oplock_break = NO_BREAK_SENT; if (oplock_type == LEVEL_II_OPLOCK) { level_II_oplocks_open++; } else { exclusive_oplocks_open++; } DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, file_id = %lu, \tv_sec = %x, tv_usec = %x\n", fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id, (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec )); return True;}/**************************************************************************** Attempt to release an oplock on a file. Decrements oplock count.****************************************************************************/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); } if (fsp->oplock_type == LEVEL_II_OPLOCK) { level_II_oplocks_open--; } 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);}/**************************************************************************** Attempt to downgrade an oplock on a file. Doesn't decrement oplock count.****************************************************************************/static void downgrade_file_oplock(files_struct *fsp){ if (koplocks) { koplocks->release_oplock(fsp); } fsp->oplock_type = LEVEL_II_OPLOCK; exclusive_oplocks_open--; level_II_oplocks_open++; fsp->sent_oplock_break = NO_BREAK_SENT;}/**************************************************************************** Remove a file oplock. Copes with level II and exclusive. Locks then unlocks the share mode lock. Client can decide to go directly to none even if a "break-to-level II" was sent.****************************************************************************/BOOL remove_oplock(files_struct *fsp){ SMB_DEV_T dev = fsp->dev; SMB_INO_T inode = fsp->inode; BOOL ret; struct share_mode_lock *lck; /* Remove the oplock flag from the sharemode. */ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, 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, dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); } release_file_oplock(fsp); talloc_free(lck); return ret;}/* * Deal with a reply when a break-to-level II was sent. */BOOL downgrade_oplock(files_struct *fsp){ SMB_DEV_T dev = fsp->dev; SMB_INO_T inode = fsp->inode; BOOL ret; struct share_mode_lock *lck; lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, 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, dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); } 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.****************************************************************************/int setup_oplock_select_set( fd_set *fds){ int maxfd = 0; if (koplocks && koplocks->notification_fd != -1) { FD_SET(koplocks->notification_fd, fds); maxfd = MAX(maxfd, koplocks->notification_fd); } return maxfd;}/**************************************************************************** Set up an oplock break message.****************************************************************************/static char *new_break_smb_message(TALLOC_CTX *mem_ctx, files_struct *fsp, uint8 cmd){ 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;}/**************************************************************************** Function to do the waiting before sending a local break.****************************************************************************/static void wait_before_sending_break(void){ struct timeval cur_tv; long wait_left = (long)lp_oplock_break_wait_time(); if (wait_left == 0) { return; } GetTimeOfDay(&cur_tv); wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) + ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000); if(wait_left > 0) { wait_left = MIN(wait_left, 1000); sys_usleep(wait_left * 1000); }}/**************************************************************************** Ensure that we have a valid oplock.****************************************************************************/static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id){ files_struct *fsp = NULL; if( DEBUGLVL( 3 ) ) { dbgtext( "initial_break_processing: called for dev = 0x%x, inode = %.0f file_id = %lu\n", (unsigned int)dev, (double)inode, file_id); 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_dif(dev, inode, 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( "dev = 0x%x, inode = %.0f file_id = %lu", (unsigned int)dev, (double)inode, file_id); 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, file_id = %lu) has no oplock.\n", (unsigned int)dev, (double)inode, fsp->file_id ); dbgtext( "Allowing break to succeed regardless.\n" ); } return NULL; } return fsp;}static void oplock_timeout_handler(struct timed_event *te, const struct timeval *now, void *private_data){ files_struct *fsp = private_data; 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);}/******************************************************************* Add a timeout handler waiting for the client reply.*******************************************************************/static void add_oplock_timeout_handler(files_struct *fsp){ if (fsp->oplock_timeout != NULL) { DEBUG(0, ("Logic problem -- have an oplock event hanging " "around\n")); } fsp->oplock_timeout = add_timed_event(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")); }}/******************************************************************* 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(int msg_type, struct process_id src, void *buf, size_t len){ struct share_mode_entry msg; files_struct *fsp; char *break_msg; BOOL sign_state; if (buf == NULL) { DEBUG(0, ("Got NULL buffer\n")); return; } if (len != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { DEBUG(0, ("Got invalid msg len %d\n", (int)len)); return; } /* De-linearize incoming message. */ message_to_share_mode_entry(&msg, buf); DEBUG(10, ("Got oplock async level 2 break message from pid %d: 0x%x/%.0f/%d\n", (int)procid_to_pid(&src), (unsigned int)msg.dev, (double)msg.inode, (int)msg.share_file_id)); fsp = initial_break_processing(msg.dev, msg.inode, 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"));
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?