Example #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);
}
Example #2
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;
}
Example #3
0
static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
				struct smb_filename *smb_fname,
				files_struct **ret_fsp,
				bool *need_close)
{
	NTSTATUS status;
	files_struct *fsp;
	struct file_id file_id;

	*need_close = false;

	if (!VALID_STAT(smb_fname->st)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);

	for(fsp = file_find_di_first(conn->sconn, file_id);
			fsp;
			fsp = file_find_di_next(fsp)) {
		if (fsp->fh->fd != -1) {
			*ret_fsp = fsp;
			return NT_STATUS_OK;
		}
	}

	/* Opens an INTERNAL_OPEN_ONLY write handle. */
	status = SMB_VFS_CREATE_FILE(
		conn,                                   /* conn */
		NULL,                                   /* req */
		0,                                      /* root_dir_fid */
		smb_fname,                              /* fname */
		FILE_WRITE_DATA,                        /* access_mask */
		(FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
			FILE_SHARE_DELETE),
		FILE_OPEN,                              /* create_disposition*/
		0,                                      /* create_options */
		0,                                      /* file_attributes */
		INTERNAL_OPEN_ONLY,                     /* oplock_request */
		NULL,					/* lease */
                0,                                      /* allocation_size */
		0,                                      /* private_flags */
		NULL,                                   /* sd */
		NULL,                                   /* ea_list */
		ret_fsp,                                /* result */
		NULL,                                   /* pinfo */
		NULL, NULL);				/* create context */

	if (NT_STATUS_IS_OK(status)) {
		*need_close = true;
	}
	return status;
}
Example #4
0
static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
			vfs_handle_struct *handle,
			files_struct *fsp,
			const char *name,
			DATA_BLOB *pblob)
{
	uint8 id_buf[16];
	TDB_DATA data;
	struct file_id id;
	struct db_context *db;
	int ret = -1;
	SMB_STRUCT_STAT sbuf;

	SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
		return NT_STATUS_INTERNAL_DB_CORRUPTION);

	if (fsp && fsp->fh->fd != -1) {
		ret = SMB_VFS_FSTAT(fsp, &sbuf);
	} else {
		if (fsp && fsp->posix_open) {
			ret = SMB_VFS_LSTAT(handle->conn, name, &sbuf);
		} else {
			ret = SMB_VFS_STAT(handle->conn, name, &sbuf);
		}
	}

	if (ret == -1) {
		return map_nt_error_from_unix(errno);
	}

	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);

	if (db->fetch(db,
			ctx,
			make_tdb_data(id_buf, sizeof(id_buf)),
			&data) == -1) {
		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, name ));

	if (pblob->length == 0 || pblob->data == NULL) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}
	return NT_STATUS_OK;
}
Example #5
0
static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle,
				files_struct *fsp,
				DATA_BLOB *pblob)
{
	uint8 id_buf[16];
	struct file_id id;
	SMB_STRUCT_STAT sbuf;
	TDB_DATA data;
	struct db_context *db;
	struct db_record *rec;
	int ret = -1;

	DEBUG(10,("store_acl_blob_fsp: storing blob length %u on file %s\n",
			(unsigned int)pblob->length, fsp->fsp_name));

	SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
		return NT_STATUS_INTERNAL_DB_CORRUPTION);

	if (fsp->fh->fd != -1) {
		ret = SMB_VFS_FSTAT(fsp, &sbuf);
	} else {
		if (fsp->posix_open) {
			ret = SMB_VFS_LSTAT(handle->conn, fsp->fsp_name, &sbuf);
		} else {
			ret = SMB_VFS_STAT(handle->conn, fsp->fsp_name, &sbuf);
		}
	}

	if (ret == -1) {
		return map_nt_error_from_unix(errno);
	}

	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);
	rec = db->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 rec->store(rec, data, 0);
}
Example #6
0
static NTSTATUS store_acl_blob_pathname(vfs_handle_struct *handle,
					const char *fname,
					DATA_BLOB *pblob)
{
	uint8 id_buf[16];
	struct file_id id;
	TDB_DATA data;
	SMB_STRUCT_STAT sbuf;
	struct db_context *db;
	struct db_record *rec;
	int ret = -1;

	DEBUG(10,("store_acl_blob_pathname: storing blob "
			"length %u on file %s\n",
			(unsigned int)pblob->length, fname));

	SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context,
		return NT_STATUS_INTERNAL_DB_CORRUPTION);

	if (lp_posix_pathnames()) {
		ret = SMB_VFS_LSTAT(handle->conn, fname, &sbuf);
	} else {
		ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
	}

	if (ret == -1) {
		return map_nt_error_from_unix(errno);
	}

	id = vfs_file_id_from_sbuf(handle->conn, &sbuf);
	push_file_id_16((char *)id_buf, &id);

	rec = db->fetch_locked(db, talloc_tos(),
				make_tdb_data(id_buf,
					sizeof(id_buf)));
	if (rec == NULL) {
		DEBUG(0, ("store_acl_blob_pathname_tdb: fetch_lock failed\n"));
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}
	data.dptr = pblob->data;
	data.dsize = pblob->length;
	return rec->store(rec, data, 0);
}
Example #7
0
static NTSTATUS acl_tdb_delete(vfs_handle_struct *handle,
				struct db_context *db,
				SMB_STRUCT_STAT *psbuf)
{
	NTSTATUS status;
	struct file_id id = vfs_file_id_from_sbuf(handle->conn, psbuf);
	struct db_record *rec = acl_tdb_lock(talloc_tos(), db, &id);

	/*
	 * If rec == NULL there's not much we can do about it
	 */

	if (rec == NULL) {
		DEBUG(10,("acl_tdb_delete: rec == NULL\n"));
		TALLOC_FREE(rec);
		return NT_STATUS_OK;
	}

	status = dbwrap_record_delete(rec);
	TALLOC_FREE(rec);
	return status;
}
Example #8
0
static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx,
						 struct tevent_context *ev,
						 struct smbd_smb2_request *smb2req,
						 struct files_struct *fsp,
						 uint8_t in_info_type,
						 uint8_t in_file_info_class,
						 uint32_t in_output_buffer_length,
						 DATA_BLOB in_input_buffer,
						 uint32_t in_additional_information,
						 uint32_t in_flags)
{
	struct tevent_req *req;
	struct smbd_smb2_getinfo_state *state;
	struct smb_request *smbreq;
	connection_struct *conn = smb2req->tcon->compat;
	NTSTATUS status;

	req = tevent_req_create(mem_ctx, &state,
				struct smbd_smb2_getinfo_state);
	if (req == NULL) {
		return NULL;
	}
	state->smb2req = smb2req;
	state->status = NT_STATUS_OK;
	state->out_output_buffer = data_blob_null;

	DEBUG(10,("smbd_smb2_getinfo_send: %s - %s\n",
		  fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));

	smbreq = smbd_smb2_fake_smb_request(smb2req);
	if (tevent_req_nomem(smbreq, req)) {
		return tevent_req_post(req, ev);
	}

	if (IS_IPC(conn)) {
		smb2_ipc_getinfo(req, state, ev,
			in_info_type, in_file_info_class);
		return tevent_req_post(req, ev);
	}

	switch (in_info_type) {
	case SMB2_GETINFO_FILE:
	{
		uint16_t file_info_level;
		char *data = NULL;
		unsigned int data_size = 0;
		bool delete_pending = false;
		struct timespec write_time_ts;
		struct file_id fileid;
		struct ea_list *ea_list = NULL;
		int lock_data_count = 0;
		char *lock_data = NULL;
		size_t fixed_portion;

		ZERO_STRUCT(write_time_ts);

		switch (in_file_info_class) {
		case 0x0F:/* RAW_FILEINFO_SMB2_ALL_EAS */
			file_info_level = 0xFF00 | in_file_info_class;
			break;

		case 0x12:/* RAW_FILEINFO_SMB2_ALL_INFORMATION */
			file_info_level = 0xFF00 | in_file_info_class;
			break;

		default:
			/* the levels directly map to the passthru levels */
			file_info_level = in_file_info_class + 1000;
			break;
		}

		if (fsp->fake_file_handle) {
			/*
			 * This is actually for the QUOTA_FAKE_FILE --metze
			 */

			/* We know this name is ok, it's already passed the checks. */

		} else if (fsp->fh->fd == -1) {
			/*
			 * This is actually a QFILEINFO on a directory
			 * handle (returned from an NT SMB). NT5.0 seems
			 * to do this call. JRA.
			 */

			if (INFO_LEVEL_IS_UNIX(file_info_level)) {
				/* Always do lstat for UNIX calls. */
				if (SMB_VFS_LSTAT(conn, fsp->fsp_name)) {
					DEBUG(3,("smbd_smb2_getinfo_send: "
						 "SMB_VFS_LSTAT of %s failed "
						 "(%s)\n", fsp_str_dbg(fsp),
						 strerror(errno)));
					status = map_nt_error_from_unix(errno);
					tevent_req_nterror(req, status);
					return tevent_req_post(req, ev);
				}
			} else if (SMB_VFS_STAT(conn, fsp->fsp_name)) {
				DEBUG(3,("smbd_smb2_getinfo_send: "
					 "SMB_VFS_STAT of %s failed (%s)\n",
					 fsp_str_dbg(fsp),
					 strerror(errno)));
				status = map_nt_error_from_unix(errno);
				tevent_req_nterror(req, status);
				return tevent_req_post(req, ev);
			}

			fileid = vfs_file_id_from_sbuf(conn,
						       &fsp->fsp_name->st);
			get_file_infos(fileid, fsp->name_hash,
				&delete_pending, &write_time_ts);
		} else {
			/*
			 * Original code - this is an open file.
			 */

			if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) {
				DEBUG(3, ("smbd_smb2_getinfo_send: "
					  "fstat of %s failed (%s)\n",
					  fsp_fnum_dbg(fsp), strerror(errno)));
				status = map_nt_error_from_unix(errno);
				tevent_req_nterror(req, status);
				return tevent_req_post(req, ev);
			}
			fileid = vfs_file_id_from_sbuf(conn,
						       &fsp->fsp_name->st);
			get_file_infos(fileid, fsp->name_hash,
				&delete_pending, &write_time_ts);
		}

		status = smbd_do_qfilepathinfo(conn, state,
					       file_info_level,
					       fsp,
					       fsp->fsp_name,
					       delete_pending,
					       write_time_ts,
					       ea_list,
					       lock_data_count,
					       lock_data,
					       STR_UNICODE,
					       in_output_buffer_length,
					       &fixed_portion,
					       &data,
					       &data_size);
		if (!NT_STATUS_IS_OK(status)) {
			SAFE_FREE(data);
			if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
				status = NT_STATUS_INVALID_INFO_CLASS;
			}
			tevent_req_nterror(req, status);
			return tevent_req_post(req, ev);
		}
		if (in_output_buffer_length < fixed_portion) {
			SAFE_FREE(data);
			tevent_req_nterror(
				req, NT_STATUS_INFO_LENGTH_MISMATCH);
			return tevent_req_post(req, ev);
		}
		if (data_size > 0) {
			state->out_output_buffer = data_blob_talloc(state,
								    data,
								    data_size);
			SAFE_FREE(data);
			if (tevent_req_nomem(state->out_output_buffer.data, req)) {
				return tevent_req_post(req, ev);
			}
			if (data_size > in_output_buffer_length) {
				state->out_output_buffer.length =
					in_output_buffer_length;
				status = STATUS_BUFFER_OVERFLOW;
			}
		}
		SAFE_FREE(data);
		break;
	}

	case SMB2_GETINFO_FS:
	{
		uint16_t file_info_level;
		char *data = NULL;
		int data_size = 0;
		size_t fixed_portion;

		/* the levels directly map to the passthru levels */
		file_info_level = in_file_info_class + 1000;

		status = smbd_do_qfsinfo(conn, state,
					 file_info_level,
					 STR_UNICODE,
					 in_output_buffer_length,
					 &fixed_portion,
					 fsp->fsp_name,
					 &data,
					 &data_size);
		/* some responses set STATUS_BUFFER_OVERFLOW and return
		   partial, but valid data */
		if (!(NT_STATUS_IS_OK(status) ||
		      NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW))) {
			SAFE_FREE(data);
			if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
				status = NT_STATUS_INVALID_INFO_CLASS;
			}
			tevent_req_nterror(req, status);
			return tevent_req_post(req, ev);
		}
		if (in_output_buffer_length < fixed_portion) {
			SAFE_FREE(data);
			tevent_req_nterror(
				req, NT_STATUS_INFO_LENGTH_MISMATCH);
			return tevent_req_post(req, ev);
		}
		if (data_size > 0) {
			state->out_output_buffer = data_blob_talloc(state,
								    data,
								    data_size);
			SAFE_FREE(data);
			if (tevent_req_nomem(state->out_output_buffer.data, req)) {
				return tevent_req_post(req, ev);
			}
			if (data_size > in_output_buffer_length) {
				state->out_output_buffer.length =
					in_output_buffer_length;
				status = STATUS_BUFFER_OVERFLOW;
			}
		}
		SAFE_FREE(data);
		break;
	}

	case SMB2_GETINFO_SECURITY:
	{
		uint8_t *p_marshalled_sd = NULL;
		size_t sd_size = 0;

		status = smbd_do_query_security_desc(conn,
				state,
				fsp,
				/* Security info wanted. */
				in_additional_information &
				SMB_SUPPORTED_SECINFO_FLAGS,
				in_output_buffer_length,
				&p_marshalled_sd,
				&sd_size);

		if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
			/* Return needed size. */
			state->out_output_buffer = data_blob_talloc(state,
								    NULL,
								    4);
			if (tevent_req_nomem(state->out_output_buffer.data, req)) {
				return tevent_req_post(req, ev);
			}
			SIVAL(state->out_output_buffer.data,0,(uint32_t)sd_size);
			state->status = NT_STATUS_BUFFER_TOO_SMALL;
			break;
		}
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(10,("smbd_smb2_getinfo_send: "
				 "smbd_do_query_security_desc of %s failed "
				 "(%s)\n", fsp_str_dbg(fsp),
				 nt_errstr(status)));
			tevent_req_nterror(req, status);
			return tevent_req_post(req, ev);
		}

		if (sd_size > 0) {
			state->out_output_buffer = data_blob_talloc(state,
								    p_marshalled_sd,
								    sd_size);
			if (tevent_req_nomem(state->out_output_buffer.data, req)) {
				return tevent_req_post(req, ev);
			}
		}
		break;
	}

	case SMB2_GETINFO_QUOTA:
		tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
		return tevent_req_post(req, ev);

	default:
		DEBUG(10,("smbd_smb2_getinfo_send: "
			"unknown in_info_type of %u "
			" for file %s\n",
			(unsigned int)in_info_type,
			fsp_str_dbg(fsp) ));

		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return tevent_req_post(req, ev);
	}

	state->status = status;
	tevent_req_done(req);
	return tevent_req_post(req, ev);
}
Example #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;
}
Example #10
0
NTSTATUS print_spool_open(files_struct *fsp,
			  const char *fname,
			  uint16_t current_vuid)
{
	NTSTATUS status;
	TALLOC_CTX *tmp_ctx;
	struct print_file_data *pf;
	struct dcerpc_binding_handle *b = NULL;
	struct spoolss_DevmodeContainer devmode_ctr;
	union spoolss_DocumentInfo info;
	int fd = -1;
	WERROR werr;

	tmp_ctx = talloc_new(fsp);
	if (!tmp_ctx) {
		return NT_STATUS_NO_MEMORY;
	}

	pf = talloc_zero(fsp, struct print_file_data);
	if (!pf) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}
	pf->svcname = talloc_strdup(pf, lp_servicename(SNUM(fsp->conn)));

	/* the document name is derived from the file name.
	 * "Remote Downlevel Document" is added in front to
	 * mimic what windows does in this case */
	pf->docname = talloc_strdup(pf, DOCNAME_DEFAULT);
	if (!pf->docname) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}
	if (fname) {
		const char *p = strrchr(fname, '/');
		if (!p) {
			p = fname;
		}
		pf->docname = talloc_asprintf_append(pf->docname, " %s", p);
		if (!pf->docname) {
			status = NT_STATUS_NO_MEMORY;
			goto done;
		}
	}

	/*
	 * Ok, now we have to open an actual file.
	 * Here is the reason:
	 * We want to write the spool job to this file in
	 * smbd for scalability reason (and also because
	 * apparently window printer drivers can seek when
	 * spooling to a file).
	 * So we first create a file, and then we pass it
	 * to spoolss in output_file so it can monitor and
	 * take over once we call EndDocPrinter().
	 * Of course we will not start writing until
	 * StartDocPrinter() actually gives the ok.
	 * smbd spooler files do not include a print jobid
	 * path component, as the jobid is only known after
	 * calling StartDocPrinter().
	 */

	pf->filename = talloc_asprintf(pf, "%s/%sXXXXXX",
					lp_pathname(SNUM(fsp->conn)),
					PRINT_SPOOL_PREFIX);
	if (!pf->filename) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}
	errno = 0;
	fd = mkstemp(pf->filename);
	if (fd == -1) {
		if (errno == EACCES) {
			/* Common setup error, force a report. */
			DEBUG(0, ("Insufficient permissions "
				  "to open spool file %s.\n",
				  pf->filename));
		} else {
			/* Normal case, report at level 3 and above. */
			DEBUG(3, ("can't open spool file %s,\n",
				  pf->filename));
			DEBUGADD(3, ("errno = %d (%s).\n",
				     errno, strerror(errno)));
		}
		status = map_nt_error_from_unix(errno);
		goto done;
	}

	/* now open a document over spoolss so that it does
	 * all printer verification, and eventually assigns
	 * a job id */

	status = rpc_pipe_open_interface(fsp->conn,
					 &ndr_table_spoolss.syntax_id,
					 fsp->conn->session_info,
					 &fsp->conn->sconn->client_id,
					 fsp->conn->sconn->msg_ctx,
					 &fsp->conn->spoolss_pipe);
	if (!NT_STATUS_IS_OK(status)) {
		goto done;
	}
	b = fsp->conn->spoolss_pipe->binding_handle;

	ZERO_STRUCT(devmode_ctr);

	status = dcerpc_spoolss_OpenPrinter(b, pf, pf->svcname,
					    "RAW", devmode_ctr,
					    SEC_FLAG_MAXIMUM_ALLOWED,
					    &pf->handle, &werr);
	if (!NT_STATUS_IS_OK(status)) {
		goto done;
	}
	if (!W_ERROR_IS_OK(werr)) {
		status = werror_to_ntstatus(werr);
		goto done;
	}

	info.info1 = talloc(tmp_ctx, struct spoolss_DocumentInfo1);
	if (!info.info1) {
		status = NT_STATUS_NO_MEMORY;
		goto done;
	}
	info.info1->document_name = pf->docname;
	info.info1->output_file = pf->filename;
	info.info1->datatype = "RAW";

	status = dcerpc_spoolss_StartDocPrinter(b, tmp_ctx, &pf->handle,
						1, info, &pf->jobid, &werr);
	if (!NT_STATUS_IS_OK(status)) {
		goto done;
	}
	if (!W_ERROR_IS_OK(werr)) {
		status = werror_to_ntstatus(werr);
		goto done;
	}

	/* Convert to RAP id. */
	pf->rap_jobid = pjobid_to_rap(pf->svcname, pf->jobid);
	if (pf->rap_jobid == 0) {
		/* No errno around here */
		status = NT_STATUS_ACCESS_DENIED;
		goto done;
	}

	/* setup a full fsp */
	status = create_synthetic_smb_fname(fsp, pf->filename, NULL,
					    NULL, &fsp->fsp_name);
	if (!NT_STATUS_IS_OK(status)) {
		goto done;
	}

	if (sys_fstat(fd, &fsp->fsp_name->st, false) != 0) {
		status = map_nt_error_from_unix(errno);
		goto done;
	}

	fsp->file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st);
	fsp->mode = fsp->fsp_name->st.st_ex_mode;
	fsp->fh->fd = fd;

	fsp->vuid = current_vuid;
	fsp->can_lock = false;
	fsp->can_read = false;
	fsp->access_mask = FILE_GENERIC_WRITE;
	fsp->can_write = true;
	fsp->modified = false;
	fsp->oplock_type = NO_OPLOCK;
	fsp->sent_oplock_break = NO_BREAK_SENT;
	fsp->is_directory = false;

	fsp->print_file = pf;

	status = NT_STATUS_OK;
