Esempio n. 1
0
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);
}
Esempio n. 2
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;
}
Esempio n. 3
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
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;
    status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    return status;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
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);
}
Esempio n. 8
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;
	}

	/*
	 * 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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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, &current_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, &current_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;
}
Esempio n. 11
0
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;
}
Esempio n. 12
0
File: durable.c Progetto: hef/samba
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;
}