diff options
author | Gerald Carter <jerry@samba.org> | 2005-09-30 17:13:37 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:04:48 -0500 |
commit | 54abd2aa66069e6baf7769c496f46d9dba18db39 (patch) | |
tree | 9cf8e88168011797319ba9e9866749201b1eac1e /source3/smbd | |
parent | 4a2cc231d22a82ed21771a72508f15d21ed63227 (diff) | |
download | samba-54abd2aa66069e6baf7769c496f46d9dba18db39.tar.gz samba-54abd2aa66069e6baf7769c496f46d9dba18db39.tar.bz2 samba-54abd2aa66069e6baf7769c496f46d9dba18db39.zip |
r10656: BIG merge from trunk. Features not copied over
* \PIPE\unixinfo
* winbindd's {group,alias}membership new functions
* winbindd's lookupsids() functionality
* swat (trunk changes to be reverted as per discussion with Deryck)
(This used to be commit 939c3cb5d78e3a2236209b296aa8aba8bdce32d3)
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/blocking.c | 24 | ||||
-rw-r--r-- | source3/smbd/change_trust_pw.c | 17 | ||||
-rw-r--r-- | source3/smbd/chgpasswd.c | 4 | ||||
-rw-r--r-- | source3/smbd/close.c | 87 | ||||
-rw-r--r-- | source3/smbd/conn.c | 2 | ||||
-rw-r--r-- | source3/smbd/connection.c | 8 | ||||
-rw-r--r-- | source3/smbd/error.c | 23 | ||||
-rw-r--r-- | source3/smbd/files.c | 33 | ||||
-rw-r--r-- | source3/smbd/mangle_hash2.c | 4 | ||||
-rw-r--r-- | source3/smbd/message.c | 11 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 29 | ||||
-rw-r--r-- | source3/smbd/open.c | 879 | ||||
-rw-r--r-- | source3/smbd/oplock.c | 1331 | ||||
-rw-r--r-- | source3/smbd/oplock_irix.c | 51 | ||||
-rw-r--r-- | source3/smbd/oplock_linux.c | 56 | ||||
-rw-r--r-- | source3/smbd/process.c | 457 | ||||
-rw-r--r-- | source3/smbd/reply.c | 133 | ||||
-rw-r--r-- | source3/smbd/server.c | 11 | ||||
-rw-r--r-- | source3/smbd/sesssetup.c | 70 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 132 |
20 files changed, 1243 insertions, 2119 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 72d021d4e6..805e45f6ea 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -80,7 +80,8 @@ static BOOL in_chained_smb(void) return (chain_size != 0); } -static void received_unlock_msg(int msg_type, pid_t src, void *buf, size_t len); +static void received_unlock_msg(int msg_type, struct process_id src, + void *buf, size_t len); /**************************************************************************** Function to push a blocking lock request onto the lock queue. @@ -127,7 +128,7 @@ BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, /* Add a pending lock record for this. */ status = brl_lock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - lock_pid, sys_getpid(), blr->fsp->conn->cnum, + lock_pid, procid_self(), blr->fsp->conn->cnum, offset, count, PENDING_LOCK, &my_lock_ctx); if (!NT_STATUS_IS_OK(status)) { @@ -351,8 +352,8 @@ static BOOL process_lockread(blocking_lock_record *blr) SSVAL(p,0,nread); p += 2; set_message_end(outbuf, p+nread); - DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n", - fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) ); + DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%lu nread=%ld\n", + fsp->fsp_name, fsp->fnum, (unsigned long)numtoread, (long)nread ) ); send_blocking_reply(outbuf,outsize); return True; @@ -526,7 +527,7 @@ void remove_pending_lock_requests_by_fid(files_struct *fsp) file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - blr->lock_pid, sys_getpid(), blr->fsp->conn->cnum, + blr->lock_pid, procid_self(), blr->fsp->conn->cnum, blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); @@ -552,7 +553,7 @@ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - blr->lock_pid, sys_getpid(), blr->fsp->conn->cnum, + blr->lock_pid, procid_self(), blr->fsp->conn->cnum, blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); } @@ -563,7 +564,8 @@ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); Set a flag as an unlock request affects one of our pending locks. *****************************************************************************/ -static void received_unlock_msg(int msg_type, pid_t src, void *buf, size_t len) +static void received_unlock_msg(int msg_type, struct process_id src, + void *buf, size_t len) { DEBUG(10,("received_unlock_msg\n")); process_blocking_lock_queue(time(NULL)); @@ -641,7 +643,7 @@ void process_blocking_lock_queue(time_t t) fsp->fnum, fsp->fsp_name )); brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, sys_getpid(), conn->cnum, + blr->lock_pid, procid_self(), conn->cnum, blr->offset, blr->count, True, NULL, NULL); blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); @@ -658,7 +660,7 @@ void process_blocking_lock_queue(time_t t) blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, sys_getpid(), conn->cnum, + blr->lock_pid, procid_self(), conn->cnum, blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); @@ -673,7 +675,7 @@ void process_blocking_lock_queue(time_t t) blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, sys_getpid(), conn->cnum, + blr->lock_pid, procid_self(), conn->cnum, blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); @@ -690,7 +692,7 @@ void process_blocking_lock_queue(time_t t) if(blocking_lock_record_process(blr)) { brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, sys_getpid(), conn->cnum, + blr->lock_pid, procid_self(), conn->cnum, blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); diff --git a/source3/smbd/change_trust_pw.c b/source3/smbd/change_trust_pw.c index 1178400e4d..738d12151d 100644 --- a/source3/smbd/change_trust_pw.c +++ b/source3/smbd/change_trust_pw.c @@ -33,7 +33,8 @@ NTSTATUS change_trust_account_password( const char *domain, const char *remote_m NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; struct in_addr pdc_ip; fstring dc_name; - struct cli_state *cli; + struct cli_state *cli = NULL; + struct rpc_pipe_client *netlogon_pipe = NULL; DEBUG(5,("change_trust_account_password: Attempting to change trust account password in domain %s....\n", domain)); @@ -71,20 +72,18 @@ NTSTATUS change_trust_account_password( const char *domain, const char *remote_m * Now start the NT Domain stuff :-). */ - if(cli_nt_session_open(cli, PI_NETLOGON) == False) { + /* Shouldn't we open this with schannel ? JRA. */ + + netlogon_pipe = cli_rpc_pipe_open_noauth(cli, PI_NETLOGON, &nt_status); + if (!netlogon_pipe) { DEBUG(0,("modify_trust_password: unable to open the domain client session to machine %s. Error was : %s.\n", - dc_name, cli_errstr(cli))); - cli_nt_session_close(cli); - cli_ulogoff(cli); + dc_name, nt_errstr(nt_status))); cli_shutdown(cli); - nt_status = NT_STATUS_UNSUCCESSFUL; goto failed; } - nt_status = trust_pw_find_change_and_store_it(cli, cli->mem_ctx, domain); + nt_status = trust_pw_find_change_and_store_it(netlogon_pipe, cli->mem_ctx, domain); - cli_nt_session_close(cli); - cli_ulogoff(cli); cli_shutdown(cli); failed: diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index 374c57a083..c1168583ae 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -946,7 +946,7 @@ static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext) int i; uint32 pwHisLen, curr_pwHisLen; - account_policy_get(AP_PASSWORD_HISTORY, &pwHisLen); + pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen); if (pwHisLen == 0) { return False; } @@ -1017,7 +1017,7 @@ NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passw return NT_STATUS_ACCOUNT_RESTRICTION; } - if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) { + if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) { DEBUG(1, ("user %s cannot change password - password too short\n", username)); DEBUGADD(1, (" account policy min password len = %d\n", min_len)); diff --git a/source3/smbd/close.c b/source3/smbd/close.c index afc645ca06..becca003b6 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -110,31 +110,30 @@ static int close_filestruct(files_struct *fsp) If any deferred opens are waiting on this close, notify them. ****************************************************************************/ -static void notify_deferred_opens(files_struct *fsp) +static void notify_deferred_opens(struct share_mode_lock *lck) { - deferred_open_entry *de_array = NULL; - int num_de_entries, i; - pid_t mypid = sys_getpid(); - - if (!lp_defer_sharing_violations()) { - return; - } - - num_de_entries = get_deferred_opens(fsp->conn, fsp->dev, fsp->inode, &de_array); - for (i = 0; i < num_de_entries; i++) { - deferred_open_entry *entry = &de_array[i]; - if (entry->pid == mypid) { - /* - * We need to notify ourself to retry the open. - * Do this by finding the queued SMB record, moving it - * to the head of the queue and changing the wait time to zero. - */ - schedule_sharing_violation_open_smb_message(entry->mid); - } else { - send_deferred_open_retry_message(entry); - } - } - SAFE_FREE(de_array); + int i; + + for (i=0; i<lck->num_share_modes; i++) { + struct share_mode_entry *e = &lck->share_modes[i]; + + if (!is_deferred_open_entry(e)) { + continue; + } + + if (procid_is_me(&e->pid)) { + /* + * We need to notify ourself to retry the open. Do + * this by finding the queued SMB record, moving it to + * the head of the queue and changing the wait time to + * zero. + */ + schedule_deferred_open_smb_message(e->op_mid); + } else { + message_send_pid(e->pid, MSG_SMB_OPEN_RETRY, + e, sizeof(*e), True); + } + } } /**************************************************************************** @@ -148,13 +147,12 @@ static void notify_deferred_opens(files_struct *fsp) static int close_normal_file(files_struct *fsp, BOOL normal_close) { - share_mode_entry *share_entry = NULL; - size_t share_entry_count = 0; BOOL delete_file = False; connection_struct *conn = fsp->conn; int saved_errno = 0; int err = 0; int err1 = 0; + struct share_mode_lock *lck; remove_pending_lock_requests_by_fid(fsp); @@ -194,23 +192,34 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close) * This prevents race conditions with the file being created. JRA. */ - lock_share_entry_fsp(fsp); - - share_entry_count = del_share_mode(fsp, &share_entry, - &delete_file); + lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, fsp->fsp_name); - DEBUG(10,("close_normal_file: share_entry_count = %lu for file %s\n", - (unsigned long)share_entry_count, fsp->fsp_name )); + if (lck == NULL) { + DEBUG(0, ("Could not get share mode lock\n")); + return EINVAL; + } - if (share_entry_count != 0) { - /* We're not the last ones -- don't delete */ - delete_file = False; + if (!del_share_mode(lck, fsp)) { + DEBUG(0, ("Could not delete share entry\n")); } - SAFE_FREE(share_entry); + delete_file = lck->delete_on_close; + + if (delete_file) { + int i; + /* See if others still have the file open. If this is the + * case, then don't delete */ + for (i=0; i<lck->num_share_modes; i++) { + if (is_valid_share_mode_entry(&lck->share_modes[i])) { + delete_file = False; + break; + } + } + } /* Notify any deferred opens waiting on this close. */ - notify_deferred_opens(fsp); + notify_deferred_opens(lck); + reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open @@ -234,7 +243,7 @@ with error %s\n", fsp->fsp_name, strerror(errno) )); process_pending_change_notify_queue((time_t)0); } - unlock_share_entry_fsp(fsp); + talloc_free(lck); if(fsp->oplock_type) release_file_oplock(fsp); @@ -296,7 +305,7 @@ static int close_directory(files_struct *fsp, BOOL normal_close) */ if (normal_close && - get_delete_on_close_flag(fsp->dev, fsp->inode)) { + get_delete_on_close_flag(fsp->dev, fsp->inode, fsp->fsp_name)) { BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name); DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n", fsp->fsp_name, ok ? "succeeded" : "failed" )); diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c index b69868ecec..bb000bac30 100644 --- a/source3/smbd/conn.c +++ b/source3/smbd/conn.c @@ -287,7 +287,7 @@ the message contains just a share name and all instances of that share are unmounted the special sharename '*' forces unmount of all shares ****************************************************************************/ -void msg_force_tdis(int msg_type, pid_t pid, void *buf, size_t len) +void msg_force_tdis(int msg_type, struct process_id pid, void *buf, size_t len) { connection_struct *conn, *next; fstring sharename; diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c index 32e2f058fc..07d3181144 100644 --- a/source3/smbd/connection.c +++ b/source3/smbd/connection.c @@ -38,7 +38,7 @@ TDB_CONTEXT *conn_tdb_ctx(void) static void make_conn_key(connection_struct *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey) { ZERO_STRUCTP(pkey); - pkey->pid = sys_getpid(); + pkey->pid = procid_self(); pkey->cnum = conn?conn->cnum:-1; fstrcpy(pkey->name, name); #ifdef DEVELOPER @@ -107,8 +107,8 @@ static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *u /* If the pid was not found delete the entry from connections.tdb */ if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) { - DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n", - (unsigned int)crec.pid, crec.cnum, crec.name)); + DEBUG(2,("pid %s doesn't exist - deleting connections %d [%s]\n", + procid_str_static(&crec.pid), crec.cnum, crec.name)); if (tdb_delete(the_tdb, kbuf) != 0) DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) )); return 0; @@ -174,7 +174,7 @@ BOOL claim_connection(connection_struct *conn, const char *name,int max_connecti /* fill in the crec */ ZERO_STRUCT(crec); crec.magic = 0x280267; - crec.pid = sys_getpid(); + crec.pid = procid_self(); crec.cnum = conn?conn->cnum:-1; if (conn) { crec.uid = conn->uid; diff --git a/source3/smbd/error.c b/source3/smbd/error.c index 090a2f6d81..3cdcae5c7f 100644 --- a/source3/smbd/error.c +++ b/source3/smbd/error.c @@ -41,23 +41,22 @@ void set_saved_error_triple(int eclass, int ecode, NTSTATUS status) override_ERR_ntstatus = status; } +void set_saved_ntstatus(NTSTATUS status) +{ + uint8 tmp_eclass; /* Hmmm. override_ERR_class is not uint8... */ + override_ERR_ntstatus = status; + ntstatus_to_dos(status, &tmp_eclass, &override_ERR_code); + override_ERR_class = tmp_eclass; + +} + /**************************************************************************** Return the current settings of the error triple. Return True if any are set. ****************************************************************************/ -BOOL get_saved_error_triple(int *peclass, int *pecode, NTSTATUS *pstatus) +NTSTATUS get_saved_ntstatus(void) { - if (peclass) { - *peclass = override_ERR_class; - } - if (pecode) { - *pecode = override_ERR_code; - } - if (pstatus) { - *pstatus = override_ERR_ntstatus; - } - - return (override_ERR_class || !NT_STATUS_IS_OK(override_ERR_ntstatus)); + return override_ERR_ntstatus; } /**************************************************************************** diff --git a/source3/smbd/files.c b/source3/smbd/files.c index 65986e9612..181e17b11f 100644 --- a/source3/smbd/files.c +++ b/source3/smbd/files.c @@ -65,7 +65,7 @@ files_struct *file_new(connection_struct *conn) { int i; static int first_file; - files_struct *fsp, *next; + files_struct *fsp; /* we want to give out file handles differently on each new connection because of a common bug in MS clients where they try to @@ -76,32 +76,21 @@ files_struct *file_new(connection_struct *conn) first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files; } + /* TODO: Port the id-tree implementation from Samba4 */ + i = bitmap_find(file_bmap, first_file); if (i == -1) { - /* - * Before we give up, go through the open files - * and see if there are any files opened with a - * batch oplock. If so break the oplock and then - * re-use that entry (if it becomes closed). - * This may help as NT/95 clients tend to keep - * files batch oplocked for quite a long time - * after they have finished with them. - */ - for (fsp=Files;fsp;fsp=next) { - next=fsp->next; - if (attempt_close_oplocked_file(fsp)) { - return file_new(conn); - } - } - DEBUG(0,("ERROR! Out of file structures\n")); - set_saved_error_triple(ERRSRV, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES); + /* TODO: We have to unconditionally return a DOS error here, + * W2k3 even returns ERRDOS/ERRnofids for ntcreate&x with + * NTSTATUS negotiated */ + set_saved_ntstatus(NT_STATUS_TOO_MANY_OPENED_FILES); return NULL; } fsp = SMB_MALLOC_P(files_struct); if (!fsp) { - set_saved_error_triple(ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY); + set_saved_ntstatus(NT_STATUS_NO_MEMORY); return NULL; } @@ -110,7 +99,7 @@ files_struct *file_new(connection_struct *conn) fsp->fh = SMB_MALLOC_P(struct fd_handle); if (!fsp->fh) { SAFE_FREE(fsp); - set_saved_error_triple(ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY); + set_saved_ntstatus(NT_STATUS_NO_MEMORY); return NULL; } @@ -293,7 +282,9 @@ files_struct *file_find_dif(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_i DLIST_PROMOTE(Files, fsp); } /* Paranoia check. */ - if (fsp->fh->fd == -1 && fsp->oplock_type != NO_OPLOCK) { + if ((fsp->fh->fd == -1) && + (fsp->oplock_type != NO_OPLOCK) && + (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) { DEBUG(0,("file_find_dif: file %s dev = %x, inode = %.0f, file_id = %u \ oplock_type = %u is a stat open with oplock type !\n", fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, (unsigned int)fsp->file_id, diff --git a/source3/smbd/mangle_hash2.c b/source3/smbd/mangle_hash2.c index 613dda9a16..335ba8e2ef 100644 --- a/source3/smbd/mangle_hash2.c +++ b/source3/smbd/mangle_hash2.c @@ -212,7 +212,7 @@ static BOOL is_mangled_component(const char *name, size_t len) { unsigned int i; - M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len)); + M_DEBUG(10,("is_mangled_component %s (len %lu) ?\n", name, (unsigned long)len)); /* check the length */ if (len > 12 || len < 8) @@ -250,7 +250,7 @@ static BOOL is_mangled_component(const char *name, size_t len) } } - M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len)); + M_DEBUG(10,("is_mangled_component %s (len %lu) -> yes\n", name, (unsigned long)len)); return True; } diff --git a/source3/smbd/message.c b/source3/smbd/message.c index 5af7d3e451..e975da3e15 100644 --- a/source3/smbd/message.c +++ b/source3/smbd/message.c @@ -43,6 +43,7 @@ static void msg_deliver(void) int fd; char *msg; int len; + ssize_t sz; if (! (*lp_msg_command())) { @@ -70,14 +71,20 @@ static void msg_deliver(void) if (msgbuf[i] == '\r' && i < (msgpos-1) && msgbuf[i+1] == '\n') { i++; continue; } - write(fd, &msgbuf[i++], 1); + sz = write(fd, &msgbuf[i++], 1); + if ( sz != 1 ) { + DEBUG(0,("Write error to fd %d: %ld(%d)\n",fd, (long)sz, errno )); + } } } else { for (i = 0; i < len;) { if (msg[i] == '\r' && i < (len-1) && msg[i+1] == '\n') { i++; continue; } - write(fd, &msg[i++],1); + sz = write(fd, &msg[i++],1); + if ( sz != 1 ) { + DEBUG(0,("Write error to fd %d: %ld(%d)\n",fd, (long)sz, errno )); + } } SAFE_FREE(msg); } diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index f9b70de0ac..3db55bad14 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -24,7 +24,6 @@ extern int max_send; extern enum protocol_types Protocol; extern int smb_read_error; -extern int global_oplock_break; extern struct current_user current_user; static const char *known_nt_pipes[] = { @@ -43,6 +42,7 @@ static const char *known_nt_pipes[] = { "\\rpcecho", "\\svcctl", "\\eventlog", + "\\unixinfo", NULL }; @@ -861,7 +861,7 @@ create_options = 0x%x root_dir_fid = 0x%x\n", } else { SCVAL(p,0, EXCLUSIVE_OPLOCK_RETURN); } - } else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { + } else if (fsp->oplock_type == LEVEL_II_OPLOCK) { SCVAL(p,0, LEVEL_II_OPLOCK_RETURN); } else { SCVAL(p,0,NO_OPLOCK_RETURN); @@ -1666,11 +1666,11 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new &info); if (!fsp1) { - get_saved_error_triple(NULL, NULL, &status); + status = get_saved_ntstatus(); if (NT_STATUS_IS_OK(status)) { status = NT_STATUS_ACCESS_DENIED; } - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); return status; } @@ -1684,11 +1684,11 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new &info); if (!fsp2) { - get_saved_error_triple(NULL, NULL, &status); + status = get_saved_ntstatus(); if (NT_STATUS_IS_OK(status)) { status = NT_STATUS_ACCESS_DENIED; } - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); close_file(fsp1,False); return status; } @@ -1989,7 +1989,7 @@ static int call_nt_transact_query_security_desc(connection_struct *conn, char *i return(UNIXERROR(ERRDOS,ERRnoaccess)); } - DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %d.\n",(int)sd_size)); + DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %lu.\n",(unsigned long)sd_size)); SIVAL(params,0,(uint32)sd_size); @@ -2743,21 +2743,6 @@ int reply_nttrans(connection_struct *conn, uint32 num_params_sofar, num_data_sofar; START_PROFILE(SMBnttrans); - if(global_oplock_break && - ((function_code == NT_TRANSACT_CREATE) || - (function_code == NT_TRANSACT_RENAME))) { - /* - * Queue this open message as we are the process of an oplock break. - */ - - DEBUG(2,("reply_nttrans: queueing message code 0x%x \ -due to being in oplock break state.\n", (unsigned int)function_code )); - - push_oplock_pending_smb_message( inbuf, length); - END_PROFILE(SMBnttrans); - return -1; - } - if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) { END_PROFILE(SMBnttrans); return ERROR_DOS(ERRSRV,ERRaccess); diff --git a/source3/smbd/open.c b/source3/smbd/open.c index ed847826d5..56d31c6940 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -24,11 +24,11 @@ extern struct current_user current_user; extern userdom_struct current_user_info; -extern uint16 global_oplock_port; extern uint16 global_smbpid; extern BOOL global_client_failed_oplock_break; -struct dev_inode_bundle { +struct deferred_open_record { + BOOL delayed_for_oplocks; SMB_DEV_T dev; SMB_INO_T inode; }; @@ -208,7 +208,6 @@ static BOOL open_file(files_struct *fsp, BOOL file_existed = VALID_STAT(*psbuf); fsp->fh->fd = -1; - fsp->oplock_type = NO_OPLOCK; errno = EPERM; /* Check permissions */ @@ -283,8 +282,7 @@ static BOOL open_file(files_struct *fsp, /* Don't create files with Microsoft wildcard characters. */ if ((local_flags & O_CREAT) && !file_existed && ms_has_wild(fname)) { - set_saved_error_triple(ERRDOS, ERRinvalidname, - NT_STATUS_OBJECT_NAME_INVALID); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_INVALID); return False; } @@ -354,7 +352,6 @@ static BOOL open_file(files_struct *fsp, } fsp->print_file = False; fsp->modified = False; - fsp->oplock_type = NO_OPLOCK; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; fsp->is_stat = False; @@ -397,7 +394,7 @@ static BOOL is_executable(const char *fname) Returns True if conflict, False if not. ****************************************************************************/ -static BOOL share_conflict(share_mode_entry *entry, +static BOOL share_conflict(struct share_mode_entry *entry, uint32 access_mask, uint32 share_access) { @@ -445,7 +442,6 @@ static BOOL share_conflict(share_mode_entry *entry, DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \ (unsigned int)(share) )); \ - set_saved_error_triple(ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION); \ return True; \ } #else @@ -454,7 +450,6 @@ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (u DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \ (unsigned int)(share) )); \ - set_saved_error_triple(ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION); \ return True; \ } #endif @@ -480,11 +475,23 @@ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (u #if defined(DEVELOPER) static void validate_my_share_entries(int num, - share_mode_entry *share_entry) + struct share_mode_entry *share_entry) { files_struct *fsp; - if (share_entry->pid != sys_getpid()) { + if (!procid_is_me(&share_entry->pid)) { + return; + } + + if (is_deferred_open_entry(share_entry) && + !open_was_deferred(share_entry->op_mid)) { + pstring str; + DEBUG(0, ("Got a deferred entry without a request: " + "PANIC: %s\n", share_mode_str(num, share_entry))); + smb_panic(str); + } + + if (!is_valid_share_mode_entry(share_entry)) { return; } @@ -497,7 +504,26 @@ static void validate_my_share_entries(int num, "share entry with an open file\n"); } + if (is_deferred_open_entry(share_entry) || + is_unused_share_mode_entry(share_entry)) { + goto panic; + } + + if ((share_entry->op_type == NO_OPLOCK) && + (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) { + /* Someone has already written to it, but I haven't yet + * noticed */ + return; + } + if (((uint16)fsp->oplock_type) != share_entry->op_type) { + goto panic; + } + + return; + + panic: + { pstring str; DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) )); @@ -510,375 +536,217 @@ static void validate_my_share_entries(int num, } #endif -struct share_mode_entry_list { - struct share_mode_entry_list *next, *prev; - share_mode_entry entry; -}; - -static void free_broken_entry_list(struct share_mode_entry_list *broken_entry_list) +static BOOL is_stat_open(uint32 access_mask) { - while (broken_entry_list) { - struct share_mode_entry_list *broken_entry = broken_entry_list; - DLIST_REMOVE(broken_entry_list, broken_entry); - SAFE_FREE(broken_entry); - } -} - -static BOOL cause_oplock_break(int request, int existing, uint32 access_mask) -{ - if ((access_mask == DELETE_ACCESS) && - (request == NO_OPLOCK)) { - /* This is a delete request */ - return (BATCH_OPLOCK_TYPE(existing) != 0); - } - - if (EXCLUSIVE_OPLOCK_TYPE(existing) && (request != NO_OPLOCK)) { - return True; - } - - if ((existing != NO_OPLOCK) && (request == NO_OPLOCK)) { - return True; - } - - return False; + return (access_mask && + ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES| + FILE_WRITE_ATTRIBUTES))==0) && + ((access_mask & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES| + FILE_WRITE_ATTRIBUTES)) != 0)); } /**************************************************************************** - Deal with open deny mode and oplock break processing. + Deal with share modes Invarient: Share mode must be locked on entry and exit. Returns -1 on error, or number of share modes on success (may be zero). ****************************************************************************/ -static int open_mode_check(connection_struct *conn, - const char *fname, - SMB_DEV_T dev, - SMB_INO_T inode, - uint32 access_mask, - uint32 share_access, - uint32 create_options, - int *p_oplock_request, - BOOL *p_all_current_opens_are_level_II) +static NTSTATUS open_mode_check(connection_struct *conn, + const char *fname, + struct share_mode_lock *lck, + uint32 access_mask, + uint32 share_access, + uint32 create_options, + BOOL *file_existed) { int i; - int num_share_modes; - int oplock_contention_count = 0; - share_mode_entry *old_shares = NULL; - BOOL broke_oplock; - BOOL delete_on_close; - num_share_modes = get_share_modes(dev, inode, &old_shares, &delete_on_close); - - if(num_share_modes == 0) { - SAFE_FREE(old_shares); - return 0; + if(lck->num_share_modes == 0) { + return NT_STATUS_OK; } + + *file_existed = True; - if (access_mask && - ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES| - FILE_WRITE_ATTRIBUTES))==0) && - ((access_mask & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES| - FILE_WRITE_ATTRIBUTES)) != 0)) { + if (is_stat_open(access_mask)) { /* Stat open that doesn't trigger oplock breaks or share mode * checks... ! JRA. */ - SAFE_FREE(old_shares); - return num_share_modes; + return NT_STATUS_OK; } /* A delete on close prohibits everything */ - if (delete_on_close) { - SAFE_FREE(old_shares); - errno = EACCES; - return -1; + if (lck->delete_on_close) { + return NT_STATUS_DELETE_PENDING; } /* * Check if the share modes will give us access. */ - do { - struct share_mode_entry_list *broken_entry_list = NULL; - struct share_mode_entry_list *broken_entry = NULL; - - broke_oplock = False; - *p_all_current_opens_are_level_II = True; - - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &old_shares[i]; - BOOL opb_ret; - #if defined(DEVELOPER) - validate_my_share_entries(i, share_entry); + for(i = 0; i < lck->num_share_modes; i++) { + validate_my_share_entries(i, &lck->share_modes[i]); + } #endif - /* - * By observation of NetBench, oplocks are broken - * *before* share modes are checked. This allows a - * file to be closed by the client if the share mode - * would deny access and the client has an oplock. - * Check if someone has an oplock on this file. If so - * we must break it before continuing. - */ + if (!lp_share_modes(SNUM(conn))) { + return NT_STATUS_OK; + } - if (!cause_oplock_break(*p_oplock_request, - share_entry->op_type, - access_mask)) { - if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { - *p_all_current_opens_are_level_II = False; - } - continue; - } + /* Now we check the share modes, after any oplock breaks. */ + for(i = 0; i < lck->num_share_modes; i++) { - /* This is an oplock break */ - - DEBUG(5,("open_mode_check: oplock_request = %d, " - "breaking oplock (%x) on file %s, " - "dev = %x, inode = %.0f\n", - *p_oplock_request, share_entry->op_type, - fname, (unsigned int)dev, (double)inode)); - - /* Ensure the reply for the open uses the correct - * sequence number. */ - /* This isn't a real deferred packet as it's response - * will also increment the sequence. - */ - srv_defer_sign_response(get_current_mid()); - - /* Oplock break - unlock to request it. */ - unlock_share_entry(conn, dev, inode); - - opb_ret = request_oplock_break(share_entry); - - /* Now relock. */ - lock_share_entry(conn, dev, inode); - - if (!opb_ret) { - DEBUG(0,("open_mode_check: FAILED when breaking " - "oplock (%x) on file %s, dev = %x, " - "inode = %.0f\n", - old_shares[i].op_type, fname, - (unsigned int)dev, (double)inode)); - SAFE_FREE(old_shares); - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); - return -1; - } - - broken_entry = SMB_MALLOC_P(struct share_mode_entry_list); - if (!broken_entry) { - smb_panic("open_mode_check: malloc fail.\n"); - } - broken_entry->entry = *share_entry; - DLIST_ADD(broken_entry_list, broken_entry); - broke_oplock = True; - - } /* end for */ - - if (broke_oplock) { - /* Update the current open table. */ - SAFE_FREE(old_shares); - num_share_modes = get_share_modes(dev, inode, - &old_shares, - &delete_on_close); + if (!is_valid_share_mode_entry(&lck->share_modes[i])) { + continue; } - if (lp_share_modes(SNUM(conn))) { - /* Now we check the share modes, after any oplock breaks. */ - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &old_shares[i]; - - /* someone else has a share lock on it, check to see - * if we can too */ - if (share_conflict(share_entry, access_mask, - share_access)) { - SAFE_FREE(old_shares); - free_broken_entry_list(broken_entry_list); - errno = EACCES; - return -1; - } - } + /* someone else has a share lock on it, check to see if we can + * too */ + if (share_conflict(&lck->share_modes[i], + access_mask, share_access)) { + return NT_STATUS_SHARING_VIOLATION; } - - for(broken_entry = broken_entry_list; broken_entry; - broken_entry = broken_entry->next) { - oplock_contention_count++; - - /* Paranoia check that this is no longer an exlusive entry. */ - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &old_shares[i]; - - if (!(share_modes_identical(&broken_entry->entry, - share_entry) && - EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type))) { - continue; - } - - /* - * This should not happen. The target left this oplock - * as exlusive.... The process *must* be dead.... - */ - - DEBUG(0,("open_mode_check: exlusive oplock left by " - "process %d after break ! For file %s, " - "dev = %x, inode = %.0f. Deleting it to " - "continue...\n", - (int)broken_entry->entry.pid, fname, - (unsigned int)dev, (double)inode)); - - if (process_exists(broken_entry->entry.pid)) { - DEBUG(0,("open_mode_check: Existent process " - "%lu left active oplock.\n", - (unsigned long)broken_entry->entry.pid )); - } - - if (del_share_entry(dev, inode, &broken_entry->entry, - NULL, &delete_on_close) == -1) { - free_broken_entry_list(broken_entry_list); - errno = EACCES; - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); - return -1; - } - - /* - * We must reload the share modes after deleting the - * other process's entry. - */ - - SAFE_FREE(old_shares); - num_share_modes = get_share_modes(dev, inode, - &old_shares, - &delete_on_close); - break; - } /* end for paranoia... */ - } /* end for broken_entry */ - free_broken_entry_list(broken_entry_list); - } while(broke_oplock); - - /* - * Refuse to grant an oplock in case the contention limit is - * reached when going through the lock list multiple times. - */ - - if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) { - *p_oplock_request = 0; - DEBUG(4,("open_mode_check: oplock contention = %d. Not granting oplock.\n", - oplock_contention_count )); } - SAFE_FREE(old_shares); - return num_share_modes; + return NT_STATUS_OK; } -/**************************************************************************** - Delete the record for a handled deferred open entry. -****************************************************************************/ +static BOOL is_delete_request(files_struct *fsp) { + return ((fsp->access_mask == DELETE_ACCESS) && + (fsp->oplock_type == NO_OPLOCK)); +} -static void delete_defered_open_entry_record(connection_struct *conn, - SMB_DEV_T dev, - SMB_INO_T inode) +/* + * 1) No files open at all: Grant whatever the client wants. + * + * 2) Exclusive (or batch) oplock around: If the requested access is a delete + * request, break if the oplock around is a batch oplock. If it's another + * requested access type, break. + * + * 3) Only level2 around: Grant level2 and do nothing else. + */ + +static BOOL delay_for_oplocks(struct share_mode_lock *lck, files_struct *fsp) { - uint16 mid = get_current_mid(); - pid_t mypid = sys_getpid(); - deferred_open_entry *de_array = NULL; - int num_de_entries, i; + int i, num_level2; + struct share_mode_entry *exclusive = NULL; + BOOL delay_it = False; + BOOL have_level2 = False; - if (!lp_defer_sharing_violations()) { - return; + if (is_stat_open(fsp->access_mask)) { + fsp->oplock_type = NO_OPLOCK; + return False; } - num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); - for (i = 0; i < num_de_entries; i++) { - deferred_open_entry *entry = &de_array[i]; - if (entry->pid == mypid && entry->mid == mid && entry->dev == dev && - entry->inode == inode) { + num_level2 = 0; - /* Remove the deferred open entry from the array. */ - delete_deferred_open_entry(entry); - SAFE_FREE(de_array); - return; + if (lck->num_share_modes == 0) { + /* No files open at all: Directly grant whatever the client + * wants. */ + + if (fsp->oplock_type == NO_OPLOCK) { + /* Store a level2 oplock, but don't tell the client */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } + return False; + } + + for (i=0; i<lck->num_share_modes; i++) { + + if (!is_valid_share_mode_entry(&lck->share_modes[i])) { + continue; + } + + if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) { + SMB_ASSERT(exclusive == NULL); + exclusive = &lck->share_modes[i]; + } + + if (lck->share_modes[i].op_type == LEVEL_II_OPLOCK) { + have_level2 = True; } } - SAFE_FREE(de_array); + + if (exclusive != NULL) { /* Found an exclusive oplock */ + SMB_ASSERT(!have_level2); + delay_it = is_delete_request(fsp) ? + BATCH_OPLOCK_TYPE(exclusive->op_type) : True; + } + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + /* We can at most grant level2 */ + fsp->oplock_type = LEVEL_II_OPLOCK; + } + + if ((fsp->oplock_type == NO_OPLOCK) && have_level2) { + /* Store a level2 oplock, but don't tell the client */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } + + if (delay_it) { + DEBUG(10, ("Sending break request to PID %s\n", + procid_str_static(&exclusive->pid))); + exclusive->op_mid = get_current_mid(); + if (!message_send_pid(exclusive->pid, MSG_SMB_BREAK_REQUEST, + exclusive, sizeof(*exclusive), True)) { + DEBUG(3, ("Could not send oplock break message\n")); + } + file_free(fsp); + } + + return delay_it; +} + +static BOOL request_timed_out(struct timeval request_time, + struct timeval timeout) +{ + struct timeval now, end_time; + GetTimeOfDay(&now); + end_time = timeval_sum(&request_time, &timeout); + return (timeval_compare(&end_time, &now) < 0); } /**************************************************************************** Handle the 1 second delay in returning a SHARING_VIOLATION error. ****************************************************************************/ -static void defer_open_sharing_error(connection_struct *conn, - struct timeval *ptv, - const char *fname, - SMB_DEV_T dev, - SMB_INO_T inode) +static void defer_open(struct share_mode_lock *lck, + struct timeval request_time, + struct timeval timeout, + struct deferred_open_record *state) { uint16 mid = get_current_mid(); - pid_t mypid = sys_getpid(); - deferred_open_entry *de_array = NULL; - int num_de_entries, i; - struct dev_inode_bundle dib; + int i; - if (!lp_defer_sharing_violations()) { - return; - } + /* Paranoia check */ - dib.dev = dev; - dib.inode = inode; + for (i=0; i<lck->num_share_modes; i++) { + struct share_mode_entry *e = &lck->share_modes[i]; - num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); - for (i = 0; i < num_de_entries; i++) { - deferred_open_entry *entry = &de_array[i]; - if (entry->pid == mypid && entry->mid == mid) { - /* - * Check if a 1 second timeout has expired. - */ - if (usec_time_diff(ptv, &entry->time) > - SHARING_VIOLATION_USEC_WAIT) { - DEBUG(10,("defer_open_sharing_error: Deleting " - "deferred open entry for mid %u, " - "file %s\n", - (unsigned int)mid, fname )); - - /* Expired, return a real error. */ - /* Remove the deferred open entry from the array. */ - - delete_deferred_open_entry(entry); - SAFE_FREE(de_array); - return; - } - /* - * If the timeout hasn't expired yet and we still have - * a sharing violation, just leave the entry in the - * deferred open array alone. We do need to reschedule - * this open call though (with the original created - * time). - */ - DEBUG(10,("defer_open_sharing_error: time [%u.%06u] " - "updating deferred open entry for mid %u, file %s\n", - (unsigned int)entry->time.tv_sec, - (unsigned int)entry->time.tv_usec, - (unsigned int)mid, fname )); - - push_sharing_violation_open_smb_message(&entry->time, - (char *)&dib, - sizeof(dib)); - SAFE_FREE(de_array); - return; + if (!is_deferred_open_entry(e)) { + continue; + } + + if (procid_is_me(&e->pid) && (e->op_mid == mid)) { + DEBUG(0, ("Trying to defer an already deferred " + "request: mid=%d, exiting\n", mid)); + exit_server("exiting"); } } + /* End paranoia check */ + DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred " - "open entry for mid %u, file %s\n", - (unsigned int)ptv->tv_sec, (unsigned int)ptv->tv_usec, - (unsigned int)mid, fname )); + "open entry for mid %u\n", + (unsigned int)request_time.tv_sec, + (unsigned int)request_time.tv_usec, + (unsigned int)mid)); - if (!push_sharing_violation_open_smb_message(ptv, (char *)&dib, sizeof(dib))) { - SAFE_FREE(de_array); - return; - } - if (!add_deferred_open(mid, ptv, dev, inode, global_oplock_port, fname)) { - remove_sharing_violation_open_smb_message(mid); + if (!push_deferred_smb_message(mid, request_time, timeout, + (char *)state, sizeof(*state))) { + exit_server("push_deferred_smb_message failed\n"); } + add_deferred_open(lck, mid, request_time, state->dev, state->inode); /* * Push the MID of this packet on the signing queue. @@ -888,8 +756,6 @@ static void defer_open_sharing_error(connection_struct *conn, */ srv_defer_sign_response(mid); - - SAFE_FREE(de_array); } /**************************************************************************** @@ -1196,8 +1062,6 @@ files_struct *open_file_ntcreate(connection_struct *conn, BOOL internal_only_open = False; SMB_DEV_T dev = 0; SMB_INO_T inode = 0; - int num_share_modes = 0; - BOOL all_current_opens_are_level_II = False; BOOL fsp_open = False; files_struct *fsp = NULL; mode_t new_unx_mode = (mode_t)0; @@ -1205,8 +1069,11 @@ files_struct *open_file_ntcreate(connection_struct *conn, int info; uint32 existing_dos_attributes = 0; struct pending_message_list *pml = NULL; - uint16 port = 0; uint16 mid = get_current_mid(); + BOOL delayed_for_oplocks = False; + struct timeval request_time = timeval_zero(); + struct share_mode_lock *lck = NULL; + NTSTATUS status; if (conn->printer) { /* @@ -1241,9 +1108,11 @@ files_struct *open_file_ntcreate(connection_struct *conn, } if ((pml = get_open_deferred_message(mid)) != NULL) { - struct dev_inode_bundle dib; + struct deferred_open_record *state = + (struct deferred_open_record *)pml->private_data.data; - memcpy(&dib, pml->private_data.data, sizeof(dib)); + request_time = pml->request_time; + delayed_for_oplocks = state->delayed_for_oplocks; /* There could be a race condition where the dev/inode pair has changed since we deferred the message. If so, just @@ -1255,24 +1124,18 @@ files_struct *open_file_ntcreate(connection_struct *conn, notified of a close and we don't want to trigger another spurious oplock break. */ - if (!file_existed || dib.dev != psbuf->st_dev || - dib.inode != psbuf->st_ino || pml->msg_time.tv_sec || - pml->msg_time.tv_usec) { - /* Ensure we don't reprocess this message. */ - remove_sharing_violation_open_smb_message(mid); - - /* Now remove the deferred open entry under lock. */ - lock_share_entry(conn, dib.dev, dib.inode); - delete_defered_open_entry_record(conn, dib.dev, - dib.inode); - unlock_share_entry(conn, dib.dev, dib.inode); - - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); - return NULL; + /* Now remove the deferred open entry under lock. */ + lck = get_share_mode_lock(NULL, state->dev, state->inode, + fname); + if (lck == NULL) { + DEBUG(0, ("could not get share mode lock\n")); + } else { + del_deferred_open_entry(lck, mid); + talloc_destroy(lck); } + /* Ensure we don't reprocess this message. */ - remove_sharing_violation_open_smb_message(mid); + remove_deferred_open_smb_message(mid); } if (!check_name(fname,conn)) { @@ -1285,7 +1148,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, } /* ignore any oplock requests if oplocks are disabled */ - if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) { + if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break || + IS_VETO_OPLOCK_PATH(conn, fname)) { oplock_request = 0; } @@ -1325,7 +1189,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, DEBUG(5,("open_file_ntcreate: FILE_OPEN " "requested for file %s and file " "doesn't exist.\n", fname )); - set_saved_error_triple(ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_NOT_FOUND); errno = ENOENT; return NULL; } @@ -1338,7 +1202,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, DEBUG(5,("open_file_ntcreate: FILE_OVERWRITE " "requested for file %s and file " "doesn't exist.\n", fname )); - set_saved_error_triple(ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_NOT_FOUND); errno = ENOENT; return NULL; } @@ -1369,8 +1233,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, break; default: - set_saved_error_triple(ERRDOS, ERRinvalidparam, - NT_STATUS_INVALID_PARAMETER); + set_saved_ntstatus(NT_STATUS_INVALID_PARAMETER); return NULL; } @@ -1447,8 +1310,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, DEBUG(5,("open_file_ntcreate: write access requested for " "file %s on read only %s\n", fname, !CAN_WRITE(conn) ? "share" : "file" )); - set_saved_error_triple(ERRDOS, ERRnoaccess, - NT_STATUS_ACCESS_DENIED); + set_saved_ntstatus(NT_STATUS_ACCESS_DENIED); errno = EACCES; return NULL; } @@ -1458,45 +1320,96 @@ files_struct *open_file_ntcreate(connection_struct *conn, return NULL; } + fsp->dev = psbuf->st_dev; + fsp->inode = psbuf->st_ino; + fsp->share_access = share_access; + fsp->fh->private_options = create_options; + fsp->access_mask = access_mask; + fsp->oplock_type = oplock_request; + + if (timeval_is_zero(&request_time)) { + request_time = fsp->open_time; + } + if (file_existed) { dev = psbuf->st_dev; inode = psbuf->st_ino; - lock_share_entry(conn, dev, inode); - - num_share_modes = open_mode_check(conn, fname, dev, inode, - access_mask, share_access, - create_options, - &oplock_request, - &all_current_opens_are_level_II); - if(num_share_modes == -1) { - - if (!internal_only_open) { - NTSTATUS status; - get_saved_error_triple(NULL, NULL, &status); - if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) { - /* Check if this can be done with the - * deny_dos and fcb calls. */ - if (create_options & - (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| - NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { - files_struct *fsp_dup; - fsp_dup = fcb_or_dos_open(conn, fname, dev, - inode, access_mask, - share_access, - create_options); - - if (fsp_dup) { - unlock_share_entry(conn, dev, inode); - file_free(fsp); - if (pinfo) { - *pinfo = FILE_WAS_OPENED; - } - conn->num_files_open++; - return fsp_dup; - } + lck = get_share_mode_lock(NULL, dev, inode, fname); + + if (lck == NULL) { + DEBUG(0, ("Could not get share mode lock\n")); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); + return NULL; + } + + if (delay_for_oplocks(lck, fsp)) { + struct deferred_open_record state; + struct timeval timeout; + + if (delayed_for_oplocks) { + DEBUG(0, ("Trying to delay for oplocks " + "twice\n")); + exit_server("exiting"); + } + + timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0); + + /* Normally the smbd we asked should respond within + * OPLOCK_BREAK_TIMEOUT seconds regardless of whether + * the client did, give twice the timeout as a safety + * measure here in case the other smbd is stuck + * somewhere else. */ + + state.delayed_for_oplocks = True; + state.dev = dev; + state.inode = inode; + + if (!request_timed_out(request_time, timeout)) { + defer_open(lck, request_time, timeout, + &state); + } + + talloc_free(lck); + return NULL; + } + + status = open_mode_check(conn, fname, lck, + access_mask, share_access, + create_options, &file_existed); + + if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { + /* DELETE_PENDING is not deferred for a second */ + set_saved_ntstatus(status); + talloc_free(lck); + file_free(fsp); + return NULL; + } + + if (!NT_STATUS_IS_OK(status)) { + + SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)); + + /* Check if this can be done with the deny_dos and fcb + * calls. */ + if (create_options & + (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| + NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { + files_struct *fsp_dup; + fsp_dup = fcb_or_dos_open(conn, fname, dev, + inode, access_mask, + share_access, + create_options); + + if (fsp_dup) { + talloc_free(lck); + file_free(fsp); + if (pinfo) { + *pinfo = FILE_WAS_OPENED; } + conn->num_files_open++; + return fsp_dup; } } @@ -1527,8 +1440,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, if (!fsp_open && errno) { /* Default error. */ - set_saved_error_triple(ERRDOS, ERRnoaccess, - NT_STATUS_ACCESS_DENIED); + set_saved_ntstatus(NT_STATUS_ACCESS_DENIED); } /* @@ -1536,27 +1448,32 @@ files_struct *open_file_ntcreate(connection_struct *conn, * cope with the braindead 1 second delay. */ - if (!internal_only_open) { - NTSTATUS status; - get_saved_error_triple(NULL, NULL, &status); - if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) { - /* The fsp->open_time here represents - * the current time of day. */ - defer_open_sharing_error(conn, - &fsp->open_time, - fname, dev, inode); + if (!internal_only_open && + lp_defer_sharing_violations()) { + struct timeval timeout; + struct deferred_open_record state; + + timeout = timeval_set(0, SHARING_VIOLATION_USEC_WAIT); + + state.delayed_for_oplocks = False; + state.dev = dev; + state.inode = inode; + + if (!request_timed_out(request_time, + timeout)) { + defer_open(lck, request_time, timeout, + &state); } } - unlock_share_entry(conn, dev, inode); + talloc_free(lck); if (fsp_open) { fd_close(conn, fsp); /* * We have detected a sharing violation here * so return the correct error code */ - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); } file_free(fsp); return NULL; @@ -1567,23 +1484,28 @@ files_struct *open_file_ntcreate(connection_struct *conn, */ } + SMB_ASSERT(!file_existed || (lck != NULL)); + /* * Ensure we pay attention to default ACLs on directories if required. */ if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) && - (def_acl = directory_has_default_acl(conn, parent_dirname(fname)))) { + (def_acl = directory_has_default_acl(conn, + parent_dirname(fname)))) { unx_mode = 0777; } DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n", - (unsigned int)flags,(unsigned int)flags2,(unsigned int)unx_mode)); + (unsigned int)flags, (unsigned int)flags2, + (unsigned int)unx_mode)); /* * open_file strips any O_TRUNC flags itself. */ - fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,unx_mode,access_mask); + fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,unx_mode, + access_mask); if (!fsp_open && (flags2 & O_EXCL) && (errno == EEXIST)) { /* @@ -1602,23 +1524,24 @@ files_struct *open_file_ntcreate(connection_struct *conn, } if (!fsp_open) { - if(file_existed) { - unlock_share_entry(conn, dev, inode); + if (lck != NULL) { + talloc_free(lck); } file_free(fsp); return NULL; } - /* - * Deal with the race condition where two smbd's detect the file - * doesn't exist and do the create at the same time. One of them will - * win and set a share mode, the other (ie. this one) should check if - * the requested share mode for this create is allowed. - */ - if (!file_existed) { /* + * Deal with the race condition where two smbd's detect the + * file doesn't exist and do the create at the same time. One + * of them will win and set a share mode, the other (ie. this + * one) should check if the requested share mode for this + * create is allowed. + */ + + /* * Now the file exists and fsp is successfully opened, * fsp->dev and fsp->inode are valid and should replace the * dev=0,inode=0 from a non existent file. Spotted by @@ -1628,70 +1551,41 @@ files_struct *open_file_ntcreate(connection_struct *conn, dev = fsp->dev; inode = fsp->inode; - lock_share_entry_fsp(fsp); - - num_share_modes = open_mode_check(conn, fname, dev, inode, - access_mask, share_access, - create_options, - &oplock_request, - &all_current_opens_are_level_II); - - if(num_share_modes == -1) { - NTSTATUS status; - get_saved_error_triple(NULL, NULL, &status); - if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) { - /* Check if this can be done with the deny_dos - * and fcb calls. */ - if (create_options & - (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| - NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { - files_struct *fsp_dup; - fsp_dup = fcb_or_dos_open(conn, fname, dev, inode, - access_mask, share_access, - create_options); - if (fsp_dup) { - unlock_share_entry(conn, dev, inode); - fd_close(conn, fsp); - file_free(fsp); - if (pinfo) { - *pinfo = FILE_WAS_OPENED; - } - conn->num_files_open++; - return fsp_dup; - } - } - - /* - * If we're returning a share violation, - * ensure we cope with the braindead 1 second - * delay. - */ - - /* The fsp->open_time here represents the - * current time of day. */ - defer_open_sharing_error(conn, &fsp->open_time, - fname, dev, inode); - } + lck = get_share_mode_lock(NULL, dev, inode, fname); - unlock_share_entry_fsp(fsp); - fd_close(conn,fsp); + if (lck == NULL) { + DEBUG(0, ("Coult not get share mode lock\n")); + fd_close(conn, fsp); file_free(fsp); - /* - * We have detected a sharing violation here, so - * return the correct code. - */ - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); return NULL; } - /* - * If there are any share modes set then the file *did* - * exist. Ensure we return the correct value for action. - */ + status = open_mode_check(conn, fname, lck, + access_mask, share_access, + create_options, &file_existed); + + if (!NT_STATUS_IS_OK(status)) { + struct deferred_open_record state; - if (num_share_modes > 0) { - file_existed = True; + fd_close(conn, fsp); + file_free(fsp); + + state.delayed_for_oplocks = False; + state.dev = dev; + state.inode = inode; + + /* Do it all over again immediately. In the second + * round we will find that the file existed and handle + * the DELETE_PENDING and FCB cases correctly. No need + * to duplicate the code here. Essentially this is a + * "goto top of this function", but don't tell + * anybody... */ + + defer_open(lck, request_time, timeval_zero(), + &state); + talloc_free(lck); + return NULL; } /* @@ -1699,6 +1593,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, */ } + SMB_ASSERT(lck != NULL); + /* note that we ignore failure for the following. It is basically a hack for NFS, and NFS will never set one of these only read them. Nobody but Samba can ever set a deny @@ -1725,7 +1621,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, */ if ((SMB_VFS_FTRUNCATE(fsp,fsp->fh->fd,0) == -1) || (SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf)==-1)) { - unlock_share_entry_fsp(fsp); + talloc_free(lck); fd_close(conn,fsp); file_free(fsp); return NULL; @@ -1761,20 +1657,14 @@ files_struct *open_file_ntcreate(connection_struct *conn, * file structs. */ - if(oplock_request && (num_share_modes == 0) && - !IS_VETO_OPLOCK_PATH(conn,fname) && - set_file_oplock(fsp, oplock_request) ) { - port = global_oplock_port; - } else if (oplock_request && all_current_opens_are_level_II) { - port = global_oplock_port; - oplock_request = LEVEL_II_OPLOCK; - set_file_oplock(fsp, oplock_request); - } else { - port = 0; - oplock_request = 0; + if ((fsp->oplock_type != NO_OPLOCK) && + (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) { + if (!set_file_oplock(fsp, fsp->oplock_type)) { + /* Could not get the kernel oplock */ + fsp->oplock_type = NO_OPLOCK; + } } - - set_share_mode(fsp, port, oplock_request); + set_share_mode(lck, fsp, 0, fsp->oplock_type); if (create_options & FILE_DELETE_ON_CLOSE) { uint32 dosattr= existing_dos_attributes; @@ -1788,19 +1678,16 @@ files_struct *open_file_ntcreate(connection_struct *conn, result = can_set_delete_on_close(fsp, True, dosattr); if (!NT_STATUS_IS_OK(result)) { - uint8 u_e_c; - uint32 u_e_code; - BOOL dummy_del_on_close; /* Remember to delete the mode we just added. */ - del_share_mode(fsp, NULL, &dummy_del_on_close); - unlock_share_entry_fsp(fsp); + del_share_mode(lck, fsp); + talloc_free(lck); fd_close(conn,fsp); file_free(fsp); - ntstatus_to_dos(result, &u_e_c, &u_e_code); - set_saved_error_triple(u_e_c, u_e_code, result); + set_saved_ntstatus(result); return NULL; } - set_delete_on_close(fsp, True); + lck->delete_on_close = True; + lck->modified = True; } if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED || @@ -1860,8 +1747,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, /* If this is a successful open, we must remove any deferred open * records. */ - delete_defered_open_entry_record(conn, fsp->dev, fsp->inode); - unlock_share_entry_fsp(fsp); + del_deferred_open_entry(lck, mid); + talloc_free(lck); conn->num_files_open++; @@ -1945,17 +1832,13 @@ files_struct *open_directory(connection_struct *conn, if (is_ntfs_stream_name(fname)) { DEBUG(0,("open_directory: %s is a stream name!\n", fname )); - /* NB. Is the DOS error ERRbadpath or ERRbaddirectory ? */ - set_saved_error_triple(ERRDOS, ERRbadpath, - NT_STATUS_NOT_A_DIRECTORY); + set_saved_ntstatus(NT_STATUS_NOT_A_DIRECTORY); return NULL; } if (dir_existed && !S_ISDIR(psbuf->st_mode)) { DEBUG(0,("open_directory: %s is not a directory !\n", fname )); - /* NB. Is the DOS error ERRbadpath or ERRbaddirectory ? */ - set_saved_error_triple(ERRDOS, ERRbadpath, - NT_STATUS_NOT_A_DIRECTORY); + set_saved_ntstatus(NT_STATUS_NOT_A_DIRECTORY); return NULL; } @@ -1967,8 +1850,7 @@ files_struct *open_directory(connection_struct *conn, DEBUG(5,("open_directory: FILE_OPEN requested " "for directory %s and it doesn't " "exist.\n", fname )); - set_saved_error_triple(ERRDOS, ERRbadfile, - NT_STATUS_OBJECT_NAME_NOT_FOUND); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_NOT_FOUND); return NULL; } info = FILE_WAS_OPENED; @@ -2008,8 +1890,7 @@ files_struct *open_directory(connection_struct *conn, "0x%x for directory %s\n", (unsigned int)create_disposition, fname)); file_free(fsp); - set_saved_error_triple(ERRDOS, ERRinvalidparam, - NT_STATUS_INVALID_PARAMETER); + set_saved_ntstatus(NT_STATUS_INVALID_PARAMETER); return NULL; } diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index c0c9e989a9..385f998b1c 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -3,6 +3,7 @@ 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 @@ -21,25 +22,17 @@ #include "includes.h" -/* 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 struct timeval smb_last_time; extern uint32 global_client_caps; -extern struct current_user current_user; extern int smb_read_error; static struct kernel_oplocks *koplocks; -static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local); - /**************************************************************************** Get the number of current exclusive oplocks. ****************************************************************************/ @@ -58,9 +51,6 @@ BOOL oplock_message_waiting(fd_set *fds) if (koplocks && koplocks->msg_waiting(fds)) return True; - if (FD_ISSET(oplock_sock, fds)) - return True; - return False; } @@ -75,13 +65,9 @@ BOOL oplock_message_waiting(fd_set *fds) ****************************************************************************/ -BOOL receive_local_message( char *buffer, int buffer_len, int timeout) +void process_kernel_oplocks(void) { - struct sockaddr_in from; - socklen_t fromlen = sizeof(from); - int32 msg_len = 0; fd_set fds; - int selrtn = -1; FD_ZERO(&fds); smb_read_error = 0; @@ -92,110 +78,29 @@ BOOL receive_local_message( char *buffer, int buffer_len, int timeout) * already been eaten. JRA. */ - if (koplocks && koplocks->msg_waiting(&fds)) { - return koplocks->receive_message(&fds, buffer, buffer_len); + if (!koplocks) { + return; } - while (timeout > 0 && selrtn == -1) { - struct timeval to; - int maxfd = oplock_sock; - time_t starttime = time(NULL); - - FD_ZERO(&fds); - maxfd = setup_oplock_select_set(&fds); + while (koplocks->msg_waiting(&fds)) { + files_struct *fsp; + struct kernel_oplock_message msg; - to.tv_sec = timeout / 1000; - to.tv_usec = (timeout % 1000) * 1000; - - DEBUG(5,("receive_local_message: doing select with timeout of %d ms\n", timeout)); - - selrtn = sys_select(maxfd+1,&fds,NULL,NULL,&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); - } + fsp = koplocks->receive_message(&fds); - /* - * Linux 2.0.x seems to have a bug in that - * it can return -1, EINTR with a timeout of zero. - * Make sure we bail out here with a read timeout - * if we got EINTR on a timeout of 1 or less. - */ - - if (timeout <= 1) { - smb_read_error = READ_TIMEOUT; - return False; - } - - /* Not a kernel interrupt - could be a SIGUSR1 message. We must restart. */ - /* We need to decrement the timeout here. */ - timeout -= ((time(NULL) - starttime)*1000); - if (timeout < 0) - timeout = 1; - - DEBUG(5,("receive_local_message: EINTR : new timeout %d ms\n", timeout)); - continue; + if (fsp == NULL) { + DEBUG(3, ("Kernel oplock message announced, but none " + "received\n")); + return; } - /* 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); + msg.dev = fsp->dev; + msg.inode = fsp->inode; + msg.file_id = fsp->file_id; + message_send_pid(pid_to_procid(sys_getpid()), + MSG_SMB_KERNEL_BREAK, + &msg, sizeof(msg), True); } - - 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 = sys_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; } /**************************************************************************** @@ -229,13 +134,19 @@ tv_sec = %x, tv_usec = %x\n", void release_file_oplock(files_struct *fsp) { - if ((fsp->oplock_type != NO_OPLOCK) && koplocks) + 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 (fsp->oplock_type) + 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; @@ -263,45 +174,58 @@ 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 )); + lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, 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; +} - if (fsp->sent_oplock_break == BREAK_TO_NONE_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) +{ + SMB_DEV_T dev = fsp->dev; + SMB_INO_T inode = fsp->inode; + 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->dev, fsp->inode, NULL); + if (lck == NULL) { + DEBUG(0,("downgrade_oplock: failed to lock share entry for " + "file %s\n", fsp->fsp_name )); + return False; } - - unlock_share_entry_fsp(fsp); + 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; } @@ -313,12 +237,7 @@ dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double) int setup_oplock_select_set( fd_set *fds) { - int maxfd = oplock_sock; - - if(oplock_sock == -1) - return 0; - - FD_SET(oplock_sock,fds); + int maxfd = 0; if (koplocks && koplocks->notification_fd != -1) { FD_SET(koplocks->notification_fd, fds); @@ -329,197 +248,31 @@ int setup_oplock_select_set( fd_set *fds) } /**************************************************************************** - 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_t cmd) { - int32 msg_len; - uint16 from_port; - char *msg_start; - pid_t remotepid; - SMB_DEV_T dev; - SMB_INO_T inode; - unsigned long file_id; - uint16 break_cmd_type; - struct sockaddr_in toaddr; - - 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, &file_id)) { - DEBUG(0,("kernel oplock break parse failure!\n")); - return False; - } - break; - - case OPLOCK_BREAK_CMD: - case LEVEL_II_OPLOCK_BREAK_CMD: - case ASYNC_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; - } - - memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); - memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); - memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); - memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id)); - - DEBUG(5,("process_local_message: (%s) oplock break request from \ -pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n", - (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II", - (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); - break; - - case RETRY_DEFERRED_OPEN_CMD: - - /* Request to retry and open that would return SHARING_VIOLATION. */ - if (msg_len != DEFERRED_OPEN_MSG_LEN) { - DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n", - (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN)); - return False; - } - { - uint16 mid; - - memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid)); - memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode)); - memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev)); - memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid)); - - DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \ -pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n", - (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid)); - - schedule_sharing_violation_open_smb_message(mid); - } - return True; - - /* - * 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)); - memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id)); + char *result = TALLOC_ARRAY(mem_ctx, char, smb_size + 8*2 + 0); - DEBUG(0,("process_local_message: unsolicited oplock break reply from \ -pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n", - (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); - - 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 == 0) && - (level_II_oplocks_open == 0)) { - /* - * 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")); - - } else { - if (!oplock_break(dev, inode, file_id, False)) { - DEBUG(0,("process_local_message: oplock break failed.\n")); - return False; - } - } - - /* - * Do the appropriate reply - none in the kernel or async level II - * case. - */ - - if (!((break_cmd_type == OPLOCK_BREAK_CMD) || - (break_cmd_type == LEVEL_II_OPLOCK_BREAK_CMD))) { - return True; - } - - /* Send the message back after OR'ing in the 'REPLY' bit. */ - SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,break_cmd_type | 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(sys_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; + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; } - DEBUG(5,("process_local_message: oplock break reply sent to pid %d, " - "port %d, for file dev = %x, inode = %.0f, file_id = %lu\n", - (int)remotepid, from_port, (unsigned int)dev, - (double)inode, file_id)); - - return True; -} - -/**************************************************************************** - Set up an oplock break message. -****************************************************************************/ - -static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2) -{ - 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); + 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; } /**************************************************************************** @@ -602,639 +355,266 @@ static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, un return fsp; } -/**************************************************************************** - Process a level II oplock break directly. - We must call this function with the share mode entry locked. -****************************************************************************/ - -static BOOL oplock_break_level2(files_struct *fsp, BOOL local_request) +static void oplock_timeout_handler(struct timed_event *te, + const struct timeval *now, + void *private_data) { - char outbuf[128]; - 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) { - BOOL sign_state; - - /* - * 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. - */ - - if (local_request) { - wait_before_sending_break(); - } - - /* Prepare the SMBlockingX message. */ - prepare_break_message( outbuf, fsp, False); - - /* Save the server smb signing state. */ - sign_state = srv_oplock_set_signing(False); - - show_msg(outbuf); - if (!send_smb(smbd_server_fd(), outbuf)) - exit_server("oplock_break_level2: send_smb failed."); + files_struct *fsp = private_data; - /* Restore the sign state to what it was. */ - srv_oplock_set_signing(sign_state); - } - - /* - * Now we must update the shared memory structure to tell - * everyone else we no longer have a level II oplock on - * this open file. We must call this function with the share mode - * entry locked so we can change the entry directly. - */ - - if(remove_share_oplock(fsp)==False) { - DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name )); - } - - release_file_oplock(fsp); - - 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, file_id = %lu\n", (unsigned int)dev, (double)inode, fsp->file_id ); - dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open ); - } - - return True; + DEBUG(0, ("Oplock break failed -- replying anyway\n")); + global_client_failed_oplock_break = True; + remove_oplock(fsp); + reply_to_oplock_break_requests(fsp); } -/**************************************************************************** - Process an oplock break directly. - This is always called with the share mode lock *NOT* held. -****************************************************************************/ - -static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local_request) +static void process_oplock_break_message(int msg_type, struct process_id src, + void *buf, size_t len) { - char *inbuf = NULL; - char *saved_inbuf = NULL; - char *outbuf = NULL; - char *saved_outbuf = NULL; - files_struct *fsp = NULL; - time_t start_time; - BOOL shutdown_server = False; - BOOL oplock_timeout = False; + struct share_mode_entry *msg = buf; + files_struct *fsp; + char *break_msg; + BOOL break_to_level2 = False; BOOL sign_state; - 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, file_id)) == NULL) - return True; - /* - * Deal with a level II oplock going break to none separately. - */ - - if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { - BOOL ret; - /* We must always call oplock_break_level2() with - the share mode entry locked. */ - if (lock_share_entry_fsp(fsp) == False) { - DEBUG(0,("oplock_break: unable to lock share entry for file %s\n", fsp->fsp_name )); - return False; - } - ret = oplock_break_level2(fsp, local_request); - unlock_share_entry_fsp(fsp); - return ret; + if (buf == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; } - /* 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, file_id = %lu)\n", (unsigned int)dev, (double)inode, fsp->file_id ); - } - - /* - * 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 (len != sizeof(*msg)) { + DEBUG(0, ("Got invalid msg len %d\n", (int)len)); + return; } - if(global_oplock_break) { - DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n")); - abort(); - } + DEBUG(10, ("Got oplock break message from pid %d: %d/%d/%d\n", + (int)procid_to_pid(&src), (int)msg->dev, (int)msg->inode, + (int)msg->share_file_id)); - /* - * 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. - */ + fsp = initial_break_processing(msg->dev, msg->inode, + msg->share_file_id); - if((inbuf = NewInBuffer(&saved_inbuf))==NULL) { - DEBUG(0,("oplock_break: malloc fail for input buffer.\n")); - return False; + if (fsp == NULL) { + /* 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")); + message_send_pid(src, MSG_SMB_BREAK_RESPONSE, + msg, sizeof(*msg), True); + return; } - if((outbuf = NewOutBuffer(&saved_outbuf))==NULL) { - DEBUG(0,("oplock_break: malloc fail for output buffer.\n")); - /* Free must be done before set.. */ - free_InBuffer(inbuf); - set_InBuffer(saved_inbuf); - return False; + 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 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. - */ - - if (local_request) { - wait_before_sending_break(); + if (EXCLUSIVE_OPLOCK_TYPE(msg->op_type) && + !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(3, ("Already downgraded oplock on %.0f/%.0f: %s\n", + (double)fsp->dev, (double)fsp->inode, + fsp->fsp_name)); + message_send_pid(src, MSG_SMB_BREAK_RESPONSE, + msg, sizeof(*msg), True); + return; } - /* Prepare the SMBlockingX message. */ + if ((msg_type == MSG_SMB_BREAK_REQUEST) && + (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))) { + break_to_level2 = True; + } - 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; + 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"); } - 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:BREAK_TO_NONE_SENT; + /* Need to wait before sending a break message to a file of our own */ + 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(outbuf); - if (!send_smb(smbd_server_fd(), outbuf)) { - srv_oplock_set_signing(sign_state); + show_msg(break_msg); + if (!send_smb(smbd_server_fd(), break_msg)) { exit_server("oplock_break: send_smb failed."); } /* Restore the sign state to what it was. */ srv_oplock_set_signing(sign_state); - /* 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); + talloc_free(break_msg); - /* - * 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; - /* - * Initialize saved_dir to something sensible: vfs_GetWd may not work well - * for root: the directory may be NFS-mounted and exported with root_squash - * (so has no root access). - */ - pstrcpy(saved_dir,saved_fsp_conn->connectpath); - vfs_GetWd(saved_fsp_conn,saved_dir); - /* Save the chain fnum. */ - file_chain_save(); - - pstrcpy(file_name, fsp->fsp_name); - - change_to_root_user(); - - /* - * From Charles Hoch <hoch@exemplary.com>. 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. With async I/O, write calls may steal the global InBuffer, - * so ensure we're using the correct one each time around the loop. - */ - - while((fsp = initial_break_processing(dev, inode, file_id)) && - OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { - - inbuf = get_InBuffer(); - outbuf = get_OutBuffer(); - - 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_BAD_SIG) { - DEBUG( 0, ("oplock_break: bad signature from client\n" )); - 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, file_id = %lu).\n", - (unsigned int)dev, (double)inode, file_id)); + if (msg_type == MSG_SMB_BREAK_REQUEST) { + fsp->sent_oplock_break = break_to_level2 ? + LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT; + } else { + /* Async level2 request, don't send a reply */ + fsp->sent_oplock_break = ASYNC_LEVEL_II_BREAK_SENT; + } + msg->pid = src; + ADD_TO_ARRAY(NULL, struct share_mode_entry, *msg, + &fsp->pending_break_messages, + &fsp->num_pending_break_messages); - break; - } + if (fsp->oplock_timeout != NULL) { + DEBUG(0, ("Logic problem -- have an oplock event hanging " + "around\n")); + } - /* - * 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. - */ + fsp->oplock_timeout = + add_timed_event(NULL, + timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0), + "oplock_timeout_handler", + oplock_timeout_handler, fsp); - process_smb(inbuf, outbuf); + if (fsp->oplock_timeout == NULL) { + DEBUG(0, ("Could not add oplock timeout handler\n")); + } +} - /* - * Die if we go over the time limit. - */ +static void process_kernel_oplock_break(int msg_type, struct process_id src, + void *buf, size_t len) +{ + struct kernel_oplock_message *msg = buf; + files_struct *fsp; + char *break_msg; + BOOL sign_state; - 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, file_id = %lu).\n", - (unsigned int)dev, (double)inode, file_id ); - } - oplock_timeout = True; - break; - } + if (buf == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; } - /* - * Go back to being the user who requested the oplock - * break. - */ - if((saved_user_conn != NULL) && (saved_vuid != UID_FIELD_INVALID) && !change_to_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"); + if (len != sizeof(*msg)) { + DEBUG(0, ("Got invalid msg len %d\n", (int)len)); + return; } - /* Including the directory. */ - vfs_ChDir(saved_fsp_conn,saved_dir); - - /* Restore the chain fnum. */ - file_chain_restore(); + DEBUG(10, ("Got kernel oplock break message from pid %d: %d/%d/%d\n", + (int)procid_to_pid(&src), (int)msg->dev, (int)msg->inode, + (int)msg->file_id)); - /* Free the buffers we've been using to recurse. */ - /* Free must be done before set.. */ - free_InBuffer(inbuf); - free_OutBuffer(outbuf); + fsp = initial_break_processing(msg->dev, msg->inode, msg->file_id); - /* Restore the global In/Out buffers. */ - set_InBuffer(saved_inbuf); - set_OutBuffer(saved_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, file_id)) && - 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); -#if FASCIST_OPLOCK_BACKOFF - global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */ -#endif + if (fsp == NULL) { + DEBUG(3, ("Got a kernel oplock break message for a file " + "I don't know about\n")); + return; } - /* - * 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"); + 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; } - /* 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(); + break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE); + if (break_msg == NULL) { + exit_server("Could not talloc break_msg\n"); } - /* We know we have no saved errors here. */ - set_saved_error_triple(0, 0, NT_STATUS_OK); + /* Save the server smb signing state. */ + sign_state = srv_oplock_set_signing(False); - if( DEBUGLVL( 3 ) ) { - dbgtext( "oplock_break: returning success for " ); - dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, file_id ); - dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open ); + show_msg(break_msg); + if (!send_smb(smbd_server_fd(), break_msg)) { + exit_server("oplock_break: send_smb failed."); } - return True; -} - -/**************************************************************************** - 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. - This function is called with no share locks held. -****************************************************************************/ + /* Restore the sign state to what it was. */ + srv_oplock_set_signing(sign_state); -BOOL request_oplock_break(share_mode_entry *share_entry) -{ - 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; - SMB_DEV_T dev = share_entry->dev; - SMB_INO_T inode = share_entry->inode; - unsigned long file_id = share_entry->share_file_id; - uint16 break_cmd_type; - - 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; - } + talloc_free(break_msg); - DEBUG(5,("request_oplock_break: breaking our own oplock\n")); + fsp->sent_oplock_break = BREAK_TO_NONE_SENT; +} -#if 1 /* JRA PARANOIA TEST.... */ - { - files_struct *fsp = file_find_dif(dev, inode, file_id); - if (!fsp) { - DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \ -dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n", - (unsigned int)dev, (double)inode, file_id )); - smb_panic("request_oplock_break: no fsp found for our own oplock\n"); - } - } -#endif /* END JRA PARANOIA TEST... */ +void reply_to_oplock_break_requests(files_struct *fsp) +{ + int i; - /* Call oplock break direct. */ - return oplock_break(dev, inode, file_id, True); + for (i=0; i<fsp->num_pending_break_messages; i++) { + struct share_mode_entry *msg = &fsp->pending_break_messages[i]; + message_send_pid(msg->pid, MSG_SMB_BREAK_RESPONSE, + msg, sizeof(*msg), 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)) { - break_cmd_type = LEVEL_II_OPLOCK_BREAK_CMD; - } else { - break_cmd_type = OPLOCK_BREAK_CMD; + SAFE_FREE(fsp->pending_break_messages); + fsp->num_pending_break_messages = 0; + if (fsp->oplock_timeout != NULL) { + talloc_free(fsp->oplock_timeout); + fsp->oplock_timeout = NULL; } + return; +} - SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,break_cmd_type); - memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid)); - memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev)); - memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode)); - memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id)); - - /* 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 synchronous oplock break message to " ); - dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n", - (unsigned int)dev, (double)inode, file_id ); - } +static void process_oplock_break_response(int msg_type, struct process_id src, + void *buf, size_t len) +{ + struct share_mode_entry *msg = buf; - if(sys_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, file_id = %lu\n", - (unsigned int)dev, (double)inode, file_id ); - dbgtext( "Error was %s\n", strerror(errno) ); - } - return False; + if (buf == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; } - /* - * 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; - - if(receive_local_message(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, file_id = %lu\n", - (unsigned int)dev, (double)inode, file_id ); - } - - /* - * 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, file_id = %lu\n", - (unsigned int)dev, (double)inode, file_id ); - 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 (ie. the one we sent with the CMD_REPLY flag OR'ed in). - */ - if((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & CMD_REPLY) && - ((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & ~CMD_REPLY) == break_cmd_type) && - (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); + if (len != sizeof(*msg)) { + DEBUG(0, ("Got invalid msg len %d\n", (int)len)); + return; } - DEBUG(3,("request_oplock_break: broke oplock.\n")); + DEBUG(10, ("Got oplock break response from pid %d: %d/%d/%d mid %d\n", + (int)procid_to_pid(&src), (int)msg->dev, (int)msg->inode, + (int)msg->share_file_id, (int)msg->op_mid)); - return True; + /* 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(int msg_type, struct process_id src, + void *buf, size_t len) { - DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name)); - - if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fh->fd != -1)) { - /* Try and break the oplock. */ - if (oplock_break(fsp->dev, fsp->inode, fsp->file_id, True)) { - if(file_find_fsp(fsp) == NULL) /* Did the oplock break close the file ? */ - return True; - } + struct share_mode_entry *msg = buf; + + if (buf == NULL) { + DEBUG(0, ("Got NULL buffer\n")); + return; } - return False; -} - -/**************************************************************************** - Send an asynchronous oplock break message to another smbd process. -****************************************************************************/ - -static BOOL request_remote_level2_async_oplock_break(share_mode_entry *share_entry) -{ - char op_break_msg[OPLOCK_BREAK_MSG_LEN]; - struct sockaddr_in addr_out; - pid_t pid = sys_getpid(); - SMB_DEV_T dev = share_entry->dev; - SMB_INO_T inode = share_entry->inode; - unsigned long file_id = share_entry->share_file_id; - - /* We need to send a ASYNC_LEVEL_II_OPLOCK_BREAK_CMD message to the port in the share mode entry. */ - - SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,ASYNC_LEVEL_II_OPLOCK_BREAK_CMD); - memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid)); - memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev)); - memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode)); - memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id)); - - /* 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_remote_level2_async_oplock_break: sending an asynchronous oplock break message to "); - dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n", - (unsigned int)dev, (double)inode, file_id ); + if (len != sizeof(*msg)) { + DEBUG(0, ("Got invalid msg len %d\n", (int)len)); + return; } - if(sys_sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0, - (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) { - if( DEBUGLVL( 0 ) ) { - dbgtext( "request_remote_level2_async_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, file_id = %lu\n", - (unsigned int)dev, (double)inode, file_id ); - dbgtext( "Error was %s\n", strerror(errno) ); - } - return False; - } + DEBUG(10, ("Got open retry msg from pid %d: %d/%d mid %d\n", + (int)procid_to_pid(&src), (int)msg->dev, (int)msg->inode, + (int)msg->op_mid)); - DEBUG(3,("request_remote_level2_async_oplock_break: sent async break message to level II entry.\n")); - return True; + 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 num_share_modes = 0; int i; - BOOL dummy; + struct share_mode_lock *lck; /* * If this file is level II oplocked then we need @@ -1247,125 +627,71 @@ 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->dev, fsp->inode, 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 )); } - num_share_modes = get_share_modes(fsp->dev, fsp->inode, &share_list, - &dummy); - DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n", - num_share_modes )); + lck->num_share_modes )); + + if (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) { + /* See if someone else has already downgraded us, then we + don't have to do anything */ + for (i=0; i<lck->num_share_modes; i++) { + struct share_mode_entry *e = &lck->share_modes[i]; + if ((e->op_type == NO_OPLOCK) && + (e->share_file_id == fsp->file_id) && + (e->dev == fsp->dev) && + (e->inode == fsp->inode) && + (procid_is_me(&e->pid))) { + /* We're done */ + fsp->oplock_type = NO_OPLOCK; + talloc_free(lck); + return; + } + } + } - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &share_list[i]; + for(i = 0; i < lck->num_share_modes; i++) { + struct share_mode_entry *share_entry = &lck->share_modes[i]; /* - * 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 + * 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) || + (share_entry->op_type == FAKE_LEVEL_II_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_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id); - - /* 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")); - - oplock_break_level2(new_fsp, True); - - } else { - - /* - * This is a remote file and so we send an asynchronous - * message. - */ - - DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock (async).\n")); - request_remote_level2_async_oplock_break(share_entry); - } - } - - SAFE_FREE(share_list); - unlock_share_entry_fsp(fsp); - - /* 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"); - } -} - -/**************************************************************************** - Send a 'retry your open' message to a process with a deferred open entry. -****************************************************************************/ - -BOOL send_deferred_open_retry_message(deferred_open_entry *entry) -{ - char de_msg[DEFERRED_OPEN_MSG_LEN]; - struct sockaddr_in addr_out; - pid_t pid = sys_getpid(); - - memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN); - SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD); - memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid)); - memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev)); - memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode)); - memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid)); - - /* 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( entry->port ); - addr_out.sin_family = AF_INET; - - if( DEBUGLVL( 3 ) ) { - dbgtext( "send_deferred_open_retry_message: sending a message to "); - dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port ); - dbgtext( "for dev = %x, inode = %.0f, mid = %u\n", - (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid ); + message_send_pid(share_entry->pid, MSG_SMB_ASYNC_LEVEL2_BREAK, + share_entry, sizeof(*share_entry), True); } - if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0, - (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) { - if( DEBUGLVL( 0 ) ) { - dbgtext( "send_deferred_open_retry_message: failed sending a message to "); - dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port ); - dbgtext( "for dev = %x, inode = %.0f, mid = %u\n", - (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid ); - dbgtext( "Error was %s\n", strerror(errno) ); - } - return False; - } - return True; + remove_all_share_oplocks(lck, fsp); + talloc_free(lck); } /**************************************************************************** @@ -1374,30 +700,18 @@ BOOL send_deferred_open_retry_message(deferred_open_entry *entry) 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); + message_register(MSG_SMB_BREAK_REQUEST, + process_oplock_break_message); + message_register(MSG_SMB_ASYNC_LEVEL2_BREAK, + process_oplock_break_message); + message_register(MSG_SMB_BREAK_RESPONSE, + process_oplock_break_response); + message_register(MSG_SMB_KERNEL_BREAK, + process_kernel_oplock_break); + message_register(MSG_SMB_OPEN_RETRY, + process_open_retry_message); if (lp_kernel_oplocks()) { #if HAVE_KERNEL_OPLOCKS_IRIX @@ -1407,8 +721,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; } diff --git a/source3/smbd/oplock_irix.c b/source3/smbd/oplock_irix.c index f4405a021e..f49aa297e4 100644 --- a/source3/smbd/oplock_irix.c +++ b/source3/smbd/oplock_irix.c @@ -86,7 +86,7 @@ Disabling kernel oplock support.\n", strerror(errno) )); * oplock break protocol. ****************************************************************************/ -static BOOL irix_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len) +static files_struct *irix_oplock_receive_message(fd_set *fds) { extern int smb_read_error; oplock_stat_t os; @@ -102,7 +102,7 @@ static BOOL irix_oplock_receive_message(fd_set *fds, char *buffer, int buffer_le DEBUG(0,("irix_oplock_receive_message: read of kernel notification failed. \ Error was %s.\n", strerror(errno) )); smb_read_error = READ_ERROR; - return False; + return NULL; } /* @@ -122,7 +122,7 @@ Error was %s.\n", strerror(errno) )); return True; } smb_read_error = READ_ERROR; - return False; + return NULL; } /* @@ -138,24 +138,8 @@ Error was %s.\n", strerror(errno) )); DEBUG(5,("irix_oplock_receive_message: kernel oplock break request received for \ dev = %x, inode = %.0f\n, file_id = %ul", (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id )); - - /* - * Create a kernel oplock break message. - */ - - /* Setup the message header */ - SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN); - SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0); - - buffer += OPBRK_CMD_HEADER_LEN; - - SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD); - - memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev)); - memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode)); - memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id)); - - return True; + + return fsp; } /**************************************************************************** @@ -215,30 +199,6 @@ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev, } /**************************************************************************** - Parse a kernel oplock message. -****************************************************************************/ - -static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len, - SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id) -{ - /* Ensure that the msg length is correct. */ - if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) { - DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n", - msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN)); - return False; - } - - memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode)); - memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev)); - memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id)); - - DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %ul\n", - (unsigned int)*dev, (double)*inode, *file_id)); - - return True; -} - -/**************************************************************************** Set *maxfd to include oplock read pipe. ****************************************************************************/ @@ -274,7 +234,6 @@ struct kernel_oplocks *irix_init_kernel_oplocks(void) koplocks.receive_message = irix_oplock_receive_message; koplocks.set_oplock = irix_set_kernel_oplock; koplocks.release_oplock = irix_release_kernel_oplock; - koplocks.parse_message = irix_kernel_oplock_parse; koplocks.msg_waiting = irix_oplock_msg_waiting; koplocks.notification_fd = oplock_pipe_read; diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c index 477832c6e8..ab0c08f7fc 100644 --- a/source3/smbd/oplock_linux.c +++ b/source3/smbd/oplock_linux.c @@ -128,10 +128,10 @@ static int linux_setlease(int fd, int leasetype) * oplock break protocol. ****************************************************************************/ -static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len) +static files_struct *linux_oplock_receive_message(fd_set *fds) { int fd; - struct files_struct *fsp; + files_struct *fsp; BlockSignals(True, RT_SIGNAL_LEASE); fd = fd_pending_array[0]; @@ -145,32 +145,7 @@ static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_l /* now we can receive more signals */ BlockSignals(False, RT_SIGNAL_LEASE); - if (fsp == NULL) { - DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd)); - return False; - } - - DEBUG(3,("linux_oplock_receive_message: kernel oplock break request received for \ -dev = %x, inode = %.0f fd = %d, fileid = %lu \n", (unsigned int)fsp->dev, (double)fsp->inode, - fd, fsp->file_id)); - - /* - * Create a kernel oplock break message. - */ - - /* Setup the message header */ - SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN); - SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0); - - buffer += OPBRK_CMD_HEADER_LEN; - - SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD); - - memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev)); - memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode)); - memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id)); - - return True; + return fsp; } /**************************************************************************** @@ -224,30 +199,6 @@ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev, } /**************************************************************************** - Parse a kernel oplock message. -****************************************************************************/ - -static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode, - SMB_DEV_T *dev, unsigned long *file_id) -{ - /* Ensure that the msg length is correct. */ - if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) { - DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %lu).\n", - msg_len, (unsigned long)KERNEL_OPLOCK_BREAK_MSG_LEN)); - return False; - } - - memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode)); - memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev)); - memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id)); - - DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n", - (unsigned int)*dev, (double)*inode, *file_id)); - - return True; -} - -/**************************************************************************** See if a oplock message is waiting. ****************************************************************************/ @@ -299,7 +250,6 @@ struct kernel_oplocks *linux_init_kernel_oplocks(void) koplocks.receive_message = linux_oplock_receive_message; koplocks.set_oplock = linux_set_kernel_oplock; koplocks.release_oplock = linux_release_kernel_oplock; - koplocks.parse_message = linux_kernel_oplock_parse; koplocks.msg_waiting = linux_oplock_msg_waiting; koplocks.notification_fd = -1; diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 8f9cc52882..0b7b94cce2 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. process incoming packets - main loop Copyright (C) Andrew Tridgell 1992-1998 + 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 @@ -44,12 +45,11 @@ int max_send = BUFFER_SIZE; int max_recv = BUFFER_SIZE; extern int last_message; -extern int global_oplock_break; extern userdom_struct current_user_info; extern int smb_read_error; SIG_ATOMIC_T reload_after_sighup = 0; SIG_ATOMIC_T got_sig_term = 0; -BOOL global_machine_password_needs_changing = False; +extern BOOL global_machine_password_needs_changing; extern int max_send; /**************************************************************************** @@ -66,106 +66,72 @@ uint16 get_current_mid(void) for processing. ****************************************************************************/ -static struct pending_message_list *smb_oplock_queue; -static struct pending_message_list *smb_sharing_violation_queue; - -enum q_type { OPLOCK_QUEUE, SHARE_VIOLATION_QUEUE }; - -/**************************************************************************** - Free up a message. -****************************************************************************/ - -static void free_queued_message(struct pending_message_list *msg) -{ - data_blob_free(&msg->buf); - data_blob_free(&msg->private_data); - SAFE_FREE(msg); -} +static struct pending_message_list *deferred_open_queue; /**************************************************************************** Function to push a message onto the tail of a linked list of smb messages ready for processing. ****************************************************************************/ -static BOOL push_queued_message(enum q_type qt, char *buf, int msg_len, struct timeval *ptv, char *private_data, size_t private_len) +static BOOL push_queued_message(char *buf, int msg_len, + struct timeval request_time, + struct timeval end_time, + char *private_data, size_t private_len) { struct pending_message_list *tmp_msg; - struct pending_message_list *msg = SMB_MALLOC_P(struct pending_message_list); + struct pending_message_list *msg; + + msg = TALLOC_ZERO_P(NULL, struct pending_message_list); if(msg == NULL) { DEBUG(0,("push_message: malloc fail (1)\n")); return False; } - memset(msg,'\0',sizeof(*msg)); - - msg->buf = data_blob(buf, msg_len); + msg->buf = data_blob_talloc(msg, buf, msg_len); if(msg->buf.data == NULL) { DEBUG(0,("push_message: malloc fail (2)\n")); - SAFE_FREE(msg); + talloc_free(msg); return False; } - if (ptv) { - msg->msg_time = *ptv; - } + msg->request_time = request_time; + msg->end_time = end_time; if (private_data) { - msg->private_data = data_blob(private_data, private_len); + msg->private_data = data_blob_talloc(msg, private_data, + private_len); if (msg->private_data.data == NULL) { DEBUG(0,("push_message: malloc fail (3)\n")); - data_blob_free(&msg->buf); - SAFE_FREE(msg); + talloc_free(msg); return False; } } - if (qt == OPLOCK_QUEUE) { - DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg); - } else { - DLIST_ADD_END(smb_sharing_violation_queue, msg, tmp_msg); - } + DLIST_ADD_END(deferred_open_queue, msg, tmp_msg); - DEBUG(10,("push_message: pushed message length %u on queue %s\n", - (unsigned int)msg_len, - qt == OPLOCK_QUEUE ? "smb_oplock_queue" : "smb_sharing_violation_queue" )); + DEBUG(10,("push_message: pushed message length %u on " + "deferred_open_queue\n", (unsigned int)msg_len)); return True; } /**************************************************************************** - Function to push an oplock smb message onto a linked list of local smb messages ready - for processing. -****************************************************************************/ - -BOOL push_oplock_pending_smb_message(char *buf, int msg_len) -{ - BOOL ret = push_queued_message(OPLOCK_QUEUE, buf, msg_len, NULL, NULL, 0); - if (ret) { - /* Push the MID of this packet on the signing queue. */ - srv_defer_sign_response(SVAL(buf,smb_mid)); - } - return ret; -} - -/**************************************************************************** Function to delete a sharing violation open message by mid. ****************************************************************************/ -void remove_sharing_violation_open_smb_message(uint16 mid) +void remove_deferred_open_smb_message(uint16 mid) { struct pending_message_list *pml; - if (!lp_defer_sharing_violations()) { - return; - } - - for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + for (pml = deferred_open_queue; pml; pml = pml->next) { if (mid == SVAL(pml->buf.data,smb_mid)) { - DEBUG(10,("remove_sharing_violation_open_smb_message: deleting mid %u len %u\n", - (unsigned int)mid, (unsigned int)pml->buf.length )); - DLIST_REMOVE(smb_sharing_violation_queue, pml); - free_queued_message(pml); + DEBUG(10,("remove_sharing_violation_open_smb_message: " + "deleting mid %u len %u\n", + (unsigned int)mid, + (unsigned int)pml->buf.length )); + DLIST_REMOVE(deferred_open_queue, pml); + talloc_free(pml); return; } } @@ -176,30 +142,26 @@ void remove_sharing_violation_open_smb_message(uint16 mid) schedule it for immediate processing. ****************************************************************************/ -void schedule_sharing_violation_open_smb_message(uint16 mid) +void schedule_deferred_open_smb_message(uint16 mid) { struct pending_message_list *pml; int i = 0; - if (!lp_defer_sharing_violations()) { - return; - } - - for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + for (pml = deferred_open_queue; pml; pml = pml->next) { uint16 msg_mid = SVAL(pml->buf.data,smb_mid); - DEBUG(10,("schedule_sharing_violation_open_smb_message: [%d] msg_mid = %u\n", i++, + DEBUG(10,("schedule_deferred_open_smb_message: [%d] msg_mid = %u\n", i++, (unsigned int)msg_mid )); if (mid == msg_mid) { - DEBUG(10,("schedule_sharing_violation_open_smb_message: scheduling mid %u\n", + DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n", mid )); - pml->msg_time.tv_sec = 0; - pml->msg_time.tv_usec = 0; - DLIST_PROMOTE(smb_sharing_violation_queue, pml); + pml->end_time.tv_sec = 0; + pml->end_time.tv_usec = 0; + DLIST_PROMOTE(deferred_open_queue, pml); return; } } - DEBUG(10,("schedule_sharing_violation_open_smb_message: failed to find message mid %u\n", + DEBUG(10,("schedule_deferred_open_smb_message: failed to find message mid %u\n", mid )); } @@ -211,13 +173,9 @@ BOOL open_was_deferred(uint16 mid) { struct pending_message_list *pml; - if (!lp_defer_sharing_violations()) { - return False; - } - - for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + for (pml = deferred_open_queue; pml; pml = pml->next) { if (SVAL(pml->buf.data,smb_mid) == mid) { - set_saved_error_triple(SMB_SUCCESS, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); return True; } } @@ -232,11 +190,7 @@ struct pending_message_list *get_open_deferred_message(uint16 mid) { struct pending_message_list *pml; - if (!lp_defer_sharing_violations()) { - return NULL; - } - - for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + for (pml = deferred_open_queue; pml; pml = pml->next) { if (SVAL(pml->buf.data,smb_mid) == mid) { return pml; } @@ -245,57 +199,216 @@ struct pending_message_list *get_open_deferred_message(uint16 mid) } /**************************************************************************** - Function to push a sharing violation open smb message onto a linked list of local smb messages ready - for processing. We must use current_inbuf here not Inbuf in case we're in a chained message set. + Function to push a deferred open smb message onto a linked list of local smb + messages ready for processing. +****************************************************************************/ + +BOOL push_deferred_smb_message(uint16 mid, + struct timeval request_time, + struct timeval timeout, + char *private_data, size_t priv_len) +{ + struct timeval end_time; + + end_time = timeval_sum(&request_time, &timeout); + + DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u " + "timeout time [%u.%06u]\n", + (unsigned int) smb_len(current_inbuf)+4, (unsigned int)mid, + (unsigned int)end_time.tv_sec, + (unsigned int)end_time.tv_usec)); + + return push_queued_message(current_inbuf, smb_len(current_inbuf)+4, + request_time, end_time, + private_data, priv_len); +} + +static struct timed_event *timed_events; + +struct timed_event { + struct timed_event *next, *prev; + struct timeval when; + const char *event_name; + void (*handler)(struct timed_event *te, + const struct timeval *now, + void *private_data); + void *private_data; +}; + +static int timed_event_destructor(void *p) +{ + struct timed_event *te = talloc_get_type_abort(p, struct timed_event); + DEBUG(10, ("Destroying timed event %lx \"%s\"\n", (unsigned long)te, + te->event_name)); + DLIST_REMOVE(timed_events, te); + return 0; +} + +/**************************************************************************** + Schedule a function for future calling, cancel with talloc_free(). + It's the responsibility of the handler to call talloc_free() on the event + handed to it. ****************************************************************************/ -BOOL push_sharing_violation_open_smb_message(struct timeval *ptv, char *private_data, size_t priv_len) +struct timed_event *add_timed_event(TALLOC_CTX *mem_ctx, + struct timeval when, + const char *event_name, + void (*handler)(struct timed_event *te, + const struct timeval *now, + void *private_data), + void *private_data) { - uint16 mid = SVAL(current_inbuf,smb_mid); - struct timeval tv; - SMB_BIG_INT tdif; + struct timed_event *te, *last_te, *cur_te; - if (!lp_defer_sharing_violations()) { - return True; + te = TALLOC_P(mem_ctx, struct timed_event); + if (te == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; } - tv = *ptv; - tdif = tv.tv_sec; - tdif *= 1000000; - tdif += tv.tv_usec; + te->when = when; + te->event_name = event_name; + te->handler = handler; + te->private_data = private_data; + + /* keep the list ordered */ + last_te = NULL; + for (cur_te = timed_events; cur_te; cur_te = cur_te->next) { + /* if the new event comes before the current one break */ + if (!timeval_is_zero(&cur_te->when) && + timeval_compare(&te->when, &cur_te->when) < 0) { + break; + } + last_te = cur_te; + } - /* Add on the timeout. */ - tdif += SHARING_VIOLATION_USEC_WAIT; - - tv.tv_sec = tdif / 1000000; - tv.tv_usec = tdif % 1000000; - - DEBUG(10,("push_sharing_violation_open_smb_message: pushing message len %u mid %u\ - timeout time [%u.%06u]\n", (unsigned int) smb_len(current_inbuf)+4, (unsigned int)mid, - (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec)); + DLIST_ADD_AFTER(timed_events, te, last_te); + talloc_set_destructor(te, timed_event_destructor); - return push_queued_message(SHARE_VIOLATION_QUEUE, current_inbuf, - smb_len(current_inbuf)+4, &tv, private_data, priv_len); + DEBUG(10, ("Added timed event \"%s\": %lx\n", event_name, + (unsigned long)te)); + return te; } +static void run_events(void) +{ + struct timeval now; + + if (timed_events == NULL) { + /* No syscall if there are no events */ + DEBUG(10, ("run_events: No events\n")); + return; + } + + GetTimeOfDay(&now); + + if (timeval_compare(&now, &timed_events->when) < 0) { + /* Nothing to do yet */ + DEBUG(10, ("run_events: Nothing to do\n")); + return; + } + + DEBUG(10, ("Running event \"%s\" %lx\n", timed_events->event_name, + (unsigned long)timed_events)); + + timed_events->handler(timed_events, &now, timed_events->private_data); + return; +} + +struct timeval timed_events_timeout(void) +{ + struct timeval now, timeout; + + if (timed_events == NULL) { + return timeval_set(SMBD_SELECT_TIMEOUT, 0); + } + + now = timeval_current(); + timeout = timeval_until(&now, &timed_events->when); + + DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)timeout.tv_sec, + (int)timeout.tv_usec)); + + return timeout; +} + +struct idle_event { + struct timed_event *te; + struct timeval interval; + BOOL (*handler)(const struct timeval *now, void *private_data); + void *private_data; +}; + +static void idle_event_handler(struct timed_event *te, + const struct timeval *now, + void *private_data) +{ + struct idle_event *event = + talloc_get_type_abort(private_data, struct idle_event); + + talloc_free(event->te); + + if (!event->handler(now, event->private_data)) { + /* Don't repeat, delete ourselves */ + talloc_free(event); + return; + } + + event->te = add_timed_event(event, timeval_sum(now, &event->interval), + "idle_event_handler", + idle_event_handler, event); + + /* We can't do much but fail here. */ + SMB_ASSERT(event->te != NULL); +} + +struct idle_event *add_idle_event(TALLOC_CTX *mem_ctx, + struct timeval interval, + BOOL (*handler)(const struct timeval *now, + void *private_data), + void *private_data) +{ + struct idle_event *result; + struct timeval now = timeval_current(); + + result = TALLOC_P(mem_ctx, struct idle_event); + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + result->interval = interval; + result->handler = handler; + result->private_data = private_data; + + result->te = add_timed_event(result, timeval_sum(&now, &interval), + "idle_event_handler", + idle_event_handler, result); + if (result->te == NULL) { + DEBUG(0, ("add_timed_event failed\n")); + talloc_free(result); + return NULL; + } + + return result; +} + /**************************************************************************** - Do all async processing in here. This includes UDB oplock messages, kernel - oplock messages, change notify events etc. + Do all async processing in here. This includes kernel oplock messages, change + notify events etc. ****************************************************************************/ -static void async_processing(char *buffer, int buffer_len) +static void async_processing(void) { DEBUG(10,("async_processing: Doing async processing.\n")); process_aio_queue(); - /* check for oplock messages (both UDP and kernel) */ - if (receive_local_message(buffer, buffer_len, 1)) { - process_local_message(buffer, buffer_len); - } + process_kernel_oplocks(); - /* Do the aio check again after receive_local_message as it does a select - and may have eaten our signal. */ + /* Do the aio check again after receive_local_message as it does a + select and may have eaten our signal. */ + /* Is this till true? -- vl */ process_aio_queue(); if (got_sig_term) { @@ -339,17 +452,17 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) { fd_set fds; int selrtn; - struct timeval to; - struct timeval *pto; + struct timeval to = timeval_set(SMBD_SELECT_TIMEOUT, 0); int maxfd; smb_read_error = 0; again: - to.tv_sec = timeout / 1000; - to.tv_usec = (timeout % 1000) * 1000; - pto = timeout > 0 ? &to : NULL; + if (timeout >= 0) { + to.tv_sec = timeout / 1000; + to.tv_usec = (timeout % 1000) * 1000; + } /* * Note that this call must be before processing any SMB @@ -359,37 +472,21 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) message_dispatch(); /* - * Check to see if we already have a message on the smb queue. - * If so - copy and return it. - */ - if(smb_oplock_queue != NULL) { - struct pending_message_list *msg = smb_oplock_queue; - memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length)); - - /* Free the message we just copied. */ - DLIST_REMOVE(smb_oplock_queue, msg); - free_queued_message(msg); - - DEBUG(5,("receive_message_or_smb: returning queued smb message.\n")); - return True; - } - - /* * Check to see if we already have a message on the deferred open queue * and it's time to schedule. */ - if(smb_sharing_violation_queue != NULL) { + if(deferred_open_queue != NULL) { BOOL pop_message = False; - struct pending_message_list *msg = smb_sharing_violation_queue; + struct pending_message_list *msg = deferred_open_queue; - if (msg->msg_time.tv_sec == 0 && msg->msg_time.tv_usec == 0) { + if (timeval_is_zero(&msg->end_time)) { pop_message = True; } else { struct timeval tv; SMB_BIG_INT tdif; GetTimeOfDay(&tv); - tdif = usec_time_diff(&msg->msg_time, &tv); + tdif = usec_time_diff(&msg->end_time, &tv); if (tdif <= 0) { /* Timed out. Schedule...*/ pop_message = True; @@ -398,9 +495,8 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) /* Make a more accurate select timeout. */ to.tv_sec = tdif / 1000000; to.tv_usec = tdif % 1000000; - pto = &to; DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n", - (unsigned int)pto->tv_sec, (unsigned int)pto->tv_usec )); + (unsigned int)to.tv_sec, (unsigned int)to.tv_usec )); } } @@ -431,7 +527,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) if (oplock_message_waiting(&fds)) { DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n")); - async_processing(buffer, buffer_len); + async_processing(); /* * After async processing we must go and do the select again, as * the state of the flag in fds for the server file descriptor is @@ -439,18 +535,26 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) */ goto again; } + + { + struct timeval tmp = timed_events_timeout(); + to = timeval_min(&to, &tmp); + if (timeval_is_zero(&to)) { + return True; + } + } FD_SET(smbd_server_fd(),&fds); maxfd = setup_oplock_select_set(&fds); - selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,pto); + selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,&to); /* if we get EINTR then maybe we have received an oplock signal - treat this as select returning 1. This is ugly, but is the best we can do until the oplock code knows more about signals */ if (selrtn == -1 && errno == EINTR) { - async_processing(buffer, buffer_len); + async_processing(); /* * After async processing we must go and do the select again, as * the state of the flag in fds for the server file descriptor is @@ -479,7 +583,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) */ if (oplock_message_waiting(&fds)) { - async_processing(buffer, buffer_len); + async_processing(); /* * After async processing we must go and do the select again, as * the state of the flag in fds for the server file descriptor is @@ -518,8 +622,6 @@ BOOL receive_next_smb(char *inbuf, int bufsize, int timeout) void respond_to_all_remaining_local_messages(void) { - char buffer[1024]; - /* * Assert we have no exclusive open oplocks. */ @@ -530,15 +632,7 @@ void respond_to_all_remaining_local_messages(void) return; } - /* - * Keep doing receive_local_message with a 1 ms timeout until - * we have no more messages. - */ - - while(receive_local_message(buffer, sizeof(buffer), 1)) { - /* Deal with oplock break requests from other smbd's. */ - process_local_message(buffer, sizeof(buffer)); - } + process_kernel_oplocks(); return; } @@ -556,8 +650,7 @@ force write permissions on print services. #define TIME_INIT (1<<2) #define CAN_IPC (1<<3) #define AS_GUEST (1<<5) -#define QUEUE_IN_OPLOCK (1<<6) -#define DO_CHDIR (1<<7) +#define DO_CHDIR (1<<6) /* define a list of possible SMB messages and their corresponding @@ -572,19 +665,19 @@ static const struct smb_message_struct { /* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, /* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, -/* 0x02 */ { "SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK }, +/* 0x02 */ { "SMBopen",reply_open,AS_USER }, /* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, /* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC }, /* 0x05 */ { "SMBflush",reply_flush,AS_USER}, -/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK}, -/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK}, +/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE }, +/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE }, /* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER}, /* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE}, /* 0x0a */ { "SMBread",reply_read,AS_USER}, /* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC }, /* 0x0c */ { "SMBlock",reply_lock,AS_USER}, /* 0x0d */ { "SMBunlock",reply_unlock,AS_USER}, -/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK }, +/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER }, /* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, /* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER}, /* 0x11 */ { "SMBexit",reply_exit,DO_CHDIR}, @@ -611,11 +704,11 @@ static const struct smb_message_struct { /* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC}, /* 0x27 */ { "SMBioctl",reply_ioctl,0}, /* 0x28 */ { "SMBioctls",NULL,AS_USER}, -/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK }, -/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK }, +/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE }, +/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE }, /* 0x2b */ { "SMBecho",reply_echo,0}, /* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, -/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, +/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC }, /* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC }, /* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC }, /* 0x30 */ { NULL, NULL, 0 }, @@ -730,12 +823,12 @@ static const struct smb_message_struct { /* 0x9d */ { NULL, NULL, 0 }, /* 0x9e */ { NULL, NULL, 0 }, /* 0x9f */ { NULL, NULL, 0 }, -/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK}, +/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC }, /* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC }, -/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, +/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC }, /* 0xa3 */ { NULL, NULL, 0 }, /* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 }, -/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK }, +/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER | NEED_WRITE }, /* 0xa6 */ { NULL, NULL, 0 }, /* 0xa7 */ { NULL, NULL, 0 }, /* 0xa8 */ { NULL, NULL, 0 }, @@ -762,7 +855,7 @@ static const struct smb_message_struct { /* 0xbd */ { NULL, NULL, 0 }, /* 0xbe */ { NULL, NULL, 0 }, /* 0xbf */ { NULL, NULL, 0 }, -/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK }, +/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER}, /* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER}, /* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER}, /* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER}, @@ -871,7 +964,7 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize pid = sys_getpid(); errno = 0; - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); last_message = type; @@ -900,19 +993,6 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n",smb_fn_name(type),(int)pid,(unsigned long)conn)); smb_dump(smb_fn_name(type), 1, inbuf, size); - if(global_oplock_break) { - if(flags & QUEUE_IN_OPLOCK) { - /* - * Queue this message as we are the process of an oplock break. - */ - - DEBUG( 2, ( "switch_message: queueing message due to being in " ) ); - DEBUGADD( 2, ( "oplock break state.\n" ) ); - - push_oplock_pending_smb_message( inbuf, size ); - return -1; - } - } /* Ensure this value is replaced in the incoming packet. */ SSVAL(inbuf,smb_uid,session_tag); @@ -1289,6 +1369,7 @@ static int setup_select_timeout(void) select_timeout *= 1000; t = change_notify_timeout(); + DEBUG(10, ("change_notify_timeout: %d\n", t)); if (t != -1) select_timeout = MIN(select_timeout, t*1000); @@ -1302,7 +1383,7 @@ static int setup_select_timeout(void) Check if services need reloading. ****************************************************************************/ -void check_reload(int t) +void check_reload(time_t t) { static pid_t mypid = 0; static time_t last_smb_conf_reload_time = 0; @@ -1644,6 +1725,8 @@ void smbd_process(void) num_smbs = 0; /* Reset smb counter. */ } + run_events(); + #if defined(DEVELOPER) clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size); #endif diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 5572f47e42..ba22a56cfb 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -30,7 +30,6 @@ extern enum protocol_types Protocol; extern int max_send; extern int max_recv; -extern int global_oplock_break; unsigned int smb_echo_count = 0; extern uint32 global_client_caps; @@ -1779,7 +1778,7 @@ static NTSTATUS can_rename(connection_struct *conn, char *fname, uint16 dirtype, } /* We need a better way to return NT status codes from open... */ - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); fsp = open_file_ntcreate(conn, fname, pst, DELETE_ACCESS, @@ -1791,12 +1790,12 @@ static NTSTATUS can_rename(connection_struct *conn, char *fname, uint16 dirtype, NULL); if (!fsp) { - NTSTATUS ret; - if (get_saved_error_triple(NULL, NULL, &ret)) { - set_saved_error_triple(0, 0, NT_STATUS_OK); + NTSTATUS ret = get_saved_ntstatus(); + if (!NT_STATUS_IS_OK(ret)) { + set_saved_ntstatus(NT_STATUS_OK); return ret; } - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); return NT_STATUS_ACCESS_DENIED; } close_file(fsp,False); @@ -1860,7 +1859,7 @@ NTSTATUS can_delete(connection_struct *conn, char *fname, uint32 dirtype, BOOL b don't do it here as we'll get it wrong. */ /* We need a better way to return NT status codes from open... */ - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); fsp = open_file_ntcreate(conn, fname, &sbuf, DELETE_ACCESS, @@ -1872,12 +1871,12 @@ NTSTATUS can_delete(connection_struct *conn, char *fname, uint32 dirtype, BOOL b NULL); if (!fsp) { - NTSTATUS ret; - if (get_saved_error_triple(NULL, NULL, &ret)) { - set_saved_error_triple(0, 0, NT_STATUS_OK); + NTSTATUS ret = get_saved_ntstatus(); + if (!NT_STATUS_IS_OK(ret)) { + set_saved_ntstatus(NT_STATUS_OK); return ret; } - set_saved_error_triple(0, 0, NT_STATUS_OK); + set_saved_ntstatus(NT_STATUS_OK); return NT_STATUS_ACCESS_DENIED; } close_file(fsp,False); @@ -2209,15 +2208,6 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s * return a zero length response here. */ - if(global_oplock_break) { - _smb_setlen(header,0); - if (write_data(smbd_server_fd(),header,4) != 4) - fail_readraw(); - DEBUG(5,("readbraw - oplock break finished\n")); - END_PROFILE(SMBreadbraw); - return -1; - } - fsp = file_fsp(inbuf,smb_vwv0); if (!FNUM_OK(fsp,conn) || !fsp->can_read) { @@ -2298,8 +2288,8 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s nread = 0; #endif - DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos, - (int)maxcount, (int)mincount, (int)nread ) ); + DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%lu min=%lu nread=%lu\n", fsp->fnum, (double)startpos, + (unsigned long)maxcount, (unsigned long)mincount, (unsigned long)nread ) ); send_file_readbraw(conn, fsp, startpos, nread, mincount, outbuf, out_buffsize); @@ -3744,7 +3734,7 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if( is_ntfs_stream_name(directory)) { DEBUG(5,("reply_mkdir: failing create on filename %s with colon in name\n", directory)); END_PROFILE(SMBmkdir); - return ERROR_FORCE_DOS(ERRDOS, ERRinvalidname); + return ERROR_NT(NT_STATUS_NOT_A_DIRECTORY); } status = mkdir_internal(conn, directory,bad_path); @@ -5084,7 +5074,8 @@ SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_forma Reply to a lockingX request. ****************************************************************************/ -int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, + int length, int bufsize) { files_struct *fsp = file_fsp(inbuf,smb_vwv2); unsigned char locktype = CVAL(inbuf,smb_vwv3); @@ -5096,7 +5087,8 @@ int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length, int32 lock_timeout = IVAL(inbuf,smb_vwv4); int i; char *data; - BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False; + BOOL large_file_format = + (locktype & LOCKING_ANDX_LARGE_FILES)?True:False; BOOL err; BOOL my_lock_ctx = False; NTSTATUS status; @@ -5125,19 +5117,25 @@ int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length, if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) { /* Client can insist on breaking to none. */ BOOL break_to_none = (oplocklevel == 0); - - DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n", - (unsigned int)oplocklevel, fsp->fnum )); + BOOL result; + + DEBUG(5,("reply_lockingX: oplock break reply (%u) from client " + "for fnum = %d\n", (unsigned int)oplocklevel, + fsp->fnum )); /* - * Make sure we have granted an exclusive or batch oplock on this file. + * Make sure we have granted an exclusive or batch oplock on + * this file. */ - if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { - DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ -no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); - - /* if this is a pure oplock break request then don't send a reply */ + if (fsp->oplock_type == 0) { + DEBUG(0,("reply_lockingX: Error : oplock break from " + "client for fnum = %d (oplock=%d) and no " + "oplock granted on this file (%s).\n", + fsp->fnum, fsp->oplock_type, fsp->fsp_name)); + + /* if this is a pure oplock break request then don't + * send a reply */ if (num_locks == 0 && num_ulocks == 0) { END_PROFILE(SMBlockingX); return -1; @@ -5147,17 +5145,30 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); } } - if (remove_oplock(fsp, break_to_none) == False) { - DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n", - fsp->fsp_name )); + if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) || + (break_to_none)) { + result = remove_oplock(fsp); + } else { + result = downgrade_oplock(fsp); } - /* if this is a pure oplock break request then don't send a reply */ + if (!result) { + DEBUG(0, ("reply_lockingX: error in removing " + "oplock on file %s\n", fsp->fsp_name)); + /* Hmmm. Is this panic justified? */ + smb_panic("internal tdb error"); + } + + reply_to_oplock_break_requests(fsp); + + /* if this is a pure oplock break request then don't send a + * reply */ if (num_locks == 0 && num_ulocks == 0) { /* Sanity check - ensure a pure oplock break is not a chained request. */ if(CVAL(inbuf,smb_vwv0) != 0xff) - DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n", + DEBUG(0,("reply_lockingX: Error : pure oplock " + "break is a chained %d request !\n", (unsigned int)CVAL(inbuf,smb_vwv0) )); END_PROFILE(SMBlockingX); return -1; @@ -5186,8 +5197,9 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); return ERROR_DOS(ERRDOS,ERRnoaccess); } - DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n", - (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name )); + DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for " + "pid %u, file %s\n", (double)offset, (double)count, + (unsigned int)lock_pid, fsp->fsp_name )); status = do_unlock(fsp,conn,lock_pid,count,offset); if (NT_STATUS_V(status)) { @@ -5219,27 +5231,34 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); return ERROR_DOS(ERRDOS,ERRnoaccess); } - DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n", - (double)offset, (double)count, (unsigned int)lock_pid, - fsp->fsp_name, (int)lock_timeout )); + DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid " + "%u, file %s timeout = %d\n", (double)offset, + (double)count, (unsigned int)lock_pid, + fsp->fsp_name, (int)lock_timeout )); status = do_lock_spin(fsp,conn,lock_pid, count,offset, - ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx); + ((locktype & 1) ? READ_LOCK:WRITE_LOCK), + &my_lock_ctx); if (NT_STATUS_V(status)) { /* - * Interesting fact found by IFSTEST /t LockOverlappedTest... - * Even if it's our own lock context, we need to wait here as - * there may be an unlock on the way. - * So I removed a "&& !my_lock_ctx" from the following - * if statement. JRA. + * Interesting fact found by IFSTEST /t + * LockOverlappedTest... Even if it's our own lock + * context, we need to wait here as there may be an + * unlock on the way. So I removed a "&& + * !my_lock_ctx" from the following if statement. JRA. */ - if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) { + if ((lock_timeout != 0) && + lp_blocking_locks(SNUM(conn)) && + ERROR_WAS_LOCK_DENIED(status)) { /* * A blocking lock was requested. Package up * this smb into a queued request and push it * onto the blocking lock queue. */ - if(push_blocking_lock_request(inbuf, length, lock_timeout, i, lock_pid, offset, count)) { + if(push_blocking_lock_request(inbuf, length, + lock_timeout, i, + lock_pid, offset, + count)) { END_PROFILE(SMBlockingX); return -1; } @@ -5259,10 +5278,12 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); for(i--; i >= 0; i--) { lock_pid = get_lock_pid( data, i, large_file_format); count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, &err); + offset = get_lock_offset( data, i, large_file_format, + &err); /* - * There is no error code marked "stupid client bug".... :-). + * There is no error code marked "stupid client + * bug".... :-). */ if(err) { END_PROFILE(SMBlockingX); @@ -5277,8 +5298,8 @@ no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); set_message(outbuf,2,0,True); - DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n", - fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) ); + DEBUG(3, ("lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n", + fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks)); END_PROFILE(SMBlockingX); return chain_reply(inbuf,outbuf,length,bufsize); diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 1ff03174fe..8310b408d0 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -96,7 +96,7 @@ static void killkids(void) somewhere else. ****************************************************************************/ -static void msg_sam_sync(int UNUSED(msg_type), pid_t UNUSED(pid), +static void msg_sam_sync(int UNUSED(msg_type), struct process_id UNUSED(pid), void *UNUSED(buf), size_t UNUSED(len)) { DEBUG(10, ("** sam sync message received, ignoring\n")); @@ -107,7 +107,8 @@ static void msg_sam_sync(int UNUSED(msg_type), pid_t UNUSED(pid), somewhere else. ****************************************************************************/ -static void msg_sam_repl(int msg_type, pid_t pid, void *buf, size_t len) +static void msg_sam_repl(int msg_type, struct process_id pid, + void *buf, size_t len) { uint32 low_serial; @@ -140,7 +141,8 @@ static BOOL open_sockets_inetd(void) return True; } -static void msg_exit_server(int msg_type, pid_t src, void *buf, size_t len) +static void msg_exit_server(int msg_type, struct process_id src, + void *buf, size_t len) { exit_server("Got a SHUTDOWN message"); } @@ -621,9 +623,6 @@ void exit_server(const char *reason) print_notify_send_messages(3); /* 3 second timeout. */ - /* run all registered exit events */ - smb_run_exit_events(); - /* delete our entry in the connections database. */ yield_connection(NULL,""); diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index bf7287aab9..fc2d8b4abb 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -139,6 +139,7 @@ static int reply_spnego_kerberos(connection_struct *conn, int length, int bufsize, DATA_BLOB *secblob) { + TALLOC_CTX *mem_ctx; DATA_BLOB ticket; char *client, *p, *domain; fstring netbios_domain_name; @@ -146,7 +147,7 @@ static int reply_spnego_kerberos(connection_struct *conn, fstring user; int sess_vuid; NTSTATUS ret; - DATA_BLOB auth_data; + PAC_DATA *pac_data; DATA_BLOB ap_rep, ap_rep_wrapped, response; auth_serversupplied_info *server_info = NULL; DATA_BLOB session_key = data_blob(NULL, 0); @@ -154,18 +155,24 @@ static int reply_spnego_kerberos(connection_struct *conn, DATA_BLOB nullblob = data_blob(NULL, 0); fstring real_username; BOOL map_domainuser_to_guest = False; + PAC_LOGON_INFO *logon_info = NULL; + int i; ZERO_STRUCT(ticket); - ZERO_STRUCT(auth_data); + ZERO_STRUCT(pac_data); ZERO_STRUCT(ap_rep); ZERO_STRUCT(ap_rep_wrapped); ZERO_STRUCT(response); + mem_ctx = talloc_init("reply_spnego_kerberos"); + if (mem_ctx == NULL) + return ERROR_NT(NT_STATUS_NO_MEMORY); + if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) { return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key); + ret = ads_verify_ticket(mem_ctx, lp_realm(), &ticket, &client, &pac_data, &ap_rep, &session_key); data_blob_free(&ticket); @@ -174,7 +181,18 @@ static int reply_spnego_kerberos(connection_struct *conn, return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - data_blob_free(&auth_data); + if (pac_data) { + + /* get the logon_info */ + for (i=0; i < pac_data->num_buffers; i++) { + + if (pac_data->pac_buffer[i].type != PAC_TYPE_LOGON_INFO) + continue; + + logon_info = pac_data->pac_buffer[i].ctr->pac.logon_info; + break; + } + } DEBUG(3,("Ticket name is [%s]\n", client)); @@ -203,7 +221,14 @@ static int reply_spnego_kerberos(connection_struct *conn, domain = p+1; - { + if (logon_info && logon_info->info3.hdr_logon_dom.uni_str_len) { + + unistr2_to_ascii(netbios_domain_name, &logon_info->info3.uni_logon_dom, -1); + domain = netbios_domain_name; + DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain)); + + } else { + /* If we have winbind running, we can (and must) shorten the username by using the short netbios name. Otherwise we will have inconsistent user names. With Kerberos, we get the @@ -231,7 +256,7 @@ static int reply_spnego_kerberos(connection_struct *conn, wb_response.data.domain_info.name); domain = netbios_domain_name; - DEBUG(10, ("Mapped to [%s]\n", domain)); + DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain)); } else { DEBUG(3, ("Could not find short name -- winbind " "not running?\n")); @@ -274,8 +299,21 @@ static int reply_spnego_kerberos(connection_struct *conn, reload_services(True); if ( map_domainuser_to_guest ) { make_server_info_guest(&server_info); + } else if (logon_info) { + ret = make_server_info_pac(&server_info, real_username, pw, logon_info); + + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(1,("make_server_info_pac failed!\n")); + SAFE_FREE(client); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + passwd_free(&pw); + return ERROR_NT(ret); + } + } else { ret = make_server_info_pw(&server_info, real_username, pw); + if ( !NT_STATUS_IS_OK(ret) ) { DEBUG(1,("make_server_info_from_pw failed!\n")); SAFE_FREE(client); @@ -284,15 +322,17 @@ static int reply_spnego_kerberos(connection_struct *conn, passwd_free(&pw); return ERROR_NT(ret); } + + /* make_server_info_pw does not set the domain. Without this we end up + * with the local netbios name in substitutions for %D. */ + + if (server_info->sam_account != NULL) { + pdb_set_domain(server_info->sam_account, domain, PDB_SET); + } } - passwd_free(&pw); - /* make_server_info_pw does not set the domain. Without this we end up - * with the local netbios name in substitutions for %D. */ - if (server_info->sam_account != NULL) { - pdb_set_domain(server_info->sam_account, domain, PDB_SET); - } + passwd_free(&pw); /* register_vuid keeps the server info */ /* register_vuid takes ownership of session_key, no need to free after this. @@ -339,6 +379,7 @@ static int reply_spnego_kerberos(connection_struct *conn, data_blob_free(&ap_rep); data_blob_free(&ap_rep_wrapped); data_blob_free(&response); + talloc_destroy(mem_ctx); return -1; /* already replied */ } @@ -348,6 +389,8 @@ static int reply_spnego_kerberos(connection_struct *conn, Send a session setup reply, wrapped in SPNEGO. Get vuid and check first. End the NTLMSSP exchange context if we are OK/complete fail + This should be split into two functions, one to handle each + leg of the NTLM auth steps. ***************************************************************************/ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *outbuf, @@ -422,6 +465,7 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out and the other end, that we are not finished yet. */ if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* NB. This is *NOT* an error case. JRA */ auth_ntlmssp_end(auth_ntlmssp_state); /* Kill the intermediate vuid */ invalidate_vuid(vuid); @@ -660,7 +704,7 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, return ret; } - if (strncmp(blob1.data, "NTLMSSP", 7) == 0) { + if (strncmp((char *)(blob1.data), "NTLMSSP", 7) == 0) { DATA_BLOB chal; NTSTATUS nt_status; if (!vuser->auth_ntlmssp_state) { diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 38363bf66a..f0b7812a1e 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -27,7 +27,6 @@ extern int max_send; extern enum protocol_types Protocol; extern int smb_read_error; -extern int global_oplock_break; extern uint32 global_client_caps; extern struct current_user current_user; @@ -2789,7 +2788,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * delete_pending = get_delete_on_close_flag(sbuf.st_dev, - sbuf.st_ino); + sbuf.st_ino, + fname); } else { /* * Original code - this is an open file. @@ -2804,7 +2804,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * pos = fsp->fh->position_information; delete_pending = get_delete_on_close_flag(sbuf.st_dev, - sbuf.st_ino); + sbuf.st_ino, + fname); access_mask = fsp->access_mask; } } else { @@ -2847,7 +2848,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * } delete_pending = get_delete_on_close_flag(sbuf.st_dev, - sbuf.st_ino); + sbuf.st_ino, + fname); if (delete_pending) { return ERROR_NT(NT_STATUS_DELETE_PENDING); } @@ -3455,91 +3457,6 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd } /**************************************************************************** - Deal with the internal needs of setting the delete on close flag. Note that - as the tdb locking is recursive, it is safe to call this from within - open_file_shared. JRA. -****************************************************************************/ - -NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, - uint32 dosmode) -{ - if (!delete_on_close) { - return NT_STATUS_OK; - } - - /* - * Only allow delete on close for writable files. - */ - - if ((dosmode & aRONLY) && - !lp_delete_readonly(SNUM(fsp->conn))) { - DEBUG(10,("can_set_delete_on_close: file %s delete on close " - "flag set but file attribute is readonly.\n", - fsp->fsp_name )); - return NT_STATUS_CANNOT_DELETE; - } - - /* - * Only allow delete on close for writable shares. - */ - - if (!CAN_WRITE(fsp->conn)) { - DEBUG(10,("can_set_delete_on_close: file %s delete on " - "close flag set but write access denied on share.\n", - fsp->fsp_name )); - return NT_STATUS_ACCESS_DENIED; - } - - /* - * Only allow delete on close for files/directories opened with delete - * intent. - */ - - if (!(fsp->access_mask & DELETE_ACCESS)) { - DEBUG(10,("can_set_delete_on_close: file %s delete on " - "close flag set but delete access denied.\n", - fsp->fsp_name )); - return NT_STATUS_ACCESS_DENIED; - } - - return NT_STATUS_OK; -} - -/**************************************************************************** - Sets the delete on close flag over all share modes on this file. - Modify the share mode entry for all files open - on this device and inode to tell other smbds we have - changed the delete on close flag. This will be noticed - in the close code, the last closer will delete the file - if flag is set. -****************************************************************************/ - -NTSTATUS set_delete_on_close(files_struct *fsp, BOOL delete_on_close) -{ - DEBUG(10,("set_delete_on_close: %s delete on close flag for " - "fnum = %d, file %s\n", - delete_on_close ? "Adding" : "Removing", fsp->fnum, - fsp->fsp_name )); - - if (fsp->is_directory || fsp->is_stat) - return NT_STATUS_OK; - - if (lock_share_entry_fsp(fsp) == False) - return NT_STATUS_ACCESS_DENIED; - - if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) { - DEBUG(0,("set_delete_on_close: failed to change delete " - "on close flag for file %s\n", - fsp->fsp_name )); - unlock_share_entry_fsp(fsp); - return NT_STATUS_ACCESS_DENIED; - } - - unlock_share_entry_fsp(fsp); - return NT_STATUS_OK; -} - -/**************************************************************************** Set a hard link (called by UNIX extensions and by NT rename with HARD link code. ****************************************************************************/ @@ -3912,16 +3829,6 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char if (fd == -1) { files_struct *new_fsp = NULL; - if(global_oplock_break) { - /* Queue this file modify as we are the process of an oplock break. */ - - DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being ")); - DEBUGADD(2,( "in oplock break state.\n")); - - push_oplock_pending_smb_message(inbuf, length); - return -1; - } - new_fsp = open_file_ntcreate(conn, fname, &sbuf, FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, @@ -4003,9 +3910,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char } /* The set is across all open files on this dev/inode pair. */ - status =set_delete_on_close(fsp, delete_on_close); - if (!NT_STATUS_IS_OK(status)) { - return ERROR_NT(status); + if (!set_delete_on_close(fsp, delete_on_close)) { + return ERROR_NT(NT_STATUS_ACCESS_DENIED); } SSVAL(params,0,0); @@ -4456,16 +4362,6 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (fd == -1) { files_struct *new_fsp = NULL; - if(global_oplock_break) { - /* Queue this file modify as we are the process of an oplock break. */ - - DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being ")); - DEBUGADD(2,( "in oplock break state.\n")); - - push_oplock_pending_smb_message(inbuf, length); - return -1; - } - new_fsp = open_file_ntcreate(conn, fname, &sbuf, FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, @@ -4859,18 +4755,6 @@ int reply_trans2(connection_struct *conn, unsigned int num_params, num_params_sofar, num_data, num_data_sofar; START_PROFILE(SMBtrans2); - if(global_oplock_break && (tran_call == TRANSACT2_OPEN)) { - /* Queue this open message as we are the process of an - * oplock break. */ - - DEBUG(2,("reply_trans2: queueing message trans2open due to being ")); - DEBUGADD(2,( "in oplock break state.\n")); - - push_oplock_pending_smb_message(inbuf, length); - END_PROFILE(SMBtrans2); - return -1; - } - if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) { END_PROFILE(SMBtrans2); |