done:
	if (!NT_STATUS_IS_OK(status)) {
		if (fd != -1) {
			close(fd);
			if (fsp->print_file) {
				unlink(fsp->print_file->filename);
			}
		}
		/* We need to delete the job from spoolss too */
		if (pf->jobid) {
			print_spool_terminate(fsp->conn, pf);
		}
	}
	talloc_free(tmp_ctx);
	return status;
}
Example #11
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;
}
Example #12
0
NTSTATUS unix_convert(TALLOC_CTX *ctx,
		      connection_struct *conn,
		      const char *orig_path,
		      struct smb_filename **smb_fname_out,
		      uint32_t ucf_flags)
{
	struct smb_filename *smb_fname = NULL;
	char *start, *end;
	char *dirpath = NULL;
	char *stream = NULL;
	bool component_was_mangled = False;
	bool name_has_wildcard = False;
	bool posix_pathnames = false;
	bool allow_wcard_last_component =
	    (ucf_flags & UCF_ALWAYS_ALLOW_WCARD_LCOMP);
	bool save_last_component = ucf_flags & UCF_SAVE_LCOMP;
	NTSTATUS status;
	int ret = -1;

	*smb_fname_out = NULL;

	smb_fname = talloc_zero(ctx, struct smb_filename);
	if (smb_fname == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	if (conn->printer) {
		/* we don't ever use the filenames on a printer share as a
			filename - so don't convert them */
		if (!(smb_fname->base_name = talloc_strdup(smb_fname,
							   orig_path))) {
			status = NT_STATUS_NO_MEMORY;
			goto err;
		}
		goto done;
	}

	DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path));

	/*
	 * Conversion to basic unix format is already done in
	 * check_path_syntax().
	 */

	/*
	 * Names must be relative to the root of the service - any leading /.
	 * and trailing /'s should have been trimmed by check_path_syntax().
	 */

