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 + -
显示快捷键?