static void irix_release_kernel_oplock(struct kernel_oplocks *_ctx, files_struct *fsp, int oplock_type) { if (DEBUGLVL(10)) { /* * Check and print out the current kernel * oplock state of this file. */ int state = sys_fcntl_long(fsp->fh->fd, F_OPLKACK, -1); dbgtext("irix_release_kernel_oplock: file %s, file_id = %s" "gen_id = %ul, has kernel oplock state " "of %x.\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, state ); } /* * Remove the kernel oplock on this file. */ if(sys_fcntl_long(fsp->fh->fd, F_OPLKACK, OP_REVOKE) < 0) { if( DEBUGLVL( 0 )) { dbgtext("irix_release_kernel_oplock: Error when " "removing kernel oplock on file " ); dbgtext("%s, file_id = %s gen_id = %ul. " "Error was %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, strerror(errno) ); } } }
static void linux_release_kernel_oplock(struct kernel_oplocks *ctx, files_struct *fsp, int oplock_type) { if (DEBUGLVL(10)) { /* * Check and print out the current kernel * oplock state of this file. */ int state = fcntl(fsp->fh->fd, F_GETLEASE, 0); dbgtext("linux_release_kernel_oplock: file %s, file_id = %s " "gen_id = %lu has kernel oplock state " "of %x.\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, state ); } /* * Remove the kernel oplock on this file. */ if ( SMB_VFS_LINUX_SETLEASE(fsp, F_UNLCK) == -1) { if (DEBUGLVL(0)) { dbgtext("linux_release_kernel_oplock: Error when " "removing kernel oplock on file " ); dbgtext("%s, file_id = %s, gen_id = %lu. " "Error was %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, strerror(errno) ); } } }
static bool irix_set_kernel_oplock(struct kernel_oplocks *_ctx, files_struct *fsp, int oplock_type) { struct irix_oplocks_context *ctx = talloc_get_type(_ctx->private_data, struct irix_oplocks_context); if (sys_fcntl_long(fsp->fh->fd, F_OPLKREG, ctx->write_fd) == -1) { if(errno != EAGAIN) { DEBUG(0,("irix_set_kernel_oplock: Unable to get " "kernel oplock on file %s, file_id %s " "gen_id = %ul. Error was %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, strerror(errno) )); } else { DEBUG(5,("irix_set_kernel_oplock: Refused oplock on " "file %s, fd = %d, file_id = %s, " "gen_id = %ul. Another process had the file " "open.\n", fsp_str_dbg(fsp), fsp->fh->fd, file_id_string_tos(&fsp->file_id), fsp->fh->gen_id )); } return False; } DEBUG(10,("irix_set_kernel_oplock: got kernel oplock on file %s, file_id = %s " "gen_id = %ul\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id)); return True; }
/* * Deal with a reply when a break-to-level II was sent. */ bool downgrade_oplock(files_struct *fsp) { bool ret; struct share_mode_lock *lck; DEBUG(10, ("downgrade_oplock called for %s\n", fsp_str_dbg(fsp))); lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0,("downgrade_oplock: failed to lock share entry for " "file %s\n", fsp_str_dbg(fsp))); return False; } ret = downgrade_share_oplock(lck, fsp); if (!ret) { DEBUG(0,("downgrade_oplock: failed to downgrade share oplock " "for file %s, %s, file_id %s\n", fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } downgrade_file_oplock(fsp); ret = update_num_read_oplocks(fsp, lck); if (!ret) { DEBUG(0, ("%s: update_num_read_oplocks failed for " "file %s, %s, %s\n", __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } TALLOC_FREE(lck); return ret; }
static files_struct *initial_break_processing( struct smbd_server_connection *sconn, struct file_id id, unsigned long file_id) { files_struct *fsp = NULL; DEBUG(3, ("initial_break_processing: called for %s/%u\n" "Current oplocks_open (exclusive = %d, levelII = %d)\n", file_id_string_tos(&id), (int)file_id, sconn->oplocks.exclusive_open, sconn->oplocks.level_II_open)); /* * We need to search the file open table for the * entry containing this dev and inode, and ensure * we have an oplock on it. */ fsp = file_find_dif(sconn, id, file_id); if(fsp == NULL) { /* The file could have been closed in the meantime - return success. */ DEBUG(3, ("initial_break_processing: cannot find open file " "with file_id %s gen_id = %lu, allowing break to " "succeed.\n", file_id_string_tos(&id), file_id)); return NULL; } /* Ensure we have an oplock on the file */ /* * There is a potential race condition in that an oplock could * have been broken due to another udp request, and yet there are * still oplock break messages being sent in the udp message * queue for this file. So return true if we don't have an oplock, * as we may have just freed it. */ if(fsp->oplock_type == NO_OPLOCK) { DEBUG(3, ("initial_break_processing: file %s (file_id = %s " "gen_id = %lu) has no oplock. Allowing break to " "succeed regardless.\n", fsp_str_dbg(fsp), file_id_string_tos(&id), fsp->fh->gen_id)); return NULL; } return fsp; }
/* * Deal with a reply when a break-to-level II was sent. */ bool downgrade_oplock(files_struct *fsp) { bool ret; struct share_mode_lock *lck; struct byte_range_lock *brl; DEBUG(10, ("downgrade_oplock called for %s\n", fsp_str_dbg(fsp))); lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0,("downgrade_oplock: failed to lock share entry for " "file %s\n", fsp_str_dbg(fsp))); return False; } ret = downgrade_share_oplock(lck, fsp); if (!ret) { DEBUG(0,("downgrade_oplock: failed to downgrade share oplock " "for file %s, %s, file_id %s\n", fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } downgrade_file_oplock(fsp); brl = brl_get_locks(talloc_tos(), fsp); if (brl != NULL) { brl_set_have_read_oplocks(brl, true); TALLOC_FREE(brl); } TALLOC_FREE(lck); return ret; }
static void print_brl(struct file_id id, struct server_id pid, enum brl_type lock_type, enum brl_flavour lock_flav, br_off start, br_off size, void *private_data) { #if NASTY_POSIX_LOCK_HACK { static SMB_INO_T lastino; if (lastino != ino) { char *cmd; if (asprintf(&cmd, "egrep POSIX.*%u /proc/locks", (int)ino) > 0) { system(cmd); SAFE_FREE(cmd); } } lastino = ino; } #endif printf("%s %s %s %.0f:%.0f(%.0f)\n", procid_str_static(&pid), file_id_string_tos(&id), lock_type==READ_LOCK?"R":"W", (double)start, (double)start+size-1,(double)size); }
files_struct *file_find_dif(struct smbd_server_connection *sconn, struct file_id id, unsigned long gen_id) { int count=0; files_struct *fsp; if (gen_id == 0) { return NULL; } for (fsp=sconn->files; fsp; fsp=fsp->next,count++) { /* We can have a fsp->fh->fd == -1 here as it could be a stat open. */ if (file_id_equal(&fsp->file_id, &id) && fsp->fh->gen_id == gen_id ) { if (count > 10) { DLIST_PROMOTE(sconn->files, fsp); } /* Paranoia check. */ if ((fsp->fh->fd == -1) && (fsp->oplock_type != NO_OPLOCK)) { DEBUG(0,("file_find_dif: file %s file_id = " "%s, gen = %u oplock_type = %u is a " "stat open with oplock type !\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), (unsigned int)fsp->fh->gen_id, (unsigned int)fsp->oplock_type )); smb_panic("file_find_dif"); } return fsp; } } return NULL; }
static void process_kernel_oplock_break(struct messaging_context *msg_ctx, void *private_data, uint32_t msg_type, struct server_id src, DATA_BLOB *data) { struct file_id id; unsigned long file_id; files_struct *fsp; struct smbd_server_connection *sconn = talloc_get_type_abort(private_data, struct smbd_server_connection); if (data->data == NULL) { DEBUG(0, ("Got NULL buffer\n")); return; } if (data->length != MSG_SMB_KERNEL_BREAK_SIZE) { DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); return; } /* Pull the data from the message. */ pull_file_id_24((char *)data->data, &id); file_id = (unsigned long)IVAL(data->data, 24); DEBUG(10, ("Got kernel oplock break message from pid %s: %s/%u\n", server_id_str(talloc_tos(), &src), file_id_string_tos(&id), (unsigned int)file_id)); fsp = initial_break_processing(sconn, id, file_id); if (fsp == NULL) { DEBUG(3, ("Got a kernel oplock break message for a file " "I don't know about\n")); return; } if (fsp->sent_oplock_break != NO_BREAK_SENT) { /* This is ok, kernel oplocks come in completely async */ DEBUG(3, ("Got a kernel oplock request while waiting for a " "break reply\n")); return; } if (sconn->using_smb2) { send_break_message_smb2(fsp, OPLOCKLEVEL_NONE); } else { send_break_message_smb1(fsp, OPLOCKLEVEL_NONE); } fsp->sent_oplock_break = BREAK_TO_NONE_SENT; add_oplock_timeout_handler(fsp); }
static bool linux_set_kernel_oplock(struct kernel_oplocks *ctx, files_struct *fsp, int oplock_type) { if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) { DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, " "fd = %d, file_id = %s. (%s)\n", fsp_str_dbg(fsp), fsp->fh->fd, file_id_string_tos(&fsp->file_id), strerror(errno))); return False; } DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, " "file_id = %s gen_id = %lu\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id)); return True; }
void scavenger_schedule_disconnected(struct files_struct *fsp) { NTSTATUS status; struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); struct timeval disconnect_time, until; uint64_t timeout_usec; struct scavenger_message msg; DATA_BLOB msg_blob; struct server_id_buf tmp; if (fsp->op == NULL) { return; } nttime_to_timeval(&disconnect_time, fsp->op->global->disconnect_time); timeout_usec = 1000 * fsp->op->global->durable_timeout_msec; until = timeval_add(&disconnect_time, timeout_usec / 1000000, timeout_usec % 1000000); ZERO_STRUCT(msg); msg.file_id = fsp->file_id; msg.open_persistent_id = fsp->op->global->open_persistent_id; msg.until = timeval_to_nttime(&until); DEBUG(10, ("smbd: %s mark file %s as disconnected at %s with timeout " "at %s in %fs\n", server_id_str_buf(self, &tmp), file_id_string_tos(&fsp->file_id), timeval_string(talloc_tos(), &disconnect_time, true), timeval_string(talloc_tos(), &until, true), fsp->op->global->durable_timeout_msec/1000.0)); SMB_ASSERT(server_id_is_disconnected(&fsp->op->global->server_id)); SMB_ASSERT(!server_id_equal(&self, &smbd_scavenger_state->parent_id)); SMB_ASSERT(!smbd_scavenger_state->am_scavenger); msg_blob = data_blob_const(&msg, sizeof(msg)); DEBUG(10, ("send message to scavenger\n")); status = messaging_send(smbd_scavenger_state->msg, smbd_scavenger_state->parent_id, MSG_SMB_SCAVENGER, &msg_blob); if (!NT_STATUS_IS_OK(status)) { struct server_id_buf tmp1, tmp2; DEBUG(2, ("Failed to send message to parent smbd %s " "from %s: %s\n", server_id_str_buf(smbd_scavenger_state->parent_id, &tmp1), server_id_str_buf(self, &tmp2), nt_errstr(status))); } }
static void print_brl(struct file_id id, struct server_id pid, enum brl_type lock_type, enum brl_flavour lock_flav, br_off start, br_off size, void *private_data) { static int count; int i; static const struct { enum brl_type lock_type; const char *desc; } lock_types[] = { { READ_LOCK, "R" }, { WRITE_LOCK, "W" }, { PENDING_READ_LOCK, "PR" }, { PENDING_WRITE_LOCK, "PW" }, { UNLOCK_LOCK, "U" } }; const char *desc="X"; const char *sharepath = ""; const char *fname = ""; struct share_mode_lock *share_mode; if (count==0) { d_printf("Byte range locks:\n"); d_printf("Pid dev:inode R/W start size SharePath Name\n"); d_printf("--------------------------------------------------------------------------------\n"); } count++; share_mode = fetch_share_mode_unlocked(NULL, id, "__unspecified__", "__unspecified__"); if (share_mode) { sharepath = share_mode->servicepath; fname = share_mode->filename; } for (i=0;i<ARRAY_SIZE(lock_types);i++) { if (lock_type == lock_types[i].lock_type) { desc = lock_types[i].desc; } } d_printf("%-10s %-15s %-4s %-9.0f %-9.0f %-24s %-24s\n", procid_str_static(&pid), file_id_string_tos(&id), desc, (double)start, (double)size, sharepath, fname); TALLOC_FREE(share_mode); }
bool remove_oplock(files_struct *fsp) { bool ret; struct share_mode_lock *lck; DEBUG(10, ("remove_oplock called for %s\n", fsp_str_dbg(fsp))); /* Remove the oplock flag from the sharemode. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0,("remove_oplock: failed to lock share entry for " "file %s\n", fsp_str_dbg(fsp))); return False; } ret = remove_share_oplock(lck, fsp); if (!ret) { DEBUG(0,("remove_oplock: failed to remove share oplock for " "file %s, %s, %s\n", fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } release_file_oplock(fsp); ret = update_num_read_oplocks(fsp, lck); if (!ret) { DEBUG(0, ("%s: update_num_read_oplocks failed for " "file %s, %s, %s\n", __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } TALLOC_FREE(lck); return ret; }
char *share_mode_str(TALLOC_CTX *ctx, int num, const struct share_mode_entry *e) { return talloc_asprintf(ctx, "share_mode_entry[%d]: " "pid = %s, share_access = 0x%x, private_options = 0x%x, " "access_mask = 0x%x, mid = 0x%llx, type= 0x%x, gen_id = %llu, " "uid = %u, flags = %u, file_id %s, name_hash = 0x%x", num, procid_str_static(&e->pid), e->share_access, e->private_options, e->access_mask, (unsigned long long)e->op_mid, e->op_type, (unsigned long long)e->share_file_id, (unsigned int)e->uid, (unsigned int)e->flags, file_id_string_tos(&e->id), (unsigned int)e->name_hash); }
bool set_write_time(struct file_id fileid, struct timespec write_time) { struct share_mode_lock *lck; DEBUG(5,("set_write_time: %s id=%s\n", timestring(talloc_tos(), convert_timespec_to_time_t(write_time)), file_id_string_tos(&fileid))); lck = get_existing_share_mode_lock(talloc_tos(), fileid); if (lck == NULL) { return False; } if (timespec_compare(&lck->data->old_write_time, &write_time) != 0) { lck->data->modified = True; lck->data->old_write_time = write_time; } TALLOC_FREE(lck); return True; }
NTSTATUS set_file_oplock(files_struct *fsp) { struct smbd_server_connection *sconn = fsp->conn->sconn; struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops; bool use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks; if (fsp->oplock_type == LEVEL_II_OPLOCK) { if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) { DEBUG(10, ("Refusing level2 oplock, kernel oplocks " "don't support them\n")); return NT_STATUS_NOT_SUPPORTED; } } if ((fsp->oplock_type != NO_OPLOCK) && use_kernel && !koplocks->ops->set_oplock(koplocks, fsp, fsp->oplock_type)) { return map_nt_error_from_unix(errno); } fsp->sent_oplock_break = NO_BREAK_SENT; if (fsp->oplock_type == LEVEL_II_OPLOCK) { sconn->oplocks.level_II_open++; } else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { sconn->oplocks.exclusive_open++; } DEBUG(5,("set_file_oplock: granted oplock on file %s, %s/%lu, " "tv_sec = %x, tv_usec = %x\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), fsp->fh->gen_id, (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec )); return NT_STATUS_OK; }
static void print_brl(struct file_id id, struct server_id pid, enum brl_type lock_type, enum brl_flavour lock_flav, br_off start, br_off size, void *private_data) { static int count; int i; static const struct { enum brl_type lock_type; const char *desc; } lock_types[] = { { READ_LOCK, "R" }, { WRITE_LOCK, "W" }, { PENDING_READ_LOCK, "PR" }, { PENDING_WRITE_LOCK, "PW" }, { UNLOCK_LOCK, "U" } }; const char *desc="X"; const char *sharepath = ""; char *fname = NULL; struct share_mode_lock *share_mode; if (count==0) { d_printf("Byte range locks:\n"); d_printf("Pid dev:inode R/W start size SharePath Name\n"); d_printf("--------------------------------------------------------------------------------\n"); } count++; share_mode = fetch_share_mode_unlocked(NULL, id); if (share_mode) { bool has_stream = share_mode->data->stream_name != NULL; fname = talloc_asprintf(NULL, "%s%s%s", share_mode->data->base_name, has_stream ? ":" : "", has_stream ? share_mode->data->stream_name : ""); } else { fname = talloc_strdup(NULL, ""); if (fname == NULL) { return; } } for (i=0;i<ARRAY_SIZE(lock_types);i++) { if (lock_type == lock_types[i].lock_type) { desc = lock_types[i].desc; } } d_printf("%-10s %-15s %-4s %-9.0f %-9.0f %-24s %-24s\n", procid_str_static(&pid), file_id_string_tos(&id), desc, (double)start, (double)size, sharepath, fname); TALLOC_FREE(fname); TALLOC_FREE(share_mode); }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; struct server_id self = messaging_server_id(conn->sconn->msg_ctx); bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; const struct security_unix_token *del_token = NULL; const struct security_token *del_nt_token = NULL; bool got_tokens = false; bool normal_close; int ret_flock, retries = 1; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(fsp->conn->sconn->ev_ctx, fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); return NT_STATUS_INVALID_PARAMETER; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->data->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (fsp->initial_delete_on_close && !is_delete_on_close_set(lck, fsp->name_hash)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (get_current_vuid(conn) != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(fsp, lck, get_current_nttok(conn), get_current_utok(conn)); if (became_user) { unbecome_user(); } } delete_file = is_delete_on_close_set(lck, fsp->name_hash); if (delete_file) { int i; /* See if others still have the file open via this pathname. If this is the case, then don't delete. If all opens are POSIX delete now. */ for (i=0; i<lck->data->num_share_modes; i++) { struct share_mode_entry *e = &lck->data->share_modes[i]; if (!is_valid_share_mode_entry(e)) { continue; } if (e->name_hash != fsp->name_hash) { continue; } if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } if (serverid_equal(&self, &e->pid) && (e->share_file_id == fsp->fh->gen_id)) { continue; } if (share_mode_stale_pid(lck->data, i)) { continue; } delete_file = False; break; } } /* * NT can set delete_on_close of the last open * reference to a file. */ normal_close = (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE); if (!normal_close || !delete_file) { status = NT_STATUS_OK; goto done; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; got_tokens = get_delete_on_close_token(lck, fsp->name_hash, &del_nt_token, &del_token); SMB_ASSERT(got_tokens); if (!unix_token_equal(del_token, get_current_utok(conn))) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)del_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(del_token->uid, del_token->gid, del_token->ngroups, del_token->groups, del_nt_token); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } retry_delete: /* temporary files with delete on close set will not be deleted on a * cifs share using a netapp backend since they are opened with * read + write access mask. * close the file to allow the delete. */ if (fsp->can_write && !S_ISDIR(fsp->fsp_name->st.st_ex_mode) && fsp->fh->ref_count == 1 && retries) { status = fd_close(fsp); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("close_remove_share_mode: Error %s closing %s\n", nt_errstr(status), smb_fname_str_dbg(fsp->fsp_name))); goto skip_retry; } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); retries = 0; goto retry_delete; } } else { if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } } /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ skip_retry: fsp->delete_on_close = false; reset_delete_on_close_lck(fsp, lck); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } /* remove filesystem sharemodes */ ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, 0, 0); if (ret_flock == -1) { DEBUG(2, ("close_remove_share_mode: removing kernel flock for " "%s failed: %s\n", fsp_str_dbg(fsp), strerror(errno))); } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } TALLOC_FREE(lck); if (delete_file) { /* * Do the notification after we released the share * mode lock. Inside notify_fname we take out another * tdb lock. With ctdb also accessing our databases, * this can lead to deadlocks. Putting this notify * after the TALLOC_FREE(lck) above we avoid locking * two records simultaneously. Notifies are async and * informational only, so calling the notify_fname * without holding the share mode lock should not do * any harm. */ notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); } return status; }
bool remove_oplock(files_struct *fsp) { bool ret; struct share_mode_lock *lck; DEBUG(10, ("remove_oplock called for %s\n", fsp_str_dbg(fsp))); /* Remove the oplock flag from the sharemode. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck == NULL) { DEBUG(0,("remove_oplock: failed to lock share entry for " "file %s\n", fsp_str_dbg(fsp))); return False; } if (fsp->oplock_type == LEVEL_II_OPLOCK) { /* * If we're the only LEVEL_II holder, we have to remove the * have_read_oplocks from the brlock entry */ struct share_mode_data *data = lck->data; uint32_t i, num_level2; num_level2 = 0; for (i=0; i<data->num_share_modes; i++) { if (data->share_modes[i].op_type == LEVEL_II_OPLOCK) { num_level2 += 1; } if (num_level2 > 1) { /* * No need to count them all... */ break; } } if (num_level2 == 1) { /* * That's only us. We are dropping that level2 oplock, * so remove the brlock flag. */ struct byte_range_lock *brl; brl = brl_get_locks(talloc_tos(), fsp); if (brl) { brl_set_have_read_oplocks(brl, false); TALLOC_FREE(brl); } } } ret = remove_share_oplock(lck, fsp); if (!ret) { DEBUG(0,("remove_oplock: failed to remove share oplock for " "file %s, %s, %s\n", fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } release_file_oplock(fsp); TALLOC_FREE(lck); return ret; }
static void process_oplock_break_message(struct messaging_context *msg_ctx, void *private_data, uint32_t msg_type, struct server_id src, DATA_BLOB *data) { struct share_mode_entry msg; files_struct *fsp; bool break_to_level2 = False; bool use_kernel; struct smbd_server_connection *sconn = talloc_get_type_abort(private_data, struct smbd_server_connection); struct server_id self = messaging_server_id(sconn->msg_ctx); struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops; uint16_t break_to; if (data->data == NULL) { DEBUG(0, ("Got NULL buffer\n")); return; } if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) { DEBUG(0, ("Got invalid msg len %d\n", (int)data->length)); return; } /* De-linearize incoming message. */ message_to_share_mode_entry(&msg, (char *)data->data); break_to = msg.op_type; DEBUG(10, ("Got oplock break to %u message from pid %s: %s/%llu\n", (unsigned)break_to, server_id_str(talloc_tos(), &src), file_id_string_tos(&msg.id), (unsigned long long)msg.share_file_id)); fsp = initial_break_processing(sconn, msg.id, msg.share_file_id); if (fsp == NULL) { /* We hit a race here. Break messages are sent, and before we * get to process this message, we have closed the file. */ DEBUG(3, ("Did not find fsp\n")); return; } if (fsp->sent_oplock_break != NO_BREAK_SENT) { /* * Nothing to do anymore */ return; } if (break_to == fsp->oplock_type) { DEBUG(3, ("Already downgraded oplock on %s: %s\n", file_id_string_tos(&fsp->file_id), fsp_str_dbg(fsp))); return; } use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks; if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) && (break_to != NO_OPLOCK) && !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) && lp_level2_oplocks(SNUM(fsp->conn))) { break_to_level2 = True; } /* Need to wait before sending a break message if we sent ourselves this message. */ if (serverid_equal(&self, &src)) { wait_before_sending_break(); } if (sconn->using_smb2) { send_break_message_smb2(fsp, break_to_level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); } else { send_break_message_smb1(fsp, break_to_level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); } if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) { /* * This is an async break without a reply and thus no timeout */ remove_oplock(fsp); return; } fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT; add_oplock_timeout_handler(fsp); }
static files_struct *irix_oplock_receive_message(struct kernel_oplocks *_ctx) { struct irix_oplocks_context *ctx = talloc_get_type(_ctx->private_data, struct irix_oplocks_context); oplock_stat_t os; char dummy; struct file_id fileid; files_struct *fsp; /* * TODO: is it correct to assume we only get one * oplock break, for each byte we read from the pipe? */ ctx->pending = false; /* * Read one byte of zero to clear the * kernel break notify message. */ if(read(ctx->read_fd, &dummy, 1) != 1) { DEBUG(0,("irix_oplock_receive_message: read of kernel " "notification failed. Error was %s.\n", strerror(errno) )); return NULL; } /* * Do a query to get the * device and inode of the file that has the break * request outstanding. */ if(sys_fcntl_ptr(ctx->read_fd, F_OPLKSTAT, &os) < 0) { DEBUG(0,("irix_oplock_receive_message: fcntl of kernel " "notification failed. Error was %s.\n", strerror(errno) )); if(errno == EAGAIN) { /* * Duplicate kernel break message - ignore. */ return NULL; } return NULL; } /* * We only have device and inode info here - we have to guess that this * is the first fsp open with this dev,ino pair. * * NOTE: this doesn't work if any VFS modules overloads * the file_id_create() hook! */ fileid = file_id_create_dev((SMB_DEV_T)os.os_dev, (SMB_INO_T)os.os_ino); if ((fsp = file_find_di_first(smbd_server_conn, fileid)) == NULL) { DEBUG(0,("irix_oplock_receive_message: unable to find open " "file with dev = %x, inode = %.0f\n", (unsigned int)os.os_dev, (double)os.os_ino )); return NULL; } DEBUG(5,("irix_oplock_receive_message: kernel oplock break request " "received for file_id %s gen_id = %ul", file_id_string_tos(&fsp->file_id), fsp->fh->gen_id )); return fsp; }
static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; bool delete_file = false; bool changed_user = false; struct share_mode_lock *lck = NULL; NTSTATUS status = NT_STATUS_OK; NTSTATUS tmp_status; struct file_id id; /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(smbd_event_context(), fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * Lock the share entries, and determine if we should delete * on close. If so delete whilst the lock is still in effect. * This prevents race conditions with the file being created. JRA. */ lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " "lock for file %s\n", fsp_str_dbg(fsp))); status = NT_STATUS_INVALID_PARAMETER; goto done; } if (fsp->write_time_forced) { DEBUG(10,("close_remove_share_mode: write time forced " "for file %s\n", fsp_str_dbg(fsp))); set_close_write_time(fsp, lck->changed_write_time); } else if (fsp->update_write_time_on_close) { /* Someone had a pending write. */ if (null_timespec(fsp->close_write_time)) { DEBUG(10,("close_remove_share_mode: update to current time " "for file %s\n", fsp_str_dbg(fsp))); /* Update to current time due to "normal" write. */ set_close_write_time(fsp, timespec_current()); } else { DEBUG(10,("close_remove_share_mode: write time pending " "for file %s\n", fsp_str_dbg(fsp))); /* Update to time set on close call. */ set_close_write_time(fsp, fsp->close_write_time); } } if (!del_share_mode(lck, fsp)) { DEBUG(0, ("close_remove_share_mode: Could not delete share " "entry for file %s\n", fsp_str_dbg(fsp))); } if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) { bool became_user = False; /* Initial delete on close was set and no one else * wrote a real delete on close. */ if (current_user.vuid != fsp->vuid) { become_user(conn, fsp->vuid); became_user = True; } fsp->delete_on_close = true; set_delete_on_close_lck(lck, True, ¤t_user.ut); if (became_user) { unbecome_user(); } } 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. If all opens are POSIX delete now. */ for (i=0; i<lck->num_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; if (is_valid_share_mode_entry(e)) { if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { continue; } delete_file = False; break; } } } /* Notify any deferred opens waiting on this close. */ notify_deferred_opens(lck); reply_to_oplock_break_requests(fsp); /* * NT can set delete_on_close of the last open * reference to a file. */ if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) || !delete_file || (lck->delete_token == NULL)) { TALLOC_FREE(lck); return NT_STATUS_OK; } /* * Ok, we have to delete the file */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp_str_dbg(fsp))); /* * Don't try to update the write time when we delete the file */ fsp->update_write_time_on_close = false; if (!unix_token_equal(lck->delete_token, ¤t_user.ut)) { /* Become the user who requested the delete. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), (unsigned int)lck->delete_token->uid)); if (!push_sec_ctx()) { smb_panic("close_remove_share_mode: file %s. failed to push " "sec_ctx.\n"); } set_sec_ctx(lck->delete_token->uid, lck->delete_token->gid, lck->delete_token->ngroups, lck->delete_token->groups, NULL); changed_user = true; } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ tmp_status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(tmp_status)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and stat failed with error %s\n", fsp_str_dbg(fsp), nt_errstr(tmp_status))); /* * Don't save the errno here, we ignore this error */ goto done; } id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); if (!file_id_equal(&fsp->file_id, &id)) { DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and dev and/or inode does not match\n", fsp_str_dbg(fsp))); DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, " "stat file_id %s\n", fsp_str_dbg(fsp), file_id_string_tos(&fsp->file_id), file_id_string_tos(&id))); /* * Don't save the errno here, we ignore this error */ goto done; } if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && !is_ntfs_stream_smb_fname(fsp->fsp_name)) { status = delete_all_streams(conn, fsp->fsp_name->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("delete_all_streams failed: %s\n", nt_errstr(status))); goto done; } } if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may * have had the file open with delete on close set and * deleted it when its last reference to this file * went away. Hence we log this but not at debug level * zero. */ DEBUG(5,("close_remove_share_mode: file %s. Delete on close " "was set and unlink failed with error %s\n", fsp_str_dbg(fsp), strerror(errno))); status = map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_FILE_NAME, fsp->fsp_name->base_name); /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode * entry - ensure we only delete once by resetting * the delete on close flag. JRA. */ fsp->delete_on_close = false; set_delete_on_close_lck(lck, False, NULL); done: if (changed_user) { /* unbecome user. */ pop_sec_ctx(); } TALLOC_FREE(lck); return status; }
bool rename_share_filename(struct messaging_context *msg_ctx, struct share_mode_lock *lck, const char *servicepath, uint32_t orig_name_hash, uint32_t new_name_hash, const struct smb_filename *smb_fname_dst) { struct share_mode_data *d = lck->data; size_t sp_len; size_t bn_len; size_t sn_len; size_t msg_len; char *frm = NULL; int i; bool strip_two_chars = false; bool has_stream = smb_fname_dst->stream_name != NULL; struct server_id self_pid = messaging_server_id(msg_ctx); DEBUG(10, ("rename_share_filename: servicepath %s newname %s\n", servicepath, smb_fname_dst->base_name)); /* * rename_internal_fsp() and rename_internals() add './' to * head of newname if newname does not contain a '/'. */ if (smb_fname_dst->base_name[0] && smb_fname_dst->base_name[1] && smb_fname_dst->base_name[0] == '.' && smb_fname_dst->base_name[1] == '/') { strip_two_chars = true; } d->servicepath = talloc_strdup(d, servicepath); d->base_name = talloc_strdup(d, smb_fname_dst->base_name + (strip_two_chars ? 2 : 0)); d->stream_name = talloc_strdup(d, smb_fname_dst->stream_name); if (d->base_name == NULL || (has_stream && d->stream_name == NULL) || d->servicepath == NULL) { DEBUG(0, ("rename_share_filename: talloc failed\n")); return False; } d->modified = True; sp_len = strlen(d->servicepath); bn_len = strlen(d->base_name); sn_len = has_stream ? strlen(d->stream_name) : 0; msg_len = MSG_FILE_RENAMED_MIN_SIZE + sp_len + 1 + bn_len + 1 + sn_len + 1; /* Set up the name changed message. */ frm = talloc_array(d, char, msg_len); if (!frm) { return False; } push_file_id_24(frm, &d->id); DEBUG(10,("rename_share_filename: msg_len = %u\n", (unsigned int)msg_len )); strlcpy(&frm[24], d->servicepath ? d->servicepath : "", sp_len+1); strlcpy(&frm[24 + sp_len + 1], d->base_name ? d->base_name : "", bn_len+1); strlcpy(&frm[24 + sp_len + 1 + bn_len + 1], d->stream_name ? d->stream_name : "", sn_len+1); /* Send the messages. */ for (i=0; i<d->num_share_modes; i++) { struct share_mode_entry *se = &d->share_modes[i]; if (!is_valid_share_mode_entry(se)) { continue; } /* If this is a hardlink to the inode with a different name, skip this. */ if (se->name_hash != orig_name_hash) { continue; } se->name_hash = new_name_hash; /* But not to ourselves... */ if (serverid_equal(&se->pid, &self_pid)) { continue; } if (share_mode_stale_pid(d, i)) { continue; } DEBUG(10,("rename_share_filename: sending rename message to " "pid %s file_id %s sharepath %s base_name %s " "stream_name %s\n", procid_str_static(&se->pid), file_id_string_tos(&d->id), d->servicepath, d->base_name, has_stream ? d->stream_name : "")); messaging_send_buf(msg_ctx, se->pid, MSG_SMB_FILE_RENAME, (uint8 *)frm, msg_len); } return True; }