static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle, files_struct *fsp, DATA_BLOB *pblob) { uint8_t id_buf[16]; struct file_id id; TDB_DATA data; struct db_context *db = acl_db; struct db_record *rec; NTSTATUS status; DEBUG(10,("store_acl_blob_fsp: storing blob length %u on file %s\n", (unsigned int)pblob->length, fsp_str_dbg(fsp))); status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } id = vfs_file_id_from_sbuf(handle->conn, &fsp->fsp_name->st); /* For backwards compatibility only store the dev/inode. */ push_file_id_16((char *)id_buf, &id); rec = dbwrap_fetch_locked(db, talloc_tos(), make_tdb_data(id_buf, sizeof(id_buf))); if (rec == NULL) { DEBUG(0, ("store_acl_blob_fsp_tdb: fetch_lock failed\n")); return NT_STATUS_INTERNAL_DB_CORRUPTION; } data.dptr = pblob->data; data.dsize = pblob->length; return dbwrap_record_store(rec, data, 0); }
static int sys_acl_set_fd_tdb(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl) { struct db_context *db; NTSTATUS status; int ret; SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1); status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return -1; } ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl); if (ret == -1) { return -1; } acl_tdb_delete(handle, db, &fsp->fsp_name->st); return 0; }
static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { struct smb_file_time ft; NTSTATUS status; struct share_mode_lock *lck = NULL; ZERO_STRUCT(ft); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } if (!VALID_STAT(fsp->fsp_name->st)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } /* On close if we're changing the real file time we * must update it in the open file db too. */ (void)set_write_time(fsp->file_id, fsp->close_write_time); lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck) { /* Close write times overwrite sticky write times so we must replace any sticky write time here. */ if (!null_timespec(lck->changed_write_time)) { (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time); } TALLOC_FREE(lck); } ft.mtime = fsp->close_write_time; /* We must use NULL for the fsp handle here, as smb_set_file_time() checks the fsp access_mask, which may not include FILE_WRITE_ATTRIBUTES. As this is a close based update, we are not directly changing the file attributes from a client call, but indirectly from a write. */ status = smb_set_file_time(fsp->conn, NULL, fsp->fsp_name, &ft, false); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("update_write_time_on_close: smb_set_file_time " "on file %s returned %s\n", fsp_str_dbg(fsp), nt_errstr(status))); return status; } return status; }
static NTSTATUS get_acl_blob(TALLOC_CTX *ctx, vfs_handle_struct *handle, files_struct *fsp, const struct smb_filename *smb_fname, DATA_BLOB *pblob) { uint8_t id_buf[16]; TDB_DATA data; struct file_id id; struct db_context *db = acl_db; NTSTATUS status = NT_STATUS_OK; SMB_STRUCT_STAT sbuf; ZERO_STRUCT(sbuf); if (fsp) { status = vfs_stat_fsp(fsp); sbuf = fsp->fsp_name->st; } else { int ret = vfs_stat_smb_basename(handle->conn, smb_fname, &sbuf); if (ret == -1) { status = map_nt_error_from_unix(errno); } } if (!NT_STATUS_IS_OK(status)) { return status; } id = vfs_file_id_from_sbuf(handle->conn, &sbuf); /* For backwards compatibility only store the dev/inode. */ push_file_id_16((char *)id_buf, &id); status = dbwrap_fetch(db, ctx, make_tdb_data(id_buf, sizeof(id_buf)), &data); if (!NT_STATUS_IS_OK(status)) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } pblob->data = data.dptr; pblob->length = data.dsize; DEBUG(10,("get_acl_blob: returned %u bytes from file %s\n", (unsigned int)data.dsize, smb_fname->base_name )); if (pblob->length == 0 || pblob->data == NULL) { return NT_STATUS_NOT_FOUND; } return NT_STATUS_OK; }
static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { struct smb_file_time ft; NTSTATUS status; struct share_mode_lock *lck = NULL; ZERO_STRUCT(ft); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } if (!VALID_STAT(fsp->fsp_name->st)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } /* On close if we're changing the real file time we * must update it in the open file db too. */ (void)set_write_time(fsp->file_id, fsp->close_write_time); lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck) { /* Close write times overwrite sticky write times so we must replace any sticky write time here. */ if (!null_timespec(lck->changed_write_time)) { (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time); } TALLOC_FREE(lck); } ft.mtime = fsp->close_write_time; status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false); if (!NT_STATUS_IS_OK(status)) { return status; } return status; }
NTSTATUS file_set_sparse(connection_struct *conn, files_struct *fsp, bool sparse) { uint32_t old_dosmode; uint32_t new_dosmode; NTSTATUS status; if (!CAN_WRITE(conn)) { DEBUG(9,("file_set_sparse: fname[%s] set[%u] " "on readonly share[%s]\n", smb_fname_str_dbg(fsp->fsp_name), sparse, lp_servicename(talloc_tos(), SNUM(conn)))); return NT_STATUS_MEDIA_WRITE_PROTECTED; } /* * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the * following access flags are granted. */ if ((fsp->access_mask & (FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | SEC_FILE_APPEND_DATA)) == 0) { DEBUG(9,("file_set_sparse: fname[%s] set[%u] " "access_mask[0x%08X] - access denied\n", smb_fname_str_dbg(fsp->fsp_name), sparse, fsp->access_mask)); return NT_STATUS_ACCESS_DENIED; } if (fsp->is_directory) { DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n", (sparse ? "set" : "clear"), smb_fname_str_dbg(fsp->fsp_name))); return NT_STATUS_INVALID_PARAMETER; } if (IS_IPC(conn) || IS_PRINT(conn)) { DEBUG(9, ("attempt to %s sparse flag over invalid conn\n", (sparse ? "set" : "clear"))); return NT_STATUS_INVALID_PARAMETER; } DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n", sparse, smb_fname_str_dbg(fsp->fsp_name))); if (!lp_store_dos_attributes(SNUM(conn))) { return NT_STATUS_INVALID_DEVICE_REQUEST; } status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } old_dosmode = dos_mode(conn, fsp->fsp_name); if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) { new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE; } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) { new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE; } else { return NT_STATUS_OK; } /* Store the DOS attributes in an EA. */ if (!set_ea_dos_attribute(conn, fsp->fsp_name, new_dosmode)) { if (errno == 0) { errno = EIO; } return map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fsp->fsp_name->base_name); fsp->is_sparse = sparse; return NT_STATUS_OK; }
static int sys_acl_set_file_tdb(vfs_handle_struct *handle, const char *path, SMB_ACL_TYPE_T type, SMB_ACL_T theacl) { struct db_context *db = acl_db; int ret = -1; struct smb_filename smb_fname = { .base_name = discard_const_p(char, path) }; ret = SMB_VFS_STAT(handle->conn, &smb_fname); if (ret == -1) { return -1; } ret = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, path, type, theacl); if (ret == -1) { return -1; } acl_tdb_delete(handle, db, &smb_fname.st); return 0; } /********************************************************************* Remove a Windows ACL - we're setting the underlying POSIX ACL. *********************************************************************/ static int sys_acl_set_fd_tdb(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl) { struct db_context *db = acl_db; NTSTATUS status; int ret; status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return -1; } ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl); if (ret == -1) { return -1; } acl_tdb_delete(handle, db, &fsp->fsp_name->st); return 0; } static struct vfs_fn_pointers vfs_acl_tdb_fns = { .connect_fn = connect_acl_tdb, .disconnect_fn = disconnect_acl_tdb, .rmdir_fn = rmdir_acl_tdb, .unlink_fn = unlink_acl_tdb, .chmod_fn = chmod_acl_module_common, .fchmod_fn = fchmod_acl_module_common, .fget_nt_acl_fn = fget_nt_acl_common, .get_nt_acl_fn = get_nt_acl_common, .fset_nt_acl_fn = fset_nt_acl_common, .chmod_acl_fn = chmod_acl_acl_module_common, .fchmod_acl_fn = fchmod_acl_acl_module_common, .sys_acl_set_file_fn = sys_acl_set_file_tdb, .sys_acl_set_fd_fn = sys_acl_set_fd_tdb }; static_decl_vfs; NTSTATUS vfs_acl_tdb_init(void) { return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "acl_tdb", &vfs_acl_tdb_fns); }
static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { struct smb_file_time ft; NTSTATUS status; struct share_mode_lock *lck = NULL; ZERO_STRUCT(ft); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } if (!VALID_STAT(fsp->fsp_name->st)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } /* * get_existing_share_mode_lock() isn't really the right * call here, as we're being called after * close_remove_share_mode() inside close_normal_file() * so it's quite normal to not have an existing share * mode here. However, get_share_mode_lock() doesn't * work because that will create a new share mode if * one doesn't exist - so stick with this call (just * ignore any error we get if the share mode doesn't * exist. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck) { /* On close if we're changing the real file time we * must update it in the open file db too. */ (void)set_write_time(fsp->file_id, fsp->close_write_time); /* Close write times overwrite sticky write times so we must replace any sticky write time here. */ if (!null_timespec(lck->data->changed_write_time)) { (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time); } TALLOC_FREE(lck); } ft.mtime = fsp->close_write_time; /* As this is a close based update, we are not directly changing the file attributes from a client call, but indirectly from a write. */ status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("update_write_time_on_close: smb_set_file_time " "on file %s returned %s\n", fsp_str_dbg(fsp), nt_errstr(status))); return status; } return status; }
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; }
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; }
static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32_t security_info, struct security_descriptor **ppdesc) { DATA_BLOB blob = data_blob_null; NTSTATUS status; uint16_t hash_type = XATTR_SD_HASH_TYPE_NONE; uint8_t hash[XATTR_SD_HASH_SIZE]; uint8_t hash_tmp[XATTR_SD_HASH_SIZE]; struct security_descriptor *psd = NULL; struct security_descriptor *pdesc_next = NULL; bool ignore_file_system_acl = lp_parm_bool(SNUM(handle->conn), ACL_MODULE_NAME, "ignore system acls", false); if (fsp && name == NULL) { name = fsp->fsp_name->base_name; } DEBUG(10, ("get_nt_acl_internal: name=%s\n", name)); /* Get the full underlying sd for the hash or to return as backup. */ if (fsp) { status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, HASH_SECURITY_INFO, &pdesc_next); } else { status = SMB_VFS_NEXT_GET_NT_ACL(handle, name, HASH_SECURITY_INFO, &pdesc_next); } if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("get_nt_acl_internal: get_next_acl for file %s " "returned %s\n", name, nt_errstr(status))); return status; } status = get_acl_blob(talloc_tos(), handle, fsp, name, &blob); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("get_nt_acl_internal: get_acl_blob returned %s\n", nt_errstr(status))); psd = pdesc_next; goto out; } status = parse_acl_blob(&blob, &psd, &hash_type, &hash[0]); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("parse_acl_blob returned %s\n", nt_errstr(status))); psd = pdesc_next; goto out; } /* Ensure the hash type is one we know. */ switch (hash_type) { case XATTR_SD_HASH_TYPE_NONE: /* No hash, just return blob sd. */ goto out; case XATTR_SD_HASH_TYPE_SHA256: break; default: DEBUG(10, ("get_nt_acl_internal: ACL blob revision " "mismatch (%u) for file %s\n", (unsigned int)hash_type, name)); TALLOC_FREE(psd); psd = pdesc_next; goto out; } if (ignore_file_system_acl) { goto out; } status = hash_sd_sha256(pdesc_next, hash_tmp); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(psd); psd = pdesc_next; goto out; } if (memcmp(&hash[0], &hash_tmp[0], XATTR_SD_HASH_SIZE) == 0) { /* Hash matches, return blob sd. */ DEBUG(10, ("get_nt_acl_internal: blob hash " "matches for file %s\n", name )); goto out; } /* Hash doesn't match, return underlying sd. */ TALLOC_FREE(psd); psd = pdesc_next; out: if (psd != pdesc_next) { /* We're returning the blob, throw * away the filesystem SD. */ TALLOC_FREE(pdesc_next); } else { SMB_STRUCT_STAT sbuf; SMB_STRUCT_STAT *psbuf = &sbuf; bool is_directory = false; /* * We're returning the underlying ACL from the * filesystem. If it's a directory, and has no * inheritable ACE entries we have to fake them. */ if (fsp) { status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } psbuf = &fsp->fsp_name->st; } else { int ret = vfs_stat_smb_fname(handle->conn, name, &sbuf); if (ret == -1) { return map_nt_error_from_unix(errno); } } is_directory = S_ISDIR(psbuf->st_ex_mode); if (ignore_file_system_acl) { TALLOC_FREE(pdesc_next); status = make_default_filesystem_acl(talloc_tos(), name, psbuf, &psd); if (!NT_STATUS_IS_OK(status)) { return status; } } else { if (is_directory && !sd_has_inheritable_components(psd, true)) { add_directory_inheritable_components(handle, name, psbuf, psd); } /* The underlying POSIX module always sets the ~SEC_DESC_DACL_PROTECTED bit, as ACLs can't be inherited in this way under POSIX. Remove it for Windows-style ACLs. */ psd->type &= ~SEC_DESC_DACL_PROTECTED; } } if (!(security_info & SECINFO_OWNER)) { psd->owner_sid = NULL; } if (!(security_info & SECINFO_GROUP)) { psd->group_sid = NULL; } if (!(security_info & SECINFO_DACL)) { psd->type &= ~SEC_DESC_DACL_PRESENT; psd->dacl = NULL; } if (!(security_info & SECINFO_SACL)) { psd->type &= ~SEC_DESC_SACL_PRESENT; psd->sacl = NULL; } TALLOC_FREE(blob.data); *ppdesc = psd; if (DEBUGLEVEL >= 10) { DEBUG(10,("get_nt_acl_internal: returning acl for %s is:\n", name )); NDR_PRINT_DEBUG(security_descriptor, psd); } return NT_STATUS_OK; }
NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, const DATA_BLOB old_cookie, TALLOC_CTX *mem_ctx, DATA_BLOB *new_cookie) { struct connection_struct *conn = fsp->conn; NTSTATUS status; enum ndr_err_code ndr_err; struct vfs_default_durable_cookie cookie; DATA_BLOB new_cookie_blob = data_blob_null; struct share_mode_lock *lck; bool ok; *new_cookie = data_blob_null; ZERO_STRUCT(cookie); ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie, (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) { return NT_STATUS_INVALID_PARAMETER; } if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) { return NT_STATUS_INVALID_PARAMETER; } if (!file_id_equal(&fsp->file_id, &cookie.id)) { return NT_STATUS_INVALID_PARAMETER; } if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) { return NT_STATUS_NOT_SUPPORTED; } /* * For now let it be simple and do not keep * delete on close files durable open */ if (fsp->initial_delete_on_close) { return NT_STATUS_NOT_SUPPORTED; } if (fsp->delete_on_close) { return NT_STATUS_NOT_SUPPORTED; } if (!VALID_STAT(fsp->fsp_name->st)) { return NT_STATUS_NOT_SUPPORTED; } if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) { return NT_STATUS_NOT_SUPPORTED; } /* 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); } /* * The above checks are done in mark_share_mode_disconnected() too * but we want to avoid getting the lock if possible */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck != NULL) { struct smb_file_time ft; ZERO_STRUCT(ft); if (fsp->write_time_forced) { ft.mtime = lck->data->changed_write_time; } else if (fsp->update_write_time_on_close) { if (null_timespec(fsp->close_write_time)) { ft.mtime = timespec_current(); } else { ft.mtime = fsp->close_write_time; } } if (!null_timespec(ft.mtime)) { round_timespec(conn->ts_res, &ft.mtime); file_ntimes(conn, fsp->fsp_name, &ft); } ok = mark_share_mode_disconnected(lck, fsp); if (!ok) { TALLOC_FREE(lck); } } if (lck != NULL) { ok = brl_mark_disconnected(fsp); if (!ok) { TALLOC_FREE(lck); } } if (lck == NULL) { return NT_STATUS_NOT_SUPPORTED; } TALLOC_FREE(lck); status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } ZERO_STRUCT(cookie); cookie.allow_reconnect = true; cookie.id = fsp->file_id; cookie.servicepath = conn->connectpath; cookie.base_name = fsp->fsp_name->base_name; cookie.initial_allocation_size = fsp->initial_allocation_size; cookie.position_information = fsp->fh->position_information; cookie.update_write_time_triggered = fsp->update_write_time_triggered; cookie.update_write_time_on_close = fsp->update_write_time_on_close; cookie.write_time_forced = fsp->write_time_forced; cookie.close_write_time = fsp->close_write_time; cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev; cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino; cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode; cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink; cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid; cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid; cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev; cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size; cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime; cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime; cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime; cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime; cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime; cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize; cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks; cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags; cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask; ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie, (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } status = fd_close(fsp); if (!NT_STATUS_IS_OK(status)) { data_blob_free(&new_cookie_blob); return status; } *new_cookie = new_cookie_blob; return NT_STATUS_OK; }