#ifdef DEVELOPER
	SMB_ASSERT(*orig_path != '/');
#endif

	/*
	 * If we trimmed down to a single '\0' character
	 * then we should use the "." directory to avoid
	 * searching the cache, but not if we are in a
	 * printing share.
	 * As we know this is valid we can return true here.
	 */

	if (!*orig_path) {
		if (!(smb_fname->base_name = talloc_strdup(smb_fname, "."))) {
			status = NT_STATUS_NO_MEMORY;
			goto err;
		}
		if (SMB_VFS_STAT(conn, smb_fname) != 0) {
			status = map_nt_error_from_unix(errno);
			goto err;
		}
		DEBUG(5, ("conversion finished \"\" -> %s\n",
			  smb_fname->base_name));
		goto done;
	}

	if (orig_path[0] == '.' && (orig_path[1] == '/' ||
				orig_path[1] == '\0')) {
		/* Start of pathname can't be "." only. */
		if (orig_path[1] == '\0' || orig_path[2] == '\0') {
			status = NT_STATUS_OBJECT_NAME_INVALID;
		} else {
			status =determine_path_error(&orig_path[2],
			    allow_wcard_last_component);
		}
		goto err;
	}

	/* Start with the full orig_path as given by the caller. */
	if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto err;
	}

	/*
	 * Large directory fix normalization. If we're case sensitive, and
	 * the case preserving parameters are set to "no", normalize the case of
	 * the incoming filename from the client WHETHER IT EXISTS OR NOT !
	 * This is in conflict with the current (3.0.20) man page, but is
	 * what people expect from the "large directory howto". I'll update
	 * the man page. Thanks to [email protected] for finding this. JRA.
	 */

	if (conn->case_sensitive && !conn->case_preserve &&
			!conn->short_case_preserve) {
		strnorm(smb_fname->base_name, lp_defaultcase(SNUM(conn)));
	}

	/*
	 * Ensure saved_last_component is valid even if file exists.
	 */

	if(save_last_component) {
		end = strrchr_m(smb_fname->base_name, '/');
		if (end) {
			smb_fname->original_lcomp = talloc_strdup(smb_fname,
								  end + 1);
		} else {
			smb_fname->original_lcomp =
			    talloc_strdup(smb_fname, smb_fname->base_name);
		}
		if (smb_fname->original_lcomp == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto err;
		}
	}

	posix_pathnames = (lp_posix_pathnames() ||
				(ucf_flags & UCF_POSIX_PATHNAMES));

	/*
	 * Strip off the stream, and add it back when we're done with the
	 * base_name.
	 */
	if (!posix_pathnames) {
		stream = strchr_m(smb_fname->base_name, ':');

		if (stream != NULL) {
			char *tmp = talloc_strdup(smb_fname, stream);
			if (tmp == NULL) {
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
			/*
			 * Since this is actually pointing into
			 * smb_fname->base_name this truncates base_name.
			 */
			*stream = '\0';
			stream = tmp;
		}
	}

	start = smb_fname->base_name;

	/*
	 * If we're providing case insensitive semantics or
	 * the underlying filesystem is case insensitive,
	 * then a case-normalized hit in the stat-cache is
	 * authoratitive. JRA.
	 *
	 * Note: We're only checking base_name.  The stream_name will be
	 * added and verified in build_stream_path().
	 */

	if((!conn->case_sensitive || !(conn->fs_capabilities &
				       FILE_CASE_SENSITIVE_SEARCH)) &&
	    stat_cache_lookup(conn, posix_pathnames, &smb_fname->base_name, &dirpath, &start,
			      &smb_fname->st)) {
		goto done;
	}

	/*
	 * Make sure "dirpath" is an allocated string, we use this for
	 * building the directories with talloc_asprintf and free it.
	 */

	if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto err;
	}

	/*
	 * If we have a wildcard we must walk the path to
	 * find where the error is, even if case sensitive
	 * is true.
	 */

	name_has_wildcard = ms_has_wild(smb_fname->base_name);
	if (name_has_wildcard && !allow_wcard_last_component) {
		/* Wildcard not valid anywhere. */
		status = NT_STATUS_OBJECT_NAME_INVALID;
		goto fail;
	}

	DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n",
		 smb_fname->base_name, dirpath, start));

	if (!name_has_wildcard) {
		/*
		 * stat the name - if it exists then we can add the stream back (if
		 * there was one) and be done!
		 */

		if (posix_pathnames) {
			ret = SMB_VFS_LSTAT(conn, smb_fname);
		} else {
			ret = SMB_VFS_STAT(conn, smb_fname);
		}

		if (ret == 0) {
			status = check_for_dot_component(smb_fname);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}
			/* Add the path (not including the stream) to the cache. */
			stat_cache_add(orig_path, smb_fname->base_name,
				       conn->case_sensitive);
			DEBUG(5,("conversion of base_name finished %s -> %s\n",
				 orig_path, smb_fname->base_name));
			goto done;
		}

		/* Stat failed - ensure we don't use it. */
		SET_STAT_INVALID(smb_fname->st);

		if (errno == ENOENT) {
			/* Optimization when creating a new file - only
			   the last component doesn't exist. */
			status = check_parent_exists(ctx,
						conn,
						posix_pathnames,
						smb_fname,
						&dirpath,
						&start);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}
		}

		/*
		 * A special case - if we don't have any wildcards or mangling chars and are case
		 * sensitive or the underlying filesystem is case insensitive then searching
		 * won't help.
		 */

		if ((conn->case_sensitive || !(conn->fs_capabilities &
					FILE_CASE_SENSITIVE_SEARCH)) &&
				!mangle_is_mangled(smb_fname->base_name, conn->params)) {

			status = check_for_dot_component(smb_fname);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}

			/*
			 * The stat failed. Could be ok as it could be
 			 * a new file.
 			 */

			if (errno == ENOTDIR || errno == ELOOP) {
				status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			} else if (errno == ENOENT) {
				/*
				 * Was it a missing last component ?
				 * or a missing intermediate component ?
				 */
				struct smb_filename parent_fname;
				const char *last_component = NULL;

				ZERO_STRUCT(parent_fname);
				if (!parent_dirname(ctx, smb_fname->base_name,
							&parent_fname.base_name,
							&last_component)) {
					status = NT_STATUS_NO_MEMORY;
					goto fail;
				}
				if (posix_pathnames) {
					ret = SMB_VFS_LSTAT(conn, &parent_fname);
				} else {
					ret = SMB_VFS_STAT(conn, &parent_fname);
				}
				if (ret == -1) {
					if (errno == ENOTDIR ||
							errno == ENOENT ||
							errno == ELOOP) {
						status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
						goto fail;
					}
				}

				/*
				 * Missing last component is ok - new file.
				 * Also deal with permission denied elsewhere.
				 * Just drop out to done.
				 */
				goto done;
			}
		}
	} else {
		/*
		 * We have a wildcard in the pathname.
		 *
		 * Optimization for common case where the wildcard
		 * is in the last component and the client already
		 * sent the correct case.
		 */
		status = check_parent_exists(ctx,
					conn,
					posix_pathnames,
					smb_fname,
					&dirpath,
					&start);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
	}

	/*
	 * is_mangled() was changed to look at an entire pathname, not
	 * just a component. JRA.
	 */

	if (mangle_is_mangled(start, conn->params)) {
		component_was_mangled = True;
	}

	/*
	 * Now we need to recursively match the name against the real
	 * directory structure.
	 */

	/*
	 * Match each part of the path name separately, trying the names
	 * as is first, then trying to scan the directory for matching names.
	 */

	for (; start ; start = (end?end+1:(char *)NULL)) {
		/*
		 * Pinpoint the end of this section of the filename.
		 */
		/* mb safe. '/' can't be in any encoded char. */
		end = strchr(start, '/');

		/*
		 * Chop the name at this point.
		 */
		if (end) {
			*end = 0;
		}

		if (save_last_component) {
			TALLOC_FREE(smb_fname->original_lcomp);
			smb_fname->original_lcomp = talloc_strdup(smb_fname,
							end ? end + 1 : start);
			if (!smb_fname->original_lcomp) {
				DEBUG(0, ("talloc failed\n"));
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
		}

		/* The name cannot have a component of "." */

		if (ISDOT(start)) {
			if (!end)  {
				/* Error code at the end of a pathname. */
				status = NT_STATUS_OBJECT_NAME_INVALID;
			} else {
				status = determine_path_error(end+1,
						allow_wcard_last_component);
			}
			goto fail;
		}

		/* The name cannot have a wildcard if it's not
		   the last component. */

		name_has_wildcard = ms_has_wild(start);

		/* Wildcards never valid within a pathname. */
		if (name_has_wildcard && end) {
			status = NT_STATUS_OBJECT_NAME_INVALID;
			goto fail;
		}

		/* Skip the stat call if it's a wildcard end. */
		if (name_has_wildcard) {
			DEBUG(5,("Wildcard %s\n",start));
			goto done;
		}

		/*
		 * Check if the name exists up to this point.
		 */

		if (posix_pathnames) {
			ret = SMB_VFS_LSTAT(conn, smb_fname);
		} else {
			ret = SMB_VFS_STAT(conn, smb_fname);
		}

		if (ret == 0) {
			/*
			 * It exists. it must either be a directory or this must
			 * be the last part of the path for it to be OK.
			 */
			if (end && !S_ISDIR(smb_fname->st.st_ex_mode)) {
				/*
				 * An intermediate part of the name isn't
				 * a directory.
				 */
				DEBUG(5,("Not a dir %s\n",start));
				*end = '/';
				/*
				 * We need to return the fact that the
				 * intermediate name resolution failed. This
				 * is used to return an error of ERRbadpath
				 * rather than ERRbadfile. Some Windows
				 * applications depend on the difference between
				 * these two errors.
				 */
				status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			}

		} else {
			char *found_name = NULL;

			/* Stat failed - ensure we don't use it. */
			SET_STAT_INVALID(smb_fname->st);

			/*
			 * Reset errno so we can detect
			 * directory open errors.
			 */
			errno = 0;

			/*
			 * Try to find this part of the path in the directory.
			 */

			if (name_has_wildcard ||
			    (get_real_filename(conn, dirpath, start,
					       talloc_tos(),
					       &found_name) == -1)) {
				char *unmangled;

				if (end) {
					/*
					 * An intermediate part of the name
					 * can't be found.
					 */
					DEBUG(5,("Intermediate not found %s\n",
							start));
					*end = '/';

					/*
					 * We need to return the fact that the
					 * intermediate name resolution failed.
					 * This is used to return an error of
					 * ERRbadpath rather than ERRbadfile.
					 * Some Windows applications depend on
					 * the difference between these two
					 * errors.
					 */

					/*
					 * ENOENT, ENOTDIR and ELOOP all map
					 * to NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */

					if (errno == ENOENT ||
							errno == ENOTDIR ||
							errno == ELOOP) {
						status =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					else {
						status =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/*
				 * ENOENT/EACCESS are the only valid errors
				 * here. EACCESS needs handling here for
				 * "dropboxes", i.e. directories where users
				 * can only put stuff with permission -wx.
				 */
				if ((errno != 0) && (errno != ENOENT)
				    && (errno != EACCES)) {
					/*
					 * ENOTDIR and ELOOP both map to
					 * NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */
					if (errno == ENOTDIR ||
							errno == ELOOP) {
						status =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					} else {
						status =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/*
				 * Just the last part of the name doesn't exist.
				 * We need to strupper() or strlower() it as
				 * this conversion may be used for file creation
				 * purposes. Fix inspired by
				 * Thomas Neumann <*****@*****.**>.
				 */
				if (!conn->case_preserve ||
				    (mangle_is_8_3(start, False,
						   conn->params) &&
						 !conn->short_case_preserve)) {
					strnorm(start,
						lp_defaultcase(SNUM(conn)));
				}

				/*
				 * check on the mangled stack to see if we can
				 * recover the base of the filename.
				 */

				if (mangle_is_mangled(start, conn->params)
				    && mangle_lookup_name_from_8_3(ctx,
					    		start,
							&unmangled,
							conn->params)) {
					char *tmp;
					size_t start_ofs =
					    start - smb_fname->base_name;

					if (*dirpath != '\0') {
						tmp = talloc_asprintf(
							smb_fname, "%s/%s",
							dirpath, unmangled);
						TALLOC_FREE(unmangled);
					}
					else {
						tmp = unmangled;
					}
					if (tmp == NULL) {
						DEBUG(0, ("talloc failed\n"));
						status = NT_STATUS_NO_MEMORY;
						goto err;
					}
					TALLOC_FREE(smb_fname->base_name);
					smb_fname->base_name = tmp;
					start =
					    smb_fname->base_name + start_ofs;
					end = start + strlen(start);
				}

				DEBUG(5,("New file %s\n",start));
				goto done;
			}


			/*
			 * Restore the rest of the string. If the string was
			 * mangled the size may have changed.
			 */
			if (end) {
				char *tmp;
				size_t start_ofs =
				    start - smb_fname->base_name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(smb_fname,
						"%s/%s/%s", dirpath,
						found_name, end+1);
				}
				else {
					tmp = talloc_asprintf(smb_fname,
						"%s/%s", found_name,
						end+1);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc_asprintf failed\n"));
					status = NT_STATUS_NO_MEMORY;
					goto err;
				}
				TALLOC_FREE(smb_fname->base_name);
				smb_fname->base_name = tmp;
				start = smb_fname->base_name + start_ofs;
				end = start + strlen(found_name);
				*end = '\0';
			} else {
				char *tmp;
				size_t start_ofs =
				    start - smb_fname->base_name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(smb_fname,
						"%s/%s", dirpath,
						found_name);
				} else {
					tmp = talloc_strdup(smb_fname,
						found_name);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc failed\n"));
					status = NT_STATUS_NO_MEMORY;
					goto err;
				}
				TALLOC_FREE(smb_fname->base_name);
				smb_fname->base_name = tmp;
				start = smb_fname->base_name + start_ofs;

				/*
				 * We just scanned for, and found the end of
				 * the path. We must return a valid stat struct
				 * if it exists. JRA.
				 */

				if (posix_pathnames) {
					ret = SMB_VFS_LSTAT(conn, smb_fname);
				} else {
					ret = SMB_VFS_STAT(conn, smb_fname);
				}

				if (ret != 0) {
					SET_STAT_INVALID(smb_fname->st);
				}
			}

			TALLOC_FREE(found_name);
		} /* end else */

#ifdef DEVELOPER
		/*
		 * This sucks!
		 * We should never provide different behaviors
		 * depending on DEVELOPER!!!
		 */
		if (VALID_STAT(smb_fname->st)) {
			bool delete_pending;
			uint32_t name_hash;

			status = file_name_hash(conn,
					smb_fname_str_dbg(smb_fname),
					&name_hash);
			if (!NT_STATUS_IS_OK(status)) {
				goto fail;
			}

			get_file_infos(vfs_file_id_from_sbuf(conn,
							     &smb_fname->st),
				       name_hash,
				       &delete_pending, NULL);
			if (delete_pending) {
				status = NT_STATUS_DELETE_PENDING;
				goto fail;
			}
		}
#endif

		/*
		 * Add to the dirpath that we have resolved so far.
		 */

		if (*dirpath != '\0') {
			char *tmp = talloc_asprintf(ctx,
					"%s/%s", dirpath, start);
			if (!tmp) {
				DEBUG(0, ("talloc_asprintf failed\n"));
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
			TALLOC_FREE(dirpath);
			dirpath = tmp;
		}
		else {
			TALLOC_FREE(dirpath);
			if (!(dirpath = talloc_strdup(ctx,start))) {
				DEBUG(0, ("talloc_strdup failed\n"));
				status = NT_STATUS_NO_MEMORY;
				goto err;
			}
		}

		/*
		 * Cache the dirpath thus far. Don't cache a name with mangled
		 * or wildcard components as this can change the size.
		 */
		if(!component_was_mangled && !name_has_wildcard) {
			stat_cache_add(orig_path, dirpath,
					conn->case_sensitive);
		}

		/*
		 * Restore the / that we wiped out earlier.
		 */
		if (end) {
			*end = '/';
		}
	}

	/*
	 * Cache the full path. Don't cache a name with mangled or wildcard
	 * components as this can change the size.
	 */

	if(!component_was_mangled && !name_has_wildcard) {
		stat_cache_add(orig_path, smb_fname->base_name,
			       conn->case_sensitive);
	}

	/*
	 * The name has been resolved.
	 */

	DEBUG(5,("conversion finished %s -> %s\n", orig_path,
		 smb_fname->base_name));

 done:
	/* Add back the stream if one was stripped off originally. */
	if (stream != NULL) {
		smb_fname->stream_name = stream;

		/* Check path now that the base_name has been converted. */
		status = build_stream_path(ctx, conn, orig_path, smb_fname);
		if (!NT_STATUS_IS_OK(status)) {
			goto fail;
		}
	}
	TALLOC_FREE(dirpath);
	*smb_fname_out = smb_fname;
	return NT_STATUS_OK;
 fail:
	DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start));
	if (*dirpath != '\0') {
		smb_fname->base_name = talloc_asprintf(smb_fname, "%s/%s",
						       dirpath, start);
	} else {
		smb_fname->base_name = talloc_strdup(smb_fname, start);
	}
	if (!smb_fname->base_name) {
		DEBUG(0, ("talloc_asprintf failed\n"));
		status = NT_STATUS_NO_MEMORY;
		goto err;
	}

	*smb_fname_out = smb_fname;
	TALLOC_FREE(dirpath);
	return status;
 err:
	TALLOC_FREE(smb_fname);
	return status;
}
Example #13
0
static int acl_common_remove_object(vfs_handle_struct *handle,
					const char *path,
					bool is_directory)
{
	connection_struct *conn = handle->conn;
	struct file_id id;
	files_struct *fsp = NULL;
	int ret = 0;
	char *parent_dir = NULL;
	const char *final_component = NULL;
	struct smb_filename local_fname;
	int saved_errno = 0;

	if (!parent_dirname(talloc_tos(), path,
			&parent_dir, &final_component)) {
		saved_errno = ENOMEM;
		goto out;
	}

	DEBUG(10,("acl_common_remove_object: removing %s %s/%s\n",
		is_directory ? "directory" : "file",
		parent_dir, final_component ));

	/* cd into the parent dir to pin it. */
	ret = SMB_VFS_CHDIR(conn, parent_dir);
	if (ret == -1) {
		saved_errno = errno;
		goto out;
	}

	ZERO_STRUCT(local_fname);
	local_fname.base_name = CONST_DISCARD(char *,final_component);

	/* Must use lstat here. */
	ret = SMB_VFS_LSTAT(conn, &local_fname);
	if (ret == -1) {
		saved_errno = errno;
		goto out;
	}

	/* Ensure we have this file open with DELETE access. */
	id = vfs_file_id_from_sbuf(conn, &local_fname.st);
	for (fsp = file_find_di_first(id); fsp; file_find_di_next(fsp)) {
		if (fsp->access_mask & DELETE_ACCESS &&
				fsp->delete_on_close) {
			/* We did open this for delete,
			 * allow the delete as root.
			 */
			break;
		}
	}

	if (!fsp) {
		DEBUG(10,("acl_common_remove_object: %s %s/%s "
			"not an open file\n",
			is_directory ? "directory" : "file",
			parent_dir, final_component ));
		saved_errno = EACCES;
		goto out;
	}

	become_root();
	if (is_directory) {
		ret = SMB_VFS_NEXT_RMDIR(handle, final_component);
	} else {
		ret = SMB_VFS_NEXT_UNLINK(handle, &local_fname);
	}
	unbecome_root();

	if (ret == -1) {
		saved_errno = errno;
	}

  out:

	TALLOC_FREE(parent_dir);

	vfs_ChDir(conn, conn->connectpath);
	if (saved_errno) {
		errno = saved_errno;
	}
	return ret;
}
Example #14
0
NTSTATUS unix_convert(TALLOC_CTX *ctx,
			connection_struct *conn,
			const char *orig_path,
			bool allow_wcard_last_component,
			char **pp_conv_path,
			char **pp_saved_last_component,
			SMB_STRUCT_STAT *pst)
{
	SMB_STRUCT_STAT st;
	char *start, *end;
	char *dirpath = NULL;
	char *name = NULL;
	char *stream = NULL;
	bool component_was_mangled = False;
	bool name_has_wildcard = False;
	bool posix_pathnames = false;
	NTSTATUS result;
	int ret = -1;

	SET_STAT_INVALID(*pst);
	*pp_conv_path = NULL;
	if(pp_saved_last_component) {
		*pp_saved_last_component = NULL;
	}

	if (conn->printer) {
		/* we don't ever use the filenames on a printer share as a
			filename - so don't convert them */
		if (!(*pp_conv_path = talloc_strdup(ctx,orig_path))) {
			return NT_STATUS_NO_MEMORY;
		}
		return NT_STATUS_OK;
	}

	DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path));

	/*
	 * Conversion to basic unix format is already done in
	 * check_path_syntax().
	 */

	/*
	 * Names must be relative to the root of the service - any leading /.
	 * and trailing /'s should have been trimmed by check_path_syntax().
	 */

#ifdef DEVELOPER
	SMB_ASSERT(*orig_path != '/');
#endif

	/*
	 * If we trimmed down to a single '\0' character
	 * then we should use the "." directory to avoid
	 * searching the cache, but not if we are in a
	 * printing share.
	 * As we know this is valid we can return true here.
	 */

	if (!*orig_path) {
		if (!(name = talloc_strdup(ctx,"."))) {
			return NT_STATUS_NO_MEMORY;
		}
		if (SMB_VFS_STAT(conn,name,&st) == 0) {
			*pst = st;
		} else {
			return map_nt_error_from_unix(errno);
		}
		DEBUG(5,("conversion finished \"\" -> %s\n",name));
		goto done;
	}

	if (orig_path[0] == '.' && (orig_path[1] == '/' ||
				orig_path[1] == '\0')) {
		/* Start of pathname can't be "." only. */
		if (orig_path[1] == '\0' || orig_path[2] == '\0') {
			result = NT_STATUS_OBJECT_NAME_INVALID;
		} else {
			result =determine_path_error(
				&orig_path[2], allow_wcard_last_component);
		}
		return result;
	}

	if (!(name = talloc_strdup(ctx, orig_path))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		return NT_STATUS_NO_MEMORY;
	}

	/*
	 * Large directory fix normalization. If we're case sensitive, and
	 * the case preserving parameters are set to "no", normalize the case of
	 * the incoming filename from the client WHETHER IT EXISTS OR NOT !
	 * This is in conflict with the current (3.0.20) man page, but is
	 * what people expect from the "large directory howto". I'll update
	 * the man page. Thanks to [email protected] for finding this. JRA.
	 */

	if (conn->case_sensitive && !conn->case_preserve &&
			!conn->short_case_preserve) {
		strnorm(name, lp_defaultcase(SNUM(conn)));
	}

	/*
	 * Ensure saved_last_component is valid even if file exists.
	 */

	if(pp_saved_last_component) {
		end = strrchr_m(name, '/');
		if (end) {
			*pp_saved_last_component = talloc_strdup(ctx, end + 1);
		} else {
			*pp_saved_last_component = talloc_strdup(ctx,
							name);
		}
	}

	posix_pathnames = lp_posix_pathnames();

	if (!posix_pathnames) {
		stream = strchr_m(name, ':');

		if (stream != NULL) {
			char *tmp = talloc_strdup(ctx, stream);
			if (tmp == NULL) {
				TALLOC_FREE(name);
				return NT_STATUS_NO_MEMORY;
			}
			*stream = '\0';
			stream = tmp;
		}
	}

	start = name;

	/* If we're providing case insentive semantics or
	 * the underlying filesystem is case insensitive,
	 * then a case-normalized hit in the stat-cache is
	 * authoratitive. JRA.
	 */

	if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) &&
			stat_cache_lookup(conn, &name, &dirpath, &start, &st)) {
		*pst = st;
		goto done;
	}

	/*
	 * Make sure "dirpath" is an allocated string, we use this for
	 * building the directories with asprintf and free it.
	 */

	if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) {
		DEBUG(0, ("talloc_strdup failed\n"));
		TALLOC_FREE(name);
		return NT_STATUS_NO_MEMORY;
	}

	/*
	 * stat the name - if it exists then we are all done!
	 */

	if (posix_pathnames) {
		ret = SMB_VFS_LSTAT(conn,name,&st);
	} else {
		ret = SMB_VFS_STAT(conn,name,&st);
	}

	if (ret == 0) {
		/* Ensure we catch all names with in "/."
		   this is disallowed under Windows. */
		const char *p = strstr(name, "/."); /* mb safe. */
		if (p) {
			if (p[2] == '/') {
				/* Error code within a pathname. */
				result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			} else if (p[2] == '\0') {
				/* Error code at the end of a pathname. */
				result = NT_STATUS_OBJECT_NAME_INVALID;
				goto fail;
			}
		}
		stat_cache_add(orig_path, name, conn->case_sensitive);
		DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
		*pst = st;
		goto done;
	}

	DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n",
				name, dirpath, start));

	/*
	 * A special case - if we don't have any mangling chars and are case
	 * sensitive or the underlying filesystem is case insentive then searching
	 * won't help.
	 */

	if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) &&
			!mangle_is_mangled(name, conn->params)) {
		goto done;
	}

	/*
	 * is_mangled() was changed to look at an entire pathname, not
	 * just a component. JRA.
	 */

	if (mangle_is_mangled(start, conn->params)) {
		component_was_mangled = True;
	}

	/*
	 * Now we need to recursively match the name against the real
	 * directory structure.
	 */

	/*
	 * Match each part of the path name separately, trying the names
	 * as is first, then trying to scan the directory for matching names.
	 */

	for (; start ; start = (end?end+1:(char *)NULL)) {
		/*
		 * Pinpoint the end of this section of the filename.
		 */
		/* mb safe. '/' can't be in any encoded char. */
		end = strchr(start, '/');

		/*
		 * Chop the name at this point.
		 */
		if (end) {
			*end = 0;
		}

		if (pp_saved_last_component) {
			TALLOC_FREE(*pp_saved_last_component);
			*pp_saved_last_component = talloc_strdup(ctx,
							end ? end + 1 : start);
			if (!*pp_saved_last_component) {
				DEBUG(0, ("talloc failed\n"));
				return NT_STATUS_NO_MEMORY;
			}
		}

		/* The name cannot have a component of "." */

		if (ISDOT(start)) {
			if (!end)  {
				/* Error code at the end of a pathname. */
				result = NT_STATUS_OBJECT_NAME_INVALID;
			} else {
				result = determine_path_error(end+1,
						allow_wcard_last_component);
			}
			goto fail;
		}

		/* The name cannot have a wildcard if it's not
		   the last component. */

		name_has_wildcard = ms_has_wild(start);

		/* Wildcard not valid anywhere. */
		if (name_has_wildcard && !allow_wcard_last_component) {
			result = NT_STATUS_OBJECT_NAME_INVALID;
			goto fail;
		}

		/* Wildcards never valid within a pathname. */
		if (name_has_wildcard && end) {
			result = NT_STATUS_OBJECT_NAME_INVALID;
			goto fail;
		}

		/*
		 * Check if the name exists up to this point.
		 */

		if (posix_pathnames) {
			ret = SMB_VFS_LSTAT(conn,name, &st);
		} else {
			ret = SMB_VFS_STAT(conn,name, &st);
		}

		if (ret == 0) {
			/*
			 * It exists. it must either be a directory or this must
			 * be the last part of the path for it to be OK.
			 */
			if (end && !(st.st_mode & S_IFDIR)) {
				/*
				 * An intermediate part of the name isn't
				 * a directory.
				 */
				DEBUG(5,("Not a dir %s\n",start));
				*end = '/';
				/*
				 * We need to return the fact that the
				 * intermediate name resolution failed. This
				 * is used to return an error of ERRbadpath
				 * rather than ERRbadfile. Some Windows
				 * applications depend on the difference between
				 * these two errors.
				 */
				result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
				goto fail;
			}

			if (!end) {
				/*
				 * We just scanned for, and found the end of
				 * the path. We must return the valid stat
				 * struct. JRA.
				 */

				*pst = st;
			}

		} else {
			char *found_name = NULL;

			/* Stat failed - ensure we don't use it. */
			SET_STAT_INVALID(st);

			/*
			 * Reset errno so we can detect
			 * directory open errors.
			 */
			errno = 0;

			/*
			 * Try to find this part of the path in the directory.
			 */

			if (name_has_wildcard ||
			    (get_real_filename_mangled(
				     conn, dirpath, start,
				     talloc_tos(), &found_name) == -1)) {
				char *unmangled;

				if (end) {
					/*
					 * An intermediate part of the name
					 * can't be found.
					 */
					DEBUG(5,("Intermediate not found %s\n",
							start));
					*end = '/';

					/*
					 * We need to return the fact that the
					 * intermediate name resolution failed.
					 * This is used to return an error of
					 * ERRbadpath rather than ERRbadfile.
					 * Some Windows applications depend on
					 * the difference between these two
					 * errors.
					 */

					/*
					 * ENOENT, ENOTDIR and ELOOP all map
					 * to NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */

					if (errno == ENOENT ||
							errno == ENOTDIR ||
							errno == ELOOP) {
						result =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					else {
						result =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/* ENOENT is the only valid error here. */
				if ((errno != 0) && (errno != ENOENT)) {
					/*
					 * ENOTDIR and ELOOP both map to
					 * NT_STATUS_OBJECT_PATH_NOT_FOUND
					 * in the filename walk.
					 */
					if (errno == ENOTDIR ||
							errno == ELOOP) {
						result =
						NT_STATUS_OBJECT_PATH_NOT_FOUND;
					}
					else {
						result =
						map_nt_error_from_unix(errno);
					}
					goto fail;
				}

				/*
				 * Just the last part of the name doesn't exist.
				 * We need to strupper() or strlower() it as
				 * this conversion may be used for file creation
				 * purposes. Fix inspired by
				 * Thomas Neumann <*****@*****.**>.
				 */
				if (!conn->case_preserve ||
				    (mangle_is_8_3(start, False,
						   conn->params) &&
						 !conn->short_case_preserve)) {
					strnorm(start,
						lp_defaultcase(SNUM(conn)));
				}

				/*
				 * check on the mangled stack to see if we can
				 * recover the base of the filename.
				 */

				if (mangle_is_mangled(start, conn->params)
				    && mangle_lookup_name_from_8_3(ctx,
					    		start,
							&unmangled,
							conn->params)) {
					char *tmp;
					size_t start_ofs = start - name;

					if (*dirpath != '\0') {
						tmp = talloc_asprintf(ctx,
							"%s/%s", dirpath,
							unmangled);
						TALLOC_FREE(unmangled);
					}
					else {
						tmp = unmangled;
					}
					if (tmp == NULL) {
						DEBUG(0, ("talloc failed\n"));
						return NT_STATUS_NO_MEMORY;
					}
					TALLOC_FREE(name);
					name = tmp;
					start = name + start_ofs;
					end = start + strlen(start);
				}

				DEBUG(5,("New file %s\n",start));
				goto done;
			}


			/*
			 * Restore the rest of the string. If the string was
			 * mangled the size may have changed.
			 */
			if (end) {
				char *tmp;
				size_t start_ofs = start - name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(ctx,
						"%s/%s/%s", dirpath,
						found_name, end+1);
				}
				else {
					tmp = talloc_asprintf(ctx,
						"%s/%s", found_name,
						end+1);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc_asprintf failed\n"));
					return NT_STATUS_NO_MEMORY;
				}
				TALLOC_FREE(name);
				name = tmp;
				start = name + start_ofs;
				end = start + strlen(found_name);
				*end = '\0';
			} else {
				char *tmp;
				size_t start_ofs = start - name;

				if (*dirpath != '\0') {
					tmp = talloc_asprintf(ctx,
						"%s/%s", dirpath,
						found_name);
				} else {
					tmp = talloc_strdup(ctx,
						found_name);
				}
				if (tmp == NULL) {
					DEBUG(0, ("talloc failed\n"));
					return NT_STATUS_NO_MEMORY;
				}
				TALLOC_FREE(name);
				name = tmp;
				start = name + start_ofs;

				/*
				 * We just scanned for, and found the end of
				 * the path. We must return a valid stat struct
				 * if it exists. JRA.
				 */

				if (posix_pathnames) {
					ret = SMB_VFS_LSTAT(conn,name, &st);
				} else {
					ret = SMB_VFS_STAT(conn,name, &st);
				}

				if (ret == 0) {
					*pst = st;
				} else {
					SET_STAT_INVALID(st);
				}
			}

			TALLOC_FREE(found_name);
		} /* end else */

#ifdef DEVELOPER
		/*
		 * This sucks!
		 * We should never provide different behaviors
		 * depending on DEVELOPER!!!
		 */
		if (VALID_STAT(st)) {
			bool delete_pending;
			get_file_infos(vfs_file_id_from_sbuf(conn, &st),
				       &delete_pending, NULL);
			if (delete_pending) {
				result = NT_STATUS_DELETE_PENDING;
				goto fail;
			}
		}
#endif

		/*
		 * Add to the dirpath that we have resolved so far.
		 */

		if (*dirpath != '\0') {
			char *tmp = talloc_asprintf(ctx,
					"%s/%s", dirpath, start);
			if (!tmp) {
				DEBUG(0, ("talloc_asprintf failed\n"));
				return NT_STATUS_NO_MEMORY;
			}
			TALLOC_FREE(dirpath);
			dirpath = tmp;
		}
		else {
			TALLOC_FREE(dirpath);
			if (!(dirpath = talloc_strdup(ctx,start))) {
				DEBUG(0, ("talloc_strdup failed\n"));
				return NT_STATUS_NO_MEMORY;
			}
		}

		/*
		 * Don't cache a name with mangled or wildcard components
		 * as this can change the size.
		 */

		if(!component_was_mangled && !name_has_wildcard) {
			stat_cache_add(orig_path, dirpath,
					conn->case_sensitive);
		}

		/*
		 * Restore the / that we wiped out earlier.
		 */
		if (end) {
			*end = '/';
		}
	}

	/*
	 * Don't cache a name with mangled or wildcard components
	 * as this can change the size.
	 */

	if(!component_was_mangled && !name_has_wildcard) {
		stat_cache_add(orig_path, name, conn->case_sensitive);
	}

	/*
	 * The name has been resolved.
	 */

	DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));

 done:
	if (stream != NULL) {
		char *tmp = NULL;

		result = build_stream_path(ctx, conn, orig_path, name, stream,
					   pst, &tmp);
		if (!NT_STATUS_IS_OK(result)) {
			goto fail;
		}

		DEBUG(10, ("build_stream_path returned %s\n", tmp));

		TALLOC_FREE(name);
		name = tmp;
	}
	*pp_conv_path = name;
	TALLOC_FREE(dirpath);
	return NT_STATUS_OK;
 fail:
	DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start));
	if (*dirpath != '\0') {
		*pp_conv_path = talloc_asprintf(ctx,
				"%s/%s", dirpath, start);
	} else {
		*pp_conv_path = talloc_strdup(ctx, start);
	}
	if (!*pp_conv_path) {
		DEBUG(0, ("talloc_asprintf failed\n"));
		return NT_STATUS_NO_MEMORY;
	}
	TALLOC_FREE(name);
	TALLOC_FREE(dirpath);
	return result;
}
Example #15
0
File: durable.c Project: hef/samba
NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
				       struct smb_request *smb1req,
				       struct smbXsrv_open *op,
				       const DATA_BLOB old_cookie,
				       TALLOC_CTX *mem_ctx,
				       files_struct **result,
				       DATA_BLOB *new_cookie)
{
	struct share_mode_lock *lck;
	struct share_mode_entry *e;
	struct files_struct *fsp = NULL;
	NTSTATUS status;
	bool ok;
	int ret;
	int flags = 0;
	struct file_id file_id;
	struct smb_filename *smb_fname = NULL;
	enum ndr_err_code ndr_err;
	struct vfs_default_durable_cookie cookie;
	DATA_BLOB new_cookie_blob = data_blob_null;

	*result = NULL;
	*new_cookie = data_blob_null;

	if (!lp_durable_handles(SNUM(conn))) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	/*
	 * the checks for kernel oplocks
	 * and similar things are done
	 * in the vfs_default_durable_cookie()
	 * call below.
	 */

	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 (!cookie.allow_reconnect) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	/* Create an smb_filename with stream_name == NULL. */
	smb_fname = synthetic_smb_fname(talloc_tos(), cookie.base_name,
					NULL, NULL);
	if (smb_fname == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	ret = SMB_VFS_LSTAT(conn, smb_fname);
	if (ret == -1) {
		status = map_nt_error_from_unix_common(errno);
		DEBUG(1, ("Unable to lstat stream: %s => %s\n",
			  smb_fname_str_dbg(smb_fname),
			  nt_errstr(status)));
		return status;
	}

	if (!S_ISREG(smb_fname->st.st_ex_mode)) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
	if (!file_id_equal(&cookie.id, &file_id)) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	/*
	 * 1. check entry in locking.tdb
	 */

	lck = get_existing_share_mode_lock(mem_ctx, file_id);
	if (lck == NULL) {
		DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
			   "not obtained from db\n"));
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	if (lck->data->num_share_modes == 0) {
		DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
			  "entry in existing share mode lock\n"));
		TALLOC_FREE(lck);
		return NT_STATUS_INTERNAL_DB_ERROR;
	}

	if (lck->data->num_share_modes > 1) {
		/*
		 * It can't be durable if there is more than one handle
		 * on the file.
		 */
		DEBUG(5, ("vfs_default_durable_reconnect: more than one "
			  "share-mode entry - can not be durable\n"));
		TALLOC_FREE(lck);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	e = &lck->data->share_modes[0];

	if (!server_id_is_disconnected(&e->pid)) {
		DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
			  "reconnect for handle that was not marked "
			  "disconnected (e.g. smbd or cluster node died)\n"));
		TALLOC_FREE(lck);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	if (e->share_file_id != op->global->open_persistent_id) {
		DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
			  "share_file_id changed %llu != %llu"
			  "(e.g. another client had opened the file)\n",
			  (unsigned long long)e->share_file_id,
			  (unsigned long long)op->global->open_persistent_id));
		TALLOC_FREE(lck);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
	    !CAN_WRITE(conn))
	{
		DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
			  "share[%s] is not writeable anymore\n",
			  lp_servicename(talloc_tos(), SNUM(conn))));
		TALLOC_FREE(lck);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	/*
	 * 2. proceed with opening file
	 */

	status = fsp_new(conn, conn, &fsp);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
			  "new fsp: %s\n", nt_errstr(status)));
		TALLOC_FREE(lck);
		return status;
	}

	fsp->fh->private_options = e->private_options;
	fsp->fh->gen_id = smbXsrv_open_hash(op);
	fsp->file_id = file_id;
	fsp->file_pid = smb1req->smbpid;
	fsp->vuid = smb1req->vuid;
	fsp->open_time = e->time;
	fsp->access_mask = e->access_mask;
	fsp->share_access = e->share_access;
	fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
	fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
	fsp->fnum = op->local_id;

	/*
	 * TODO:
	 * Do we need to store the modified flag in the DB?
	 */
	fsp->modified = false;
	/*
	 * no durables for directories
	 */
	fsp->is_directory = false;
	/*
	 * For normal files, can_lock == !is_directory
	 */
	fsp->can_lock = true;
	/*
	 * We do not support aio write behind for smb2
	 */
	fsp->aio_write_behind = false;
	fsp->oplock_type = e->op_type;

	fsp->initial_allocation_size = cookie.initial_allocation_size;
	fsp->fh->position_information = cookie.position_information;
	fsp->update_write_time_triggered = cookie.update_write_time_triggered;
	fsp->update_write_time_on_close = cookie.update_write_time_on_close;
	fsp->write_time_forced = cookie.write_time_forced;
	fsp->close_write_time = cookie.close_write_time;

	status = fsp_set_smb_fname(fsp, smb_fname);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(lck);
		fsp_free(fsp);
		DEBUG(0, ("vfs_default_durable_reconnect: "
			  "fsp_set_smb_fname failed: %s\n",
			  nt_errstr(status)));
		return status;
	}

	op->compat = fsp;
	fsp->op = op;

	e->pid = messaging_server_id(conn->sconn->msg_ctx);
	e->op_mid = smb1req->mid;
	e->share_file_id = fsp->fh->gen_id;

	ok = brl_reconnect_disconnected(fsp);
	if (!ok) {
		status = NT_STATUS_INTERNAL_ERROR;
		DEBUG(1, ("vfs_default_durable_reconnect: "
			  "failed to reopen brlocks: %s\n",
			  nt_errstr(status)));
		TALLOC_FREE(lck);
		op->compat = NULL;
		fsp_free(fsp);
		return status;
	}

	/*
	 * TODO: properly calculate open flags
	 */
	if (fsp->can_write && fsp->can_read) {
		flags = O_RDWR;
	} else if (fsp->can_write) {
		flags = O_WRONLY;
	} else if (fsp->can_read) {
		flags = O_RDONLY;
	}

	status = fd_open(conn, fsp, flags, 0 /* mode */);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(lck);
		DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
			  "file: %s\n", nt_errstr(status)));
		op->compat = NULL;
		fsp_free(fsp);
		return status;
	}

	/*
	 * We now check the stat info stored in the cookie against
	 * the current stat data from the file we just opened.
	 * If any detail differs, we deny the durable reconnect,
	 * because in that case it is very likely that someone
	 * opened the file while the handle was disconnected,
	 * which has to be interpreted as an oplock break.
	 */

	ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
	if (ret == -1) {
		status = map_nt_error_from_unix_common(errno);
		DEBUG(1, ("Unable to fstat stream: %s => %s\n",
			  smb_fname_str_dbg(smb_fname),
			  nt_errstr(status)));
		ret = SMB_VFS_CLOSE(fsp);
		if (ret == -1) {
			DEBUG(0, ("vfs_default_durable_reconnect: "
				  "SMB_VFS_CLOSE failed (%s) - leaking file "
				  "descriptor\n", strerror(errno)));
		}
		TALLOC_FREE(lck);
		op->compat = NULL;
		fsp_free(fsp);
		return status;
	}

	if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
		ret = SMB_VFS_CLOSE(fsp);
		if (ret == -1) {
			DEBUG(0, ("vfs_default_durable_reconnect: "
				  "SMB_VFS_CLOSE failed (%s) - leaking file "
				  "descriptor\n", strerror(errno)));
		}
		TALLOC_FREE(lck);
		op->compat = NULL;
		fsp_free(fsp);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
	if (!file_id_equal(&cookie.id, &file_id)) {
		ret = SMB_VFS_CLOSE(fsp);
		if (ret == -1) {
			DEBUG(0, ("vfs_default_durable_reconnect: "
				  "SMB_VFS_CLOSE failed (%s) - leaking file "
				  "descriptor\n", strerror(errno)));
		}
		TALLOC_FREE(lck);
		op->compat = NULL;
		fsp_free(fsp);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
						      &fsp->fsp_name->st,
						      fsp_str_dbg(fsp));
	if (!ok) {
		ret = SMB_VFS_CLOSE(fsp);
		if (ret == -1) {
			DEBUG(0, ("vfs_default_durable_reconnect: "
				  "SMB_VFS_CLOSE failed (%s) - leaking file "
				  "descriptor\n", strerror(errno)));
		}
		TALLOC_FREE(lck);
		op->compat = NULL;
		fsp_free(fsp);
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	status = set_file_oplock(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
			  "after opening file: %s\n", nt_errstr(status)));
		ret = SMB_VFS_CLOSE(fsp);
		if (ret == -1) {
			DEBUG(0, ("vfs_default_durable_reconnect: "
				  "SMB_VFS_CLOSE failed (%s) - leaking file "
				  "descriptor\n", strerror(errno)));
		}
		TALLOC_FREE(lck);
		op->compat = NULL;
		fsp_free(fsp);
		return status;
	}

	status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(lck);
		DEBUG(1, ("vfs_default_durable_reconnect: "
			  "vfs_default_durable_cookie - %s\n",
			  nt_errstr(status)));
		op->compat = NULL;
		fsp_free(fsp);
		return status;
	}

	smb1req->chain_fsp = fsp;
	smb1req->smb2req->compat_chain_fsp = fsp;

	DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
		   fsp_str_dbg(fsp)));

	/*
	 * release the sharemode lock: this writes the changes
	 */
	lck->data->modified = true;
	TALLOC_FREE(lck);

	*result = fsp;
	*new_cookie = new_cookie_blob;

	return NT_STATUS_OK;
}